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