aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPieter Wuille <pieter.wuille@gmail.com>2012-04-29 02:11:56 +0200
committerPieter Wuille <pieter.wuille@gmail.com>2012-06-23 01:11:32 +0200
commit70f7f0038592a28e846f02d084f0119fc34eb52f (patch)
tree7439cec074260c4eee55026c5a9ab37e0bcf6f58
parente0be8da3924031ea115c93551dfa463f84d3a120 (diff)
downloadbitcoin-70f7f0038592a28e846f02d084f0119fc34eb52f.tar.xz
Node support for Tor hidden services
This commit adds support for .onion addresses (mapped into the IPv6 by using OnionCat's range and encoding), and the ability to connect to them via a SOCKS5 proxy.
-rw-r--r--src/netbase.cpp91
-rw-r--r--src/netbase.h7
-rw-r--r--src/test/base32_tests.cpp2
-rw-r--r--src/test/netbase_tests.cpp13
-rw-r--r--src/util.cpp2
5 files changed, 94 insertions, 21 deletions
diff --git a/src/netbase.cpp b/src/netbase.cpp
index ffd3ea68a5..21fca4737a 100644
--- a/src/netbase.cpp
+++ b/src/netbase.cpp
@@ -57,6 +57,15 @@ void SplitHostPort(std::string in, int &portOut, std::string &hostOut) {
bool static LookupIntern(const char *pszName, std::vector<CNetAddr>& vIP, unsigned int nMaxSolutions, bool fAllowLookup)
{
vIP.clear();
+
+ {
+ CNetAddr addr;
+ if (addr.SetSpecial(std::string(pszName))) {
+ vIP.push_back(addr);
+ return true;
+ }
+ }
+
struct addrinfo aiHint;
memset(&aiHint, 0, sizeof(struct addrinfo));
@@ -530,6 +539,32 @@ void CNetAddr::SetIP(const CNetAddr& ipIn)
memcpy(ip, ipIn.ip, sizeof(ip));
}
+static const unsigned char pchOnionCat[] = {0xFD,0x87,0xD8,0x7E,0xEB,0x43};
+static const unsigned char pchGarliCat[] = {0xFD,0x60,0xDB,0x4D,0xDD,0xB5};
+
+bool CNetAddr::SetSpecial(const std::string &strName)
+{
+ if (strName.size()>6 && strName.substr(strName.size() - 6, 6) == ".onion") {
+ std::vector<unsigned char> vchAddr = DecodeBase32(strName.substr(0, strName.size() - 6).c_str());
+ if (vchAddr.size() != 16-sizeof(pchOnionCat))
+ return false;
+ memcpy(ip, pchOnionCat, sizeof(pchOnionCat));
+ for (unsigned int i=0; i<16-sizeof(pchOnionCat); i++)
+ ip[i + sizeof(pchOnionCat)] = vchAddr[i];
+ return true;
+ }
+ if (strName.size()>11 && strName.substr(strName.size() - 11, 11) == ".oc.b32.i2p") {
+ std::vector<unsigned char> vchAddr = DecodeBase32(strName.substr(0, strName.size() - 11).c_str());
+ if (vchAddr.size() != 16-sizeof(pchGarliCat))
+ return false;
+ memcpy(ip, pchOnionCat, sizeof(pchGarliCat));
+ for (unsigned int i=0; i<16-sizeof(pchGarliCat); i++)
+ ip[i + sizeof(pchGarliCat)] = vchAddr[i];
+ return true;
+ }
+ return false;
+}
+
CNetAddr::CNetAddr()
{
Init();
@@ -576,7 +611,7 @@ bool CNetAddr::IsIPv4() const
bool CNetAddr::IsIPv6() const
{
- return (!IsIPv4());
+ return (!IsIPv4() && !IsTor() && !IsI2P());
}
bool CNetAddr::IsRFC1918() const
@@ -635,15 +670,13 @@ bool CNetAddr::IsRFC4843() const
return (GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0x00 && (GetByte(12) & 0xF0) == 0x10);
}
-bool CNetAddr::IsOnionCat() const
+bool CNetAddr::IsTor() const
{
- static const unsigned char pchOnionCat[] = {0xFD,0x87,0xD8,0x7E,0xEB,0x43};
return (memcmp(ip, pchOnionCat, sizeof(pchOnionCat)) == 0);
}
-bool CNetAddr::IsGarliCat() const
+bool CNetAddr::IsI2P() const
{
- static const unsigned char pchGarliCat[] = {0xFD,0x60,0xDB,0x4D,0xDD,0xB5};
return (memcmp(ip, pchGarliCat, sizeof(pchGarliCat)) == 0);
}
@@ -705,7 +738,7 @@ bool CNetAddr::IsValid() const
bool CNetAddr::IsRoutable() const
{
- return IsValid() && !(IsRFC1918() || IsRFC3927() || IsRFC4862() || (IsRFC4193() && !IsOnionCat() && !IsGarliCat()) || IsRFC4843() || IsLocal());
+ return IsValid() && !(IsRFC1918() || IsRFC3927() || IsRFC4862() || (IsRFC4193() && !IsTor() && !IsI2P()) || IsRFC4843() || IsLocal());
}
enum Network CNetAddr::GetNetwork() const
@@ -716,10 +749,10 @@ enum Network CNetAddr::GetNetwork() const
if (IsIPv4())
return NET_IPV4;
- if (IsOnionCat())
+ if (IsTor())
return NET_TOR;
- if (IsGarliCat())
+ if (IsI2P())
return NET_I2P;
return NET_IPV6;
@@ -727,6 +760,10 @@ enum Network CNetAddr::GetNetwork() const
std::string CNetAddr::ToStringIP() const
{
+ if (IsTor())
+ return EncodeBase32(&ip[6], 10) + ".onion";
+ if (IsI2P())
+ return EncodeBase32(&ip[6], 10) + ".oc.b32.i2p";
CService serv(*this, 0);
#ifdef USE_IPV6
struct sockaddr_storage sockaddr;
@@ -739,7 +776,7 @@ std::string CNetAddr::ToStringIP() const
if (!getnameinfo((const struct sockaddr*)&sockaddr, socklen, name, sizeof(name), NULL, 0, NI_NUMERICHOST))
return std::string(name);
}
- if (IsIPv4())
+ if (IsIPv4())
return strprintf("%u.%u.%u.%u", GetByte(3), GetByte(2), GetByte(1), GetByte(0));
else
return strprintf("%x:%x:%x:%x:%x:%x:%x:%x",
@@ -828,6 +865,18 @@ std::vector<unsigned char> CNetAddr::GetGroup() const
vchRet.push_back(GetByte(2) ^ 0xFF);
return vchRet;
}
+ else if (IsTor())
+ {
+ nClass = NET_TOR;
+ nStartByte = 6;
+ nBits = 4;
+ }
+ else if (IsI2P())
+ {
+ nClass = NET_I2P;
+ nStartByte = 6;
+ nBits = 4;
+ }
// for he.net, use /36 groups
else if (GetByte(15) == 0x20 && GetByte(14) == 0x11 && GetByte(13) == 0x04 && GetByte(12) == 0x70)
nBits = 36;
@@ -861,11 +910,11 @@ void CNetAddr::print() const
printf("CNetAddr(%s)\n", ToString().c_str());
}
-// for IPv6 partners: for unknown/Teredo partners: for IPv4 partners:
-// 0 - unroutable // 0 - unroutable // 0 - unroutable
-// 1 - teredo // 1 - teredo // 1 - ipv4
-// 2 - tunneled ipv6 // 2 - tunneled ipv6
-// 3 - ipv4 // 3 - ipv6
+// for IPv6 partners: for unknown/Teredo partners: for IPv4 partners: for Tor partners: for I2P partners:
+// 0 - unroutable // 0 - unroutable // 0 - unroutable // 0 - unroutable // 0 - unroutable
+// 1 - teredo // 1 - teredo // 1 - ipv4 // 1 - the rest // 1 - the rest
+// 2 - tunneled ipv6 // 2 - tunneled ipv6 // 2 - ip4 // 2 - I2P
+// 3 - ipv4 // 3 - ipv6 // 3 - tor
// 4 - ipv6 // 4 - ipv4
int CNetAddr::GetReachabilityFrom(const CNetAddr *paddrPartner) const
{
@@ -873,6 +922,18 @@ int CNetAddr::GetReachabilityFrom(const CNetAddr *paddrPartner) const
return 0;
if (paddrPartner && paddrPartner->IsIPv4())
return IsIPv4() ? 1 : 0;
+ if (paddrPartner && paddrPartner->IsTor()) {
+ if (IsIPv4())
+ return 2;
+ if (IsTor())
+ return 3;
+ return 1;
+ }
+ if (paddrPartner && paddrPartner->IsI2P()) {
+ if (IsI2P())
+ return 2;
+ return 1;
+ }
if (IsRFC4380())
return 1;
if (IsRFC3964() || IsRFC6052())
@@ -1036,7 +1097,7 @@ std::string CService::ToStringPort() const
std::string CService::ToStringIPPort() const
{
- if (IsIPv4()) {
+ if (IsIPv4() || IsTor() || IsI2P()) {
return ToStringIP() + ":" + ToStringPort();
} else {
return "[" + ToStringIP() + "]:" + ToStringPort();
diff --git a/src/netbase.h b/src/netbase.h
index 7a797e2fd6..36f29b0b32 100644
--- a/src/netbase.h
+++ b/src/netbase.h
@@ -44,8 +44,9 @@ class CNetAddr
explicit CNetAddr(const std::string &strIp, bool fAllowLookup = false);
void Init();
void SetIP(const CNetAddr& ip);
+ bool SetSpecial(const std::string &strName); // for Tor and I2P addresses
bool IsIPv4() const; // IPv4 mapped address (::FFFF:0:0/96, 0.0.0.0/0)
- bool IsIPv6() const; // IPv6 address (not IPv4)
+ bool IsIPv6() const; // IPv6 address (not mapped IPv4, not Tor/I2P)
bool IsRFC1918() const; // IPv4 private networks (10.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12)
bool IsRFC3849() const; // IPv6 documentation address (2001:0DB8::/32)
bool IsRFC3927() const; // IPv4 autoconfig (169.254.0.0/16)
@@ -56,8 +57,8 @@ class CNetAddr
bool IsRFC4862() const; // IPv6 autoconfig (FE80::/64)
bool IsRFC6052() const; // IPv6 well-known prefix (64:FF9B::/96)
bool IsRFC6145() const; // IPv6 IPv4-translated address (::FFFF:0:0:0/96)
- bool IsOnionCat() const;
- bool IsGarliCat() const;
+ bool IsTor() const;
+ bool IsI2P() const;
bool IsLocal() const;
bool IsRoutable() const;
bool IsValid() const;
diff --git a/src/test/base32_tests.cpp b/src/test/base32_tests.cpp
index 756c72b94d..fdf3285913 100644
--- a/src/test/base32_tests.cpp
+++ b/src/test/base32_tests.cpp
@@ -7,7 +7,7 @@ BOOST_AUTO_TEST_SUITE(base32_tests)
BOOST_AUTO_TEST_CASE(base32_testvectors)
{
static const std::string vstrIn[] = {"","f","fo","foo","foob","fooba","foobar"};
- static const std::string vstrOut[] = {"","MY======","MZXQ====","MZXW6===","MZXW6YQ=","MZXW6YTB","MZXW6YTBOI======"};
+ static const std::string vstrOut[] = {"","my======","mzxq====","mzxw6===","mzxw6yq=","mzxw6ytb","mzxw6ytboi======"};
for (unsigned int i=0; i<sizeof(vstrIn)/sizeof(vstrIn[0]); i++)
{
std::string strEnc = EncodeBase32(vstrIn[i]);
diff --git a/src/test/netbase_tests.cpp b/src/test/netbase_tests.cpp
index f0828f39fc..e5a7562d97 100644
--- a/src/test/netbase_tests.cpp
+++ b/src/test/netbase_tests.cpp
@@ -34,7 +34,7 @@ BOOST_AUTO_TEST_CASE(netbase_properties)
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("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca").IsTor());
BOOST_CHECK(CNetAddr("127.0.0.1").IsLocal());
BOOST_CHECK(CNetAddr("::1").IsLocal());
BOOST_CHECK(CNetAddr("8.8.8.8").IsRoutable());
@@ -88,4 +88,15 @@ BOOST_AUTO_TEST_CASE(netbase_lookupnumeric)
BOOST_CHECK(TestParse(":::", ""));
}
+BOOST_AUTO_TEST_CASE(onioncat_test)
+{
+ // values from http://www.cypherpunk.at/onioncat/wiki/OnionCat
+ CNetAddr addr1("5wyqrzbvrdsumnok.onion");
+ CNetAddr addr2("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca");
+ BOOST_CHECK(addr1 == addr2);
+ BOOST_CHECK(addr1.IsTor());
+ BOOST_CHECK(addr1.ToStringIP() == "5wyqrzbvrdsumnok.onion");
+ BOOST_CHECK(addr1.IsRoutable());
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/util.cpp b/src/util.cpp
index 931d27ba00..5fff27160f 100644
--- a/src/util.cpp
+++ b/src/util.cpp
@@ -704,7 +704,7 @@ string DecodeBase64(const string& str)
string EncodeBase32(const unsigned char* pch, size_t len)
{
- static const char *pbase32 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
+ static const char *pbase32 = "abcdefghijklmnopqrstuvwxyz234567";
string strRet="";
strRet.reserve((len+4)/5*8);