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