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