LCOV - code coverage report
Current view: top level - src - torcontrol.cpp (source / functions) Coverage Total Hit
Test: fuzz_coverage.info Lines: 54.1 % 410 222
Test Date: 2026-11-11 05:55:56 Functions: 57.1 % 28 16
Branches: 33.6 % 780 262

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

Generated by: LCOV version 2.5.0-full