commit
817ee0d826
11 changed files with 489 additions and 41 deletions
96
doc/Tor.txt
Normal file
96
doc/Tor.txt
Normal file
|
@ -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
|
||||
|
23
src/init.cpp
23
src/init.cpp
|
@ -222,6 +222,7 @@ std::string HelpMessage()
|
|||
" -timeout=<n> " + _("Specify connection timeout (in milliseconds)") + "\n" +
|
||||
" -proxy=<ip:port> " + _("Connect through socks proxy") + "\n" +
|
||||
" -socks=<n> " + _("Select the version of socks proxy to use (4-5, default: 5)") + "\n" +
|
||||
" -tor=<ip:port> " + _("Use proxy to reach tor hidden services (default: same as -proxy)") + "\n"
|
||||
" -dns " + _("Allow DNS lookups for -addnode, -seednode and -connect") + "\n" +
|
||||
" -port=<port> " + _("Listen for connections on <port> (default: 8333 or testnet: 18333)") + "\n" +
|
||||
" -maxconnections=<n> " + _("Maintain at most <n> connections to peers (default: 125)") + "\n" +
|
||||
|
@ -229,12 +230,12 @@ std::string HelpMessage()
|
|||
" -connect=<ip> " + _("Connect only to the specified node(s)") + "\n" +
|
||||
" -seednode=<ip> " + _("Connect to a node to retrieve peer addresses, and disconnect") + "\n" +
|
||||
" -externalip=<ip> " + _("Specify your own public address") + "\n" +
|
||||
" -onlynet=<net> " + _("Only connect to nodes in network <net> (IPv4 or IPv6)") + "\n" +
|
||||
" -onlynet=<net> " + _("Only connect to nodes in network <net> (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=<addr> " + _("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=<n> " + _("Threshold for disconnecting misbehaving peers (default: 100)") + "\n" +
|
||||
" -bantime=<n> " + _("Number of seconds to keep misbehaving peers from reconnecting (default: 86400)") + "\n" +
|
||||
" -maxreceivebuffer=<n> " + _("Maximum per-connection receive buffer, <n>*1000 bytes (default: 10000)") + "\n" +
|
||||
|
@ -476,8 +477,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()));
|
||||
|
||||
|
@ -490,6 +493,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
|
||||
|
|
|
@ -2426,7 +2426,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);
|
||||
}
|
||||
|
|
13
src/net.cpp
13
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();
|
||||
|
@ -543,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<string>()), nBestHeight);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
||||
|
|
160
src/netbase.cpp
160
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;
|
||||
|
@ -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,27 +910,82 @@ 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
|
||||
// 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 (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;
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CService::Init()
|
||||
|
@ -1036,7 +1140,7 @@ std::string CService::ToStringPort() const
|
|||
|
||||
std::string CService::ToStringIPPort() const
|
||||
{
|
||||
if (IsIPv4()) {
|
||||
if (IsIPv4() || IsTor() || IsI2P()) {
|
||||
return ToStringIP() + ":" + ToStringPort();
|
||||
} else {
|
||||
return "[" + ToStringIP() + "]:" + ToStringPort();
|
||||
|
|
|
@ -25,7 +25,7 @@ enum Network
|
|||
NET_TOR,
|
||||
NET_I2P,
|
||||
|
||||
NET_MAX
|
||||
NET_MAX,
|
||||
};
|
||||
|
||||
extern int nConnectTimeout;
|
||||
|
@ -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;
|
||||
|
|
20
src/test/base32_tests.cpp
Normal file
20
src/test/base32_tests.cpp
Normal file
|
@ -0,0 +1,20 @@
|
|||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#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<sizeof(vstrIn)/sizeof(vstrIn[0]); i++)
|
||||
{
|
||||
std::string strEnc = EncodeBase32(vstrIn[i]);
|
||||
BOOST_CHECK(strEnc == vstrOut[i]);
|
||||
std::string strDec = DecodeBase32(vstrOut[i]);
|
||||
BOOST_CHECK(strDec == vstrIn[i]);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
|
@ -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()
|
||||
|
|
187
src/util.cpp
187
src/util.cpp
|
@ -703,6 +703,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<pchEnd)
|
||||
{
|
||||
int enc = *(pch++);
|
||||
switch (mode)
|
||||
{
|
||||
case 0: // we have no bits
|
||||
strRet += pbase32[enc >> 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<nPadding[mode]; n++)
|
||||
strRet += '=';
|
||||
}
|
||||
|
||||
return strRet;
|
||||
}
|
||||
|
||||
string EncodeBase32(const string& str)
|
||||
{
|
||||
return EncodeBase32((const unsigned char*)str.c_str(), str.size());
|
||||
}
|
||||
|
||||
vector<unsigned char> 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<unsigned char> 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<unsigned char> vchRet = DecodeBase32(str.c_str());
|
||||
return string((const char*)&vchRet[0], vchRet.size());
|
||||
}
|
||||
|
||||
|
||||
bool WildcardMatch(const char* psz, const char* mask)
|
||||
{
|
||||
|
|
|
@ -147,6 +147,10 @@ std::vector<unsigned char> 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<unsigned char> 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);
|
||||
|
|
Loading…
Reference in a new issue