* add websocks

* enable socks, websocks and httpproxy as client tunnels

* remove old websocks config
This commit is contained in:
Jeff Becker 2017-01-07 08:32:50 -05:00
parent 43c1a87c48
commit c5d3c0c6f8
8 changed files with 408 additions and 341 deletions

View file

@ -8,6 +8,8 @@
#include "Identity.h" #include "Identity.h"
#include "util.h" #include "util.h"
#include "ClientContext.h" #include "ClientContext.h"
#include "SOCKS.h"
#include "WebSocks.h"
namespace i2p namespace i2p
{ {
@ -424,7 +426,11 @@ namespace client
try try
{ {
std::string type = section.second.get<std::string> (I2P_TUNNELS_SECTION_TYPE); std::string type = section.second.get<std::string> (I2P_TUNNELS_SECTION_TYPE);
if (type == I2P_TUNNELS_SECTION_TYPE_CLIENT || type == I2P_TUNNELS_SECTION_TYPE_UDPCLIENT) if (type == I2P_TUNNELS_SECTION_TYPE_CLIENT
|| type == I2P_TUNNELS_SECTION_TYPE_SOCKS
|| type == I2P_TUNNELS_SECTION_TYPE_WEBSOCKS
|| type == I2P_TUNNELS_SECTION_TYPE_HTTPPROXY
|| type == I2P_TUNNELS_SECTION_TYPE_UDPCLIENT)
{ {
// mandatory params // mandatory params
std::string dest = section.second.get<std::string> (I2P_CLIENT_TUNNEL_DESTINATION); std::string dest = section.second.get<std::string> (I2P_CLIENT_TUNNEL_DESTINATION);
@ -466,19 +472,45 @@ namespace client
LogPrint(eLogError, "Clients: I2P Client forward for endpoint ", end, " already exists"); LogPrint(eLogError, "Clients: I2P Client forward for endpoint ", end, " already exists");
} else { } else {
// tcp client boost::asio::ip::tcp::endpoint clientEndpoint;
auto clientTunnel = new I2PClientTunnel (name, dest, address, port, localDestination, destinationPort); I2PService * clientTunnel = nullptr;
if (m_ClientTunnels.insert (std::make_pair (clientTunnel->GetAcceptor ().local_endpoint (), if (type == I2P_TUNNELS_SECTION_TYPE_SOCKS)
std::unique_ptr<I2PClientTunnel>(clientTunnel))).second) {
// socks proxy
clientTunnel = new i2p::proxy::SOCKSProxy(address, port, "", destinationPort, localDestination);
clientEndpoint = ((i2p::proxy::SOCKSProxy*)clientTunnel)->GetAcceptor().local_endpoint();
}
else if (type == I2P_TUNNELS_SECTION_TYPE_HTTPPROXY)
{
// http proxy
clientTunnel = new i2p::proxy::HTTPProxy(address, port, localDestination);
clientEndpoint = ((i2p::proxy::HTTPProxy*)clientTunnel)->GetAcceptor().local_endpoint();
}
else if (type == I2P_TUNNELS_SECTION_TYPE_WEBSOCKS)
{
// websocks proxy
clientTunnel = new WebSocks(address, port, localDestination);;
clientEndpoint = ((WebSocks*)clientTunnel)->GetLocalEndpoint();
}
else
{
// tcp client
clientTunnel = new I2PClientTunnel (name, dest, address, port, localDestination, destinationPort);
clientEndpoint = ((I2PClientTunnel*)clientTunnel)->GetAcceptor().local_endpoint();
}
if (m_ClientTunnels.insert (std::make_pair (clientEndpoint, std::unique_ptr<I2PService>(clientTunnel))).second)
{ {
clientTunnel->Start (); clientTunnel->Start ();
numClientTunnels++; numClientTunnels++;
} }
else else
LogPrint (eLogError, "Clients: I2P client tunnel for endpoint ", clientTunnel->GetAcceptor ().local_endpoint (), " already exists"); LogPrint (eLogError, "Clients: I2P client tunnel for endpoint ", clientEndpoint, "already exists");
} }
} }
else if (type == I2P_TUNNELS_SECTION_TYPE_SERVER || type == I2P_TUNNELS_SECTION_TYPE_HTTP || type == I2P_TUNNELS_SECTION_TYPE_IRC || type == I2P_TUNNELS_SECTION_TYPE_UDPSERVER) else if (type == I2P_TUNNELS_SECTION_TYPE_SERVER
|| type == I2P_TUNNELS_SECTION_TYPE_HTTP
|| type == I2P_TUNNELS_SECTION_TYPE_IRC
|| type == I2P_TUNNELS_SECTION_TYPE_UDPSERVER)
{ {
// mandatory params // mandatory params
std::string host = section.second.get<std::string> (I2P_SERVER_TUNNEL_HOST); std::string host = section.second.get<std::string> (I2P_SERVER_TUNNEL_HOST);

View file

@ -26,6 +26,9 @@ namespace client
const char I2P_TUNNELS_SECTION_TYPE_IRC[] = "irc"; const char I2P_TUNNELS_SECTION_TYPE_IRC[] = "irc";
const char I2P_TUNNELS_SECTION_TYPE_UDPCLIENT[] = "udpclient"; const char I2P_TUNNELS_SECTION_TYPE_UDPCLIENT[] = "udpclient";
const char I2P_TUNNELS_SECTION_TYPE_UDPSERVER[] = "udpserver"; const char I2P_TUNNELS_SECTION_TYPE_UDPSERVER[] = "udpserver";
const char I2P_TUNNELS_SECTION_TYPE_SOCKS[] = "socks";
const char I2P_TUNNELS_SECTION_TYPE_WEBSOCKS[] = "websocks";
const char I2P_TUNNELS_SECTION_TYPE_HTTPPROXY[] = "httpproxy";
const char I2P_CLIENT_TUNNEL_PORT[] = "port"; const char I2P_CLIENT_TUNNEL_PORT[] = "port";
const char I2P_CLIENT_TUNNEL_ADDRESS[] = "address"; const char I2P_CLIENT_TUNNEL_ADDRESS[] = "address";
const char I2P_CLIENT_TUNNEL_DESTINATION[] = "destination"; const char I2P_CLIENT_TUNNEL_DESTINATION[] = "destination";
@ -93,7 +96,7 @@ namespace client
i2p::proxy::HTTPProxy * m_HttpProxy; i2p::proxy::HTTPProxy * m_HttpProxy;
i2p::proxy::SOCKSProxy * m_SocksProxy; i2p::proxy::SOCKSProxy * m_SocksProxy;
std::map<boost::asio::ip::tcp::endpoint, std::unique_ptr<I2PClientTunnel> > m_ClientTunnels; // local endpoint->tunnel std::map<boost::asio::ip::tcp::endpoint, std::unique_ptr<I2PService> > m_ClientTunnels; // local endpoint->tunnel
std::map<std::pair<i2p::data::IdentHash, int>, std::unique_ptr<I2PServerTunnel> > m_ServerTunnels; // <destination,port>->tunnel std::map<std::pair<i2p::data::IdentHash, int>, std::unique_ptr<I2PServerTunnel> > m_ServerTunnels; // <destination,port>->tunnel
std::mutex m_ForwardsMutex; std::mutex m_ForwardsMutex;

View file

@ -203,12 +203,6 @@ namespace config {
("websockets.address", value<std::string>()->default_value("127.0.0.1"), "address to bind websocket server on") ("websockets.address", value<std::string>()->default_value("127.0.0.1"), "address to bind websocket server on")
("websockets.port", value<uint16_t>()->default_value(7666), "port to bind websocket server on"); ("websockets.port", value<uint16_t>()->default_value(7666), "port to bind websocket server on");
options_description websocks("WebSOCKS options");
websocks.add_options()
("websocks.enabled", value<bool>()->default_value(false), "enable WebSOCKS server")
("websocks.address", value<std::string>()->default_value("127.0.0.1"), "address to bind WebSOCKS server on")
("websocks.port", value<uint16_t>()->default_value(7666), "port to bind WebSOCKS server on");
m_OptionsDesc m_OptionsDesc
.add(general) .add(general)
.add(limits) .add(limits)
@ -225,7 +219,6 @@ namespace config {
.add(addressbook) .add(addressbook)
.add(trust) .add(trust)
.add(websocket) .add(websocket)
.add(websocks)
; ;
} }

View file

@ -27,7 +27,6 @@
#include "Event.h" #include "Event.h"
#include "Websocket.h" #include "Websocket.h"
#include "WebSocks.h"
namespace i2p namespace i2p
{ {
@ -44,7 +43,6 @@ namespace i2p
std::unique_ptr<i2p::transport::UPnP> UPnP; std::unique_ptr<i2p::transport::UPnP> UPnP;
#ifdef WITH_EVENTS #ifdef WITH_EVENTS
std::unique_ptr<i2p::event::WebsocketServer> m_WebsocketServer; std::unique_ptr<i2p::event::WebsocketServer> m_WebsocketServer;
std::unique_ptr<i2p::client::WebSocks> m_WebSocksServer;
#endif #endif
}; };
@ -309,15 +307,6 @@ namespace i2p
d.m_WebsocketServer->Start(); d.m_WebsocketServer->Start();
i2p::event::core.SetListener(d.m_WebsocketServer->ToListener()); i2p::event::core.SetListener(d.m_WebsocketServer->ToListener());
} }
bool websocks; i2p::config::GetOption("websocks.enabled", websocks);
if (websocks) {
std::string websocksAddr; i2p::config::GetOption("websocks.address", websocksAddr);
uint16_t websocksPort; i2p::config::GetOption("websocks.port", websocksPort);
LogPrint(eLogInfo, "Daemon: starting up WebSOCKS server at ", websocksAddr, ":", websocksPort);
d.m_WebSocksServer = std::unique_ptr<i2p::client::WebSocks>(new i2p::client::WebSocks(websocksAddr, websocksPort));
d.m_WebSocksServer->Start();
}
#endif #endif
return true; return true;
} }

View file

@ -121,10 +121,11 @@ namespace client
void Stop (); void Stop ();
const boost::asio::ip::tcp::acceptor& GetAcceptor () const { return m_Acceptor; }; const boost::asio::ip::tcp::acceptor& GetAcceptor () const { return m_Acceptor; };
virtual const char* GetName() { return "Generic TCP/IP accepting daemon"; }
protected: protected:
virtual std::shared_ptr<I2PServiceHandler> CreateHandler(std::shared_ptr<boost::asio::ip::tcp::socket> socket) = 0; virtual std::shared_ptr<I2PServiceHandler> CreateHandler(std::shared_ptr<boost::asio::ip::tcp::socket> socket) = 0;
virtual const char* GetName() { return "Generic TCP/IP accepting daemon"; }
private: private:
void Accept(); void Accept();
void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr<boost::asio::ip::tcp::socket> socket); void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr<boost::asio::ip::tcp::socket> socket);

View file

@ -22,254 +22,273 @@ namespace i2p
{ {
namespace client namespace client
{ {
typedef websocketpp::server<websocketpp::config::asio> WebSocksServerImpl; typedef websocketpp::server<websocketpp::config::asio> WebSocksServerImpl;
typedef std::function<void(std::shared_ptr<i2p::stream::Stream>)> StreamConnectFunc; typedef std::function<void(std::shared_ptr<i2p::stream::Stream>)> StreamConnectFunc;
struct IWebSocksConn struct IWebSocksConn : public I2PServiceHandler
{ {
virtual void Close() = 0; IWebSocksConn(I2PService * parent) : I2PServiceHandler(parent) {}
virtual void GotMessage(const websocketpp::connection_hdl & conn, WebSocksServerImpl::message_ptr msg) = 0; virtual void Close() = 0;
}; virtual void GotMessage(const websocketpp::connection_hdl & conn, WebSocksServerImpl::message_ptr msg) = 0;
};
typedef std::shared_ptr<IWebSocksConn> WebSocksConn_ptr; typedef std::shared_ptr<IWebSocksConn> WebSocksConn_ptr;
WebSocksConn_ptr CreateWebSocksConn(const websocketpp::connection_hdl & conn, WebSocksImpl * parent); WebSocksConn_ptr CreateWebSocksConn(const websocketpp::connection_hdl & conn, WebSocksImpl * parent);
class WebSocksImpl class WebSocksImpl
{ {
typedef std::mutex mutex_t; typedef std::mutex mutex_t;
typedef std::unique_lock<mutex_t> lock_t; typedef std::unique_lock<mutex_t> lock_t;
typedef std::shared_ptr<ClientDestination> Destination_t; typedef std::shared_ptr<ClientDestination> Destination_t;
public: public:
typedef WebSocksServerImpl ServerImpl; typedef WebSocksServerImpl ServerImpl;
typedef ServerImpl::message_ptr MessagePtr; typedef ServerImpl::message_ptr MessagePtr;
WebSocksImpl(const std::string & addr, int port) : WebSocksImpl(const std::string & addr, int port) :
m_Run(false), Parent(nullptr),
m_Addr(addr), m_Run(false),
m_Port(port), m_Addr(addr),
m_Thread(nullptr) m_Port(port),
m_Thread(nullptr)
{ {
m_Server.init_asio(); m_Server.init_asio();
m_Server.set_open_handler(std::bind(&WebSocksImpl::ConnOpened, this, std::placeholders::_1)); m_Server.set_open_handler(std::bind(&WebSocksImpl::ConnOpened, this, std::placeholders::_1));
i2p::data::PrivateKeys k = i2p::data::PrivateKeys::CreateRandomKeys(i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519);
m_Dest = std::make_shared<ClientDestination>(k, false);
} }
ServerImpl::connection_ptr GetConn(const websocketpp::connection_hdl & conn) void InitializeDestination(WebSocks * parent)
{ {
return m_Server.get_con_from_hdl(conn); Parent = parent;
} m_Dest = Parent->GetLocalDestination();
}
void CloseConn(const websocketpp::connection_hdl & conn) ServerImpl::connection_ptr GetConn(const websocketpp::connection_hdl & conn)
{ {
auto c = GetConn(conn); return m_Server.get_con_from_hdl(conn);
if(c) c->close(websocketpp::close::status::normal, "closed"); }
}
void CreateStreamTo(const std::string & addr, int port, StreamConnectFunc complete) void CloseConn(const websocketpp::connection_hdl & conn)
{ {
auto & addressbook = i2p::client::context.GetAddressBook(); auto c = GetConn(conn);
i2p::data::IdentHash ident; if(c) c->close(websocketpp::close::status::normal, "closed");
if(addressbook.GetIdentHash(addr, ident)) { }
// address found
m_Dest->CreateStream(complete, ident, port);
} else {
// not found
complete(nullptr);
}
}
void ConnOpened(websocketpp::connection_hdl conn) void CreateStreamTo(const std::string & addr, int port, StreamConnectFunc complete)
{ {
m_Conns.push_back(CreateWebSocksConn(conn, this)); auto & addressbook = i2p::client::context.GetAddressBook();
} i2p::data::IdentHash ident;
if(addressbook.GetIdentHash(addr, ident)) {
// address found
m_Dest->CreateStream(complete, ident, port);
} else {
// not found
complete(nullptr);
}
}
void Start() void ConnOpened(websocketpp::connection_hdl conn)
{ {
if(m_Run) return; // already started auto ptr = CreateWebSocksConn(conn, this);
m_Server.listen(boost::asio::ip::address::from_string(m_Addr), m_Port); Parent->AddHandler(ptr);
m_Server.start_accept(); m_Conns.push_back(ptr);
m_Run = true; }
m_Thread = new std::thread([&] (){
while(m_Run) {
try {
m_Server.run();
} catch( std::exception & ex) {
LogPrint(eLogError, "Websocks runtime exception: ", ex.what());
}
}
});
m_Dest->Start();
}
void Stop() void Start()
{ {
for(const auto & conn : m_Conns) if(m_Run) return; // already started
conn->Close(); m_Server.listen(boost::asio::ip::address::from_string(m_Addr), m_Port);
m_Server.start_accept();
m_Run = true;
m_Thread = new std::thread([&] (){
while(m_Run) {
try {
m_Server.run();
} catch( std::exception & ex) {
LogPrint(eLogError, "Websocks runtime exception: ", ex.what());
}
}
});
m_Dest->Start();
}
m_Dest->Stop(); void Stop()
m_Run = false; {
m_Server.stop(); for(const auto & conn : m_Conns)
if(m_Thread) { conn->Close();
m_Thread->join();
delete m_Thread;
}
m_Thread = nullptr;
}
private: m_Dest->Stop();
std::vector<WebSocksConn_ptr> m_Conns; m_Run = false;
bool m_Run; m_Server.stop();
ServerImpl m_Server; if(m_Thread) {
std::string m_Addr; m_Thread->join();
int m_Port; delete m_Thread;
std::thread * m_Thread; }
Destination_t m_Dest; m_Thread = nullptr;
}
boost::asio::ip::tcp::endpoint GetLocalEndpoint()
{
return boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(m_Addr), m_Port);
}
WebSocks * Parent;
private:
std::vector<WebSocksConn_ptr> m_Conns;
bool m_Run;
ServerImpl m_Server;
std::string m_Addr;
int m_Port;
std::thread * m_Thread;
Destination_t m_Dest;
}; };
struct WebSocksConn : public IWebSocksConn struct WebSocksConn : public IWebSocksConn , public std::enable_shared_from_this<WebSocksConn>
{ {
enum ConnState enum ConnState
{ {
eWSCInitial, eWSCInitial,
eWSCTryConnect, eWSCTryConnect,
eWSCFailConnect, eWSCFailConnect,
eWSCOkayConnect, eWSCOkayConnect,
eWSCClose, eWSCClose,
eWSCEnd eWSCEnd
}; };
typedef WebSocksServerImpl ServerImpl; typedef WebSocksServerImpl ServerImpl;
typedef ServerImpl::message_ptr Message_t; typedef ServerImpl::message_ptr Message_t;
typedef websocketpp::connection_hdl ServerConn; typedef websocketpp::connection_hdl ServerConn;
typedef std::shared_ptr<ClientDestination> Destination_t; typedef std::shared_ptr<ClientDestination> Destination_t;
typedef std::shared_ptr<i2p::stream::StreamingDestination> StreamDest_t; typedef std::shared_ptr<i2p::stream::StreamingDestination> StreamDest_t;
typedef std::shared_ptr<i2p::stream::Stream> Stream_t; typedef std::shared_ptr<i2p::stream::Stream> Stream_t;
ServerConn m_Conn; ServerConn m_Conn;
Stream_t m_Stream; Stream_t m_Stream;
ConnState m_State; ConnState m_State;
WebSocksImpl * m_Parent; WebSocksImpl * m_Parent;
std::string m_RemoteAddr; std::string m_RemoteAddr;
int m_RemotePort; int m_RemotePort;
uint8_t m_RecvBuf[2048]; uint8_t m_RecvBuf[2048];
WebSocksConn(const ServerConn & conn, WebSocksImpl * parent) : WebSocksConn(const ServerConn & conn, WebSocksImpl * parent) :
m_Conn(conn), IWebSocksConn(parent->Parent),
m_Stream(nullptr), m_Conn(conn),
m_State(eWSCInitial), m_Stream(nullptr),
m_Parent(parent) m_State(eWSCInitial),
{ m_Parent(parent)
{
} }
~WebSocksConn() ~WebSocksConn()
{ {
Close(); Close();
} }
void EnterState(ConnState state) void EnterState(ConnState state)
{ {
LogPrint(eLogDebug, "websocks: state ", m_State, " -> ", state); LogPrint(eLogDebug, "websocks: state ", m_State, " -> ", state);
switch(m_State) switch(m_State)
{ {
case eWSCInitial: case eWSCInitial:
if (state == eWSCClose) { if (state == eWSCClose) {
m_State = eWSCClose; m_State = eWSCClose;
// connection was opened but never used // connection was opened but never used
LogPrint(eLogInfo, "websocks: connection closed but never used"); LogPrint(eLogInfo, "websocks: connection closed but never used");
Close(); Close();
return; return;
} else if (state == eWSCTryConnect) { } else if (state == eWSCTryConnect) {
// we will try to connect // we will try to connect
m_State = eWSCTryConnect; m_State = eWSCTryConnect;
m_Parent->CreateStreamTo(m_RemoteAddr, m_RemotePort, std::bind(&WebSocksConn::ConnectResult, this, std::placeholders::_1)); m_Parent->CreateStreamTo(m_RemoteAddr, m_RemotePort, std::bind(&WebSocksConn::ConnectResult, this, std::placeholders::_1));
} else { } else {
LogPrint(eLogWarning, "websocks: invalid state change ", m_State, " -> ", state); LogPrint(eLogWarning, "websocks: invalid state change ", m_State, " -> ", state);
} }
return; return;
case eWSCTryConnect: case eWSCTryConnect:
if(state == eWSCOkayConnect) { if(state == eWSCOkayConnect) {
// we connected okay // we connected okay
LogPrint(eLogDebug, "websocks: connected to ", m_RemoteAddr, ":", m_RemotePort); LogPrint(eLogDebug, "websocks: connected to ", m_RemoteAddr, ":", m_RemotePort);
SendResponse(""); SendResponse("");
m_State = eWSCOkayConnect; m_State = eWSCOkayConnect;
} else if(state == eWSCFailConnect) { } else if(state == eWSCFailConnect) {
// we did not connect okay // we did not connect okay
LogPrint(eLogDebug, "websocks: failed to connect to ", m_RemoteAddr, ":", m_RemotePort); LogPrint(eLogDebug, "websocks: failed to connect to ", m_RemoteAddr, ":", m_RemotePort);
SendResponse("failed to connect"); SendResponse("failed to connect");
m_State = eWSCFailConnect; m_State = eWSCFailConnect;
EnterState(eWSCInitial); EnterState(eWSCInitial);
} else if(state == eWSCClose) { } else if(state == eWSCClose) {
// premature close // premature close
LogPrint(eLogWarning, "websocks: websocket connection closed prematurely"); LogPrint(eLogWarning, "websocks: websocket connection closed prematurely");
m_State = eWSCClose; m_State = eWSCClose;
} else { } else {
LogPrint(eLogWarning, "websocks: invalid state change ", m_State, " -> ", state); LogPrint(eLogWarning, "websocks: invalid state change ", m_State, " -> ", state);
} }
return; return;
case eWSCFailConnect: case eWSCFailConnect:
if (state == eWSCInitial) { if (state == eWSCInitial) {
// reset to initial state so we can try connecting again // reset to initial state so we can try connecting again
m_RemoteAddr = ""; m_RemoteAddr = "";
m_RemotePort = 0; m_RemotePort = 0;
LogPrint(eLogDebug, "websocks: reset websocket conn to initial state"); LogPrint(eLogDebug, "websocks: reset websocket conn to initial state");
m_State = eWSCInitial; m_State = eWSCInitial;
} else if (state == eWSCClose) { } else if (state == eWSCClose) {
// we are going to close the connection // we are going to close the connection
m_State = eWSCClose; m_State = eWSCClose;
Close(); Close();
} else { } else {
LogPrint(eLogWarning, "websocks: invalid state change ", m_State, " -> ", state); LogPrint(eLogWarning, "websocks: invalid state change ", m_State, " -> ", state);
} }
return; return;
case eWSCOkayConnect: case eWSCOkayConnect:
if(state == eWSCClose) { if(state == eWSCClose) {
// graceful close // graceful close
m_State = eWSCClose; m_State = eWSCClose;
Close(); Close();
} else { } else {
LogPrint(eLogWarning, "websocks: invalid state change ", m_State, " -> ", state); LogPrint(eLogWarning, "websocks: invalid state change ", m_State, " -> ", state);
} }
case eWSCClose: case eWSCClose:
if(state == eWSCEnd) { if(state == eWSCEnd) {
LogPrint(eLogDebug, "websocks: socket ended"); LogPrint(eLogDebug, "websocks: socket ended");
} else { Kill();
LogPrint(eLogWarning, "websocks: invalid state change ", m_State, " -> ", state); auto me = shared_from_this();
} Done(me);
return; } else {
default: LogPrint(eLogWarning, "websocks: invalid state change ", m_State, " -> ", state);
LogPrint(eLogError, "websocks: bad state ", m_State); }
} return;
} default:
LogPrint(eLogError, "websocks: bad state ", m_State);
}
}
void StartForwarding() void StartForwarding()
{ {
LogPrint(eLogDebug, "websocks: begin forwarding data"); LogPrint(eLogDebug, "websocks: begin forwarding data");
uint8_t b[1]; uint8_t b[1];
m_Stream->Send(b, 0); m_Stream->Send(b, 0);
AsyncRecv(); AsyncRecv();
} }
void HandleAsyncRecv(const boost::system::error_code &ec, std::size_t n) void HandleAsyncRecv(const boost::system::error_code &ec, std::size_t n)
{ {
if(ec) { if(ec) {
// error // error
LogPrint(eLogWarning, "websocks: connection error ", ec.message()); LogPrint(eLogWarning, "websocks: connection error ", ec.message());
EnterState(eWSCClose); EnterState(eWSCClose);
} else { } else {
// forward data // forward data
LogPrint(eLogDebug, "websocks recv ", n); LogPrint(eLogDebug, "websocks recv ", n);
std::string str((char*)m_RecvBuf, n); std::string str((char*)m_RecvBuf, n);
auto conn = m_Parent->GetConn(m_Conn); auto conn = m_Parent->GetConn(m_Conn);
if(!conn) { if(!conn) {
LogPrint(eLogWarning, "websocks: connection is gone"); LogPrint(eLogWarning, "websocks: connection is gone");
EnterState(eWSCClose); EnterState(eWSCClose);
return; return;
@ -277,98 +296,98 @@ namespace client
conn->send(str); conn->send(str);
AsyncRecv(); AsyncRecv();
} }
} }
void AsyncRecv() void AsyncRecv()
{ {
m_Stream->AsyncReceive( m_Stream->AsyncReceive(
boost::asio::buffer(m_RecvBuf, sizeof(m_RecvBuf)), boost::asio::buffer(m_RecvBuf, sizeof(m_RecvBuf)),
std::bind(&WebSocksConn::HandleAsyncRecv, this, std::placeholders::_1, std::placeholders::_2), 60); std::bind(&WebSocksConn::HandleAsyncRecv, this, std::placeholders::_1, std::placeholders::_2), 60);
} }
/** @brief send error message or empty string for success */ /** @brief send error message or empty string for success */
void SendResponse(const std::string & errormsg) void SendResponse(const std::string & errormsg)
{ {
boost::property_tree::ptree resp; boost::property_tree::ptree resp;
if(errormsg.size()) { if(errormsg.size()) {
resp.put("error", errormsg); resp.put("error", errormsg);
resp.put("success", 0); resp.put("success", 0);
} else { } else {
resp.put("success", 1); resp.put("success", 1);
} }
std::ostringstream ss; std::ostringstream ss;
write_json(ss, resp); write_json(ss, resp);
auto conn = m_Parent->GetConn(m_Conn); auto conn = m_Parent->GetConn(m_Conn);
if(conn) conn->send(ss.str()); if(conn) conn->send(ss.str());
} }
void ConnectResult(Stream_t stream) void ConnectResult(Stream_t stream)
{ {
m_Stream = stream; m_Stream = stream;
if(m_State == eWSCClose) { if(m_State == eWSCClose) {
// premature close of websocket // premature close of websocket
Close(); Close();
return; return;
} }
if(m_Stream) { if(m_Stream) {
// connect good // connect good
EnterState(eWSCOkayConnect); EnterState(eWSCOkayConnect);
StartForwarding(); StartForwarding();
} else { } else {
// connect failed // connect failed
EnterState(eWSCFailConnect); EnterState(eWSCFailConnect);
} }
} }
virtual void GotMessage(const websocketpp::connection_hdl & conn, WebSocksServerImpl::message_ptr msg) virtual void GotMessage(const websocketpp::connection_hdl & conn, WebSocksServerImpl::message_ptr msg)
{ {
(void) conn; (void) conn;
std::string payload = msg->get_payload(); std::string payload = msg->get_payload();
if(m_State == eWSCOkayConnect) if(m_State == eWSCOkayConnect)
{ {
// forward to server // forward to server
LogPrint(eLogDebug, "websocks: forward ", payload.size()); LogPrint(eLogDebug, "websocks: forward ", payload.size());
m_Stream->Send((uint8_t*)payload.c_str(), payload.size()); m_Stream->Send((uint8_t*)payload.c_str(), payload.size());
} else if (m_State == eWSCInitial) { } else if (m_State == eWSCInitial) {
// recv connect request // recv connect request
auto itr = payload.find(":"); auto itr = payload.find(":");
if(itr == std::string::npos) { if(itr == std::string::npos) {
// no port // no port
m_RemotePort = 0; m_RemotePort = 0;
m_RemoteAddr = payload; m_RemoteAddr = payload;
} else { } else {
// includes port // includes port
m_RemotePort = std::stoi(payload.substr(itr+1)); m_RemotePort = std::stoi(payload.substr(itr+1));
m_RemoteAddr = payload.substr(0, itr); m_RemoteAddr = payload.substr(0, itr);
} }
EnterState(eWSCTryConnect); EnterState(eWSCTryConnect);
} else { } else {
// wtf? // wtf?
LogPrint(eLogWarning, "websocks: got message in invalid state ", m_State); LogPrint(eLogWarning, "websocks: got message in invalid state ", m_State);
} }
} }
virtual void Close() virtual void Close()
{ {
if(m_State == eWSCClose) { if(m_State == eWSCClose) {
LogPrint(eLogDebug, "websocks: closing connection"); LogPrint(eLogDebug, "websocks: closing connection");
if(m_Stream) m_Stream->Close(); if(m_Stream) m_Stream->Close();
m_Parent->CloseConn(m_Conn); m_Parent->CloseConn(m_Conn);
EnterState(eWSCEnd); EnterState(eWSCEnd);
} else { } else {
EnterState(eWSCClose); EnterState(eWSCClose);
} }
} }
}; };
WebSocksConn_ptr CreateWebSocksConn(const websocketpp::connection_hdl & conn, WebSocksImpl * parent) WebSocksConn_ptr CreateWebSocksConn(const websocketpp::connection_hdl & conn, WebSocksImpl * parent)
{ {
auto ptr = std::make_shared<WebSocksConn>(conn, parent); auto ptr = std::make_shared<WebSocksConn>(conn, parent);
auto c = parent->GetConn(conn); auto c = parent->GetConn(conn);
c->set_message_handler(std::bind(&WebSocksConn::GotMessage, ptr.get(), std::placeholders::_1, std::placeholders::_2)); c->set_message_handler(std::bind(&WebSocksConn::GotMessage, ptr.get(), std::placeholders::_1, std::placeholders::_2));
return ptr; return ptr;
} }
} }
} }
@ -383,7 +402,7 @@ namespace client
class WebSocksImpl class WebSocksImpl
{ {
public: public:
WebSocksImpl(const std::string & addr, int port) WebSocksImpl(const std::string & addr, int port) : m_Addr(addr), m_Port(port)
{ {
} }
@ -398,9 +417,20 @@ namespace client
void Stop() void Stop()
{ {
} }
void InitializeDestination(WebSocks * parent)
{
}
boost::asio::ip::tcp::endpoint GetLocalEndpoint()
{
return boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(m_Addr), m_Port);
}
std::string m_Addr;
int m_Port;
}; };
} }
} }
@ -410,17 +440,28 @@ namespace i2p
{ {
namespace client namespace client
{ {
WebSocks::WebSocks(const std::string & addr, int port) : m_Impl(new WebSocksImpl(addr, port)) {} WebSocks::WebSocks(const std::string & addr, int port, std::shared_ptr<ClientDestination> localDestination) : m_Impl(new WebSocksImpl(addr, port))
{
m_Impl->InitializeDestination(this);
}
WebSocks::~WebSocks() { delete m_Impl; } WebSocks::~WebSocks() { delete m_Impl; }
void WebSocks::Start() void WebSocks::Start()
{ {
m_Impl->Start(); m_Impl->Start();
GetLocalDestination()->Start();
}
boost::asio::ip::tcp::endpoint WebSocks::GetLocalEndpoint() const
{
return m_Impl->GetLocalEndpoint();
} }
void WebSocks::Stop() void WebSocks::Stop()
{ {
m_Impl->Stop(); m_Impl->Stop();
GetLocalDestination()->Stop();
} }
} }
} }

View file

@ -1,27 +1,34 @@
#ifndef WEBSOCKS_H_ #ifndef WEBSOCKS_H_
#define WEBSOCKS_H_ #define WEBSOCKS_H_
#include <string> #include <string>
#include <memory>
#include "I2PService.h"
#include "Destination.h"
namespace i2p namespace i2p
{ {
namespace client namespace client
{ {
class WebSocksImpl; class WebSocksImpl;
/** @brief websocket socks proxy server */ /** @brief websocket socks proxy server */
class WebSocks class WebSocks : public i2p::client::I2PService
{ {
public: public:
WebSocks(const std::string & addr, int port); WebSocks(const std::string & addr, int port, std::shared_ptr<ClientDestination> localDestination);
~WebSocks(); ~WebSocks();
void Start(); void Start();
void Stop(); void Stop();
private: boost::asio::ip::tcp::endpoint GetLocalEndpoint() const;
WebSocksImpl * m_Impl;
}; const char * GetName() { return "WebSOCKS Proxy"; }
private:
WebSocksImpl * m_Impl;
};
} }
} }
#endif #endif

View file

@ -59,7 +59,8 @@ LOCAL_SRC_FILES := DaemonAndroid.cpp i2pd_android.cpp \
../../TunnelPool.cpp \ ../../TunnelPool.cpp \
../../Timestamp.cpp \ ../../Timestamp.cpp \
../../Event.cpp \ ../../Event.cpp \
../../BloomFilter.cpp \ ../../WebSocks.cpp \
../../BloomFilter.cpp \
../../util.cpp \ ../../util.cpp \
../../i2pd.cpp ../../UPnP.cpp ../../i2pd.cpp ../../UPnP.cpp