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 [ # # ]: 0 : 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 : : 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 : : * Log a message in the `BCLog::I2P` category. 161 : : * @param[in] fmt printf(3)-like format string. 162 : : * @param[in] args printf(3)-like arguments that correspond to `fmt`. 163 : : */ 164 : : template <typename... Args> 165 : : void Log(const std::string& fmt, const Args&... args) const; 166 : : 167 : : /** 168 : : * Send request and get a reply from the SAM proxy. 169 : : * @param[in] sock A socket that is connected to the SAM proxy. 170 : : * @param[in] request Raw request to send, a newline terminator is appended to it. 171 : : * @param[in] check_result_ok If true then after receiving the reply a check is made 172 : : * whether it contains "RESULT=OK" and an exception is thrown if it does not. 173 : : * @throws std::runtime_error if an error occurs 174 : : */ 175 : : Reply SendRequestAndGetReply(const Sock& sock, 176 : : const std::string& request, 177 : : bool check_result_ok = true) const; 178 : : 179 : : /** 180 : : * Open a new connection to the SAM proxy. 181 : : * @return a connected socket 182 : : * @throws std::runtime_error if an error occurs 183 : : */ 184 : : std::unique_ptr<Sock> Hello() const EXCLUSIVE_LOCKS_REQUIRED(m_mutex); 185 : : 186 : : /** 187 : : * Check the control socket for errors and possibly disconnect. 188 : : */ 189 : : void CheckControlSock() EXCLUSIVE_LOCKS_REQUIRED(!m_mutex); 190 : : 191 : : /** 192 : : * Generate a new destination with the SAM proxy and set `m_private_key` to it. 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 DestGenerate(const Sock& sock) EXCLUSIVE_LOCKS_REQUIRED(m_mutex); 197 : : 198 : : /** 199 : : * Generate a new destination with the SAM proxy, set `m_private_key` to it and save 200 : : * it on disk to `m_private_key_file`. 201 : : * @param[in] sock Socket to use for talking to the SAM proxy. 202 : : * @throws std::runtime_error if an error occurs 203 : : */ 204 : : void GenerateAndSavePrivateKey(const Sock& sock) EXCLUSIVE_LOCKS_REQUIRED(m_mutex); 205 : : 206 : : /** 207 : : * Derive own destination from `m_private_key`. 208 : : * @see https://geti2p.net/spec/common-structures#destination 209 : : * @return an I2P destination 210 : : */ 211 : : Binary MyDestination() const EXCLUSIVE_LOCKS_REQUIRED(m_mutex); 212 : : 213 : : /** 214 : : * Create the session if not already created. Reads the private key file and connects to the 215 : : * SAM proxy. 216 : : * @throws std::runtime_error if an error occurs 217 : : */ 218 : : void CreateIfNotCreatedAlready() EXCLUSIVE_LOCKS_REQUIRED(m_mutex); 219 : : 220 : : /** 221 : : * Open a new connection to the SAM proxy and issue "STREAM ACCEPT" request using the existing 222 : : * session id. 223 : : * @return the idle socket that is waiting for a peer to connect to us 224 : : * @throws std::runtime_error if an error occurs 225 : : */ 226 : : std::unique_ptr<Sock> StreamAccept() EXCLUSIVE_LOCKS_REQUIRED(m_mutex); 227 : : 228 : : /** 229 : : * Destroy the session, closing the internally used sockets. 230 : : */ 231 : : void Disconnect() EXCLUSIVE_LOCKS_REQUIRED(m_mutex); 232 : : 233 : : /** 234 : : * The name of the file where this peer's private key is stored (in binary). 235 : : */ 236 : : const fs::path m_private_key_file; 237 : : 238 : : /** 239 : : * The SAM control service proxy. 240 : : */ 241 : : const Proxy m_control_host; 242 : : 243 : : /** 244 : : * Cease network activity when this is signaled. 245 : : */ 246 : : CThreadInterrupt* const m_interrupt; 247 : : 248 : : /** 249 : : * Mutex protecting the members that can be concurrently accessed. 250 : : */ 251 : : mutable Mutex m_mutex; 252 : : 253 : : /** 254 : : * The private key of this peer. 255 : : * @see The reply to the "DEST GENERATE" command in https://geti2p.net/en/docs/api/samv3 256 : : */ 257 : : Binary m_private_key GUARDED_BY(m_mutex); 258 : : 259 : : /** 260 : : * SAM control socket. 261 : : * Used to connect to the I2P SAM service and create a session 262 : : * ("SESSION CREATE"). With the established session id we later open 263 : : * other connections to the SAM service to accept incoming I2P 264 : : * connections and make outgoing ones. 265 : : * If not connected then this unique_ptr will be empty. 266 : : * See https://geti2p.net/en/docs/api/samv3 267 : : */ 268 : : std::unique_ptr<Sock> m_control_sock GUARDED_BY(m_mutex); 269 : : 270 : : /** 271 : : * Our .b32.i2p address. 272 : : * Derived from `m_private_key`. 273 : : */ 274 : : CService m_my_addr GUARDED_BY(m_mutex); 275 : : 276 : : /** 277 : : * SAM session id. 278 : : */ 279 : : std::string m_session_id GUARDED_BY(m_mutex); 280 : : 281 : : /** 282 : : * Whether this is a transient session (the I2P private key will not be 283 : : * read or written to disk). 284 : : */ 285 : : const bool m_transient; 286 : : }; 287 : : 288 : : } // namespace sam 289 : : } // namespace i2p 290 : : 291 : : #endif // BITCOIN_I2P_H