Browse Source

addrv2 changes

p2p
zanzibar 3 years ago
parent
commit
1b76850a03
  1. 148
      src/addrman.h
  2. 14
      src/httpserver.cpp
  3. 6
      src/init.cpp
  4. 24
      src/net.cpp
  5. 38
      src/netaddress.h
  6. 66
      src/netbase.cpp
  7. 83
      src/netbase.h
  8. 248
      src/prevector.h
  9. 11
      src/rpc/net.cpp
  10. 69
      src/serialize.h

148
src/addrman.h

@ -34,6 +34,7 @@
#include <map>
#include <set>
#include <stdint.h>
#include <streams.h>
#include <vector>
/**
@ -196,9 +197,32 @@ public:
class CAddrMan
{
friend class CAddrManTest;
private:
protected:
//! critical section to protect the inner data structures
mutable CCriticalSection cs;
private:
//! Serialization versions.
enum Format : uint8_t {
V0_HISTORICAL = 0, //!< historic format
V1_DETERMINISTIC = 1, //!< for pre-asmap files
V2_ASMAP = 2, //!< for files including asmap version
V3_HIP155 = 3, //!< same as V2_ASMAP plus addresses are in HIP155 format
};
//! The maximum format this software knows it can unserialize. Also, we always serialize
//! in this format.
//! The format (first byte in the serialized stream) can be higher than this and
//! still this software may be able to unserialize the file - if the second byte
//! (see `lowest_compatible` in `Unserialize()`) is less or equal to this.
static constexpr Format FILE_FORMAT = Format::V3_HIP155;
//! The initial value of a field that is incremented every time an incompatible format
//! change is made (such that old software versions would not be able to parse and
//! understand the new file format). This is 32 because we overtook the "key size"
//! field which was 32 historically.
//! @note Don't increment this. Increment `lowest_compatible` in `Serialize()` instead.
static constexpr uint8_t INCOMPATIBILITY_BASE = 32;
//! last used nId
int nIdCount;
@ -340,13 +364,20 @@ public:
* very little in common.
*/
template<typename Stream>
void Serialize(Stream &s) const
void Serialize(Stream &s_) const
{
LOCK(cs);
unsigned char nVersion = 2;
s << nVersion;
s << ((unsigned char)32);
// Always serialize in the latest version (FILE_FORMAT).
OverrideStream<Stream> s(&s_, s_.GetType(), s_.GetVersion() | ADDRV2_FORMAT);
s << static_cast<uint8_t>(FILE_FORMAT);
// Increment `lowest_compatible` iff a newly introduced format is incompatible with
// the previous one.
static constexpr uint8_t lowest_compatible = Format::V3_HIP155;
s << static_cast<uint8_t>(INCOMPATIBILITY_BASE + lowest_compatible);
s << nKey;
s << nNew;
s << nTried;
@ -397,22 +428,40 @@ public:
}
template<typename Stream>
void Unserialize(Stream& s)
void Unserialize(Stream& s_)
{
LOCK(cs);
Clear();
unsigned char nVersion;
s >> nVersion;
unsigned char nKeySize;
s >> nKeySize;
if (nKeySize != 32) throw std::ios_base::failure("Incorrect keysize in addrman deserialization");
Format format;
s_ >> Using<CustomUintFormatter<1>>(format);
int stream_version = s_.GetVersion();
if (format >= Format::V3_HIP155) {
// Add ADDRV2_FORMAT to the version so that the CNetAddr and CAddress
// unserialize methods know that an address in addrv2 format is coming.
stream_version |= ADDRV2_FORMAT;
}
OverrideStream<Stream> s(&s_, s_.GetType(), stream_version);
uint8_t compat;
s >> compat;
const uint8_t lowest_compatible = compat - INCOMPATIBILITY_BASE;
if (lowest_compatible > FILE_FORMAT) {
throw std::ios_base::failure(strprintf(
"Unsupported format of addrman database: %u. It is compatible with formats >=%u, "
"but the maximum supported by this version of %s is %u.",
format, lowest_compatible, PACKAGE_NAME, static_cast<uint8_t>(FILE_FORMAT)));
}
s >> nKey;
s >> nNew;
s >> nTried;
int nUBuckets = 0;
s >> nUBuckets;
if (nVersion != 0) {
if (format >= Format::V1_DETERMINISTIC) {
nUBuckets ^= (1 << 30);
}
@ -456,47 +505,64 @@ public:
nTried -= nLost;
// Store positions in the new table buckets to apply later (if possible).
std::map<int, int> entryToBucket; // Represents which entry belonged to which bucket when serializing
for (int bucket = 0; bucket < nUBuckets; bucket++) {
int nSize = 0;
s >> nSize;
for (int n = 0; n < nSize; n++) {
int nIndex = 0;
s >> nIndex;
if (nIndex >= 0 && nIndex < nNew) {
entryToBucket[nIndex] = bucket;
// An entry may appear in up to ADDRMAN_NEW_BUCKETS_PER_ADDRESS buckets,
// so we store all bucket-entry_index pairs to iterate through later.
std::vector<std::pair<int, int>> bucket_entries;
for (int bucket = 0; bucket < nUBuckets; ++bucket) {
int num_entries{0};
s >> num_entries;
for (int n = 0; n < num_entries; ++n) {
int entry_index{0};
s >> entry_index;
if (entry_index >= 0 && entry_index < nNew) {
bucket_entries.emplace_back(bucket, entry_index);
}
}
}
uint256 supplied_asmap_version;
// If the bucket count and asmap checksum haven't changed, then attempt
// to restore the entries to the buckets/positions they were in before
// serialization.
uint256 supplied_asmap_checksum;
if (m_asmap.size() != 0) {
supplied_asmap_version = SerializeHash(m_asmap);
supplied_asmap_checksum = SerializeHash(m_asmap);
}
uint256 serialized_asmap_version;
if (nVersion > 1) {
s >> serialized_asmap_version;
uint256 serialized_asmap_checksum;
if (format >= Format::V2_ASMAP) {
s >> serialized_asmap_checksum;
}
for (int n = 0; n < nNew; n++) {
CAddrInfo &info = mapInfo[n];
int bucket = entryToBucket[n];
int nUBucketPos = info.GetBucketPosition(nKey, true, bucket);
if (nVersion == 2 && nUBuckets == ADDRMAN_NEW_BUCKET_COUNT && vvNew[bucket][nUBucketPos] == -1 &&
info.nRefCount < ADDRMAN_NEW_BUCKETS_PER_ADDRESS && serialized_asmap_version == supplied_asmap_version) {
const bool restore_bucketing{nUBuckets == ADDRMAN_NEW_BUCKET_COUNT &&
serialized_asmap_checksum == supplied_asmap_checksum};
if (!restore_bucketing) {
LogPrint("addrman", "Bucketing method was updated, re-bucketing addrman entries from disk\n");
}
for (auto bucket_entry : bucket_entries) {
int bucket{bucket_entry.first};
const int entry_index{bucket_entry.second};
CAddrInfo& info = mapInfo[entry_index];
// The entry shouldn't appear in more than
// ADDRMAN_NEW_BUCKETS_PER_ADDRESS. If it has already, just skip
// this bucket_entry.
if (info.nRefCount >= ADDRMAN_NEW_BUCKETS_PER_ADDRESS) continue;
int bucket_position = info.GetBucketPosition(nKey, true, bucket);
if (restore_bucketing && vvNew[bucket][bucket_position] == -1) {
// Bucketing has not changed, using existing bucket positions for the new table
vvNew[bucket][nUBucketPos] = n;
info.nRefCount++;
vvNew[bucket][bucket_position] = entry_index;
++info.nRefCount;
} else {
// In case the new table data cannot be used (nVersion unknown, bucket count wrong or new asmap),
// In case the new table data cannot be used (bucket count wrong or new asmap),
// try to give them a reference based on their primary source address.
LogPrint("addrman", "Bucketing method was updated, re-bucketing addrman entries from disk\n");
bucket = info.GetNewBucket(nKey, m_asmap);
nUBucketPos = info.GetBucketPosition(nKey, true, bucket);
if (vvNew[bucket][nUBucketPos] == -1) {
vvNew[bucket][nUBucketPos] = n;
info.nRefCount++;
bucket_position = info.GetBucketPosition(nKey, true, bucket);
if (vvNew[bucket][bucket_position] == -1) {
vvNew[bucket][bucket_position] = entry_index;
++info.nRefCount;
}
}
}

14
src/httpserver.cpp

@ -201,12 +201,17 @@ static bool ClientAllowed(const CNetAddr& netaddr)
static bool InitHTTPAllowList()
{
rpc_allow_subnets.clear();
rpc_allow_subnets.push_back(CSubNet("127.0.0.0/8")); // always allow IPv4 local subnet
rpc_allow_subnets.push_back(CSubNet("::1")); // always allow IPv6 localhost
CNetAddr localv4;
CNetAddr localv6;
LookupHost("127.0.0.1", localv4, false);
LookupHost("::1", localv6, false);
rpc_allow_subnets.push_back(CSubNet(localv4,8)); // always allow IPv4 local subnet
rpc_allow_subnets.push_back(CSubNet(localv6)); // always allow IPv6 localhost
if (mapMultiArgs.count("-rpcallowip")) {
const std::vector<std::string>& vAllow = mapMultiArgs["-rpcallowip"];
BOOST_FOREACH (std::string strAllow, vAllow) {
CSubNet subnet(strAllow);
CSubNet subnet;
LookupSubNet(strAllow, subnet);
if (!subnet.IsValid()) {
uiInterface.ThreadSafeMessageBox(
strprintf("Invalid -rpcallowip subnet specification: %s. Valid are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24).", strAllow),
@ -618,7 +623,8 @@ CService HTTPRequest::GetPeer()
const char* address = "";
uint16_t port = 0;
evhttp_connection_get_peer(con, (char**)&address, &port);
peer = CService(address, port);
//peer = CService(address, port);
peer = LookupNumeric(address, port);
}
return peer;
}

6
src/init.cpp

@ -1611,7 +1611,11 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
std::string proxyArg = GetArg("-proxy", "");
SetLimited(NET_ONION);
if (proxyArg != "" && proxyArg != "0") {
proxyType addrProxy = proxyType(CService(proxyArg, 9050), proxyRandomize);
CService proxyAddr;
if (!Lookup(proxyArg, proxyAddr, 9050, fNameLookup)) {
return InitError(strprintf(_("Invalid -proxy address or hostname: '%s'"), proxyArg));
}
proxyType addrProxy = proxyType(proxyAddr, proxyRandomize);
if (!addrProxy.IsValid())
return InitError(strprintf(_("Invalid -proxy address: '%s'"), proxyArg));

24
src/net.cpp

@ -206,7 +206,7 @@ static std::vector<CAddress> convertSeed6(const std::vector<SeedSpec6> &vSeedsIn
// one by discovery.
CAddress GetLocalAddress(const CNetAddr *paddrPeer)
{
CAddress ret(CService("0.0.0.0",GetListenPort()),0);
CAddress ret(CService(CNetAddr(),GetListenPort()), nLocalServices);
CService addr;
if (GetLocal(addr, paddrPeer))
{
@ -515,8 +515,10 @@ void CNode::PushVersion()
int nBestHeight = g_signals.GetHeight().get_value_or(0);
int64_t nTime = (fInbound ? GetTime() : GetTime());
CAddress addrYou = (addr.IsRoutable() && !IsProxy(addr) ? addr : CAddress(CService("0.0.0.0",0)));
CAddress addrMe = GetLocalAddress(&addr);
CAddress addrYou = addr.IsRoutable() && !IsProxy(addr) && addr.IsAddrV1Compatible() ?
addr :
CAddress(CService(), addr.nServices);
GetRandBytes((unsigned char*)&nLocalHostNonce, sizeof(nLocalHostNonce));
if (fLogIPs)
LogPrint("net", "send version message: version %d, blocks=%d, us=%s, them=%s, peer=%d\n", PROTOCOL_VERSION, nBestHeight, addrMe.ToString(), addrYou.ToString(), id);
@ -570,8 +572,9 @@ bool CNode::IsBanned(CSubNet subnet)
}
void CNode::Ban(const CNetAddr& addr, int64_t bantimeoffset, bool sinceUnixEpoch) {
CSubNet subNet(addr.ToString()+(addr.IsIPv4() ? "/32" : "/128"));
Ban(subNet, bantimeoffset, sinceUnixEpoch);
//CSubNet subNet(addr.ToString()+(addr.IsIPv4() ? "/32" : "/128"));
CSubNet sub_net(addr);
Ban(sub_net, bantimeoffset, sinceUnixEpoch);
}
void CNode::Ban(const CSubNet& subNet, int64_t bantimeoffset, bool sinceUnixEpoch) {
@ -585,8 +588,9 @@ void CNode::Ban(const CSubNet& subNet, int64_t bantimeoffset, bool sinceUnixEpoc
}
bool CNode::Unban(const CNetAddr &addr) {
CSubNet subNet(addr.ToString()+(addr.IsIPv4() ? "/32" : "/128"));
return Unban(subNet);
//CSubNet subNet(addr.ToString()+(addr.IsIPv4() ? "/32" : "/128"));
CSubNet sub_net(addr);
return Unban(sub_net);
}
bool CNode::Unban(const CSubNet &subNet) {
@ -1382,7 +1386,9 @@ void ThreadDNSAddressSeed()
}
}
}
addrman.Add(vAdd, CNetAddr(seed.name, true));
CNetAddr net_addr;
LookupHost(seed.name,net_addr, true);
addrman.Add(vAdd, net_addr);
}
}
@ -1466,7 +1472,9 @@ void ThreadOpenConnections()
static bool done = false;
if (!done) {
LogPrintf("Adding fixed seed nodes.\n");
addrman.Add(convertSeed6(Params().FixedSeeds()), CNetAddr("127.0.0.1"));
CNetAddr net_addr;
LookupHost("127.0.0.1", net_addr, false);
addrman.Add(convertSeed6(Params().FixedSeeds()), net_addr);
done = true;
}
}

38
src/netaddress.h

@ -297,13 +297,13 @@ class CNetAddr
switch (m_net) {
case NET_IPV6:
assert(m_addr.size() == sizeof(arr));
memcpy(arr, m_addr.data(), m_addr.size());
memcpy(arr, &m_addr, m_addr.size());
return;
case NET_IPV4:
prefix_size = sizeof(IPV4_IN_IPV6_PREFIX);
assert(prefix_size + m_addr.size() == sizeof(arr));
memcpy(arr, IPV4_IN_IPV6_PREFIX.data(), prefix_size);
memcpy(arr + prefix_size, m_addr.data(), m_addr.size());
memcpy(arr + prefix_size, &m_addr, m_addr.size());
return;
case NET_ONION:
if (m_addr.size() == ADDR_TORV3_SIZE) {
@ -312,13 +312,13 @@ class CNetAddr
prefix_size = sizeof(TORV2_IN_IPV6_PREFIX);
assert(prefix_size + m_addr.size() == sizeof(arr));
memcpy(arr, TORV2_IN_IPV6_PREFIX.data(), prefix_size);
memcpy(arr + prefix_size, m_addr.data(), m_addr.size());
memcpy(arr + prefix_size, &m_addr, m_addr.size());
return;
case NET_INTERNAL:
prefix_size = sizeof(INTERNAL_IN_IPV6_PREFIX);
assert(prefix_size + m_addr.size() == sizeof(arr));
memcpy(arr, INTERNAL_IN_IPV6_PREFIX.data(), prefix_size);
memcpy(arr + prefix_size, m_addr.data(), m_addr.size());
memcpy(arr + prefix_size, &m_addr, m_addr.size());
return;
case NET_I2P:
break;
@ -416,13 +416,12 @@ class CNetAddr
}
// Do some special checks on IPv6 addresses.
// Recognize NET_INTERNAL embedded in IPv6, such addresses are not
// gossiped but could be coming from addrman, when unserializing from
// disk.
if (HasPrefix(m_addr, INTERNAL_IN_IPV6_PREFIX)) {
m_net = NET_INTERNAL;
memmove(m_addr.data(), m_addr.data() + INTERNAL_IN_IPV6_PREFIX.size(),
memmove(&m_addr, &m_addr + INTERNAL_IN_IPV6_PREFIX.size(),
ADDR_INTERNAL_SIZE);
m_addr.resize(ADDR_INTERNAL_SIZE);
return;
@ -477,22 +476,23 @@ class CSubNet
friend bool operator!=(const CSubNet& a, const CSubNet& b) { return !(a == b); }
friend bool operator<(const CSubNet& a, const CSubNet& b);
SERIALIZE_METHODS(CSubNet, obj)
{
READWRITE(obj.network);
if (obj.network.IsIPv4()) {
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(network);
if (network.IsIPv4()) {
// Before commit 102867c587f5f7954232fb8ed8e85cda78bb4d32, CSubNet used the last 4 bytes of netmask
// to store the relevant bytes for an IPv4 mask. For compatibility reasons, keep doing so in
// serialized form.
unsigned char dummy[12] = {0};
READWRITE(dummy);
READWRITE(MakeSpan(obj.netmask).first(4));
READWRITE(MakeSpan(netmask).first(4));
} else {
READWRITE(obj.netmask);
READWRITE(netmask);
}
READWRITE(obj.valid);
READWRITE(valid);
// Mark invalid if the result doesn't pass sanity checking.
SER_READ(obj, if (obj.valid) obj.valid = obj.SanityCheck());
//SER_READ(obj, if (obj.valid) obj.valid = obj.SanityCheck());
}
};
@ -521,10 +521,12 @@ class CService : public CNetAddr
CService(const struct in6_addr& ipv6Addr, uint16_t port);
explicit CService(const struct sockaddr_in6& addr);
SERIALIZE_METHODS(CService, obj)
{
READWRITEAS(CNetAddr, obj);
READWRITE(Using<BigEndianFormatter<2>>(obj.port));
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action) {
//READWRITE(CNetAddr, obj);
READWRITE(*(CNetAddr*)this);
READWRITE(Using<BigEndianFormatter<2>>(port));
}
};

66
src/netbase.cpp

@ -217,6 +217,19 @@ bool LookupHost(const char *pszName, std::vector<CNetAddr>& vIP, unsigned int nM
return LookupIntern(strHost.c_str(), vIP, nMaxSolutions, fAllowLookup);
}
bool LookupHost(const std::string& name, CNetAddr& addr, bool fAllowLookup, DNSLookupFn dns_lookup_function)
{
if (!ValidAsCString(name)) {
return false;
}
std::vector<CNetAddr> vIP;
LookupHost(name, vIP, 1, fAllowLookup, dns_lookup_function);
if(vIP.empty())
return false;
addr = vIP.front();
return true;
}
bool Lookup(const char *pszName, std::vector<CService>& vAddr, int portDefault, bool fAllowLookup, unsigned int nMaxSolutions)
{
if (pszName[0] == 0)
@ -245,9 +258,17 @@ bool Lookup(const char *pszName, CService& addr, int portDefault, bool fAllowLoo
return true;
}
bool LookupNumeric(const char *pszName, CService& addr, int portDefault)
CService LookupNumeric(const std::string& name, uint16_t portDefault, DNSLookupFn dns_lookup_function)
{
return Lookup(pszName, addr, portDefault, false);
if (!ValidAsCString(name)) {
return {};
}
CService addr;
// "1.2:345" will fail to resolve the ip, but will still set the port.
// If the ip fails to resolve, re-init the result.
if(!Lookup(name, addr, portDefault, false, dns_lookup_function))
addr = CService();
return addr;
}
struct timeval MillisToTimeval(int64_t nTimeout)
@ -616,6 +637,47 @@ static bool ConnectThroughProxy(const proxyType &proxy, const std::string& strDe
return true;
}
bool LookupSubNet(const std::string& strSubnet, CSubNet& ret, DNSLookupFn dns_lookup_function)
{
if (!ValidAsCString(strSubnet)) {
return false;
}
size_t slash = strSubnet.find_last_of('/');
std::vector<CNetAddr> vIP;
std::string strAddress = strSubnet.substr(0, slash);
// TODO: Use LookupHost(const std::string&, CNetAddr&, bool) instead to just get
// one CNetAddr.
if (LookupHost(strAddress, vIP, 1, false, dns_lookup_function))
{
CNetAddr network = vIP[0];
if (slash != strSubnet.npos)
{
std::string strNetmask = strSubnet.substr(slash + 1);
uint8_t n;
if (ParseUInt8(strNetmask, &n)) {
// If valid number, assume CIDR variable-length subnet masking
ret = CSubNet(network, n);
return ret.IsValid();
}
else // If not a valid number, try full netmask syntax
{
// Never allow lookup for netmask
if (LookupHost(strNetmask, vIP, 1, false, dns_lookup_function)) {
ret = CSubNet(network, vIP[0]);
return ret.IsValid();
}
}
}
else
{
ret = CSubNet(network);
return ret.IsValid();
}
}
return false;
}
bool ConnectSocket(const CService &addrDest, SOCKET& hSocketRet, int nTimeout, bool *outProxyConnectionFailed)
{
proxyType proxy;

83
src/netbase.h

@ -63,10 +63,91 @@ bool GetProxy(enum Network net, proxyType &proxyInfoOut);
bool IsProxy(const CNetAddr &addr);
bool SetNameProxy(const proxyType &addrProxy);
bool HaveNameProxy();
using DNSLookupFn = std::function<std::vector<CNetAddr>(const std::string&, bool)>;
extern DNSLookupFn g_dns_lookup;
/**
* Resolve a host string to its corresponding network addresses.
*
* @param name The string representing a host. Could be a name or a numerical
* IP address (IPv6 addresses in their bracketed form are
* allowed).
* @param[out] vIP The resulting network addresses to which the specified host
* string resolved.
*
* @returns Whether or not the specified host string successfully resolved to
* any resulting network addresses.
*
* @see Lookup(const std::string&, std::vector<CService>&, uint16_t, bool, unsigned int, DNSLookupFn)
* for additional parameter descriptions.
*/
bool LookupHost(const char *pszName, std::vector<CNetAddr>& vIP, unsigned int nMaxSolutions = 0, bool fAllowLookup = true);
/**
* Resolve a host string to its first corresponding network address.
*
* @see LookupHost(const std::string&, std::vector<CNetAddr>&, uint16_t, bool, DNSLookupFn)
* for additional parameter descriptions.
*/
bool LookupHost(const std::string& name, CNetAddr& addr, bool fAllowLookup, DNSLookupFn dns_lookup_function = g_dns_lookup);
/**
* Resolve a service string to its corresponding service.
*
* @param name The string representing a service. Could be a name or a
* numerical IP address (IPv6 addresses should be in their
* disambiguated bracketed form), optionally followed by a uint16_t port
* number. (e.g. example.com:8333 or
* [2001:db8:85a3:8d3:1319:8a2e:370:7348]:420)
* @param[out] vAddr The resulting services to which the specified service string
* resolved.
* @param portDefault The default port for resulting services if not specified
* by the service string.
* @param fAllowLookup Whether or not hostname lookups are permitted. If yes,
* external queries may be performed.
* @param nMaxSolutions The maximum number of results we want, specifying 0
* means "as many solutions as we get."
*
* @returns Whether or not the service string successfully resolved to any
* resulting services.
*/
bool Lookup(const std::string& name, std::vector<CService>& vAddr, uint16_t portDefault, bool fAllowLookup, unsigned int nMaxSolutions, DNSLookupFn dns_lookup_function = g_dns_lookup);
/**
* Resolve a service string to its first corresponding service.
*
* @see Lookup(const std::string&, std::vector<CService>&, uint16_t, bool, unsigned int, DNSLookupFn)
* for additional parameter descriptions.
*/
bool Lookup(const std::string& name, CService& addr, uint16_t portDefault, bool fAllowLookup, DNSLookupFn dns_lookup_function = g_dns_lookup);
bool Lookup(const char *pszName, CService& addr, int portDefault = 0, bool fAllowLookup = true);
bool Lookup(const char *pszName, std::vector<CService>& vAddr, int portDefault = 0, bool fAllowLookup = true, unsigned int nMaxSolutions = 0);
bool LookupNumeric(const char *pszName, CService& addr, int portDefault = 0);
/**
* Resolve a service string with a numeric IP to its first corresponding
* service.
*
* @returns The resulting CService if the resolution was successful, [::]:0 otherwise.
*
* @see Lookup(const std::string&, std::vector<CService>&, uint16_t, bool, unsigned int, DNSLookupFn)
* for additional parameter descriptions.
*/
CService LookupNumeric(const std::string& name, uint16_t portDefault = 0, DNSLookupFn dns_lookup_function = g_dns_lookup);
/**
* Parse and resolve a specified subnet string into the appropriate internal
* representation.
*
* @param strSubnet A string representation of a subnet of the form `network
* address [ "/", ( CIDR-style suffix | netmask ) ]`(e.g.
* `2001:db8::/32`, `192.0.2.0/255.255.255.0`, or `8.8.8.8`).
*
* @returns Whether the operation succeeded or not.
*/
bool LookupSubNet(const std::string& strSubnet, CSubNet& subnet, DNSLookupFn dns_lookup_function = g_dns_lookup);
bool ConnectSocket(const CService &addr, SOCKET& hSocketRet, int nTimeout, bool *outProxyConnectionFailed = 0);
bool ConnectSocketByName(CService &addr, SOCKET& hSocketRet, const char *pszDest, int portDefault, int nTimeout, bool *outProxyConnectionFailed = 0);
/** Return readable error string for a network error code */

248
src/prevector.h

@ -1,17 +1,20 @@
// Copyright (c) 2016-2021 The Hush developers
// Distributed under the GPLv3 software license, see the accompanying
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
#ifndef _HUSH_PREVECTOR_H_
#define _HUSH_PREVECTOR_H_
// Copyright (c) 2015-2020 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_PREVECTOR_H
#define BITCOIN_PREVECTOR_H
#include <util.h>
#include <assert.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <iterator>
#pragma pack(push, 1)
#include <algorithm>
#include <cstddef>
#include <type_traits>
#include <utility>
/** Implements a drop-in replacement for std::vector<T> which stores up to N
* elements directly (without heap allocation). The types Size and Diff are
* used to store element counts, and can be any unsigned + signed type.
@ -130,7 +133,7 @@ public:
typedef const T* pointer;
typedef const T& reference;
typedef std::bidirectional_iterator_tag iterator_category;
const_reverse_iterator(T* ptr_) : ptr(ptr_) {}
const_reverse_iterator(const T* ptr_) : ptr(ptr_) {}
const_reverse_iterator(reverse_iterator x) : ptr(&(*x)) {}
const T& operator*() const { return *ptr; }
const T* operator->() const { return ptr; }
@ -143,19 +146,25 @@ public:
};
private:
size_type _size;
union {
#pragma pack(push, 1)
union direct_or_indirect {
char direct[sizeof(T) * N];
struct {
size_type capacity;
char* indirect;
};
} _union;
size_type capacity;
} indirect_contents;
};
#pragma pack(pop)
alignas(char*) direct_or_indirect _union = {};
size_type _size = 0;
static_assert(alignof(char*) % alignof(size_type) == 0 && sizeof(char*) % alignof(size_type) == 0, "size_type cannot have more restrictive alignment requirement than pointer");
static_assert(alignof(char*) % alignof(T) == 0, "value_type T cannot have more restrictive alignment requirement than pointer");
T* direct_ptr(difference_type pos) { return reinterpret_cast<T*>(_union.direct) + pos; }
const T* direct_ptr(difference_type pos) const { return reinterpret_cast<const T*>(_union.direct) + pos; }
T* indirect_ptr(difference_type pos) { return reinterpret_cast<T*>(_union.indirect) + pos; }
const T* indirect_ptr(difference_type pos) const { return reinterpret_cast<const T*>(_union.indirect) + pos; }
T* indirect_ptr(difference_type pos) { return reinterpret_cast<T*>(_union.indirect_contents.indirect) + pos; }
const T* indirect_ptr(difference_type pos) const { return reinterpret_cast<const T*>(_union.indirect_contents.indirect) + pos; }
bool is_direct() const { return _size <= N; }
void change_capacity(size_type new_capacity) {
@ -173,17 +182,17 @@ private:
/* FIXME: Because malloc/realloc here won't call new_handler if allocation fails, assert
success. These should instead use an allocator or new/delete so that handlers
are called as necessary, but performance would be slightly degraded by doing so. */
_union.indirect = static_cast<char*>(realloc(_union.indirect, ((size_t)sizeof(T)) * new_capacity));
if (!_union.indirect) { new_handler_terminate(); }
_union.capacity = new_capacity;
_union.indirect_contents.indirect = static_cast<char*>(realloc(_union.indirect_contents.indirect, ((size_t)sizeof(T)) * new_capacity));
assert(_union.indirect_contents.indirect);
_union.indirect_contents.capacity = new_capacity;
} else {
char* new_indirect = static_cast<char*>(malloc(((size_t)sizeof(T)) * new_capacity));
if (!new_indirect) { new_handler_terminate(); }
assert(new_indirect);
T* src = direct_ptr(0);
T* dst = reinterpret_cast<T*>(new_indirect);
memcpy(dst, src, size() * sizeof(T));
_union.indirect = new_indirect;
_union.capacity = new_capacity;
_union.indirect_contents.indirect = new_indirect;
_union.indirect_contents.capacity = new_capacity;
_size += N + 1;
}
}
@ -192,16 +201,27 @@ private:
T* item_ptr(difference_type pos) { return is_direct() ? direct_ptr(pos) : indirect_ptr(pos); }
const T* item_ptr(difference_type pos) const { return is_direct() ? direct_ptr(pos) : indirect_ptr(pos); }
void fill(T* dst, ptrdiff_t count, const T& value = T{}) {
std::fill_n(dst, count, value);
}
template<typename InputIterator>
void fill(T* dst, InputIterator first, InputIterator last) {
while (first != last) {
new(static_cast<void*>(dst)) T(*first);
++dst;
++first;
}
}
public:
void assign(size_type n, const T& val) {
clear();
if (capacity() < n) {
change_capacity(n);
}
while (size() < n) {
_size++;
new(static_cast<void*>(item_ptr(size() - 1))) T(val);
}
_size += n;
fill(item_ptr(0), n, val);
}
template<typename InputIterator>
@ -211,60 +231,51 @@ public:
if (capacity() < n) {
change_capacity(n);
}
while (first != last) {
_size++;
new(static_cast<void*>(item_ptr(size() - 1))) T(*first);
++first;
}
_size += n;
fill(item_ptr(0), first, last);
}
prevector() : _size(0) {}
prevector() {}
explicit prevector(size_type n) : _size(0) {
explicit prevector(size_type n) {
resize(n);
}
explicit prevector(size_type n, const T& val = T()) : _size(0) {
explicit prevector(size_type n, const T& val) {
change_capacity(n);
while (size() < n) {
_size++;
new(static_cast<void*>(item_ptr(size() - 1))) T(val);
}
_size += n;
fill(item_ptr(0), n, val);
}
template<typename InputIterator>
prevector(InputIterator first, InputIterator last) : _size(0) {
prevector(InputIterator first, InputIterator last) {
size_type n = last - first;
change_capacity(n);
while (first != last) {
_size++;
new(static_cast<void*>(item_ptr(size() - 1))) T(*first);
++first;
}
_size += n;
fill(item_ptr(0), first, last);
}
prevector(const prevector<N, T, Size, Diff>& other) : _size(0) {
change_capacity(other.size());
const_iterator it = other.begin();
while (it != other.end()) {
_size++;
new(static_cast<void*>(item_ptr(size() - 1))) T(*it);
++it;
}
prevector(const prevector<N, T, Size, Diff>& other) {
size_type n = other.size();
change_capacity(n);
_size += n;
fill(item_ptr(0), other.begin(), other.end());
}
prevector(prevector<N, T, Size, Diff>&& other) {
swap(other);
}
prevector& operator=(const prevector<N, T, Size, Diff>& other) {
if (&other == this) {
return *this;
}
resize(0);
change_capacity(other.size());
const_iterator it = other.begin();
while (it != other.end()) {
_size++;
new(static_cast<void*>(item_ptr(size() - 1))) T(*it);
++it;
}
assign(other.begin(), other.end());
return *this;
}
prevector& operator=(prevector<N, T, Size, Diff>&& other) {
swap(other);
return *this;
}
@ -290,7 +301,7 @@ public:
if (is_direct()) {
return N;
} else {
return _union.capacity;
return _union.indirect_contents.capacity;
}
}
@ -303,17 +314,20 @@ public:
}
void resize(size_type new_size) {
while (size() > new_size) {
item_ptr(size() - 1)->~T();
_size--;
size_type cur_size = size();
if (cur_size == new_size) {
return;
}
if (cur_size > new_size) {
erase(item_ptr(new_size), end());
return;
}
if (new_size > capacity()) {
change_capacity(new_size);
}
while (size() < new_size) {
_size++;
new(static_cast<void*>(item_ptr(size() - 1))) T();
}
ptrdiff_t increase = new_size - cur_size;
fill(item_ptr(cur_size), increase);
_size += increase;
}
void reserve(size_type new_capacity) {
@ -336,10 +350,11 @@ public:
if (capacity() < new_size) {
change_capacity(new_size + (new_size >> 1));
}
memmove(item_ptr(p + 1), item_ptr(p), (size() - p) * sizeof(T));
T* ptr = item_ptr(p);
memmove(ptr + 1, ptr, (size() - p) * sizeof(T));
_size++;
new(static_cast<void*>(item_ptr(p))) T(value);
return iterator(item_ptr(p));
new(static_cast<void*>(ptr)) T(value);
return iterator(ptr);
}
void insert(iterator pos, size_type count, const T& value) {
@ -348,11 +363,10 @@ public:
if (capacity() < new_size) {
change_capacity(new_size + (new_size >> 1));
}
memmove(item_ptr(p + count), item_ptr(p), (size() - p) * sizeof(T));
T* ptr = item_ptr(p);
memmove(ptr + count, ptr, (size() - p) * sizeof(T));
_size += count;
for (size_type i = 0; i < count; i++) {
new(static_cast<void*>(item_ptr(p + i))) T(value);
}
fill(item_ptr(p), count, value);
}
template<typename InputIterator>
@ -363,45 +377,69 @@ public:
if (capacity() < new_size) {
change_capacity(new_size + (new_size >> 1));
}
memmove(item_ptr(p + count), item_ptr(p), (size() - p) * sizeof(T));
T* ptr = item_ptr(p);
memmove(ptr + count, ptr, (size() - p) * sizeof(T));
_size += count;
while (first != last) {
new(static_cast<void*>(item_ptr(p))) T(*first);
++p;
++first;
fill(ptr, first, last);
}
inline void resize_uninitialized(size_type new_size) {
// resize_uninitialized changes the size of the prevector but does not initialize it.
// If size < new_size, the added elements must be initialized explicitly.
if (capacity() < new_size) {
change_capacity(new_size);
_size += new_size - size();
return;
}
if (new_size < size()) {
erase(item_ptr(new_size), end());
} else {
_size += new_size - size();
}
}
iterator erase(iterator pos) {
(*pos).~T();
memmove(&(*pos), &(*pos) + 1, ((char*)&(*end())) - ((char*)(1 + &(*pos))));
_size--;
return pos;
return erase(pos, pos + 1);
}
iterator erase(iterator first, iterator last) {
// Erase is not allowed to the change the object's capacity. That means
// that when starting with an indirectly allocated prevector with
// size and capacity > N, the result may be a still indirectly allocated
// prevector with size <= N and capacity > N. A shrink_to_fit() call is
// necessary to switch to the (more efficient) directly allocated
// representation (with capacity N and size <= N).
iterator p = first;
char* endp = (char*)&(*end());
while (p != last) {
(*p).~T();
_size--;
++p;
if (!std::is_trivially_destructible<T>::value) {
while (p != last) {
(*p).~T();
_size--;
++p;
}
} else {
_size -= last - p;
}
memmove(&(*first), &(*last), endp - ((char*)(&(*last))));
return first;
}
void push_back(const T& value) {
template<typename... Args>
void emplace_back(Args&&... args) {
size_type new_size = size() + 1;
if (capacity() < new_size) {
change_capacity(new_size + (new_size >> 1));
}
new(item_ptr(size())) T(value);
new(item_ptr(size())) T(std::forward<Args>(args)...);
_size++;
}
void push_back(const T& value) {
emplace_back(value);
}
void pop_back() {
_size--;
erase(end() - 1, end());
}
T& front() {
@ -421,20 +459,17 @@ public:
}
void swap(prevector<N, T, Size, Diff>& other) {
if (_size & other._size & 1) {
std::swap(_union.capacity, other._union.capacity);
std::swap(_union.indirect, other._union.indirect);
} else {
std::swap(_union, other._union);
}
std::swap(_union, other._union);
std::swap(_size, other._size);
}
~prevector() {
clear();
if (!std::is_trivially_destructible<T>::value) {
clear();
}
if (!is_direct()) {
free(_union.indirect);
_union.indirect = NULL;
free(_union.indirect_contents.indirect);
_union.indirect_contents.indirect = nullptr;
}
}
@ -486,10 +521,17 @@ public:
if (is_direct()) {
return 0;
} else {
return ((size_t)(sizeof(T))) * _union.capacity;
return ((size_t)(sizeof(T))) * _union.indirect_contents.capacity;
}
}
value_type* data() {
return item_ptr(0);
}
const value_type* data() const {
return item_ptr(0);
}
};
#pragma pack(pop)
#endif
#endif // BITCOIN_PREVECTOR_H

11
src/rpc/net.cpp

@ -583,10 +583,13 @@ UniValue setban(const UniValue& params, bool fHelp, const CPubKey& mypk)
if (params[0].get_str().find("/") != string::npos)
isSubnet = true;
if (!isSubnet)
netAddr = CNetAddr(params[0].get_str());
else
subNet = CSubNet(params[0].get_str());
if (!isSubnet) {
CNetAddr resolved;
LookupHost(params[0].get_str(), resolved, false);
netAddr = resolved;
} else {
LookupSubNet(params[0].get_str(), subNet);
}
if (! (isSubnet ? subNet.IsValid() : netAddr.IsValid()) )
throw JSONRPCError(RPC_CLIENT_NODE_ALREADY_ADDED, "Error: Invalid IP/Subnet");

69
src/serialize.h

@ -237,6 +237,75 @@ enum
SerializationOp(s, CSerActionUnserialize()); \
}
/** Simple wrapper class to serialize objects using a formatter; used by Using(). */
template<typename Formatter, typename T>
class Wrapper
{
static_assert(std::is_lvalue_reference<T>::value, "Wrapper needs an lvalue reference type T");
protected:
T m_object;
public:
explicit Wrapper(T obj) : m_object(obj) {}
template<typename Stream> void Serialize(Stream &s) const { Formatter().Ser(s, m_object); }
template<typename Stream> void Unserialize(Stream &s) { Formatter().Unser(s, m_object); }
};
/** Cause serialization/deserialization of an object to be done using a specified formatter class.
*
* To use this, you need a class Formatter that has public functions Ser(stream, const object&) for
* serialization, and Unser(stream, object&) for deserialization. Serialization routines (inside
* READWRITE, or directly with << and >> operators), can then use Using<Formatter>(object).
*
* This works by constructing a Wrapper<Formatter, T>-wrapped version of object, where T is
* const during serialization, and non-const during deserialization, which maintains const
* correctness.
*/
template<typename Formatter, typename T>
static inline Wrapper<Formatter, T&> Using(T&& t) { return Wrapper<Formatter, T&>(t); }
/** Serialization wrapper class for custom integers and enums.
*
* It permits specifying the serialized size (1 to 8 bytes) and endianness.
*
* Use the big endian mode for values that are stored in memory in native
* byte order, but serialized in big endian notation. This is only intended
* to implement serializers that are compatible with existing formats, and
* its use is not recommended for new data structures.
*/
template<int Bytes, bool BigEndian = false>
struct CustomUintFormatter
{
static_assert(Bytes > 0 && Bytes <= 8, "CustomUintFormatter Bytes out of range");
static constexpr uint64_t MAX = 0xffffffffffffffff >> (8 * (8 - Bytes));
template <typename Stream, typename I> void Ser(Stream& s, I v)
{
if (v < 0 || v > MAX) throw std::ios_base::failure("CustomUintFormatter value out of range");
if (BigEndian) {
uint64_t raw = htobe64(v);
s.write(((const char*)&raw) + 8 - Bytes, Bytes);
} else {
uint64_t raw = htole64(v);
s.write((const char*)&raw, Bytes);
}
}
template <typename Stream, typename I> void Unser(Stream& s, I& v)
{
using U = typename std::conditional<std::is_enum<I>::value, std::underlying_type<I>, std::common_type<I>>::type::type;
static_assert(std::numeric_limits<U>::max() >= MAX && std::numeric_limits<U>::min() <= 0, "Assigned type too small");
uint64_t raw = 0;
if (BigEndian) {
s.read(((char*)&raw) + 8 - Bytes, Bytes);
v = static_cast<I>(be64toh(raw));
} else {
s.read((char*)&raw, Bytes);
v = static_cast<I>(le64toh(raw));
}
}
};
template<int Bytes> using BigEndianFormatter = CustomUintFormatter<Bytes, true>;
template<typename Stream> inline void Serialize(Stream& s, char a ) { ser_writedata8(s, a); } // TODO Get rid of bare char
template<typename Stream> inline void Serialize(Stream& s, int8_t a ) { ser_writedata8(s, a); }
template<typename Stream> inline void Serialize(Stream& s, uint8_t a ) { ser_writedata8(s, a); }

Loading…
Cancel
Save