i2p: destroy the session if we get an unexpected error from the I2P router

From https://geti2p.net/en/docs/api/samv3:

  If SILENT=false was passed, which is the default value, the SAM bridge
  sends the client a ASCII line containing the base64 public destination
  key of the requesting peer

So, `Accept()` is supposed to receive a Base64 encoded destination of
the connecting peer, but if it receives something like this instead:

  STREAM STATUS RESULT=I2P_ERROR MESSAGE="Session was closed"

then destroy the session.
This commit is contained in:
Vasil Dimov
2023-07-14 15:17:43 +02:00
parent 762404a68c
commit 5c8e15c451
3 changed files with 139 additions and 26 deletions

View File

@@ -12,6 +12,7 @@
#include <netaddress.h>
#include <netbase.h>
#include <random.h>
#include <sync.h>
#include <tinyformat.h>
#include <util/fs.h>
#include <util/readwritefile.h>
@@ -153,27 +154,59 @@ bool Session::Listen(Connection& conn)
bool Session::Accept(Connection& conn)
{
try {
while (!*m_interrupt) {
Sock::Event occurred;
if (!conn.sock->Wait(MAX_WAIT_FOR_IO, Sock::RECV, &occurred)) {
throw std::runtime_error("wait on socket failed");
}
AssertLockNotHeld(m_mutex);
if (occurred == 0) {
// Timeout, no incoming connections or errors within MAX_WAIT_FOR_IO.
continue;
}
std::string errmsg;
bool disconnect{false};
const std::string& peer_dest =
conn.sock->RecvUntilTerminator('\n', MAX_WAIT_FOR_IO, *m_interrupt, MAX_MSG_SIZE);
conn.peer = CService(DestB64ToAddr(peer_dest), I2P_SAM31_PORT);
return true;
while (!*m_interrupt) {
Sock::Event occurred;
if (!conn.sock->Wait(MAX_WAIT_FOR_IO, Sock::RECV, &occurred)) {
errmsg = "wait on socket failed";
break;
}
} catch (const std::runtime_error& e) {
Log("Error accepting: %s", e.what());
if (occurred == 0) {
// Timeout, no incoming connections or errors within MAX_WAIT_FOR_IO.
continue;
}
std::string peer_dest;
try {
peer_dest = conn.sock->RecvUntilTerminator('\n', MAX_WAIT_FOR_IO, *m_interrupt, MAX_MSG_SIZE);
} catch (const std::runtime_error& e) {
errmsg = e.what();
break;
}
CNetAddr peer_addr;
try {
peer_addr = DestB64ToAddr(peer_dest);
} catch (const std::runtime_error& e) {
// The I2P router is expected to send the Base64 of the connecting peer,
// but it may happen that something like this is sent instead:
// STREAM STATUS RESULT=I2P_ERROR MESSAGE="Session was closed"
// In that case consider the session damaged and close it right away,
// even if the control socket is alive.
if (peer_dest.find("RESULT=I2P_ERROR") != std::string::npos) {
errmsg = strprintf("unexpected reply that hints the session is unusable: %s", peer_dest);
disconnect = true;
} else {
errmsg = e.what();
}
break;
}
conn.peer = CService(peer_addr, I2P_SAM31_PORT);
return true;
}
Log("Error accepting%s: %s", disconnect ? " (will close the session)" : "", errmsg);
if (disconnect) {
LOCK(m_mutex);
Disconnect();
} else {
CheckControlSock();
}
return false;