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