mirror of
https://github.com/PurpleI2P/i2pd
synced 2024-11-09 15:50:26 +03:00
common code for SOCKS5 proxy connectivity
This commit is contained in:
parent
4cb2ad48be
commit
158160f5c0
@ -19,9 +19,10 @@
|
|||||||
#include "RouterContext.h"
|
#include "RouterContext.h"
|
||||||
#include "Transports.h"
|
#include "Transports.h"
|
||||||
#include "NetDb.hpp"
|
#include "NetDb.hpp"
|
||||||
#include "NTCP2.h"
|
|
||||||
#include "HTTP.h"
|
#include "HTTP.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
#include "Socks5.h"
|
||||||
|
#include "NTCP2.h"
|
||||||
|
|
||||||
#if defined(__linux__) && !defined(_NETINET_IN_H)
|
#if defined(__linux__) && !defined(_NETINET_IN_H)
|
||||||
#include <linux/in6.h>
|
#include <linux/in6.h>
|
||||||
@ -1728,47 +1729,18 @@ namespace transport
|
|||||||
case eSocksProxy:
|
case eSocksProxy:
|
||||||
{
|
{
|
||||||
// TODO: support username/password auth etc
|
// TODO: support username/password auth etc
|
||||||
static const uint8_t buff[3] = {SOCKS5_VER, 0x01, 0x00};
|
Socks5Handshake (conn->GetSocket(), conn->GetRemoteEndpoint (),
|
||||||
boost::asio::async_write(conn->GetSocket(), boost::asio::buffer(buff, 3), boost::asio::transfer_all(),
|
[conn, timer](const boost::system::error_code& ec)
|
||||||
[] (const boost::system::error_code & ec, std::size_t transferred)
|
{
|
||||||
{
|
|
||||||
(void) transferred;
|
|
||||||
if(ec)
|
|
||||||
{
|
|
||||||
LogPrint(eLogWarning, "NTCP2: SOCKS5 write error ", ec.message());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
auto readbuff = std::make_shared<std::vector<uint8_t> >(2);
|
|
||||||
boost::asio::async_read(conn->GetSocket(), boost::asio::buffer(readbuff->data (), 2),
|
|
||||||
[this, readbuff, timer, conn](const boost::system::error_code & ec, std::size_t transferred)
|
|
||||||
{
|
|
||||||
if(ec)
|
|
||||||
{
|
|
||||||
LogPrint(eLogError, "NTCP2: SOCKS5 read error ", ec.message());
|
|
||||||
timer->cancel();
|
|
||||||
conn->Terminate();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if(transferred == 2)
|
|
||||||
{
|
|
||||||
if((*readbuff)[1] == 0x00)
|
|
||||||
{
|
|
||||||
AfterSocksHandshake(conn, timer);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if ((*readbuff)[1] == 0xff)
|
|
||||||
{
|
|
||||||
LogPrint(eLogError, "NTCP2: SOCKS5 proxy rejected authentication");
|
|
||||||
timer->cancel();
|
|
||||||
conn->Terminate();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
LogPrint(eLogError, "NTCP2:", (int)(*readbuff)[1]);
|
|
||||||
}
|
|
||||||
LogPrint(eLogError, "NTCP2: SOCKS5 server gave invalid response");
|
|
||||||
timer->cancel();
|
timer->cancel();
|
||||||
conn->Terminate();
|
if (!ec)
|
||||||
});
|
conn->ClientLogin();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LogPrint(eLogError, "NTCP2: SOCKS proxy handshake error ", ec.message());
|
||||||
|
conn->Terminate();
|
||||||
|
}
|
||||||
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case eHTTPProxy:
|
case eHTTPProxy:
|
||||||
@ -1836,71 +1808,6 @@ namespace transport
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NTCP2Server::AfterSocksHandshake(std::shared_ptr<NTCP2Session> conn, std::shared_ptr<boost::asio::deadline_timer> timer)
|
|
||||||
{
|
|
||||||
// build request
|
|
||||||
size_t sz = 6; // header + port
|
|
||||||
auto buff = std::make_shared<std::vector<int8_t> >(256);
|
|
||||||
auto readbuff = std::make_shared<std::vector<int8_t> >(256);
|
|
||||||
(*buff)[0] = SOCKS5_VER;
|
|
||||||
(*buff)[1] = SOCKS5_CMD_CONNECT;
|
|
||||||
(*buff)[2] = 0x00;
|
|
||||||
|
|
||||||
auto& ep = conn->GetRemoteEndpoint ();
|
|
||||||
if(ep.address ().is_v4 ())
|
|
||||||
{
|
|
||||||
(*buff)[3] = SOCKS5_ATYP_IPV4;
|
|
||||||
auto addrbytes = ep.address ().to_v4().to_bytes();
|
|
||||||
sz += 4;
|
|
||||||
memcpy(buff->data () + 4, addrbytes.data(), 4);
|
|
||||||
}
|
|
||||||
else if (ep.address ().is_v6 ())
|
|
||||||
{
|
|
||||||
(*buff)[3] = SOCKS5_ATYP_IPV6;
|
|
||||||
auto addrbytes = ep.address ().to_v6().to_bytes();
|
|
||||||
sz += 16;
|
|
||||||
memcpy(buff->data () + 4, addrbytes.data(), 16);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// We mustn't really fall here because all connections are made to IP addresses
|
|
||||||
LogPrint(eLogError, "NTCP2: Tried to connect to unexpected address via proxy");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
htobe16buf(buff->data () + sz - 2, ep.port ());
|
|
||||||
boost::asio::async_write(conn->GetSocket(), boost::asio::buffer(buff->data (), sz), boost::asio::transfer_all(),
|
|
||||||
[buff](const boost::system::error_code & ec, std::size_t written)
|
|
||||||
{
|
|
||||||
if(ec)
|
|
||||||
{
|
|
||||||
LogPrint(eLogError, "NTCP2: Failed to write handshake to socks proxy ", ec.message());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
boost::asio::async_read(conn->GetSocket(), boost::asio::buffer(readbuff->data (), SOCKS5_UDP_IPV4_REQUEST_HEADER_SIZE), // read min reply size
|
|
||||||
boost::asio::transfer_all(),
|
|
||||||
[timer, conn, readbuff](const boost::system::error_code & e, std::size_t transferred)
|
|
||||||
{
|
|
||||||
if (e)
|
|
||||||
LogPrint(eLogError, "NTCP2: SOCKS proxy read error ", e.message());
|
|
||||||
else if (!(*readbuff)[1]) // succeeded
|
|
||||||
{
|
|
||||||
boost::system::error_code ec;
|
|
||||||
size_t moreBytes = conn->GetSocket ().available(ec);
|
|
||||||
if (moreBytes) // read remaining portion of reply if ipv6 received
|
|
||||||
boost::asio::read (conn->GetSocket (), boost::asio::buffer(readbuff->data (), moreBytes), boost::asio::transfer_all (), ec);
|
|
||||||
timer->cancel();
|
|
||||||
conn->ClientLogin();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint(eLogError, "NTCP2: Proxy reply error ", (int)(*readbuff)[1]);
|
|
||||||
timer->cancel();
|
|
||||||
conn->Terminate();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void NTCP2Server::SetLocalAddress (const boost::asio::ip::address& localAddress)
|
void NTCP2Server::SetLocalAddress (const boost::asio::ip::address& localAddress)
|
||||||
{
|
{
|
||||||
auto addr = std::make_shared<boost::asio::ip::tcp::endpoint>(boost::asio::ip::tcp::endpoint(localAddress, 0));
|
auto addr = std::make_shared<boost::asio::ip::tcp::endpoint>(boost::asio::ip::tcp::endpoint(localAddress, 0));
|
||||||
|
@ -267,8 +267,7 @@ namespace transport
|
|||||||
|
|
||||||
void HandleConnect (const boost::system::error_code& ecode, std::shared_ptr<NTCP2Session> conn, std::shared_ptr<boost::asio::deadline_timer> timer);
|
void HandleConnect (const boost::system::error_code& ecode, std::shared_ptr<NTCP2Session> conn, std::shared_ptr<boost::asio::deadline_timer> timer);
|
||||||
void HandleProxyConnect(const boost::system::error_code& ecode, std::shared_ptr<NTCP2Session> conn, std::shared_ptr<boost::asio::deadline_timer> timer);
|
void HandleProxyConnect(const boost::system::error_code& ecode, std::shared_ptr<NTCP2Session> conn, std::shared_ptr<boost::asio::deadline_timer> timer);
|
||||||
void AfterSocksHandshake(std::shared_ptr<NTCP2Session> conn, std::shared_ptr<boost::asio::deadline_timer> timer);
|
|
||||||
|
|
||||||
// timer
|
// timer
|
||||||
void ScheduleTermination ();
|
void ScheduleTermination ();
|
||||||
void HandleTerminationTimer (const boost::system::error_code& ecode);
|
void HandleTerminationTimer (const boost::system::error_code& ecode);
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "SSU2Session.h"
|
#include "SSU2Session.h"
|
||||||
|
#include "Socks5.h"
|
||||||
|
|
||||||
namespace i2p
|
namespace i2p
|
||||||
{
|
{
|
||||||
|
154
libi2pd/Socks5.h
Normal file
154
libi2pd/Socks5.h
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024, The PurpleI2P Project
|
||||||
|
*
|
||||||
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
|
*
|
||||||
|
* See full license text in LICENSE file at top of project tree
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SOCKS5_H__
|
||||||
|
#define SOCKS5_H__
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <memory>
|
||||||
|
#include <boost/asio.hpp>
|
||||||
|
#include "I2PEndian.h"
|
||||||
|
|
||||||
|
namespace i2p
|
||||||
|
{
|
||||||
|
namespace transport
|
||||||
|
{
|
||||||
|
// SOCKS5 constants
|
||||||
|
const uint8_t SOCKS5_VER = 0x05;
|
||||||
|
const uint8_t SOCKS5_CMD_CONNECT = 0x01;
|
||||||
|
const uint8_t SOCKS5_CMD_UDP_ASSOCIATE = 0x03;
|
||||||
|
const uint8_t SOCKS5_ATYP_IPV4 = 0x01;
|
||||||
|
const uint8_t SOCKS5_ATYP_IPV6 = 0x04;
|
||||||
|
const uint8_t SOCKS5_ATYP_NAME = 0x03;
|
||||||
|
const size_t SOCKS5_UDP_IPV4_REQUEST_HEADER_SIZE = 10;
|
||||||
|
const size_t SOCKS5_UDP_IPV6_REQUEST_HEADER_SIZE = 22;
|
||||||
|
|
||||||
|
// SOCKS5 handshake
|
||||||
|
template<typename Socket, typename Handler>
|
||||||
|
void Socks5Connect (Socket& s, Handler handler, std::shared_ptr<std::vector<uint8_t> > buff, uint16_t port)
|
||||||
|
{
|
||||||
|
if (buff && buff->size () >= 6)
|
||||||
|
{
|
||||||
|
(*buff)[0] = SOCKS5_VER;
|
||||||
|
(*buff)[1] = SOCKS5_CMD_CONNECT;
|
||||||
|
(*buff)[2] = 0x00;
|
||||||
|
htobe16buf(buff->data () + buff->size () - 2, port);
|
||||||
|
boost::asio::async_write(s, boost::asio::buffer(*buff), boost::asio::transfer_all(),
|
||||||
|
[buff, &s, handler](const boost::system::error_code& ec, std::size_t transferred)
|
||||||
|
{
|
||||||
|
(void) transferred;
|
||||||
|
if (!ec)
|
||||||
|
{
|
||||||
|
auto readbuff = std::make_shared<std::vector<int8_t> >(262); // max possible
|
||||||
|
boost::asio::async_read(s, boost::asio::buffer(readbuff->data (), 7), boost::asio::transfer_all(), // read min reply size
|
||||||
|
[readbuff, &s, handler](const boost::system::error_code& ec, std::size_t transferred)
|
||||||
|
{
|
||||||
|
if (!ec)
|
||||||
|
{
|
||||||
|
if (!(*readbuff)[1]) // succeeded
|
||||||
|
{
|
||||||
|
boost::system::error_code ec;
|
||||||
|
size_t moreBytes = s.available(ec);
|
||||||
|
if (!ec && moreBytes) // read remaining portion of reply
|
||||||
|
boost::asio::read (s, boost::asio::buffer(readbuff->data (), moreBytes), boost::asio::transfer_all (), ec);
|
||||||
|
if (!ec)
|
||||||
|
handler (boost::system::error_code ());
|
||||||
|
else
|
||||||
|
handler (boost::asio::error::make_error_code (boost::asio::error::connection_aborted));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
handler (boost::asio::error::make_error_code (boost::asio::error::connection_refused));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
handler (ec);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
handler (ec);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
handler (boost::asio::error::make_error_code (boost::asio::error::no_buffer_space));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Socket, typename Handler>
|
||||||
|
void Socks5Connect (Socket& s, const boost::asio::ip::tcp::endpoint& ep, Handler handler)
|
||||||
|
{
|
||||||
|
std::shared_ptr<std::vector<uint8_t> > buff;
|
||||||
|
if(ep.address ().is_v4 ())
|
||||||
|
{
|
||||||
|
buff = std::make_shared<std::vector<uint8_t> >(10);
|
||||||
|
(*buff)[3] = SOCKS5_ATYP_IPV4;
|
||||||
|
auto addrbytes = ep.address ().to_v4().to_bytes();
|
||||||
|
memcpy(buff->data () + 4, addrbytes.data(), 4);
|
||||||
|
}
|
||||||
|
else if (ep.address ().is_v6 ())
|
||||||
|
{
|
||||||
|
buff = std::make_shared<std::vector<uint8_t> >(22);
|
||||||
|
(*buff)[3] = SOCKS5_ATYP_IPV6;
|
||||||
|
auto addrbytes = ep.address ().to_v6().to_bytes();
|
||||||
|
memcpy(buff->data () + 4, addrbytes.data(), 16);
|
||||||
|
}
|
||||||
|
if (buff)
|
||||||
|
Socks5Connect (s, handler, buff, ep.port ());
|
||||||
|
else
|
||||||
|
handler (boost::asio::error::make_error_code (boost::asio::error::fault));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Socket, typename Handler>
|
||||||
|
void Socks5Connect (Socket& s, const std::pair<std::string, uint16_t>& ep, Handler handler)
|
||||||
|
{
|
||||||
|
auto& addr = ep.first;
|
||||||
|
if (addr.length () <= 255)
|
||||||
|
{
|
||||||
|
auto buff = std::make_shared<std::vector<uint8_t> >(addr.length () + 7);
|
||||||
|
(*buff)[3] = SOCKS5_ATYP_NAME;
|
||||||
|
(*buff)[4] = addr.length ();
|
||||||
|
memcpy (buff->data () + 5, addr.c_str (), addr.length ());
|
||||||
|
Socks5Connect (s, handler, buff, ep.second);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
handler (boost::asio::error::make_error_code (boost::asio::error::name_too_long));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<typename Socket, typename Endpoint, typename Handler>
|
||||||
|
void Socks5Handshake (Socket& s, Endpoint ep, Handler handler)
|
||||||
|
{
|
||||||
|
static const uint8_t methodSelection[3] = { SOCKS5_VER, 0x01, 0x00 }; // 1 method, no auth
|
||||||
|
boost::asio::async_write(s, boost::asio::buffer(methodSelection, 3), boost::asio::transfer_all(),
|
||||||
|
[&s, ep, handler] (const boost::system::error_code& ec, std::size_t transferred)
|
||||||
|
{
|
||||||
|
(void) transferred;
|
||||||
|
if (!ec)
|
||||||
|
{
|
||||||
|
auto readbuff = std::make_shared<std::vector<uint8_t> >(2);
|
||||||
|
boost::asio::async_read(s, boost::asio::buffer(*readbuff), boost::asio::transfer_all(),
|
||||||
|
[&s, ep, handler, readbuff] (const boost::system::error_code& ec, std::size_t transferred)
|
||||||
|
{
|
||||||
|
if (!ec)
|
||||||
|
{
|
||||||
|
if (transferred == 2 && (*readbuff)[1] == 0x00) // no auth
|
||||||
|
Socks5Connect (s, ep, handler);
|
||||||
|
else
|
||||||
|
handler (boost::asio::error::make_error_code (boost::asio::error::invalid_argument));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
handler (ec);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
handler (ec);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2023, The PurpleI2P Project
|
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
@ -187,15 +187,6 @@ namespace transport
|
|||||||
uint64_t m_LastActivityTimestamp, m_LastBandwidthUpdateTimestamp;
|
uint64_t m_LastActivityTimestamp, m_LastBandwidthUpdateTimestamp;
|
||||||
uint32_t m_InBandwidth, m_OutBandwidth;
|
uint32_t m_InBandwidth, m_OutBandwidth;
|
||||||
};
|
};
|
||||||
|
|
||||||
// SOCKS5 proxy
|
|
||||||
const uint8_t SOCKS5_VER = 0x05;
|
|
||||||
const uint8_t SOCKS5_CMD_CONNECT = 0x01;
|
|
||||||
const uint8_t SOCKS5_CMD_UDP_ASSOCIATE = 0x03;
|
|
||||||
const uint8_t SOCKS5_ATYP_IPV4 = 0x01;
|
|
||||||
const uint8_t SOCKS5_ATYP_IPV6 = 0x04;
|
|
||||||
const size_t SOCKS5_UDP_IPV4_REQUEST_HEADER_SIZE = 10;
|
|
||||||
const size_t SOCKS5_UDP_IPV6_REQUEST_HEADER_SIZE = 22;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user