You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

irc.cpp 10KB

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