40 to.resize(from.size());
41 for (
size_t i = 0; i < from.size(); ++i) {
71 const std::string& std_b64 =
SwapBase64(i2p_b64);
75 throw std::runtime_error(
strprintf(
"Cannot decode Base64: \"%s\"", i2p_b64));
89 hasher.
Write(dest.data(), dest.size());
94 const std::string addr_str =
EncodeBase32(hash,
false) +
".b32.i2p";
96 throw std::runtime_error(
strprintf(
"Cannot parse I2P address: \"%s\"", addr_str));
119 : m_private_key_file(private_key_file), m_control_host(control_host), m_interrupt(interrupt),
138 }
catch (
const std::runtime_error& e) {
139 Log(
"Error listening: %s", e.what());
151 throw std::runtime_error(
"wait on socket failed");
159 const std::string& peer_dest =
166 }
catch (
const std::runtime_error& e) {
167 Log(
"Error accepting: %s", e.what());
184 std::string session_id;
185 std::unique_ptr<Sock> sock;
192 session_id = m_session_id;
197 const Reply& lookup_reply =
200 const std::string& dest = lookup_reply.
Get(
"VALUE");
203 *sock,
strprintf(
"STREAM CONNECT ID=%s DESTINATION=%s SILENT=false", session_id, dest),
206 const std::string& result = connect_reply.
Get(
"RESULT");
208 if (result ==
"OK") {
209 conn.
sock = std::move(sock);
213 if (result ==
"INVALID_ID") {
216 throw std::runtime_error(
"Invalid session id");
219 if (result ==
"CANT_REACH_PEER" || result ==
"TIMEOUT") {
223 throw std::runtime_error(
strprintf(
"\"%s\"", connect_reply.
full));
224 }
catch (
const std::runtime_error& e) {
225 Log(
"Error connecting to %s: %s", to.
ToString(), e.what());
235 const auto& pos =
keys.find(key);
236 if (pos ==
keys.end() || !pos->second.has_value()) {
237 throw std::runtime_error(
240 return pos->second.value();
243 template <
typename... Args>
250 const std::string& request,
251 bool check_result_ok)
const 258 reply.
request = request.substr(0, 14) ==
"SESSION CREATE" ?
"SESSION CREATE ..." : request;
264 static constexpr
auto recv_timeout = 3min;
269 const auto& pos = std::find(kv.begin(), kv.end(),
'=');
270 if (pos != kv.end()) {
271 reply.
keys.emplace(std::string{kv.begin(), pos}, std::string{pos + 1, kv.end()});
273 reply.
keys.emplace(std::string{kv.begin(), kv.end()}, std::nullopt);
277 if (check_result_ok && reply.
Get(
"RESULT") !=
"OK") {
278 throw std::runtime_error(
279 strprintf(
"Unexpected reply to \"%s\": \"%s\"", request, reply.
full));
290 throw std::runtime_error(
"Cannot create socket");
307 if (!m_control_sock->IsConnected(errmsg)) {
308 Log(
"Control socket error: %s", errmsg);
329 std::string(m_private_key.begin(), m_private_key.end()))) {
330 throw std::runtime_error(
340 static constexpr
size_t DEST_LEN_BASE = 387;
341 static constexpr
size_t CERT_LEN_POS = 385;
344 memcpy(&cert_len, &m_private_key.at(CERT_LEN_POS),
sizeof(cert_len));
347 const size_t dest_len = DEST_LEN_BASE + cert_len;
349 return Binary{m_private_key.begin(), m_private_key.begin() + dest_len};
355 if (m_control_sock->IsConnected(errmsg)) {
365 m_private_key.assign(data.begin(), data.end());
374 session_id, private_key_b64));
377 m_session_id = session_id;
378 m_control_sock = std::move(sock);
380 LogPrintf(
"I2P: SAM session created: session id=%s, my address=%s\n", m_session_id,
381 m_my_addr.ToString());
389 *sock,
strprintf(
"STREAM ACCEPT ID=%s SILENT=false", m_session_id),
false);
391 const std::string& result = reply.
Get(
"RESULT");
393 if (result ==
"OK") {
397 if (result ==
"INVALID_ID") {
408 if (m_session_id.empty()) {
409 Log(
"Destroying incomplete session");
411 Log(
"Destroying session %s", m_session_id);
414 m_control_sock->Reset();
415 m_session_id.clear();
CSHA256 & Write(const unsigned char *data, size_t len)
std::string full
Full, unparsed reply.
void DestGenerate(const Sock &sock) EXCLUSIVE_LOCKS_REQUIRED(m_mutex)
Generate a new destination with the SAM proxy and set m_private_key to it.
#define LogPrint(category,...)
Binary MyDestination() const EXCLUSIVE_LOCKS_REQUIRED(m_mutex)
Derive own destination from m_private_key.
void CreateIfNotCreatedAlready() EXCLUSIVE_LOCKS_REQUIRED(m_mutex)
Create the session if not already created.
std::unique_ptr< Sock > StreamAccept() EXCLUSIVE_LOCKS_REQUIRED(m_mutex)
Open a new connection to the SAM proxy and issue "STREAM ACCEPT" request using the existing session i...
uint256 GetRandHash() noexcept
Mutex m_mutex
Mutex protecting the members that can be concurrently accessed.
static Binary DecodeI2PBase64(const std::string &i2p_b64)
Decode an I2P-style Base64 string.
bool Connect(const CService &to, Connection &conn, bool &proxy_error)
Connect to an I2P peer.
virtual void SendComplete(const std::string &data, std::chrono::milliseconds timeout, CThreadInterrupt &interrupt) const
Send the given data, retrying on transient errors.
bool Accept(Connection &conn)
Wait for and accept a new incoming connection.
Reply SendRequestAndGetReply(const Sock &sock, const std::string &request, bool check_result_ok=true) const
Send request and get a reply from the SAM proxy.
CService me
Our I2P address.
std::vector< unsigned char > DecodeBase64(const char *p, bool *pf_invalid)
void Log(const std::string &fmt, const Args &... args) const
Log a message in the BCLog::I2P category.
bool SetSpecial(const std::string &addr)
Parse a Tor or I2P address and set this object to it.
std::vector< Span< const char > > Split(const Span< const char > &sp, char sep)
Split a string on every instance of sep, returning a vector.
An established connection with another peer.
virtual std::string RecvUntilTerminator(uint8_t terminator, std::chrono::milliseconds timeout, CThreadInterrupt &interrupt, size_t max_data) const
Read from socket until a terminator character is encountered.
A reply from the SAM proxy.
const fs::path m_private_key_file
The name of the file where this peer's private key is stored (in binary).
bool WriteBinaryFile(const fs::path &filename, const std::string &data)
Write contents of std::string to a file.
std::string Get(const std::string &key) const
Get the value of a given key.
std::string EncodeBase64(Span< const unsigned char > input)
static CNetAddr DestB64ToAddr(const std::string &dest)
Derive the .b32.i2p address of an I2P destination (I2P-style Base64).
static constexpr uint16_t I2P_SAM31_PORT
SAM 3.1 and earlier do not support specifying ports and force the port to 0.
bool ConnectSocketDirectly(const CService &addrConnect, const Sock &sock, int nTimeout, bool manual_connection)
Try to connect to the specified service on the specified socket.
std::string ToStringIP() const
static CNetAddr DestBinToAddr(const Binary &dest)
Derive the .b32.i2p address of an I2P destination (binary).
A combination of a network address (CNetAddr) and a (TCP) port.
void Finalize(unsigned char hash[OUTPUT_SIZE])
Session(const fs::path &private_key_file, const CService &control_host, CThreadInterrupt *interrupt)
Construct a session.
std::pair< bool, std::string > ReadBinaryFile(const fs::path &filename, size_t maxsize=std::numeric_limits< size_t >::max())
Read full contents of a file and return them in a std::string.
std::unique_ptr< Sock > Hello() const EXCLUSIVE_LOCKS_REQUIRED(m_mutex)
Open a new connection to the SAM proxy.
std::string request
Request, used for detailed error reporting.
static constexpr size_t MAX_MSG_SIZE
The maximum size of an incoming message from the I2P SAM proxy (in bytes).
void GenerateAndSavePrivateKey(const Sock &sock) EXCLUSIVE_LOCKS_REQUIRED(m_mutex)
Generate a new destination with the SAM proxy, set m_private_key to it and save it on disk to m_priva...
std::string EncodeBase32(Span< const unsigned char > input, bool pad)
Base32 encode.
void CheckControlSock()
Check the control socket for errors and possibly disconnect.
static constexpr Event RECV
If passed to Wait(), then it will wait for readiness to read from the socket.
const CService m_control_host
The host and port of the SAM control service.
uint16_t be16toh(uint16_t big_endian_16bits)
void Disconnect() EXCLUSIVE_LOCKS_REQUIRED(m_mutex)
Destroy the session, closing the internally used sockets.
std::string GetHex() const
static std::string SwapBase64(const std::string &from)
Swap Standard Base64 <-> I2P Base64.
std::unordered_map< std::string, std::optional< std::string > > keys
A map of keywords from the parsed reply.
static const size_t OUTPUT_SIZE
CThreadInterrupt *const m_interrupt
Cease network activity when this is signaled.
~Session()
Destroy the session, closing the internally used sockets.
std::vector< uint8_t > Binary
Binary data.
std::string ToString() const
RAII helper class that manages a socket.
std::function< std::unique_ptr< Sock >const CService &)> CreateSock
Socket factory.
A hasher class for SHA-256.
std::unique_ptr< Sock > sock
Connected socket.
CService peer
The peer's I2P address.
bool Listen(Connection &conn)
Start listening for an incoming connection.
static constexpr auto MAX_WAIT_FOR_IO
Maximum time to wait for I/O readiness.