LCOV - code coverage report
Current view: top level - src - torcontrol.cpp (source / functions) Coverage Total Hit
Test: test_bitcoin_coverage.info Lines: 27.7 % 404 112
Test Date: 2026-04-25 06:24:37 Functions: 44.4 % 27 12
Branches: 14.3 % 760 109

             Branch data     Line data    Source code
       1                 :             : // Copyright (c) 2015-present The Bitcoin Core developers
       2                 :             : // Copyright (c) 2017 The Zcash developers
       3                 :             : // Distributed under the MIT software license, see the accompanying
       4                 :             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       5                 :             : 
       6                 :             : #include <torcontrol.h>
       7                 :             : 
       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 <logging.h>
      14                 :             : #include <net.h>
      15                 :             : #include <netaddress.h>
      16                 :             : #include <netbase.h>
      17                 :             : #include <random.h>
      18                 :             : #include <tinyformat.h>
      19                 :             : #include <util/check.h>
      20                 :             : #include <util/fs.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>
      26                 :             : 
      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>
      40                 :             : 
      41                 :             : using util::ReplaceAll;
      42                 :             : using util::SplitString;
      43                 :             : using util::ToString;
      44                 :             : 
      45                 :             : /** Default control ip and port */
      46                 :             : const std::string DEFAULT_TOR_CONTROL = "127.0.0.1:" + ToString(DEFAULT_TOR_CONTROL_PORT);
      47                 :             : /** Tor cookie size (from control-spec.txt) */
      48                 :             : constexpr int TOR_COOKIE_SIZE = 32;
      49                 :             : /** Size of client/server nonce for SAFECOOKIE */
      50                 :             : constexpr int TOR_NONCE_SIZE = 32;
      51                 :             : /** For computing server_hash in SAFECOOKIE */
      52                 :             : static const std::string TOR_SAFE_SERVERKEY = "Tor safe cookie authentication server-to-controller hash";
      53                 :             : /** For computing clientHash in SAFECOOKIE */
      54                 :             : static const std::string TOR_SAFE_CLIENTKEY = "Tor safe cookie authentication controller-to-server hash";
      55                 :             : /** Exponential backoff configuration - initial timeout in seconds */
      56                 :             : constexpr std::chrono::duration<double> RECONNECT_TIMEOUT_START{1.0};
      57                 :             : /** Exponential backoff configuration - growth factor */
      58                 :             : constexpr double RECONNECT_TIMEOUT_EXP = 1.5;
      59                 :             : /** Maximum reconnect timeout in seconds to prevent excessive delays */
      60                 :             : constexpr 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                 :             :  */
      65                 :             : constexpr int MAX_LINE_LENGTH = 100000;
      66                 :             : /** Maximum number of lines received on TorControlConnection per reply to avoid
      67                 :             :  * memory exhaustion. The largest expected now is 5 (PROTOCOLINFO), but future
      68                 :             :  * changes to this file might need to re-evaluate MAX_LINE_COUNT.
      69                 :             :  */
      70                 :             : constexpr int MAX_LINE_COUNT = 1000;
      71                 :             : /** Timeout for socket operations */
      72                 :             : constexpr auto SOCKET_SEND_TIMEOUT = 10s;
      73                 :             : 
      74                 :             : /****** Low-level TorControlConnection ********/
      75                 :             : 
      76                 :           1 : TorControlConnection::TorControlConnection(CThreadInterrupt& interrupt)
      77         [ +  - ]:           1 :     : m_interrupt(interrupt)
      78                 :             : {
      79                 :           1 : }
      80                 :             : 
      81                 :           1 : TorControlConnection::~TorControlConnection()
      82                 :             : {
      83                 :           1 :     Disconnect();
      84                 :           1 : }
      85                 :             : 
      86                 :           0 : bool TorControlConnection::Connect(const std::string& tor_control_center)
      87                 :             : {
      88         [ #  # ]:           0 :     if (m_sock) {
      89                 :           0 :         Disconnect();
      90                 :             :     }
      91                 :             : 
      92         [ #  # ]:           0 :     std::optional<CService> control_service = Lookup(tor_control_center, DEFAULT_TOR_CONTROL_PORT, fNameLookup);
      93         [ #  # ]:           0 :     if (!control_service.has_value()) {
      94         [ #  # ]:           0 :         LogWarning("tor: Failed to look up control center %s", tor_control_center);
      95                 :             :         return false;
      96                 :             :     }
      97                 :             : 
      98         [ #  # ]:           0 :     m_sock = ConnectDirectly(control_service.value(), /*manual_connection=*/true);
      99         [ #  # ]:           0 :     if (!m_sock) {
     100         [ #  # ]:           0 :         LogWarning("tor: Error connecting to address %s", tor_control_center);
     101                 :             :         return false;
     102                 :             :     }
     103                 :             : 
     104         [ #  # ]:           0 :     m_recv_buffer.clear();
     105                 :           0 :     m_message.Clear();
     106                 :           0 :     m_reply_handlers.clear();
     107                 :             : 
     108   [ #  #  #  #  :           0 :     LogDebug(BCLog::TOR, "Successfully connected to Tor control port");
                   #  # ]
     109                 :             :     return true;
     110                 :           0 : }
     111                 :             : 
     112                 :           1 : void TorControlConnection::Disconnect()
     113                 :             : {
     114         [ -  + ]:           1 :     m_sock.reset();
     115         [ -  + ]:           1 :     m_recv_buffer.clear();
     116                 :           1 :     m_message.Clear();
     117                 :           1 :     m_reply_handlers.clear();
     118                 :           1 : }
     119                 :             : 
     120                 :           0 : bool TorControlConnection::IsConnected() const
     121                 :             : {
     122         [ #  # ]:           0 :     if (!m_sock) return false;
     123         [ #  # ]:           0 :     std::string errmsg;
     124         [ #  # ]:           0 :     const bool connected{m_sock->IsConnected(errmsg)};
     125   [ #  #  #  # ]:           0 :     if (!connected && !errmsg.empty()) {
     126   [ #  #  #  #  :           0 :         LogDebug(BCLog::TOR, "Connection check failed: %s", errmsg);
                   #  # ]
     127                 :             :     }
     128                 :           0 :     return connected;
     129                 :           0 : }
     130                 :             : 
     131                 :           0 : bool TorControlConnection::WaitForData(std::chrono::milliseconds timeout)
     132                 :             : {
     133         [ #  # ]:           0 :     if (!m_sock) return false;
     134                 :             : 
     135                 :           0 :     Sock::Event event{0};
     136         [ #  # ]:           0 :     if (!m_sock->Wait(timeout, Sock::RECV, &event)) {
     137                 :             :         return false;
     138                 :             :     }
     139         [ #  # ]:           0 :     if (event & Sock::ERR) {
     140         [ #  # ]:           0 :         LogDebug(BCLog::TOR, "Socket error detected");
     141                 :           0 :         Disconnect();
     142                 :           0 :         return false;
     143                 :             :     }
     144                 :             : 
     145                 :           0 :     return (event & Sock::RECV);
     146                 :             : }
     147                 :             : 
     148                 :           0 : bool TorControlConnection::ReceiveAndProcess()
     149                 :             : {
     150         [ #  # ]:           0 :     if (!m_sock) return false;
     151                 :             : 
     152                 :           0 :     std::byte buf[4096];
     153                 :           0 :     ssize_t nread = m_sock->Recv(buf, sizeof(buf), MSG_DONTWAIT);
     154                 :             : 
     155         [ #  # ]:           0 :     if (nread < 0) {
     156                 :           0 :         int err = WSAGetLastError();
     157   [ #  #  #  # ]:           0 :         if (err == WSAEWOULDBLOCK || err == WSAEINTR || err == WSAEINPROGRESS) {
     158                 :             :             // No data available currently
     159                 :             :             return true;
     160                 :             :         }
     161         [ #  # ]:           0 :         LogWarning("tor: Error reading from socket: %s", NetworkErrorString(err));
     162                 :           0 :         return false;
     163                 :             :     }
     164                 :             : 
     165         [ #  # ]:           0 :     if (nread == 0) {
     166         [ #  # ]:           0 :         LogDebug(BCLog::TOR, "End of stream");
     167                 :           0 :         return false;
     168                 :             :     }
     169                 :             : 
     170                 :           0 :     m_recv_buffer.insert(m_recv_buffer.end(), buf, buf + nread);
     171                 :           0 :     try {
     172         [ #  # ]:           0 :         return ProcessBuffer();
     173         [ -  - ]:           0 :     } catch (const std::runtime_error& e) {
     174         [ -  - ]:           0 :         LogWarning("tor: Error processing receive buffer: %s", e.what());
     175                 :           0 :         return false;
     176                 :           0 :     }
     177                 :             : }
     178                 :             : 
     179                 :           0 : bool TorControlConnection::ProcessBuffer()
     180                 :             : {
     181         [ #  # ]:           0 :     util::LineReader reader(m_recv_buffer, MAX_LINE_LENGTH);
     182                 :           0 :     auto start = reader.it;
     183                 :             : 
     184         [ #  # ]:           0 :     while (auto line = reader.ReadLine()) {
     185   [ #  #  #  # ]:           0 :         if (m_message.lines.size() == MAX_LINE_COUNT) {
     186   [ #  #  #  # ]:           0 :             throw std::runtime_error(strprintf("Control port reply exceeded %d lines, disconnecting", MAX_LINE_COUNT));
     187                 :             :         }
     188                 :             :         // Skip short lines
     189   [ #  #  #  # ]:           0 :         if (line->size() < 4) continue;
     190                 :             : 
     191                 :             :         // Parse: <code><separator><data>
     192                 :             :         // <status>(-|+| )<data>
     193   [ #  #  #  # ]:           0 :         m_message.code = ToIntegral<int>(line->substr(0, 3)).value_or(0);
     194         [ #  # ]:           0 :         m_message.lines.push_back(line->substr(4));
     195         [ #  # ]:           0 :         char separator = (*line)[3]; // '-', '+', or ' '
     196                 :             : 
     197         [ #  # ]:           0 :         if (separator == ' ') {
     198         [ #  # ]:           0 :             if (m_message.code >= 600) {
     199                 :             :                 // Async notifications are currently unused
     200                 :             :                 // Synchronous and asynchronous messages are never interleaved
     201   [ #  #  #  #  :           0 :                 LogDebug(BCLog::TOR, "Received async notification %i", m_message.code);
                   #  # ]
     202         [ #  # ]:           0 :             } else if (!m_reply_handlers.empty()) {
     203                 :             :                 // Invoke reply handler with message
     204         [ #  # ]:           0 :                 m_reply_handlers.front()(*this, m_message);
     205                 :           0 :                 m_reply_handlers.pop_front();
     206                 :             :             } else {
     207   [ #  #  #  #  :           0 :                 LogDebug(BCLog::TOR, "Received unexpected sync reply %i", m_message.code);
                   #  # ]
     208                 :             :             }
     209                 :           0 :             m_message.Clear();
     210                 :             :         }
     211                 :           0 :     }
     212                 :             : 
     213                 :           0 :     m_recv_buffer.erase(m_recv_buffer.begin(), m_recv_buffer.begin() + std::distance(start, reader.it));
     214                 :           0 :     return true;
     215                 :             : }
     216                 :             : 
     217                 :           0 : bool TorControlConnection::Command(const std::string &cmd, const ReplyHandlerCB& reply_handler)
     218                 :             : {
     219         [ #  # ]:           0 :     if (!m_sock) return false;
     220                 :             : 
     221                 :           0 :     std::string command = cmd + "\r\n";
     222                 :           0 :     try {
     223   [ #  #  #  # ]:           0 :         m_sock->SendComplete(std::span<const char>{command}, SOCKET_SEND_TIMEOUT, m_interrupt);
     224         [ -  - ]:           0 :     } catch (const std::runtime_error& e) {
     225         [ -  - ]:           0 :         LogWarning("tor: Error sending command: %s", e.what());
     226                 :           0 :         return false;
     227                 :           0 :     }
     228                 :             : 
     229         [ #  # ]:           0 :     m_reply_handlers.push_back(reply_handler);
     230                 :             :     return true;
     231                 :           0 : }
     232                 :             : 
     233                 :             : /****** General parsing utilities ********/
     234                 :             : 
     235                 :             : /* Split reply line in the form 'AUTH METHODS=...' into a type
     236                 :             :  * 'AUTH' and arguments 'METHODS=...'.
     237                 :             :  * Grammar is implicitly defined in https://spec.torproject.org/control-spec by
     238                 :             :  * the server reply formats for PROTOCOLINFO (S3.21) and AUTHCHALLENGE (S3.24).
     239                 :             :  */
     240                 :          10 : std::pair<std::string,std::string> SplitTorReplyLine(const std::string &s)
     241                 :             : {
     242                 :          10 :     size_t ptr=0;
     243                 :          10 :     std::string type;
     244   [ -  +  +  +  :          82 :     while (ptr < s.size() && s[ptr] != ' ') {
                   +  + ]
     245         [ +  - ]:          72 :         type.push_back(s[ptr]);
     246                 :          72 :         ++ptr;
     247                 :             :     }
     248         [ +  + ]:          10 :     if (ptr < s.size())
     249                 :           9 :         ++ptr; // skip ' '
     250         [ +  - ]:          20 :     return make_pair(type, s.substr(ptr));
     251                 :          10 : }
     252                 :             : 
     253                 :             : /** Parse reply arguments in the form 'METHODS=COOKIE,SAFECOOKIE COOKIEFILE=".../control_auth_cookie"'.
     254                 :             :  * Returns a map of keys to values, or an empty map if there was an error.
     255                 :             :  * Grammar is implicitly defined in https://spec.torproject.org/control-spec by
     256                 :             :  * the server reply formats for PROTOCOLINFO (S3.21), AUTHCHALLENGE (S3.24),
     257                 :             :  * and ADD_ONION (S3.27). See also sections 2.1 and 2.3.
     258                 :             :  */
     259                 :          27 : std::map<std::string,std::string> ParseTorReplyMapping(const std::string &s)
     260                 :             : {
     261                 :          27 :     std::map<std::string,std::string> mapping;
     262                 :          27 :     size_t ptr=0;
     263   [ -  +  +  + ]:          58 :     while (ptr < s.size()) {
     264                 :          38 :         std::string key, value;
     265   [ -  +  +  +  :         246 :         while (ptr < s.size() && s[ptr] != '=' && s[ptr] != ' ') {
             +  +  +  + ]
     266         [ +  - ]:         208 :             key.push_back(s[ptr]);
     267                 :         208 :             ++ptr;
     268                 :             :         }
     269         [ +  + ]:          38 :         if (ptr == s.size()) // unexpected end of line
     270                 :           1 :             return std::map<std::string,std::string>();
     271         [ +  + ]:          37 :         if (s[ptr] == ' ') // The remaining string is an OptArguments
     272                 :             :             break;
     273                 :          32 :         ++ptr; // skip '='
     274   [ -  +  +  + ]:          32 :         if (ptr < s.size() && s[ptr] == '"') { // Quoted string
     275                 :          18 :             ++ptr; // skip opening '"'
     276                 :          18 :             bool escape_next = false;
     277   [ -  +  +  +  :         224 :             while (ptr < s.size() && (escape_next || s[ptr] != '"')) {
             +  +  +  + ]
     278                 :             :                 // Repeated backslashes must be interpreted as pairs
     279   [ +  +  +  + ]:         206 :                 escape_next = (s[ptr] == '\\' && !escape_next);
     280         [ +  - ]:         206 :                 value.push_back(s[ptr]);
     281                 :         206 :                 ++ptr;
     282                 :             :             }
     283         [ +  + ]:          18 :             if (ptr == s.size()) // unexpected end of line
     284                 :           1 :                 return std::map<std::string,std::string>();
     285                 :          17 :             ++ptr; // skip closing '"'
     286                 :             :             /**
     287                 :             :              * Unescape value. Per https://spec.torproject.org/control-spec section 2.1.1:
     288                 :             :              *
     289                 :             :              *   For future-proofing, controller implementers MAY use the following
     290                 :             :              *   rules to be compatible with buggy Tor implementations and with
     291                 :             :              *   future ones that implement the spec as intended:
     292                 :             :              *
     293                 :             :              *     Read \n \t \r and \0 ... \377 as C escapes.
     294                 :             :              *     Treat a backslash followed by any other character as that character.
     295                 :             :              */
     296                 :          17 :             std::string escaped_value;
     297   [ -  +  +  + ]:         183 :             for (size_t i = 0; i < value.size(); ++i) {
     298         [ +  + ]:         166 :                 if (value[i] == '\\') {
     299                 :             :                     // This will always be valid, because if the QuotedString
     300                 :             :                     // ended in an odd number of backslashes, then the parser
     301                 :             :                     // would already have returned above, due to a missing
     302                 :             :                     // terminating double-quote.
     303                 :          23 :                     ++i;
     304         [ +  + ]:          23 :                     if (value[i] == 'n') {
     305         [ +  - ]:           1 :                         escaped_value.push_back('\n');
     306         [ +  + ]:          22 :                     } else if (value[i] == 't') {
     307         [ +  - ]:           1 :                         escaped_value.push_back('\t');
     308         [ +  + ]:          21 :                     } else if (value[i] == 'r') {
     309         [ +  - ]:           1 :                         escaped_value.push_back('\r');
     310   [ +  +  +  + ]:          20 :                     } else if ('0' <= value[i] && value[i] <= '7') {
     311                 :             :                         size_t j;
     312                 :             :                         // Octal escape sequences have a limit of three octal digits,
     313                 :             :                         // but terminate at the first character that is not a valid
     314                 :             :                         // octal digit if encountered sooner.
     315   [ +  +  +  +  :          21 :                         for (j = 1; j < 3 && (i+j) < value.size() && '0' <= value[i+j] && value[i+j] <= '7'; ++j) {}
             +  -  +  + ]
     316                 :             :                         // Tor restricts first digit to 0-3 for three-digit octals.
     317                 :             :                         // A leading digit of 4-7 would therefore be interpreted as
     318                 :             :                         // a two-digit octal.
     319   [ +  +  +  + ]:          11 :                         if (j == 3 && value[i] > '3') {
     320                 :           1 :                             j--;
     321                 :             :                         }
     322                 :          11 :                         const auto end{i + j};
     323                 :          11 :                         uint8_t val{0};
     324         [ +  + ]:          31 :                         while (i < end) {
     325                 :          20 :                             val *= 8;
     326                 :          20 :                             val += value[i++] - '0';
     327                 :             :                         }
     328         [ +  - ]:          11 :                         escaped_value.push_back(char(val));
     329                 :             :                         // Account for automatic incrementing at loop end
     330                 :          11 :                         --i;
     331                 :             :                     } else {
     332         [ +  - ]:           9 :                         escaped_value.push_back(value[i]);
     333                 :             :                     }
     334                 :             :                 } else {
     335         [ +  - ]:         143 :                     escaped_value.push_back(value[i]);
     336                 :             :                 }
     337                 :             :             }
     338         [ +  - ]:          34 :             value = escaped_value;
     339                 :          17 :         } else { // Unquoted value. Note that values can contain '=' at will, just no spaces
     340   [ -  +  +  +  :         132 :             while (ptr < s.size() && s[ptr] != ' ') {
                   +  + ]
     341         [ +  - ]:         118 :                 value.push_back(s[ptr]);
     342                 :         118 :                 ++ptr;
     343                 :             :             }
     344                 :             :         }
     345   [ -  +  +  +  :          31 :         if (ptr < s.size() && s[ptr] == ' ')
                   +  - ]
     346                 :          11 :             ++ptr; // skip ' ' after key=value
     347   [ +  -  +  - ]:          62 :         mapping[key] = value;
     348                 :          38 :     }
     349                 :          25 :     return mapping;
     350                 :          27 : }
     351                 :             : 
     352                 :           1 : TorController::TorController(const std::string& tor_control_center, const CService& target)
     353         [ -  + ]:           1 :     : m_tor_control_center(tor_control_center),
     354         [ +  - ]:           1 :       m_conn(m_interrupt),
     355         [ +  - ]:           1 :       m_reconnect(true),
     356         [ +  - ]:           1 :       m_reconnect_timeout(RECONNECT_TIMEOUT_START),
     357   [ -  +  +  -  :           2 :       m_target(target)
                   +  - ]
     358                 :             : {
     359                 :             :     // Read service private key if cached
     360   [ +  -  +  - ]:           1 :     std::pair<bool,std::string> pkf = ReadBinaryFile(GetPrivateKeyFile());
     361         [ -  + ]:           1 :     if (pkf.first) {
     362   [ #  #  #  #  :           0 :         LogDebug(BCLog::TOR, "Reading cached private key from %s", fs::PathToString(GetPrivateKeyFile()));
             #  #  #  # ]
     363         [ #  # ]:           0 :         m_private_key = pkf.second;
     364                 :             :     }
     365         [ +  - ]:           2 :     m_thread = std::thread(&util::TraceThread, "torcontrol", [this] { ThreadControl(); });
     366                 :           1 : }
     367                 :             : 
     368                 :           1 : TorController::~TorController()
     369                 :             : {
     370                 :           1 :     Interrupt();
     371                 :           1 :     Join();
     372         [ -  + ]:           1 :     if (m_service.IsValid()) {
     373                 :           0 :         RemoveLocal(m_service);
     374                 :             :     }
     375                 :           2 : }
     376                 :             : 
     377                 :           2 : void TorController::Interrupt()
     378                 :             : {
     379                 :           2 :     m_reconnect = false;
     380                 :           2 :     m_interrupt();
     381                 :           2 : }
     382                 :             : 
     383                 :           2 : void TorController::Join()
     384                 :             : {
     385         [ +  + ]:           2 :     if (m_thread.joinable()) {
     386                 :           1 :         m_thread.join();
     387                 :             :     }
     388                 :           2 : }
     389                 :             : 
     390                 :           1 : void TorController::ThreadControl()
     391                 :             : {
     392         [ +  - ]:           1 :     LogDebug(BCLog::TOR, "Entering Tor control thread");
     393                 :             : 
     394         [ -  + ]:           1 :     while (!m_interrupt) {
     395                 :             :         // Try to connect if not connected already
     396         [ #  # ]:           0 :         if (!m_conn.IsConnected()) {
     397         [ #  # ]:           0 :             LogDebug(BCLog::TOR, "Attempting to connect to Tor control port %s", m_tor_control_center);
     398                 :             : 
     399         [ #  # ]:           0 :             if (!m_conn.Connect(m_tor_control_center)) {
     400                 :           0 :                 LogWarning("tor: Initiating connection to Tor control port %s failed", m_tor_control_center);
     401         [ #  # ]:           0 :                 if (!m_reconnect) {
     402                 :             :                     break;
     403                 :             :                 }
     404                 :             :                 // Wait before retrying with exponential backoff
     405         [ #  # ]:           0 :                 LogDebug(BCLog::TOR, "Retrying in %.1f seconds", m_reconnect_timeout.count());
     406         [ #  # ]:           0 :                 if (!m_interrupt.sleep_for(std::chrono::duration_cast<std::chrono::milliseconds>(m_reconnect_timeout))) {
     407                 :             :                     break;
     408                 :             :                 }
     409                 :           0 :                 m_reconnect_timeout = std::min(m_reconnect_timeout * RECONNECT_TIMEOUT_EXP, RECONNECT_TIMEOUT_MAX);
     410                 :           0 :                 continue;
     411                 :           0 :             }
     412                 :             :             // Successfully connected, reset timeout and trigger connected callback
     413                 :           0 :             m_reconnect_timeout = RECONNECT_TIMEOUT_START;
     414                 :           0 :             connected_cb(m_conn);
     415                 :             :         }
     416                 :             :         // Wait for data with a timeout
     417         [ #  # ]:           0 :         if (!m_conn.WaitForData(std::chrono::seconds(1))) {
     418                 :             :             // Check if still connected
     419         [ #  # ]:           0 :             if (!m_conn.IsConnected()) {
     420         [ #  # ]:           0 :                 LogDebug(BCLog::TOR, "Lost connection to Tor control port");
     421                 :           0 :                 disconnected_cb(m_conn);
     422                 :           0 :                 continue;
     423                 :           0 :             }
     424                 :             :             // Just a timeout, continue waiting
     425                 :           0 :             continue;
     426                 :           0 :         }
     427                 :             :         // Process incoming data
     428         [ #  # ]:           0 :         if (!m_conn.ReceiveAndProcess()) {
     429                 :           0 :             disconnected_cb(m_conn);
     430                 :             :         }
     431                 :             :     }
     432         [ +  - ]:           1 :     LogDebug(BCLog::TOR, "Exited Tor control thread");
     433                 :           1 : }
     434                 :             : 
     435                 :           0 : void TorController::get_socks_cb(TorControlConnection& _conn, const TorControlReply& reply)
     436                 :             : {
     437                 :             :     // NOTE: We can only get here if -onion is unset
     438         [ #  # ]:           0 :     std::string socks_location;
     439         [ #  # ]:           0 :     if (reply.code == TOR_REPLY_OK) {
     440         [ #  # ]:           0 :         for (const auto& line : reply.lines) {
     441   [ #  #  #  # ]:           0 :             if (line.starts_with("net/listeners/socks=")) {
     442         [ #  # ]:           0 :                 const std::string port_list_str = line.substr(20);
     443   [ #  #  #  # ]:           0 :                 std::vector<std::string> port_list = SplitString(port_list_str, ' ');
     444                 :             : 
     445         [ #  # ]:           0 :                 for (auto& portstr : port_list) {
     446         [ #  # ]:           0 :                     if (portstr.empty()) continue;
     447   [ #  #  #  #  :           0 :                     if ((portstr[0] == '"' || portstr[0] == '\'') && portstr.size() >= 2 && (*portstr.rbegin() == portstr[0])) {
             #  #  #  # ]
     448         [ #  # ]:           0 :                         portstr = portstr.substr(1, portstr.size() - 2);
     449         [ #  # ]:           0 :                         if (portstr.empty()) continue;
     450                 :             :                     }
     451         [ #  # ]:           0 :                     socks_location = portstr;
     452   [ #  #  #  # ]:           0 :                     if (portstr.starts_with("127.0.0.1:")) {
     453                 :             :                         // Prefer localhost - ignore other ports
     454                 :             :                         break;
     455                 :             :                     }
     456                 :             :                 }
     457                 :           0 :             }
     458                 :             :         }
     459         [ #  # ]:           0 :         if (!socks_location.empty()) {
     460   [ #  #  #  #  :           0 :             LogDebug(BCLog::TOR, "Get SOCKS port command yielded %s", socks_location);
                   #  # ]
     461                 :             :         } else {
     462         [ #  # ]:           0 :             LogWarning("tor: Get SOCKS port command returned nothing");
     463                 :             :         }
     464         [ #  # ]:           0 :     } else if (reply.code == TOR_REPLY_UNRECOGNIZED) {
     465         [ #  # ]:           0 :         LogWarning("tor: Get SOCKS port command failed with unrecognized command (You probably should upgrade Tor)");
     466                 :             :     } else {
     467         [ #  # ]:           0 :         LogWarning("tor: Get SOCKS port command failed; error code %d", reply.code);
     468                 :             :     }
     469                 :             : 
     470         [ #  # ]:           0 :     CService resolved;
     471   [ #  #  #  # ]:           0 :     Assume(!resolved.IsValid());
     472         [ #  # ]:           0 :     if (!socks_location.empty()) {
     473   [ #  #  #  # ]:           0 :         resolved = LookupNumeric(socks_location, DEFAULT_TOR_SOCKS_PORT);
     474                 :             :     }
     475   [ #  #  #  # ]:           0 :     if (!resolved.IsValid()) {
     476                 :             :         // Fallback to old behaviour
     477   [ #  #  #  #  :           0 :         resolved = LookupNumeric("127.0.0.1", DEFAULT_TOR_SOCKS_PORT);
                   #  # ]
     478                 :             :     }
     479                 :             : 
     480   [ #  #  #  # ]:           0 :     Assume(resolved.IsValid());
     481   [ #  #  #  #  :           0 :     LogDebug(BCLog::TOR, "Configuring onion proxy for %s", resolved.ToStringAddrPort());
             #  #  #  # ]
     482                 :             : 
     483                 :             :     // Add Tor as proxy for .onion addresses.
     484                 :             :     // Enable stream isolation to prevent connection correlation and enhance privacy, by forcing a different Tor circuit for every connection.
     485                 :             :     // For this to work, the IsolateSOCKSAuth flag must be enabled on SOCKSPort (which is the default, see the IsolateSOCKSAuth section of Tor's manual page).
     486                 :           0 :     Proxy addrOnion = Proxy(resolved, /*tor_stream_isolation=*/ true);
     487         [ #  # ]:           0 :     SetProxy(NET_ONION, addrOnion);
     488                 :             : 
     489   [ #  #  #  # ]:           0 :     const auto onlynets = gArgs.GetArgs("-onlynet");
     490                 :             : 
     491                 :           0 :     const bool onion_allowed_by_onlynet{
     492   [ #  #  #  # ]:           0 :         onlynets.empty() ||
     493         [ #  # ]:           0 :         std::any_of(onlynets.begin(), onlynets.end(), [](const auto& n) {
     494                 :           0 :             return ParseNetwork(n) == NET_ONION;
     495                 :           0 :         })};
     496                 :             : 
     497                 :           0 :     if (onion_allowed_by_onlynet) {
     498                 :             :         // If NET_ONION is reachable, then the below is a noop.
     499                 :             :         //
     500                 :             :         // If NET_ONION is not reachable, then none of -proxy or -onion was given.
     501                 :             :         // Since we are here, then -torcontrol and -torpassword were given.
     502         [ #  # ]:           0 :         g_reachable_nets.Add(NET_ONION);
     503                 :             :     }
     504                 :           0 : }
     505                 :             : 
     506                 :           0 : static std::string MakeAddOnionCmd(const std::string& private_key, const std::string& target, bool enable_pow)
     507                 :             : {
     508                 :             :     // Note that the 'virtual' port is always the default port to avoid decloaking nodes using other ports.
     509                 :           0 :     return strprintf("ADD_ONION %s%s Port=%i,%s",
     510                 :             :                      private_key,
     511         [ #  # ]:           0 :                      enable_pow ? " PoWDefensesEnabled=1" : "",
     512         [ #  # ]:           0 :                      Params().GetDefaultPort(),
     513                 :           0 :                      target);
     514                 :             : }
     515                 :             : 
     516                 :           0 : void TorController::add_onion_cb(TorControlConnection& _conn, const TorControlReply& reply, bool pow_was_enabled)
     517                 :             : {
     518         [ #  # ]:           0 :     if (reply.code == TOR_REPLY_OK) {
     519   [ #  #  #  # ]:           0 :         LogDebug(BCLog::TOR, "ADD_ONION successful (PoW defenses %s)", pow_was_enabled ? "enabled" : "disabled");
     520         [ #  # ]:           0 :         for (const std::string &s : reply.lines) {
     521                 :           0 :             std::map<std::string,std::string> m = ParseTorReplyMapping(s);
     522         [ #  # ]:           0 :             std::map<std::string,std::string>::iterator i;
     523   [ #  #  #  # ]:           0 :             if ((i = m.find("ServiceID")) != m.end())
     524         [ #  # ]:           0 :                 m_service_id = i->second;
     525   [ #  #  #  # ]:           0 :             if ((i = m.find("PrivateKey")) != m.end())
     526         [ #  # ]:           0 :                 m_private_key = i->second;
     527                 :           0 :         }
     528         [ #  # ]:           0 :         if (m_service_id.empty()) {
     529                 :           0 :             LogWarning("tor: Error parsing ADD_ONION parameters:");
     530         [ #  # ]:           0 :             for (const std::string &s : reply.lines) {
     531   [ #  #  #  # ]:           0 :                 LogWarning("    %s", SanitizeString(s));
     532                 :             :             }
     533                 :             :             return;
     534                 :             :         }
     535   [ #  #  #  #  :           0 :         m_service = LookupNumeric(std::string(m_service_id+".onion"), Params().GetDefaultPort());
                   #  # ]
     536         [ #  # ]:           0 :         LogInfo("Got tor service ID %s, advertising service %s", m_service_id, m_service.ToStringAddrPort());
     537   [ #  #  #  # ]:           0 :         if (WriteBinaryFile(GetPrivateKeyFile(), m_private_key)) {
     538   [ #  #  #  #  :           0 :             LogDebug(BCLog::TOR, "Cached service private key to %s", fs::PathToString(GetPrivateKeyFile()));
                   #  # ]
     539                 :             :         } else {
     540   [ #  #  #  # ]:           0 :             LogWarning("tor: Error writing service private key to %s", fs::PathToString(GetPrivateKeyFile()));
     541                 :             :         }
     542                 :           0 :         AddLocal(m_service, LOCAL_MANUAL);
     543                 :             :         // ... onion requested - keep connection open
     544         [ #  # ]:           0 :     } else if (reply.code == TOR_REPLY_UNRECOGNIZED) {
     545                 :           0 :         LogWarning("tor: Add onion failed with unrecognized command (You probably need to upgrade Tor)");
     546   [ #  #  #  # ]:           0 :     } else if (pow_was_enabled && reply.code == TOR_REPLY_SYNTAX_ERROR) {
     547         [ #  # ]:           0 :         LogDebug(BCLog::TOR, "ADD_ONION failed with PoW defenses, retrying without");
     548   [ #  #  #  # ]:           0 :         _conn.Command(MakeAddOnionCmd(m_private_key, m_target.ToStringAddrPort(), /*enable_pow=*/false),
     549         [ #  # ]:           0 :                       [this](TorControlConnection& conn, const TorControlReply& reply) {
     550                 :           0 :                           add_onion_cb(conn, reply, /*pow_was_enabled=*/false);
     551                 :             :                       });
     552                 :             :     } else {
     553                 :           0 :         LogWarning("tor: Add onion failed; error code %d", reply.code);
     554                 :             :     }
     555                 :             : }
     556                 :             : 
     557                 :           0 : void TorController::auth_cb(TorControlConnection& _conn, const TorControlReply& reply)
     558                 :             : {
     559         [ #  # ]:           0 :     if (reply.code == TOR_REPLY_OK) {
     560         [ #  # ]:           0 :         LogDebug(BCLog::TOR, "Authentication successful");
     561                 :             : 
     562                 :             :         // Now that we know Tor is running setup the proxy for onion addresses
     563                 :             :         // if -onion isn't set to something else.
     564   [ #  #  #  #  :           0 :         if (gArgs.GetArg("-onion", "") == "") {
                   #  # ]
     565   [ #  #  #  # ]:           0 :             _conn.Command("GETINFO net/listeners/socks", std::bind_front(&TorController::get_socks_cb, this));
     566                 :             :         }
     567                 :             : 
     568                 :             :         // Finally - now create the service
     569         [ #  # ]:           0 :         if (m_private_key.empty()) { // No private key, generate one
     570                 :           0 :             m_private_key = "NEW:ED25519-V3"; // Explicitly request key type - see issue #9214
     571                 :             :         }
     572                 :             :         // Request onion service, redirect port.
     573   [ #  #  #  # ]:           0 :         _conn.Command(MakeAddOnionCmd(m_private_key, m_target.ToStringAddrPort(), /*enable_pow=*/true),
     574         [ #  # ]:           0 :                       [this](TorControlConnection& conn, const TorControlReply& reply) {
     575                 :           0 :                           add_onion_cb(conn, reply, /*pow_was_enabled=*/true);
     576                 :             :                       });
     577                 :             :     } else {
     578                 :           0 :         LogWarning("tor: Authentication failed");
     579                 :             :     }
     580                 :           0 : }
     581                 :             : 
     582                 :             : /** Compute Tor SAFECOOKIE response.
     583                 :             :  *
     584                 :             :  *    ServerHash is computed as:
     585                 :             :  *      HMAC-SHA256("Tor safe cookie authentication server-to-controller hash",
     586                 :             :  *                  CookieString | ClientNonce | ServerNonce)
     587                 :             :  *    (with the HMAC key as its first argument)
     588                 :             :  *
     589                 :             :  *    After a controller sends a successful AUTHCHALLENGE command, the
     590                 :             :  *    next command sent on the connection must be an AUTHENTICATE command,
     591                 :             :  *    and the only authentication string which that AUTHENTICATE command
     592                 :             :  *    will accept is:
     593                 :             :  *
     594                 :             :  *      HMAC-SHA256("Tor safe cookie authentication controller-to-server hash",
     595                 :             :  *                  CookieString | ClientNonce | ServerNonce)
     596                 :             :  *
     597                 :             :  */
     598                 :           0 : static std::vector<uint8_t> ComputeResponse(std::string_view key, std::span<const uint8_t> cookie, std::span<const uint8_t> client_nonce, std::span<const uint8_t> server_nonce)
     599                 :             : {
     600                 :           0 :     CHMAC_SHA256 computeHash((const uint8_t*)key.data(), key.size());
     601                 :           0 :     std::vector<uint8_t> computedHash(CHMAC_SHA256::OUTPUT_SIZE, 0);
     602         [ #  # ]:           0 :     computeHash.Write(cookie.data(), cookie.size());
     603         [ #  # ]:           0 :     computeHash.Write(client_nonce.data(), client_nonce.size());
     604         [ #  # ]:           0 :     computeHash.Write(server_nonce.data(), server_nonce.size());
     605         [ #  # ]:           0 :     computeHash.Finalize(computedHash.data());
     606                 :           0 :     return computedHash;
     607                 :           0 : }
     608                 :             : 
     609                 :           0 : void TorController::authchallenge_cb(TorControlConnection& _conn, const TorControlReply& reply)
     610                 :             : {
     611         [ #  # ]:           0 :     if (reply.code == TOR_REPLY_OK) {
     612         [ #  # ]:           0 :         LogDebug(BCLog::TOR, "SAFECOOKIE authentication challenge successful");
     613         [ #  # ]:           0 :         if (reply.lines.empty()) {
     614                 :           0 :             LogWarning("tor: AUTHCHALLENGE reply was empty");
     615                 :           0 :             return;
     616                 :             :         }
     617                 :           0 :         std::pair<std::string,std::string> l = SplitTorReplyLine(reply.lines[0]);
     618         [ #  # ]:           0 :         if (l.first == "AUTHCHALLENGE") {
     619         [ #  # ]:           0 :             std::map<std::string,std::string> m = ParseTorReplyMapping(l.second);
     620         [ #  # ]:           0 :             if (m.empty()) {
     621   [ #  #  #  #  :           0 :                 LogWarning("tor: Error parsing AUTHCHALLENGE parameters: %s", SanitizeString(l.second));
                   #  # ]
     622                 :           0 :                 return;
     623                 :             :             }
     624   [ #  #  #  #  :           0 :             std::vector<uint8_t> server_hash = ParseHex(m["SERVERHASH"]);
             #  #  #  # ]
     625   [ #  #  #  #  :           0 :             std::vector<uint8_t> server_nonce = ParseHex(m["SERVERNONCE"]);
             #  #  #  # ]
     626   [ #  #  #  #  :           0 :             LogDebug(BCLog::TOR, "AUTHCHALLENGE ServerHash %s ServerNonce %s", HexStr(server_hash), HexStr(server_nonce));
          #  #  #  #  #  
                #  #  # ]
     627   [ #  #  #  # ]:           0 :             if (server_nonce.size() != 32) {
     628         [ #  # ]:           0 :                 LogWarning("tor: ServerNonce is not 32 bytes, as required by spec");
     629                 :             :                 return;
     630                 :             :             }
     631                 :             : 
     632   [ #  #  #  #  :           0 :             std::vector<uint8_t> computed_server_hash = ComputeResponse(TOR_SAFE_SERVERKEY, m_cookie, m_client_nonce, server_nonce);
             #  #  #  # ]
     633         [ #  # ]:           0 :             if (computed_server_hash != server_hash) {
     634   [ #  #  #  #  :           0 :                 LogWarning("tor: ServerHash %s does not match expected ServerHash %s", HexStr(server_hash), HexStr(computed_server_hash));
             #  #  #  # ]
     635                 :           0 :                 return;
     636                 :             :             }
     637                 :             : 
     638   [ #  #  #  #  :           0 :             std::vector<uint8_t> computedClientHash = ComputeResponse(TOR_SAFE_CLIENTKEY, m_cookie, m_client_nonce, server_nonce);
          #  #  #  #  #  
                      # ]
     639   [ #  #  #  #  :           0 :             _conn.Command("AUTHENTICATE " + HexStr(computedClientHash), std::bind_front(&TorController::auth_cb, this));
             #  #  #  # ]
     640                 :           0 :         } else {
     641         [ #  # ]:           0 :             LogWarning("tor: Invalid reply to AUTHCHALLENGE");
     642                 :             :         }
     643                 :           0 :     } else {
     644                 :           0 :         LogWarning("tor: SAFECOOKIE authentication challenge failed");
     645                 :             :     }
     646                 :             : }
     647                 :             : 
     648                 :           0 : void TorController::protocolinfo_cb(TorControlConnection& _conn, const TorControlReply& reply)
     649                 :             : {
     650         [ #  # ]:           0 :     if (reply.code == TOR_REPLY_OK) {
     651                 :           0 :         std::set<std::string> methods;
     652                 :           0 :         std::string cookiefile;
     653                 :             :         /*
     654                 :             :          * 250-AUTH METHODS=COOKIE,SAFECOOKIE COOKIEFILE="/home/x/.tor/control_auth_cookie"
     655                 :             :          * 250-AUTH METHODS=NULL
     656                 :             :          * 250-AUTH METHODS=HASHEDPASSWORD
     657                 :             :          */
     658         [ #  # ]:           0 :         for (const std::string &s : reply.lines) {
     659         [ #  # ]:           0 :             std::pair<std::string,std::string> l = SplitTorReplyLine(s);
     660         [ #  # ]:           0 :             if (l.first == "AUTH") {
     661         [ #  # ]:           0 :                 std::map<std::string,std::string> m = ParseTorReplyMapping(l.second);
     662         [ #  # ]:           0 :                 std::map<std::string,std::string>::iterator i;
     663   [ #  #  #  # ]:           0 :                 if ((i = m.find("METHODS")) != m.end()) {
     664   [ #  #  #  # ]:           0 :                     std::vector<std::string> m_vec = SplitString(i->second, ',');
     665         [ #  # ]:           0 :                     methods = std::set<std::string>(m_vec.begin(), m_vec.end());
     666                 :           0 :                 }
     667   [ #  #  #  # ]:           0 :                 if ((i = m.find("COOKIEFILE")) != m.end())
     668         [ #  # ]:           0 :                     cookiefile = i->second;
     669         [ #  # ]:           0 :             } else if (l.first == "VERSION") {
     670         [ #  # ]:           0 :                 std::map<std::string,std::string> m = ParseTorReplyMapping(l.second);
     671         [ #  # ]:           0 :                 std::map<std::string,std::string>::iterator i;
     672   [ #  #  #  # ]:           0 :                 if ((i = m.find("Tor")) != m.end()) {
     673   [ #  #  #  #  :           0 :                     LogDebug(BCLog::TOR, "Connected to Tor version %s", i->second);
                   #  # ]
     674                 :             :                 }
     675                 :           0 :             }
     676                 :           0 :         }
     677         [ #  # ]:           0 :         for (const std::string &s : methods) {
     678   [ #  #  #  #  :           0 :             LogDebug(BCLog::TOR, "Supported authentication method: %s", s);
                   #  # ]
     679                 :             :         }
     680                 :             :         // Prefer NULL, otherwise SAFECOOKIE. If a password is provided, use HASHEDPASSWORD
     681                 :             :         /* Authentication:
     682                 :             :          *   cookie:   hex-encoded ~/.tor/control_auth_cookie
     683                 :             :          *   password: "password"
     684                 :             :          */
     685   [ #  #  #  #  :           0 :         std::string torpassword = gArgs.GetArg("-torpassword", "");
                   #  # ]
     686         [ #  # ]:           0 :         if (!torpassword.empty()) {
     687   [ #  #  #  # ]:           0 :             if (methods.contains("HASHEDPASSWORD")) {
     688   [ #  #  #  #  :           0 :                 LogDebug(BCLog::TOR, "Using HASHEDPASSWORD authentication");
                   #  # ]
     689   [ #  #  #  #  :           0 :                 ReplaceAll(torpassword, "\"", "\\\"");
                   #  # ]
     690   [ #  #  #  #  :           0 :                 _conn.Command("AUTHENTICATE \"" + torpassword + "\"", std::bind_front(&TorController::auth_cb, this));
                   #  # ]
     691                 :             :             } else {
     692         [ #  # ]:           0 :                 LogWarning("tor: Password provided with -torpassword, but HASHEDPASSWORD authentication is not available");
     693                 :             :             }
     694   [ #  #  #  # ]:           0 :         } else if (methods.contains("NULL")) {
     695   [ #  #  #  #  :           0 :             LogDebug(BCLog::TOR, "Using NULL authentication");
                   #  # ]
     696   [ #  #  #  #  :           0 :             _conn.Command("AUTHENTICATE", std::bind_front(&TorController::auth_cb, this));
                   #  # ]
     697   [ #  #  #  # ]:           0 :         } else if (methods.contains("SAFECOOKIE")) {
     698                 :             :             // Cookie: hexdump -e '32/1 "%02x""\n"'  ~/.tor/control_auth_cookie
     699   [ #  #  #  #  :           0 :             LogDebug(BCLog::TOR, "Using SAFECOOKIE authentication, reading cookie authentication from %s", cookiefile);
                   #  # ]
     700   [ #  #  #  # ]:           0 :             std::pair<bool,std::string> status_cookie = ReadBinaryFile(fs::PathFromString(cookiefile), TOR_COOKIE_SIZE);
     701   [ #  #  #  # ]:           0 :             if (status_cookie.first && status_cookie.second.size() == TOR_COOKIE_SIZE) {
     702                 :             :                 // _conn.Command("AUTHENTICATE " + HexStr(status_cookie.second), std::bind_front(&TorController::auth_cb, this));
     703   [ #  #  #  # ]:           0 :                 m_cookie = std::vector<uint8_t>(status_cookie.second.begin(), status_cookie.second.end());
     704   [ #  #  #  # ]:           0 :                 m_client_nonce = std::vector<uint8_t>(TOR_NONCE_SIZE, 0);
     705         [ #  # ]:           0 :                 GetRandBytes(m_client_nonce);
     706   [ #  #  #  #  :           0 :                 _conn.Command("AUTHCHALLENGE SAFECOOKIE " + HexStr(m_client_nonce), std::bind_front(&TorController::authchallenge_cb, this));
          #  #  #  #  #  
                      # ]
     707                 :             :             } else {
     708         [ #  # ]:           0 :                 if (status_cookie.first) {
     709         [ #  # ]:           0 :                     LogWarning("tor: Authentication cookie %s is not exactly %i bytes, as is required by the spec", cookiefile, TOR_COOKIE_SIZE);
     710                 :             :                 } else {
     711         [ #  # ]:           0 :                     LogWarning("tor: Authentication cookie %s could not be opened (check permissions)", cookiefile);
     712                 :             :                 }
     713                 :             :             }
     714   [ #  #  #  # ]:           0 :         } else if (methods.contains("HASHEDPASSWORD")) {
     715         [ #  # ]:           0 :             LogWarning("tor: The only supported authentication mechanism left is password, but no password provided with -torpassword");
     716                 :             :         } else {
     717         [ #  # ]:           0 :             LogWarning("tor: No supported authentication method");
     718                 :             :         }
     719                 :           0 :     } else {
     720                 :           0 :         LogWarning("tor: Requesting protocol info failed");
     721                 :             :     }
     722                 :           0 : }
     723                 :             : 
     724                 :           0 : void TorController::connected_cb(TorControlConnection& _conn)
     725                 :             : {
     726                 :           0 :     m_reconnect_timeout = RECONNECT_TIMEOUT_START;
     727                 :             :     // First send a PROTOCOLINFO command to figure out what authentication is expected
     728   [ #  #  #  #  :           0 :     if (!_conn.Command("PROTOCOLINFO 1", std::bind_front(&TorController::protocolinfo_cb, this)))
                   #  # ]
     729                 :           0 :         LogWarning("tor: Error sending initial protocolinfo command");
     730                 :           0 : }
     731                 :             : 
     732                 :           0 : void TorController::disconnected_cb(TorControlConnection& _conn)
     733                 :             : {
     734                 :             :     // Stop advertising service when disconnected
     735         [ #  # ]:           0 :     if (m_service.IsValid())
     736                 :           0 :         RemoveLocal(m_service);
     737                 :           0 :     m_service = CService();
     738         [ #  # ]:           0 :     if (!m_reconnect)
     739                 :             :         return;
     740                 :             : 
     741         [ #  # ]:           0 :     LogDebug(BCLog::TOR, "Not connected to Tor control port %s, will retry", m_tor_control_center);
     742                 :           0 :     _conn.Disconnect();
     743                 :             : }
     744                 :             : 
     745                 :           1 : fs::path TorController::GetPrivateKeyFile()
     746                 :             : {
     747         [ +  - ]:           3 :     return gArgs.GetDataDirNet() / "onion_v3_private_key";
     748                 :             : }
     749                 :             : 
     750                 :           1 : CService DefaultOnionServiceTarget(uint16_t port)
     751                 :             : {
     752                 :           1 :     struct in_addr onion_service_target;
     753                 :           1 :     onion_service_target.s_addr = htonl(INADDR_LOOPBACK);
     754                 :           1 :     return {onion_service_target, port};
     755                 :             : }
        

Generated by: LCOV version 2.0-1