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 [ # # ]: 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) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex); 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 : 0 : 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