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