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