Browse Source

SSU2: handle standard network errors more correctly

Signed-off-by: R4SAS <r4sas@i2pmail.org>
pull/1827/head
R4SAS 2 years ago
parent
commit
f1437feede
Signed by: r4sas
GPG Key ID: 66F6C87B98EBCFE2
  1. 152
      libi2pd/SSU2.cpp

152
libi2pd/SSU2.cpp

@ -47,21 +47,21 @@ namespace transport
{ {
found = true; found = true;
if (address->IsV6 ()) if (address->IsV6 ())
{ {
uint16_t mtu; i2p::config::GetOption ("ssu2.mtu6", mtu); uint16_t mtu; i2p::config::GetOption ("ssu2.mtu6", mtu);
if (!mtu || mtu > SSU2_MAX_PACKET_SIZE - SOCKS5_UDP_IPV6_REQUEST_HEADER_SIZE) if (!mtu || mtu > SSU2_MAX_PACKET_SIZE - SOCKS5_UDP_IPV6_REQUEST_HEADER_SIZE)
mtu = SSU2_MAX_PACKET_SIZE - SOCKS5_UDP_IPV6_REQUEST_HEADER_SIZE; mtu = SSU2_MAX_PACKET_SIZE - SOCKS5_UDP_IPV6_REQUEST_HEADER_SIZE;
i2p::context.SetMTU (mtu, false); i2p::context.SetMTU (mtu, false);
} }
else else
{ {
uint16_t mtu; i2p::config::GetOption ("ssu2.mtu4", mtu); uint16_t mtu; i2p::config::GetOption ("ssu2.mtu4", mtu);
if (!mtu || mtu > SSU2_MAX_PACKET_SIZE - SOCKS5_UDP_IPV4_REQUEST_HEADER_SIZE) if (!mtu || mtu > SSU2_MAX_PACKET_SIZE - SOCKS5_UDP_IPV4_REQUEST_HEADER_SIZE)
mtu = SSU2_MAX_PACKET_SIZE - SOCKS5_UDP_IPV4_REQUEST_HEADER_SIZE; mtu = SSU2_MAX_PACKET_SIZE - SOCKS5_UDP_IPV4_REQUEST_HEADER_SIZE;
i2p::context.SetMTU (mtu, true); i2p::context.SetMTU (mtu, true);
} }
continue; // we don't need port for proxy continue; // we don't need port for proxy
} }
auto port = address->port; auto port = address->port;
if (!port) if (!port)
{ {
@ -103,11 +103,11 @@ namespace transport
} }
} }
if (found) if (found)
{ {
if (m_IsThroughProxy) if (m_IsThroughProxy)
ConnectToProxy (); ConnectToProxy ();
m_ReceiveService.Start (); m_ReceiveService.Start ();
} }
ScheduleTermination (); ScheduleTermination ();
ScheduleResend (false); ScheduleResend (false);
} }
@ -136,11 +136,11 @@ namespace transport
m_SocketV6.close (); m_SocketV6.close ();
if (m_UDPAssociateSocket) if (m_UDPAssociateSocket)
{ {
m_UDPAssociateSocket->close (); m_UDPAssociateSocket->close ();
m_UDPAssociateSocket.reset (nullptr); m_UDPAssociateSocket.reset (nullptr);
} }
StopIOService (); StopIOService ();
m_Sessions.clear (); m_Sessions.clear ();
@ -168,11 +168,11 @@ namespace transport
m_AddressV6 = localAddress; m_AddressV6 = localAddress;
uint16_t mtu; i2p::config::GetOption ("ssu2.mtu6", mtu); uint16_t mtu; i2p::config::GetOption ("ssu2.mtu6", mtu);
if (!mtu) if (!mtu)
{ {
int maxMTU = i2p::util::net::GetMaxMTU (localAddress.to_v6 ()); int maxMTU = i2p::util::net::GetMaxMTU (localAddress.to_v6 ());
mtu = i2p::util::net::GetMTU (localAddress); mtu = i2p::util::net::GetMTU (localAddress);
if (mtu > maxMTU) mtu = maxMTU; if (mtu > maxMTU) mtu = maxMTU;
} }
else else
if (mtu > (int)SSU2_MAX_PACKET_SIZE) mtu = SSU2_MAX_PACKET_SIZE; if (mtu > (int)SSU2_MAX_PACKET_SIZE) mtu = SSU2_MAX_PACKET_SIZE;
if (mtu < (int)SSU2_MIN_PACKET_SIZE) mtu = SSU2_MIN_PACKET_SIZE; if (mtu < (int)SSU2_MIN_PACKET_SIZE) mtu = SSU2_MIN_PACKET_SIZE;
@ -236,7 +236,19 @@ namespace transport
void SSU2Server::HandleReceivedFrom (const boost::system::error_code& ecode, size_t bytes_transferred, void SSU2Server::HandleReceivedFrom (const boost::system::error_code& ecode, size_t bytes_transferred,
Packet * packet, boost::asio::ip::udp::socket& socket) Packet * packet, boost::asio::ip::udp::socket& socket)
{ {
if (!ecode) if (!ecode
|| ecode == boost::asio::error::connection_refused
|| ecode == boost::asio::error::connection_reset
|| ecode == boost::asio::error::network_unreachable
|| ecode == boost::asio::error::host_unreachable
#ifdef _WIN32 // windows can throw WinAPI error, which is not handled by ASIO
|| ecode.value() == boost::winapi::ERROR_CONNECTION_REFUSED_
|| ecode.value() == boost::winapi::ERROR_NETWORK_UNREACHABLE_
|| ecode.value() == boost::winapi::ERROR_HOST_UNREACHABLE_
#endif
)
// just try continue reading when received ICMP response otherwise socket can crash,
// but better to find out which host were sent it and mark that router as unreachable
{ {
i2p::transport::transports.UpdateReceivedBytes (bytes_transferred); i2p::transport::transports.UpdateReceivedBytes (bytes_transferred);
packet->len = bytes_transferred; packet->len = bytes_transferred;
@ -285,12 +297,12 @@ namespace transport
ConnectToProxy (); ConnectToProxy ();
} }
else else
{ {
auto ep = socket.local_endpoint (); auto ep = socket.local_endpoint ();
socket.close (); socket.close ();
OpenSocket (ep); OpenSocket (ep);
Receive (socket); Receive (socket);
} }
} }
} }
} }
@ -301,7 +313,7 @@ namespace transport
{ {
if (m_IsThroughProxy) if (m_IsThroughProxy)
ProcessNextPacketFromProxy (packet->buf, packet->len); ProcessNextPacketFromProxy (packet->buf, packet->len);
else else
ProcessNextPacket (packet->buf, packet->len, packet->from); ProcessNextPacket (packet->buf, packet->len, packet->from);
m_PacketsPool.ReleaseMt (packet); m_PacketsPool.ReleaseMt (packet);
if (m_LastSession && m_LastSession->GetState () != eSSU2SessionStateTerminated) if (m_LastSession && m_LastSession->GetState () != eSSU2SessionStateTerminated)
@ -314,7 +326,7 @@ namespace transport
if (m_IsThroughProxy) if (m_IsThroughProxy)
for (auto& packet: packets) for (auto& packet: packets)
ProcessNextPacketFromProxy (packet->buf, packet->len); ProcessNextPacketFromProxy (packet->buf, packet->len);
else else
for (auto& packet: packets) for (auto& packet: packets)
ProcessNextPacket (packet->buf, packet->len, packet->from); ProcessNextPacket (packet->buf, packet->len, packet->from);
m_PacketsPool.ReleaseMt (packets); m_PacketsPool.ReleaseMt (packets);
@ -446,7 +458,7 @@ namespace transport
} }
return nullptr; return nullptr;
} }
void SSU2Server::ProcessNextPacket (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint) void SSU2Server::ProcessNextPacket (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint)
{ {
if (len < 24) return; if (len < 24) return;
@ -499,7 +511,7 @@ namespace transport
if (m_LastSession && m_LastSession->GetState () == eSSU2SessionStateClosing) if (m_LastSession && m_LastSession->GetState () == eSSU2SessionStateClosing)
m_LastSession->RequestTermination (eSSU2TerminationReasonIdleTimeout); // send termination again m_LastSession->RequestTermination (eSSU2TerminationReasonIdleTimeout); // send termination again
break; break;
case eSSU2SessionStateClosingConfirmed: case eSSU2SessionStateClosingConfirmed:
case eSSU2SessionStateTerminated: case eSSU2SessionStateTerminated:
m_LastSession = nullptr; m_LastSession = nullptr;
break; break;
@ -518,7 +530,7 @@ namespace transport
{ {
std::unique_lock<std::mutex> l(m_PendingOutgoingSessionsMutex); std::unique_lock<std::mutex> l(m_PendingOutgoingSessionsMutex);
m_PendingOutgoingSessions.erase (it1); // we are done with that endpoint m_PendingOutgoingSessions.erase (it1); // we are done with that endpoint
} }
else else
it1->second->ProcessRetry (buf, len); it1->second->ProcessRetry (buf, len);
} }
@ -531,7 +543,7 @@ namespace transport
} }
} }
} }
void SSU2Server::Send (const uint8_t * header, size_t headerLen, const uint8_t * payload, size_t payloadLen, void SSU2Server::Send (const uint8_t * header, size_t headerLen, const uint8_t * payload, size_t payloadLen,
const boost::asio::ip::udp::endpoint& to) const boost::asio::ip::udp::endpoint& to)
{ {
@ -539,7 +551,7 @@ namespace transport
{ {
SendThroughProxy (header, headerLen, nullptr, 0, payload, payloadLen, to); SendThroughProxy (header, headerLen, nullptr, 0, payload, payloadLen, to);
return; return;
} }
std::vector<boost::asio::const_buffer> bufs std::vector<boost::asio::const_buffer> bufs
{ {
boost::asio::buffer (header, headerLen), boost::asio::buffer (header, headerLen),
@ -859,11 +871,11 @@ namespace transport
if (it != m_OutgoingTokens.end ()) if (it != m_OutgoingTokens.end ())
{ {
if (i2p::util::GetSecondsSinceEpoch () + SSU2_TOKEN_EXPIRATION_THRESHOLD > it->second.second) if (i2p::util::GetSecondsSinceEpoch () + SSU2_TOKEN_EXPIRATION_THRESHOLD > it->second.second)
{ {
// token expired // token expired
m_OutgoingTokens.erase (it); m_OutgoingTokens.erase (it);
return 0; return 0;
} }
return it->second.first; return it->second.first;
} }
return 0; return 0;
@ -879,7 +891,7 @@ namespace transport
return it->second.first; return it->second.first;
else // token expired else // token expired
m_IncomingTokens.erase (it); m_IncomingTokens.erase (it);
} }
uint64_t token; uint64_t token;
RAND_bytes ((uint8_t *)&token, 8); RAND_bytes ((uint8_t *)&token, 8);
m_IncomingTokens.emplace (ep, std::make_pair (token, ts + SSU2_TOKEN_EXPIRATION_TIMEOUT)); m_IncomingTokens.emplace (ep, std::make_pair (token, ts + SSU2_TOKEN_EXPIRATION_TIMEOUT));
@ -1125,23 +1137,23 @@ namespace transport
size_t requestHeaderSize = 0; size_t requestHeaderSize = 0;
memset (m_UDPRequestHeader, 0, 3); memset (m_UDPRequestHeader, 0, 3);
if (to.address ().is_v6 ()) if (to.address ().is_v6 ())
{ {
m_UDPRequestHeader[3] = SOCKS5_ATYP_IPV6; m_UDPRequestHeader[3] = SOCKS5_ATYP_IPV6;
memcpy (m_UDPRequestHeader + 4, to.address ().to_v6().to_bytes().data(), 16); memcpy (m_UDPRequestHeader + 4, to.address ().to_v6().to_bytes().data(), 16);
requestHeaderSize = SOCKS5_UDP_IPV6_REQUEST_HEADER_SIZE; requestHeaderSize = SOCKS5_UDP_IPV6_REQUEST_HEADER_SIZE;
} }
else else
{ {
m_UDPRequestHeader[3] = SOCKS5_ATYP_IPV4; m_UDPRequestHeader[3] = SOCKS5_ATYP_IPV4;
memcpy (m_UDPRequestHeader + 4, to.address ().to_v4().to_bytes().data(), 4); memcpy (m_UDPRequestHeader + 4, to.address ().to_v4().to_bytes().data(), 4);
requestHeaderSize = SOCKS5_UDP_IPV4_REQUEST_HEADER_SIZE; requestHeaderSize = SOCKS5_UDP_IPV4_REQUEST_HEADER_SIZE;
} }
htobe16buf (m_UDPRequestHeader + requestHeaderSize - 2, to.port ()); htobe16buf (m_UDPRequestHeader + requestHeaderSize - 2, to.port ());
std::vector<boost::asio::const_buffer> bufs; std::vector<boost::asio::const_buffer> bufs;
bufs.push_back (boost::asio::buffer (m_UDPRequestHeader, requestHeaderSize)); bufs.push_back (boost::asio::buffer (m_UDPRequestHeader, requestHeaderSize));
bufs.push_back (boost::asio::buffer (header, headerLen)); bufs.push_back (boost::asio::buffer (header, headerLen));
if (headerX) bufs.push_back (boost::asio::buffer (headerX, headerXLen)); if (headerX) bufs.push_back (boost::asio::buffer (headerX, headerXLen));
bufs.push_back (boost::asio::buffer (payload, payloadLen)); bufs.push_back (boost::asio::buffer (payload, payloadLen));
boost::system::error_code ec; boost::system::error_code ec;
@ -1150,7 +1162,7 @@ namespace transport
i2p::transport::transports.UpdateSentBytes (headerLen + payloadLen); i2p::transport::transports.UpdateSentBytes (headerLen + payloadLen);
else else
LogPrint (eLogError, "SSU2: Send exception: ", ec.message (), " to ", to); LogPrint (eLogError, "SSU2: Send exception: ", ec.message (), " to ", to);
} }
void SSU2Server::ProcessNextPacketFromProxy (uint8_t * buf, size_t len) void SSU2Server::ProcessNextPacketFromProxy (uint8_t * buf, size_t len)
{ {
@ -1158,11 +1170,11 @@ namespace transport
{ {
LogPrint (eLogWarning, "SSU2: Proxy packet fragmentation is not supported"); LogPrint (eLogWarning, "SSU2: Proxy packet fragmentation is not supported");
return; return;
} }
size_t offset = 0; size_t offset = 0;
boost::asio::ip::udp::endpoint ep; boost::asio::ip::udp::endpoint ep;
switch (buf[3]) // ATYP switch (buf[3]) // ATYP
{ {
case SOCKS5_ATYP_IPV4: case SOCKS5_ATYP_IPV4:
{ {
offset = SOCKS5_UDP_IPV4_REQUEST_HEADER_SIZE; offset = SOCKS5_UDP_IPV4_REQUEST_HEADER_SIZE;
@ -1172,7 +1184,7 @@ namespace transport
uint16_t port = bufbe16toh (buf + 8); uint16_t port = bufbe16toh (buf + 8);
ep = boost::asio::ip::udp::endpoint (boost::asio::ip::address_v4 (bytes), port); ep = boost::asio::ip::udp::endpoint (boost::asio::ip::address_v4 (bytes), port);
break; break;
} }
case SOCKS5_ATYP_IPV6: case SOCKS5_ATYP_IPV6:
{ {
offset = SOCKS5_UDP_IPV6_REQUEST_HEADER_SIZE; offset = SOCKS5_UDP_IPV6_REQUEST_HEADER_SIZE;
@ -1182,15 +1194,15 @@ namespace transport
uint16_t port = bufbe16toh (buf + 20); uint16_t port = bufbe16toh (buf + 20);
ep = boost::asio::ip::udp::endpoint (boost::asio::ip::address_v6 (bytes), port); ep = boost::asio::ip::udp::endpoint (boost::asio::ip::address_v6 (bytes), port);
break; break;
} }
default: default:
{ {
LogPrint (eLogWarning, "SSU2: Unknown ATYP ", (int)buf[3], " from proxy relay"); LogPrint (eLogWarning, "SSU2: Unknown ATYP ", (int)buf[3], " from proxy relay");
return; return;
} }
} }
ProcessNextPacket (buf + offset, len - offset, ep); ProcessNextPacket (buf + offset, len - offset, ep);
} }
void SSU2Server::ConnectToProxy () void SSU2Server::ConnectToProxy ()
{ {
@ -1208,7 +1220,7 @@ namespace transport
else else
HandshakeWithProxy (); HandshakeWithProxy ();
}); });
} }
void SSU2Server::HandshakeWithProxy () void SSU2Server::HandshakeWithProxy ()
{ {
@ -1223,13 +1235,13 @@ namespace transport
if (ecode) if (ecode)
{ {
LogPrint(eLogError, "SSU2: Proxy write error ", ecode.message()); LogPrint(eLogError, "SSU2: Proxy write error ", ecode.message());
m_UDPAssociateSocket.reset (nullptr); m_UDPAssociateSocket.reset (nullptr);
ReconnectToProxy (); ReconnectToProxy ();
} }
else else
ReadHandshakeWithProxyReply (); ReadHandshakeWithProxyReply ();
}); });
} }
void SSU2Server::ReadHandshakeWithProxyReply () void SSU2Server::ReadHandshakeWithProxyReply ()
{ {
@ -1243,7 +1255,7 @@ namespace transport
LogPrint(eLogError, "SSU2: Proxy read error ", ecode.message()); LogPrint(eLogError, "SSU2: Proxy read error ", ecode.message());
m_UDPAssociateSocket.reset (nullptr); m_UDPAssociateSocket.reset (nullptr);
ReconnectToProxy (); ReconnectToProxy ();
} }
else else
{ {
if (m_UDPRequestHeader[0] == SOCKS5_VER && !m_UDPRequestHeader[1]) if (m_UDPRequestHeader[0] == SOCKS5_VER && !m_UDPRequestHeader[1])
@ -1252,10 +1264,10 @@ namespace transport
{ {
LogPrint(eLogError, "SSU2: Invalid proxy reply"); LogPrint(eLogError, "SSU2: Invalid proxy reply");
m_UDPAssociateSocket.reset (nullptr); m_UDPAssociateSocket.reset (nullptr);
} }
} }
}); });
} }
void SSU2Server::SendUDPAssociateRequest () void SSU2Server::SendUDPAssociateRequest ()
{ {
@ -1272,13 +1284,13 @@ namespace transport
if (ecode) if (ecode)
{ {
LogPrint(eLogError, "SSU2: Proxy write error ", ecode.message()); LogPrint(eLogError, "SSU2: Proxy write error ", ecode.message());
m_UDPAssociateSocket.reset (nullptr); m_UDPAssociateSocket.reset (nullptr);
ReconnectToProxy (); ReconnectToProxy ();
} }
else else
ReadUDPAssociateReply (); ReadUDPAssociateReply ();
}); });
} }
void SSU2Server::ReadUDPAssociateReply () void SSU2Server::ReadUDPAssociateReply ()
{ {
@ -1292,11 +1304,11 @@ namespace transport
LogPrint(eLogError, "SSU2: Proxy read error ", ecode.message()); LogPrint(eLogError, "SSU2: Proxy read error ", ecode.message());
m_UDPAssociateSocket.reset (nullptr); m_UDPAssociateSocket.reset (nullptr);
ReconnectToProxy (); ReconnectToProxy ();
} }
else else
{ {
if (m_UDPRequestHeader[0] == SOCKS5_VER && !m_UDPRequestHeader[1]) if (m_UDPRequestHeader[0] == SOCKS5_VER && !m_UDPRequestHeader[1])
{ {
if (m_UDPRequestHeader[3] == SOCKS5_ATYP_IPV4) if (m_UDPRequestHeader[3] == SOCKS5_ATYP_IPV4)
{ {
boost::asio::ip::address_v4::bytes_type bytes; boost::asio::ip::address_v4::bytes_type bytes;
@ -1306,21 +1318,21 @@ namespace transport
m_SocketV4.open (boost::asio::ip::udp::v4 ()); m_SocketV4.open (boost::asio::ip::udp::v4 ());
Receive (m_SocketV4); Receive (m_SocketV4);
ReadUDPAssociateSocket (); ReadUDPAssociateSocket ();
} }
else else
{ {
LogPrint(eLogError, "SSU2: Proxy UDP associate unsupported ATYP ", (int)m_UDPRequestHeader[3]); LogPrint(eLogError, "SSU2: Proxy UDP associate unsupported ATYP ", (int)m_UDPRequestHeader[3]);
m_UDPAssociateSocket.reset (nullptr); m_UDPAssociateSocket.reset (nullptr);
} }
} }
else else
{ {
LogPrint(eLogError, "SSU2: Proxy UDP associate error ", (int)m_UDPRequestHeader[1]); LogPrint(eLogError, "SSU2: Proxy UDP associate error ", (int)m_UDPRequestHeader[1]);
m_UDPAssociateSocket.reset (nullptr); m_UDPAssociateSocket.reset (nullptr);
} }
} }
}); });
} }
void SSU2Server::ReadUDPAssociateSocket () void SSU2Server::ReadUDPAssociateSocket ()
{ {
@ -1336,18 +1348,18 @@ namespace transport
m_ProxyRelayEndpoint.reset (nullptr); m_ProxyRelayEndpoint.reset (nullptr);
m_SocketV4.close (); m_SocketV4.close ();
ConnectToProxy (); // try to reconnect immediately ConnectToProxy (); // try to reconnect immediately
} }
else else
ReadUDPAssociateSocket (); ReadUDPAssociateSocket ();
}); });
} }
void SSU2Server::ReconnectToProxy () void SSU2Server::ReconnectToProxy ()
{ {
LogPrint(eLogInfo, "SSU2: Reconnect to proxy after ", SSU2_PROXY_CONNECT_RETRY_TIMEOUT, " seconds"); LogPrint(eLogInfo, "SSU2: Reconnect to proxy after ", SSU2_PROXY_CONNECT_RETRY_TIMEOUT, " seconds");
if (m_ProxyConnectRetryTimer) if (m_ProxyConnectRetryTimer)
m_ProxyConnectRetryTimer->cancel (); m_ProxyConnectRetryTimer->cancel ();
else else
m_ProxyConnectRetryTimer.reset (new boost::asio::deadline_timer (m_ReceiveService.GetService ())); m_ProxyConnectRetryTimer.reset (new boost::asio::deadline_timer (m_ReceiveService.GetService ()));
m_ProxyConnectRetryTimer->expires_from_now (boost::posix_time::seconds (SSU2_PROXY_CONNECT_RETRY_TIMEOUT)); m_ProxyConnectRetryTimer->expires_from_now (boost::posix_time::seconds (SSU2_PROXY_CONNECT_RETRY_TIMEOUT));
m_ProxyConnectRetryTimer->async_wait ( m_ProxyConnectRetryTimer->async_wait (
@ -1361,8 +1373,8 @@ namespace transport
ConnectToProxy (); ConnectToProxy ();
} }
}); });
} }
bool SSU2Server::SetProxy (const std::string& address, uint16_t port) bool SSU2Server::SetProxy (const std::string& address, uint16_t port)
{ {
boost::system::error_code ecode; boost::system::error_code ecode;
@ -1371,14 +1383,14 @@ namespace transport
{ {
m_IsThroughProxy = true; m_IsThroughProxy = true;
m_ProxyEndpoint.reset (new boost::asio::ip::tcp::endpoint (addr, port)); m_ProxyEndpoint.reset (new boost::asio::ip::tcp::endpoint (addr, port));
} }
else else
{ {
if (ecode) if (ecode)
LogPrint (eLogError, "SSU2: Invalid proxy address ", address, " ", ecode.message()); LogPrint (eLogError, "SSU2: Invalid proxy address ", address, " ", ecode.message());
return false; return false;
} }
return true; return true;
} }
} }
} }

Loading…
Cancel
Save