From c4c99ade653e8e8f62ed4af4c22e196c8310d0d8 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Sun, 22 Jan 2012 20:32:58 +0100 Subject: [PATCH 1/7] Base32 encoding/decoding --- src/util.cpp | 187 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/util.h | 4 ++ 2 files changed, 191 insertions(+) diff --git a/src/util.cpp b/src/util.cpp index b07c9c1b..931d27ba 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -702,6 +702,193 @@ string DecodeBase64(const string& str) return string((const char*)&vchRet[0], vchRet.size()); } +string EncodeBase32(const unsigned char* pch, size_t len) +{ + static const char *pbase32 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; + + string strRet=""; + strRet.reserve((len+4)/5*8); + + int mode=0, left=0; + const unsigned char *pchEnd = pch+len; + + while (pch> 3]; + left = (enc & 7) << 2; + mode = 1; + break; + + case 1: // we have three bits + strRet += pbase32[left | (enc >> 6)]; + strRet += pbase32[(enc >> 1) & 31]; + left = (enc & 1) << 4; + mode = 2; + break; + + case 2: // we have one bit + strRet += pbase32[left | (enc >> 4)]; + left = (enc & 15) << 1; + mode = 3; + break; + + case 3: // we have four bits + strRet += pbase32[left | (enc >> 7)]; + strRet += pbase32[(enc >> 2) & 31]; + left = (enc & 3) << 3; + mode = 4; + break; + + case 4: // we have two bits + strRet += pbase32[left | (enc >> 5)]; + strRet += pbase32[enc & 31]; + mode = 0; + } + } + + static const int nPadding[5] = {0, 6, 4, 3, 1}; + if (mode) + { + strRet += pbase32[left]; + for (int n=0; n DecodeBase32(const char* p, bool* pfInvalid) +{ + static const int decode32_table[256] = + { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 0, 1, 2, + 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 25, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 + }; + + if (pfInvalid) + *pfInvalid = false; + + vector vchRet; + vchRet.reserve((strlen(p))*5/8); + + int mode = 0; + int left = 0; + + while (1) + { + int dec = decode32_table[(unsigned char)*p]; + if (dec == -1) break; + p++; + switch (mode) + { + case 0: // we have no bits and get 5 + left = dec; + mode = 1; + break; + + case 1: // we have 5 bits and keep 2 + vchRet.push_back((left<<3) | (dec>>2)); + left = dec & 3; + mode = 2; + break; + + case 2: // we have 2 bits and keep 7 + left = left << 5 | dec; + mode = 3; + break; + + case 3: // we have 7 bits and keep 4 + vchRet.push_back((left<<1) | (dec>>4)); + left = dec & 15; + mode = 4; + break; + + case 4: // we have 4 bits, and keep 1 + vchRet.push_back((left<<4) | (dec>>1)); + left = dec & 1; + mode = 5; + break; + + case 5: // we have 1 bit, and keep 6 + left = left << 5 | dec; + mode = 6; + break; + + case 6: // we have 6 bits, and keep 3 + vchRet.push_back((left<<2) | (dec>>3)); + left = dec & 7; + mode = 7; + break; + + case 7: // we have 3 bits, and keep 0 + vchRet.push_back((left<<5) | dec); + mode = 0; + break; + } + } + + if (pfInvalid) + switch (mode) + { + case 0: // 8n base32 characters processed: ok + break; + + case 1: // 8n+1 base32 characters processed: impossible + case 3: // +3 + case 6: // +6 + *pfInvalid = true; + break; + + case 2: // 8n+2 base32 characters processed: require '======' + if (left || p[0] != '=' || p[1] != '=' || p[2] != '=' || p[3] != '=' || p[4] != '=' || p[5] != '=' || decode32_table[(unsigned char)p[6]] != -1) + *pfInvalid = true; + break; + + case 4: // 8n+4 base32 characters processed: require '====' + if (left || p[0] != '=' || p[1] != '=' || p[2] != '=' || p[3] != '=' || decode32_table[(unsigned char)p[4]] != -1) + *pfInvalid = true; + break; + + case 5: // 8n+5 base32 characters processed: require '===' + if (left || p[0] != '=' || p[1] != '=' || p[2] != '=' || decode32_table[(unsigned char)p[3]] != -1) + *pfInvalid = true; + break; + + case 7: // 8n+7 base32 characters processed: require '=' + if (left || p[0] != '=' || decode32_table[(unsigned char)p[1]] != -1) + *pfInvalid = true; + break; + } + + return vchRet; +} + +string DecodeBase32(const string& str) +{ + vector vchRet = DecodeBase32(str.c_str()); + return string((const char*)&vchRet[0], vchRet.size()); +} + bool WildcardMatch(const char* psz, const char* mask) { diff --git a/src/util.h b/src/util.h index c9b2b2f1..058f9097 100644 --- a/src/util.h +++ b/src/util.h @@ -146,6 +146,10 @@ std::vector DecodeBase64(const char* p, bool* pfInvalid = NULL); std::string DecodeBase64(const std::string& str); std::string EncodeBase64(const unsigned char* pch, size_t len); std::string EncodeBase64(const std::string& str); +std::vector DecodeBase32(const char* p, bool* pfInvalid = NULL); +std::string DecodeBase32(const std::string& str); +std::string EncodeBase32(const unsigned char* pch, size_t len); +std::string EncodeBase32(const std::string& str); void ParseParameters(int argc, const char*const argv[]); bool WildcardMatch(const char* psz, const char* mask); bool WildcardMatch(const std::string& str, const std::string& mask); From e0be8da3924031ea115c93551dfa463f84d3a120 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Sun, 29 Jan 2012 11:38:33 +0100 Subject: [PATCH 2/7] Unit tests for base32 encode/decode --- src/test/base32_tests.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 src/test/base32_tests.cpp diff --git a/src/test/base32_tests.cpp b/src/test/base32_tests.cpp new file mode 100644 index 00000000..756c72b9 --- /dev/null +++ b/src/test/base32_tests.cpp @@ -0,0 +1,20 @@ +#include + +#include "util.h" + +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======"}; + for (unsigned int i=0; i Date: Sun, 29 Apr 2012 02:11:56 +0200 Subject: [PATCH 3/7] 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. --- src/netbase.cpp | 91 +++++++++++++++++++++++++++++++------- src/netbase.h | 7 +-- src/test/base32_tests.cpp | 2 +- src/test/netbase_tests.cpp | 13 +++++- src/util.cpp | 2 +- 5 files changed, 94 insertions(+), 21 deletions(-) diff --git a/src/netbase.cpp b/src/netbase.cpp index ffd3ea68..21fca473 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& 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 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 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 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 7a797e2f..36f29b0b 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 756c72b9..fdf32859 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 Date: Tue, 1 May 2012 17:32:42 +0200 Subject: [PATCH 4/7] Rewrite CNetAddr::GetReachabilityFrom() Add support for Tor/I2P networks, and make code more readable. --- src/netbase.cpp | 103 ++++++++++++++++++++++++++++++++++-------------- src/netbase.h | 2 +- 2 files changed, 74 insertions(+), 31 deletions(-) diff --git a/src/netbase.cpp b/src/netbase.cpp index 21fca473..aa767cd3 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -910,39 +910,82 @@ void CNetAddr::print() const printf("CNetAddr(%s)\n", ToString().c_str()); } -// 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 +// private extensions to enum Network, only returned by GetExtNetwork, +// and only used in GetReachabilityFrom +static const int NET_UNKNOWN = NET_MAX + 0; +static const int NET_TEREDO = NET_MAX + 1; +int static GetExtNetwork(const CNetAddr *addr) +{ + if (addr == NULL) + return NET_UNKNOWN; + if (addr->IsRFC4380()) + return NET_TEREDO; + return addr->GetNetwork(); +} + +/** Calculates a metric for how reachable (*this) is from a given partner */ int CNetAddr::GetReachabilityFrom(const CNetAddr *paddrPartner) const { - if (!IsValid() || !IsRoutable()) - 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; + enum Reachability { + REACH_UNREACHABLE, + REACH_DEFAULT, + REACH_TEREDO, + REACH_IPV6_WEAK, + REACH_IPV4, + REACH_IPV6_STRONG, + REACH_PRIVATE + }; + + if (!IsRoutable()) + return REACH_UNREACHABLE; + + int ourNet = GetExtNetwork(this); + int theirNet = GetExtNetwork(paddrPartner); + bool fTunnel = IsRFC3964() || IsRFC6052() || IsRFC6145(); + + switch(theirNet) { + case NET_IPV4: + switch(ourNet) { + default: return REACH_DEFAULT; + case NET_IPV4: return REACH_IPV4; + } + case NET_IPV6: + switch(ourNet) { + default: return REACH_DEFAULT; + case NET_TEREDO: return REACH_TEREDO; + case NET_IPV4: return REACH_IPV4; + case NET_IPV6: return fTunnel ? REACH_IPV6_WEAK : REACH_IPV6_STRONG; // only prefer giving our IPv6 address if it's not tunneled + } + case NET_TOR: + switch(ourNet) { + default: return REACH_DEFAULT; + case NET_IPV4: return REACH_IPV4; // Tor users can connect to IPv4 as well + case NET_TOR: return REACH_PRIVATE; + } + case NET_I2P: + switch(ourNet) { + default: return REACH_DEFAULT; + case NET_I2P: return REACH_PRIVATE; + } + case NET_TEREDO: + switch(ourNet) { + default: return REACH_DEFAULT; + case NET_TEREDO: return REACH_TEREDO; + case NET_IPV6: return REACH_IPV6_WEAK; + case NET_IPV4: return REACH_IPV4; + } + case NET_UNKNOWN: + case NET_UNROUTABLE: + default: + switch(ourNet) { + default: return REACH_DEFAULT; + case NET_TEREDO: return REACH_TEREDO; + case NET_IPV6: return REACH_IPV6_WEAK; + case NET_IPV4: return REACH_IPV4; + case NET_I2P: return REACH_PRIVATE; // assume connections from unroutable addresses are + case NET_TOR: return REACH_PRIVATE; // either from Tor/I2P, or don't care about our address + } } - if (IsRFC4380()) - return 1; - if (IsRFC3964() || IsRFC6052()) - return 2; - bool fRealIPv6 = paddrPartner && !paddrPartner->IsRFC4380() && paddrPartner->IsValid() && paddrPartner->IsRoutable(); - if (fRealIPv6) - return IsIPv4() ? 3 : 4; - else - return IsIPv4() ? 4 : 3; } void CService::Init() diff --git a/src/netbase.h b/src/netbase.h index 36f29b0b..f097d7f5 100644 --- a/src/netbase.h +++ b/src/netbase.h @@ -25,7 +25,7 @@ enum Network NET_TOR, NET_I2P, - NET_MAX + NET_MAX, }; extern int nConnectTimeout; From 54ce3bad64ea4dff64f16c12b287383ad96a875a Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 1 May 2012 21:04:07 +0200 Subject: [PATCH 5/7] Add -tor and related configuration --- src/init.cpp | 23 ++++++++++++++++++++--- src/net.cpp | 12 +++++++++--- src/net.h | 1 + 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index 4ab7bdc4..62fff5f9 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -222,6 +222,7 @@ std::string HelpMessage() " -timeout= " + _("Specify connection timeout (in milliseconds)") + "\n" + " -proxy= " + _("Connect through socks proxy") + "\n" + " -socks= " + _("Select the version of socks proxy to use (4-5, default: 5)") + "\n" + + " -tor= " + _("Use proxy to reach tor hidden services (default: same as -proxy)") + "\n" " -dns " + _("Allow DNS lookups for -addnode, -seednode and -connect") + "\n" + " -port= " + _("Listen for connections on (default: 8333 or testnet: 18333)") + "\n" + " -maxconnections= " + _("Maintain at most connections to peers (default: 125)") + "\n" + @@ -229,12 +230,12 @@ std::string HelpMessage() " -connect= " + _("Connect only to the specified node(s)") + "\n" + " -seednode= " + _("Connect to a node to retrieve peer addresses, and disconnect") + "\n" + " -externalip= " + _("Specify your own public address") + "\n" + - " -onlynet= " + _("Only connect to nodes in network (IPv4 or IPv6)") + "\n" + + " -onlynet= " + _("Only connect to nodes in network (IPv4, IPv6 or Tor)") + "\n" + " -discover " + _("Discover own IP address (default: 1 when listening and no -externalip)") + "\n" + " -irc " + _("Find peers using internet relay chat (default: 0)") + "\n" + " -listen " + _("Accept connections from outside (default: 1 if no -proxy or -connect)") + "\n" + " -bind= " + _("Bind to given address. Use [host]:port notation for IPv6") + "\n" + - " -dnsseed " + _("Find peers using DNS lookup (default: 1)") + "\n" + + " -dnsseed " + _("Find peers using DNS lookup (default: 1 unless -connect)") + "\n" + " -banscore= " + _("Threshold for disconnecting misbehaving peers (default: 100)") + "\n" + " -bantime= " + _("Number of seconds to keep misbehaving peers from reconnecting (default: 86400)") + "\n" + " -maxreceivebuffer= " + _("Maximum per-connection receive buffer, *1000 bytes (default: 10000)") + "\n" + @@ -469,8 +470,10 @@ bool AppInit2() } } + CService addrProxy; + bool fProxy = false; if (mapArgs.count("-proxy")) { - CService addrProxy = CService(mapArgs["-proxy"], 9050); + addrProxy = CService(mapArgs["-proxy"], 9050); if (!addrProxy.IsValid()) return InitError(strprintf(_("Invalid -proxy address: '%s'"), mapArgs["-proxy"].c_str())); @@ -483,6 +486,20 @@ bool AppInit2() #endif SetNameProxy(addrProxy, nSocksVersion); } + fProxy = true; + } + + // -tor can override normal proxy, -notor disables tor entirely + if (!(mapArgs.count("-tor") && mapArgs["-tor"] == "0") && (fProxy || mapArgs.count("-tor"))) { + CService addrOnion; + if (!mapArgs.count("-tor")) + addrOnion = addrProxy; + else + addrOnion = CService(mapArgs["-tor"], 9050); + if (!addrOnion.IsValid()) + return InitError(strprintf(_("Invalid -tor address: '%s'"), mapArgs["-tor"].c_str())); + SetProxy(NET_TOR, addrOnion, 5); + SetReachable(NET_TOR); } // see Step 2: parameter interactions for more information about these diff --git a/src/net.cpp b/src/net.cpp index 804cb0f5..441d28bb 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -206,6 +206,14 @@ void static AdvertizeLocal() } } +void SetReachable(enum Network net, bool fFlag) +{ + LOCK(cs_mapLocalHost); + vfReachable[net] = fFlag; + if (net == NET_IPV6 && fFlag) + vfReachable[NET_IPV4] = true; +} + // learn a new local address bool AddLocal(const CService& addr, int nScore) { @@ -228,9 +236,7 @@ bool AddLocal(const CService& addr, int nScore) info.nScore = nScore; info.nPort = addr.GetPort() + (fAlready ? 1 : 0); } - enum Network net = addr.GetNetwork(); - vfReachable[net] = true; - if (net == NET_IPV6) vfReachable[NET_IPV4] = true; + SetReachable(addr.GetNetwork()); } AdvertizeLocal(); diff --git a/src/net.h b/src/net.h index c9c96572..fb998a29 100644 --- a/src/net.h +++ b/src/net.h @@ -64,6 +64,7 @@ bool SeenLocal(const CService& addr); bool IsLocal(const CService& addr); bool GetLocal(CService &addr, const CNetAddr *paddrPeer = NULL); bool IsReachable(const CNetAddr &addr); +void SetReachable(enum Network net, bool fFlag = true); CAddress GetLocalAddress(const CNetAddr *paddrPeer = NULL); From 863e995b79ec388bf292d80f181912d01e20e2e5 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 1 May 2012 22:03:51 +0200 Subject: [PATCH 6/7] Debug version messages --- src/main.cpp | 2 +- src/net.cpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index 2d3e3513..9ca4889b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2419,7 +2419,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) pfrom->fSuccessfullyConnected = true; - printf("version message: version %d, blocks=%d\n", pfrom->nVersion, pfrom->nStartingHeight); + printf("receive version message: version %d, blocks=%d, us=%s, them=%s, peer=%s\n", pfrom->nVersion, pfrom->nStartingHeight, addrMe.ToString().c_str(), addrFrom.ToString().c_str(), pfrom->addr.ToString().c_str()); cPeerBlockCounts.input(pfrom->nStartingHeight); } diff --git a/src/net.cpp b/src/net.cpp index 441d28bb..77fd334e 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -549,6 +549,7 @@ void CNode::PushVersion() CAddress addrYou = (addr.IsRoutable() && !IsProxy(addr) ? addr : CAddress(CService("0.0.0.0",0))); CAddress addrMe = GetLocalAddress(&addr); RAND_bytes((unsigned char*)&nLocalHostNonce, sizeof(nLocalHostNonce)); + printf("send version message: version %d, blocks=%d, us=%s, them=%s, peer=%s\n", PROTOCOL_VERSION, nBestHeight, addrMe.ToString().c_str(), addrYou.ToString().c_str(), addr.ToString().c_str()); PushMessage("version", PROTOCOL_VERSION, nLocalServices, nTime, addrYou, addrMe, nLocalHostNonce, FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, std::vector()), nBestHeight); } From d789a3f1403fb6709a3ca26ef9ecc75b0b0a68a4 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 1 May 2012 21:53:44 +0200 Subject: [PATCH 7/7] Some documentation about tor --- doc/Tor.txt | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 doc/Tor.txt diff --git a/doc/Tor.txt b/doc/Tor.txt new file mode 100644 index 00000000..f44b016f --- /dev/null +++ b/doc/Tor.txt @@ -0,0 +1,96 @@ +TOR SUPPORT IN BITCOIN +====================== + +It is possible to run Bitcoin as a Tor hidden service, and connect to such services. + +The following assumes you have a Tor proxy running on port 9050. Many distributions +default to having a SOCKS proxy listening on port 9050, but others may not. +In particular, the Tor Browser Bundle defaults to listening on a random port. See +https://www.torproject.org/docs/faq.html.en#TBBSocksPort for how to properly +configure Tor. + + +1. Run bitcoin behind a Tor proxy +--------------------------------- + +The first step is running Bitcoin behind a Tor proxy. This will already make all +outgoing connections be anonimized, but more is possible. + +-socks=5 SOCKS5 supports connecting-to-hostname, which can be used instead + of doing a (leaking) local DNS lookup. SOCKS5 is the default, + but SOCKS4 does not support this. (SOCKS4a does, but isn't + implemented). + +-proxy=ip:port Set the proxy server. If SOCKS5 is selected (default), this proxy + server will be used to try to reach .onion addresses as well. + +-tor=ip:port Set the proxy server to use for tor hidden services. You do not + need to set this if it's the same as -proxy. You can use -notor + to explicitly disable access to hidden service. + +-dnsseed DNS seeds are not resolved directly when a SOCKS5 proxy server is + set. Rather, a short-lived proxy connection to the dns seed + hostname is attempted, and peer addresses are requested. + +-listen When using -proxy, listening is disabled by default. If you want + to run a hidden service (see next section), you'll need to enable + it explicitly. + +-connect=X When behing a Tor proxy, you can specify .onion addresses instead +-addnode=X of IP addresses or hostnames in these parameters. It requires +-seednode=X SOCKS5. In Tor mode, such addresses can also be exchanged with + other P2P nodes. + +In a typical situation, this suffices to run behind a Tor proxy: + + ./bitcoin -proxy=127.0.0.1:9050 + + +2. Run a bitcoin hidden server +------------------------------ + +If you configure your Tor system accordingly, it is possible to make your node also +reachable from the Tor network. Add these lines to your /etc/tor/torrc (or equivalent +config file): + + HiddenServiceDir /var/lib/tor/bitcoin-service/ + HiddenServicePort 8333 127.0.0.1:8333 + +The directory can be different of course, but (both) 8333's should be equal to your +bitcoind's P2P listen port (8333 by default). + +-externalip=X You can tell bitcoin about its publically reachable address using + this option, and this can be a .onion address. Given the above + configuration, you can find your onion address in + /var/lib/tor/bitcoin-service/hostname. Onion addresses are given + preference for your node to advertize itself with, for connections + coming from unroutable addresses (such as 127.0.0.1, where the + Tor proxy typically runs). + +-listen You'll need to enable listening for incoming connections, as this + is off by default behind a proxy. + +-discover When -externalip is specified, no attempt is made to discover local + IPv4 or IPv6 addresses. If you want to run a dual stack, reachable + from both Tor and IPv4 (or IPv6), you'll need to either pass your + other addresses using -externalip, or explicitly enable -discover. + Note that both addresses of a dual-stack system may be easily + linkable using traffic analysis. + +In a typical situation, where you're only reachable via Tor, this should suffice: + + ./bitcoind -proxy=127.0.0.1:9050 -externalip=57qr3yd1nyntf5k.onion -listen + +(obviously replace the Onion address with your own). If you don't care too much +about hiding your node, and want to be reachable on IPv4 as well, additionally +specify: + + ./bitcoind ... -discover + +and open port 8333 on your firewall (or use -upnp). + +If you only want to use Tor to reach onion addresses, but not use it as a proxy +for normal IPv4/IPv6 communication, use: + + ./bitcoin -tor=127.0.0.1:9050 -externalip=57qr3yd1nyntf5k.onion -discover +