diff --git a/AddressBook.cpp b/AddressBook.cpp
index 7dfa6964..f78666a0 100644
--- a/AddressBook.cpp
+++ b/AddressBook.cpp
@@ -240,7 +240,7 @@ namespace client
m_Storage = CreateStorage ();
m_Storage->AddAddress (ident);
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)
diff --git a/AddressBook.h b/AddressBook.h
index 47b819db..5b24c017 100644
--- a/AddressBook.h
+++ b/AddressBook.h
@@ -54,7 +54,9 @@ namespace client
void StopSubscriptions ();
void LoadHostsFromStream (std::istream& f);
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:
AddressBookStorage * CreateStorage ();
diff --git a/ClientContext.cpp b/ClientContext.cpp
index 46bace37..b6126fcb 100644
--- a/ClientContext.cpp
+++ b/ClientContext.cpp
@@ -141,7 +141,7 @@ namespace client
s.read ((char *)buf, len);
keys.FromBuffer (buf, len);
delete[] buf;
- LogPrint ("Local address ", keys.GetPublic ().GetIdentHash ().ToBase32 (), ".b32.i2p loaded");
+ LogPrint ("Local address ", m_AddressBook.ToAddress(keys.GetPublic ().GetIdentHash ()), " loaded");
}
else
{
@@ -154,7 +154,7 @@ namespace client
f.write ((char *)buf, len);
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);
@@ -197,7 +197,7 @@ namespace client
auto it = m_Destinations.find (keys.GetPublic ().GetIdentHash ());
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 ())
{
it->second->Start ();
diff --git a/Destination.cpp b/Destination.cpp
index 2a60f673..e299a5c6 100644
--- a/Destination.cpp
+++ b/Destination.cpp
@@ -48,7 +48,7 @@ namespace client
}
m_Pool = i2p::tunnel::tunnels.CreateTunnelPool (this, inboundTunnelLen, outboundTunnelLen);
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:
}
@@ -245,7 +245,7 @@ namespace client
delete it1->second;
m_LeaseSetRequests.erase (it1);
}
- }
+ }
void ClientDestination::HandleDatabaseSearchReplyMessage (const uint8_t * buf, size_t len)
{
diff --git a/HTTPServer.cpp b/HTTPServer.cpp
index f2679817..9da6f206 100644
--- a/HTTPServer.cpp
+++ b/HTTPServer.cpp
@@ -792,7 +792,7 @@ namespace util
std::string b32 = it.first.ToBase32 ();
s << "";
- s << b32 << ".b32.i2p
" << std::endl;
+ s << i2p::client::context.GetAddressBook ().ToAddress(it.second->GetIdentHash()) << "
" << std::endl;
}
}
@@ -832,7 +832,7 @@ namespace util
s << "
Streams:
";
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 << " [out:" << it.second->GetSendQueueSize () << "][in:" << it.second->GetReceiveQueueSize () << "]";
s << "
"<< std::endl;
diff --git a/SOCKS.cpp b/SOCKS.cpp
index a92ead5f..29ef3955 100644
--- a/SOCKS.cpp
+++ b/SOCKS.cpp
@@ -4,6 +4,7 @@
#include "Destination.h"
#include "ClientContext.h"
#include "I2PEndian.h"
+#include
#include
namespace i2p
@@ -22,215 +23,192 @@ namespace proxy
}
}
+ void SOCKSHandler::Done() {
+ if (m_parent) m_parent->RemoveHandler (shared_from_this ());
+ }
+
void SOCKSHandler::Terminate() {
- CloseStream();
- CloseSock();
- delete this; // HACK: ew
- }
-
- void SOCKSHandler::Socks5AuthNegoFailed()
- {
- LogPrint(eLogWarning,"--- SOCKS5 authentication negotiation failed");
- boost::asio::async_write(*m_sock, boost::asio::buffer("\x05\xff",2),
- std::bind(&SOCKSHandler::SentSocksFailed, this,
- std::placeholders::_1));
- }
-
- void SOCKSHandler::Socks5ChooseAuth()
- {
- LogPrint(eLogDebug,"--- SOCKS5 choosing authentication method");
- //TODO: Choose right method
- boost::asio::async_write(*m_sock, boost::asio::buffer("\x05\x00",2),
- std::bind(&SOCKSHandler::SentSocksResponse, this,
- std::placeholders::_1, nullptr));
- }
-
- static const char *socks5Replies[9] = {
- "\x05\x00\x00\x01\x00\x00\x00\x00\x00\x00",
- "\x05\x01\x00\x01\x00\x00\x00\x00\x00\x00",
- "\x05\x02\x00\x01\x00\x00\x00\x00\x00\x00",
- "\x05\x03\x00\x01\x00\x00\x00\x00\x00\x00",
- "\x05\x04\x00\x01\x00\x00\x00\x00\x00\x00",
- "\x05\x05\x00\x01\x00\x00\x00\x00\x00\x00",
- "\x05\x06\x00\x01\x00\x00\x00\x00\x00\x00",
- "\x05\x07\x00\x01\x00\x00\x00\x00\x00\x00",
- "\x05\x08\x00\x01\x00\x00\x00\x00\x00\x00" };
-
- /* All hope is lost */
- void SOCKSHandler::SocksRequestFailed()
- {
- switch (m_socksv) {
- case SOCKS4:
- LogPrint(eLogWarning,"--- SOCKS4 failed");
- //TODO: send the right response
- boost::asio::async_write(*m_sock, boost::asio::buffer("\x00\x5b\x00\x00\x00\x00\x00\x00",8),
- std::bind(&SOCKSHandler::SentSocksFailed, this, std::placeholders::_1));
- break;
- case SOCKS5:
- assert(m_error <= SOCKS5_ADDR_UNSUP);
- LogPrint(eLogWarning,"--- SOCKS5 failed");
- //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;
- }
- }
-
- void SOCKSHandler::SocksRequestSuccess()
- {
- std::shared_ptr> response(new std::vector);
- switch (m_socksv) {
- case SOCKS4:
- LogPrint(eLogInfo,"--- SOCKS4 connection success");
- //TODO: send the right response
- 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;
- case SOCKS5:
- LogPrint(eLogInfo,"--- SOCKS5 connection success");
- //TODO: send the right response using the port? and the localside i2p address
- boost::asio::async_write(*m_sock, boost::asio::buffer("\x05\x00\x00\x01\x00\x00\x00\x00\x00\x00",10),
- std::bind(&SOCKSHandler::SentSocksResponse, this,
- std::placeholders::_1, response));
- break;
- }
- }
-
-
- void SOCKSHandler::CloseSock()
- {
+ if (dead.exchange(true)) return;
if (m_sock) {
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::CloseStream()
- {
- if (m_stream) {
- LogPrint(eLogDebug,"--- SOCKS close stream");
- m_stream.reset ();
- }
- }
-
- std::size_t SOCKSHandler::HandleData(uint8_t *sock_buff, std::size_t len)
+ boost::asio::const_buffers_1 SOCKSHandler::GenerateSOCKS4Response(SOCKSHandler::errTypes error, uint32_t ip, uint16_t port)
{
- assert(len); // This should always be called with a least a byte left to parse
- switch (m_state) {
- case GET_VERSION:
- return HandleVersion(sock_buff);
- case SOCKS5_AUTHNEGO:
- return HandleSOCKS5AuthNego(sock_buff,len);
- case SOCKS_REQUEST:
- return HandleSOCKSRequest(sock_buff,len);
- default:
- LogPrint(eLogError,"--- SOCKS state?? ", m_state);
- Terminate();
- return 0;
+ assert(error >= SOCKS4_OK);
+ m_response[0] = '\x00'; //Version
+ m_response[1] = error; //Response code
+ htobe16buf(m_response+2,port); //Port
+ htobe32buf(m_response+4,ip); //IP
+ return boost::asio::const_buffers_1(m_response,8);
+ }
+
+ boost::asio::const_buffers_1 SOCKSHandler::GenerateSOCKS5Response(SOCKSHandler::errTypes error, SOCKSHandler::addrTypes type,
+ const SOCKSHandler::address &addr, uint16_t port)
+ {
+ size_t size;
+ assert(error <= SOCKS5_ADDR_UNSUP);
+ m_response[0] = '\x05'; //Version
+ m_response[1] = error; //Response code
+ 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);
+ }
+
+ bool SOCKSHandler::Socks5ChooseAuth()
+ {
+ m_response[0] = '\x05'; //Version
+ m_response[1] = m_authchosen; //Response code
+ boost::asio::const_buffers_1 response(m_response,2);
+ if (m_authchosen == AUTH_UNACCEPTABLE) {
+ LogPrint(eLogWarning,"--- SOCKS5 authentication negotiation failed");
+ boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksFailed, this, std::placeholders::_1));
+ return false;
+ } 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;
}
}
- std::size_t SOCKSHandler::HandleVersion(uint8_t *sock_buff)
+ /* All hope is lost beyond this point */
+ void SOCKSHandler::SocksRequestFailed(SOCKSHandler::errTypes error)
{
- switch (*sock_buff) {
+ boost::asio::const_buffers_1 response(nullptr,0);
+ assert(error != SOCKS4_OK && error != SOCKS5_OK);
+ switch (m_socksv) {
case SOCKS4:
- m_state = SOCKS_REQUEST; // Switch to the 4 handler
- m_pstate = GET_COMMAND; //Initialize the parser at the right position
+ LogPrint(eLogWarning,"--- SOCKS4 failed: ", error);
+ if (error < SOCKS4_OK) error = SOCKS4_FAIL; //Transparently map SOCKS5 errors
+ response = GenerateSOCKS4Response(error, m_4aip, m_port);
break;
case SOCKS5:
- m_state = SOCKS5_AUTHNEGO; // Switch to the 5 handler
- m_pstate = GET5_AUTHNUM; //Initialize the parser at the right position
+ LogPrint(eLogWarning,"--- SOCKS5 failed: ", error);
+ response = GenerateSOCKS5Response(error, m_addrtype, m_address, m_port);
break;
- default:
- LogPrint(eLogError,"--- SOCKS rejected invalid version: ", ((int)*sock_buff));
- Terminate();
- return 0;
}
- m_socksv = (SOCKSHandler::socksVersions) *sock_buff;
- return 1;
+ boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksFailed, this, std::placeholders::_1));
}
- std::size_t SOCKSHandler::HandleSOCKS5AuthNego(uint8_t *sock_buff, std::size_t len)
+ void SOCKSHandler::SocksRequestSuccess()
{
- std::size_t rv = 0;
- while (len > 0) {
- rv++;
- switch (m_pstate)
- {
- case GET5_AUTHNUM:
- m_authleft = *sock_buff;
- m_pstate = GET5_AUTH;
- break;
- case GET5_AUTH:
- m_authleft --;
- if (*sock_buff == AUTH_NONE)
- m_authchosen = AUTH_NONE;
- if ( m_authleft == 0 ) {
- if (m_authchosen == AUTH_UNACCEPTABLE) {
- //TODO: we maybe want support for other methods!
- 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;
- default:
- LogPrint(eLogError,"--- SOCKS5 parse state?? ", m_pstate);
- Terminate();
- return 0;
- }
- sock_buff++;
- len--;
+ 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) {
+ case SOCKS4:
+ LogPrint(eLogInfo,"--- SOCKS4 connection success");
+ response = GenerateSOCKS4Response(SOCKS4_OK, m_4aip, m_port);
+ break;
+ case SOCKS5:
+ LogPrint(eLogInfo,"--- SOCKS5 connection success");
+ auto s = i2p::client::context.GetAddressBook().ToAddress(m_parent->GetLocalDestination()->GetIdentHash());
+ address ad; ad.dns.FromString(s);
+ //HACK only 16 bits passed in port as SOCKS5 doesn't allow for more
+ response = GenerateSOCKS5Response(SOCKS5_OK, ADDR_DNS, ad, m_stream->GetRecvStreamID());
+ break;
}
- return rv;
+ boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksDone, this, std::placeholders::_1));
}
- bool SOCKSHandler::ValidateSOCKSRequest() {
+ void SOCKSHandler::EnterState(SOCKSHandler::state nstate, uint8_t parseleft) {
+ switch (nstate) {
+ case GET_PORT: parseleft = 2; break;
+ case GET_IPV4: m_addrtype = ADDR_IPV4; m_address.ip = 0; parseleft = 4; break;
+ case GET4_IDENT: m_4aip = m_address.ip; break;
+ case GET4A_HOST:
+ case GET5_HOST: m_addrtype = ADDR_DNS; m_address.dns.size = 0; break;
+ case GET5_IPV6: m_addrtype = ADDR_IPV6; parseleft = 16; break;
+ default:;
+ }
+ m_parseleft = parseleft;
+ m_state = nstate;
+ }
+
+ void 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;
+ 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);
- m_error = SOCKS5_ADDR_UNSUP;
break;
case SOCKS4:
LogPrint(eLogError,"--- SOCKS4a rejected because it's actually SOCKS4");
break;
}
- SocksRequestFailed();
- return false;
+ SocksRequestFailed(SOCKS5_ADDR_UNSUP);
+ return;
}
//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;
+ if(m_addrtype == ADDR_DNS && m_address.dns.ToString().find(".i2p") == std::string::npos) {
+ LogPrint(eLogError,"--- SOCKS invalid hostname: ", m_address.dns.ToString());
+ SocksRequestFailed(SOCKS5_ADDR_UNSUP);
+ return;
}
- return true;
}
- std::size_t SOCKSHandler::HandleSOCKSRequest(uint8_t *sock_buff, std::size_t len)
+
+ bool SOCKSHandler::HandleData(uint8_t *sock_buff, std::size_t len)
{
- std::size_t rv = 0;
+ assert(len); // This should always be called with a least a byte left to parse
while (len > 0) {
- rv++;
- switch (m_pstate)
- {
+ switch (m_state) {
+ case GET_SOCKSV:
+ m_socksv = (SOCKSHandler::socksVersions) *sock_buff;
+ switch (*sock_buff) {
+ case SOCKS4:
+ EnterState(GET_COMMAND); //Initialize the parser at the right position
+ break;
+ case SOCKS5:
+ EnterState(GET5_AUTHNUM); //Initialize the parser at the right position
+ break;
+ default:
+ LogPrint(eLogError,"--- SOCKS rejected invalid version: ", ((int)*sock_buff));
+ Terminate();
+ return false;
+ }
+ break;
+ case GET5_AUTHNUM:
+ EnterState(GET5_AUTH, *sock_buff);
+ break;
+ case GET5_AUTH:
+ m_parseleft --;
+ if (*sock_buff == AUTH_NONE)
+ m_authchosen = AUTH_NONE;
+ if ( m_parseleft == 0 ) {
+ if (!Socks5ChooseAuth()) return false;
+ EnterState(GET5_REQUESTV);
+ }
+ break;
case GET_COMMAND:
switch (*sock_buff) {
case CMD_CONNECT:
@@ -240,124 +218,110 @@ namespace proxy
if (m_socksv == SOCKS5) break;
default:
LogPrint(eLogError,"--- SOCKS invalid command: ", ((int)*sock_buff));
- m_error = SOCKS5_GEN_FAIL;
- SocksRequestFailed();
- return 0;
+ SocksRequestFailed(SOCKS5_GEN_FAIL);
+ return false;
}
m_cmd = (SOCKSHandler::cmdTypes)*sock_buff;
switch (m_socksv) {
- case SOCKS5: m_pstate = GET5_GETRSV; break;
- case SOCKS4: m_pstate = GET_PORT; m_addrleft = 2; break;
+ case SOCKS5: EnterState(GET5_GETRSV); break;
+ case SOCKS4: EnterState(GET_PORT); break;
}
break;
case GET_PORT:
m_port = (m_port << 8)|((uint16_t)*sock_buff);
- m_addrleft--;
- if (m_addrleft == 0) {
+ m_parseleft--;
+ if (m_parseleft == 0) {
switch (m_socksv) {
- case SOCKS5: m_pstate = DONE; break;
- case SOCKS4: m_pstate = GET_IPV4; m_addrleft = 4; break;
+ case SOCKS5: EnterState(DONE); break;
+ case SOCKS4: EnterState(GET_IPV4); break;
}
}
break;
case GET_IPV4:
- m_ip = (m_ip << 8)|((uint32_t)*sock_buff);
- m_addrleft--;
- if (m_addrleft == 0) {
+ m_address.ip = (m_address.ip << 8)|((uint32_t)*sock_buff);
+ m_parseleft--;
+ if (m_parseleft == 0) {
switch (m_socksv) {
- case SOCKS5: m_pstate = GET_PORT; m_addrleft = 2; break;
- case SOCKS4: m_pstate = GET4_IDENT; break;
+ case SOCKS5: EnterState(GET_PORT); break;
+ case SOCKS4: EnterState(GET4_IDENT); m_4aip = m_address.ip; break;
}
}
break;
case GET4_IDENT:
if (!*sock_buff) {
- if( m_ip == 0 || m_ip > 255 ) {
- m_addrtype = ADDR_IPV4;
- m_pstate = DONE;
+ if( m_4aip == 0 || m_4aip > 255 ) {
+ EnterState(DONE);
} else {
- m_addrtype = ADDR_DNS;
- m_pstate = GET4A_HOST;
+ EnterState(GET4A_HOST);
}
}
break;
case GET4A_HOST:
if (!*sock_buff) {
- m_pstate = DONE;
+ EnterState(DONE);
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");
- SocksRequestFailed();
- return 0;
+ SocksRequestFailed(SOCKS4_FAIL);
+ return false;
}
- m_destination.push_back(*sock_buff);
+ m_address.dns.push_back(*sock_buff);
break;
case GET5_REQUESTV:
if (*sock_buff != SOCKS5) {
LogPrint(eLogError,"--- SOCKS5 rejected unknown request version: ", ((int)*sock_buff));
- m_error = SOCKS5_GEN_FAIL;
- SocksRequestFailed();
- return 0;
+ SocksRequestFailed(SOCKS5_GEN_FAIL);
+ return false;
}
- m_pstate = GET_COMMAND;
+ EnterState(GET_COMMAND);
break;
case GET5_GETRSV:
if ( *sock_buff != 0 ) {
LogPrint(eLogError,"--- SOCKS5 unknown reserved field: ", ((int)*sock_buff));
- m_error = SOCKS5_GEN_FAIL;
- SocksRequestFailed();
- return 0;
+ SocksRequestFailed(SOCKS5_GEN_FAIL);
+ return false;
}
- m_pstate = GET5_GETADDRTYPE;
+ EnterState(GET5_GETADDRTYPE);
break;
case GET5_GETADDRTYPE:
switch (*sock_buff) {
- case ADDR_IPV4: m_pstate = GET_IPV4; m_addrleft = 4; break;
- case ADDR_IPV6: m_pstate = GET5_IPV6; m_addrleft = 16; break;
- case ADDR_DNS : m_pstate = GET5_HOST_SIZE; break;
+ case ADDR_IPV4: EnterState(GET_IPV4); break;
+ case ADDR_IPV6: EnterState(GET5_IPV6); break;
+ case ADDR_DNS : EnterState(GET5_HOST_SIZE); break;
default:
LogPrint(eLogError,"--- SOCKS5 unknown address type: ", ((int)*sock_buff));
- m_error = SOCKS5_GEN_FAIL;
- SocksRequestFailed();
- return 0;
+ SocksRequestFailed(SOCKS5_GEN_FAIL);
+ return false;
}
- m_addrtype = (SOCKSHandler::addrTypes)*sock_buff;
break;
case GET5_IPV6:
- m_ipv6[16-m_addrleft] = *sock_buff;
- m_addrleft--;
- if (m_addrleft == 0) {
- m_pstate = GET_PORT;
- m_addrleft = 2;
- }
+ m_address.ipv6[16-m_parseleft] = *sock_buff;
+ m_parseleft--;
+ if (m_parseleft == 0) EnterState(GET_PORT);
break;
case GET5_HOST_SIZE:
- m_addrleft = *sock_buff;
- m_pstate = GET5_HOST;
+ EnterState(GET5_HOST, *sock_buff);
break;
case GET5_HOST:
- m_destination.push_back(*sock_buff);
- m_addrleft--;
- if (m_addrleft == 0) {
- m_pstate = GET_PORT;
- m_addrleft = 2;
- }
+ m_address.dns.push_back(*sock_buff);
+ m_parseleft--;
+ if (m_parseleft == 0) EnterState(GET_PORT);
break;
default:
- LogPrint(eLogError,"--- SOCKS parse state?? ", m_pstate);
+ LogPrint(eLogError,"--- SOCKS parse state?? ", m_state);
Terminate();
- return 0;
- }
- if (m_pstate == DONE) {
- m_state = READY;
- m_need_more = false;
- return (ValidateSOCKSRequest() ? rv : 0);
+ return false;
}
sock_buff++;
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)
@@ -369,32 +333,17 @@ namespace proxy
return;
}
- std::size_t pos = 0;
- m_need_more = true;
- while (pos != len && m_state != READY && m_need_more) {
- 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 ;
+ if (HandleData(m_sock_buff, len)) {
+ if (m_state == DONE) {
+ LogPrint(eLogInfo,"--- SOCKS requested ", m_address.dns.ToString(), ":" , m_port);
+ m_parent->GetLocalDestination ()->CreateStream (
+ std::bind (&SOCKSHandler::HandleStreamRequestComplete,
+ this, std::placeholders::_1), m_address.dns.ToString(), m_port);
+ } else {
+ AsyncSockRead();
}
-
- m_parent->GetLocalDestination ()->CreateStream (
- std::bind (&SOCKSHandler::HandleStreamRequestComplete,
- this, std::placeholders::_1), m_destination, m_port);
- } else if (m_need_more) {
- LogPrint (eLogDebug,"--- SOCKS Need more data");
- AsyncSockRead();
}
+
}
void SOCKSHandler::SentSocksFailed(const boost::system::error_code & ecode)
@@ -406,41 +355,43 @@ namespace proxy
Terminate();
}
}
-
- void SOCKSHandler::SentSocksResponse(const boost::system::error_code & ecode, std::shared_ptr> response)
+
+ void SOCKSHandler::SentSocksDone(const boost::system::error_code & ecode)
{
- response.reset(); // Information wants to be free, so does memory
if (!ecode) {
- if(m_state == READY) {
- LogPrint (eLogInfo,"--- SOCKS New I2PTunnel connection");
- auto connection = std::make_shared((i2p::client::I2PTunnel *)m_parent, m_sock, m_stream);
- m_parent->AddConnection (connection);
- connection->I2PConnect ();
- } else {
- LogPrint (eLogDebug,"--- SOCKS Go to next state");
- AsyncSockRead();
- }
+ if (dead.exchange(true)) return;
+ LogPrint (eLogInfo,"--- SOCKS New I2PTunnel connection");
+ auto connection = std::make_shared((i2p::client::I2PTunnel *)m_parent, m_sock, m_stream);
+ m_parent->AddConnection (connection);
+ connection->I2PConnect ();
+ Done();
}
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 ());
Terminate();
}
}
-
+
void SOCKSHandler::HandleStreamRequestComplete (std::shared_ptr stream)
{
if (stream) {
m_stream = stream;
SocksRequestSuccess();
} else {
- m_error = SOCKS5_HOST_UNREACH;
LogPrint (eLogError,"--- SOCKS Issue when creating the stream, check the previous warnings for more info.");
- SocksRequestFailed();
+ SocksRequestFailed(SOCKS5_HOST_UNREACH);
}
}
-
void SOCKSServer::Start ()
{
m_Acceptor.listen ();
@@ -452,6 +403,7 @@ namespace proxy
m_Acceptor.close();
m_Timer.cancel ();
ClearConnections ();
+ ClearHandlers();
}
void SOCKSServer::Accept ()
@@ -459,14 +411,31 @@ namespace proxy
auto newSocket = new boost::asio::ip::tcp::socket (GetService ());
m_Acceptor.async_accept (*newSocket, std::bind (&SOCKSServer::HandleAccept, this,
std::placeholders::_1, newSocket));
- }
+ }
+
+ void SOCKSServer::AddHandler (std::shared_ptr handler) {
+ std::unique_lock l(m_HandlersMutex);
+ m_Handlers.insert (handler);
+ }
+
+ void SOCKSServer::RemoveHandler (std::shared_ptr handler)
+ {
+ std::unique_lock l(m_HandlersMutex);
+ m_Handlers.erase (handler);
+ }
+
+ void SOCKSServer::ClearHandlers ()
+ {
+ std::unique_lock l(m_HandlersMutex);
+ m_Handlers.clear ();
+ }
void SOCKSServer::HandleAccept (const boost::system::error_code& ecode, boost::asio::ip::tcp::socket * socket)
{
if (!ecode)
{
LogPrint(eLogDebug,"--- SOCKS accepted");
- new SOCKSHandler(this, socket);
+ AddHandler(std::make_shared (this, socket));
Accept();
}
else
diff --git a/SOCKS.h b/SOCKS.h
index 873fa2c6..3fd0f52f 100644
--- a/SOCKS.h
+++ b/SOCKS.h
@@ -2,8 +2,11 @@
#define SOCKS_H__
#include
-#include
+#include
+#include
#include
+#include
+#include
#include "Identity.h"
#include "Streaming.h"
#include "I2PTunnel.h"
@@ -16,18 +19,23 @@ namespace proxy
const size_t socks_buffer_size = 8192;
const size_t max_socks_hostname_size = 255; // Limit for socks5 and bad idea to traverse
- class SOCKSServer;
- class SOCKSHandler {
+ struct SOCKSDnsAddress {
+ 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 {
private:
enum state {
- GET_VERSION, // Get SOCKS version
- SOCKS5_AUTHNEGO, //Authentication negotiation
- SOCKS5_AUTH, //Authentication
- SOCKS_REQUEST, //Request
- READY // Ready to connect
- };
- enum parseState {
+ GET_SOCKSV,
GET_COMMAND,
GET_PORT,
GET_IPV4,
@@ -78,59 +86,68 @@ namespace proxy
SOCKS4 = 4, // SOCKS4
SOCKS5 = 5 // SOCKS5
};
+ union address {
+ uint32_t ip;
+ SOCKSDnsAddress dns;
+ uint8_t ipv6[16];
+ };
-
- void GotClientRequest(boost::system::error_code & ecode, std::string & host, uint16_t port);
- std::size_t HandleData(uint8_t *sock_buff, std::size_t len);
- 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 EnterState(state nstate, uint8_t parseleft = 1);
+ bool HandleData(uint8_t *sock_buff, std::size_t len);
+ void ValidateSOCKSRequest();
void HandleSockRecv(const boost::system::error_code & ecode, std::size_t bytes_transfered);
+ void Done();
void Terminate();
- void CloseSock();
- void CloseStream();
void AsyncSockRead();
- void Socks5AuthNegoFailed();
- void Socks5ChooseAuth();
- void SocksRequestFailed();
+ boost::asio::const_buffers_1 GenerateSOCKS5SelectAuth(authMethods method);
+ boost::asio::const_buffers_1 GenerateSOCKS4Response(errTypes error, uint32_t ip, uint16_t port);
+ 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 SentSocksFailed(const boost::system::error_code & ecode);
- //HACK: we need to pass the shared_ptr to ensure the buffer will live enough
- void SentSocksResponse(const boost::system::error_code & ecode, std::shared_ptr> response);
+ void SentSocksDone(const boost::system::error_code & ecode);
+ void SentSocksResponse(const boost::system::error_code & ecode);
void HandleStreamRequestComplete (std::shared_ptr stream);
uint8_t m_sock_buff[socks_buffer_size];
SOCKSServer * m_parent;
boost::asio::ip::tcp::socket * m_sock;
std::shared_ptr m_stream;
- state m_state;
- parseState m_pstate;
- uint8_t m_command;
+ uint8_t m_response[7+max_socks_hostname_size];
+ address m_address; //Address
+ uint32_t m_4aip; //Used in 4a requests
uint16_t m_port;
- uint32_t m_ip;
- uint8_t m_ipv6[16];
- std::string m_destination;
- uint8_t m_authleft; //Authentication methods left
- //TODO: this will probably be more elegant as enums
+ uint8_t m_command;
+ uint8_t m_parseleft; //Octets left to parse
authMethods m_authchosen; //Authentication 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
cmdTypes m_cmd; // Command requested
- bool m_need_more; //The parser still needs to receive more data
+ state m_state;
+ std::atomic dead; //To avoid cleaning up multiple times
public:
SOCKSHandler(SOCKSServer * parent, boost::asio::ip::tcp::socket * sock) :
- m_parent(parent), m_sock(sock), m_stream(nullptr), m_state(GET_VERSION),
- m_authchosen(AUTH_UNACCEPTABLE), m_addrtype(ADDR_IPV4), m_error(SOCKS5_GEN_FAIL)
- { AsyncSockRead(); m_destination.reserve(max_socks_hostname_size+1); }
- ~SOCKSHandler() { CloseSock(); CloseStream(); }
+ m_parent(parent), m_sock(sock), m_stream(nullptr),
+ m_authchosen(AUTH_UNACCEPTABLE), m_addrtype(ADDR_IPV4), dead(false)
+ { m_address.ip = 0; AsyncSockRead(); EnterState(GET_SOCKSV); }
+ ~SOCKSHandler() { Terminate(); }
};
class SOCKSServer: public i2p::client::I2PTunnel
{
+ private:
+ std::set > 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:
SOCKSServer(int port) : I2PTunnel(nullptr),
m_Acceptor (GetService (), boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4(), port)),
@@ -139,17 +156,10 @@ namespace proxy
void Start ();
void Stop ();
-
- private:
-
- 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;
- };
+ void AddHandler (std::shared_ptr handler);
+ void RemoveHandler (std::shared_ptr handler);
+ void ClearHandlers ();
+ };
typedef SOCKSServer SOCKSProxy;
}