Only include net.h when we have to
[novacoin.git] / src / irc.cpp
1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Distributed under the MIT/X11 software license, see the accompanying
3 // file license.txt or http://www.opensource.org/licenses/mit-license.php.
4
5 #include "headers.h"
6 #include "irc.h"
7 #include "net.h"
8
9 using namespace std;
10 using namespace boost;
11
12 int nGotIRCAddresses = 0;
13 bool fGotExternalIP = false;
14
15 void ThreadIRCSeed2(void* parg);
16
17
18
19
20 #pragma pack(push, 1)
21 struct ircaddr
22 {
23     int ip;
24     short port;
25 };
26 #pragma pack(pop)
27
28 string EncodeAddress(const CAddress& addr)
29 {
30     struct ircaddr tmp;
31     tmp.ip    = addr.ip;
32     tmp.port  = addr.port;
33
34     vector<unsigned char> vch(UBEGIN(tmp), UEND(tmp));
35     return string("u") + EncodeBase58Check(vch);
36 }
37
38 bool DecodeAddress(string str, CAddress& addr)
39 {
40     vector<unsigned char> vch;
41     if (!DecodeBase58Check(str.substr(1), vch))
42         return false;
43
44     struct ircaddr tmp;
45     if (vch.size() != sizeof(tmp))
46         return false;
47     memcpy(&tmp, &vch[0], sizeof(tmp));
48
49     addr = CAddress(tmp.ip, ntohs(tmp.port), NODE_NETWORK);
50     return true;
51 }
52
53
54
55
56
57
58 static bool Send(SOCKET hSocket, const char* pszSend)
59 {
60     if (strstr(pszSend, "PONG") != pszSend)
61         printf("IRC SENDING: %s\n", pszSend);
62     const char* psz = pszSend;
63     const char* pszEnd = psz + strlen(psz);
64     while (psz < pszEnd)
65     {
66         int ret = send(hSocket, psz, pszEnd - psz, MSG_NOSIGNAL);
67         if (ret < 0)
68             return false;
69         psz += ret;
70     }
71     return true;
72 }
73
74 bool RecvLine(SOCKET hSocket, string& strLine)
75 {
76     strLine = "";
77     loop
78     {
79         char c;
80         int nBytes = recv(hSocket, &c, 1, 0);
81         if (nBytes > 0)
82         {
83             if (c == '\n')
84                 continue;
85             if (c == '\r')
86                 return true;
87             strLine += c;
88             if (strLine.size() >= 9000)
89                 return true;
90         }
91         else if (nBytes <= 0)
92         {
93             if (fShutdown)
94                 return false;
95             if (nBytes < 0)
96             {
97                 int nErr = WSAGetLastError();
98                 if (nErr == WSAEMSGSIZE)
99                     continue;
100                 if (nErr == WSAEWOULDBLOCK || nErr == WSAEINTR || nErr == WSAEINPROGRESS)
101                 {
102                     Sleep(10);
103                     continue;
104                 }
105             }
106             if (!strLine.empty())
107                 return true;
108             if (nBytes == 0)
109             {
110                 // socket closed
111                 printf("IRC socket closed\n");
112                 return false;
113             }
114             else
115             {
116                 // socket error
117                 int nErr = WSAGetLastError();
118                 printf("IRC recv failed: %d\n", nErr);
119                 return false;
120             }
121         }
122     }
123 }
124
125 bool RecvLineIRC(SOCKET hSocket, string& strLine)
126 {
127     loop
128     {
129         bool fRet = RecvLine(hSocket, strLine);
130         if (fRet)
131         {
132             if (fShutdown)
133                 return false;
134             vector<string> vWords;
135             ParseString(strLine, ' ', vWords);
136             if (vWords.size() >= 1 && vWords[0] == "PING")
137             {
138                 strLine[1] = 'O';
139                 strLine += '\r';
140                 Send(hSocket, strLine.c_str());
141                 continue;
142             }
143         }
144         return fRet;
145     }
146 }
147
148 int RecvUntil(SOCKET hSocket, const char* psz1, const char* psz2=NULL, const char* psz3=NULL, const char* psz4=NULL)
149 {
150     loop
151     {
152         string strLine;
153         strLine.reserve(10000);
154         if (!RecvLineIRC(hSocket, strLine))
155             return 0;
156         printf("IRC %s\n", strLine.c_str());
157         if (psz1 && strLine.find(psz1) != -1)
158             return 1;
159         if (psz2 && strLine.find(psz2) != -1)
160             return 2;
161         if (psz3 && strLine.find(psz3) != -1)
162             return 3;
163         if (psz4 && strLine.find(psz4) != -1)
164             return 4;
165     }
166 }
167
168 bool Wait(int nSeconds)
169 {
170     if (fShutdown)
171         return false;
172     printf("IRC waiting %d seconds to reconnect\n", nSeconds);
173     for (int i = 0; i < nSeconds; i++)
174     {
175         if (fShutdown)
176             return false;
177         Sleep(1000);
178     }
179     return true;
180 }
181
182 bool RecvCodeLine(SOCKET hSocket, const char* psz1, string& strRet)
183 {
184     strRet.clear();
185     loop
186     {
187         string strLine;
188         if (!RecvLineIRC(hSocket, strLine))
189             return false;
190
191         vector<string> vWords;
192         ParseString(strLine, ' ', vWords);
193         if (vWords.size() < 2)
194             continue;
195
196         if (vWords[1] == psz1)
197         {
198             printf("IRC %s\n", strLine.c_str());
199             strRet = strLine;
200             return true;
201         }
202     }
203 }
204
205 bool GetIPFromIRC(SOCKET hSocket, string strMyName, unsigned int& ipRet)
206 {
207     Send(hSocket, strprintf("USERHOST %s\r", strMyName.c_str()).c_str());
208
209     string strLine;
210     if (!RecvCodeLine(hSocket, "302", strLine))
211         return false;
212
213     vector<string> vWords;
214     ParseString(strLine, ' ', vWords);
215     if (vWords.size() < 4)
216         return false;
217
218     string str = vWords[3];
219     if (str.rfind("@") == string::npos)
220         return false;
221     string strHost = str.substr(str.rfind("@")+1);
222
223     // Hybrid IRC used by lfnet always returns IP when you userhost yourself,
224     // but in case another IRC is ever used this should work.
225     printf("GetIPFromIRC() got userhost %s\n", strHost.c_str());
226     if (fUseProxy)
227         return false;
228     CAddress addr(strHost, 0, true);
229     if (!addr.IsValid())
230         return false;
231     ipRet = addr.ip;
232
233     return true;
234 }
235
236
237
238 void ThreadIRCSeed(void* parg)
239 {
240     IMPLEMENT_RANDOMIZE_STACK(ThreadIRCSeed(parg));
241     try
242     {
243         ThreadIRCSeed2(parg);
244     }
245     catch (std::exception& e) {
246         PrintExceptionContinue(&e, "ThreadIRCSeed()");
247     } catch (...) {
248         PrintExceptionContinue(NULL, "ThreadIRCSeed()");
249     }
250     printf("ThreadIRCSeed exiting\n");
251 }
252
253 void ThreadIRCSeed2(void* parg)
254 {
255     /* Dont advertise on IRC if we don't allow incoming connections */
256     if (mapArgs.count("-connect") || fNoListen)
257         return;
258
259     if (GetBoolArg("-noirc"))
260         return;
261     printf("ThreadIRCSeed started\n");
262     int nErrorWait = 10;
263     int nRetryWait = 10;
264     bool fNameInUse = false;
265     bool fTOR = (fUseProxy && addrProxy.port == htons(9050));
266
267     while (!fShutdown)
268     {
269         //CAddress addrConnect("216.155.130.130:6667"); // chat.freenode.net
270         CAddress addrConnect("92.243.23.21:6667"); // irc.lfnet.org
271         if (!fTOR)
272         {
273             //struct hostent* phostent = gethostbyname("chat.freenode.net");
274             CAddress addrIRC("irc.lfnet.org:6667", 0, true);
275             if (addrIRC.IsValid())
276                 addrConnect = addrIRC;
277         }
278
279         SOCKET hSocket;
280         if (!ConnectSocket(addrConnect, hSocket))
281         {
282             printf("IRC connect failed\n");
283             nErrorWait = nErrorWait * 11 / 10;
284             if (Wait(nErrorWait += 60))
285                 continue;
286             else
287                 return;
288         }
289
290         if (!RecvUntil(hSocket, "Found your hostname", "using your IP address instead", "Couldn't look up your hostname", "ignoring hostname"))
291         {
292             closesocket(hSocket);
293             hSocket = INVALID_SOCKET;
294             nErrorWait = nErrorWait * 11 / 10;
295             if (Wait(nErrorWait += 60))
296                 continue;
297             else
298                 return;
299         }
300
301         string strMyName;
302         if (addrLocalHost.IsRoutable() && !fUseProxy && !fNameInUse)
303             strMyName = EncodeAddress(addrLocalHost);
304         else
305             strMyName = strprintf("x%u", GetRand(1000000000));
306
307         Send(hSocket, strprintf("NICK %s\r", strMyName.c_str()).c_str());
308         Send(hSocket, strprintf("USER %s 8 * : %s\r", strMyName.c_str(), strMyName.c_str()).c_str());
309
310         int nRet = RecvUntil(hSocket, " 004 ", " 433 ");
311         if (nRet != 1)
312         {
313             closesocket(hSocket);
314             hSocket = INVALID_SOCKET;
315             if (nRet == 2)
316             {
317                 printf("IRC name already in use\n");
318                 fNameInUse = true;
319                 Wait(10);
320                 continue;
321             }
322             nErrorWait = nErrorWait * 11 / 10;
323             if (Wait(nErrorWait += 60))
324                 continue;
325             else
326                 return;
327         }
328         Sleep(500);
329
330         // Get our external IP from the IRC server and re-nick before joining the channel
331         CAddress addrFromIRC;
332         if (GetIPFromIRC(hSocket, strMyName, addrFromIRC.ip))
333         {
334             printf("GetIPFromIRC() returned %s\n", addrFromIRC.ToStringIP().c_str());
335             if (!fUseProxy && addrFromIRC.IsRoutable())
336             {
337                 // IRC lets you to re-nick
338                 fGotExternalIP = true;
339                 addrLocalHost.ip = addrFromIRC.ip;
340                 strMyName = EncodeAddress(addrLocalHost);
341                 Send(hSocket, strprintf("NICK %s\r", strMyName.c_str()).c_str());
342             }
343         }
344
345         Send(hSocket, fTestNet ? "JOIN #bitcoinTEST\r" : "JOIN #bitcoin\r");
346         Send(hSocket, fTestNet ? "WHO #bitcoinTEST\r"  : "WHO #bitcoin\r");
347
348         int64 nStart = GetTime();
349         string strLine;
350         strLine.reserve(10000);
351         while (!fShutdown && RecvLineIRC(hSocket, strLine))
352         {
353             if (strLine.empty() || strLine.size() > 900 || strLine[0] != ':')
354                 continue;
355
356             vector<string> vWords;
357             ParseString(strLine, ' ', vWords);
358             if (vWords.size() < 2)
359                 continue;
360
361             char pszName[10000];
362             pszName[0] = '\0';
363
364             if (vWords[1] == "352" && vWords.size() >= 8)
365             {
366                 // index 7 is limited to 16 characters
367                 // could get full length name at index 10, but would be different from join messages
368                 strlcpy(pszName, vWords[7].c_str(), sizeof(pszName));
369                 printf("IRC got who\n");
370             }
371
372             if (vWords[1] == "JOIN" && vWords[0].size() > 1)
373             {
374                 // :username!username@50000007.F000000B.90000002.IP JOIN :#channelname
375                 strlcpy(pszName, vWords[0].c_str() + 1, sizeof(pszName));
376                 if (strchr(pszName, '!'))
377                     *strchr(pszName, '!') = '\0';
378                 printf("IRC got join\n");
379             }
380
381             if (pszName[0] == 'u')
382             {
383                 CAddress addr;
384                 if (DecodeAddress(pszName, addr))
385                 {
386                     addr.nTime = GetAdjustedTime();
387                     if (AddAddress(addr, 51 * 60))
388                         printf("IRC got new address: %s\n", addr.ToString().c_str());
389                     nGotIRCAddresses++;
390                 }
391                 else
392                 {
393                     printf("IRC decode failed\n");
394                 }
395             }
396         }
397         closesocket(hSocket);
398         hSocket = INVALID_SOCKET;
399
400         // IRC usually blocks TOR, so only try once
401         if (fTOR)
402             return;
403
404         if (GetTime() - nStart > 20 * 60)
405         {
406             nErrorWait /= 3;
407             nRetryWait /= 3;
408         }
409
410         nRetryWait = nRetryWait * 11 / 10;
411         if (!Wait(nRetryWait += 60))
412             return;
413     }
414 }
415
416
417
418
419
420
421
422
423
424
425 #ifdef TEST
426 int main(int argc, char *argv[])
427 {
428     WSADATA wsadata;
429     if (WSAStartup(MAKEWORD(2,2), &wsadata) != NO_ERROR)
430     {
431         printf("Error at WSAStartup()\n");
432         return false;
433     }
434
435     ThreadIRCSeed(NULL);
436
437     WSACleanup();
438     return 0;
439 }
440 #endif