Merge pull request #295 from svost/patch
[novacoin.git] / src / irc.cpp
index fe96a90..a29874e 100644 (file)
@@ -1,18 +1,15 @@
 // Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2011 The Bitcoin developers
+// Copyright (c) 2009-2012 The Bitcoin developers
 // Distributed under the MIT/X11 software license, see the accompanying
-// file license.txt or http://www.opensource.org/licenses/mit-license.php.
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
 
-#include "headers.h"
 #include "irc.h"
+#include "base58.h"
 #include "net.h"
-#include "strlcpy.h"
 
 using namespace std;
-using namespace boost;
 
 int nGotIRCAddresses = 0;
-bool fGotExternalIP = false;
 
 void ThreadIRCSeed2(void* parg);
 
@@ -22,22 +19,25 @@ void ThreadIRCSeed2(void* parg);
 #pragma pack(push, 1)
 struct ircaddr
 {
-    int ip;
-    short port;
+    struct in_addr ip;
+    unsigned short port;
 };
 #pragma pack(pop)
 
-string EncodeAddress(const CAddress& addr)
+string EncodeAddress(const CService& addr)
 {
     struct ircaddr tmp;
-    tmp.ip    = addr.ip;
-    tmp.port  = addr.port;
+    if (addr.GetInAddr(&tmp.ip))
+    {
+        tmp.port = htons(addr.GetPort());
 
-    vector<unsigned char> vch(UBEGIN(tmp), UEND(tmp));
-    return string("u") + EncodeBase58Check(vch);
+        vector<unsigned char> vch(UBEGIN(tmp), UEND(tmp));
+        return string("u") + EncodeBase58Check(vch);
+    }
+    return "";
 }
 
-bool DecodeAddress(string str, CAddress& addr)
+bool DecodeAddress(string str, CService& addr)
 {
     vector<unsigned char> vch;
     if (!DecodeBase58Check(str.substr(1), vch))
@@ -48,7 +48,7 @@ bool DecodeAddress(string str, CAddress& addr)
         return false;
     memcpy(&tmp, &vch[0], sizeof(tmp));
 
-    addr = CAddress(tmp.ip, ntohs(tmp.port), NODE_NETWORK);
+    addr = CService(tmp.ip, ntohs(tmp.port));
     return true;
 }
 
@@ -73,60 +73,9 @@ static bool Send(SOCKET hSocket, const char* pszSend)
     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
+    for ( ; ; )
     {
         bool fRet = RecvLine(hSocket, strLine);
         if (fRet)
@@ -149,20 +98,20 @@ bool RecvLineIRC(SOCKET hSocket, string& strLine)
 
 int RecvUntil(SOCKET hSocket, const char* psz1, const char* psz2=NULL, const char* psz3=NULL, const char* psz4=NULL)
 {
-    loop
+    for ( ; ; )
     {
         string strLine;
         strLine.reserve(10000);
         if (!RecvLineIRC(hSocket, strLine))
             return 0;
         printf("IRC %s\n", strLine.c_str());
-        if (psz1 && strLine.find(psz1) != -1)
+        if (psz1 && strLine.find(psz1) != string::npos)
             return 1;
-        if (psz2 && strLine.find(psz2) != -1)
+        if (psz2 && strLine.find(psz2) != string::npos)
             return 2;
-        if (psz3 && strLine.find(psz3) != -1)
+        if (psz3 && strLine.find(psz3) != string::npos)
             return 3;
-        if (psz4 && strLine.find(psz4) != -1)
+        if (psz4 && strLine.find(psz4) != string::npos)
             return 4;
     }
 }
@@ -184,7 +133,7 @@ bool Wait(int nSeconds)
 bool RecvCodeLine(SOCKET hSocket, const char* psz1, string& strRet)
 {
     strRet.clear();
-    loop
+    for ( ; ; )
     {
         string strLine;
         if (!RecvLineIRC(hSocket, strLine))
@@ -204,7 +153,7 @@ bool RecvCodeLine(SOCKET hSocket, const char* psz1, string& strRet)
     }
 }
 
-bool GetIPFromIRC(SOCKET hSocket, string strMyName, unsigned int& ipRet)
+bool GetIPFromIRC(SOCKET hSocket, string strMyName, CNetAddr& ipRet)
 {
     Send(hSocket, strprintf("USERHOST %s\r", strMyName.c_str()).c_str());
 
@@ -225,12 +174,10 @@ bool GetIPFromIRC(SOCKET hSocket, string strMyName, unsigned int& ipRet)
     // 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;
-    CAddress addr(strHost, 0, true);
+    CNetAddr addr(strHost, true);
     if (!addr.IsValid())
         return false;
-    ipRet = addr.ip;
+    ipRet = addr;
 
     return true;
 }
@@ -239,7 +186,11 @@ bool GetIPFromIRC(SOCKET hSocket, string strMyName, unsigned int& ipRet)
 
 void ThreadIRCSeed(void* parg)
 {
-    IMPLEMENT_RANDOMIZE_STACK(ThreadIRCSeed(parg));
+    // Make this thread recognisable as the IRC seeding thread
+    RenameThread("novacoin-ircseed");
+
+    printf("ThreadIRCSeed started\n");
+
     try
     {
         ThreadIRCSeed2(parg);
@@ -249,27 +200,35 @@ void ThreadIRCSeed(void* parg)
     } catch (...) {
         PrintExceptionContinue(NULL, "ThreadIRCSeed()");
     }
-    printf("ThreadIRCSeed exiting\n");
+    printf("ThreadIRCSeed exited\n");
 }
 
 void ThreadIRCSeed2(void* parg)
 {
-    /* Dont advertise on IRC if we don't allow incoming connections */
-    if (mapArgs.count("-connect") || fNoListen)
+    // Don't connect to IRC if we won't use IPv4 connections.
+    if (IsLimited(NET_IPV4))
         return;
 
-    if (GetBoolArg("-noirc"))
+    // ... or if we won't make outbound connections and won't accept inbound ones.
+    if (mapArgs.count("-connect") && fNoListen)
         return;
-    printf("ThreadIRCSeed started\n");
+
+    // ... or if IRC is not enabled.
+    if (!GetBoolArg("-irc", true))
+        return;
+
+    printf("ThreadIRCSeed trying to connect...\n");
+
     int nErrorWait = 10;
     int nRetryWait = 10;
-    bool fNameInUse = false;
+    int nNameRetry = 0;
 
     while (!fShutdown)
     {
-        CAddress addrConnect("92.243.23.21", 6667); // irc.lfnet.org
+        const uint16_t nIrcPort = 6667;
+        CService addrConnect("92.243.23.21", nIrcPort); // irc.lfnet.org
 
-        CAddress addrIRC("irc.lfnet.org", 6667, true);
+        CService addrIRC("irc.lfnet.org", nIrcPort, true);
         if (addrIRC.IsValid())
             addrConnect = addrIRC;
 
@@ -286,8 +245,7 @@ void ThreadIRCSeed2(void* parg)
 
         if (!RecvUntil(hSocket, "Found your hostname", "using your IP address instead", "Couldn't look up your hostname", "ignoring hostname"))
         {
-            closesocket(hSocket);
-            hSocket = INVALID_SOCKET;
+            CloseSocket(hSocket);
             nErrorWait = nErrorWait * 11 / 10;
             if (Wait(nErrorWait += 60))
                 continue;
@@ -295,11 +253,15 @@ void ThreadIRCSeed2(void* parg)
                 return;
         }
 
+        CNetAddr addrIPv4("1.2.3.4"); // arbitrary IPv4 address to make GetLocal prefer IPv4 addresses
+        CService addrLocal;
         string strMyName;
-        if (addrLocalHost.IsRoutable() && !fUseProxy && !fNameInUse)
-            strMyName = EncodeAddress(addrLocalHost);
-        else
-            strMyName = strprintf("x%u", GetRand(1000000000));
+        // Don't use our IP as our nick if we're not listening
+        // or if it keeps failing because the nick is already in use.
+        if (!fNoListen && GetLocal(addrLocal, &addrIPv4) && nNameRetry<3)
+            strMyName = EncodeAddress(GetLocalAddress(&addrConnect));
+        if (strMyName.empty())
+            strMyName = strprintf("x%" PRIu64 "", 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());
@@ -307,12 +269,11 @@ void ThreadIRCSeed2(void* parg)
         int nRet = RecvUntil(hSocket, " 004 ", " 433 ");
         if (nRet != 1)
         {
-            closesocket(hSocket);
-            hSocket = INVALID_SOCKET;
+            CloseSocket(hSocket);
             if (nRet == 2)
             {
                 printf("IRC name already in use\n");
-                fNameInUse = true;
+                nNameRetry++;
                 Wait(10);
                 continue;
             }
@@ -322,34 +283,38 @@ void ThreadIRCSeed2(void* parg)
             else
                 return;
         }
+        nNameRetry = 0;
         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))
+        CNetAddr addrFromIRC;
+        if (GetIPFromIRC(hSocket, strMyName, addrFromIRC))
         {
-            printf("GetIPFromIRC() returned %s\n", addrFromIRC.ToStringIP().c_str());
-            if (!fUseProxy && addrFromIRC.IsRoutable())
+            printf("GetIPFromIRC() returned %s\n", addrFromIRC.ToString().c_str());
+            // Don't use our IP as our nick if we're not listening
+            if (!fNoListen && addrFromIRC.IsRoutable())
             {
                 // IRC lets you to re-nick
-                fGotExternalIP = true;
-                addrLocalHost.ip = addrFromIRC.ip;
-                strMyName = EncodeAddress(addrLocalHost);
+                AddLocal(addrFromIRC, LOCAL_IRC);
+                strMyName = EncodeAddress(GetLocalAddress(&addrConnect));
                 Send(hSocket, strprintf("NICK %s\r", strMyName.c_str()).c_str());
             }
         }
-        
+
         if (fTestNet) {
-            Send(hSocket, "JOIN #bitcoinTEST\r");
-            Send(hSocket, "WHO #bitcoinTEST\r");
+            Send(hSocket, "JOIN #novacoinTEST2\r");
+            Send(hSocket, "WHO #novacoinTEST2\r");
         } else {
-            // randomly join #bitcoin00-#bitcoin99
-            int channel_number = GetRandInt(100);
-            Send(hSocket, strprintf("JOIN #bitcoin%02d\r", channel_number).c_str());
-            Send(hSocket, strprintf("WHO #bitcoin%02d\r", channel_number).c_str());
+            // randomly join #novacoin00-#novacoin05
+            // int channel_number = GetRandInt(5);
+
+            // Channel number is always 0 for initial release
+            int channel_number = 0;
+            Send(hSocket, strprintf("JOIN #novacoin%02d\r", channel_number).c_str());
+            Send(hSocket, strprintf("WHO #novacoin%02d\r", channel_number).c_str());
         }
 
-        int64 nStart = GetTime();
+        int64_t nStart = GetTime();
         string strLine;
         strLine.reserve(10000);
         while (!fShutdown && RecvLineIRC(hSocket, strLine))
@@ -362,33 +327,30 @@ void ThreadIRCSeed2(void* parg)
             if (vWords.size() < 2)
                 continue;
 
-            char pszName[10000];
-            pszName[0] = '\0';
+            std::string strName;
 
             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));
+                strName = vWords[7];
                 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';
+                strName = vWords[0].substr(1, vWords[0].find('!', 1) - 1);
                 printf("IRC got join\n");
             }
 
-            if (pszName[0] == 'u')
+            if (strName.compare(0,1, "u") == 0)
             {
                 CAddress addr;
-                if (DecodeAddress(pszName, addr))
+                if (DecodeAddress(strName, addr))
                 {
                     addr.nTime = GetAdjustedTime();
-                    if (AddAddress(addr, 51 * 60))
+                    if (addrman.Add(addr, addrConnect, 51 * 60))
                         printf("IRC got new address: %s\n", addr.ToString().c_str());
                     nGotIRCAddresses++;
                 }
@@ -398,8 +360,7 @@ void ThreadIRCSeed2(void* parg)
                 }
             }
         }
-        closesocket(hSocket);
-        hSocket = INVALID_SOCKET;
+        CloseSocket(hSocket);
 
         if (GetTime() - nStart > 20 * 60)
         {