i2p.h raw

   1  // Copyright (c) 2020-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  #ifndef BITCOIN_I2P_H
   6  #define BITCOIN_I2P_H
   7  
   8  #include <compat/compat.h>
   9  #include <netaddress.h>
  10  #include <netbase.h>
  11  #include <sync.h>
  12  #include <util/fs.h>
  13  #include <util/sock.h>
  14  #include <util/threadinterrupt.h>
  15  
  16  #include <memory>
  17  #include <optional>
  18  #include <string>
  19  #include <unordered_map>
  20  #include <vector>
  21  
  22  namespace i2p {
  23  
  24  /**
  25   * Binary data.
  26   */
  27  using Binary = std::vector<uint8_t>;
  28  
  29  /**
  30   * An established connection with another peer.
  31   */
  32  struct Connection {
  33      /** Connected socket. */
  34      std::unique_ptr<Sock> sock;
  35  
  36      /** Our I2P address. */
  37      CService me;
  38  
  39      /** The peer's I2P address. */
  40      CService peer;
  41  };
  42  
  43  namespace sam {
  44  
  45  /**
  46   * The maximum size of an incoming message from the I2P SAM proxy (in bytes).
  47   * Used to avoid a runaway proxy from sending us an "unlimited" amount of data without a terminator.
  48   * The longest known message is ~1400 bytes, so this is high enough not to be triggered during
  49   * normal operation, yet low enough to avoid a malicious proxy from filling our memory.
  50   */
  51  static constexpr size_t MAX_MSG_SIZE{65536};
  52  
  53  /**
  54   * I2P SAM session.
  55   */
  56  class Session
  57  {
  58  public:
  59      /**
  60       * Construct a session. This will not initiate any IO, the session will be lazily created
  61       * later when first used.
  62       * @param[in] private_key_file Path to a private key file. If the file does not exist then the
  63       * private key will be generated and saved into the file.
  64       * @param[in] control_host Location of the SAM proxy.
  65       * @param[in,out] interrupt If this is signaled then all operations are canceled as soon as
  66       * possible and executing methods throw an exception.
  67       */
  68      Session(const fs::path& private_key_file,
  69              const Proxy& control_host,
  70              std::shared_ptr<CThreadInterrupt> interrupt);
  71  
  72      /**
  73       * Construct a transient session which will generate its own I2P private key
  74       * rather than read the one from disk (it will not be saved on disk either and
  75       * will be lost once this object is destroyed). This will not initiate any IO,
  76       * the session will be lazily created later when first used.
  77       * @param[in] control_host Location of the SAM proxy.
  78       * @param[in,out] interrupt If this is signaled then all operations are canceled as soon as
  79       * possible and executing methods throw an exception.
  80       */
  81      Session(const Proxy& control_host, std::shared_ptr<CThreadInterrupt> interrupt);
  82  
  83      /**
  84       * Destroy the session, closing the internally used sockets. The sockets that have been
  85       * returned by `Accept()` or `Connect()` will not be closed, but they will be closed by
  86       * the SAM proxy because the session is destroyed. So they will return an error next time
  87       * we try to read or write to them.
  88       */
  89      ~Session();
  90  
  91      /**
  92       * Start listening for an incoming connection.
  93       * @param[out] conn Upon successful completion the `sock` and `me` members will be set
  94       * to the listening socket and address.
  95       * @return true on success
  96       */
  97      bool Listen(Connection& conn) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);
  98  
  99      /**
 100       * Wait for and accept a new incoming connection.
 101       * @param[in,out] conn The `sock` member is used for waiting and accepting. Upon successful
 102       * completion the `peer` member will be set to the address of the incoming peer.
 103       * @return true on success
 104       */
 105      bool Accept(Connection& conn) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);
 106  
 107      /**
 108       * Connect to an I2P peer.
 109       * @param[in] to Peer to connect to.
 110       * @param[out] conn Established connection. Only set if `true` is returned.
 111       * @param[out] proxy_error If an error occurs due to proxy or general network failure, then
 112       * this is set to `true`. If an error occurs due to unreachable peer (likely peer is down), then
 113       * it is set to `false`. Only set if `false` is returned.
 114       * @return true on success
 115       */
 116      bool Connect(const CService& to, Connection& conn, bool& proxy_error) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);
 117  
 118  private:
 119      /**
 120       * A reply from the SAM proxy.
 121       */
 122      struct Reply {
 123          /**
 124           * Full, unparsed reply.
 125           */
 126          std::string full;
 127  
 128          /**
 129           * Request, used for detailed error reporting.
 130           */
 131          std::string request;
 132  
 133          /**
 134           * A map of keywords from the parsed reply.
 135           * For example, if the reply is "A=X B C=YZ", then the map will be
 136           * keys["A"] == "X"
 137           * keys["B"] == (empty std::optional)
 138           * keys["C"] == "YZ"
 139           */
 140          std::unordered_map<std::string, std::optional<std::string>> keys;
 141  
 142          /**
 143           * Get the value of a given key.
 144           * For example if the reply is "A=X B" then:
 145           * Value("A") -> "X"
 146           * Value("B") -> throws
 147           * Value("C") -> throws
 148           * @param[in] key Key whose value to retrieve
 149           * @returns the key's value
 150           * @throws std::runtime_error if the key is not present or if it has no value
 151           */
 152          std::string Get(const std::string& key) const;
 153      };
 154  
 155      /**
 156       * Send request and get a reply from the SAM proxy.
 157       * @param[in] sock A socket that is connected to the SAM proxy.
 158       * @param[in] request Raw request to send, a newline terminator is appended to it.
 159       * @param[in] check_result_ok If true then after receiving the reply a check is made
 160       * whether it contains "RESULT=OK" and an exception is thrown if it does not.
 161       * @throws std::runtime_error if an error occurs
 162       */
 163      Reply SendRequestAndGetReply(const Sock& sock,
 164                                   const std::string& request,
 165                                   bool check_result_ok = true) const;
 166  
 167      /**
 168       * Open a new connection to the SAM proxy.
 169       * @return a connected socket
 170       * @throws std::runtime_error if an error occurs
 171       */
 172      std::unique_ptr<Sock> Hello() const EXCLUSIVE_LOCKS_REQUIRED(m_mutex);
 173  
 174      /**
 175       * Check the control socket for errors and possibly disconnect.
 176       */
 177      void CheckControlSock() EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);
 178  
 179      /**
 180       * Generate a new destination with the SAM proxy and set `m_private_key` to it.
 181       * @param[in] sock Socket to use for talking to the SAM proxy.
 182       * @throws std::runtime_error if an error occurs
 183       */
 184      void DestGenerate(const Sock& sock) EXCLUSIVE_LOCKS_REQUIRED(m_mutex);
 185  
 186      /**
 187       * Generate a new destination with the SAM proxy, set `m_private_key` to it and save
 188       * it on disk to `m_private_key_file`.
 189       * @param[in] sock Socket to use for talking to the SAM proxy.
 190       * @throws std::runtime_error if an error occurs
 191       */
 192      void GenerateAndSavePrivateKey(const Sock& sock) EXCLUSIVE_LOCKS_REQUIRED(m_mutex);
 193  
 194      /**
 195       * Derive own destination from `m_private_key`.
 196       * @see https://geti2p.net/spec/common-structures#destination
 197       * @return an I2P destination
 198       */
 199      Binary MyDestination() const EXCLUSIVE_LOCKS_REQUIRED(m_mutex);
 200  
 201      /**
 202       * Create the session if not already created. Reads the private key file and connects to the
 203       * SAM proxy.
 204       * @throws std::runtime_error if an error occurs
 205       */
 206      void CreateIfNotCreatedAlready() EXCLUSIVE_LOCKS_REQUIRED(m_mutex);
 207  
 208      /**
 209       * Open a new connection to the SAM proxy and issue "STREAM ACCEPT" request using the existing
 210       * session id.
 211       * @return the idle socket that is waiting for a peer to connect to us
 212       * @throws std::runtime_error if an error occurs
 213       */
 214      std::unique_ptr<Sock> StreamAccept() EXCLUSIVE_LOCKS_REQUIRED(m_mutex);
 215  
 216      /**
 217       * Destroy the session, closing the internally used sockets.
 218       */
 219      void Disconnect() EXCLUSIVE_LOCKS_REQUIRED(m_mutex);
 220  
 221      /**
 222       * The name of the file where this peer's private key is stored (in binary).
 223       */
 224      const fs::path m_private_key_file;
 225  
 226      /**
 227       * The SAM control service proxy.
 228       */
 229      const Proxy m_control_host;
 230  
 231      /**
 232       * Cease network activity when this is signaled.
 233       */
 234      const std::shared_ptr<CThreadInterrupt> m_interrupt;
 235  
 236      /**
 237       * Mutex protecting the members that can be concurrently accessed.
 238       */
 239      mutable Mutex m_mutex;
 240  
 241      /**
 242       * The private key of this peer.
 243       * @see The reply to the "DEST GENERATE" command in https://geti2p.net/en/docs/api/samv3
 244       */
 245      Binary m_private_key GUARDED_BY(m_mutex);
 246  
 247      /**
 248       * SAM control socket.
 249       * Used to connect to the I2P SAM service and create a session
 250       * ("SESSION CREATE"). With the established session id we later open
 251       * other connections to the SAM service to accept incoming I2P
 252       * connections and make outgoing ones.
 253       * If not connected then this unique_ptr will be empty.
 254       * See https://geti2p.net/en/docs/api/samv3
 255       */
 256      std::unique_ptr<Sock> m_control_sock GUARDED_BY(m_mutex);
 257  
 258      /**
 259       * Our .b32.i2p address.
 260       * Derived from `m_private_key`.
 261       */
 262      CService m_my_addr GUARDED_BY(m_mutex);
 263  
 264      /**
 265       * SAM session id.
 266       */
 267      std::string m_session_id GUARDED_BY(m_mutex);
 268  
 269      /**
 270       * Whether this is a transient session (the I2P private key will not be
 271       * read or written to disk).
 272       */
 273      const bool m_transient;
 274  };
 275  
 276  } // namespace sam
 277  } // namespace i2p
 278  
 279  #endif // BITCOIN_I2P_H
 280