LCOV - code coverage report
Current view: top level - src - i2p.h (source / functions) Hit Total Coverage
Test: fuzz_coverage.info Lines: 1 1 100.0 %
Date: 2023-10-05 15:40:34 Functions: 4 4 100.0 %
Branches: 1 2 50.0 %

           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 <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         [ -  + ]:         92 : 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                 :            :      * If not connected then this unique_ptr will be empty.
     265                 :            :      * See https://geti2p.net/en/docs/api/samv3
     266                 :            :      */
     267                 :            :     std::unique_ptr<Sock> m_control_sock GUARDED_BY(m_mutex);
     268                 :            : 
     269                 :            :     /**
     270                 :            :      * Our .b32.i2p address.
     271                 :            :      * Derived from `m_private_key`.
     272                 :            :      */
     273                 :            :     CService m_my_addr GUARDED_BY(m_mutex);
     274                 :            : 
     275                 :            :     /**
     276                 :            :      * SAM session id.
     277                 :            :      */
     278                 :            :     std::string m_session_id GUARDED_BY(m_mutex);
     279                 :            : 
     280                 :            :     /**
     281                 :            :      * Whether this is a transient session (the I2P private key will not be
     282                 :            :      * read or written to disk).
     283                 :            :      */
     284                 :            :     const bool m_transient;
     285                 :            : };
     286                 :            : 
     287                 :            : } // namespace sam
     288                 :            : } // namespace i2p
     289                 :            : 
     290                 :            : #endif // BITCOIN_I2P_H

Generated by: LCOV version 1.14