aboutsummaryrefslogtreecommitdiff
path: root/src/irc.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/irc.cpp')
-rw-r--r--src/irc.cpp445
1 files changed, 445 insertions, 0 deletions
diff --git a/src/irc.cpp b/src/irc.cpp
new file mode 100644
index 0000000000..5adaf11658
--- /dev/null
+++ b/src/irc.cpp
@@ -0,0 +1,445 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Distributed under the MIT/X11 software license, see the accompanying
+// file license.txt or http://www.opensource.org/licenses/mit-license.php.
+
+#include "headers.h"
+
+int nGotIRCAddresses = 0;
+bool fGotExternalIP = false;
+
+void ThreadIRCSeed2(void* parg);
+
+
+
+
+#pragma pack(push, 1)
+struct ircaddr
+{
+ int ip;
+ short port;
+};
+#pragma pack(pop)
+
+string EncodeAddress(const CAddress& addr)
+{
+ struct ircaddr tmp;
+ tmp.ip = addr.ip;
+ tmp.port = addr.port;
+
+ vector<unsigned char> vch(UBEGIN(tmp), UEND(tmp));
+ return string("u") + EncodeBase58Check(vch);
+}
+
+bool DecodeAddress(string str, CAddress& addr)
+{
+ vector<unsigned char> vch;
+ if (!DecodeBase58Check(str.substr(1), vch))
+ return false;
+
+ struct ircaddr tmp;
+ if (vch.size() != sizeof(tmp))
+ return false;
+ memcpy(&tmp, &vch[0], sizeof(tmp));
+
+ addr = CAddress(tmp.ip, tmp.port, NODE_NETWORK);
+ return true;
+}
+
+
+
+
+
+
+static bool Send(SOCKET hSocket, const char* pszSend)
+{
+ if (strstr(pszSend, "PONG") != pszSend)
+ printf("IRC SENDING: %s\n", pszSend);
+ const char* psz = pszSend;
+ const char* pszEnd = psz + strlen(psz);
+ while (psz < pszEnd)
+ {
+ int ret = send(hSocket, psz, pszEnd - psz, MSG_NOSIGNAL);
+ if (ret < 0)
+ return false;
+ psz += ret;
+ }
+ return true;
+}
+
+bool RecvLine(SOCKET hSocket, string& strLine)
+{
+ strLine = "";
+ loop
+ {
+ char c;
+ int nBytes = recv(hSocket, &c, 1, 0);
+ if (nBytes > 0)
+ {
+ if (c == '\n')
+ continue;
+ if (c == '\r')
+ return true;
+ strLine += c;
+ if (strLine.size() >= 9000)
+ return true;
+ }
+ else if (nBytes <= 0)
+ {
+ if (fShutdown)
+ return false;
+ if (nBytes < 0)
+ {
+ int nErr = WSAGetLastError();
+ if (nErr == WSAEMSGSIZE)
+ continue;
+ if (nErr == WSAEWOULDBLOCK || nErr == WSAEINTR || nErr == WSAEINPROGRESS)
+ {
+ Sleep(10);
+ continue;
+ }
+ }
+ if (!strLine.empty())
+ return true;
+ if (nBytes == 0)
+ {
+ // socket closed
+ printf("IRC socket closed\n");
+ return false;
+ }
+ else
+ {
+ // socket error
+ int nErr = WSAGetLastError();
+ printf("IRC recv failed: %d\n", nErr);
+ return false;
+ }
+ }
+ }
+}
+
+bool RecvLineIRC(SOCKET hSocket, string& strLine)
+{
+ loop
+ {
+ bool fRet = RecvLine(hSocket, strLine);
+ if (fRet)
+ {
+ if (fShutdown)
+ return false;
+ vector<string> vWords;
+ ParseString(strLine, ' ', vWords);
+ if (vWords.size() >= 1 && vWords[0] == "PING")
+ {
+ strLine[1] = 'O';
+ strLine += '\r';
+ Send(hSocket, strLine.c_str());
+ continue;
+ }
+ }
+ return fRet;
+ }
+}
+
+int RecvUntil(SOCKET hSocket, const char* psz1, const char* psz2=NULL, const char* psz3=NULL, const char* psz4=NULL)
+{
+ loop
+ {
+ string strLine;
+ strLine.reserve(10000);
+ if (!RecvLineIRC(hSocket, strLine))
+ return 0;
+ printf("IRC %s\n", strLine.c_str());
+ if (psz1 && strLine.find(psz1) != -1)
+ return 1;
+ if (psz2 && strLine.find(psz2) != -1)
+ return 2;
+ if (psz3 && strLine.find(psz3) != -1)
+ return 3;
+ if (psz4 && strLine.find(psz4) != -1)
+ return 4;
+ }
+}
+
+bool Wait(int nSeconds)
+{
+ if (fShutdown)
+ return false;
+ printf("IRC waiting %d seconds to reconnect\n", nSeconds);
+ for (int i = 0; i < nSeconds; i++)
+ {
+ if (fShutdown)
+ return false;
+ Sleep(1000);
+ }
+ return true;
+}
+
+bool RecvCodeLine(SOCKET hSocket, const char* psz1, string& strRet)
+{
+ strRet.clear();
+ loop
+ {
+ string strLine;
+ if (!RecvLineIRC(hSocket, strLine))
+ return false;
+
+ vector<string> vWords;
+ ParseString(strLine, ' ', vWords);
+ if (vWords.size() < 2)
+ continue;
+
+ if (vWords[1] == psz1)
+ {
+ printf("IRC %s\n", strLine.c_str());
+ strRet = strLine;
+ return true;
+ }
+ }
+}
+
+bool GetIPFromIRC(SOCKET hSocket, string strMyName, unsigned int& ipRet)
+{
+ Send(hSocket, strprintf("USERHOST %s\r", strMyName.c_str()).c_str());
+
+ string strLine;
+ if (!RecvCodeLine(hSocket, "302", strLine))
+ return false;
+
+ vector<string> vWords;
+ ParseString(strLine, ' ', vWords);
+ if (vWords.size() < 4)
+ return false;
+
+ string str = vWords[3];
+ if (str.rfind("@") == string::npos)
+ return false;
+ string strHost = str.substr(str.rfind("@")+1);
+
+ unsigned int a=0, b=0, c=0, d=0;
+ if (sscanf(strHost.c_str(), "%u.%u.%u.%u", &a, &b, &c, &d) == 4 &&
+ inet_addr(strHost.c_str()) != INADDR_NONE)
+ {
+ printf("GetIPFromIRC() userhost is IP %s\n", strHost.c_str());
+ ipRet = CAddress(strHost).ip;
+ }
+ else
+ {
+ // Hybrid IRC used by lfnet always returns IP when you userhost yourself,
+ // but in case another IRC is ever used this should work.
+ printf("GetIPFromIRC() got userhost %s\n", strHost.c_str());
+ if (fUseProxy)
+ return false;
+ struct hostent* phostent = gethostbyname(strHost.c_str());
+ if (!phostent || !phostent->h_addr_list || !phostent->h_addr_list[0])
+ return false;
+ ipRet = *(u_long*)phostent->h_addr_list[0];
+ }
+
+ return true;
+}
+
+
+
+void ThreadIRCSeed(void* parg)
+{
+ IMPLEMENT_RANDOMIZE_STACK(ThreadIRCSeed(parg));
+ try
+ {
+ ThreadIRCSeed2(parg);
+ }
+ catch (std::exception& e) {
+ PrintExceptionContinue(&e, "ThreadIRCSeed()");
+ } catch (...) {
+ PrintExceptionContinue(NULL, "ThreadIRCSeed()");
+ }
+ printf("ThreadIRCSeed exiting\n");
+}
+
+void ThreadIRCSeed2(void* parg)
+{
+ /* Dont advertise on IRC if we don't allow incoming connections */
+ if (mapArgs.count("-connect") || fNoListen)
+ return;
+
+ if (GetBoolArg("-noirc"))
+ return;
+ printf("ThreadIRCSeed started\n");
+ int nErrorWait = 10;
+ int nRetryWait = 10;
+ bool fNameInUse = false;
+ bool fTOR = (fUseProxy && addrProxy.port == htons(9050));
+
+ while (!fShutdown)
+ {
+ //CAddress addrConnect("216.155.130.130:6667"); // chat.freenode.net
+ CAddress addrConnect("92.243.23.21:6667"); // irc.lfnet.org
+ if (!fTOR)
+ {
+ //struct hostent* phostent = gethostbyname("chat.freenode.net");
+ struct hostent* phostent = gethostbyname("irc.lfnet.org");
+ if (phostent && phostent->h_addr_list && phostent->h_addr_list[0])
+ addrConnect = CAddress(*(u_long*)phostent->h_addr_list[0], htons(6667));
+ }
+
+ SOCKET hSocket;
+ if (!ConnectSocket(addrConnect, hSocket))
+ {
+ printf("IRC connect failed\n");
+ nErrorWait = nErrorWait * 11 / 10;
+ if (Wait(nErrorWait += 60))
+ continue;
+ else
+ return;
+ }
+
+ if (!RecvUntil(hSocket, "Found your hostname", "using your IP address instead", "Couldn't look up your hostname", "ignoring hostname"))
+ {
+ closesocket(hSocket);
+ hSocket = INVALID_SOCKET;
+ nErrorWait = nErrorWait * 11 / 10;
+ if (Wait(nErrorWait += 60))
+ continue;
+ else
+ return;
+ }
+
+ string strMyName;
+ if (addrLocalHost.IsRoutable() && !fUseProxy && !fNameInUse)
+ strMyName = EncodeAddress(addrLocalHost);
+ else
+ strMyName = strprintf("x%u", GetRand(1000000000));
+
+ Send(hSocket, strprintf("NICK %s\r", strMyName.c_str()).c_str());
+ Send(hSocket, strprintf("USER %s 8 * : %s\r", strMyName.c_str(), strMyName.c_str()).c_str());
+
+ int nRet = RecvUntil(hSocket, " 004 ", " 433 ");
+ if (nRet != 1)
+ {
+ closesocket(hSocket);
+ hSocket = INVALID_SOCKET;
+ if (nRet == 2)
+ {
+ printf("IRC name already in use\n");
+ fNameInUse = true;
+ Wait(10);
+ continue;
+ }
+ nErrorWait = nErrorWait * 11 / 10;
+ if (Wait(nErrorWait += 60))
+ continue;
+ else
+ return;
+ }
+ Sleep(500);
+
+ // Get our external IP from the IRC server and re-nick before joining the channel
+ CAddress addrFromIRC;
+ if (GetIPFromIRC(hSocket, strMyName, addrFromIRC.ip))
+ {
+ printf("GetIPFromIRC() returned %s\n", addrFromIRC.ToStringIP().c_str());
+ if (!fUseProxy && addrFromIRC.IsRoutable())
+ {
+ // IRC lets you to re-nick
+ fGotExternalIP = true;
+ addrLocalHost.ip = addrFromIRC.ip;
+ strMyName = EncodeAddress(addrLocalHost);
+ Send(hSocket, strprintf("NICK %s\r", strMyName.c_str()).c_str());
+ }
+ }
+
+ Send(hSocket, fTestNet ? "JOIN #bitcoinTEST\r" : "JOIN #bitcoin\r");
+ Send(hSocket, fTestNet ? "WHO #bitcoinTEST\r" : "WHO #bitcoin\r");
+
+ int64 nStart = GetTime();
+ string strLine;
+ strLine.reserve(10000);
+ while (!fShutdown && RecvLineIRC(hSocket, strLine))
+ {
+ if (strLine.empty() || strLine.size() > 900 || strLine[0] != ':')
+ continue;
+
+ vector<string> vWords;
+ ParseString(strLine, ' ', vWords);
+ if (vWords.size() < 2)
+ continue;
+
+ char pszName[10000];
+ pszName[0] = '\0';
+
+ if (vWords[1] == "352" && vWords.size() >= 8)
+ {
+ // index 7 is limited to 16 characters
+ // could get full length name at index 10, but would be different from join messages
+ strlcpy(pszName, vWords[7].c_str(), sizeof(pszName));
+ printf("IRC got who\n");
+ }
+
+ if (vWords[1] == "JOIN" && vWords[0].size() > 1)
+ {
+ // :username!username@50000007.F000000B.90000002.IP JOIN :#channelname
+ strlcpy(pszName, vWords[0].c_str() + 1, sizeof(pszName));
+ if (strchr(pszName, '!'))
+ *strchr(pszName, '!') = '\0';
+ printf("IRC got join\n");
+ }
+
+ if (pszName[0] == 'u')
+ {
+ CAddress addr;
+ if (DecodeAddress(pszName, addr))
+ {
+ addr.nTime = GetAdjustedTime();
+ if (AddAddress(addr, 51 * 60))
+ printf("IRC got new address\n");
+ nGotIRCAddresses++;
+ }
+ else
+ {
+ printf("IRC decode failed\n");
+ }
+ }
+ }
+ closesocket(hSocket);
+ hSocket = INVALID_SOCKET;
+
+ // IRC usually blocks TOR, so only try once
+ if (fTOR)
+ return;
+
+ if (GetTime() - nStart > 20 * 60)
+ {
+ nErrorWait /= 3;
+ nRetryWait /= 3;
+ }
+
+ nRetryWait = nRetryWait * 11 / 10;
+ if (!Wait(nRetryWait += 60))
+ return;
+ }
+}
+
+
+
+
+
+
+
+
+
+
+#ifdef TEST
+int main(int argc, char *argv[])
+{
+ WSADATA wsadata;
+ if (WSAStartup(MAKEWORD(2,2), &wsadata) != NO_ERROR)
+ {
+ printf("Error at WSAStartup()\n");
+ return false;
+ }
+
+ ThreadIRCSeed(NULL);
+
+ WSACleanup();
+ return 0;
+}
+#endif