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;
}

SOCKS5协议解析的更多相关文章

  1. SOCKS5 协议解析

    代理 根据 HTTP 1.1 的定义,proxy 是: An intermediary program which acts as both a server and a client for the ...

  2. HTTP协议和SOCKS5协议

    HTTP协议和SOCKS5协议 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 我们平时上网的时候基本上是离不开浏览器的,尤其是搜索资料的时候,那么这个浏览器是如何工作的呢?用的又是 ...

  3. ts 协议解析

    pes : http://wenku.baidu.com/link?url=KjcA0qXqZ1bWVQTa8i1YOmygofldSQL7Pjj-zGRw1e_6_LFmVLo5DIWF0SNwVn ...

  4. [转]netty对http协议解析原理

    本文主要介绍netty对http协议解析原理,着重讲解keep-alive,gzip,truncked等机制,详细描述了netty如何实现对http解析的高性能. 1 http协议 1.1 描述 标示 ...

  5. twemproxyRedis协议解析探索——剖析twemproxy代码正编

    这篇文章会对twemproxyRedis协议解析代码部分进行一番简单的分析,同时给出twemproxy目前支持的所有Redis命令.在这篇文章开始前,我想大家去简单地理解一下有限状态机,当然不理解也是 ...

  6. B/S 架构中,网络模型的分解与协议解析

    前言 如果是C/S专业毕业的或者是学过计算机网络课程的童鞋们,相信大家都知道网络模型的划分,本文首先来聊一聊目前对于B/S结构中,网络模型分解的两种方式. 没错,相信大家看到这个图片的时候就已经明白了 ...

  7. 详解BLE 空中包格式—兼BLE Link layer协议解析

    BLE有几种空中包格式?常见的PDU命令有哪些?PDU和MTU的区别是什么?DLE又是什么?BLE怎么实现重传的?BLE ACK机制原理是什么?希望这篇文章能帮你回答以上问题. 虽然BLE空中包(pa ...

  8. netty对http协议解析原理解析

    本文主要介绍netty对http协议解析原理,着重讲解keep-alive,gzip,truncked等机制,详细描述了netty如何实现对http解析的高性能. 1 http协议 1.1 描述 标示 ...

  9. MODBUS协议解析中常用的转换帮助类(C#)

    p{ text-align:center; } blockquote > p > span{ text-align:center; font-size: 18px; color: #ff0 ...

  10. AOSP中的HLS协议解析

    [时间:2018-04] [状态:Open] [关键词:流媒体,stream,HLS, AOSP, 源码分析,HttpLiveSource, LiveSession,PlaylistFetcher] ...

随机推荐

  1. kafka 集群环境搭建

    本文为博主原创,未经允许不得转载: 如果搭建单机节点 kafka 可看我的这篇博客: 搭建 kafka 集群环境,只需要在搭建单机 kafka 环境的基础上,多增加几个kafka 服务实例即可. 多增 ...

  2. mysql too many connections 解决

    本文为博主原创,转载请注明出处: 由于在开发过程中,很多人连接共同一个数据库,在工具连接到mysql, 并执行sql时,提示 too many connections ,这是由于数据库连接太多,以致于 ...

  3. 3. Oracle数据库异常关闭,导致错误3. Oracle数据库异常关闭,导致错误ERROR: ORA-01034: ORACLE ngt available; ORA-27101: shared memory realm does not exist

    之前由于电脑没电,强制关机,导致Oracle数据库异常关闭,再次启动电脑登陆数据库时,发生以下错误: 当我尝试重新启动数据库时,发生错误: 经过查阅资料后得知:缺少INITXE.ORA文件,需要从下图 ...

  4. k8s~istio的安装与核心组件

    安装istio 在线安装:https://istio.io/latest/docs/setup/getting-started/#download 或者直接在这里下载:https://github.c ...

  5. [转帖]Linux内存之Cache

    一. Linux内存之Cache 1.1.Cache 1.1.1.什么是Cache? Cache存储器,是位于CPU和主存储器DRAM之间的一块高速缓冲存储器,规模较小,但是速度很快,通常由SRAM( ...

  6. [转帖]通过拓扑 label 进行副本调度

    https://docs.pingcap.com/zh/tidb/stable/schedule-replicas-by-topology-labels#%E5%9F%BA%E4%BA%8E%E6%8 ...

  7. KylinV10升级部分软件的简单方法

    背景 2022-12-26有同事晚上在群里反馈客户现场的测试环境内存紧张. 我这边第一反应是进程重复了,导致内存使用量飙升. 告知现场使用 ps -ef |grep java |grep caf 发现 ...

  8. Redis labs 的安装

    Install and setup This guide shows how to install Redis Enterprise Software, which includes several ...

  9. CS231N Assignment1 SVM 笔记

    svm.ipynb 为SVM实现一个完全矢量化的损失函数 为其解析梯度实现完全矢量化表达式 使用数值梯度检查实现结果 使用验证集调整学习率和正则化 使用 SGD 优化损失函数 可视化最终学习权重 第一 ...

  10. Skia 编译及踩坑实践

    本文要点 •了解并入门 Skia.OpenGL 和 Vulkan •了解 Skia 在后端渲染上的坑点 前言 Skia 是什么 Skia 是一个开源 2D 图形库,提供可跨各种硬件和软件平台工作的通用 ...