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