socks5学习

-------------本文仅用于学习记录使用

一 、SOCKS5 协议原理详解

SOCKS 全称socket secure,是一种网络传输协议,主要用于客户端与外网服务器之间通讯的中间传递。在OSI模型中,SOCKS是会话层的协议,位于表示层与传输层之间,最新协议是SOCKS5。

SOCKS5的特点
1.SOCKS5相比于SOCKS4,加入了UDP协议支持,在框架上加入了强认证功能,并且地址信息也加入了域名和IPV6 的支持。
2、SOCKS5服务器在将通讯请求发送给真正服务器的过程中,对于请求数据包本身不加任何改变,只是传递数据包,而不关心是何种应用协议,所以SOCKS代理服务器比应用层代理服务器更快。
3.与VPN(虚拟专用网络)相比,SOCKS5可以代理应用层的某些应用,而不是代理全局网络,而VPN控制的是你电脑的整个网络,只要需要连接到互联网的流量都会经过VPN。

1、交互原理

①首先客户端向代理服务器发出请求信息,用以协商版本和认证方法。
随后代理服务器应答,将选择的方法发送给客户端。
②客户端和代理服务器进入由选定认证方法所决定的子协商过程,子协商过程结束后,
客户端发送请求信息,其中包含目标服务器的IP地址和端口。代理服务器验证客户端身份,通过后会与目标服务器连接,目标服务器经过代理服务器向客户端返回状态响应。
③连接完成后,代理服务器开始作为中转站中转数据。
在这里插入图片描述

2、交互原理详解

【1】认证
(图示上面描述,下面为占字节数)
①客户端向代理服务器发送代理请求,其中包含了代理的版本和认证方式:
在这里插入图片描述 VER:1字节,版本号,固定为0x05,代表使用SOCKS5协议
NMETHODS(1字节):方法数目,表示后面的方法编号列表(METHODS字段)中有多少种客户端支持的认证方法
METHOD(1-255字节):方法编号列表,存储客户端支持的认证方法的编号
【2】代理服务器从给定的方法编号列表中选择一个方法并返回选择报文
在这里插入图片描述

METHOD字段用来返回选择的方法编号
支持的认证方式有(16进制表示):
0x00: 不需要认证
0x01: GSSAPI认证
0x02: 用户名和密码方式认证
0x03: IANA认证
0x80-0xfe: 保留的认证方式
0xff: 不支持任何认证方式,当客户端收到此信息必须关闭连接。
【3】协商完成后,客户端向代理服务器发送代理请求,客户端会向代理服务器发送下面格式的请求
在这里插入图片描述
CMD:指令编号,
0x01 CONNECT 指令,用于 TCP 代理
0x02 BIND 指令,一般用于要客户端主动接受来自服务器连接时
0x03 UDP ASSOCIATE 指令,用于 UDP 代理

RSV:保留字段:必须为 0

ATYP:地址类型:
0x01 表明地址为(DST.ADDR字段)IPV4 地址,长度为4字节
0x03域名,表明地址为域名,第一个字节用作域名的长度标识
0x04 表明地址为IPV6 地址,长度为16字节

DST.ADDR:目标地址:4个字节,要访问的目标服务器的地址或域名,类型由ATYP字段决定
DST.PORT:目标端口号:目标地址对应的端口号。
【4】SOCKS 服务端会根据请求类型和源、目标地址,执行对应操作,并且返回对应的一个或多个报文信息,格式如下:
在这里插入图片描述
REP:请求的结果,
0x00 成功
0x01常规 SOCKS 服务故障
0x02 规则不允许的连接
0x03 网络不可达
0x04 主机无法访问
0x05 拒绝连接
0x06 连接超时
0x07 不支持的命令
0x08 不支持的地址类型
RSV:保留字段:必须为 0
ATYP:地址类型
BND.ADDR:绑定地址:即请求成功后客户端需要连接的代理服务器的地址或域名,客户端之后的通信均通过改地址对应的服务器
BND.PORT:绑定端口号:绑定地址对应的端口号

【5】当代理服务器返回成功消息,则后续客户端通过绑定地址和绑定端口号与代理服务器通信,由代理服务器转发客户端的请求到目标服务器,并将目标服务器的响应转发给客户端。

3、案例详解

   #1、握手阶段:接收客户端发送的握手请求并返回握手响应信息。
   #2、请求阶段:接收客户端发送的请求信息,提取出请求的目标地址和端口号。
   #3、响应阶段:向客户端发送请求成功的响应信息,并尝试连接目标服务器。
   #4、转发阶段:建立客户端与目标服务器之间的连接,并进行数据转发。
#在使用 Python 实现 SOCKS5 代理服务器时,handle() 函数通常是用于处理客户端连接的回调函数,当有新的客户端连接请求到达时(新的连接请求,比如socket connnect 建立之后  就传输数据包了,断线重连会再次触发handle),该函数将被触发执行。具体来说,在 Socket 编程中,可以使用 socketserver 模块提供的 StreamRequestHandler 类来实现一个 SOCKS5 代理服务器。而 StreamRequestHandler 类中的 handle() 方法就是用于处理客户端连接请求的回调函数。当有新的客户端连接请求到达时,handle() 方法将被自动调用,并传入已连接的客户端 Socket 对象和客户端地址信息。
    def handle(self):
        sock = None
        remote = None
        try:
            sock = self.connection  #当前连接的套接字对象
            # 1、二中【1】【2】步、代理服务器接收客户端发送的握手请求(代理客户端连接的时候是sock5协议,如proxydroid,发过来就是eg:050100  05 1字节 表示socks5协议的版本号),并向客户端发送握手响应无需认证返回  
            data = sock.recv(262) #读取一定字节即可 ,如1024或其他
            data = '0500' #16进制 05 代表使用SOCKS5协议 00: 不需要认证
            data = binascii.unhexlify(data)
            sock.send(data)
            # 2. 二中【3】步,
            data = self.rfile.read(4) 
            # self.rfile 指的是客户端连接套接字对象的输入流,用于从客户端向代理服务器发送数据。先读到ATYP-地址类型
            data = binascii.hexlify(data)
            mode = data [ 1 ]  #CMD
            addrtype = data [ 3 ] #ATYP:地址类型:
            #DST.ADDR
            if addrtype == 1: 
                addr = socket.inet_ntoa(self.rfile.read(4))
               #如果 ATYP 的值为 0x01,表示请求的是 IPv4 地址,则需要从数据包中提取出 4 字节的二进制格式地址,并使用 socket.inet_ntoa() 函数将其转换成点分十进制格式的 IP 地址
            elif addrtype == 3:  # Domain name
                addr = self.rfile.read(ord(sock.recv(1) [ 0 ]))
            #如果 ATYP 的值为 0x03,表示请求的是域名地址,则需要先提取出一个字节作为域名长度,然后再提取出对应长度的字节作为域名信息
            #DST.PORT
            port = struct.unpack('>H', self.rfile.read(2))
            #3、二中【4】步,SOCKS 服务端会根据请求类型和源、目标地址,执行对应操作,并且返回对应的一个或多个报文信息
            reply = b'05000001' # ①05 VER socks5协议 ②00 REP 成功 ③00 RSV:保留字段:必须为 0 ④01:ATYP:地址类型 IPv4
            reply = binascii.unhexlify(reply)
            try:
                if mode == 1:  # CMD:指令编号,0x01 CONNECT 指令,用于 TCP 代理
                    remote = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                    remote.connect((addr, port [ 0 ]))  # 和游戏服务器建立连接
                else:
                    reply = b'05070001'  # REP 0x07 不支持的命令 
                 #BND.ADDR:绑定地址:即请求成功后客户端需要连接的代理服务器的地址或域名,客户端之后     的通信均通过改地址对应的服务器
#BND.PORT:绑定端口号:绑定地址对应的端口号
                local = remote.getsockname() 
                reply += socket.inet_aton(local [ 0 ]) + struct.pack(">H", local [ 1 ])
            except socket.error:
                # REP 0x05 拒绝连接
                reply = b'05050001000000000000'
                reply = binascii.unhexlify(reply)
                #组装响应完毕,回过去
            sock.send(reply)
            # 4、二中【5】步,使用select模块来实现双向数据传输,将客户端和目标服务器之间的数据互相转发。
            if reply [ 1 ] == 0:  # 3、中②00 REP 成功
                if mode == 1:  # 1. Tcp connect
                    sockets = [sock, remote]
                    while True:
                        rlist, wlist, xlist = select.select(sockets, [], sockets, 5)
                        if xlist:
                            break
                        for rsock in rlist:
                            wsock = remote_socket if rsock is client_socket else client_socket
                            data = rsock.recv(4096)
                            if not data:
                                sockets.remove(rsock)
                                continue
                            wsock.sendall(data)
        except socket.error:
            pass
        except IndexError:
            pass


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值