/ src / torcontrol.h
torcontrol.h
  1  // Copyright (c) 2015-present The Bitcoin Core developers
  2  // Distributed under the MIT software license, see the accompanying
  3  // file COPYING or http://www.opensource.org/licenses/mit-license.php.
  4  
  5  /**
  6   * Functionality for communicating with Tor.
  7   */
  8  #ifndef BITCOIN_TORCONTROL_H
  9  #define BITCOIN_TORCONTROL_H
 10  
 11  #include <netaddress.h>
 12  #include <util/fs.h>
 13  #include <util/sock.h>
 14  #include <util/threadinterrupt.h>
 15  
 16  #include <cstdint>
 17  #include <deque>
 18  #include <functional>
 19  #include <memory>
 20  #include <string>
 21  #include <thread>
 22  #include <vector>
 23  
 24  constexpr uint16_t DEFAULT_TOR_SOCKS_PORT{9050};
 25  constexpr int DEFAULT_TOR_CONTROL_PORT = 9051;
 26  extern const std::string DEFAULT_TOR_CONTROL;
 27  static const bool DEFAULT_LISTEN_ONION = true;
 28  
 29  /** Tor control reply code. Ref: https://spec.torproject.org/control-spec/replies.html */
 30  constexpr int TOR_REPLY_OK{250};
 31  constexpr int TOR_REPLY_UNRECOGNIZED{510};
 32  constexpr int TOR_REPLY_SYNTAX_ERROR{512}; //!< Syntax error in command argument
 33  
 34  CService DefaultOnionServiceTarget(uint16_t port);
 35  
 36  /** Reply from Tor, can be single or multi-line */
 37  class TorControlReply
 38  {
 39  public:
 40      TorControlReply() { Clear(); }
 41  
 42      int code;
 43      std::vector<std::string> lines;
 44  
 45      void Clear()
 46      {
 47          code = 0;
 48          lines.clear();
 49      }
 50  };
 51  
 52  /** Low-level handling for Tor control connection.
 53   * Speaks the SMTP-like protocol as defined in torspec/control-spec.txt
 54   */
 55  class TorControlConnection
 56  {
 57  public:
 58      typedef std::function<void(TorControlConnection &,const TorControlReply &)> ReplyHandlerCB;
 59  
 60      /** Create a new TorControlConnection.
 61       */
 62      explicit TorControlConnection(CThreadInterrupt& interrupt);
 63      ~TorControlConnection();
 64  
 65      /**
 66       * Connect to a Tor control port.
 67       * tor_control_center is address of the form host:port.
 68       * Return true on success.
 69       */
 70      bool Connect(const std::string& tor_control_center);
 71  
 72      /**
 73       * Disconnect from Tor control port.
 74       */
 75      void Disconnect();
 76  
 77      /** Send a command, register a handler for the reply.
 78       * A trailing CRLF is automatically added.
 79       * Return true on success.
 80       */
 81      bool Command(const std::string &cmd, const ReplyHandlerCB& reply_handler);
 82  
 83      /**
 84       * Check if the connection is established.
 85       */
 86      bool IsConnected() const;
 87  
 88      /**
 89       * Wait for data to be available on the socket.
 90       * @param[in] timeout Maximum time to wait
 91       * @return true if data is available, false on timeout or error
 92       */
 93      bool WaitForData(std::chrono::milliseconds timeout);
 94  
 95      /**
 96       * Read available data from socket and process complete replies.
 97       * Dispatches to registered reply handlers.
 98       * @return true if connection is still open, false if connection was closed
 99       */
100      bool ReceiveAndProcess();
101  
102  private:
103      /** Reference to interrupt object for clean shutdown */
104      CThreadInterrupt& m_interrupt;
105      /** Socket for the connection */
106      std::unique_ptr<Sock> m_sock;
107      /** Message being received */
108      TorControlReply m_message;
109      /** Response handlers */
110      std::deque<ReplyHandlerCB> m_reply_handlers;
111      /** Buffer for incoming data */
112      std::vector<std::byte> m_recv_buffer;
113      /** Process complete lines from the receive buffer */
114      bool ProcessBuffer();
115  };
116  
117  /****** Bitcoin specific TorController implementation ********/
118  
119  /** Controller that connects to Tor control socket, authenticate, then create
120   * and maintain an ephemeral onion service.
121   */
122  class TorController
123  {
124  public:
125      TorController(const std::string& tor_control_center, const CService& target);
126      TorController() : m_conn(m_interrupt) {
127          // Used for testing only.
128      }
129      ~TorController();
130  
131      /** Get name of file to store private key in */
132      fs::path GetPrivateKeyFile();
133  
134      /** Interrupt the controller thread */
135      void Interrupt();
136  
137      /** Wait for the controller thread to exit */
138      void Join();
139  private:
140      CThreadInterrupt m_interrupt;
141      std::thread m_thread;
142      const std::string m_tor_control_center;
143      TorControlConnection m_conn;
144      std::string m_private_key;
145      std::string m_service_id;
146      std::atomic<bool> m_reconnect;
147      std::chrono::duration<double> m_reconnect_timeout;
148      CService m_service;
149      const CService m_target;
150      /** Cookie for SAFECOOKIE auth */
151      std::vector<uint8_t> m_cookie;
152      /** ClientNonce for SAFECOOKIE auth */
153      std::vector<uint8_t> m_client_nonce;
154      /** Main control thread */
155      void ThreadControl();
156  
157  public:
158      /** Callback for GETINFO net/listeners/socks result */
159      void get_socks_cb(TorControlConnection& conn, const TorControlReply& reply);
160      /** Callback for ADD_ONION result */
161      void add_onion_cb(TorControlConnection& conn, const TorControlReply& reply, bool pow_was_enabled);
162      /** Callback for AUTHENTICATE result */
163      void auth_cb(TorControlConnection& conn, const TorControlReply& reply);
164      /** Callback for AUTHCHALLENGE result */
165      void authchallenge_cb(TorControlConnection& conn, const TorControlReply& reply);
166      /** Callback for PROTOCOLINFO result */
167      void protocolinfo_cb(TorControlConnection& conn, const TorControlReply& reply);
168      /** Callback after successful connection */
169      void connected_cb(TorControlConnection& conn);
170      /** Callback after connection lost or failed connection attempt */
171      void disconnected_cb(TorControlConnection& conn);
172  };
173  
174  #endif // BITCOIN_TORCONTROL_H