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