diff --git a/src/addrman.h b/src/addrman.h index 2a77bb117..e408cc8dc 100644 --- a/src/addrman.h +++ b/src/addrman.h @@ -34,6 +34,7 @@ #include #include #include +#include #include /** @@ -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 - 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 s(&s_, s_.GetType(), s_.GetVersion() | ADDRV2_FORMAT); + + s << static_cast(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(INCOMPATIBILITY_BASE + lowest_compatible); s << nKey; s << nNew; s << nTried; @@ -397,22 +428,40 @@ public: } template - 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>(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 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(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 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> 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; } } } diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 0e4e42c2b..e379f60b6 100644 --- a/src/httpserver.cpp +++ b/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& 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; } diff --git a/src/init.cpp b/src/init.cpp index 2624945bf..bb5e790ba 100644 --- a/src/init.cpp +++ b/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)); diff --git a/src/net.cpp b/src/net.cpp index 808df788c..dece35877 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -206,7 +206,7 @@ static std::vector convertSeed6(const std::vector &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; } } diff --git a/src/netaddress.h b/src/netaddress.h index 0f8d99cd8..e6d7c5ddc 100644 --- a/src/netaddress.h +++ b/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 + 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>(obj.port)); + ADD_SERIALIZE_METHODS; + template + inline void SerializationOp(Stream& s, Operation ser_action) { + //READWRITE(CNetAddr, obj); + READWRITE(*(CNetAddr*)this); + READWRITE(Using>(port)); } }; diff --git a/src/netbase.cpp b/src/netbase.cpp index 8c6025d4b..8b8ba3bb0 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -217,6 +217,19 @@ bool LookupHost(const char *pszName, std::vector& 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 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& 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 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; diff --git a/src/netbase.h b/src/netbase.h index 2a9df47ef..2f5763c88 100644 --- a/src/netbase.h +++ b/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(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&, uint16_t, bool, unsigned int, DNSLookupFn) + * for additional parameter descriptions. + */ bool LookupHost(const char *pszName, std::vector& 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&, 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& 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&, 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& 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&, 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 */ diff --git a/src/prevector.h b/src/prevector.h index 5d71e298b..aa20efaaa 100644 --- a/src/prevector.h +++ b/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 #include #include #include #include -#include -#pragma pack(push, 1) +#include +#include +#include +#include + /** Implements a drop-in replacement for std::vector 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(_union.direct) + pos; } const T* direct_ptr(difference_type pos) const { return reinterpret_cast(_union.direct) + pos; } - T* indirect_ptr(difference_type pos) { return reinterpret_cast(_union.indirect) + pos; } - const T* indirect_ptr(difference_type pos) const { return reinterpret_cast(_union.indirect) + pos; } + T* indirect_ptr(difference_type pos) { return reinterpret_cast(_union.indirect_contents.indirect) + pos; } + const T* indirect_ptr(difference_type pos) const { return reinterpret_cast(_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(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(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(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(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 + void fill(T* dst, InputIterator first, InputIterator last) { + while (first != last) { + new(static_cast(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(item_ptr(size() - 1))) T(val); - } + _size += n; + fill(item_ptr(0), n, val); } template @@ -211,60 +231,51 @@ public: if (capacity() < n) { change_capacity(n); } - while (first != last) { - _size++; - new(static_cast(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(item_ptr(size() - 1))) T(val); - } + _size += n; + fill(item_ptr(0), n, val); } template - 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(item_ptr(size() - 1))) T(*first); - ++first; - } + _size += n; + fill(item_ptr(0), first, last); } - prevector(const prevector& other) : _size(0) { - change_capacity(other.size()); - const_iterator it = other.begin(); - while (it != other.end()) { - _size++; - new(static_cast(item_ptr(size() - 1))) T(*it); - ++it; - } + prevector(const prevector& other) { + size_type n = other.size(); + change_capacity(n); + _size += n; + fill(item_ptr(0), other.begin(), other.end()); + } + + prevector(prevector&& other) { + swap(other); } prevector& operator=(const prevector& 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(item_ptr(size() - 1))) T(*it); - ++it; - } + assign(other.begin(), other.end()); + return *this; + } + + prevector& operator=(prevector&& 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(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(item_ptr(p))) T(value); - return iterator(item_ptr(p)); + new(static_cast(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(item_ptr(p + i))) T(value); - } + fill(item_ptr(p), count, value); } template @@ -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(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::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 + 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)...); _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& 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::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 diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index 46c3cf24d..0f2b9fb34 100644 --- a/src/rpc/net.cpp +++ b/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"); diff --git a/src/serialize.h b/src/serialize.h index c2f8185f4..eee6dbc5a 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -237,6 +237,75 @@ enum SerializationOp(s, CSerActionUnserialize()); \ } +/** Simple wrapper class to serialize objects using a formatter; used by Using(). */ +template +class Wrapper +{ + static_assert(std::is_lvalue_reference::value, "Wrapper needs an lvalue reference type T"); +protected: + T m_object; +public: + explicit Wrapper(T obj) : m_object(obj) {} + template void Serialize(Stream &s) const { Formatter().Ser(s, m_object); } + template 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(object). + * + * This works by constructing a Wrapper-wrapped version of object, where T is + * const during serialization, and non-const during deserialization, which maintains const + * correctness. + */ +template +static inline Wrapper Using(T&& t) { return Wrapper(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 +struct CustomUintFormatter +{ + static_assert(Bytes > 0 && Bytes <= 8, "CustomUintFormatter Bytes out of range"); + static constexpr uint64_t MAX = 0xffffffffffffffff >> (8 * (8 - Bytes)); + + template 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 void Unser(Stream& s, I& v) + { + using U = typename std::conditional::value, std::underlying_type, std::common_type>::type::type; + static_assert(std::numeric_limits::max() >= MAX && std::numeric_limits::min() <= 0, "Assigned type too small"); + uint64_t raw = 0; + if (BigEndian) { + s.read(((char*)&raw) + 8 - Bytes, Bytes); + v = static_cast(be64toh(raw)); + } else { + s.read((char*)&raw, Bytes); + v = static_cast(le64toh(raw)); + } + } +}; + +template using BigEndianFormatter = CustomUintFormatter; template inline void Serialize(Stream& s, char a ) { ser_writedata8(s, a); } // TODO Get rid of bare char template inline void Serialize(Stream& s, int8_t a ) { ser_writedata8(s, a); } template inline void Serialize(Stream& s, uint8_t a ) { ser_writedata8(s, a); }