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.
1// Copyright (c) 2015-present The Bitcoin Core developers2// Copyright (c) 2017 The Zcash developers3// Distributed under the MIT software license, see the accompanying4// file COPYING or http://www.opensource.org/licenses/mit-license.php.6#include <torcontrol.h>8#include <chainparams.h>9#include <chainparamsbase.h>10#include <common/args.h>11#include <compat/compat.h>12#include <crypto/hmac_sha256.h>13#include <net.h>14#include <netaddress.h>15#include <netbase.h>16#include <random.h>17#include <tinyformat.h>18#include <util/check.h>19#include <util/fs.h>20#include <util/log.h>21#include <util/readwritefile.h>22#include <util/strencodings.h>23#include <util/string.h>24#include <util/thread.h>25#include <util/time.h>27#include <algorithm>28#include <cassert>29#include <chrono>30#include <cstdint>31#include <cstdlib>32#include <deque>33#include <functional>34#include <map>35#include <optional>36#include <set>37#include <thread>38#include <utility>39#include <vector>41using util::ReplaceAll;42using util::SplitString;43using util::ToString;45/** Default control ip and port */46const std::string DEFAULT_TOR_CONTROL = "127.0.0.1:" + ToString(DEFAULT_TOR_CONTROL_PORT);47/** Tor cookie size (from control-spec.txt) */48constexpr int TOR_COOKIE_SIZE = 32;49/** Size of client/server nonce for SAFECOOKIE */50constexpr int TOR_NONCE_SIZE = 32;51/** For computing server_hash in SAFECOOKIE */52static const std::string TOR_SAFE_SERVERKEY = "Tor safe cookie authentication server-to-controller hash";53/** For computing clientHash in SAFECOOKIE */54static const std::string TOR_SAFE_CLIENTKEY = "Tor safe cookie authentication controller-to-server hash";55/** Exponential backoff configuration - initial timeout in seconds */56constexpr std::chrono::duration<double> RECONNECT_TIMEOUT_START{1.0};57/** Exponential backoff configuration - growth factor */58constexpr double RECONNECT_TIMEOUT_EXP = 1.5;59/** Maximum reconnect timeout in seconds to prevent excessive delays */60constexpr std::chrono::duration<double> RECONNECT_TIMEOUT_MAX{600.0};61/** Maximum length for lines received on TorControlConnection.62* tor-control-spec.txt mentions that there is explicitly no limit defined to line length,63* this is belt-and-suspenders sanity limit to prevent memory exhaustion.64*/65constexpr int MAX_LINE_LENGTH = 100000;66/** Maximum number of lines received on TorControlConnection per reply to avoid67* memory exhaustion. The largest expected now is 5 (PROTOCOLINFO), but future68* changes to this file might need to re-evaluate MAX_LINE_COUNT.69*/70constexpr int MAX_LINE_COUNT = 1000;71/** Timeout for socket operations */72constexpr auto SOCKET_SEND_TIMEOUT = 10s;74/****** Low-level TorControlConnection ********/76TorControlConnection::TorControlConnection(CThreadInterrupt& interrupt)77: m_interrupt(interrupt)78{79}81TorControlConnection::~TorControlConnection()82{83Disconnect();84}86bool TorControlConnection::Connect(const std::string& tor_control_center)87{88if (m_sock) {89Disconnect();90}92std::optional<CService> control_service = Lookup(tor_control_center, DEFAULT_TOR_CONTROL_PORT, fNameLookup);93if (!control_service.has_value()) {94LogWarning("tor: Failed to look up control center %s", tor_control_center);95return false;96}98m_sock = ConnectDirectly(control_service.value(), /*manual_connection=*/true);99if (!m_sock) {100LogWarning("tor: Error connecting to address %s", tor_control_center);101return false;102}104m_recv_buffer.clear();105m_message.Clear();106m_reply_handlers.clear();108LogDebug(BCLog::TOR, "Successfully connected to Tor control port");109return true;110}112void TorControlConnection::Disconnect()113{114m_sock.reset();115m_recv_buffer.clear();116m_message.Clear();117m_reply_handlers.clear();118}120bool TorControlConnection::IsConnected() const121{122if (!m_sock) return false;123std::string errmsg;124const bool connected{m_sock->IsConnected(errmsg)};125if (!connected && !errmsg.empty()) {126LogDebug(BCLog::TOR, "Connection check failed: %s", errmsg);127}128return connected;129}131bool TorControlConnection::WaitForData(std::chrono::milliseconds timeout)132{133if (!m_sock) return false;135Sock::Event event{0};136if (!m_sock->Wait(timeout, Sock::RecvEvent, &event)) {137return false;138}139if (event & Sock::ErrorEvent) {140LogDebug(BCLog::TOR, "Socket error detected");141Disconnect();142return false;143}145return (event & Sock::RecvEvent);146}148bool TorControlConnection::ReceiveAndProcess()149{150if (!m_sock) return false;152char buf[4096];153ssize_t nread = m_sock->Recv(buf, sizeof(buf), MSG_DONTWAIT);155if (nread < 0) {156int err = WSAGetLastError();157if (err == WSAEWOULDBLOCK || err == WSAEINTR || err == WSAEINPROGRESS) {158// No data available currently159return true;160}161LogWarning("tor: Error reading from socket: %s", NetworkErrorString(err));162return false;163}165if (nread == 0) {166LogDebug(BCLog::TOR, "End of stream");167return false;168}170m_recv_buffer.insert(m_recv_buffer.end(), buf, buf + nread);171try {172return ProcessBuffer();173} catch (const std::runtime_error& e) {174LogWarning("tor: Error processing receive buffer: %s", e.what());175return false;176}177}179bool TorControlConnection::ProcessBuffer()180{181util::LineReader reader(m_recv_buffer, MAX_LINE_LENGTH);183while (auto line = reader.ReadLine()) {184if (m_message.lines.size() == MAX_LINE_COUNT) {185throw std::runtime_error(strprintf("Control port reply exceeded %d lines, disconnecting", MAX_LINE_COUNT));186}187// Skip short lines188if (line->size() < 4) continue;190// Parse: <code><separator><data>191// <status>(-|+| )<data>192m_message.code = ToIntegral<int>(line->substr(0, 3)).value_or(0);193m_message.lines.emplace_back(line->substr(4));194char separator = (*line)[3]; // '-', '+', or ' '196if (separator == ' ') {197if (m_message.code >= 600) {198// Async notifications are currently unused199// Synchronous and asynchronous messages are never interleaved200LogDebug(BCLog::TOR, "Received async notification %i", m_message.code);201} else if (!m_reply_handlers.empty()) {202// Invoke reply handler with message203m_reply_handlers.front()(*this, m_message);204m_reply_handlers.pop_front();205} else {206LogDebug(BCLog::TOR, "Received unexpected sync reply %i", m_message.code);207}208m_message.Clear();209}210}212m_recv_buffer.erase(m_recv_buffer.begin(), m_recv_buffer.begin() + reader.Consumed());213return true;214}216bool TorControlConnection::Command(const std::string &cmd, const ReplyHandlerCB& reply_handler)217{218if (!m_sock) return false;220std::string command = cmd + "\r\n";221try {222m_sock->SendComplete(std::span<const char>{command}, SOCKET_SEND_TIMEOUT, m_interrupt);223} catch (const std::runtime_error& e) {224LogWarning("tor: Error sending command: %s", e.what());225return false;226}228m_reply_handlers.push_back(reply_handler);229return true;230}232/****** General parsing utilities ********/234/* Split reply line in the form 'AUTH METHODS=...' into a type235* 'AUTH' and arguments 'METHODS=...'.236* Grammar is implicitly defined in https://spec.torproject.org/control-spec by237* the server reply formats for PROTOCOLINFO (S3.21) and AUTHCHALLENGE (S3.24).238*/239std::pair<std::string,std::string> SplitTorReplyLine(const std::string &s)240{241size_t ptr=0;242std::string type;243while (ptr < s.size() && s[ptr] != ' ') {244type.push_back(s[ptr]);245++ptr;246}247if (ptr < s.size())248++ptr; // skip ' '249return make_pair(type, s.substr(ptr));250}