Browse Source

Merge pull request #136 from klondi/master

Improve SOCKS, handle  .b32.i2p conversions into address book
pull/138/head
orignal 10 years ago
parent
commit
7e4c416bc1
  1. 2
      AddressBook.cpp
  2. 4
      AddressBook.h
  3. 6
      ClientContext.cpp
  4. 2
      Destination.cpp
  5. 4
      HTTPServer.cpp
  6. 463
      SOCKS.cpp
  7. 108
      SOCKS.h

2
AddressBook.cpp

@ -240,7 +240,7 @@ namespace client
m_Storage = CreateStorage (); m_Storage = CreateStorage ();
m_Storage->AddAddress (ident); m_Storage->AddAddress (ident);
m_Addresses[address] = ident.GetIdentHash (); m_Addresses[address] = ident.GetIdentHash ();
LogPrint (address,"->",ident.GetIdentHash ().ToBase32 (), ".b32.i2p added"); LogPrint (address,"->", ToAddress(ident.GetIdentHash ()), " added");
} }
void AddressBook::InsertAddress (const i2p::data::IdentityEx& address) void AddressBook::InsertAddress (const i2p::data::IdentityEx& address)

4
AddressBook.h

@ -54,7 +54,9 @@ namespace client
void StopSubscriptions (); void StopSubscriptions ();
void LoadHostsFromStream (std::istream& f); void LoadHostsFromStream (std::istream& f);
void DownloadComplete (bool success); void DownloadComplete (bool success);
//This method returns the ".b32.i2p" address
std::string ToAddress(const i2p::data::IdentHash& ident) { return ident.ToBase32().append(".b32.i2p"); }
std::string ToAddress(const i2p::data::IdentityEx& ident) { return ToAddress(ident.GetIdentHash ()); }
private: private:
AddressBookStorage * CreateStorage (); AddressBookStorage * CreateStorage ();

6
ClientContext.cpp

@ -141,7 +141,7 @@ namespace client
s.read ((char *)buf, len); s.read ((char *)buf, len);
keys.FromBuffer (buf, len); keys.FromBuffer (buf, len);
delete[] buf; delete[] buf;
LogPrint ("Local address ", keys.GetPublic ().GetIdentHash ().ToBase32 (), ".b32.i2p loaded"); LogPrint ("Local address ", m_AddressBook.ToAddress(keys.GetPublic ().GetIdentHash ()), " loaded");
} }
else else
{ {
@ -154,7 +154,7 @@ namespace client
f.write ((char *)buf, len); f.write ((char *)buf, len);
delete[] buf; delete[] buf;
LogPrint ("New private keys file ", fullPath, " for ", keys.GetPublic ().GetIdentHash ().ToBase32 (), ".b32.i2p created"); LogPrint ("New private keys file ", fullPath, " for ", m_AddressBook.ToAddress(keys.GetPublic ().GetIdentHash ()), " created");
} }
auto localDestination = new ClientDestination (keys, isPublic); auto localDestination = new ClientDestination (keys, isPublic);
@ -197,7 +197,7 @@ namespace client
auto it = m_Destinations.find (keys.GetPublic ().GetIdentHash ()); auto it = m_Destinations.find (keys.GetPublic ().GetIdentHash ());
if (it != m_Destinations.end ()) if (it != m_Destinations.end ())
{ {
LogPrint ("Local destination ", keys.GetPublic ().GetIdentHash ().ToBase32 (), ".b32.i2p exists"); LogPrint ("Local destination ", m_AddressBook.ToAddress(keys.GetPublic ().GetIdentHash ()), " exists");
if (!it->second->IsRunning ()) if (!it->second->IsRunning ())
{ {
it->second->Start (); it->second->Start ();

2
Destination.cpp

@ -48,7 +48,7 @@ namespace client
} }
m_Pool = i2p::tunnel::tunnels.CreateTunnelPool (this, inboundTunnelLen, outboundTunnelLen); m_Pool = i2p::tunnel::tunnels.CreateTunnelPool (this, inboundTunnelLen, outboundTunnelLen);
if (m_IsPublic) if (m_IsPublic)
LogPrint (eLogInfo, "Local address ", GetIdentHash ().ToBase32 (), ".b32.i2p created"); LogPrint (eLogInfo, "Local address ", i2p::client::context.GetAddressBook ().ToAddress(GetIdentHash()), " created");
m_StreamingDestination = new i2p::stream::StreamingDestination (*this); // TODO: m_StreamingDestination = new i2p::stream::StreamingDestination (*this); // TODO:
} }

4
HTTPServer.cpp

@ -792,7 +792,7 @@ namespace util
std::string b32 = it.first.ToBase32 (); std::string b32 = it.first.ToBase32 ();
s << "<a href=/?" << HTTP_COMMAND_LOCAL_DESTINATION; s << "<a href=/?" << HTTP_COMMAND_LOCAL_DESTINATION;
s << "&" << HTTP_PARAM_BASE32_ADDRESS << "=" << b32 << ">"; s << "&" << HTTP_PARAM_BASE32_ADDRESS << "=" << b32 << ">";
s << b32 << ".b32.i2p</a><br>" << std::endl; s << i2p::client::context.GetAddressBook ().ToAddress(it.second->GetIdentHash()) << "</a><br>" << std::endl;
} }
} }
@ -832,7 +832,7 @@ namespace util
s << "<br><b>Streams:</b><br>"; s << "<br><b>Streams:</b><br>";
for (auto it: dest->GetStreamingDestination ()->GetStreams ()) for (auto it: dest->GetStreamingDestination ()->GetStreams ())
{ {
s << it.first << "->" << it.second->GetRemoteIdentity ().GetIdentHash ().ToBase32 () << ".b32.i2p "; s << it.first << "->" << i2p::client::context.GetAddressBook ().ToAddress(it.second->GetRemoteIdentity ()) << " ";
s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]"; s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
s << " [out:" << it.second->GetSendQueueSize () << "][in:" << it.second->GetReceiveQueueSize () << "]"; s << " [out:" << it.second->GetSendQueueSize () << "][in:" << it.second->GetReceiveQueueSize () << "]";
s << "<br>"<< std::endl; s << "<br>"<< std::endl;

463
SOCKS.cpp

@ -4,6 +4,7 @@
#include "Destination.h" #include "Destination.h"
#include "ClientContext.h" #include "ClientContext.h"
#include "I2PEndian.h" #include "I2PEndian.h"
#include <cstring>
#include <cassert> #include <cassert>
namespace i2p namespace i2p
@ -22,215 +23,192 @@ namespace proxy
} }
} }
void SOCKSHandler::Done() {
if (m_parent) m_parent->RemoveHandler (shared_from_this ());
}
void SOCKSHandler::Terminate() { void SOCKSHandler::Terminate() {
CloseStream(); if (dead.exchange(true)) return;
CloseSock(); if (m_sock) {
delete this; // HACK: ew LogPrint(eLogDebug,"--- SOCKS close sock");
m_sock->close();
delete m_sock;
m_sock = nullptr;
}
if (m_stream) {
LogPrint(eLogDebug,"--- SOCKS close stream");
m_stream.reset ();
}
Done();
} }
void SOCKSHandler::Socks5AuthNegoFailed() boost::asio::const_buffers_1 SOCKSHandler::GenerateSOCKS4Response(SOCKSHandler::errTypes error, uint32_t ip, uint16_t port)
{ {
LogPrint(eLogWarning,"--- SOCKS5 authentication negotiation failed"); assert(error >= SOCKS4_OK);
boost::asio::async_write(*m_sock, boost::asio::buffer("\x05\xff",2), m_response[0] = '\x00'; //Version
std::bind(&SOCKSHandler::SentSocksFailed, this, m_response[1] = error; //Response code
std::placeholders::_1)); htobe16buf(m_response+2,port); //Port
htobe32buf(m_response+4,ip); //IP
return boost::asio::const_buffers_1(m_response,8);
} }
void SOCKSHandler::Socks5ChooseAuth() boost::asio::const_buffers_1 SOCKSHandler::GenerateSOCKS5Response(SOCKSHandler::errTypes error, SOCKSHandler::addrTypes type,
const SOCKSHandler::address &addr, uint16_t port)
{ {
LogPrint(eLogDebug,"--- SOCKS5 choosing authentication method"); size_t size;
//TODO: Choose right method assert(error <= SOCKS5_ADDR_UNSUP);
boost::asio::async_write(*m_sock, boost::asio::buffer("\x05\x00",2), m_response[0] = '\x05'; //Version
std::bind(&SOCKSHandler::SentSocksResponse, this, m_response[1] = error; //Response code
std::placeholders::_1, nullptr)); m_response[2] = '\x00'; //RSV
m_response[3] = type; //Address type
switch (type) {
case ADDR_IPV4:
size = 10;
htobe32buf(m_response+4,addr.ip);
break;
case ADDR_IPV6:
size = 22;
memcpy(m_response+4,addr.ipv6, 16);
break;
case ADDR_DNS:
size = 7+addr.dns.size;
m_response[4] = addr.dns.size;
memcpy(m_response+5,addr.dns.value, addr.dns.size);
break;
}
htobe16buf(m_response+size-2,port); //Port
return boost::asio::const_buffers_1(m_response,size);
} }
static const char *socks5Replies[9] = { bool SOCKSHandler::Socks5ChooseAuth()
"\x05\x00\x00\x01\x00\x00\x00\x00\x00\x00", {
"\x05\x01\x00\x01\x00\x00\x00\x00\x00\x00", m_response[0] = '\x05'; //Version
"\x05\x02\x00\x01\x00\x00\x00\x00\x00\x00", m_response[1] = m_authchosen; //Response code
"\x05\x03\x00\x01\x00\x00\x00\x00\x00\x00", boost::asio::const_buffers_1 response(m_response,2);
"\x05\x04\x00\x01\x00\x00\x00\x00\x00\x00", if (m_authchosen == AUTH_UNACCEPTABLE) {
"\x05\x05\x00\x01\x00\x00\x00\x00\x00\x00", LogPrint(eLogWarning,"--- SOCKS5 authentication negotiation failed");
"\x05\x06\x00\x01\x00\x00\x00\x00\x00\x00", boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksFailed, this, std::placeholders::_1));
"\x05\x07\x00\x01\x00\x00\x00\x00\x00\x00", return false;
"\x05\x08\x00\x01\x00\x00\x00\x00\x00\x00" }; } else {
LogPrint(eLogDebug,"--- SOCKS5 choosing authentication method: ", m_authchosen);
boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksResponse, this, std::placeholders::_1));
return true;
}
}
/* All hope is lost */ /* All hope is lost beyond this point */
void SOCKSHandler::SocksRequestFailed() void SOCKSHandler::SocksRequestFailed(SOCKSHandler::errTypes error)
{ {
boost::asio::const_buffers_1 response(nullptr,0);
assert(error != SOCKS4_OK && error != SOCKS5_OK);
switch (m_socksv) { switch (m_socksv) {
case SOCKS4: case SOCKS4:
LogPrint(eLogWarning,"--- SOCKS4 failed"); LogPrint(eLogWarning,"--- SOCKS4 failed: ", error);
//TODO: send the right response if (error < SOCKS4_OK) error = SOCKS4_FAIL; //Transparently map SOCKS5 errors
boost::asio::async_write(*m_sock, boost::asio::buffer("\x00\x5b\x00\x00\x00\x00\x00\x00",8), response = GenerateSOCKS4Response(error, m_4aip, m_port);
std::bind(&SOCKSHandler::SentSocksFailed, this, std::placeholders::_1));
break; break;
case SOCKS5: case SOCKS5:
assert(m_error <= SOCKS5_ADDR_UNSUP); LogPrint(eLogWarning,"--- SOCKS5 failed: ", error);
LogPrint(eLogWarning,"--- SOCKS5 failed"); response = GenerateSOCKS5Response(error, m_addrtype, m_address, m_port);
//TODO: use error properly and address type m_error
boost::asio::async_write(*m_sock, boost::asio::buffer(socks5Replies[m_error],10),
std::bind(&SOCKSHandler::SentSocksFailed, this, std::placeholders::_1));
break; break;
} }
boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksFailed, this, std::placeholders::_1));
} }
void SOCKSHandler::SocksRequestSuccess() void SOCKSHandler::SocksRequestSuccess()
{ {
std::shared_ptr<std::vector<uint8_t>> response(new std::vector<uint8_t>); boost::asio::const_buffers_1 response(nullptr,0);
//TODO: this should depend on things like the command type and callbacks may change
switch (m_socksv) { switch (m_socksv) {
case SOCKS4: case SOCKS4:
LogPrint(eLogInfo,"--- SOCKS4 connection success"); LogPrint(eLogInfo,"--- SOCKS4 connection success");
//TODO: send the right response response = GenerateSOCKS4Response(SOCKS4_OK, m_4aip, m_port);
boost::asio::async_write(*m_sock, boost::asio::buffer("\x00\x5a\x00\x00\x00\x00\x00\x00",8),
std::bind(&SOCKSHandler::SentSocksResponse, this,
std::placeholders::_1, nullptr));
break; break;
case SOCKS5: case SOCKS5:
LogPrint(eLogInfo,"--- SOCKS5 connection success"); LogPrint(eLogInfo,"--- SOCKS5 connection success");
//TODO: send the right response using the port? and the localside i2p address auto s = i2p::client::context.GetAddressBook().ToAddress(m_parent->GetLocalDestination()->GetIdentHash());
boost::asio::async_write(*m_sock, boost::asio::buffer("\x05\x00\x00\x01\x00\x00\x00\x00\x00\x00",10), address ad; ad.dns.FromString(s);
std::bind(&SOCKSHandler::SentSocksResponse, this, //HACK only 16 bits passed in port as SOCKS5 doesn't allow for more
std::placeholders::_1, response)); response = GenerateSOCKS5Response(SOCKS5_OK, ADDR_DNS, ad, m_stream->GetRecvStreamID());
break; break;
} }
boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksDone, this, std::placeholders::_1));
} }
void SOCKSHandler::EnterState(SOCKSHandler::state nstate, uint8_t parseleft) {
void SOCKSHandler::CloseSock() switch (nstate) {
{ case GET_PORT: parseleft = 2; break;
if (m_sock) { case GET_IPV4: m_addrtype = ADDR_IPV4; m_address.ip = 0; parseleft = 4; break;
LogPrint(eLogDebug,"--- SOCKS close sock"); case GET4_IDENT: m_4aip = m_address.ip; break;
m_sock->close(); case GET4A_HOST:
delete m_sock; case GET5_HOST: m_addrtype = ADDR_DNS; m_address.dns.size = 0; break;
m_sock = nullptr; case GET5_IPV6: m_addrtype = ADDR_IPV6; parseleft = 16; break;
default:;
} }
m_parseleft = parseleft;
m_state = nstate;
} }
void SOCKSHandler::CloseStream() void SOCKSHandler::ValidateSOCKSRequest() {
{ if ( m_cmd != CMD_CONNECT ) {
if (m_stream) { //TODO: we need to support binds and other shit!
LogPrint(eLogDebug,"--- SOCKS close stream"); LogPrint(eLogError,"--- SOCKS unsupported command: ", m_cmd);
m_stream.reset (); SocksRequestFailed(SOCKS5_CMD_UNSUP);
return;
} }
//TODO: we may want to support other address types!
if ( m_addrtype != ADDR_DNS ) {
switch (m_socksv) {
case SOCKS5:
LogPrint(eLogError,"--- SOCKS5 unsupported address type: ", m_addrtype);
break;
case SOCKS4:
LogPrint(eLogError,"--- SOCKS4a rejected because it's actually SOCKS4");
break;
} }
SocksRequestFailed(SOCKS5_ADDR_UNSUP);
std::size_t SOCKSHandler::HandleData(uint8_t *sock_buff, std::size_t len) return;
{ }
assert(len); // This should always be called with a least a byte left to parse //TODO: we may want to support other domains
switch (m_state) { if(m_addrtype == ADDR_DNS && m_address.dns.ToString().find(".i2p") == std::string::npos) {
case GET_VERSION: LogPrint(eLogError,"--- SOCKS invalid hostname: ", m_address.dns.ToString());
return HandleVersion(sock_buff); SocksRequestFailed(SOCKS5_ADDR_UNSUP);
case SOCKS5_AUTHNEGO: return;
return HandleSOCKS5AuthNego(sock_buff,len);
case SOCKS_REQUEST:
return HandleSOCKSRequest(sock_buff,len);
default:
LogPrint(eLogError,"--- SOCKS state?? ", m_state);
Terminate();
return 0;
} }
} }
std::size_t SOCKSHandler::HandleVersion(uint8_t *sock_buff) bool SOCKSHandler::HandleData(uint8_t *sock_buff, std::size_t len)
{ {
assert(len); // This should always be called with a least a byte left to parse
while (len > 0) {
switch (m_state) {
case GET_SOCKSV:
m_socksv = (SOCKSHandler::socksVersions) *sock_buff;
switch (*sock_buff) { switch (*sock_buff) {
case SOCKS4: case SOCKS4:
m_state = SOCKS_REQUEST; // Switch to the 4 handler EnterState(GET_COMMAND); //Initialize the parser at the right position
m_pstate = GET_COMMAND; //Initialize the parser at the right position
break; break;
case SOCKS5: case SOCKS5:
m_state = SOCKS5_AUTHNEGO; // Switch to the 5 handler EnterState(GET5_AUTHNUM); //Initialize the parser at the right position
m_pstate = GET5_AUTHNUM; //Initialize the parser at the right position
break; break;
default: default:
LogPrint(eLogError,"--- SOCKS rejected invalid version: ", ((int)*sock_buff)); LogPrint(eLogError,"--- SOCKS rejected invalid version: ", ((int)*sock_buff));
Terminate(); Terminate();
return 0; return false;
}
m_socksv = (SOCKSHandler::socksVersions) *sock_buff;
return 1;
} }
break;
std::size_t SOCKSHandler::HandleSOCKS5AuthNego(uint8_t *sock_buff, std::size_t len)
{
std::size_t rv = 0;
while (len > 0) {
rv++;
switch (m_pstate)
{
case GET5_AUTHNUM: case GET5_AUTHNUM:
m_authleft = *sock_buff; EnterState(GET5_AUTH, *sock_buff);
m_pstate = GET5_AUTH;
break; break;
case GET5_AUTH: case GET5_AUTH:
m_authleft --; m_parseleft --;
if (*sock_buff == AUTH_NONE) if (*sock_buff == AUTH_NONE)
m_authchosen = AUTH_NONE; m_authchosen = AUTH_NONE;
if ( m_authleft == 0 ) { if ( m_parseleft == 0 ) {
if (m_authchosen == AUTH_UNACCEPTABLE) { if (!Socks5ChooseAuth()) return false;
//TODO: we maybe want support for other methods! EnterState(GET5_REQUESTV);
LogPrint(eLogError,"--- SOCKS5 couldn't negotiate authentication");
Socks5AuthNegoFailed();
return 0;
}
m_pstate = GET5_REQUESTV;
m_state = SOCKS_REQUEST;
m_need_more = false;
Socks5ChooseAuth();
return rv;
} }
break; break;
default:
LogPrint(eLogError,"--- SOCKS5 parse state?? ", m_pstate);
Terminate();
return 0;
}
sock_buff++;
len--;
}
return rv;
}
bool SOCKSHandler::ValidateSOCKSRequest() {
if ( m_cmd != CMD_CONNECT ) {
//TODO: we need to support binds and other shit!
LogPrint(eLogError,"--- SOCKS unsupported command: ", m_cmd);
m_error = SOCKS5_CMD_UNSUP;
SocksRequestFailed();
return false;
}
//TODO: we may want to support other address types!
if ( m_addrtype != ADDR_DNS ) {
switch (m_socksv) {
case SOCKS5:
LogPrint(eLogError,"--- SOCKS5 unsupported address type: ", m_addrtype);
m_error = SOCKS5_ADDR_UNSUP;
break;
case SOCKS4:
LogPrint(eLogError,"--- SOCKS4a rejected because it's actually SOCKS4");
break;
}
SocksRequestFailed();
return false;
}
//TODO: we may want to support other domains
if(m_addrtype == ADDR_DNS && m_destination.find(".i2p") == std::string::npos) {
LogPrint(eLogError,"--- SOCKS invalid hostname: ", m_destination);
m_error = SOCKS5_ADDR_UNSUP;
SocksRequestFailed();
return false;
}
return true;
}
std::size_t SOCKSHandler::HandleSOCKSRequest(uint8_t *sock_buff, std::size_t len)
{
std::size_t rv = 0;
while (len > 0) {
rv++;
switch (m_pstate)
{
case GET_COMMAND: case GET_COMMAND:
switch (*sock_buff) { switch (*sock_buff) {
case CMD_CONNECT: case CMD_CONNECT:
@ -240,124 +218,110 @@ namespace proxy
if (m_socksv == SOCKS5) break; if (m_socksv == SOCKS5) break;
default: default:
LogPrint(eLogError,"--- SOCKS invalid command: ", ((int)*sock_buff)); LogPrint(eLogError,"--- SOCKS invalid command: ", ((int)*sock_buff));
m_error = SOCKS5_GEN_FAIL; SocksRequestFailed(SOCKS5_GEN_FAIL);
SocksRequestFailed(); return false;
return 0;
} }
m_cmd = (SOCKSHandler::cmdTypes)*sock_buff; m_cmd = (SOCKSHandler::cmdTypes)*sock_buff;
switch (m_socksv) { switch (m_socksv) {
case SOCKS5: m_pstate = GET5_GETRSV; break; case SOCKS5: EnterState(GET5_GETRSV); break;
case SOCKS4: m_pstate = GET_PORT; m_addrleft = 2; break; case SOCKS4: EnterState(GET_PORT); break;
} }
break; break;
case GET_PORT: case GET_PORT:
m_port = (m_port << 8)|((uint16_t)*sock_buff); m_port = (m_port << 8)|((uint16_t)*sock_buff);
m_addrleft--; m_parseleft--;
if (m_addrleft == 0) { if (m_parseleft == 0) {
switch (m_socksv) { switch (m_socksv) {
case SOCKS5: m_pstate = DONE; break; case SOCKS5: EnterState(DONE); break;
case SOCKS4: m_pstate = GET_IPV4; m_addrleft = 4; break; case SOCKS4: EnterState(GET_IPV4); break;
} }
} }
break; break;
case GET_IPV4: case GET_IPV4:
m_ip = (m_ip << 8)|((uint32_t)*sock_buff); m_address.ip = (m_address.ip << 8)|((uint32_t)*sock_buff);
m_addrleft--; m_parseleft--;
if (m_addrleft == 0) { if (m_parseleft == 0) {
switch (m_socksv) { switch (m_socksv) {
case SOCKS5: m_pstate = GET_PORT; m_addrleft = 2; break; case SOCKS5: EnterState(GET_PORT); break;
case SOCKS4: m_pstate = GET4_IDENT; break; case SOCKS4: EnterState(GET4_IDENT); m_4aip = m_address.ip; break;
} }
} }
break; break;
case GET4_IDENT: case GET4_IDENT:
if (!*sock_buff) { if (!*sock_buff) {
if( m_ip == 0 || m_ip > 255 ) { if( m_4aip == 0 || m_4aip > 255 ) {
m_addrtype = ADDR_IPV4; EnterState(DONE);
m_pstate = DONE;
} else { } else {
m_addrtype = ADDR_DNS; EnterState(GET4A_HOST);
m_pstate = GET4A_HOST;
} }
} }
break; break;
case GET4A_HOST: case GET4A_HOST:
if (!*sock_buff) { if (!*sock_buff) {
m_pstate = DONE; EnterState(DONE);
break; break;
} }
if (m_destination.size() > max_socks_hostname_size) { if (m_address.dns.size >= max_socks_hostname_size) {
LogPrint(eLogError,"--- SOCKS4a destination is too large"); LogPrint(eLogError,"--- SOCKS4a destination is too large");
SocksRequestFailed(); SocksRequestFailed(SOCKS4_FAIL);
return 0; return false;
} }
m_destination.push_back(*sock_buff); m_address.dns.push_back(*sock_buff);
break; break;
case GET5_REQUESTV: case GET5_REQUESTV:
if (*sock_buff != SOCKS5) { if (*sock_buff != SOCKS5) {
LogPrint(eLogError,"--- SOCKS5 rejected unknown request version: ", ((int)*sock_buff)); LogPrint(eLogError,"--- SOCKS5 rejected unknown request version: ", ((int)*sock_buff));
m_error = SOCKS5_GEN_FAIL; SocksRequestFailed(SOCKS5_GEN_FAIL);
SocksRequestFailed(); return false;
return 0;
} }
m_pstate = GET_COMMAND; EnterState(GET_COMMAND);
break; break;
case GET5_GETRSV: case GET5_GETRSV:
if ( *sock_buff != 0 ) { if ( *sock_buff != 0 ) {
LogPrint(eLogError,"--- SOCKS5 unknown reserved field: ", ((int)*sock_buff)); LogPrint(eLogError,"--- SOCKS5 unknown reserved field: ", ((int)*sock_buff));
m_error = SOCKS5_GEN_FAIL; SocksRequestFailed(SOCKS5_GEN_FAIL);
SocksRequestFailed(); return false;
return 0;
} }
m_pstate = GET5_GETADDRTYPE; EnterState(GET5_GETADDRTYPE);
break; break;
case GET5_GETADDRTYPE: case GET5_GETADDRTYPE:
switch (*sock_buff) { switch (*sock_buff) {
case ADDR_IPV4: m_pstate = GET_IPV4; m_addrleft = 4; break; case ADDR_IPV4: EnterState(GET_IPV4); break;
case ADDR_IPV6: m_pstate = GET5_IPV6; m_addrleft = 16; break; case ADDR_IPV6: EnterState(GET5_IPV6); break;
case ADDR_DNS : m_pstate = GET5_HOST_SIZE; break; case ADDR_DNS : EnterState(GET5_HOST_SIZE); break;
default: default:
LogPrint(eLogError,"--- SOCKS5 unknown address type: ", ((int)*sock_buff)); LogPrint(eLogError,"--- SOCKS5 unknown address type: ", ((int)*sock_buff));
m_error = SOCKS5_GEN_FAIL; SocksRequestFailed(SOCKS5_GEN_FAIL);
SocksRequestFailed(); return false;
return 0;
} }
m_addrtype = (SOCKSHandler::addrTypes)*sock_buff;
break; break;
case GET5_IPV6: case GET5_IPV6:
m_ipv6[16-m_addrleft] = *sock_buff; m_address.ipv6[16-m_parseleft] = *sock_buff;
m_addrleft--; m_parseleft--;
if (m_addrleft == 0) { if (m_parseleft == 0) EnterState(GET_PORT);
m_pstate = GET_PORT;
m_addrleft = 2;
}
break; break;
case GET5_HOST_SIZE: case GET5_HOST_SIZE:
m_addrleft = *sock_buff; EnterState(GET5_HOST, *sock_buff);
m_pstate = GET5_HOST;
break; break;
case GET5_HOST: case GET5_HOST:
m_destination.push_back(*sock_buff); m_address.dns.push_back(*sock_buff);
m_addrleft--; m_parseleft--;
if (m_addrleft == 0) { if (m_parseleft == 0) EnterState(GET_PORT);
m_pstate = GET_PORT;
m_addrleft = 2;
}
break; break;
default: default:
LogPrint(eLogError,"--- SOCKS parse state?? ", m_pstate); LogPrint(eLogError,"--- SOCKS parse state?? ", m_state);
Terminate(); Terminate();
return 0; return false;
}
if (m_pstate == DONE) {
m_state = READY;
m_need_more = false;
return (ValidateSOCKSRequest() ? rv : 0);
} }
sock_buff++; sock_buff++;
len--; len--;
if (len && m_state == DONE) {
LogPrint(eLogError,"--- SOCKS rejected because we can't handle extra data");
SocksRequestFailed(SOCKS5_GEN_FAIL);
return false;
}
} }
return rv; return true;
} }
void SOCKSHandler::HandleSockRecv(const boost::system::error_code & ecode, std::size_t len) void SOCKSHandler::HandleSockRecv(const boost::system::error_code & ecode, std::size_t len)
@ -369,34 +333,19 @@ namespace proxy
return; return;
} }
std::size_t pos = 0; if (HandleData(m_sock_buff, len)) {
m_need_more = true; if (m_state == DONE) {
while (pos != len && m_state != READY && m_need_more) { LogPrint(eLogInfo,"--- SOCKS requested ", m_address.dns.ToString(), ":" , m_port);
assert(pos < len); //We are overflowing the buffer otherwise
std::size_t rv = HandleData(m_sock_buff + pos, len - pos);
if (!rv) return; //Something went wrong die misserably
pos += rv;
}
assert(!(m_state == READY && m_need_more));
if (m_state == READY) {
LogPrint(eLogInfo,"--- SOCKS requested ", m_destination, ":" , m_port);
if (pos != len) {
LogPrint(eLogError,"--- SOCKS rejected because we can't handle extra data");
SocksRequestFailed();
return ;
}
m_parent->GetLocalDestination ()->CreateStream ( m_parent->GetLocalDestination ()->CreateStream (
std::bind (&SOCKSHandler::HandleStreamRequestComplete, std::bind (&SOCKSHandler::HandleStreamRequestComplete,
this, std::placeholders::_1), m_destination, m_port); this, std::placeholders::_1), m_address.dns.ToString(), m_port);
} else if (m_need_more) { } else {
LogPrint (eLogDebug,"--- SOCKS Need more data");
AsyncSockRead(); AsyncSockRead();
} }
} }
}
void SOCKSHandler::SentSocksFailed(const boost::system::error_code & ecode) void SOCKSHandler::SentSocksFailed(const boost::system::error_code & ecode)
{ {
if (!ecode) { if (!ecode) {
@ -407,22 +356,26 @@ namespace proxy
} }
} }
void SOCKSHandler::SentSocksResponse(const boost::system::error_code & ecode, std::shared_ptr<std::vector<uint8_t>> response) void SOCKSHandler::SentSocksDone(const boost::system::error_code & ecode)
{ {
response.reset(); // Information wants to be free, so does memory
if (!ecode) { if (!ecode) {
if(m_state == READY) { if (dead.exchange(true)) return;
LogPrint (eLogInfo,"--- SOCKS New I2PTunnel connection"); LogPrint (eLogInfo,"--- SOCKS New I2PTunnel connection");
auto connection = std::make_shared<i2p::client::I2PTunnelConnection>((i2p::client::I2PTunnel *)m_parent, m_sock, m_stream); auto connection = std::make_shared<i2p::client::I2PTunnelConnection>((i2p::client::I2PTunnel *)m_parent, m_sock, m_stream);
m_parent->AddConnection (connection); m_parent->AddConnection (connection);
connection->I2PConnect (); connection->I2PConnect ();
} else { Done();
LogPrint (eLogDebug,"--- SOCKS Go to next state");
AsyncSockRead();
}
} }
else else
{ {
LogPrint (eLogError,"--- SOCKS Closing socket after completion reply because: ", ecode.message ());
Terminate();
}
}
void SOCKSHandler::SentSocksResponse(const boost::system::error_code & ecode)
{
if (ecode) {
LogPrint (eLogError,"--- SOCKS Closing socket after sending reply because: ", ecode.message ()); LogPrint (eLogError,"--- SOCKS Closing socket after sending reply because: ", ecode.message ());
Terminate(); Terminate();
} }
@ -434,13 +387,11 @@ namespace proxy
m_stream = stream; m_stream = stream;
SocksRequestSuccess(); SocksRequestSuccess();
} else { } else {
m_error = SOCKS5_HOST_UNREACH;
LogPrint (eLogError,"--- SOCKS Issue when creating the stream, check the previous warnings for more info."); LogPrint (eLogError,"--- SOCKS Issue when creating the stream, check the previous warnings for more info.");
SocksRequestFailed(); SocksRequestFailed(SOCKS5_HOST_UNREACH);
} }
} }
void SOCKSServer::Start () void SOCKSServer::Start ()
{ {
m_Acceptor.listen (); m_Acceptor.listen ();
@ -452,6 +403,7 @@ namespace proxy
m_Acceptor.close(); m_Acceptor.close();
m_Timer.cancel (); m_Timer.cancel ();
ClearConnections (); ClearConnections ();
ClearHandlers();
} }
void SOCKSServer::Accept () void SOCKSServer::Accept ()
@ -461,12 +413,29 @@ namespace proxy
std::placeholders::_1, newSocket)); std::placeholders::_1, newSocket));
} }
void SOCKSServer::AddHandler (std::shared_ptr<SOCKSHandler> handler) {
std::unique_lock<std::mutex> l(m_HandlersMutex);
m_Handlers.insert (handler);
}
void SOCKSServer::RemoveHandler (std::shared_ptr<SOCKSHandler> handler)
{
std::unique_lock<std::mutex> l(m_HandlersMutex);
m_Handlers.erase (handler);
}
void SOCKSServer::ClearHandlers ()
{
std::unique_lock<std::mutex> l(m_HandlersMutex);
m_Handlers.clear ();
}
void SOCKSServer::HandleAccept (const boost::system::error_code& ecode, boost::asio::ip::tcp::socket * socket) void SOCKSServer::HandleAccept (const boost::system::error_code& ecode, boost::asio::ip::tcp::socket * socket)
{ {
if (!ecode) if (!ecode)
{ {
LogPrint(eLogDebug,"--- SOCKS accepted"); LogPrint(eLogDebug,"--- SOCKS accepted");
new SOCKSHandler(this, socket); AddHandler(std::make_shared<SOCKSHandler> (this, socket));
Accept(); Accept();
} }
else else

108
SOCKS.h

@ -2,8 +2,11 @@
#define SOCKS_H__ #define SOCKS_H__
#include <memory> #include <memory>
#include <vector> #include <string>
#include <set>
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include <mutex>
#include <atomic>
#include "Identity.h" #include "Identity.h"
#include "Streaming.h" #include "Streaming.h"
#include "I2PTunnel.h" #include "I2PTunnel.h"
@ -16,18 +19,23 @@ namespace proxy
const size_t socks_buffer_size = 8192; const size_t socks_buffer_size = 8192;
const size_t max_socks_hostname_size = 255; // Limit for socks5 and bad idea to traverse const size_t max_socks_hostname_size = 255; // Limit for socks5 and bad idea to traverse
class SOCKSServer; struct SOCKSDnsAddress {
class SOCKSHandler { uint8_t size;
char value[max_socks_hostname_size];
void FromString (std::string str) {
size = str.length();
if (str.length() > max_socks_hostname_size) size = max_socks_hostname_size;
memcpy(value,str.c_str(),size);
}
std::string ToString() { return std::string(value, size); }
void push_back (char c) { value[size++] = c; }
};
class SOCKSServer;
class SOCKSHandler: public std::enable_shared_from_this<SOCKSHandler> {
private: private:
enum state { enum state {
GET_VERSION, // Get SOCKS version GET_SOCKSV,
SOCKS5_AUTHNEGO, //Authentication negotiation
SOCKS5_AUTH, //Authentication
SOCKS_REQUEST, //Request
READY // Ready to connect
};
enum parseState {
GET_COMMAND, GET_COMMAND,
GET_PORT, GET_PORT,
GET_IPV4, GET_IPV4,
@ -78,59 +86,68 @@ namespace proxy
SOCKS4 = 4, // SOCKS4 SOCKS4 = 4, // SOCKS4
SOCKS5 = 5 // SOCKS5 SOCKS5 = 5 // SOCKS5
}; };
union address {
uint32_t ip;
SOCKSDnsAddress dns;
uint8_t ipv6[16];
};
void EnterState(state nstate, uint8_t parseleft = 1);
void GotClientRequest(boost::system::error_code & ecode, std::string & host, uint16_t port); bool HandleData(uint8_t *sock_buff, std::size_t len);
std::size_t HandleData(uint8_t *sock_buff, std::size_t len); void ValidateSOCKSRequest();
std::size_t HandleVersion(uint8_t *sock_buff);
std::size_t HandleSOCKS5AuthNego(uint8_t *sock_buff, std::size_t len);
std::size_t HandleSOCKSRequest(uint8_t *sock_buff, std::size_t len);
bool ValidateSOCKSRequest();
void HandleSockRecv(const boost::system::error_code & ecode, std::size_t bytes_transfered); void HandleSockRecv(const boost::system::error_code & ecode, std::size_t bytes_transfered);
void Done();
void Terminate(); void Terminate();
void CloseSock();
void CloseStream();
void AsyncSockRead(); void AsyncSockRead();
void Socks5AuthNegoFailed(); boost::asio::const_buffers_1 GenerateSOCKS5SelectAuth(authMethods method);
void Socks5ChooseAuth(); boost::asio::const_buffers_1 GenerateSOCKS4Response(errTypes error, uint32_t ip, uint16_t port);
void SocksRequestFailed(); boost::asio::const_buffers_1 GenerateSOCKS5Response(errTypes error, addrTypes type, const address &addr, uint16_t port);
bool Socks5ChooseAuth();
void SocksRequestFailed(errTypes error);
void SocksRequestSuccess(); void SocksRequestSuccess();
void SentSocksFailed(const boost::system::error_code & ecode); void SentSocksFailed(const boost::system::error_code & ecode);
//HACK: we need to pass the shared_ptr to ensure the buffer will live enough void SentSocksDone(const boost::system::error_code & ecode);
void SentSocksResponse(const boost::system::error_code & ecode, std::shared_ptr<std::vector<uint8_t>> response); void SentSocksResponse(const boost::system::error_code & ecode);
void HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream); void HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream);
uint8_t m_sock_buff[socks_buffer_size]; uint8_t m_sock_buff[socks_buffer_size];
SOCKSServer * m_parent; SOCKSServer * m_parent;
boost::asio::ip::tcp::socket * m_sock; boost::asio::ip::tcp::socket * m_sock;
std::shared_ptr<i2p::stream::Stream> m_stream; std::shared_ptr<i2p::stream::Stream> m_stream;
state m_state; uint8_t m_response[7+max_socks_hostname_size];
parseState m_pstate; address m_address; //Address
uint8_t m_command; uint32_t m_4aip; //Used in 4a requests
uint16_t m_port; uint16_t m_port;
uint32_t m_ip; uint8_t m_command;
uint8_t m_ipv6[16]; uint8_t m_parseleft; //Octets left to parse
std::string m_destination;
uint8_t m_authleft; //Authentication methods left
//TODO: this will probably be more elegant as enums
authMethods m_authchosen; //Authentication chosen authMethods m_authchosen; //Authentication chosen
addrTypes m_addrtype; //Address type chosen addrTypes m_addrtype; //Address type chosen
uint8_t m_addrleft; //Octets of DNS address left
errTypes m_error; //Error cause
socksVersions m_socksv; //Socks version socksVersions m_socksv; //Socks version
cmdTypes m_cmd; // Command requested cmdTypes m_cmd; // Command requested
bool m_need_more; //The parser still needs to receive more data state m_state;
std::atomic<bool> dead; //To avoid cleaning up multiple times
public: public:
SOCKSHandler(SOCKSServer * parent, boost::asio::ip::tcp::socket * sock) : SOCKSHandler(SOCKSServer * parent, boost::asio::ip::tcp::socket * sock) :
m_parent(parent), m_sock(sock), m_stream(nullptr), m_state(GET_VERSION), m_parent(parent), m_sock(sock), m_stream(nullptr),
m_authchosen(AUTH_UNACCEPTABLE), m_addrtype(ADDR_IPV4), m_error(SOCKS5_GEN_FAIL) m_authchosen(AUTH_UNACCEPTABLE), m_addrtype(ADDR_IPV4), dead(false)
{ AsyncSockRead(); m_destination.reserve(max_socks_hostname_size+1); } { m_address.ip = 0; AsyncSockRead(); EnterState(GET_SOCKSV); }
~SOCKSHandler() { CloseSock(); CloseStream(); } ~SOCKSHandler() { Terminate(); }
}; };
class SOCKSServer: public i2p::client::I2PTunnel class SOCKSServer: public i2p::client::I2PTunnel
{ {
private:
std::set<std::shared_ptr<SOCKSHandler> > m_Handlers;
boost::asio::ip::tcp::acceptor m_Acceptor;
boost::asio::deadline_timer m_Timer;
std::mutex m_HandlersMutex;
private:
void Accept();
void HandleAccept(const boost::system::error_code& ecode, boost::asio::ip::tcp::socket * socket);
public: public:
SOCKSServer(int port) : I2PTunnel(nullptr), SOCKSServer(int port) : I2PTunnel(nullptr),
m_Acceptor (GetService (), boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4(), port)), m_Acceptor (GetService (), boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4(), port)),
@ -139,16 +156,9 @@ namespace proxy
void Start (); void Start ();
void Stop (); void Stop ();
void AddHandler (std::shared_ptr<SOCKSHandler> handler);
private: void RemoveHandler (std::shared_ptr<SOCKSHandler> handler);
void ClearHandlers ();
void Accept();
void HandleAccept(const boost::system::error_code& ecode, boost::asio::ip::tcp::socket * socket);
private:
boost::asio::ip::tcp::acceptor m_Acceptor;
boost::asio::deadline_timer m_Timer;
}; };
typedef SOCKSServer SOCKSProxy; typedef SOCKSServer SOCKSProxy;

Loading…
Cancel
Save