@Leatington

Как реализовать двухуровневый SOCKS прокси, он же обратный прокси?

Простыми словами:

Есть ПК с серым 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()
  • Вопрос задан
  • 89 просмотров
Пригласить эксперта
Ответы на вопрос 1
@rrambo
Если на домашнем ПК линукс то можно реализовать так:
на vds сервер openvpn, для двух клиентов — домашнего ПК и ПК из интернета с которого надо выйти в сеть от имени домашнего ПК. Ovpn в режиме что бы была связность между его клиентами. На домашнем ПК ставите сокс сервер dante, который слушает tun интерфейс . С клиента который находится во вне, но подключен к тому же впн серверу что и домашний пк, используя сокс сервер домашнего пк, выходите в интернет.
Если на домашнем ПК виндоуз то все так же только надо найти сокс сервер нормальный для вин, я в свое время не находил или не мог нормально настроить) есть 3proxy но как то не мог его завести под вин
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы