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