# -*- coding: utf-8 -*-
import select
import socket
import struct
from socketserver import StreamRequestHandler as Tcp, ThreadingTCPServer
SOCKS_VERSION = 5 # socks版本
"""
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
一、客户端认证请求
+----+----------+----------+
|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 |
+----+-----+-------+------+----------+----------+
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
"""
class DYProxy(Tcp):
# 用户认证 用户名/密码
username = 'dyboy'
password = '123456'
def handle(self):
print("客户端:", self.client_address, " 请求连接!")
"""
一、客户端认证请求
+----+----------+----------+
|VER | NMETHODS | METHODS |
+----+----------+----------+
| 1 | 1 | 1~255 |
+----+----------+----------+
"""
# 从客户端读取并解包两个字节的数据
header = self.connection.recv(2)
VER, NMETHODS = struct.unpack("!BB", header)
# 设置socks5协议,METHODS字段的数目大于0
assert VER == SOCKS_VERSION, 'SOCKS版本错误'
# 接受支持的方法
# 无需认证:0x00 用户名密码认证:0x02
# assert NMETHODS > 0
methods = self.IsAvailable(NMETHODS)
# 检查是否支持该方式,不支持则断开连接
if 0 not in set(methods):
self.server.close_request(self.request)
"""
二、服务端回应认证
+----+--------+
|VER | METHOD |
+----+--------+
| 1 | 1 |
+----+--------+
"""
# 发送协商响应数据包
self.connection.sendall(struct.pack("!BB", SOCKS_VERSION, 0))
# 校验用户名和密码
# if not self.VerifyAuth():
# return
"""
三、客户端连接请求(连接目的网络)
+----+-----+-------+------+----------+----------+
|VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT |
+----+-----+-------+------+----------+----------+
| 1 | 1 | 1 | 1 | Variable | 2 |
+----+-----+-------+------+----------+----------+
"""
version, cmd, _, address_type = struct.unpack("!BBBB", self.connection.recv(4))
assert version == SOCKS_VERSION, 'socks版本错误'
if address_type == 1: # IPv4
# 转换IPV4地址字符串(xxx.xxx.xxx.xxx)成为32位打包的二进制格式(长度为4个字节的二进制字符串)
address = socket.inet_ntoa(self.connection.recv(4))
elif address_type == 3: # Domain
domain_length = ord(self.connection.recv(1)[0])
address = self.connection.recv(domain_length)
port = struct.unpack('!H', self.connection.recv(2))[0]
"""
四、服务端回应连接
+----+-----+-------+------+----------+----------+
|VER | REP | RSV | ATYP | BND.ADDR | BND.PORT |
+----+-----+-------+------+----------+----------+
| 1 | 1 | 1 | 1 | Variable | 2 |
+----+-----+-------+------+----------+----------+
"""
# 响应,只支持CONNECT请求
try:
if cmd == 1: # CONNECT
remote = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
remote.connect((address, port))
bind_address = remote.getsockname()
print('已建立连接:', address, port)
else:
self.server.close_request(self.request)
addr = struct.unpack("!I", socket.inet_aton(bind_address[0]))[0]
port = bind_address[1]
reply = struct.pack("!BBBBIH", SOCKS_VERSION, 0, 0, address_type, addr, port)
except Exception as err:
print(err)
# 响应拒绝连接的错误
reply = self.ReplyFaild(address_type, 5)
self.connection.sendall(reply) # 发送回复包
# 建立连接成功,开始交换数据
if reply[1] == 0 and cmd == 1:
self.ExchangeData(self.connection, remote)
self.server.close_request(self.request)
def IsAvailable(self, n):
"""
检查是否支持该验证方式
"""
methods = []
for i in range(n):
methods.append(ord(self.connection.recv(1)))
return methods
def VerifyAuth(self):
"""
校验用户名和密码
"""
version = ord(self.connection.recv(1))
assert version == 1
username_len = ord(self.connection.recv(1))
username = self.connection.recv(username_len).decode('utf-8')
password_len = ord(self.connection.recv(1))
password = self.connection.recv(password_len).decode('utf-8')
if username == self.username and password == self.password:
# 验证成功, status = 0
response = struct.pack("!BB", version, 0)
self.connection.sendall(response)
return True
# 验证失败, status != 0
response = struct.pack("!BB", version, 0xFF)
self.connection.sendall(response)
self.server.close_request(self.request)
return False
def ReplyFaild(self, address_type, error_number):
"""
生成连接失败的回复包
"""
return struct.pack("!BBBBIH", SOCKS_VERSION, error_number, 0, address_type, 0, 0)
def ExchangeData(self, client, remote):
"""
交换数据
"""
while True:
# 等待数据
rs, ws, es = select.select([client, remote], [], [])
if client in rs:
data = client.recv(4096)
if remote.send(data) <= 0:
break
if remote in rs:
data = remote.recv(4096)
if client.send(data) <= 0:
break
if __name__ == '__main__':
# 服务器上创建一个TCP多线程服务,监听2019端口
Server = ThreadingTCPServer(('0.0.0.0', 2019), DYProxy)
print("**********************************************************")
print("************************* DYPROXY ************************")
print("************************* 1.0 ************************")
print("******************** IP:xxx.xxx.xxx.xxx ******************")
print("*********************** PORT:2019 **********************")
print("**********************************************************")
Server.serve_forever();
服务端
# -*- coding: utf-8 -*-
# author: DYBOY
# time: 2019-5-18 17:27:49
# desc: 测试使用socks5代理访问
import socket
import socks
import requests
# 设置代理
socks.set_default_proxy(socks.SOCKS5, "服务器IP", 2019)
# 如果使用账号密码验证,那么使用下面这行连接方式
# socks.set_default_proxy(socks.SOCKS5, "服务器IP", 2019,username='dyboy', password='123456')
socket.socket = socks.socksocket
# 测试访问 重庆大学
test_url = 'http://cqu.edu.cn'
html = requests.get(test_url,timeout=8)
html.encoding = 'utf-8'
print(html.text)