Here’s an example of a simple SOCKS5 proxy server in C++ using the Boost Asio library:
#include <iostream>
#include <boost/asio.hpp>
using boost::asio::ip::tcp;
class socks5_session : public std::enable_shared_from_this<socks5_session>
{
public:
socks5_session(tcp::socket socket) : socket_(std::move(socket)), state_(0)
{
}
void start()
{
do_read();
}
private:
void do_read()
{
auto self(shared_from_this());
socket_.async_receive(boost::asio::buffer(buffer_),
[this, self](boost::system::error_code ec, std::size_t bytes_transferred)
{
if (!ec)
{
switch (state_)
{
case 0: // authentication methods
if (buffer_[0] == 0x05 && buffer_[1] >= 1 && buffer_[2] <= 3)
{
// send selection message
buffer_[0] = 0x05;
buffer_[1] = 0x00; // no authentication required
boost::asio::async_write(socket_, boost::asio::buffer(buffer_, 2),
[this, self](boost::system::error_code ec, std::size_t /* bytes_transferred */)
{
if (!ec)
{
state_ = 1;
do_read();
}
});
}
else
{
std::cerr << "Invalid authentication methods message\n";
}
break;
case 1: // connection request
if (buffer_[0] == 0x05 && buffer_[1] == 0x01 && buffer_[2] == 0x00)
{
// parse destination address
int addr_type = buffer_[3];
std::size_t addr_len;
if (addr_type == 0x01) // IPv4 address
addr_len = 4;
else if (addr_type == 0x03) // domain name
addr_len = buffer_[4] + 1;
else if (addr_type == 0x04) // IPv6 address
addr_len = 16;
else // invalid address type
{
std::cerr << "Invalid address type\n";
return;
}
if (bytes_transferred < 4 + addr_len + 2)
{
std::cerr << "Incomplete connection request message\n";
return;
}
std::string dest_addr(buffer_.begin() + 4, buffer_.begin() + 4 + addr_len);
int dest_port = buffer_[4 + addr_len] << 8 | buffer_[4 + addr_len + 1];
// connect to destination server
tcp::resolver resolver(socket_.get_io_service());
resolver.async_resolve(dest_addr, std::to_string(dest_port),
[this, self](boost::system::error_code ec, tcp::resolver::iterator it)
{
if (!ec)
{
do_connect(it);
}
});
}
else
{
std::cerr << "Invalid connection request message\n";
}
break;
case 2: // forward data
boost::asio::async_write(destination_socket_, boost::asio::buffer(buffer_, bytes_transferred),
[this, self](boost::system::error_code ec, std::size_t /* bytes_transferred */)
{
if (ec)
{
std::cerr << "Forward error: " << ec.message() << "\n";
socket_.close();
}
else
{
do_read();
}
});
break;
}
}
else
{
std::cerr << "Read error: " << ec.message() << "\n";
socket_.close();
}
});
}
void do_connect(tcp::resolver::iterator it)
{
auto self(shared_from_this());
destination_socket_.async_connect(*it,
[this, self](boost::system::error_code ec)
{
if (!ec)
{
// send success message
buffer_[0] = 0x05;
buffer_[1] = 0x00; // success
buffer_[2] = 0x00; // reserved
buffer_[3] = 0x01; // IPv4 address
boost::asio::ip::address_v4 address = destination_socket_.remote_endpoint().address().to_v4();
std::memcpy(buffer_.data() + 4, &address, 4);
int port = destination_socket_.remote_endpoint().port();
buffer_[8] = port >> 8;
buffer_[9] = port & 0xff;
boost::asio::async_write(socket_, boost::asio::buffer(buffer_, 10),
[this, self](boost::system::error_code ec, std::size_t /* bytes_transferred */)
{
if (!ec)
{
state_ = 2;
do_read();
}
});
}
else
{
std::cerr << "Connect error: " << ec.message() << "\n";
buffer_[0] = 0x05;
buffer_[1] = 0x03; // destination unreachable
buffer_[2] = 0x00; // reserved
boost::asio::async_write(socket_, boost::asio::buffer(buffer_, 3),
[this, self](boost::system::error_code ec, std::size_t /* bytes_transferred */)
{
socket_.close();
});
}
});
}
tcp::socket socket_;
tcp::socket destination_socket_;
std::array<char, 1024> buffer_;
int state_;
};
class socks5_server
{
public:
socks5_server(boost::asio::io_service& io_service, short port)
: acceptor_(io_service, tcp::endpoint(tcp::v4(), port))
{
do_accept();
}
private:
void do_accept()
{
acceptor_.async_accept([this](boost::system::error_code ec, tcp::socket socket)
{
if (!ec)
{
std::make_shared<socks5_session>(std::move(socket))->start();
}
do_accept();
});
}
tcp::acceptor acceptor_;
};
int main(int argc, char* argv[])
{
try
{
if (argc != 2)
{
std::cerr << "Usage: socks5_server <port>\n";
return 1;
}
boost::asio::io_service io_service;
socks5_server server(io_service, std::atoi(argv[1]));
io_service.run();
}
catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << "\n";
}
return 0;
}
This code listens on a specified port for incoming SOCKS5 connections, and proxies the TCP traffic to the destination server. It supports both IPv4 and domain name destinations. Note that this is just a basic example and doesn’t handle all possible error conditions or edge cases.