45c175bb23264bf81d9378087b27081eb7c7e282
[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 "strlcpy.h"
8 #include "base58.h"
9 #include "net.h"
10
11 using namespace std;
12 using namespace boost;
13
14 int nGotIRCAddresses = 0;
15
16 void ThreadIRCSeed2(void* parg);
17
18
19
20
21 #pragma pack(push, 1)
22 struct ircaddr
23 {
24     struct in_addr ip;
25     unsigned short port;
26 };
27 #pragma pack(pop)
28
29 string EncodeAddress(const CService& addr)
30 {
31     struct ircaddr tmp;
32     if (addr.GetInAddr(&tmp.ip))
33     {
34         tmp.port = htons(addr.GetPort());
35
36         vector<unsigned char> vch(UBEGIN(tmp), UEND(tmp));
37         return string("u") + EncodeBase58Check(vch);
38     }
39     return "";
40 }
41
42 bool DecodeAddress(string str, CService& 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 = CService(tmp.ip, ntohs(tmp.port));
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 RecvLineIRC(SOCKET hSocket, string& strLine)
79 {
80     while (true)
81     {
82         bool fRet = RecvLine(hSocket, strLine);
83         if (fRet)
84         {
85             if (fShutdown)
86                 return false;
87             vector<string> vWords;
88             ParseString(strLine, ' ', vWords);
89             if (vWords.size() >= 1 && vWords[0] == "PING")
90             {
91                 strLine[1] = 'O';
92                 strLine += '\r';
93                 Send(hSocket, strLine.c_str());
94                 continue;
95             }
96         }
97         return fRet;
98     }
99 }
100
101 int RecvUntil(SOCKET hSocket, const char* psz1, const char* psz2=NULL, const char* psz3=NULL, const char* psz4=NULL)
102 {
103     while (true)
104     {
105         string strLine;
106         strLine.reserve(10000);
107         if (!RecvLineIRC(hSocket, strLine))
108             return 0;
109         printf("IRC %s\n", strLine.c_str());
110         if (psz1 && strLine.find(psz1) != string::npos)
111             return 1;
112         if (psz2 && strLine.find(psz2) != string::npos)
113             return 2;
114         if (psz3 && strLine.find(psz3) != string::npos)
115             return 3;
116         if (psz4 && strLine.find(psz4) != string::npos)
117             return 4;
118     }
119 }
120
121 bool Wait(int nSeconds)
122 {
123     if (fShutdown)
124         return false;
125     printf("IRC waiting %d seconds to reconnect\n", nSeconds);
126     for (int i = 0; i < nSeconds; i++)
127     {
128         if (fShutdown)
129             return false;
130         Sleep(1000);
131     }
132     return true;
133 }
134
135 bool RecvCodeLine(SOCKET hSocket, const char* psz1, string& strRet)
136 {
137     strRet.clear();
138     while (true)
139     {
140         string strLine;
141         if (!RecvLineIRC(hSocket, strLine))
142             return false;
143
144         vector<string> vWords;
145         ParseString(strLine, ' ', vWords);
146         if (vWords.size() < 2)
147             continue;
148
149         if (vWords[1] == psz1)
150         {
151             printf("IRC %s\n", strLine.c_str());
152             strRet = strLine;
153             return true;
154         }
155     }
156 }
157
158 bool GetIPFromIRC(SOCKET hSocket, string strMyName, CNetAddr& ipRet)
159 {
160     Send(hSocket, strprintf("USERHOST %s\r", strMyName.c_str()).c_str());
161
162     string strLine;
163     if (!RecvCodeLine(hSocket, "302", strLine))
164         return false;
165
166     vector<string> vWords;
167     ParseString(strLine, ' ', vWords);
168     if (vWords.size() < 4)
169         return false;
170
171     string str = vWords[3];
172     if (str.rfind("@") == string::npos)
173         return false;
174     string strHost = str.substr(str.rfind("@")+1);
175
176     // Hybrid IRC used by lfnet always returns IP when you userhost yourself,
177     // but in case another IRC is ever used this should work.
178     printf("GetIPFromIRC() got userhost %s\n", strHost.c_str());
179     CNetAddr addr(strHost, true);
180     if (!addr.IsValid())
181         return false;
182     ipRet = addr;
183
184     return true;
185 }
186
187
188
189 void ThreadIRCSeed(void* parg)
190 {
191     // Make this thread recognisable as the IRC seeding thread
192     RenameThread("novacoin-ircseed");
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 started\n");
221     int nErrorWait = 10;
222     int nRetryWait = 10;
223     int nNameRetry = 0;
224
225     while (!fShutdown)
226     {
227         CService addrConnect("92.243.23.21", 6667); // irc.lfnet.org
228
229         CService addrIRC("irc.lfnet.org", 6667, true);
230         if (addrIRC.IsValid())
231             addrConnect = addrIRC;
232
233         SOCKET hSocket;
234         if (!ConnectSocket(addrConnect, hSocket))
235         {
236             printf("IRC connect failed\n");
237             nErrorWait = nErrorWait * 11 / 10;
238             if (Wait(nErrorWait += 60))
239                 continue;
240             else
241                 return;
242         }
243
244         if (!RecvUntil(hSocket, "Found your hostname", "using your IP address instead", "Couldn't look up your hostname", "ignoring hostname"))
245         {
246             closesocket(hSocket);
247             hSocket = INVALID_SOCKET;
248             nErrorWait = nErrorWait * 11 / 10;
249             if (Wait(nErrorWait += 60))
250                 continue;
251             else
252                 return;
253         }
254
255         CNetAddr addrIPv4("1.2.3.4"); // arbitrary IPv4 address to make GetLocal prefer IPv4 addresses
256         CService addrLocal;
257         string strMyName;
258         // Don't use our IP as our nick if we're not listening
259         // or if it keeps failing because the nick is already in use.
260         if (!fNoListen && GetLocal(addrLocal, &addrIPv4) && nNameRetry<3)
261             strMyName = EncodeAddress(GetLocalAddress(&addrConnect));
262         if (strMyName == "")
263             strMyName = strprintf("x%" PRIu64 "", GetRand(1000000000));
264
265         Send(hSocket, strprintf("NICK %s\r", strMyName.c_str()).c_str());
266         Send(hSocket, strprintf("USER %s 8 * : %s\r", strMyName.c_str(), strMyName.c_str()).c_str());
267
268         int nRet = RecvUntil(hSocket, " 004 ", " 433 ");
269         if (nRet != 1)
270         {
271             closesocket(hSocket);
272             hSocket = INVALID_SOCKET;
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             char pszName[10000];
331             pszName[0] = '\0';
332
333             if (vWords[1] == "352" && vWords.size() >= 8)
334             {
335                 // index 7 is limited to 16 characters
336                 // could get full length name at index 10, but would be different from join messages
337                 strlcpy(pszName, vWords[7].c_str(), sizeof(pszName));
338                 printf("IRC got who\n");
339             }
340
341             if (vWords[1] == "JOIN" && vWords[0].size() > 1)
342             {
343                 // :username!username@50000007.F000000B.90000002.IP JOIN :#channelname
344                 strlcpy(pszName, vWords[0].c_str() + 1, sizeof(pszName));
345                 if (strchr(pszName, '!'))
346                     *strchr(pszName, '!') = '\0';
347                 printf("IRC got join\n");
348             }
349
350             if (pszName[0] == 'u')
351             {
352                 CAddress addr;
353                 if (DecodeAddress(pszName, addr))
354                 {
355                     addr.nTime = GetAdjustedTime();
356                     if (addrman.Add(addr, addrConnect, 51 * 60))
357                         printf("IRC got new address: %s\n", addr.ToString().c_str());
358                     nGotIRCAddresses++;
359                 }
360                 else
361                 {
362                     printf("IRC decode failed\n");
363                 }
364             }
365         }
366         closesocket(hSocket);
367         hSocket = INVALID_SOCKET;
368
369         if (GetTime() - nStart > 20 * 60)
370         {
371             nErrorWait /= 3;
372             nRetryWait /= 3;
373         }
374
375         nRetryWait = nRetryWait * 11 / 10;
376         if (!Wait(nRetryWait += 60))
377             return;
378     }
379 }
380
381
382
383
384
385
386
387
388
389
390 #ifdef TEST
391 int main(int argc, char *argv[])
392 {
393     WSADATA wsadata;
394     if (WSAStartup(MAKEWORD(2,2), &wsadata) != NO_ERROR)
395     {
396         printf("Error at WSAStartup()\n");
397         return false;
398     }
399
400     ThreadIRCSeed(NULL);
401
402     WSACleanup();
403     return 0;
404 }
405 #endif