SOCKS5协议解析

socks的官方文档:https://www.ietf.org/rfc/rfc1928.txt

本文改变其他作者之手,在原文基础上加入客户端的编写,完善了服务端代码,原文是Linux端的程序代码,本文改为了Windows端程序代码。以下为原文链接:从零实现加密隧道(二):socks5 协议详解

  SOCKS5 是一种网络传输协议,主要用于客户端与外网服务器之间通讯的中间传递。SOCKS5 服务器通过将前端发来的请求转发给真正的目标服务器,模拟了一个前端的行为。在这里,前端和SOCKS5之间也是通过TCP/IP协议进行通讯,前端将原本要发送给真正服务器的请求发送给SOCKS5服务器,然后SOCKS5服务器将请求转发给真正的服务器。

注意:本文中程序仅适用于Windows端

一、SOCKS5通信流程

1.客户端与服务器身份验证

2.代理服务器响应客户端请求

3.客户端向代理服务器发送请求地址

4.代理服务器响应客户端请求(代理与远程服务器建立链接,代理服务器相应客户端请求)

5.客户端向代理传输数据

6.代理服务器将数据转发给远程服务器

7.远程服务器将数据发送到代理服务器

8.代理服务器将数据转发给客户端

 

(一)客户端发送的报头

VERSION METHODS_COUNT METHODS
1字节 1字节 1到255字节,长度由METHODS_COUNT值决定
0x05 0x03 ……

  VERSION:socks 版本,这里用的是 socks5,所以是0x05。

  METHODS_COUNT: METHODS 部分的长度。

  METHODS:代表客户端拥有的加密方式。每个方法占 1 字节。当前的定义是:

  0x00 不加密
  0x01 GSSAPI
  0x02 用户名、密码认证
  0x03 - 0x7F 由IANA分配(保留)
  0x80 - 0xFE 为私人方法保留
  0xFF 无可接受的方法

// 客户端认证请求
typedef struct client_license_request {

	char ver;			// 客户端的协议版本号  0x05:socks5 0x04:socks4
	char nmethods;		// 客户端所支持认证方式的长度
	char methods[255];  // 客户端支持的认证方式(可以有255种)

}client_license_request;
// Client端 -- 发送认证信息
	client_license_request license_request;
	license_request = {0};
	license_request.ver = 0x5;

	send_len = send(s_server, (char *)&license_request, sizeof(license_request),0);
	if (send_len < 0) 
	{
		cout << "验证认证信息失败!" << endl;

	}
// 代理服务器端 -- 接收认证信息
int srlen = 0;
//接收认证信息

char buffer[257];
recv(fd, buffer, sizeof(buffer), 0);
client_license_request * license_request = (client_license_request *)buffer;

//验证认证信息
printf("客户端版本%d\n", license_request->ver);
if (license_request->ver != 0x5)
{
	printf("协议版本错误\n");
	return 0;
}

 

(二)代理服务器响应的报头

VERSION METHODS
1字节 1字节
0x05 从客户端发送的加密方式里面选一个

  VERSION:socks 版本,这里用的是 socks5,所以是0x05。
  METHODS:代表代理服务器选择了一种握手方式。占 1 字节。
  例如,代理服务器发送的 5 0,代表 版本5 选择了“不加密”的握手方式。

  如果客户端的所有握手方式代理服务器都不满足,直接断开连接。

  如果代理服务器发送 5 2,代表 版本5 选择了“用户名、密码认证”的握手方式。此时客户端会发送账号密码数据给代理服务器,再由代理服务器检验,并返回结果。格式如下:

VERSION USERNAME_LENGTH USERNAME PASSWORD_LENGTH PASSWORD
1字节 1字节 1-255字节 1字节 1-255字节
0x01 0x01 …… 0x01 ……

VERSION:认证子协商版本(与 SOCKS 协议版本的0x05无关系)
USERNAME_LENGTH:用户名长度
USERNAME:用户名字节数组,长度为 USERNAME_LENGTH
PASSWORD_LENGTH:密码长度
PASSWORD:密码字节数组,长度为 PASSWORD_LENGTH

VERSION USERNAME_LENGTH
1字节 1字节
0x01 0x01

     VERSION:认证子协商版本,与客户端 VERSION 字段一致

   STATUS:认证结果(0x00 认证成功 / 大于0x00 认证失败)

// 服务端回应认证
typedef struct server_license_response {
	char ver;     // 服务端的协议版本号
	char method;  // 服务端选择的认证方式
}server_license_response;
// 代理服务器端 -- 响应认证信息
server_license_response license_response;
license_response.ver = 0x5;
license_response.method = 0x0;
char buff[2] = { 0 };
memcpy(buff, &license_response, sizeof(buff));

//回应认证信息
srlen = send(fd, buff, sizeof(buff), 0);
if (srlen <= 0)
{

}
printf("已发送回应请求\n");

 

// Client端 -- 接收代理服务器的回应
server_license_response license_response;
license_response = { 0 };
recv(s_server,(char*)&license_response, sizeof(license_response), 0);
if (license_response.ver != 0x5 || license_response.method != 0x0)
{
	cout << "代理服务器回应认证失败!" << endl;

}

 

(三)客户端发送需要访问的IP和端口,以及协议

VERSION COMMAND RSV ADDRESS_TYPE DST.ADDR DST.PORT
1字节 1字节 1字节 1字节 可变成长度 2字节

VERSION:SOCKS 协议版本,固定 0x05
COMMAND:命令
  0x01:CONNECT请求,连接上游服务器(使用TCP)
  0x02:BIND 绑定,客户端会接收来自代理服务器的链接,著名的FTP被动模式
  0x03:UDP ASSOCIATE UDP 中继(UDP 转发)
RSV:保留字段,无实际作用
ADDRESS_TYPE:目标服务器地址类型
  0x01:表示 IPv4 地址
  0x03:域名地址(没有打错,就是没有0x02)
  0x04:IPv6 地址
DST.ADDR:目标服务器地址(如果是ipv6,该字段的第一个字节是域名长度,剩下字节为域名)
DST.PORT:目标服务器端口

// 客户端连接请求
#pragma pack(1)
typedef struct client_connect_request {
	char ver;			//客户端协议版本号
	char cmd;			//连接方式
	char rsv = 0x00;    //保留位0x00
	char type;			//类型
	char addr[20] = "10.18.33.21"; //目的服务器ip
	char port[6] = "2019";			//目的服务器端口
}client_connect_request;
// Client端 -- 向代理服务器发送请求
client_connect_request connect_request;
connect_request.ver = 0x5;
connect_request.cmd = 0x1;
connect_request.type = 0x01;

send_len = send(s_server,(char *)&connect_request, sizeof(client_connect_request) , 0);
if (send_len < 0)
{
	cout << "向代理服务器发送请求失败!" << endl;
}

 

(四)代理服务器响应

VERSION RESPONSE RSV ADDRESS_TYPE BND.ADDR BND.PORT
1字节 1字节 1字节 1字节 1-255字节 2字节

VERSION:SOCKS 协议版本,固定 0x05
RESPONSE:响应命令
  0x00:代理服务器连接目标服务器成功
  0x01:代理服务器故障
  0x02:代理服务器规则集不允许连接
  0x03:网络无法访问
  0x04:目标服务器无法访问(主机名无效)
  0x05:连接目标服务器被拒绝
  0x06:TTL已过期
  0x07:不支持的命令
  0x08:不支持的目标服务器地址类型
  0x09 - 0xFF:未分配
RSV:保留字段
ADDRESS_TYPE:后面的地址类型
  0x01:ipv4
  0x03:域名
  0x04:ipv6
BND.ADDR:代理服务器连接目标服务器成功后的代理服务器 IP
BND.PORT:代理服务器连接目标服务器成功后的代理服务器端口

// 代理服务器端 -- 接收IP与PORT,链接目标机,发回成功信息给 Client
char buf[4096];
srlen = recv(fd, buf, 4, 0);		// 03 05 00 01
if (srlen <= 0)
{

}
if (srlen <= 0) return -1;
if (srlen < 4) return 0;

if (buf[0] != 0x05 || buf[2] != 0x00)
{

}

int client_fd = 0;

char ip4[256], port[5];
int re = -1;
if (buf[3] == 0x04)
{ // 如果是 ipv6
    // ...
    return 0;
}
else if (buf[3] == 0x01) { // 如果是 ipv4
    srlen = recv(fd, ip4, 4, 0);
    srlen = recv(fd, port, 2, 0);

    ip4[4] = '\0';
    port[2] = '\0';

    client_fd = socket(AF_INET, SOCK_STREAM, 0);
    struct sockaddr_in server;
    server.sin_family = AF_INET;
    memcpy(&server.sin_addr.s_addr, ip4, 4);
    server.sin_port = *((uint16_t*)port);

    re = connect(client_fd, (struct sockaddr*)&server, sizeof(server));
    if (re == -1)
    {

    }
    // ...
}
else if (buf[3] == 0x03) { // 是用域名表示的
    // 域名字段中第一个字节是真实的域名的长度,后面才是真实的域名
    char doname_len;
    char doname[256];
    srlen = recv(fd, &doname_len, 1, 0);

    if (len < 1) return 0;

    len = recv(fd, doname, doname_len, 0);
    doname[len] = '\0';

    struct hostent* host = gethostbyname(doname);

    if (host != nullptr)
    {
        memcpy(ip4, host->h_addr, host->h_length);
        len = recv(fd, port, 2, 0);

        client_fd = socket(AF_INET, SOCK_STREAM, 0);
        struct sockaddr_in server;
        server.sin_family = AF_INET;
        memcpy(&server.sin_addr.s_addr, ip4, 4);
        server.sin_port = *((uint16_t*)port);

        re = connect(client_fd, (struct sockaddr*)&server, sizeof(server));
        if (re == -1)
        {

        }
    }
}
else
{
}

//成功连接则发送回应信息
//回应连接信息
char buffer1[10] = { 0 };

server_connect_response connect_response = { 0 };

connect_response.ver = 0x5;
connect_response.rep = 0x00;  //连接成功标志
connect_response.rsv = 0x00;
connect_response.type = 0x01;

memcpy(buffer1, &connect_response, sizeof(connect_response));//服务端回应数据 设置版本号与结果位,ip与端口号未使用
send(fd, buffer1, sizeof(buffer1), 0);

printf("已发送回应请求\n");

if (client_fd != 0 && re != -1)
{
    ForwardData(fd, client_fd);
}
// Client -- 接收代理服务器链接消息是否成功
server_connect_response connect_response;
connect_response = { 0 };
recv_len = recv(s_server, (char*)&connect_response, sizeof(connect_response), 0);
if (recv_len < 0)
{
    cout << "代理服务器接收请求失败!" << endl;

}
else if(connect_response.ver != 0x5 || connect_response.rep != 0x00 || connect_response.rsv != 0x00 || connect_response.type != 0x01)
{
    cout << "代理服务器接收请求失败!" << endl;

}
else
{
    cout << "向代理服务器发送请求成功!" << endl;
}

 

(五)通信

// Client端 -- 测试:发送数据给代理服务器,再接收代理服务器发送的数据
char recv_buffer[BUFF_SIZE]; 
char tmp[] = "Hello";
memcpy(recv_buffer, tmp, sizeof(tmp)+1);
send_len = send(s_server, (char *)&recv_buffer, sizeof(recv_buffer), 0);

char buff3[100];
recv_len = recv(s_server, (char*)&buff3, sizeof(buff3), 0);
printf(buff3);
//代理服务器进行数据转发
int ForwardData(int sock, int real_server_sock)		// sock:链接Client的socket; real_server_sock:链接内网的socket
{
	char recv_buffer[4096] = { 0 };
	fd_set fd_read;
	struct timeval time_out;
	time_out.tv_sec = 0;
	time_out.tv_usec = TIME_OUT;
	int ret = 0;
	printf("线程%u-开始进行数据转发\n", (int)GetCurrentThread());

	fd_set fd_read2;
	int ret2;

	while (1)
	{
		FD_ZERO(&fd_read);
		FD_SET(sock, &fd_read);
		FD_SET(real_server_sock, &fd_read);
		ret = select((sock < real_server_sock ? sock : real_server_sock) + 1, &fd_read, NULL, NULL, &time_out);	//&time_out
		if (-1 == ret)
		{
			break;
		}
		else if (0 == ret)
		{
			continue;
		}
		if (FD_ISSET(sock, &fd_read))
		{
			memset(recv_buffer, 0, 4096);
			ret = recv(sock, recv_buffer, 4096, 0);
			if (ret > 0)
			{
				printf("从浏览器 %d 接收数据\r\n", sock);
				ret = send(real_server_sock, recv_buffer, ret, 0);
				if (ret == -1)
				{
					break;
				}
				printf("向内网 %d 发送数据\r\n", real_server_sock);
			}
			else if (ret == 0)
			{
				break;
			}
			else
			{
				break;
			}
		}
		else if (FD_ISSET(real_server_sock, &fd_read))
		{
			memset(recv_buffer, 0, 4096);
			ret = recv(real_server_sock, recv_buffer, 4096, 0);
			if (ret > 0)
			{
				printf("从内网 %d 接收数据\r\n", real_server_sock);
				ret = send(sock, recv_buffer, ret, 0);
				if (ret == -1)
				{
					break;
				}
				printf("向浏览器 %d 发送数据\r\n", sock);
			}
			else if (ret == 0)
			{
				break;
			}
			else
			{
				break;
			}
		}
	}
	return 0;
}

 

二、源代码

注意:本文中程序仅适用于Windows端

// 代理服务器端
#include<iostream>
#include<winsock.h>
#pragma comment(lib,"ws2_32.lib")

#define _WINSOCK_DEPRECATED_NO_WARNINGS


#define BUFF_SIZE 1024   // 设置转发缓冲区
#define TIME_OUT 6000000 // 设置复用IO延时

unsigned int __g1 = 0;
CRITICAL_SECTION __CriticalSection;

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++*/


//++++++++++++     sock5协议结构体定义     ++++++++++++++


/*++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
/*
一、客户端认证请求
	+----+----------+----------+
	|VER | NMETHODS | METHODS  |
	+----+----------+----------+
	| 1  |    1     |  1~255   |
	+----+----------+----------+
二、服务端回应认证
	+----+--------+
	|VER | METHOD |
	+----+--------+
	| 1  |   1    |
	+----+--------+
三、客户端连接请求(连接目的网络)
	+----+-----+-------+------+----------+----------+
	|VER | CMD |  RSV  | ATYP | DST.ADDR | DST.PORT |
	+----+-----+-------+------+----------+----------+
	| 1  |  1  |   1   |  1   | Variable |    2     |
	+----+-----+-------+------+----------+----------+
四、服务端回应连接
	+----+-----+-------+------+----------+----------+
	|VER | REP |  RSV  | ATYP | BND.ADDR | BND.PORT |
	+----+-----+-------+------+----------+----------+
	| 1  |  1  |   1   |  1   | Variable |    2     |
	+----+-----+-------+------+----------+----------+
*/

//以下为协议结构体定义

//一、客户端认证请求
typedef struct client_license_request {

	char ver;       // 客户端的协议版本号  0x05:socks5 0x04:socks4
	char nmethods;    // 客户端所支持认证方式的长度
	char methods[255];  //客户端支持的认证方式(可以有255种)

}client_license_request;


//二、服务端回应认证
typedef struct server_license_response {
	char ver;     // 服务端的协议版本号
	char method;  //服务端选择的认证方式
}server_license_response;

//三、客户端连接请求
typedef struct client_connect_request {
	char ver;    //客户端协议版本号
	char cmd;    //连接方式
	char rsv;    //保留位0x00
	char type;   //类型
	char addr[20]; //目的服务器ip
	char port[6]; //目的服务器端口
}client_connect_request;


//四、服务端回应连接
typedef struct server_connect_response {
	char ver; //版本
	char rep; //连接状态
	char rsv; //保留0x00
	char type; //类型
	char addr[4]; //bind ip
	char port[2]; //bind port


}server_connect_response;

int socketfd_tcp;  //TCP监听套接字


struct thread_parameter
{
	struct sockaddr_in *addr_client;
	int len;
	int fd;
};


//代理服务器进行数据转发
int ForwardData(int sock, int real_server_sock)		// sock:链接Client的socket; real_server_sock:链接内网的socket
{
	char recv_buffer[4096] = { 0 };
	fd_set fd_read;
	struct timeval time_out;
	time_out.tv_sec = 0;
	time_out.tv_usec = TIME_OUT;
	int ret = 0;
	printf("线程%u-开始进行数据转发\n", (int)GetCurrentThread());

	fd_set fd_read2;
	int ret2;

	while (1)
	{
		FD_ZERO(&fd_read);
		FD_SET(sock, &fd_read);
		FD_SET(real_server_sock, &fd_read);
		ret = select((sock < real_server_sock ? sock : real_server_sock) + 1, &fd_read, NULL, NULL, &time_out);	//&time_out
		if (-1 == ret)
		{
			break;
		}
		else if (0 == ret)
		{
			continue;
		}
		if (FD_ISSET(sock, &fd_read))
		{
			memset(recv_buffer, 0, 4096);
			ret = recv(sock, recv_buffer, 4096, 0);
			if (ret > 0)
			{
				printf("从浏览器 %d 接收数据\r\n", sock);
				ret = send(real_server_sock, recv_buffer, ret, 0);
				if (ret == -1)
				{
					break;
				}
				printf("向内网 %d 发送数据\r\n", real_server_sock);
			}
			else if (ret == 0)
			{
				break;
			}
			else
			{
				break;
			}
		}
		else if (FD_ISSET(real_server_sock, &fd_read))
		{
			memset(recv_buffer, 0, 4096);
			ret = recv(real_server_sock, recv_buffer, 4096, 0);
			if (ret > 0)
			{
				printf("从内网 %d 接收数据\r\n", real_server_sock);
				ret = send(sock, recv_buffer, ret, 0);
				if (ret == -1)
				{
					break;
				}
				printf("向浏览器 %d 发送数据\r\n", sock);
			}
			else if (ret == 0)
			{
				break;
			}
			else
			{
				break;
			}
		}
	}
	return 0;


}




// 一、初始化套接字库(WSAStartup)
void initialization()
{
	WORD w_req = MAKEWORD(2, 2);// 版本号
	WSADATA wsadata;
	int err;

	err = WSAStartup(w_req, &wsadata);	// WSAStartup 函数启动进程对 Winsock DLL的使用
	if (err != 0)
	{
		std::cout << "初始化套接字库失败!" << std::endl;
	}
	else {
		std::cout << "初始化套接字库成功!" << std::endl;
	}

	// 检测版本号
	if (LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wHighVersion) != 2) {
		std::cout << "套接字库版本号不符!" << std::endl;
		WSACleanup();
	}
	else {
		std::cout << "套接字库版本正确!" << std::endl;
	}
}

//创建TCP套接字
void tcp_creat()
{
	socketfd_tcp = socket(AF_INET, SOCK_STREAM, 0);
	if (socketfd_tcp == -1)
	{
		perror("socketfd_tcp");
		exit(-1);
	}


	struct sockaddr_in addr_tcp = {0};
	//bzero(&addr_tcp, sizeof(addr_tcp));

	addr_tcp.sin_family = AF_INET;
	addr_tcp.sin_port = htons(80);
	addr_tcp.sin_addr.s_addr = INADDR_ANY;

	int re = bind(socketfd_tcp, (struct sockaddr *)&addr_tcp, sizeof(addr_tcp));
	if (re == -1)
	{
		perror("bind");
		exit(-1);
	}

	re = listen(socketfd_tcp, 100);  //队列长度设为100
	if (re == -1)
	{
		perror("listen");
		exit(-1);
	}

}

//代理服务器连接目的服务器
int connect_dest_server(client_connect_request * connect_request)
{
	int fd = socket(AF_INET, SOCK_STREAM, 0);
	if (fd == -1)
	{
		perror("socketfd_tcp");
		return -1;
	}

	struct sockaddr_in sin_server = { 0 };
	//bzero(&sin_server, sizeof(sin_server));
	sin_server.sin_family = AF_INET;
	sin_server.sin_addr.S_un.S_addr = inet_addr(connect_request->addr);
	u_short port = atoi(connect_request->port);
	sin_server.sin_port = htons(port);

	/*2 连接服务器*/
	int re = connect(fd, (struct sockaddr *)&sin_server, sizeof(sin_server));
	if (re == -1)
	{
		// printf("目的服务器连接失败\n");
		return -1;
	}
	// printf("目的服务器连接成功\n");
	return fd;
}



//socks5认证连接
int sock5_license(struct sockaddr_in *addr_client, int len, int fd)
{
	int srlen = 0;
	//接收认证信息

	char buffer[257];
	recv(fd, buffer, sizeof(buffer), 0);
	client_license_request * license_request = (client_license_request *)buffer;

	//验证认证信息
	printf("客户端版本%d\n", license_request->ver);
	if (license_request->ver != 0x5)
	{
		printf("协议版本错误\n");
		return 0;
	}
	printf("客户认证信息通过,回应认证请求\n");

	server_license_response license_response;
	license_response.ver = 0x5;
	license_response.method = 0x0;
	char buff[2] = { 0 };
	memcpy(buff, &license_response, sizeof(buff));

	//回应认证信息
	srlen = send(fd, buff, sizeof(buff), 0);
	if (srlen <= 0)
	{

	}
	printf("已发送回应请求\n");

	char buf[4096];
	srlen = recv(fd, buf, 4, 0);		// 03 05 00 01
	if (srlen <= 0)
	{

	}
	if (srlen <= 0) return -1;
	if (srlen < 4) return 0;

	if (buf[0] != 0x05 || buf[2] != 0x00)
	{

	}

	int client_fd = 0;

	char ip4[256], port[5];
	int re = -1;
	if (buf[3] == 0x04)
	{ // 如果是 ipv6
		// ...
		return 0;
	}
	else if (buf[3] == 0x01) { // 如果是 ipv4
		srlen = recv(fd, ip4, 4, 0);
		srlen = recv(fd, port, 2, 0);

		ip4[4] = '\0';
		port[2] = '\0';

		client_fd = socket(AF_INET, SOCK_STREAM, 0);
		struct sockaddr_in server;
		server.sin_family = AF_INET;
		memcpy(&server.sin_addr.s_addr, ip4, 4);
		server.sin_port = *((uint16_t*)port);

		re = connect(client_fd, (struct sockaddr*)&server, sizeof(server));
		if (re == -1)
		{

		}
		// ...
	}
	else if (buf[3] == 0x03) { // 是用域名表示的
	 // 域名字段中第一个字节是真实的域名的长度,后面才是真实的域名
		char doname_len;
		char doname[256];
		srlen = recv(fd, &doname_len, 1, 0);

		if (len < 1) return 0;

		len = recv(fd, doname, doname_len, 0);
		doname[len] = '\0';

		struct hostent* host = gethostbyname(doname);

		if (host != nullptr)
		{
			memcpy(ip4, host->h_addr, host->h_length);
			len = recv(fd, port, 2, 0);

			client_fd = socket(AF_INET, SOCK_STREAM, 0);
			struct sockaddr_in server;
			server.sin_family = AF_INET;
			memcpy(&server.sin_addr.s_addr, ip4, 4);
			server.sin_port = *((uint16_t*)port);

			re = connect(client_fd, (struct sockaddr*)&server, sizeof(server));
			if (re == -1)
			{

			}
		}
	}
	else
	{
	}

	//成功连接则发送回应信息
	//回应连接信息
	char buffer1[10] = { 0 };

	server_connect_response connect_response = { 0 };

	connect_response.ver = 0x5;
	connect_response.rep = 0x00;  //连接成功标志
	connect_response.rsv = 0x00;
	connect_response.type = 0x01;

	memcpy(buffer1, &connect_response, sizeof(connect_response));//服务端回应数据 设置版本号与结果位,ip与端口号未使用
	send(fd, buffer1, sizeof(buffer1), 0);

	printf("已发送回应请求\n");

	if (client_fd != 0 && re != -1)
	{
		ForwardData(fd, client_fd);
	}
}



// 等待TCP连接,每个客户端分出一条线程,跳转执行socks5认证函数↑↑↑
DWORD WINAPI pthread_tcp(LPVOID ParameterData)
{
	thread_parameter parameter = *(thread_parameter *)ParameterData;

	//打印客户端信息
	//char ip[20] = { 0 };
	//unsigned short port;
	////inet_ntop(AF_INET, &addr_client.sin_addr, ip, len);
	//port = ntohs(addr_client.sin_port); //转换为本机字节序
	//printf("%s:%hu已连接\n", ip, port);

	//执行socks5认证
	sock5_license(parameter.addr_client, parameter.len, parameter.fd);
	printf("线程%d-退出\n", (HANDLE)GetCurrentThread());

	return NULL;

}


int main(void)
{
	// 一、初始化套接字库(WSAStartup)
	initialization();
	//创建TCP套接字
	tcp_creat();
	printf("初始化完成等待连接\n");

	while (1)
	{
		printf("线程%d-正在运行\n", (HANDLE)GetCurrentThread());
		struct sockaddr_in addr_client = { 0 };
		int len = sizeof(addr_client);

		int fd = accept(socketfd_tcp, (struct sockaddr *)&addr_client, &len);

		//thread_parameter parameter;
		//parameter.addr_client = &addr_client;
		//parameter.fd = fd;
		//parameter.len = len;
		//CreateThread(NULL, NULL, pthread_tcp, &parameter, NULL, NULL);
		sock5_license(&addr_client, len, fd);
	}

	//释放DLL资源
	WSACleanup();
}
// Client端
#include<iostream>
#include<winsock.h>
#pragma comment(lib,"ws2_32.lib")

#define _WINSOCK_DEPRECATED_NO_WARNINGS
#define BUFF_SIZE 1024   // 设置转发缓冲区

using namespace std;

//以下为协议结构体定义

//一、客户端认证请求
typedef struct client_license_request {

	char ver;			// 客户端的协议版本号  0x05:socks5 0x04:socks4
	char nmethods;		// 客户端所支持认证方式的长度
	char methods[255];  //客户端支持的认证方式(可以有255种)

}client_license_request;


//二、服务端回应认证
typedef struct server_license_response {
	char ver;     // 服务端的协议版本号
	char method;  //服务端选择的认证方式
}server_license_response;

//三、客户端连接请求
#pragma pack(1)
typedef struct client_connect_request {
	char ver;			//客户端协议版本号
	char cmd;			//连接方式
	char rsv = 0x00;    //保留位0x00
	char type;			//类型
	char addr[20] = "10.18.33.21"; //目的服务器ip
	char port[6] = "2019";			//目的服务器端口
}client_connect_request;


//四、服务端回应连接
typedef struct server_connect_response {
	char ver; //版本
	char rep; //连接状态
	char rsv; //保留0x00
	char type; //类型
	char addr[4]; //bind ip
	char port[2]; //bind port


}server_connect_response;




// 一、初始化套接字库(WSAStartup)
void initialization()
{
	// 初始化套接字库
	WORD w_req = MAKEWORD(2, 2);// 版本号

	WSADATA wsadata;
	int err;
	err = WSAStartup(w_req, &wsadata);	// WSAStartup 函数启动进程对 Winsock DLL的使用
	if (err != 0)
	{
		cout << "初始化套接字库失败!" << endl;
	}
	else
	{
		cout << "初始化套接字库成功!" << endl;
	}

	//检测版本号
	if (LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wHighVersion) != 2) {
		cout << "套接字库版本号不符!" << endl;
		WSACleanup();
	}
	else {
		cout << "套接字库版本正确!" << endl;
	}
}

int main()
{
	// 定义长度变量
	int send_len = 0;
	int recv_len = 0;

	// 定义服务端套接字
	SOCKET s_server;

	// 服务端地址
	SOCKADDR_IN server_addr;

	// 一、初始化套接字库(WSAStartup)
	initialization();

	// -----------------------------------------------------------------------------------------

	// 二、填充服务端协议地址信息
	server_addr.sin_family = AF_INET;								// 设置服务器地址家族
	server_addr.sin_addr.S_un.S_addr = inet_addr("192.168.37.129");	// 主机 IPv4 地址,要跟哪个Sock5代理服务器通信
	server_addr.sin_port = htons(2018);								// 设置Sock5代理服务器端口号(与Sock5代理服务器一致)

	// -----------------------------------------------------------------------------------------

	// 三、创建套接字、 connect:客户端请求服务端连接

	s_server = socket(AF_INET, SOCK_STREAM, 0);			// 创建套接字(流套接字)

	if (connect(s_server, (SOCKADDR *)&server_addr, sizeof(SOCKADDR)) == SOCKET_ERROR)	// connect客户端请求服务端连接
	{
		cout << "服务器连接失败!" << endl;

		WSACleanup();
	}
	else 
	{
		cout << "服务器连接成功!" << endl;
	}

	// -----------------------------------------------------------------------------------------


	// 四、发送,接收数据(send/recv)



	// 发送认证信息
	client_license_request license_request;
	license_request = {0};
	license_request.ver = 0x5;

	send_len = send(s_server, (char *)&license_request, sizeof(license_request),0);
	if (send_len < 0) 
	{
		cout << "验证认证信息失败!" << endl;

	}

	// 接收代理服务器的回应
	server_license_response license_response;
	license_response = { 0 };
	recv(s_server,(char*)&license_response, sizeof(license_response), 0);
	if (license_response.ver != 0x5 || license_response.method != 0x0)
	{
		cout << "代理服务器回应认证失败!" << endl;

	}

	// 向代理服务器发送请求
	client_connect_request connect_request;
	connect_request.ver = 0x5;
	connect_request.cmd = 0x1;
	connect_request.type = 0x01;

	send_len = send(s_server,(char *)&connect_request, sizeof(client_connect_request) , 0);
	if (send_len < 0)
	{
		cout << "向代理服务器发送请求失败!" << endl;

	}

	// 接收代理服务器是否成功
	server_connect_response connect_response;
	connect_response = { 0 };
	recv_len = recv(s_server, (char*)&connect_response, sizeof(connect_response), 0);
	if (recv_len < 0)
	{
		cout << "代理服务器接收请求失败!" << endl;

	}
	else if(connect_response.ver != 0x5 || connect_response.rep != 0x00 || connect_response.rsv != 0x00 || connect_response.type != 0x01)
	{
		cout << "代理服务器接收请求失败!" << endl;

	}
	else
	{
		cout << "向代理服务器发送请求成功!" << endl;
	}

	// -----------------------------------------------------------------------------------------


	char recv_buffer[BUFF_SIZE]; 
	char tmp[] = "Hello";
	memcpy(recv_buffer, tmp, sizeof(tmp)+1);

	send_len = send(s_server, (char *)&recv_buffer, sizeof(recv_buffer), 0);

	char buff3[100];
	recv_len = recv(s_server, (char*)&buff3, sizeof(buff3), 0);

	printf(buff3);

exit:
	//五、关闭套接字
	closesocket(s_server);

	//六、释放DLL资源
	WSACleanup();
	return 0;
}

 

 

posted @ 2022-08-16 18:35  人类观察者  阅读(1875)  评论(0编辑  收藏  举报