torcontrol.h raw

   1  // Copyright (c) 2015-2021 The Limenka 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 LIMENKA_TORCONTROL_H
   9  #define LIMENKA_TORCONTROL_H
  10  
  11  #include <limenka-build-config.h> // IWYU pragma: keep
  12  
  13  #include <netaddress.h>
  14  #include <util/fs.h>
  15  
  16  #include <event2/util.h>
  17  
  18  #include <cstdint>
  19  #include <deque>
  20  #include <functional>
  21  #include <string>
  22  #include <vector>
  23  
  24  namespace subprocess {
  25  class Popen;
  26  }
  27  
  28  constexpr int DEFAULT_TOR_CONTROL_PORT = 9051;
  29  extern const std::string DEFAULT_TOR_CONTROL;
  30  extern const std::string DEFAULT_TOR_EXECUTE;
  31  static const bool DEFAULT_LISTEN_ONION = true;
  32  
  33  /** Tor control reply code. Ref: https://spec.torproject.org/control-spec/replies.html */
  34  constexpr int TOR_REPLY_SYNTAX_ERROR{512}; //!< Syntax error in command argument
  35  
  36  void StartTorControl(CService onion_service_target);
  37  void InterruptTorControl();
  38  void StopTorControl();
  39  
  40  CService DefaultOnionServiceTarget(uint16_t port);
  41  
  42  /** Reply from Tor, can be single or multi-line */
  43  class TorControlReply
  44  {
  45  public:
  46      TorControlReply() { Clear(); }
  47  
  48      int code;
  49      std::vector<std::string> lines;
  50  
  51      void Clear()
  52      {
  53          code = 0;
  54          lines.clear();
  55      }
  56  };
  57  
  58  /** Low-level handling for Tor control connection.
  59   * Speaks the SMTP-like protocol as defined in torspec/control-spec.txt
  60   */
  61  class TorControlConnection
  62  {
  63  public:
  64      typedef std::function<void(TorControlConnection&)> ConnectionCB;
  65      typedef std::function<void(TorControlConnection &,const TorControlReply &)> ReplyHandlerCB;
  66      static void IgnoreReplyHandler(TorControlConnection &, const TorControlReply &);
  67  
  68      /** Create a new TorControlConnection.
  69       */
  70      explicit TorControlConnection(struct event_base *base);
  71      ~TorControlConnection();
  72  
  73      /**
  74       * Connect to a Tor control port.
  75       * tor_control_center is address of the form host:port.
  76       * connected is the handler that is called when connection is successfully established.
  77       * disconnected is a handler that is called when the connection is broken.
  78       * Return true on success.
  79       */
  80      bool Connect(const std::string& tor_control_center, const ConnectionCB& connected, const ConnectionCB& disconnected);
  81  
  82      /**
  83       * Disconnect from Tor control port.
  84       */
  85      void Disconnect();
  86  
  87      /** Send a command, register a handler for the reply.
  88       * A trailing CRLF is automatically added.
  89       * Return true on success.
  90       */
  91      bool Command(const std::string &cmd, const ReplyHandlerCB& reply_handler = IgnoreReplyHandler);
  92  
  93  private:
  94      /** Callback when ready for use */
  95      std::function<void(TorControlConnection&)> connected;
  96      /** Callback when connection lost */
  97      std::function<void(TorControlConnection&)> disconnected;
  98      /** Libevent event base */
  99      struct event_base *base;
 100      /** Connection to control socket */
 101      struct bufferevent* b_conn{nullptr};
 102      /** Message being received */
 103      TorControlReply message;
 104      /** Response handlers */
 105      std::deque<ReplyHandlerCB> reply_handlers;
 106  
 107      /** Libevent handlers: internal */
 108      static void readcb(struct bufferevent *bev, void *ctx);
 109      static void eventcb(struct bufferevent *bev, short what, void *ctx);
 110  };
 111  
 112  /****** Limenka specific TorController implementation ********/
 113  
 114  /** Controller that connects to Tor control socket, authenticate, then create
 115   * and maintain an ephemeral onion service.
 116   */
 117  class TorController
 118  {
 119  public:
 120      TorController(struct event_base* base, const std::string& tor_control_center, const CService& target, const std::string& execute);
 121      TorController() : conn{nullptr} {
 122          // Used for testing only.
 123      }
 124      ~TorController();
 125  
 126      /** Get name of file to store private key in */
 127      fs::path GetPrivateKeyFile();
 128  
 129      /** Reconnect, after getting disconnected */
 130      void Reconnect();
 131  private:
 132      struct event_base* base;
 133      const std::string m_connect_tor_control_center;
 134      std::string m_current_tor_control_center;
 135      TorControlConnection conn;
 136      std::string private_key;
 137      std::string service_id;
 138      bool m_try_exec{true};
 139      bool reconnect;
 140      struct event *reconnect_ev = nullptr;
 141      float reconnect_timeout;
 142      std::string m_execute{DEFAULT_TOR_EXECUTE};
 143  #ifdef ENABLE_TOR_SUBPROCESS
 144      subprocess::Popen *m_process{nullptr};
 145  #endif
 146      CService service;
 147      const CService m_target;
 148      /** Cookie for SAFECOOKIE auth */
 149      std::vector<uint8_t> cookie;
 150      /** ClientNonce for SAFECOOKIE auth */
 151      std::vector<uint8_t> clientNonce;
 152  
 153  public:
 154      /** Callback for GETINFO net/listeners/socks result */
 155      void get_socks_cb(TorControlConnection& conn, const TorControlReply& reply);
 156      /** Callback for ADD_ONION result */
 157      void add_onion_cb(TorControlConnection& conn, const TorControlReply& reply, bool pow_was_enabled);
 158      /** Callback for AUTHENTICATE result */
 159      void auth_cb(TorControlConnection& conn, const TorControlReply& reply);
 160      /** Callback for AUTHCHALLENGE result */
 161      void authchallenge_cb(TorControlConnection& conn, const TorControlReply& reply);
 162      /** Callback for PROTOCOLINFO result */
 163      void protocolinfo_cb(TorControlConnection& conn, const TorControlReply& reply);
 164      /** Callback after successful connection */
 165      void connected_cb(TorControlConnection& conn);
 166      /** Callback after connection lost or failed connection attempt */
 167      void disconnected_cb(TorControlConnection& conn);
 168  
 169      /** Callback for reconnect timer */
 170      static void reconnect_cb(evutil_socket_t fd, short what, void *arg);
 171  
 172      std::string LaunchTor();
 173  };
 174  
 175  #endif // LIMENKA_TORCONTROL_H
 176