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, ¶meter, 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协议解析的更多相关文章
- SOCKS5 协议解析
代理 根据 HTTP 1.1 的定义,proxy 是: An intermediary program which acts as both a server and a client for the ...
- HTTP协议和SOCKS5协议
HTTP协议和SOCKS5协议 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 我们平时上网的时候基本上是离不开浏览器的,尤其是搜索资料的时候,那么这个浏览器是如何工作的呢?用的又是 ...
- ts 协议解析
pes : http://wenku.baidu.com/link?url=KjcA0qXqZ1bWVQTa8i1YOmygofldSQL7Pjj-zGRw1e_6_LFmVLo5DIWF0SNwVn ...
- [转]netty对http协议解析原理
本文主要介绍netty对http协议解析原理,着重讲解keep-alive,gzip,truncked等机制,详细描述了netty如何实现对http解析的高性能. 1 http协议 1.1 描述 标示 ...
- twemproxyRedis协议解析探索——剖析twemproxy代码正编
这篇文章会对twemproxyRedis协议解析代码部分进行一番简单的分析,同时给出twemproxy目前支持的所有Redis命令.在这篇文章开始前,我想大家去简单地理解一下有限状态机,当然不理解也是 ...
- B/S 架构中,网络模型的分解与协议解析
前言 如果是C/S专业毕业的或者是学过计算机网络课程的童鞋们,相信大家都知道网络模型的划分,本文首先来聊一聊目前对于B/S结构中,网络模型分解的两种方式. 没错,相信大家看到这个图片的时候就已经明白了 ...
- 详解BLE 空中包格式—兼BLE Link layer协议解析
BLE有几种空中包格式?常见的PDU命令有哪些?PDU和MTU的区别是什么?DLE又是什么?BLE怎么实现重传的?BLE ACK机制原理是什么?希望这篇文章能帮你回答以上问题. 虽然BLE空中包(pa ...
- netty对http协议解析原理解析
本文主要介绍netty对http协议解析原理,着重讲解keep-alive,gzip,truncked等机制,详细描述了netty如何实现对http解析的高性能. 1 http协议 1.1 描述 标示 ...
- MODBUS协议解析中常用的转换帮助类(C#)
p{ text-align:center; } blockquote > p > span{ text-align:center; font-size: 18px; color: #ff0 ...
- AOSP中的HLS协议解析
[时间:2018-04] [状态:Open] [关键词:流媒体,stream,HLS, AOSP, 源码分析,HttpLiveSource, LiveSession,PlaylistFetcher] ...
随机推荐
- kafka 集群环境搭建
本文为博主原创,未经允许不得转载: 如果搭建单机节点 kafka 可看我的这篇博客: 搭建 kafka 集群环境,只需要在搭建单机 kafka 环境的基础上,多增加几个kafka 服务实例即可. 多增 ...
- mysql too many connections 解决
本文为博主原创,转载请注明出处: 由于在开发过程中,很多人连接共同一个数据库,在工具连接到mysql, 并执行sql时,提示 too many connections ,这是由于数据库连接太多,以致于 ...
- 3. Oracle数据库异常关闭,导致错误3. Oracle数据库异常关闭,导致错误ERROR: ORA-01034: ORACLE ngt available; ORA-27101: shared memory realm does not exist
之前由于电脑没电,强制关机,导致Oracle数据库异常关闭,再次启动电脑登陆数据库时,发生以下错误: 当我尝试重新启动数据库时,发生错误: 经过查阅资料后得知:缺少INITXE.ORA文件,需要从下图 ...
- k8s~istio的安装与核心组件
安装istio 在线安装:https://istio.io/latest/docs/setup/getting-started/#download 或者直接在这里下载:https://github.c ...
- [转帖]Linux内存之Cache
一. Linux内存之Cache 1.1.Cache 1.1.1.什么是Cache? Cache存储器,是位于CPU和主存储器DRAM之间的一块高速缓冲存储器,规模较小,但是速度很快,通常由SRAM( ...
- [转帖]通过拓扑 label 进行副本调度
https://docs.pingcap.com/zh/tidb/stable/schedule-replicas-by-topology-labels#%E5%9F%BA%E4%BA%8E%E6%8 ...
- KylinV10升级部分软件的简单方法
背景 2022-12-26有同事晚上在群里反馈客户现场的测试环境内存紧张. 我这边第一反应是进程重复了,导致内存使用量飙升. 告知现场使用 ps -ef |grep java |grep caf 发现 ...
- Redis labs 的安装
Install and setup This guide shows how to install Redis Enterprise Software, which includes several ...
- CS231N Assignment1 SVM 笔记
svm.ipynb 为SVM实现一个完全矢量化的损失函数 为其解析梯度实现完全矢量化表达式 使用数值梯度检查实现结果 使用验证集调整学习率和正则化 使用 SGD 优化损失函数 可视化最终学习权重 第一 ...
- Skia 编译及踩坑实践
本文要点 •了解并入门 Skia.OpenGL 和 Vulkan •了解 Skia 在后端渲染上的坑点 前言 Skia 是什么 Skia 是一个开源 2D 图形库,提供可跨各种硬件和软件平台工作的通用 ...