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. SpringBoot Serverless 实战 | 监控调试

    SpringBoot 是基于 Java Spring 框架的套件,它预装了 Spring 的一系列组件,让开发者只需要很少的配置就可以创建独立运行的应用程序.在云原生的世界,有大量的平台可以运行 Sp ...

  2. ava进阶(39)--守护线程与定时器

    文档目录: 一.守护线程 二.定时器 ---------------------------------------分割线:正文------------------------------------ ...

  3. python常见面试题讲解(十一)字符串反转-五种解法

    题目描述 写出一个程序,接受一个字符串,然后输出该字符串反转后的字符串.(字符串长度不超过1000) 输入描述: 输入N个字符 输出描述: 输出该字符串反转后的字符串 示例1 输入 abcd 输出 d ...

  4. Redis 哨兵模式高可用

    本文为博主原创,未经允许不得转载: 目录: 1. 哨兵 Sentinel 介绍 2. 哨兵架构特点及工作原理 3. redis哨兵架构搭建步骤 4. 哨兵数据丢失 5. spring boot 整合  ...

  5. C++数据结构(树)

    树是一种递归定义的数据结构,如果树中节点的各子树从左到右是有次序的,不能互换,则称该树为有序树,否则叫无序树. 关于树的节点: 节点拥有的子树的个数叫做节点的度 如果度为0,那么该节点叫做叶节点或终端 ...

  6. B2033 A*B 问题

    A*B 问题 题目描述 输入两个正整数 \(A\) 和 \(B\),求 \(A \times B\) 的值.注意乘积的范围和数据类型的选择. 输入格式 一行,包含两个正整数 \(A\) 和 \(B\) ...

  7. java - classpath 的配置

    classpath C:\Program Files\Java\jdk\jre\lib\rt.jar

  8. .NET技术面试题系列(2) -sql server数据库优化规范

    1.数据库优化规范 a.索引 每个表格都要求建立主键,主键上不一定需要强制建立聚集索引. 聚集索引,表中存储的数据按照索引的顺序存储,即逻辑顺序决定了表中相应行的物理顺序,因此聚集索引的字段值应是不会 ...

  9. [转帖]oracle ZHS16GBK的数据库导入到字符集为AL32UTF8的数据库(转载+自己经验总结)

    字符集子集向其超集转换是可行的,如此例 ZHS16GBK转换为AL32UTF8. 导出使用的字符集将会记录在导出文件中,当文件导入时,将会检查导出时使用的字符集设置,如果这个字符集不同于导入客户端的N ...

  10. K8S Only IPV6的创建过程之二 完整版

    K8S Only IPV6的创建过程之二 完整版 整体过程 1. 阿里云新增一台虚拟机, 开通IPV6.与数据库,redis实现物理隔离. 并且进行环境基本处理,安装kubeadm等组件. 2. ku ...