LCOV - code coverage report
Current view: top level - src - i2p.h (source / functions) Hit Total Coverage
Test: fuzz_coverage.info Lines: 0 1 0.0 %
Date: 2023-09-26 12:08:55 Functions: 0 4 0.0 %

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

Generated by: LCOV version 1.14