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" +
|
" -timeout=<n> " + _("Specify connection timeout (in milliseconds)") + "\n" +
|
||||||
" -proxy=<ip:port> " + _("Connect through socks proxy") + "\n" +
|
" -proxy=<ip:port> " + _("Connect through socks proxy") + "\n" +
|
||||||
" -socks=<n> " + _("Select the version of socks proxy to use (4-5, default: 5)") + "\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" +
|
" -dns " + _("Allow DNS lookups for -addnode, -seednode and -connect") + "\n" +
|
||||||
" -port=<port> " + _("Listen for connections on <port> (default: 8333 or testnet: 18333)") + "\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" +
|
" -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" +
|
" -connect=<ip> " + _("Connect only to the specified node(s)") + "\n" +
|
||||||
" -seednode=<ip> " + _("Connect to a node to retrieve peer addresses, and disconnect") + "\n" +
|
" -seednode=<ip> " + _("Connect to a node to retrieve peer addresses, and disconnect") + "\n" +
|
||||||
" -externalip=<ip> " + _("Specify your own public address") + "\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" +
|
" -discover " + _("Discover own IP address (default: 1 when listening and no -externalip)") + "\n" +
|
||||||
" -irc " + _("Find peers using internet relay chat (default: 0)") + "\n" +
|
" -irc " + _("Find peers using internet relay chat (default: 0)") + "\n" +
|
||||||
" -listen " + _("Accept connections from outside (default: 1 if no -proxy or -connect)") + "\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" +
|
" -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" +
|
" -banscore=<n> " + _("Threshold for disconnecting misbehaving peers (default: 100)") + "\n" +
|
||||||
" -bantime=<n> " + _("Number of seconds to keep misbehaving peers from reconnecting (default: 86400)") + "\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" +
|
" -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")) {
|
if (mapArgs.count("-proxy")) {
|
||||||
CService addrProxy = CService(mapArgs["-proxy"], 9050);
|
addrProxy = CService(mapArgs["-proxy"], 9050);
|
||||||
if (!addrProxy.IsValid())
|
if (!addrProxy.IsValid())
|
||||||
return InitError(strprintf(_("Invalid -proxy address: '%s'"), mapArgs["-proxy"].c_str()));
|
return InitError(strprintf(_("Invalid -proxy address: '%s'"), mapArgs["-proxy"].c_str()));
|
||||||
|
|
||||||
|
@ -490,6 +493,20 @@ bool AppInit2()
|
||||||
#endif
|
#endif
|
||||||
SetNameProxy(addrProxy, nSocksVersion);
|
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
|
// 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;
|
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);
|
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
|
// learn a new local address
|
||||||
bool AddLocal(const CService& addr, int nScore)
|
bool AddLocal(const CService& addr, int nScore)
|
||||||
{
|
{
|
||||||
|
@ -228,9 +236,7 @@ bool AddLocal(const CService& addr, int nScore)
|
||||||
info.nScore = nScore;
|
info.nScore = nScore;
|
||||||
info.nPort = addr.GetPort() + (fAlready ? 1 : 0);
|
info.nPort = addr.GetPort() + (fAlready ? 1 : 0);
|
||||||
}
|
}
|
||||||
enum Network net = addr.GetNetwork();
|
SetReachable(addr.GetNetwork());
|
||||||
vfReachable[net] = true;
|
|
||||||
if (net == NET_IPV6) vfReachable[NET_IPV4] = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AdvertizeLocal();
|
AdvertizeLocal();
|
||||||
|
@ -543,6 +549,7 @@ void CNode::PushVersion()
|
||||||
CAddress addrYou = (addr.IsRoutable() && !IsProxy(addr) ? addr : CAddress(CService("0.0.0.0",0)));
|
CAddress addrYou = (addr.IsRoutable() && !IsProxy(addr) ? addr : CAddress(CService("0.0.0.0",0)));
|
||||||
CAddress addrMe = GetLocalAddress(&addr);
|
CAddress addrMe = GetLocalAddress(&addr);
|
||||||
RAND_bytes((unsigned char*)&nLocalHostNonce, sizeof(nLocalHostNonce));
|
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,
|
PushMessage("version", PROTOCOL_VERSION, nLocalServices, nTime, addrYou, addrMe,
|
||||||
nLocalHostNonce, FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, std::vector<string>()), nBestHeight);
|
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 IsLocal(const CService& addr);
|
||||||
bool GetLocal(CService &addr, const CNetAddr *paddrPeer = NULL);
|
bool GetLocal(CService &addr, const CNetAddr *paddrPeer = NULL);
|
||||||
bool IsReachable(const CNetAddr &addr);
|
bool IsReachable(const CNetAddr &addr);
|
||||||
|
void SetReachable(enum Network net, bool fFlag = true);
|
||||||
CAddress GetLocalAddress(const CNetAddr *paddrPeer = NULL);
|
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)
|
bool static LookupIntern(const char *pszName, std::vector<CNetAddr>& vIP, unsigned int nMaxSolutions, bool fAllowLookup)
|
||||||
{
|
{
|
||||||
vIP.clear();
|
vIP.clear();
|
||||||
|
|
||||||
|
{
|
||||||
|
CNetAddr addr;
|
||||||
|
if (addr.SetSpecial(std::string(pszName))) {
|
||||||
|
vIP.push_back(addr);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct addrinfo aiHint;
|
struct addrinfo aiHint;
|
||||||
memset(&aiHint, 0, sizeof(struct addrinfo));
|
memset(&aiHint, 0, sizeof(struct addrinfo));
|
||||||
|
|
||||||
|
@ -530,6 +539,32 @@ void CNetAddr::SetIP(const CNetAddr& ipIn)
|
||||||
memcpy(ip, ipIn.ip, sizeof(ip));
|
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()
|
CNetAddr::CNetAddr()
|
||||||
{
|
{
|
||||||
Init();
|
Init();
|
||||||
|
@ -576,7 +611,7 @@ bool CNetAddr::IsIPv4() const
|
||||||
|
|
||||||
bool CNetAddr::IsIPv6() const
|
bool CNetAddr::IsIPv6() const
|
||||||
{
|
{
|
||||||
return (!IsIPv4());
|
return (!IsIPv4() && !IsTor() && !IsI2P());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CNetAddr::IsRFC1918() const
|
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);
|
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);
|
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);
|
return (memcmp(ip, pchGarliCat, sizeof(pchGarliCat)) == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -705,7 +738,7 @@ bool CNetAddr::IsValid() const
|
||||||
|
|
||||||
bool CNetAddr::IsRoutable() 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
|
enum Network CNetAddr::GetNetwork() const
|
||||||
|
@ -716,10 +749,10 @@ enum Network CNetAddr::GetNetwork() const
|
||||||
if (IsIPv4())
|
if (IsIPv4())
|
||||||
return NET_IPV4;
|
return NET_IPV4;
|
||||||
|
|
||||||
if (IsOnionCat())
|
if (IsTor())
|
||||||
return NET_TOR;
|
return NET_TOR;
|
||||||
|
|
||||||
if (IsGarliCat())
|
if (IsI2P())
|
||||||
return NET_I2P;
|
return NET_I2P;
|
||||||
|
|
||||||
return NET_IPV6;
|
return NET_IPV6;
|
||||||
|
@ -727,6 +760,10 @@ enum Network CNetAddr::GetNetwork() const
|
||||||
|
|
||||||
std::string CNetAddr::ToStringIP() 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);
|
CService serv(*this, 0);
|
||||||
#ifdef USE_IPV6
|
#ifdef USE_IPV6
|
||||||
struct sockaddr_storage sockaddr;
|
struct sockaddr_storage sockaddr;
|
||||||
|
@ -828,6 +865,18 @@ std::vector<unsigned char> CNetAddr::GetGroup() const
|
||||||
vchRet.push_back(GetByte(2) ^ 0xFF);
|
vchRet.push_back(GetByte(2) ^ 0xFF);
|
||||||
return vchRet;
|
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
|
// for he.net, use /36 groups
|
||||||
else if (GetByte(15) == 0x20 && GetByte(14) == 0x11 && GetByte(13) == 0x04 && GetByte(12) == 0x70)
|
else if (GetByte(15) == 0x20 && GetByte(14) == 0x11 && GetByte(13) == 0x04 && GetByte(12) == 0x70)
|
||||||
nBits = 36;
|
nBits = 36;
|
||||||
|
@ -861,27 +910,82 @@ void CNetAddr::print() const
|
||||||
printf("CNetAddr(%s)\n", ToString().c_str());
|
printf("CNetAddr(%s)\n", ToString().c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
// for IPv6 partners: for unknown/Teredo partners: for IPv4 partners:
|
// private extensions to enum Network, only returned by GetExtNetwork,
|
||||||
// 0 - unroutable // 0 - unroutable // 0 - unroutable
|
// and only used in GetReachabilityFrom
|
||||||
// 1 - teredo // 1 - teredo // 1 - ipv4
|
static const int NET_UNKNOWN = NET_MAX + 0;
|
||||||
// 2 - tunneled ipv6 // 2 - tunneled ipv6
|
static const int NET_TEREDO = NET_MAX + 1;
|
||||||
// 3 - ipv4 // 3 - ipv6
|
int static GetExtNetwork(const CNetAddr *addr)
|
||||||
// 4 - ipv6 // 4 - ipv4
|
{
|
||||||
|
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
|
int CNetAddr::GetReachabilityFrom(const CNetAddr *paddrPartner) const
|
||||||
{
|
{
|
||||||
if (!IsValid() || !IsRoutable())
|
enum Reachability {
|
||||||
return 0;
|
REACH_UNREACHABLE,
|
||||||
if (paddrPartner && paddrPartner->IsIPv4())
|
REACH_DEFAULT,
|
||||||
return IsIPv4() ? 1 : 0;
|
REACH_TEREDO,
|
||||||
if (IsRFC4380())
|
REACH_IPV6_WEAK,
|
||||||
return 1;
|
REACH_IPV4,
|
||||||
if (IsRFC3964() || IsRFC6052())
|
REACH_IPV6_STRONG,
|
||||||
return 2;
|
REACH_PRIVATE
|
||||||
bool fRealIPv6 = paddrPartner && !paddrPartner->IsRFC4380() && paddrPartner->IsValid() && paddrPartner->IsRoutable();
|
};
|
||||||
if (fRealIPv6)
|
|
||||||
return IsIPv4() ? 3 : 4;
|
if (!IsRoutable())
|
||||||
else
|
return REACH_UNREACHABLE;
|
||||||
return IsIPv4() ? 4 : 3;
|
|
||||||
|
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()
|
void CService::Init()
|
||||||
|
@ -1036,7 +1140,7 @@ std::string CService::ToStringPort() const
|
||||||
|
|
||||||
std::string CService::ToStringIPPort() const
|
std::string CService::ToStringIPPort() const
|
||||||
{
|
{
|
||||||
if (IsIPv4()) {
|
if (IsIPv4() || IsTor() || IsI2P()) {
|
||||||
return ToStringIP() + ":" + ToStringPort();
|
return ToStringIP() + ":" + ToStringPort();
|
||||||
} else {
|
} else {
|
||||||
return "[" + ToStringIP() + "]:" + ToStringPort();
|
return "[" + ToStringIP() + "]:" + ToStringPort();
|
||||||
|
|
|
@ -25,7 +25,7 @@ enum Network
|
||||||
NET_TOR,
|
NET_TOR,
|
||||||
NET_I2P,
|
NET_I2P,
|
||||||
|
|
||||||
NET_MAX
|
NET_MAX,
|
||||||
};
|
};
|
||||||
|
|
||||||
extern int nConnectTimeout;
|
extern int nConnectTimeout;
|
||||||
|
@ -44,8 +44,9 @@ class CNetAddr
|
||||||
explicit CNetAddr(const std::string &strIp, bool fAllowLookup = false);
|
explicit CNetAddr(const std::string &strIp, bool fAllowLookup = false);
|
||||||
void Init();
|
void Init();
|
||||||
void SetIP(const CNetAddr& ip);
|
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 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 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 IsRFC3849() const; // IPv6 documentation address (2001:0DB8::/32)
|
||||||
bool IsRFC3927() const; // IPv4 autoconfig (169.254.0.0/16)
|
bool IsRFC3927() const; // IPv4 autoconfig (169.254.0.0/16)
|
||||||
|
@ -56,8 +57,8 @@ class CNetAddr
|
||||||
bool IsRFC4862() const; // IPv6 autoconfig (FE80::/64)
|
bool IsRFC4862() const; // IPv6 autoconfig (FE80::/64)
|
||||||
bool IsRFC6052() const; // IPv6 well-known prefix (64:FF9B::/96)
|
bool IsRFC6052() const; // IPv6 well-known prefix (64:FF9B::/96)
|
||||||
bool IsRFC6145() const; // IPv6 IPv4-translated address (::FFFF:0:0:0/96)
|
bool IsRFC6145() const; // IPv6 IPv4-translated address (::FFFF:0:0:0/96)
|
||||||
bool IsOnionCat() const;
|
bool IsTor() const;
|
||||||
bool IsGarliCat() const;
|
bool IsI2P() const;
|
||||||
bool IsLocal() const;
|
bool IsLocal() const;
|
||||||
bool IsRoutable() const;
|
bool IsRoutable() const;
|
||||||
bool IsValid() 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("2001:10::").IsRFC4843());
|
||||||
BOOST_CHECK(CNetAddr("FE80::").IsRFC4862());
|
BOOST_CHECK(CNetAddr("FE80::").IsRFC4862());
|
||||||
BOOST_CHECK(CNetAddr("64:FF9B::").IsRFC6052());
|
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("127.0.0.1").IsLocal());
|
||||||
BOOST_CHECK(CNetAddr("::1").IsLocal());
|
BOOST_CHECK(CNetAddr("::1").IsLocal());
|
||||||
BOOST_CHECK(CNetAddr("8.8.8.8").IsRoutable());
|
BOOST_CHECK(CNetAddr("8.8.8.8").IsRoutable());
|
||||||
|
@ -88,4 +88,15 @@ BOOST_AUTO_TEST_CASE(netbase_lookupnumeric)
|
||||||
BOOST_CHECK(TestParse(":::", ""));
|
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()
|
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());
|
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)
|
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 DecodeBase64(const std::string& str);
|
||||||
std::string EncodeBase64(const unsigned char* pch, size_t len);
|
std::string EncodeBase64(const unsigned char* pch, size_t len);
|
||||||
std::string EncodeBase64(const std::string& str);
|
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[]);
|
void ParseParameters(int argc, const char*const argv[]);
|
||||||
bool WildcardMatch(const char* psz, const char* mask);
|
bool WildcardMatch(const char* psz, const char* mask);
|
||||||
bool WildcardMatch(const std::string& str, const std::string& mask);
|
bool WildcardMatch(const std::string& str, const std::string& mask);
|
||||||
|
|
Loading…
Reference in a new issue