changeset 2796:aeb039c3cf49 draft

Merge pull request #1399 from sipa/ipparse Improve parsing of IPv6 addresses
author Gavin Andresen <gavinandresen@gmail.com>
date Mon, 18 Jun 2012 11:39:16 -0700
parents 8ed4bc27e882 (current diff) 11c3829233e7 (diff)
children 2ce3b09cb9d8
files src/netbase.cpp src/netbase.h
diffstat 3 files changed, 119 insertions(+), 43 deletions(-) [+]
line wrap: on
line diff
--- a/src/netbase.cpp
+++ b/src/netbase.cpp
@@ -33,6 +33,27 @@
     return NET_UNROUTABLE;
 }
 
+void SplitHostPort(std::string in, int &portOut, std::string &hostOut) {
+    size_t colon = in.find_last_of(':');
+    // if a : is found, and it either follows a [...], or no other : is in the string, treat it as port separator
+    bool fHaveColon = colon != in.npos;
+    bool fBracketed = fHaveColon && (in[0]=='[' && in[colon-1]==']'); // if there is a colon, and in[0]=='[', colon is not 0, so in[colon-1] is safe
+    bool fMultiColon = fHaveColon && (in.find_last_of(':',colon-1) != in.npos);
+    if (fHaveColon && (colon==0 || fBracketed || !fMultiColon)) {
+        char *endp = NULL;
+        int n = strtol(in.c_str() + colon + 1, &endp, 10);
+        if (endp && *endp == 0 && n >= 0) {
+            in = in.substr(0, colon);
+            if (n > 0 && n < 0x10000)
+                portOut = n;
+        }
+    }
+    if (in.size()>0 && in[0] == '[' && in[in.size()-1] == ']')
+        hostOut = in.substr(1, in.size()-2);
+    else
+        hostOut = in;
+}
+
 bool static LookupIntern(const char *pszName, std::vector<CNetAddr>& vIP, unsigned int nMaxSolutions, bool fAllowLookup)
 {
     vIP.clear();
@@ -114,36 +135,11 @@
     if (pszName[0] == 0)
         return false;
     int port = portDefault;
-    char psz[256];
-    char *pszHost = psz;
-    strlcpy(psz, pszName, sizeof(psz));
-    char* pszColon = strrchr(psz+1,':');
-    char *pszPortEnd = NULL;
-    int portParsed = pszColon ? strtoul(pszColon+1, &pszPortEnd, 10) : 0;
-    if (pszColon && pszPortEnd && pszPortEnd[0] == 0)
-    {
-        if (psz[0] == '[' && pszColon[-1] == ']')
-        {
-            pszHost = psz+1;
-            pszColon[-1] = 0;
-        }
-        else
-            pszColon[0] = 0;
-        if (port >= 0 && port <= USHRT_MAX)
-            port = portParsed;
-    }
-    else
-    {
-        if (psz[0] == '[' && psz[strlen(psz)-1] == ']')
-        {
-            pszHost = psz+1;
-            psz[strlen(psz)-1] = 0;
-        }
-
-    }
+    std::string hostname = "";
+    SplitHostPort(std::string(pszName), port, hostname);
 
     std::vector<CNetAddr> vIP;
-    bool fRet = LookupIntern(pszHost, vIP, nMaxSolutions, fAllowLookup);
+    bool fRet = LookupIntern(hostname.c_str(), vIP, nMaxSolutions, fAllowLookup);
     if (!fRet)
         return false;
     vAddr.resize(vIP.size());
@@ -496,22 +492,9 @@
 
 bool ConnectSocketByName(CService &addr, SOCKET& hSocketRet, const char *pszDest, int portDefault, int nTimeout)
 {
-    string strDest(pszDest);
+    string strDest;
     int port = portDefault;
-
-    // split hostname and port
-    size_t colon = strDest.find_last_of(':');
-    if (colon != strDest.npos) {
-        char *endp = NULL;
-        int n = strtol(pszDest + colon + 1, &endp, 10);
-        if (endp && *endp == 0 && n >= 0) {
-            strDest = strDest.substr(0, colon);
-            if (n > 0 && n < 0x10000)
-                port = n;
-        }
-    }
-    if (strDest[0] == '[' && strDest[strDest.size()-1] == ']')
-        strDest = strDest.substr(1, strDest.size()-2);
+    SplitHostPort(string(pszDest), port, strDest);
 
     SOCKET hSocket = INVALID_SOCKET;
     CService addrResolved(CNetAddr(strDest, fNameLookup && !nameproxyInfo.second), port);
--- a/src/netbase.h
+++ b/src/netbase.h
@@ -133,6 +133,7 @@
 };
 
 enum Network ParseNetwork(std::string net);
+void SplitHostPort(std::string in, int &portOut, std::string &hostOut);
 bool SetProxy(enum Network net, CService addrProxy, int nSocksVersion = 5);
 bool GetProxy(enum Network net, CService &addrProxy);
 bool IsProxy(const CNetAddr &addr);
new file mode 100644
--- /dev/null
+++ b/src/test/netbase_tests.cpp
@@ -0,0 +1,92 @@
+#include <boost/test/unit_test.hpp>
+
+#include <string>
+#include <vector>
+
+#include "netbase.h"
+
+using namespace std;
+
+BOOST_AUTO_TEST_SUITE(netbase_tests)
+
+BOOST_AUTO_TEST_CASE(netbase_networks)
+{
+    BOOST_CHECK(CNetAddr("127.0.0.1").GetNetwork()                              == NET_UNROUTABLE);
+    BOOST_CHECK(CNetAddr("::1").GetNetwork()                                    == NET_UNROUTABLE);
+    BOOST_CHECK(CNetAddr("8.8.8.8").GetNetwork()                                == NET_IPV4);
+    BOOST_CHECK(CNetAddr("2001::8888").GetNetwork()                             == NET_IPV6);
+    BOOST_CHECK(CNetAddr("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca").GetNetwork() == NET_TOR);
+}
+
+BOOST_AUTO_TEST_CASE(netbase_properties)
+{
+    BOOST_CHECK(CNetAddr("127.0.0.1").IsIPv4());
+    BOOST_CHECK(CNetAddr("::FFFF:192.168.1.1").IsIPv4());
+    BOOST_CHECK(CNetAddr("::1").IsIPv6());
+    BOOST_CHECK(CNetAddr("10.0.0.1").IsRFC1918());
+    BOOST_CHECK(CNetAddr("192.168.1.1").IsRFC1918());
+    BOOST_CHECK(CNetAddr("172.31.255.255").IsRFC1918());
+    BOOST_CHECK(CNetAddr("2001:0DB8::").IsRFC3849());
+    BOOST_CHECK(CNetAddr("169.254.1.1").IsRFC3927());
+    BOOST_CHECK(CNetAddr("2002::1").IsRFC3964());
+    BOOST_CHECK(CNetAddr("FC00::").IsRFC4193());
+    BOOST_CHECK(CNetAddr("2001::2").IsRFC4380());
+    BOOST_CHECK(CNetAddr("2001:10::").IsRFC4843());
+    BOOST_CHECK(CNetAddr("FE80::").IsRFC4862());
+    BOOST_CHECK(CNetAddr("64:FF9B::").IsRFC6052());
+    BOOST_CHECK(CNetAddr("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca").IsOnionCat());
+    BOOST_CHECK(CNetAddr("127.0.0.1").IsLocal());
+    BOOST_CHECK(CNetAddr("::1").IsLocal());
+    BOOST_CHECK(CNetAddr("8.8.8.8").IsRoutable());
+    BOOST_CHECK(CNetAddr("2001::1").IsRoutable());
+    BOOST_CHECK(CNetAddr("127.0.0.1").IsValid());
+}
+
+bool static TestSplitHost(string test, string host, int port)
+{
+    string hostOut;
+    int portOut = -1;
+    SplitHostPort(test, portOut, hostOut);
+    return hostOut == host && port == portOut;
+}
+
+BOOST_AUTO_TEST_CASE(netbase_splithost)
+{
+    BOOST_CHECK(TestSplitHost("www.bitcoin.org", "www.bitcoin.org", -1));
+    BOOST_CHECK(TestSplitHost("[www.bitcoin.org]", "www.bitcoin.org", -1));
+    BOOST_CHECK(TestSplitHost("www.bitcoin.org:80", "www.bitcoin.org", 80));
+    BOOST_CHECK(TestSplitHost("[www.bitcoin.org]:80", "www.bitcoin.org", 80));
+    BOOST_CHECK(TestSplitHost("127.0.0.1", "127.0.0.1", -1));
+    BOOST_CHECK(TestSplitHost("127.0.0.1:8333", "127.0.0.1", 8333));
+    BOOST_CHECK(TestSplitHost("[127.0.0.1]", "127.0.0.1", -1));
+    BOOST_CHECK(TestSplitHost("[127.0.0.1]:8333", "127.0.0.1", 8333));
+    BOOST_CHECK(TestSplitHost("::ffff:127.0.0.1", "::ffff:127.0.0.1", -1));
+    BOOST_CHECK(TestSplitHost("[::ffff:127.0.0.1]:8333", "::ffff:127.0.0.1", 8333));
+    BOOST_CHECK(TestSplitHost("[::]:8333", "::", 8333));
+    BOOST_CHECK(TestSplitHost("::8333", "::8333", -1));
+    BOOST_CHECK(TestSplitHost(":8333", "", 8333));
+    BOOST_CHECK(TestSplitHost("[]:8333", "", 8333));
+    BOOST_CHECK(TestSplitHost("", "", -1));
+}
+
+bool static TestParse(string src, string canon)
+{
+    CService addr;
+    if (!LookupNumeric(src.c_str(), addr, 65535))
+        return canon == "";
+    return canon == addr.ToString();
+}
+
+BOOST_AUTO_TEST_CASE(netbase_lookupnumeric)
+{
+    BOOST_CHECK(TestParse("127.0.0.1", "127.0.0.1:65535"));
+    BOOST_CHECK(TestParse("127.0.0.1:8333", "127.0.0.1:8333"));
+    BOOST_CHECK(TestParse("::ffff:127.0.0.1", "127.0.0.1:65535"));
+    BOOST_CHECK(TestParse("::", "[::]:65535"));
+    BOOST_CHECK(TestParse("[::]:8333", "[::]:8333"));
+    BOOST_CHECK(TestParse("[127.0.0.1]", "127.0.0.1:65535"));
+    BOOST_CHECK(TestParse(":::", ""));
+    BOOST_CHECK(TestParse("128.5.1", "128.5.0.1:65535"));
+}
+
+BOOST_AUTO_TEST_SUITE_END()