Inside Bitcoin
Opening the selected file…
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
502enum Network CNetAddr::GetNetwork() const503{504if (IsInternal())505return NET_INTERNAL;507if (!IsRoutable())508return NET_UNROUTABLE;510return m_net;511}513static std::string IPv4ToString(std::span<const uint8_t> a)514{515return strprintf("%u.%u.%u.%u", a[0], a[1], a[2], a[3]);516}518// Return an IPv6 address text representation with zero compression as described in RFC 5952519// ("A Recommendation for IPv6 Address Text Representation").520static std::string IPv6ToString(std::span<const uint8_t> a, uint32_t scope_id)521{522assert(a.size() == ADDR_IPV6_SIZE);523const std::array groups{524ReadBE16(&a[0]),525ReadBE16(&a[2]),526ReadBE16(&a[4]),527ReadBE16(&a[6]),528ReadBE16(&a[8]),529ReadBE16(&a[10]),530ReadBE16(&a[12]),531ReadBE16(&a[14]),532};534// The zero compression implementation is inspired by Rust's std::net::Ipv6Addr, see535// https://github.com/rust-lang/rust/blob/cc4103089f40a163f6d143f06359cba7043da29b/library/std/src/net/ip.rs#L1635-L1683536struct ZeroSpan {537size_t start_index{0};538size_t len{0};539};541// Find longest sequence of consecutive all-zero fields. Use first zero sequence if two or more542// zero sequences of equal length are found.543ZeroSpan longest, current;544for (size_t i{0}; i < groups.size(); ++i) {545if (groups[i] != 0) {546current = {i + 1, 0};547continue;548}549current.len += 1;550if (current.len > longest.len) {551longest = current;552}553}555std::string r;556r.reserve(39);557for (size_t i{0}; i < groups.size(); ++i) {558// Replace the longest sequence of consecutive all-zero fields with two colons ("::").559if (longest.len >= 2 && i >= longest.start_index && i < longest.start_index + longest.len) {560if (i == longest.start_index) {561r += "::";562}563continue;564}565r += strprintf("%s%x", ((!r.empty() && r.back() != ':') ? ":" : ""), groups[i]);566}568if (scope_id != 0) {569r += strprintf("%%%u", scope_id);570}572return r;573}575std::string OnionToString(std::span<const uint8_t> addr)576{577uint8_t checksum[torv3::CHECKSUM_LEN];578torv3::Checksum(addr, checksum);579// TORv3 onion_address = base32(PUBKEY | CHECKSUM | VERSION) + ".onion"580prevector<torv3::TOTAL_LEN, uint8_t> address{addr.begin(), addr.end()};581address.insert(address.end(), checksum, checksum + torv3::CHECKSUM_LEN);582address.insert(address.end(), torv3::VERSION, torv3::VERSION + sizeof(torv3::VERSION));583return EncodeBase32(address) + ".onion";584}586std::string CNetAddr::ToStringAddr() const587{588switch (m_net) {589case NET_IPV4:590return IPv4ToString(m_addr);591case NET_IPV6:592return IPv6ToString(m_addr, m_scope_id);593case NET_ONION:594return OnionToString(m_addr);595case NET_I2P:596return EncodeBase32(m_addr, false /* don't pad with = */) + ".b32.i2p";597case NET_CJDNS:598return IPv6ToString(m_addr, 0);599case NET_INTERNAL:600return EncodeBase32(m_addr) + ".internal";601case NET_UNROUTABLE: // m_net is never and should not be set to NET_UNROUTABLE602case NET_MAX: // m_net is never and should not be set to NET_MAX603assert(false);604} // no default case, so the compiler can warn about missing cases606assert(false);607}609bool operator==(const CNetAddr& a, const CNetAddr& b)610{611return a.m_net == b.m_net && a.m_addr == b.m_addr;612}614bool operator<(const CNetAddr& a, const CNetAddr& b)615{616return std::tie(a.m_net, a.m_addr) < std::tie(b.m_net, b.m_addr);617}619/**620* Try to get our IPv4 address.621*622* @param[out] pipv4Addr The in_addr struct to which to copy.623*624* @returns Whether or not the operation was successful, in particular, whether625* or not our address was an IPv4 address.626*627* @see CNetAddr::IsIPv4()628*/629bool CNetAddr::GetInAddr(struct in_addr* pipv4Addr) const630{631if (!IsIPv4())632return false;633assert(sizeof(*pipv4Addr) == m_addr.size());634memcpy(pipv4Addr, m_addr.data(), m_addr.size());635return true;636}638/**639* Try to get our IPv6 (or CJDNS) address.640*641* @param[out] pipv6Addr The in6_addr struct to which to copy.642*643* @returns Whether or not the operation was successful, in particular, whether644* or not our address was an IPv6 address.645*646* @see CNetAddr::IsIPv6()647*/648bool CNetAddr::GetIn6Addr(struct in6_addr* pipv6Addr) const649{650if (!IsIPv6() && !IsCJDNS()) {651return false;652}653assert(sizeof(*pipv6Addr) == m_addr.size());654memcpy(pipv6Addr, m_addr.data(), m_addr.size());655return true;656}658bool CNetAddr::HasLinkedIPv4() const659{660return IsRoutable() && (IsIPv4() || IsRFC6145() || IsRFC6052() || IsRFC3964() || IsRFC4380());661}663uint32_t CNetAddr::GetLinkedIPv4() const664{665if (IsIPv4()) {666return ReadBE32(m_addr.data());667} else if (IsRFC6052() || IsRFC6145()) {668// mapped IPv4, SIIT translated IPv4: the IPv4 address is the last 4 bytes of the address669return ReadBE32(std::span{m_addr}.last(ADDR_IPV4_SIZE).data());670} else if (IsRFC3964()) {671// 6to4 tunneled IPv4: the IPv4 address is in bytes 2-6672return ReadBE32(std::span{m_addr}.subspan(2, ADDR_IPV4_SIZE).data());673} else if (IsRFC4380()) {674// Teredo tunneled IPv4: the IPv4 address is in the last 4 bytes of the address, but bitflipped675return ~ReadBE32(std::span{m_addr}.last(ADDR_IPV4_SIZE).data());676}677assert(false);678}680Network CNetAddr::GetNetClass() const681{682// Make sure that if we return NET_IPV6, then IsIPv6() is true. The callers expect that.684// Check for "internal" first because such addresses are also !IsRoutable()685// and we don't want to return NET_UNROUTABLE in that case.686if (IsInternal()) {687return NET_INTERNAL;688}689if (!IsRoutable()) {690return NET_UNROUTABLE;691}692if (HasLinkedIPv4()) {693return NET_IPV4;694}695return m_net;696}698std::vector<unsigned char> CNetAddr::GetAddrBytes() const699{700if (IsAddrV1Compatible()) {701uint8_t serialized[V1_SERIALIZATION_SIZE];702SerializeV1Array(serialized);703return {std::begin(serialized), std::end(serialized)};704}705return std::vector<unsigned char>(m_addr.begin(), m_addr.end());706}708// private extensions to enum Network, only returned by GetExtNetwork,709// and only used in GetReachabilityFrom710static const int NET_TEREDO = NET_MAX;711int static GetExtNetwork(const CNetAddr& addr)712{713if (addr.IsRFC4380())714return NET_TEREDO;715return addr.GetNetwork();716}718/** Calculates a metric for how reachable (*this) is from a given partner */719int CNetAddr::GetReachabilityFrom(const CNetAddr& paddrPartner) const720{721enum Reachability {722REACH_UNREACHABLE,723REACH_DEFAULT,724REACH_TEREDO,725REACH_IPV6_WEAK,726REACH_IPV4,727REACH_IPV6_STRONG,728REACH_PRIVATE729};731if (!IsRoutable() || IsInternal())732return REACH_UNREACHABLE;734int ourNet = GetExtNetwork(*this);735int theirNet = GetExtNetwork(paddrPartner);736bool fTunnel = IsRFC3964() || IsRFC6052() || IsRFC6145();738switch(theirNet) {739case NET_IPV4:740switch(ourNet) {741default: return REACH_DEFAULT;742case NET_IPV4: return REACH_IPV4;743}744case NET_IPV6:745switch(ourNet) {746default: return REACH_DEFAULT;747case NET_TEREDO: return REACH_TEREDO;748case NET_IPV4: return REACH_IPV4;749case NET_IPV6: return fTunnel ? REACH_IPV6_WEAK : REACH_IPV6_STRONG; // only prefer giving our IPv6 address if it's not tunnelled750}