Простыми словами:
Есть ПК с серым IP.
Есть VPS с белым IP.
Нужно, чтобы бы я мог с любой точки мира подключиться к своему ПК с серым IP, а точнее использовать его IP, как будто я выхожу в сеть из дома.
Решил написать прокси-сервер на Python. Сначала реализовал обычный (чтобы был IP VPS) и это прекрасно работало. Но когда я начал делать третий уровень (на основном ПК с серым IP), что-то стало не получаться.
Код сервера:
import threading
import select
import socket
admin_data = None
client_data = None
ip = 'тут ip'
SOCKS_VERSION = 5
def get_available_methods(nmethods, connection):
methods = []
for i in range(nmethods):
methods.append(ord(connection.recv(1)))
return methods
def verify_credentials(connection):
version = ord(connection.recv(1))
username_len = ord(connection.recv(1))
username = connection.recv(username_len).decode('utf-8')
password_len = ord(connection.recv(1))
password = connection.recv(password_len).decode('utf-8')
if username == 'username' and password == 'password':
# success, status = 0
response = bytes([version, 0])
connection.sendall(response)
return True
# failure, status != 0
response = bytes([version, 0xFF])
connection.sendall(response)
connection.close()
return False
def generate_failed_reply(address_type, error_number):
return b''.join([
SOCKS_VERSION.to_bytes(1, 'big'),
error_number.to_bytes(1, 'big'),
int(0).to_bytes(1, 'big'),
address_type.to_bytes(1, 'big'),
int(0).to_bytes(4, 'big'),
int(0).to_bytes(4, 'big')
])
def forward_to_third_pc(data):
global client_data
client_data.send(data)
response = client_data.recv(4096)
return response
def send_server_info_to_third_pc(connection, address_type, address, port):
preamble = b'\xFF\xFE'
if address_type == 1: # IPv4
addr_bytes = socket.inet_aton(address)
elif address_type == 3: # Domain name
addr_bytes = len(address).to_bytes(1, 'big') + address.encode('utf-8')
port_bytes = port.to_bytes(2, 'big')
packet = preamble + address_type.to_bytes(1, 'big') + addr_bytes + port_bytes
connection.sendall(packet)
def handle_client(connection):
version, nmethods = connection.recv(2)
methods = get_available_methods(nmethods, connection)
if 2 not in set(methods):
connection.close()
return
connection.sendall(bytes([SOCKS_VERSION, 2]))
if not verify_credentials(connection):
return
version, cmd, _, address_type = connection.recv(4)
if address_type == 1: # IPv4
address = socket.inet_ntoa(connection.recv(4))
elif address_type == 3: # Domain name
domain_length = connection.recv(1)[0]
address = connection.recv(domain_length)
address = socket.gethostbyname(address)
port = int.from_bytes(connection.recv(2), 'big', signed=False)
send_server_info_to_third_pc(client_data, address_type, address, port)
'''
try:
if cmd == 1: # CONNECT
remote = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
remote.connect((address, port))
bind_address = remote.getsockname()
print("* Connected to {} {}".format(address, port))
else:
connection.close()
addr = int.from_bytes(socket.inet_aton(bind_address[0]), 'big', signed=False)
port = bind_address[1]
reply = b''.join([
SOCKS_VERSION.to_bytes(1, 'big'),
int(0).to_bytes(1, 'big'),
int(0).to_bytes(1, 'big'),
int(1).to_bytes(1, 'big'),
addr.to_bytes(4, 'big'),
port.to_bytes(2, 'big')
])
except Exception as e:
reply = generate_failed_reply(address_type, 5)
connection.sendall(reply)
'''
try:
if cmd == 1: # CONNECT
print("* Connection simulated to {} {}".format(address, port))
addr = 0x0100007F
port = 0x0050
reply = b''.join([
SOCKS_VERSION.to_bytes(1, 'big'),
int(0).to_bytes(1, 'big'),
int(0).to_bytes(1, 'big'),
int(1).to_bytes(1, 'big'), # IPv4
addr.to_bytes(4, 'big'),
port.to_bytes(2, 'big')
])
else:
connection.close()
except Exception as e:
reply = generate_failed_reply(address_type, 5)
connection.sendall(reply)
'''
if reply[1] == 0 and cmd == 1:
while True:
# wait until client or remote is available for read
r, w, e = select.select([connection, remote], [], [])
if connection in r:
data = connection.recv(4096)
if remote.send(data) <= 0:
break
if remote in r:
data = remote.recv(4096)
if connection.send(data) <= 0:
break
'''
if reply[1] == 0 and cmd == 1:
while True:
r, w, e = select.select([connection, client_data], [], [])
if connection in r:
data = connection.recv(4096)
if data:
response = forward_to_third_pc(data)
if connection.send(response) <= 0:
break
connection.close()
def admin(port):
global admin_data
admin = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
admin.bind((ip, port))
admin.listen()
while True:
admin_socket, admin_address = admin.accept()
print(f'Admin connection from: {admin_address}')
admin_data = admin_socket
t = threading.Thread(target=handle_client, args=(admin_socket,))
t.start()
admin_socket.close()
admin_data = None
admin.close()
def client(port):
global client_data
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.bind((ip, port))
client.listen()
while True:
client_socket, client_address = client.accept()
print(f'Client connection from: {client_address}')
data = client_socket.recv(1024)
if data:
if data.decode() == 'Hello':
if not client_data:
client_socket.send('Good'.encode())
client_data = client_socket
print(data.decode())
client_socket.close()
client_data = None
client.close()
thread1 = threading.Thread(target=admin, args=(44444,))
thread2 = threading.Thread(target=client, args=(55555,))
thread1.start()
thread2.start()
Код клиента:
import socket
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_socket.connect(('тут ip', 55555))
authorized = False
address = None
port = None
try:
while True:
if not authorized:
client_socket.send('Hello'.encode())
data = client_socket.recv(1024)
if data.decode() == 'Good':
authorized = True
print('authorized')
else:
preamble = client_socket.recv(2)
if preamble == b'\xFF\xFE':
address_type = int.from_bytes(client_socket.recv(1), 'big')
if address_type == 1:
address = socket.inet_ntoa(client_socket.recv(4))
elif address_type == 3:
domain_length = int.from_bytes(client_socket.recv(1), 'big')
address = client_socket.recv(domain_length).decode()
port = int.from_bytes(client_socket.recv(2), 'big')
print('SUCC!' + address + ' ' + str(port))
else:
data = preamble + client_socket.recv(4096)
if data:
try:
if address and port:
remote = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
remote.connect((address, port))
bind_address = remote.getsockname()
print("* Connected to {} {}".format(address, port))
while True:
r, w, e = select.select([client_socket, remote], [], [])
if client_socket in r:
data = client_socket.recv(4096)
if remote.send(data) <= 0:
break
if remote in r:
data = remote.recv(4096)
if client_socket.send(data) <= 0:
break
print(f'Get: {data.decode()}')
except:
print(f'Get: {data}')
except KeyboardInterrupt:
print("Stopped.")
client_socket.close()