LCOV - code coverage report
Current view: top level - src - torcontrol.cpp (source / functions) Coverage Total Hit
Test: fuzz_coverage.info Lines: 54.4 % 399 217
Test Date: 2026-08-05 06:42:35 Functions: 59.3 % 27 16
Branches: 34.1 % 742 253

             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 <net.h>
      14                 :             : #include <netaddress.h>
      15                 :             : #include <netbase.h>
      16                 :             : #include <random.h>
      17                 :             : #include <tinyformat.h>
      18                 :             : #include <util/check.h>
      19                 :             : #include <util/fs.h>
      20                 :             : #include <util/log.h>
      21                 :             : #include <util/readwritefile.h>
      22                 :             : #include <util/strencodings.h>
      23                 :             : #include <util/string.h>
      24                 :             : #include <util/thread.h>
      25                 :             : #include <util/time.h>
      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                 :        1240 : TorControlConnection::TorControlConnection(CThreadInterrupt& interrupt)
      77         [ +  - ]:        1240 :     : m_interrupt(interrupt)
      78                 :             : {
      79                 :        1240 : }
      80                 :             : 
      81                 :        1240 : TorControlConnection::~TorControlConnection()
      82                 :             : {
      83                 :        1240 :     Disconnect();
      84                 :        1240 : }
      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                 :        1240 : void TorControlConnection::Disconnect()
     113                 :             : {
     114         [ -  + ]:        1240 :     m_sock.reset();
     115                 :        1240 :     m_recv_buffer.clear();
     116                 :        1240 :     m_message.Clear();
     117                 :        1240 :     m_reply_handlers.clear();
     118                 :        1240 : }
     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::RecvEvent, &event)) {
     137                 :             :         return false;
     138                 :             :     }
     139         [ #  # ]:           0 :     if (event & Sock::ErrorEvent) {
     140         [ #  # ]:           0 :         LogDebug(BCLog::TOR, "Socket error detected");
     141                 :           0 :         Disconnect();
     142                 :           0 :         return false;
     143                 :             :     }
     144                 :             : 
     145                 :           0 :     return (event & Sock::RecvEvent);
     146                 :             : }
     147                 :             : 
     148                 :           0 : bool TorControlConnection::ReceiveAndProcess()
     149                 :             : {
     150         [ #  # ]:           0 :     if (!m_sock) return false;
     151                 :             : 
     152                 :           0 :     char 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                 :             : 
     183         [ #  # ]:           0 :     while (auto line = reader.ReadLine()) {
     184   [ #  #  #  # ]:           0 :         if (m_message.lines.size() == MAX_LINE_COUNT) {
     185   [ #  #  #  # ]:           0 :             throw std::runtime_error(strprintf("Control port reply exceeded %d lines, disconnecting", MAX_LINE_COUNT));
     186                 :             :         }
     187                 :             :         // Skip short lines
     188         [ #  # ]:           0 :         if (line->size() < 4) continue;
     189                 :             : 
     190                 :             :         // Parse: <code><separator><data>
     191                 :             :         // <status>(-|+| )<data>
     192         [ #  # ]:           0 :         m_message.code = ToIntegral<int>(line->substr(0, 3)).value_or(0);
     193                 :           0 :         m_message.lines.emplace_back(line->substr(4));
     194         [ #  # ]:           0 :         char separator = (*line)[3]; // '-', '+', or ' '
     195                 :             : 
     196         [ #  # ]:           0 :         if (separator == ' ') {
     197         [ #  # ]:           0 :             if (m_message.code >= 600) {
     198                 :             :                 // Async notifications are currently unused
     199                 :             :                 // Synchronous and asynchronous messages are never interleaved
     200         [ #  # ]:           0 :                 LogDebug(BCLog::TOR, "Received async notification %i", m_message.code);
     201         [ #  # ]:           0 :             } else if (!m_reply_handlers.empty()) {
     202                 :             :                 // Invoke reply handler with message
     203                 :           0 :                 m_reply_handlers.front()(*this, m_message);
     204                 :           0 :                 m_reply_handlers.pop_front();
     205                 :             :             } else {
     206         [ #  # ]:           0 :                 LogDebug(BCLog::TOR, "Received unexpected sync reply %i", m_message.code);
     207                 :             :             }
     208                 :           0 :             m_message.Clear();
     209                 :             :         }
     210                 :             :     }
     211                 :             : 
     212                 :           0 :     m_recv_buffer.erase(m_recv_buffer.begin(), m_recv_buffer.begin() + reader.Consumed());
     213                 :           0 :     return true;
     214                 :             : }
     215                 :             : 
     216                 :       35453 : bool TorControlConnection::Command(const std::string &cmd, const ReplyHandlerCB& reply_handler)
     217                 :             : {
     218         [ -  + ]:       35453 :     if (!m_sock) return false;
     219                 :             : 
     220                 :           0 :     std::string command = cmd + "\r\n";
     221                 :           0 :     try {
     222   [ #  #  #  # ]:           0 :         m_sock->SendComplete(std::span<const char>{command}, SOCKET_SEND_TIMEOUT, m_interrupt);
     223         [ -  - ]:           0 :     } catch (const std::runtime_error& e) {
     224         [ -  - ]:           0 :         LogWarning("tor: Error sending command: %s", e.what());
     225                 :           0 :         return false;
     226                 :           0 :     }
     227                 :             : 
     228         [ #  # ]:           0 :     m_reply_handlers.push_back(reply_handler);
     229                 :             :     return true;
     230                 :           0 : }
     231                 :             : 
     232                 :             : /****** General parsing utilities ********/
     233                 :             : 
     234                 :             : /* Split reply line in the form 'AUTH METHODS=...' into a type
     235                 :             :  * 'AUTH' and arguments 'METHODS=...'.
     236                 :             :  * Grammar is implicitly defined in https://spec.torproject.org/control-spec by
     237                 :             :  * the server reply formats for PROTOCOLINFO (S3.21) and AUTHCHALLENGE (S3.24).
     238                 :             :  */
     239                 :      325231 : std::pair<std::string,std::string> SplitTorReplyLine(const std::string &s)
     240                 :             : {
     241                 :      325231 :     size_t ptr=0;
     242                 :      325231 :     std::string type;
     243   [ -  +  +  +  :     2324935 :     while (ptr < s.size() && s[ptr] != ' ') {
                   +  + ]
     244         [ +  - ]:     1999704 :         type.push_back(s[ptr]);
     245                 :     1999704 :         ++ptr;
     246                 :             :     }
     247         [ +  + ]:      325231 :     if (ptr < s.size())
     248                 :       90501 :         ++ptr; // skip ' '
     249         [ +  - ]:      650462 :     return make_pair(type, s.substr(ptr));
     250                 :      325231 : }
     251                 :             : 
     252                 :             : /** Parse reply arguments in the form 'METHODS=COOKIE,SAFECOOKIE COOKIEFILE=".../control_auth_cookie"'.
     253                 :             :  * Returns a map of keys to values, or an empty map if there was an error.
     254                 :             :  * Grammar is implicitly defined in https://spec.torproject.org/control-spec by
     255                 :             :  * the server reply formats for PROTOCOLINFO (S3.21), AUTHCHALLENGE (S3.24),
     256                 :             :  * and ADD_ONION (S3.27). See also sections 2.1 and 2.3.
     257                 :             :  */
     258                 :      255332 : std::map<std::string,std::string> ParseTorReplyMapping(const std::string &s)
     259                 :             : {
     260                 :      255332 :     std::map<std::string,std::string> mapping;
     261                 :      255332 :     size_t ptr=0;
     262   [ -  +  +  + ]:      373188 :     while (ptr < s.size()) {
     263                 :      247141 :         std::string key, value;
     264   [ -  +  +  +  :     1479452 :         while (ptr < s.size() && s[ptr] != '=' && s[ptr] != ' ') {
             +  +  +  + ]
     265         [ +  - ]:     1232311 :             key.push_back(s[ptr]);
     266                 :     1232311 :             ++ptr;
     267                 :             :         }
     268         [ +  + ]:      247141 :         if (ptr == s.size()) // unexpected end of line
     269                 :       87160 :             return std::map<std::string,std::string>();
     270         [ +  + ]:      159981 :         if (s[ptr] == ' ') // The remaining string is an OptArguments
     271                 :             :             break;
     272                 :      123086 :         ++ptr; // skip '='
     273   [ +  +  +  + ]:      123086 :         if (ptr < s.size() && s[ptr] == '"') { // Quoted string
     274                 :       26300 :             ++ptr; // skip opening '"'
     275                 :       26300 :             bool escape_next = false;
     276   [ -  +  +  +  :      166055 :             while (ptr < s.size() && (escape_next || s[ptr] != '"')) {
             +  +  +  + ]
     277                 :             :                 // Repeated backslashes must be interpreted as pairs
     278   [ +  +  +  + ]:      139755 :                 escape_next = (s[ptr] == '\\' && !escape_next);
     279         [ +  - ]:      139755 :                 value.push_back(s[ptr]);
     280                 :      139755 :                 ++ptr;
     281                 :             :             }
     282         [ +  + ]:       26300 :             if (ptr == s.size()) // unexpected end of line
     283                 :        5230 :                 return std::map<std::string,std::string>();
     284                 :       21070 :             ++ptr; // skip closing '"'
     285                 :             :             /**
     286                 :             :              * Unescape value. Per https://spec.torproject.org/control-spec section 2.1.1:
     287                 :             :              *
     288                 :             :              *   For future-proofing, controller implementers MAY use the following
     289                 :             :              *   rules to be compatible with buggy Tor implementations and with
     290                 :             :              *   future ones that implement the spec as intended:
     291                 :             :              *
     292                 :             :              *     Read \n \t \r and \0 ... \377 as C escapes.
     293                 :             :              *     Treat a backslash followed by any other character as that character.
     294                 :             :              */
     295                 :       21070 :             std::string escaped_value;
     296   [ -  +  +  + ]:       96779 :             for (size_t i = 0; i < value.size(); ++i) {
     297         [ +  + ]:       75709 :                 if (value[i] == '\\') {
     298                 :             :                     // This will always be valid, because if the QuotedString
     299                 :             :                     // ended in an odd number of backslashes, then the parser
     300                 :             :                     // would already have returned above, due to a missing
     301                 :             :                     // terminating double-quote.
     302                 :       34334 :                     ++i;
     303         [ +  + ]:       34334 :                     if (value[i] == 'n') {
     304         [ +  - ]:         672 :                         escaped_value.push_back('\n');
     305         [ +  + ]:       33662 :                     } else if (value[i] == 't') {
     306         [ +  - ]:         464 :                         escaped_value.push_back('\t');
     307         [ +  + ]:       33198 :                     } else if (value[i] == 'r') {
     308         [ +  - ]:        1843 :                         escaped_value.push_back('\r');
     309   [ +  +  +  + ]:       31355 :                     } else if ('0' <= value[i] && value[i] <= '7') {
     310                 :             :                         size_t j;
     311                 :             :                         // Octal escape sequences have a limit of three octal digits,
     312                 :             :                         // but terminate at the first character that is not a valid
     313                 :             :                         // octal digit if encountered sooner.
     314   [ +  +  +  +  :       42552 :                         for (j = 1; j < 3 && (i+j) < value.size() && '0' <= value[i+j] && value[i+j] <= '7'; ++j) {}
             +  +  +  + ]
     315                 :             :                         // Tor restricts first digit to 0-3 for three-digit octals.
     316                 :             :                         // A leading digit of 4-7 would therefore be interpreted as
     317                 :             :                         // a two-digit octal.
     318   [ +  +  +  + ]:       30097 :                         if (j == 3 && value[i] > '3') {
     319                 :        2054 :                             j--;
     320                 :             :                         }
     321                 :       30097 :                         const auto end{i + j};
     322                 :       30097 :                         uint8_t val{0};
     323         [ +  + ]:       70595 :                         while (i < end) {
     324                 :       40498 :                             val *= 8;
     325                 :       40498 :                             val += value[i++] - '0';
     326                 :             :                         }
     327         [ +  - ]:       30097 :                         escaped_value.push_back(char(val));
     328                 :             :                         // Account for automatic incrementing at loop end
     329                 :       30097 :                         --i;
     330                 :             :                     } else {
     331         [ +  - ]:        1258 :                         escaped_value.push_back(value[i]);
     332                 :             :                     }
     333                 :             :                 } else {
     334         [ +  - ]:       41375 :                     escaped_value.push_back(value[i]);
     335                 :             :                 }
     336                 :             :             }
     337         [ +  - ]:       42140 :             value = escaped_value;
     338                 :       21070 :         } else { // Unquoted value. Note that values can contain '=' at will, just no spaces
     339   [ -  +  +  +  :      350599 :             while (ptr < s.size() && s[ptr] != ' ') {
                   +  + ]
     340         [ +  - ]:      253813 :                 value.push_back(s[ptr]);
     341                 :      253813 :                 ++ptr;
     342                 :             :             }
     343                 :             :         }
     344   [ -  +  +  +  :      117856 :         if (ptr < s.size() && s[ptr] == ' ')
                   +  + ]
     345                 :       49187 :             ++ptr; // skip ' ' after key=value
     346   [ +  -  +  - ]:      235712 :         mapping[key] = value;
     347                 :      247141 :     }
     348                 :      162942 :     return mapping;
     349                 :      255332 : }
     350                 :             : 
     351                 :           0 : TorController::TorController(const std::string& tor_control_center, const CService& target)
     352         [ #  # ]:           0 :     : m_tor_control_center(tor_control_center),
     353         [ #  # ]:           0 :       m_conn(m_interrupt),
     354         [ #  # ]:           0 :       m_reconnect(true),
     355         [ #  # ]:           0 :       m_reconnect_timeout(RECONNECT_TIMEOUT_START),
     356   [ #  #  #  #  :           0 :       m_target(target)
                   #  # ]
     357                 :             : {
     358                 :             :     // Read service private key if cached
     359   [ #  #  #  # ]:           0 :     std::pair<bool,std::string> pkf = ReadBinaryFile(GetPrivateKeyFile());
     360         [ #  # ]:           0 :     if (pkf.first) {
     361   [ #  #  #  #  :           0 :         LogDebug(BCLog::TOR, "Reading cached private key from %s", fs::PathToString(GetPrivateKeyFile()));
             #  #  #  # ]
     362         [ #  # ]:           0 :         m_private_key = pkf.second;
     363                 :             :     }
     364         [ #  # ]:           0 :     m_thread = std::thread(&util::TraceThread, "torcontrol", [this] { ThreadControl(); });
     365                 :           0 : }
     366                 :             : 
     367                 :         620 : TorController::~TorController()
     368                 :             : {
     369                 :         620 :     Interrupt();
     370                 :         620 :     Join();
     371         [ -  + ]:         620 :     if (m_service.IsValid()) {
     372                 :           0 :         RemoveLocal(m_service);
     373                 :             :     }
     374                 :        1240 : }
     375                 :             : 
     376                 :         620 : void TorController::Interrupt()
     377                 :             : {
     378                 :         620 :     m_reconnect = false;
     379                 :         620 :     m_interrupt();
     380                 :         620 : }
     381                 :             : 
     382                 :         620 : void TorController::Join()
     383                 :             : {
     384         [ -  + ]:         620 :     if (m_thread.joinable()) {
     385                 :           0 :         m_thread.join();
     386                 :             :     }
     387                 :         620 : }
     388                 :             : 
     389                 :           0 : void TorController::ThreadControl()
     390                 :             : {
     391         [ #  # ]:           0 :     LogDebug(BCLog::TOR, "Entering Tor control thread");
     392                 :             : 
     393         [ #  # ]:           0 :     while (!m_interrupt) {
     394                 :             :         // Try to connect if not connected already
     395         [ #  # ]:           0 :         if (!m_conn.IsConnected()) {
     396         [ #  # ]:           0 :             LogDebug(BCLog::TOR, "Attempting to connect to Tor control port %s", m_tor_control_center);
     397                 :             : 
     398         [ #  # ]:           0 :             if (!m_conn.Connect(m_tor_control_center)) {
     399                 :           0 :                 LogWarning("tor: Initiating connection to Tor control port %s failed", m_tor_control_center);
     400         [ #  # ]:           0 :                 if (!m_reconnect) {
     401                 :             :                     break;
     402                 :             :                 }
     403                 :             :                 // Wait before retrying with exponential backoff
     404         [ #  # ]:           0 :                 LogDebug(BCLog::TOR, "Retrying in %.1f seconds", m_reconnect_timeout.count());
     405         [ #  # ]:           0 :                 if (!m_interrupt.sleep_for(std::chrono::duration_cast<std::chrono::milliseconds>(m_reconnect_timeout))) {
     406                 :             :                     break;
     407                 :             :                 }
     408                 :           0 :                 m_reconnect_timeout = std::min(m_reconnect_timeout * RECONNECT_TIMEOUT_EXP, RECONNECT_TIMEOUT_MAX);
     409                 :           0 :                 continue;
     410                 :             :             }
     411                 :             :             // Successfully connected, reset timeout and trigger connected callback
     412                 :           0 :             m_reconnect_timeout = RECONNECT_TIMEOUT_START;
     413                 :           0 :             connected_cb(m_conn);
     414                 :             :         }
     415                 :             :         // Wait for data with a timeout
     416         [ #  # ]:           0 :         if (!m_conn.WaitForData(std::chrono::seconds(1))) {
     417                 :             :             // Check if still connected
     418         [ #  # ]:           0 :             if (!m_conn.IsConnected()) {
     419         [ #  # ]:           0 :                 LogDebug(BCLog::TOR, "Lost connection to Tor control port");
     420                 :           0 :                 disconnected_cb(m_conn);
     421                 :           0 :                 continue;
     422                 :             :             }
     423                 :             :             // Just a timeout, continue waiting
     424                 :           0 :             continue;
     425                 :             :         }
     426                 :             :         // Process incoming data
     427         [ #  # ]:           0 :         if (!m_conn.ReceiveAndProcess()) {
     428                 :           0 :             disconnected_cb(m_conn);
     429                 :             :         }
     430                 :             :     }
     431         [ #  # ]:           0 :     LogDebug(BCLog::TOR, "Exited Tor control thread");
     432                 :           0 : }
     433                 :             : 
     434                 :        1331 : void TorController::get_socks_cb(TorControlConnection& _conn, const TorControlReply& reply)
     435                 :             : {
     436                 :             :     // NOTE: We can only get here if -onion is unset
     437         [ +  + ]:        1331 :     std::string socks_location;
     438         [ +  + ]:        1331 :     if (reply.code == TOR_REPLY_OK) {
     439         [ +  + ]:         573 :         for (const auto& line : reply.lines) {
     440   [ -  +  -  + ]:         350 :             if (line.starts_with("net/listeners/socks=")) {
     441         [ #  # ]:           0 :                 const std::string port_list_str = line.substr(20);
     442   [ #  #  #  # ]:           0 :                 std::vector<std::string> port_list = SplitString(port_list_str, ' ');
     443                 :             : 
     444         [ #  # ]:           0 :                 for (auto& portstr : port_list) {
     445         [ #  # ]:           0 :                     if (portstr.empty()) continue;
     446   [ #  #  #  #  :           0 :                     if ((portstr[0] == '"' || portstr[0] == '\'') && portstr.size() >= 2 && (*portstr.rbegin() == portstr[0])) {
             #  #  #  # ]
     447         [ #  # ]:           0 :                         portstr = portstr.substr(1, portstr.size() - 2);
     448         [ #  # ]:           0 :                         if (portstr.empty()) continue;
     449                 :             :                     }
     450         [ #  # ]:           0 :                     socks_location = portstr;
     451   [ #  #  #  # ]:           0 :                     if (portstr.starts_with("127.0.0.1:")) {
     452                 :             :                         // Prefer localhost - ignore other ports
     453                 :             :                         break;
     454                 :             :                     }
     455                 :             :                 }
     456                 :           0 :             }
     457                 :             :         }
     458         [ -  + ]:         223 :         if (!socks_location.empty()) {
     459   [ #  #  #  #  :           0 :             LogDebug(BCLog::TOR, "Get SOCKS port command yielded %s", socks_location);
                   #  # ]
     460                 :             :         } else {
     461         [ +  - ]:         223 :             LogWarning("tor: Get SOCKS port command returned nothing");
     462                 :             :         }
     463         [ +  + ]:        1108 :     } else if (reply.code == TOR_REPLY_UNRECOGNIZED) {
     464         [ +  - ]:         803 :         LogWarning("tor: Get SOCKS port command failed with unrecognized command (You probably should upgrade Tor)");
     465                 :             :     } else {
     466         [ +  - ]:         305 :         LogWarning("tor: Get SOCKS port command failed; error code %d", reply.code);
     467                 :             :     }
     468                 :             : 
     469         [ +  - ]:        1331 :     CService resolved;
     470   [ +  -  -  + ]:        1331 :     Assume(!resolved.IsValid());
     471         [ -  + ]:        1331 :     if (!socks_location.empty()) {
     472   [ #  #  #  # ]:           0 :         resolved = LookupNumeric(socks_location, DEFAULT_TOR_SOCKS_PORT);
     473                 :             :     }
     474   [ +  -  +  - ]:        1331 :     if (!resolved.IsValid()) {
     475                 :             :         // Fallback to old behaviour
     476   [ +  -  +  -  :        2662 :         resolved = LookupNumeric("127.0.0.1", DEFAULT_TOR_SOCKS_PORT);
                   +  - ]
     477                 :             :     }
     478                 :             : 
     479   [ +  -  -  + ]:        1331 :     Assume(resolved.IsValid());
     480   [ +  -  -  +  :        1331 :     LogDebug(BCLog::TOR, "Configuring onion proxy for %s", resolved.ToStringAddrPort());
             -  -  -  - ]
     481                 :             : 
     482                 :             :     // Add Tor as proxy for .onion addresses.
     483                 :             :     // Enable stream isolation to prevent connection correlation and enhance privacy, by forcing a different Tor circuit for every connection.
     484                 :             :     // 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).
     485                 :        1331 :     Proxy addrOnion = Proxy(resolved, /*tor_stream_isolation=*/ true);
     486         [ +  - ]:        1331 :     SetProxy(NET_ONION, addrOnion);
     487                 :             : 
     488   [ +  -  +  - ]:        1331 :     const auto onlynets = gArgs.GetArgs("-onlynet");
     489                 :             : 
     490                 :        1331 :     const bool onion_allowed_by_onlynet{
     491   [ -  +  -  - ]:        1331 :         onlynets.empty() ||
     492         [ #  # ]:           0 :         std::any_of(onlynets.begin(), onlynets.end(), [](const auto& n) {
     493                 :           0 :             return ParseNetwork(n) == NET_ONION;
     494                 :        2662 :         })};
     495                 :             : 
     496                 :        1331 :     if (onion_allowed_by_onlynet) {
     497                 :             :         // If NET_ONION is reachable, then the below is a noop.
     498                 :             :         //
     499                 :             :         // If NET_ONION is not reachable, then none of -proxy or -onion was given.
     500                 :             :         // Since we are here, then -torcontrol and -torpassword were given.
     501         [ +  - ]:        1331 :         g_reachable_nets.Add(NET_ONION);
     502                 :             :     }
     503                 :        2662 : }
     504                 :             : 
     505                 :       34501 : static std::string MakeAddOnionCmd(const std::string& private_key, const std::string& target, bool enable_pow)
     506                 :             : {
     507                 :             :     // Note that the 'virtual' port is always the default port to avoid decloaking nodes using other ports.
     508                 :       34501 :     return strprintf("ADD_ONION %s%s Port=%i,%s",
     509                 :             :                      private_key,
     510         [ +  + ]:       34501 :                      enable_pow ? " PoWDefensesEnabled=1" : "",
     511         [ +  + ]:       34501 :                      Params().GetDefaultPort(),
     512                 :       34501 :                      target);
     513                 :             : }
     514                 :             : 
     515                 :      204289 : void TorController::add_onion_cb(TorControlConnection& _conn, const TorControlReply& reply, bool pow_was_enabled)
     516                 :             : {
     517         [ +  + ]:      204289 :     if (reply.code == TOR_REPLY_OK) {
     518   [ -  +  -  - ]:       34233 :         LogDebug(BCLog::TOR, "ADD_ONION successful (PoW defenses %s)", pow_was_enabled ? "enabled" : "disabled");
     519         [ +  + ]:      274430 :         for (const std::string &s : reply.lines) {
     520                 :      240197 :             std::map<std::string,std::string> m = ParseTorReplyMapping(s);
     521         [ +  - ]:      240197 :             std::map<std::string,std::string>::iterator i;
     522   [ +  -  +  + ]:      480394 :             if ((i = m.find("ServiceID")) != m.end())
     523         [ +  - ]:        1894 :                 m_service_id = i->second;
     524   [ +  -  +  + ]:      480394 :             if ((i = m.find("PrivateKey")) != m.end())
     525         [ +  - ]:      241986 :                 m_private_key = i->second;
     526                 :      240197 :         }
     527         [ +  + ]:       34233 :         if (m_service_id.empty()) {
     528                 :        6974 :             LogWarning("tor: Error parsing ADD_ONION parameters:");
     529         [ +  + ]:       54964 :             for (const std::string &s : reply.lines) {
     530   [ -  +  +  - ]:       47990 :                 LogWarning("    %s", SanitizeString(s));
     531                 :             :             }
     532                 :             :             return;
     533                 :             :         }
     534   [ +  -  +  -  :       27259 :         m_service = LookupNumeric(std::string(m_service_id+".onion"), Params().GetDefaultPort());
                   +  - ]
     535         [ +  - ]:       27259 :         LogInfo("Got tor service ID %s, advertising service %s", m_service_id, m_service.ToStringAddrPort());
     536   [ +  -  +  - ]:       54518 :         if (WriteBinaryFile(GetPrivateKeyFile(), m_private_key)) {
     537   [ -  +  -  -  :       27259 :             LogDebug(BCLog::TOR, "Cached service private key to %s", fs::PathToString(GetPrivateKeyFile()));
                   -  - ]
     538                 :             :         } else {
     539   [ #  #  #  # ]:           0 :             LogWarning("tor: Error writing service private key to %s", fs::PathToString(GetPrivateKeyFile()));
     540                 :             :         }
     541                 :       27259 :         AddLocal(m_service, LOCAL_MANUAL);
     542                 :             :         // ... onion requested - keep connection open
     543         [ +  + ]:      170056 :     } else if (reply.code == TOR_REPLY_UNRECOGNIZED) {
     544                 :       33558 :         LogWarning("tor: Add onion failed with unrecognized command (You probably need to upgrade Tor)");
     545   [ +  +  +  + ]:      136498 :     } else if (pow_was_enabled && reply.code == TOR_REPLY_SYNTAX_ERROR) {
     546         [ -  + ]:       33549 :         LogDebug(BCLog::TOR, "ADD_ONION failed with PoW defenses, retrying without");
     547   [ +  -  +  - ]:       67098 :         _conn.Command(MakeAddOnionCmd(m_private_key, m_target.ToStringAddrPort(), /*enable_pow=*/false),
     548         [ +  - ]:       67098 :                       [this](TorControlConnection& conn, const TorControlReply& reply) {
     549                 :           0 :                           add_onion_cb(conn, reply, /*pow_was_enabled=*/false);
     550                 :             :                       });
     551                 :             :     } else {
     552                 :      102949 :         LogWarning("tor: Add onion failed; error code %d", reply.code);
     553                 :             :     }
     554                 :             : }
     555                 :             : 
     556                 :       19357 : void TorController::auth_cb(TorControlConnection& _conn, const TorControlReply& reply)
     557                 :             : {
     558         [ +  + ]:       19357 :     if (reply.code == TOR_REPLY_OK) {
     559         [ -  + ]:         952 :         LogDebug(BCLog::TOR, "Authentication successful");
     560                 :             : 
     561                 :             :         // Now that we know Tor is running setup the proxy for onion addresses
     562                 :             :         // if -onion isn't set to something else.
     563   [ +  -  +  -  :         952 :         if (gArgs.GetArg("-onion", "") == "") {
                   +  - ]
     564   [ +  -  +  - ]:        1904 :             _conn.Command("GETINFO net/listeners/socks", std::bind_front(&TorController::get_socks_cb, this));
     565                 :             :         }
     566                 :             : 
     567                 :             :         // Finally - now create the service
     568         [ +  + ]:         952 :         if (m_private_key.empty()) { // No private key, generate one
     569                 :          85 :             m_private_key = "NEW:ED25519-V3"; // Explicitly request key type - see issue #9214
     570                 :             :         }
     571                 :             :         // Request onion service, redirect port.
     572   [ +  -  +  - ]:        1904 :         _conn.Command(MakeAddOnionCmd(m_private_key, m_target.ToStringAddrPort(), /*enable_pow=*/true),
     573         [ +  - ]:        1904 :                       [this](TorControlConnection& conn, const TorControlReply& reply) {
     574                 :           0 :                           add_onion_cb(conn, reply, /*pow_was_enabled=*/true);
     575                 :             :                       });
     576                 :             :     } else {
     577                 :       18405 :         LogWarning("tor: Authentication failed");
     578                 :             :     }
     579                 :       19357 : }
     580                 :             : 
     581                 :             : /** Compute Tor SAFECOOKIE response.
     582                 :             :  *
     583                 :             :  *    ServerHash is computed as:
     584                 :             :  *      HMAC-SHA256("Tor safe cookie authentication server-to-controller hash",
     585                 :             :  *                  CookieString | ClientNonce | ServerNonce)
     586                 :             :  *    (with the HMAC key as its first argument)
     587                 :             :  *
     588                 :             :  *    After a controller sends a successful AUTHCHALLENGE command, the
     589                 :             :  *    next command sent on the connection must be an AUTHENTICATE command,
     590                 :             :  *    and the only authentication string which that AUTHENTICATE command
     591                 :             :  *    will accept is:
     592                 :             :  *
     593                 :             :  *      HMAC-SHA256("Tor safe cookie authentication controller-to-server hash",
     594                 :             :  *                  CookieString | ClientNonce | ServerNonce)
     595                 :             :  *
     596                 :             :  */
     597                 :           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)
     598                 :             : {
     599                 :           0 :     CHMAC_SHA256 computeHash((const uint8_t*)key.data(), key.size());
     600                 :           0 :     std::vector<uint8_t> computedHash(CHMAC_SHA256::OUTPUT_SIZE, 0);
     601         [ #  # ]:           0 :     computeHash.Write(cookie.data(), cookie.size());
     602         [ #  # ]:           0 :     computeHash.Write(client_nonce.data(), client_nonce.size());
     603         [ #  # ]:           0 :     computeHash.Write(server_nonce.data(), server_nonce.size());
     604         [ #  # ]:           0 :     computeHash.Finalize(computedHash.data());
     605                 :           0 :     return computedHash;
     606                 :           0 : }
     607                 :             : 
     608                 :       18516 : void TorController::authchallenge_cb(TorControlConnection& _conn, const TorControlReply& reply)
     609                 :             : {
     610         [ +  + ]:       18516 :     if (reply.code == TOR_REPLY_OK) {
     611         [ -  + ]:         761 :         LogDebug(BCLog::TOR, "SAFECOOKIE authentication challenge successful");
     612         [ +  + ]:         761 :         if (reply.lines.empty()) {
     613                 :         116 :             LogWarning("tor: AUTHCHALLENGE reply was empty");
     614                 :         116 :             return;
     615                 :             :         }
     616                 :         645 :         std::pair<std::string,std::string> l = SplitTorReplyLine(reply.lines[0]);
     617         [ +  + ]:         645 :         if (l.first == "AUTHCHALLENGE") {
     618         [ +  - ]:         208 :             std::map<std::string,std::string> m = ParseTorReplyMapping(l.second);
     619         [ +  + ]:         208 :             if (m.empty()) {
     620   [ -  +  +  -  :          27 :                 LogWarning("tor: Error parsing AUTHCHALLENGE parameters: %s", SanitizeString(l.second));
                   +  - ]
     621                 :          27 :                 return;
     622                 :             :             }
     623   [ +  -  +  -  :         181 :             std::vector<uint8_t> server_hash = ParseHex(m["SERVERHASH"]);
             -  +  +  - ]
     624   [ +  -  +  -  :         181 :             std::vector<uint8_t> server_nonce = ParseHex(m["SERVERNONCE"]);
             -  +  +  - ]
     625                 :         181 :             LogDebug(BCLog::TOR, "AUTHCHALLENGE ServerHash %s ServerNonce %s", HexStr(server_hash), HexStr(server_nonce));
           [ +  -  -  +  
          -  -  -  -  -  
                -  -  - ]
     626   [ -  +  +  - ]:         181 :             if (server_nonce.size() != 32) {
     627         [ +  - ]:         181 :                 LogWarning("tor: ServerNonce is not 32 bytes, as required by spec");
     628                 :             :                 return;
     629                 :             :             }
     630                 :             : 
     631   [ #  #  #  #  :           0 :             std::vector<uint8_t> computed_server_hash = ComputeResponse(TOR_SAFE_SERVERKEY, m_cookie, m_client_nonce, server_nonce);
             #  #  #  # ]
     632         [ #  # ]:           0 :             if (computed_server_hash != server_hash) {
     633   [ #  #  #  #  :           0 :                 LogWarning("tor: ServerHash %s does not match expected ServerHash %s", HexStr(server_hash), HexStr(computed_server_hash));
             #  #  #  # ]
     634                 :           0 :                 return;
     635                 :             :             }
     636                 :             : 
     637   [ #  #  #  #  :           0 :             std::vector<uint8_t> computedClientHash = ComputeResponse(TOR_SAFE_CLIENTKEY, m_cookie, m_client_nonce, server_nonce);
          #  #  #  #  #  
                      # ]
     638   [ #  #  #  #  :           0 :             _conn.Command("AUTHENTICATE " + HexStr(computedClientHash), std::bind_front(&TorController::auth_cb, this));
             #  #  #  # ]
     639                 :         389 :         } else {
     640         [ +  - ]:         437 :             LogWarning("tor: Invalid reply to AUTHCHALLENGE");
     641                 :             :         }
     642                 :         645 :     } else {
     643                 :       17755 :         LogWarning("tor: SAFECOOKIE authentication challenge failed");
     644                 :             :     }
     645                 :             : }
     646                 :             : 
     647                 :       36774 : void TorController::protocolinfo_cb(TorControlConnection& _conn, const TorControlReply& reply)
     648                 :             : {
     649         [ +  + ]:       36774 :     if (reply.code == TOR_REPLY_OK) {
     650                 :       24723 :         std::set<std::string> methods;
     651                 :       24723 :         std::string cookiefile;
     652                 :             :         /*
     653                 :             :          * 250-AUTH METHODS=COOKIE,SAFECOOKIE COOKIEFILE="/home/x/.tor/control_auth_cookie"
     654                 :             :          * 250-AUTH METHODS=NULL
     655                 :             :          * 250-AUTH METHODS=HASHEDPASSWORD
     656                 :             :          */
     657         [ +  + ]:      349309 :         for (const std::string &s : reply.lines) {
     658         [ +  - ]:      324586 :             std::pair<std::string,std::string> l = SplitTorReplyLine(s);
     659         [ +  + ]:      324586 :             if (l.first == "AUTH") {
     660         [ +  - ]:       12432 :                 std::map<std::string,std::string> m = ParseTorReplyMapping(l.second);
     661         [ +  - ]:       12432 :                 std::map<std::string,std::string>::iterator i;
     662   [ +  -  +  + ]:       24864 :                 if ((i = m.find("METHODS")) != m.end()) {
     663   [ -  +  +  - ]:       10101 :                     std::vector<std::string> m_vec = SplitString(i->second, ',');
     664         [ +  - ]:       20202 :                     methods = std::set<std::string>(m_vec.begin(), m_vec.end());
     665                 :       10101 :                 }
     666   [ +  -  +  + ]:       24864 :                 if ((i = m.find("COOKIEFILE")) != m.end())
     667         [ +  - ]:       12809 :                     cookiefile = i->second;
     668         [ +  + ]:      324586 :             } else if (l.first == "VERSION") {
     669         [ +  - ]:        2495 :                 std::map<std::string,std::string> m = ParseTorReplyMapping(l.second);
     670         [ +  - ]:        2495 :                 std::map<std::string,std::string>::iterator i;
     671   [ +  -  +  + ]:        4990 :                 if ((i = m.find("Tor")) != m.end()) {
     672   [ +  -  -  +  :        2495 :                     LogDebug(BCLog::TOR, "Connected to Tor version %s", i->second);
                   -  - ]
     673                 :             :                 }
     674                 :        2495 :             }
     675                 :      324586 :         }
     676         [ +  + ]:       28057 :         for (const std::string &s : methods) {
     677   [ +  -  -  +  :        3334 :             LogDebug(BCLog::TOR, "Supported authentication method: %s", s);
                   -  - ]
     678                 :             :         }
     679                 :             :         // Prefer NULL, otherwise SAFECOOKIE. If a password is provided, use HASHEDPASSWORD
     680                 :             :         /* Authentication:
     681                 :             :          *   cookie:   hex-encoded ~/.tor/control_auth_cookie
     682                 :             :          *   password: "password"
     683                 :             :          */
     684   [ +  -  +  -  :       49446 :         std::string torpassword = gArgs.GetArg("-torpassword", "");
                   +  - ]
     685         [ -  + ]:       24723 :         if (!torpassword.empty()) {
     686   [ #  #  #  # ]:           0 :             if (methods.contains("HASHEDPASSWORD")) {
     687   [ #  #  #  #  :           0 :                 LogDebug(BCLog::TOR, "Using HASHEDPASSWORD authentication");
                   #  # ]
     688   [ #  #  #  #  :           0 :                 ReplaceAll(torpassword, "\"", "\\\"");
                   #  # ]
     689   [ #  #  #  #  :           0 :                 _conn.Command("AUTHENTICATE \"" + torpassword + "\"", std::bind_front(&TorController::auth_cb, this));
                   #  # ]
     690                 :             :             } else {
     691         [ #  # ]:           0 :                 LogWarning("tor: Password provided with -torpassword, but HASHEDPASSWORD authentication is not available");
     692                 :             :             }
     693   [ +  -  -  + ]:       24723 :         } else if (methods.contains("NULL")) {
     694   [ #  #  #  #  :           0 :             LogDebug(BCLog::TOR, "Using NULL authentication");
                   #  # ]
     695   [ #  #  #  #  :           0 :             _conn.Command("AUTHENTICATE", std::bind_front(&TorController::auth_cb, this));
                   #  # ]
     696   [ +  -  -  + ]:       24723 :         } else if (methods.contains("SAFECOOKIE")) {
     697                 :             :             // Cookie: hexdump -e '32/1 "%02x""\n"'  ~/.tor/control_auth_cookie
     698   [ #  #  #  #  :           0 :             LogDebug(BCLog::TOR, "Using SAFECOOKIE authentication, reading cookie authentication from %s", cookiefile);
                   #  # ]
     699   [ #  #  #  # ]:           0 :             std::pair<bool,std::string> status_cookie = ReadBinaryFile(fs::PathFromString(cookiefile), TOR_COOKIE_SIZE);
     700   [ #  #  #  # ]:           0 :             if (status_cookie.first && status_cookie.second.size() == TOR_COOKIE_SIZE) {
     701                 :             :                 // _conn.Command("AUTHENTICATE " + HexStr(status_cookie.second), std::bind_front(&TorController::auth_cb, this));
     702   [ #  #  #  # ]:           0 :                 m_cookie = std::vector<uint8_t>(status_cookie.second.begin(), status_cookie.second.end());
     703   [ #  #  #  # ]:           0 :                 m_client_nonce = std::vector<uint8_t>(TOR_NONCE_SIZE, 0);
     704         [ #  # ]:           0 :                 GetRandBytes(m_client_nonce);
     705   [ #  #  #  #  :           0 :                 _conn.Command("AUTHCHALLENGE SAFECOOKIE " + HexStr(m_client_nonce), std::bind_front(&TorController::authchallenge_cb, this));
          #  #  #  #  #  
                      # ]
     706                 :             :             } else {
     707         [ #  # ]:           0 :                 if (status_cookie.first) {
     708         [ #  # ]:           0 :                     LogWarning("tor: Authentication cookie %s is not exactly %i bytes, as is required by the spec", cookiefile, TOR_COOKIE_SIZE);
     709                 :             :                 } else {
     710         [ #  # ]:           0 :                     LogWarning("tor: Authentication cookie %s could not be opened (check permissions)", cookiefile);
     711                 :             :                 }
     712                 :             :             }
     713   [ +  -  -  + ]:       24723 :         } else if (methods.contains("HASHEDPASSWORD")) {
     714         [ #  # ]:           0 :             LogWarning("tor: The only supported authentication mechanism left is password, but no password provided with -torpassword");
     715                 :             :         } else {
     716         [ +  - ]:       24723 :             LogWarning("tor: No supported authentication method");
     717                 :             :         }
     718                 :       24723 :     } else {
     719                 :       12051 :         LogWarning("tor: Requesting protocol info failed");
     720                 :             :     }
     721                 :       36774 : }
     722                 :             : 
     723                 :           0 : void TorController::connected_cb(TorControlConnection& _conn)
     724                 :             : {
     725                 :           0 :     m_reconnect_timeout = RECONNECT_TIMEOUT_START;
     726                 :             :     // First send a PROTOCOLINFO command to figure out what authentication is expected
     727   [ #  #  #  #  :           0 :     if (!_conn.Command("PROTOCOLINFO 1", std::bind_front(&TorController::protocolinfo_cb, this)))
                   #  # ]
     728                 :           0 :         LogWarning("tor: Error sending initial protocolinfo command");
     729                 :           0 : }
     730                 :             : 
     731                 :           0 : void TorController::disconnected_cb(TorControlConnection& _conn)
     732                 :             : {
     733                 :             :     // Stop advertising service when disconnected
     734         [ #  # ]:           0 :     if (m_service.IsValid())
     735                 :           0 :         RemoveLocal(m_service);
     736                 :           0 :     m_service = CService();
     737         [ #  # ]:           0 :     if (!m_reconnect)
     738                 :             :         return;
     739                 :             : 
     740         [ #  # ]:           0 :     LogDebug(BCLog::TOR, "Not connected to Tor control port %s, will retry", m_tor_control_center);
     741                 :           0 :     _conn.Disconnect();
     742                 :             : }
     743                 :             : 
     744                 :       27259 : fs::path TorController::GetPrivateKeyFile()
     745                 :             : {
     746         [ +  - ]:       81777 :     return gArgs.GetDataDirNet() / "onion_v3_private_key";
     747                 :             : }
     748                 :             : 
     749                 :           0 : CService DefaultOnionServiceTarget(uint16_t port)
     750                 :             : {
     751                 :           0 :     struct in_addr onion_service_target;
     752                 :           0 :     onion_service_target.s_addr = htonl(INADDR_LOOPBACK);
     753                 :           0 :     return {onion_service_target, port};
     754                 :             : }
        

Generated by: LCOV version 2.5.0-full