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