LCOV - code coverage report
Current view: top level - src - torcontrol.cpp (source / functions) Coverage Total Hit
Test: fuzz_coverage.info Lines: 51.0 % 402 205
Test Date: 2026-04-11 05:57:11 Functions: 59.3 % 27 16
Branches: 31.0 % 752 233

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

Generated by: LCOV version 2.0-1