SOCK5代理服务的设计与实现

对于服务器而言,SOCK5协议的流程可用四个步骤解释:

协商 > 验证 > 接收请求 > 转发数据

SOCK5在RFC1928及RFC1929中有详细说明,RFC1928描述了SOCK5的“协商、接收请求”,RFC1929描述了SOCK5的“验证”过程。

对于上面的四个步骤,可简要地概述如下(以客户端对过代理connect远程主机为例):

1、 在SOCK5的协商过程中,客户端向服务器发送了SOCK代理的版本号(对于SOCK5而言当然就是0x05了),以及客户端支持的验证方法,服务器将会接收到以下数据结构的数据包:

-------------------------------------------------------------------------------------------

|       0x05       |      Len |      m.1 |      m.2 |      m.3 |      … …      |      m.n |

-------------------------------------------------------------------------------------------

0x05 :      是版本号,在“协商、验证、接收请求”的过程中都会在数据报头中

Len   :      批明了客户端支持的验证方法的数据,每个验证方法占用一个字节的空间

               常见的验证方法是“用户名 - 密码”、“不需要验证”,它们的代码分别为 0x02、0x00

如果客户端只支持以上两种验证的话,就会发送如下的数据包给服务器:

---------------------------------------------------------------------

|       0x05       |      0x02       |      0x00       |      0x02       |

---------------------------------------------------------------------

0                    1                   2                   3                   4

SOCK5代理服务器接收到客户端的信息后,会先检查版本号,如果为0x05的话,再检查是否支持客户的验证方式。根据上面的假设,客户端发送了0x00、0x02的验证代码号给服务器,SOCK5服务如果是需要“用户名 - 密码”的话,就返回0x02,如果是不需任何验证,就返回0x00,但如果这两种方式都不支持,那就返回0xff。回应客户端的数据包格式为:

                     ---------------------------------------

                     |      0x05       |      验证方法       |

                     ---------------------------------------

                     0                   1                          2

2、 如果上面的协商过程中,服务器所支持的是“用户名 - 密码”验证方式,那就需要进行验证过程,验证过程就是客户端发送密码给SOCK5服务器,服务器

判断其正确性再回应客户端的一个过程。

客户端发送验证信息的数据包格式如下:(假设“用户名长度为m”)

                     ------------------------------------------------------------------------------------------------

                     |      0x05       |      用户名长度    |      用户名    |      密码长度       |      密码       |

                     ------------------------------------------------------------------------------------------------

                     0                   1                          2                   m                         m+1               n

                     SOCK5服务器的应答数据包格式如下:

                     ---------------------------------------

                     |      0x05       |      状态码           |

                     ---------------------------------------

                     0                   1                          2

                     格式中的数据项与前面的“协商”部分相似,至于服务器发送出去的状态码,如果为 0x00 ,则表明验证成功,否则验证失败。

3、 进行了验证过程后(对于无需任何验证的0x00,不会经过上面2的步骤),服务器就等待接收客户要连接的远程主机了,客户端会发送如下结构的数据给SOCK5

代理服务器:

                     ------------------------------------------------------------------------------------------

                     |      0x05       |      cmd |      0x00       |      atyp |      dst_addr |      dst_port   |

                     ------------------------------------------------------------------------------------------

                     0                   1            2                   3            4

                     cmd        :    指明客户端请求代理服务做的动作,这里的例子为为TCP Connect连接(也是最为常见的动作),其值为 0x01

                     atyp        :    指明dst_addr的类型,对于IPv4地址,其值为 0x01

                     dst_addr :    在这里为IPv4地址,占4个字节

                     dst_port   :    要连接的远程主机的端口号,占2个字节

服务器接收收该请求后,就会连接远程主机,回应客户端如下数据结构的信息:

                     ------------------------------------------------------------------------------------------

                     |      0x05       |      rep   |      0x00       |      atyp |      dst_addr |      dst_port   |

                     ------------------------------------------------------------------------------------------

                     rep          :    如果服务器连接成功,其值为 0x00 ,其他的值可以参考RFC,也可以笼统地设置成 0xff

                     atyp        :    与客户端的值一样,为 0x01 (表示后面的地址类型为 IPv4)

                     dst_addr :    SOCK5服务器连接到远程主机的IP地址

                     dst_port   :    SOCK5服务器连接到远程主机的端口号

4、 当顺利地通过了上面的步骤以后,服务器就能给客户端转发数据了。转发过程可以描述为“收到数据.远程主机 – 发送数据.客户端”或者是“收到数据. 客户

端– 发送数据. 远程主机”的形式,在Windows的WinSock2.0下,如果SOCK5代理服务器与客户连接的套接字为 sClt ,而与远程主机连接的套接字为 sRmt ,那么伪语言表示为:

                     while(无错)

                     {

                            有数据到达;

                            if(是远程主机发送过来的)

                            {

                                   recv(sRmt, buf);

                                   send(sClt, buf);

}

else

{

                                   recv(sClt, buf);

                                   send(sRmt, buf);

}

}

在实现SOCK5代理服务器的时候,关键在于转发这部分,不仅仅因为转发是目的之根本,也因为在转发的时候得考虑系统及网络资源的占用,我在实现的时候采用了“每个客户端一个线程 + select模型”,这样子的话可以简化设计及实现,而且可用阻塞send(在非阻塞模型中,send会立即返回,但并不说明数据完全发送,在WSAEWOULDBLOCK错误发生时还得保存下未发送的数据,在一方接收慢另一方接收快的情况下可能会造成服务器缓存数据过多 … …. 等等)。当然,如果要实现支持代理大量客户端的话,还得优化线程及对数据的收发。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是一个简单的 C 语言实现 SOCKS5 代理程序的示例代码: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <arpa/inet.h> #include <unistd.h> #define PORT 8888 int main(int argc, char const *argv[]) { int proxy_sock, client_sock, server_sock, valread; struct sockaddr_in proxy_addr, client_addr, server_addr; char buffer[1024] = {0}; // 创建代理服务器 socket if ((proxy_sock = socket(AF_INET, SOCK_STREAM, 0)) == 0) { perror("socket failed"); exit(EXIT_FAILURE); } // 设置代理服务器地址 proxy_addr.sin_family = AF_INET; proxy_addr.sin_addr.s_addr = INADDR_ANY; proxy_addr.sin_port = htons(PORT); // 绑定代理服务器 socket if (bind(proxy_sock, (struct sockaddr *)&proxy_addr, sizeof(proxy_addr)) < 0) { perror("bind failed"); exit(EXIT_FAILURE); } // 开始监听 if (listen(proxy_sock, 3) < 0) { perror("listen failed"); exit(EXIT_FAILURE); } while (1) { printf("Waiting for incoming connection...\n"); int addr_len = sizeof(client_addr); // 接受客户端连接 if ((client_sock = accept(proxy_sock, (struct sockaddr *)&client_addr, (socklen_t *)&addr_len)) < 0) { perror("accept failed"); exit(EXIT_FAILURE); } printf("Client connected from %s:%d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); // 读取客户端请求 valread = read(client_sock, buffer, 1024); printf("%s\n", buffer); // 连接目标服务器 if ((server_sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("socket failed"); exit(EXIT_FAILURE); } // 设置目标服务器地址 server_addr.sin_family = AF_INET; server_addr.sin_port = htons(80); if (inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr) <= 0) { perror("inet_pton failed"); exit(EXIT_FAILURE); } // 连接目标服务器 if (connect(server_sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) { perror("connect failed"); exit(EXIT_FAILURE); } // 将客户端请求转发给目标服务器 send(server_sock, buffer, strlen(buffer), 0); printf("Request forwarded to server\n"); // 读取目标服务器响应 valread = read(server_sock, buffer, 1024); printf("Response received from server\n"); // 将目标服务器响应转发给客户端 send(client_sock, buffer, strlen(buffer), 0); printf("Response forwarded to client\n"); close(client_sock); close(server_sock); } return 0; } ``` 这段代码创建了一个 SOCKS5 代理服务器,监听指定端口号。当客户端连接到该端口时,代理服务器会读取客户端请求连接到目标服务器,将客户端请求转发给目标服务器,并读取目标服务器响应并将其转发给客户端。注意,该示例代码中的目标服务器地址和端口号是硬编码的,需要根据实际情况进行修改。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值