Inside Bitcoin
Opening the selected file…
Inside Bitcoin
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.
1// Copyright (c) 2024-present The Bitcoin Core developers2// Distributed under the MIT software license, see the accompanying3// file COPYING or http://www.opensource.org/licenses/mit-license.php.5#include <common/pcp.h>6#include <netbase.h>7#include <test/util/logging.h>8#include <test/util/common.h>9#include <test/util/setup_common.h>10#include <util/time.h>12#include <boost/test/unit_test.hpp>14#include <algorithm>15#include <deque>17using namespace std::literals;19static CThreadInterrupt g_interrupt;21/// UDP test server operation.22struct TestOp {23std::chrono::milliseconds delay;24enum Op {25SEND, // Expect send (with optional data)26RECV, // Expect receive (with data)27NOP, // Just delay28} op;29std::vector<uint8_t> data;31//! Injected error.32//! Set this field to a non-zero value to return the errno code from send or receive operation.33int error;35TestOp(std::chrono::milliseconds delay_in, Op op_in, const std::vector<uint8_t> &data_in, int error_in):36delay(delay_in), op(op_in), data(data_in), error(error_in) {}37};39/// Save the value of CreateSock and restore when the test ends.40class PCPTestingSetup : public BasicTestingSetup41{42public:43explicit PCPTestingSetup(const ChainType chainType = ChainType::MAIN,44TestOpts opts = {})45: BasicTestingSetup{chainType, opts},46m_create_sock_orig{CreateSock}47{48const std::optional<CService> local_ipv4{Lookup("192.168.0.6", 1, false)};49const std::optional<CService> local_ipv6{Lookup("2a10:1234:5678:9abc:def0:1234:5678:9abc", 1, false)};50const std::optional<CService> gateway_ipv4{Lookup("192.168.0.1", 1, false)};51const std::optional<CService> gateway_ipv6{Lookup("2a10:1234:5678:9abc:def0:0000:0000:0000", 1, false)};52BOOST_REQUIRE(local_ipv4);53BOOST_REQUIRE(local_ipv6);54BOOST_REQUIRE(gateway_ipv4);55BOOST_REQUIRE(gateway_ipv6);56default_local_ipv4 = *local_ipv4;57default_local_ipv6 = *local_ipv6;58default_gateway_ipv4 = *gateway_ipv4;59default_gateway_ipv6 = *gateway_ipv6;61struct in_addr inaddr_any;62inaddr_any.s_addr = htonl(INADDR_ANY);63bind_any_ipv4 = CNetAddr(inaddr_any);64}66~PCPTestingSetup()67{68CreateSock = m_create_sock_orig;69MockableSteadyClock::ClearMockTime();70}72// Default testing nonce.73static constexpr PCPMappingNonce TEST_NONCE{0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc};74// Default network addresses.75CNetAddr default_local_ipv4;76CNetAddr default_local_ipv6;77CNetAddr default_gateway_ipv4;78CNetAddr default_gateway_ipv6;79// IPv4 bind80CNetAddr bind_any_ipv4;81private:82const decltype(CreateSock) m_create_sock_orig;83};85/** Simple scripted UDP server emulation for testing.86*/87class PCPTestSock final : public Sock88{89public:90// Note: we awkwardly mark all methods as const, and properties as mutable,91// because Sock expects all networking calls to be const.92explicit PCPTestSock(const CNetAddr &local_ip, const CNetAddr &gateway_ip, const std::vector<TestOp> &script)93: Sock{INVALID_SOCKET},94m_script(script),95m_local_ip(local_ip),96m_gateway_ip(gateway_ip)97{98PrepareOp();99}101PCPTestSock& operator=(Sock&& other) override102{103assert(false && "Move of Sock into PCPTestSock not allowed.");104return *this;105}107ssize_t Send(const void* data, size_t len, int) const override {108if (!m_connected) return -1;109std::span in_pkt = std::span(static_cast<const uint8_t*>(data), len);110if (AtEndOfScript() || CurOp().op != TestOp::SEND) {111// Ignore sends after end of script, or sends when we expect a receive.112FailScript();113return len;114}115if (CurOp().error) return -1; // Inject failure116if (CurOp().data.empty() || std::ranges::equal(CurOp().data, in_pkt)) {117AdvanceOp();118} else {119// Wrong send, fail script120FailScript();121}122return len;123}125ssize_t Recv(void* buf, size_t len, int flags) const override126{127if (!m_connected || AtEndOfScript() || CurOp().op != TestOp::RECV || m_time_left != 0s) {128return -1;129}130if (CurOp().error) return -1; // Inject failure131const auto &recv_pkt = CurOp().data;132const size_t consume_bytes{std::min(len, recv_pkt.size())};133std::memcpy(buf, recv_pkt.data(), consume_bytes);134if ((flags & MSG_PEEK) == 0) {135AdvanceOp();136}137return consume_bytes;138}140int Connect(const sockaddr* sa, socklen_t sa_len) const override {141CService service;142if (service.SetSockAddr(sa, sa_len) && service == CService(m_gateway_ip, 5351)) {143if (m_bound.IsBindAny()) { // If bind-any, bind to local ip.144m_bound = CService(m_local_ip, 0);145}146if (m_bound.GetPort() == 0) { // If no port assigned, assign port 1.147m_bound = CService(m_bound, 1);148}149m_connected = true;150return 0;151}152return -1;153}155int Bind(const sockaddr* sa, socklen_t sa_len) const override {156CService service;157if (service.SetSockAddr(sa, sa_len)) {158// Can only bind to one of our local ips159if (!service.IsBindAny() && service != m_local_ip) {160return -1;161}162m_bound = service;163return 0;164}165return -1;166}168int Listen(int) const override { return -1; }170std::unique_ptr<Sock> Accept(sockaddr* addr, socklen_t* addr_len) const override171{172return nullptr;173};175int GetSockOpt(int level, int opt_name, void* opt_val, socklen_t* opt_len) const override176{177return -1;178}180int SetSockOpt(int, int, const void*, socklen_t) const override { return 0; }182int GetSockName(sockaddr* name, socklen_t* name_len) const override183{184// Return the address we've been bound to.185return m_bound.GetSockAddr(name, name_len) ? 0 : -1;186}188bool SetNonBlocking() const override { return true; }190bool IsSelectable() const override { return true; }192bool Wait(std::chrono::milliseconds timeout,193Event requested,194Event* occurred = nullptr) const override195{196// Only handles receive events.197if (AtEndOfScript() || requested != Sock::RecvEvent) {198m_clock += timeout;199} else {200std::chrono::milliseconds delay = std::min(m_time_left, timeout);201m_clock += delay;202m_time_left -= delay;203if (CurOp().op == TestOp::RECV && m_time_left == 0s && occurred != nullptr) {204*occurred = Sock::RecvEvent;205}206if (CurOp().op == TestOp::NOP) {207// This was a pure delay operation, move to the next op.208AdvanceOp();209}210}211return true;212}214bool WaitMany(std::chrono::milliseconds timeout, EventsPerSock& events_per_sock) const override215{216return false;217}219bool IsConnected(std::string&) const override220{221return true;222}224private:225const std::vector<TestOp> m_script;226mutable size_t m_script_ptr = 0;227mutable std::chrono::milliseconds m_time_left;228mutable FakeSteadyClock m_clock{};229mutable bool m_connected{false};230mutable CService m_bound;231mutable CNetAddr m_local_ip;232mutable CNetAddr m_gateway_ip;234bool AtEndOfScript() const { return m_script_ptr == m_script.size(); }235const TestOp &CurOp() const {236BOOST_REQUIRE(m_script_ptr < m_script.size());237return m_script[m_script_ptr];238}240void PrepareOp() const {241if (AtEndOfScript()) return;242m_time_left = CurOp().delay;243}245void AdvanceOp() const246{247m_script_ptr += 1;248PrepareOp();249}
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.