LCOV - code coverage report
Current view: top level - src - i2p.h (source / functions) Coverage Total Hit
Test: fuzz_coverage.info Lines: 100.0 % 1 1
Test Date: 2024-11-04 04:15:01 Functions: - 0 0
Branches: 50.0 % 2 1

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

Generated by: LCOV version 2.0-1