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