网络传输之socket5代理服务
使用方法:
- 编译环境 win10 x64下 vs2019编译器
- server端如果启动失败使用管理员权限,默认监听1080端口。多线程(多客户端)情况下没有测试过,目前没有支持账号密码的形式访问。
- 客户端需要指定代理服务器的IP和端口和 目标服务器的IP和端口。
- 支持windows 自带的curl测试。
Server.cpp
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <winsock2.h>
#include <windows.h>
#include <iostream>
#include <string>
#include <vector>
#include <sstream>
#include <cstring>
#include <algorithm>
#include <thread>
#pragma comment(lib, "ws2_32.lib")
#define SOCKS5_VERSION 0x05
#define AUTH_METHOD_NO_AUTH 0x00
#define REQUEST_CONNECT 0x01
#define ADDRESS_TYPE_IPV4 0x01
#define ADDRESS_TYPE_DOMAINNAME 0x03
#define ADDRESS_TYPE_IPV6 0x04
// SOCKS5 handshake request structure
struct Socks5HandshakeRequest {
unsigned char version; // SOCKS5 version number
unsigned char nMethods; // Number of supported authentication methods
unsigned char authMethod[255]; // Chosen authentication method (0x00 - no authentication)
// Constructor
Socks5HandshakeRequest()
: version(SOCKS5_VERSION), nMethods(0x01)
{
memset(authMethod, 0, 255);
}
};
// SOCKS5 handshake response structure
struct Socks5HandshakeResponse {
unsigned char version; // SOCKS5 version number
unsigned char authMethod; // Chosen authentication method (0x00 - no authentication)
// Constructor
Socks5HandshakeResponse(unsigned char v = SOCKS5_VERSION, unsigned char method = AUTH_METHOD_NO_AUTH)
: version(v), authMethod(method) {}
};
// SOCKS5 connection request structure
#pragma pack(push, 1) // Set structure to 1-byte alignment
struct Socks5ConnectRequest {
unsigned char version; // SOCKS5 version number
unsigned char requestType; // Request type (0x01 - CONNECT)
unsigned char reserved; // Reserved field
unsigned char addressType; // Address type (e.g., IPv4, domain name)
unsigned short targetPort; // Target port
char domainNameLength; // Target host len
char* targetHost; // Target host (domain name)
// Constructor
Socks5ConnectRequest(const char* host, unsigned short port)
: version(SOCKS5_VERSION), requestType(REQUEST_CONNECT), reserved(0x00), addressType(ADDRESS_TYPE_DOMAINNAME),
targetHost((char*) host), targetPort(port) {
}
// Convert the structure to a byte array for sending
void toByteArray(unsigned char* buffer) const {
buffer[0] = version;
buffer[1] = requestType;
buffer[2] = reserved;
buffer[3] = addressType;
memcpy(buffer+4, targetHost, domainNameLength);
unsigned short port = htons(targetPort);
buffer[domainNameLength + 6] = port & 0xFF;
buffer[domainNameLength + 5] = (port >> 8) & 0xFF;
}
};
#pragma pack(pop) // Restore previous alignment
// SOCKS5 connection response structure
struct Socks5ConnectResponse {
unsigned char version; // SOCKS5 version number
unsigned char reply; // Response code (0x00 - success)
unsigned char reserved; // Reserved field
unsigned char addressType; // Address type
unsigned char boundAddress[4]; // Bound address (IPv4)
unsigned short boundPort; // Bound port
// Constructor
Socks5ConnectResponse()
: version(SOCKS5_VERSION), reply(0x00), reserved(0x00), addressType(ADDRESS_TYPE_IPV4) {
std::memset(boundAddress, 0, sizeof(boundAddress));
boundPort = 0;
}
};
// Initialize Winsock
void initWinsock() {
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
std::cerr << "WSAStartup failed!" << std::endl;
exit(1);
}
}
// Handle client connection
void handleClient(SOCKET clientSocket) {
// 1. Handshake phase
Socks5HandshakeRequest handshakeRequest;
int bytesReceived = recv(clientSocket, (char*)&handshakeRequest,sizeof(handshakeRequest), 0);
if (handshakeRequest.version != SOCKS5_VERSION) {
std::cerr << "Invalid SOCKS5 handshake request!" << std::endl;
closesocket(clientSocket);
return;
}
/*handshakeRequest.authMethod = new unsigned char[handshakeRequest.nMethods+1]{ };
bytesReceived = recv(clientSocket, (char*)handshakeRequest.authMethod, handshakeRequest.nMethods, 0);
if (bytesReceived != handshakeRequest.nMethods) {
std::cerr << "Invalid SOCKS5 handshake request!" << std::endl;
closesocket(clientSocket);
return;
}*/
Socks5HandshakeResponse handshakeResponse;
bytesReceived=send(clientSocket, (char*)&handshakeResponse, 2, 0);
char connectbuffer[1024]{};
// 2. Connection request phase
Socks5ConnectRequest connectRequest("", 0);
bytesReceived = recv(clientSocket, connectbuffer, 1024, 0);
if (bytesReceived < 4) {
std::cerr << "Invalid SOCKS5 connect request!" << std::endl;
closesocket(clientSocket);
return;
}
connectRequest.version = connectbuffer[0];
connectRequest.requestType= connectbuffer[1];
connectRequest.reserved=connectbuffer[2];
connectRequest.addressType= connectbuffer[3];
// Check address type // Target address and port
char urlbuffer[256]{};
if (connectRequest.addressType == ADDRESS_TYPE_IPV4) {
connectRequest.targetHost = new char[4] {};
connectRequest.domainNameLength = 4;
memcpy(connectRequest.targetHost, &connectbuffer[4], 4);
sprintf_s(urlbuffer, "%u.%u.%u.%u", (unsigned char)connectRequest.targetHost[0], (unsigned char)connectRequest.targetHost[1], (unsigned char)connectRequest.targetHost[2], (unsigned char)connectRequest.targetHost[3]);
connectRequest.targetPort = connectbuffer[connectRequest.domainNameLength + 4];
connectRequest.targetPort <<= 8;
connectRequest.targetPort |= (unsigned char)connectbuffer[connectRequest.domainNameLength + 5];
}
else if (connectRequest.addressType == ADDRESS_TYPE_DOMAINNAME) {
connectRequest.domainNameLength = connectbuffer[4];
connectRequest.targetHost = new char[connectRequest.domainNameLength] {};
struct hostent* host = gethostbyname(&connectbuffer[5]);
if (host == NULL)
return;
for (int i = 0; host->h_addr_list[i]; i++)
{
strcpy_s(urlbuffer, inet_ntoa(*(struct in_addr*)host->h_addr_list[i]));
break;
}
memcpy(connectRequest.targetHost, urlbuffer,strlen(urlbuffer));
connectRequest.targetPort = connectbuffer[connectRequest.domainNameLength + 5];
connectRequest.targetPort <<= 8;
connectRequest.targetPort |= (unsigned char)connectbuffer[connectRequest.domainNameLength + 6];
}
else
{
std::cerr << "Only DOMAINNAME address type is supported!" << std::endl;
closesocket(clientSocket);
return;
}
std::cout << "Connecting to target: " << urlbuffer << ":" << connectRequest.targetPort << std::endl;
// 3. Establish connection to target server
SOCKET targetSocket = socket(AF_INET, SOCK_STREAM, 0);
sockaddr_in targetAddr;
targetAddr.sin_family = AF_INET;
targetAddr.sin_port = htons(connectRequest.targetPort);
targetAddr.sin_addr.s_addr = inet_addr(urlbuffer);
if (connect(targetSocket, (sockaddr*)&targetAddr, sizeof(targetAddr)) == SOCKET_ERROR) {
std::cerr << "Failed to connect to target server!" << std::endl;
// 4. Send connection failure response to client
Socks5ConnectResponse connectResponse;
connectResponse.reply = 1;
send(clientSocket, (char*)&connectResponse, sizeof(connectResponse), 0);
closesocket(clientSocket);
return;
}
// 4. Send connection success response to client
Socks5ConnectResponse connectResponse;
send(clientSocket, (char*)&connectResponse, sizeof(connectResponse), 0);
// 5. Data forwarding phase
fd_set readfds;
while (true) {
FD_ZERO(&readfds);
FD_SET(clientSocket, &readfds);
FD_SET(targetSocket, &readfds);
int selectResult = select(0, &readfds, nullptr, nullptr, nullptr);
if (selectResult == SOCKET_ERROR) {
std::cerr << "Select error!" << std::endl;
break;
}
if (FD_ISSET(clientSocket, &readfds)) {
char buffer[4096]{};
int bytesReceived = recv(clientSocket, buffer, sizeof(buffer), 0);
if (bytesReceived <= 0) {
break; // Client closed connection
}
std::cout <<"clientSocket buffer:" << buffer << std::endl;
send(targetSocket, buffer, bytesReceived, 0);
}
if (FD_ISSET(targetSocket, &readfds)) {
char buffer[4096]{};
int bytesReceived = recv(targetSocket, buffer, sizeof(buffer), 0);
if (bytesReceived <= 0) {
break; // Target server closed connection
}
std::cout << "targetSocket buffer:" << buffer << std::endl;
send(clientSocket, buffer, bytesReceived, 0);
}
}
// Clean up
closesocket(clientSocket);
closesocket(targetSocket);
}
// Start SOCKS5 server
SOCKET startSocks5Server(const std::string& serverHost, unsigned short serverPort) {
SOCKET serverSocket = socket(AF_INET, SOCK_STREAM, 0);
if (serverSocket == INVALID_SOCKET) {
std::cerr << "Server socket creation failed!" << std::endl;
exit(1);
}
sockaddr_in serverAddr;
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(serverPort);
serverAddr.sin_addr.s_addr = INADDR_ANY;
if (bind(serverSocket, (sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {
std::cerr << "Binding failed!" << std::endl;
closesocket(serverSocket);
exit(1);
}
if (listen(serverSocket, 5) == SOCKET_ERROR) {
std::cerr << "Listen failed!" << std::endl;
closesocket(serverSocket);
exit(1);
}
std::cout << "SOCKS5 server started on " << serverHost << ":" << serverPort << std::endl;
return serverSocket;
}
int main() {
// Initialize Winsock
initWinsock();
// Start SOCKS5 server
SOCKET serverSocket = startSocks5Server("", 1080);
// Accept client connections
while (true) {
SOCKET clientSocket = accept(serverSocket, nullptr, nullptr);
if (clientSocket == INVALID_SOCKET) {
std::cerr << "Accept failed!" << std::endl;
continue;
}
// Create a threaded client connection
std::thread(handleClient, clientSocket).detach();
}
// Close the server socket
closesocket(serverSocket);
WSACleanup();
return 0;
}
client.cpp
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <winsock2.h>
#include <windows.h>
#include <iostream>
#include <string>
#include <vector>
#include <sstream>
#include <cstring>
#include <algorithm>
#pragma comment(lib, "ws2_32.lib")
// SOCKS5 protocol constant definitions
#define SOCKS5_VERSION 0x05
#define AUTH_METHOD_NO_AUTH 0x00
#define REQUEST_CONNECT 0x01
#define ADDRESS_TYPE_IPV4 0x01
#define ADDRESS_TYPE_DOMAINNAME 0x03
#define ADDRESS_TYPE_IPV6 0x04
// SOCKS5 handshake request structure
struct Socks5HandshakeRequest {
unsigned char version; // SOCKS5 version number
unsigned char nMethods; // Number of supported authentication methods
unsigned char authMethod[255]; // Chosen authentication method (0x00 - no authentication)
// Constructor
Socks5HandshakeRequest()
: version(SOCKS5_VERSION), nMethods(0x01) {
authMethod[0] = AUTH_METHOD_NO_AUTH;
}
};
// SOCKS5 handshake response structure
struct Socks5HandshakeResponse {
unsigned char version; // SOCKS5 version number
unsigned char authMethod; // Chosen authentication method (0x00 - no authentication)
// Constructor
Socks5HandshakeResponse(unsigned char v = SOCKS5_VERSION, unsigned char method = AUTH_METHOD_NO_AUTH)
: version(v), authMethod(method) {}
};
// SOCKS5 connection request structure
#pragma pack(push, 1) // Set structure to 1-byte alignment
struct Socks5ConnectRequest {
unsigned char version; // SOCKS5 version number
unsigned char requestType; // Request type (0x01 - CONNECT)
unsigned char reserved; // Reserved field
unsigned char addressType; // Address type (e.g., IPv4, domain name)
unsigned short targetPort; // Target port
char domainNameLength; // Target host len
char* targetHost; // Target host (domain name)
// Constructor
Socks5ConnectRequest(const char* host, unsigned short port)
: version(SOCKS5_VERSION), requestType(REQUEST_CONNECT), reserved(0x00), addressType(ADDRESS_TYPE_DOMAINNAME),
targetHost((char*)host), targetPort(port) {
}
// Convert the structure to a byte array for sending
int toByteArray(unsigned char* buffer) const {
buffer[0] = version;
buffer[1] = requestType;
buffer[2] = reserved;
buffer[3] = addressType;
if (addressType == ADDRESS_TYPE_DOMAINNAME)
{
buffer[4] = domainNameLength;
memcpy(buffer + 5, targetHost, domainNameLength);
unsigned short port = htons(targetPort);
buffer[domainNameLength + 5] = port & 0xFF;
buffer[domainNameLength + 6] = (port >> 8) & 0xFF;
return domainNameLength + 7;
}
else
{
memcpy(buffer + 4, targetHost, domainNameLength);
unsigned short port = htons(targetPort);
buffer[domainNameLength + 4] = port & 0xFF;
buffer[domainNameLength + 5] = (port >> 8) & 0xFF;
}
return domainNameLength + 6;
}
};
#pragma pack(pop) // Restore previous alignment
// SOCKS5 connection response structure
struct Socks5ConnectResponse {
unsigned char version; // SOCKS5 version number
unsigned char reply; // Response code (0x00 - success)
unsigned char reserved; // Reserved field
unsigned char addressType; // Address type
unsigned char boundAddress[4]; // Bound address (IPv4)
unsigned short boundPort; // Bound port
// Constructor
Socks5ConnectResponse()
: version(SOCKS5_VERSION), reply(0x00), reserved(0x00), addressType(ADDRESS_TYPE_IPV4) {
std::memset(boundAddress, 0, sizeof(boundAddress));
boundPort = 0;
}
};
// Target server connection information structure
struct TargetConnectionInfo {
std::string targetHost; // Target host address (domain name or IP address)
unsigned short targetPort; // Target port
// Constructor
TargetConnectionInfo(const std::string& host, unsigned short port)
: targetHost(host), targetPort(port) {}
};
// SOCKS5 proxy information structure
struct Socks5ProxyInfo {
std::string proxyHost; // SOCKS5 proxy server address
unsigned short proxyPort; // SOCKS5 proxy server port
// Constructor
Socks5ProxyInfo(const std::string& host, unsigned short port)
: proxyHost(host), proxyPort(port) {}
};
// Complete connection information structure
struct ConnectionInfo {
Socks5ProxyInfo proxyInfo; // SOCKS5 proxy information
TargetConnectionInfo targetInfo; // Target server information
bool useSocks5Proxy; // Whether to use SOCKS5 proxy
// Constructor
ConnectionInfo(const std::string& proxyHost, unsigned short proxyPort,
const std::string& targetHost, unsigned short targetPort, bool useProxy)
: proxyInfo(proxyHost, proxyPort), targetInfo(targetHost, targetPort), useSocks5Proxy(useProxy) {}
};
// HTTP request structure
struct HttpRequest {
std::string method; // Request method (e.g., GET, POST)
std::string url; // Request URL
std::string host; // Target host
std::string connection; // Connection options (e.g., close)
// Constructor
HttpRequest(const std::string& method, const std::string& url, const std::string& host)
: method(method), url(url), host(host), connection("close") {}
// Convert HTTP request to string
std::string toString() const {
std::stringstream ss;
ss << method << " " << url << " HTTP/1.1\r\n";
ss << "Host: " << host << "\r\n";
ss << "Connection: " << connection << "\r\n";
ss << "\r\n";
return ss.str();
}
};
// HTTP response structure
struct HttpResponse {
int statusCode; // Status code
std::string statusMessage; // Status message
std::string body; // Response body
// Constructor
HttpResponse(int code, const std::string& message, const std::string& bodyContent)
: statusCode(code), statusMessage(message), body(bodyContent) {}
};
// Initialize Winsock
void initWinsock() {
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
std::cerr << "WSAStartup failed!" << std::endl;
exit(1);
}
}
// Create and connect to the SOCKS5 proxy server
SOCKET connectToSocks5Proxy(const ConnectionInfo& connInfo) {
SOCKET proxySocket = socket(AF_INET, SOCK_STREAM, 0);
if (proxySocket == INVALID_SOCKET) {
std::cerr << "Socket creation failed!" << std::endl;
exit(1);
}
sockaddr_in proxyAddr;
proxyAddr.sin_family = AF_INET;
proxyAddr.sin_port = htons(connInfo.proxyInfo.proxyPort);
proxyAddr.sin_addr.s_addr = inet_addr(connInfo.proxyInfo.proxyHost.c_str());
if (connect(proxySocket, (sockaddr*)&proxyAddr, sizeof(proxyAddr)) == SOCKET_ERROR) {
std::cerr << "Failed to connect to SOCKS5 proxy!" << std::endl;
closesocket(proxySocket);
exit(1);
}
return proxySocket;
}
// Send SOCKS5 handshake request and receive response
void socks5Handshake(SOCKET proxySocket) {
Socks5HandshakeRequest handshakeRequest;
send(proxySocket, (char*)&handshakeRequest, sizeof(handshakeRequest), 0);
Socks5HandshakeResponse handshakeResponse;
int bytesReceived = recv(proxySocket, (char*)&handshakeResponse, sizeof(handshakeResponse), 0);
if (bytesReceived != 2 || handshakeResponse.version != SOCKS5_VERSION || handshakeResponse.authMethod != AUTH_METHOD_NO_AUTH) {
std::cerr << "SOCKS5 handshake failed!" << std::endl;
closesocket(proxySocket);
exit(1);
}
}
int GetIpString(std::string strIpAdd, char iIp[])
{
int OppPos = 0, nowPos = 0, i = 0;
while (OppPos = strIpAdd.find_first_of('.', nowPos))
{
if (std::string::npos == OppPos)
{
iIp[i] = atoi(strIpAdd.substr(nowPos).c_str());
break;
}
iIp[i] = atoi(strIpAdd.substr(nowPos, OppPos - nowPos).c_str());
nowPos = OppPos + 1;
i++;
}
return i+1;
}
// Send connection request to target server
void sendConnectRequest(SOCKET proxySocket, const ConnectionInfo& connInfo) {
char ip[125]{};
int len=GetIpString(connInfo.targetInfo.targetHost.c_str(), ip);
Socks5ConnectRequest connectRequest(connInfo.targetInfo.targetHost.c_str(), connInfo.targetInfo.targetPort);
connectRequest.domainNameLength= strlen(connInfo.targetInfo.targetHost.c_str())+1;
len = 7 + connectRequest.domainNameLength;
unsigned char* requestBuffer = new unsigned char[len] {};
len=connectRequest.toByteArray(requestBuffer);
send(proxySocket, (char*)requestBuffer, len, 0);
Socks5ConnectResponse connectResponse;
int bytesReceived = recv(proxySocket, (char*)&connectResponse, sizeof(connectResponse), 0);
if (bytesReceived != sizeof(connectResponse) || connectResponse.reply != 0x00) {
std::cerr << "SOCKS5 connection request failed!" << std::endl;
closesocket(proxySocket);
exit(1);
}
}
// Send HTTP request and receive response
void sendHttpRequest(SOCKET proxySocket, const ConnectionInfo& connInfo) {
HttpRequest httpRequest("GET", "/", connInfo.targetInfo.targetHost);
std::string requestStr = httpRequest.toString();
// Send HTTP request
send(proxySocket, requestStr.c_str(), requestStr.size(), 0);
// Receive HTTP response
char buffer[4096];
int bytesReceived;
while ((bytesReceived = recv(proxySocket, buffer, sizeof(buffer), 0)) > 0) {
std::cout.write(buffer, bytesReceived);
}
}
// Main function
int main() {
// Create a ConnectionInfo structure instance
ConnectionInfo connInfo(
"127.0.0.1", // SOCKS5 proxy server address
1080, // SOCKS5 proxy server port
"127.0.0.1", // Target host address
888, // Target port
true // Whether to use SOCKS5 proxy
);
// Initialize Winsock
initWinsock();
// Connect to SOCKS5 proxy
SOCKET proxySocket = connectToSocks5Proxy(connInfo);
// Perform SOCKS5 handshake
socks5Handshake(proxySocket);
// Send connection request to target server
sendConnectRequest(proxySocket, connInfo);
// Send HTTP request and receive response
sendHttpRequest(proxySocket, connInfo);
// Close connection
closesocket(proxySocket);
WSACleanup();
return 0;
}