晴耕雨読

working in the fields on fine days and reading books on rainy days

TCPパケットの中継サーバ(プロキシサーバ)をPythonで実装する

TCPパケットの中継サーバ(プロキシサーバ)をPythonで実装する場合のプログラム

使い方

ローカルの 9000/tcp へのパケットを別IPの 8000/tcp へ転送する場合:

python -m http.server 8000 &
python proxy.py 127.0.0.1 9000 192.168.0.10 8000 False &
curl 127.0.0.1:9000

実装

import sys
import socket
from threading import Thread


def hexdump(src, length=16):
    result = []

    for i in range(0, len(src), length):
        s = src[i:i+length]
        hexa = ' '.join(['{:02X}'.format(x) for x in s])
        text = ''.join([chr(x) if x >= 32 and x < 127 else '.' for x in s])
        result.append('{:04X}   {}{}    {}'.format(i, hexa, ((length-len(s))*3)*' ', text))
    for s in result:
        print(s)

def received_from(connection):
    buffer = b''
    connection.settimeout(2)

    try:
        recv_len = 1
        while recv_len:
            data = connection.recv(4096)
            buffer += data
            recv_len = len(data)
            if recv_len < 4096:
                break
    except:
        pass

    return buffer


def request_handler(buffer):
    return buffer

def response_handler(buffer):
    return buffer


def proxy_handler(client_socket, remote_host, remote_port, receive_first):
    remote_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    remote_socket.connect((remote_host, remote_port))

    if receive_first:
        remote_buffer = received_from(remote_socket)
        hexdump(remote_buffer)

        remote_buffer = response_handler(remote_buffer)

        if len(remote_buffer):
            print('[<==] Sending {} bytes to localhost.'.format(len(remote_buffer)))
            client_socket.send(remote_buffer)

    while True:
        local_buffer = received_from(client_socket)
        if len(local_buffer):
            print('[==>] Received {} bytes from localhost.'.format(len(local_buffer)))
            hexdump(local_buffer)

            local_buffer = request_handler(local_buffer)

            remote_socket.send(local_buffer)
            print('[==>] Sent to remote.')

        remote_buffer = received_from(remote_socket)

        if len(remote_buffer):
            print('[<==] Received {} bytes from remote.'.format(len(remote_buffer)))
            hexdump(remote_buffer)

            remote_buffer = response_handler(remote_buffer)
            client_socket.send(remote_buffer)

            print('[<==] Sent to localhost.')


def server_loop(local_host, local_port, remote_host, remote_port, receive_first):
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    try:
        server.bind((local_host, local_port))
    except:
        print('[!!] Failed to listen on {}:{}'.format(local_host, local_port))
        print('Check for other listening sockets or correct permissions.')
        sys.exit(0)

    print('[*] Listening on {}:{}'.format(local_host, local_port))
    server.listen(5)

    while True:
        client_socket, addr = server.accept()
        print('[==>] Received incoming connection from {}:{}'.format(addr[0], addr[1]))
        proxy_thread = Thread(target=proxy_handler,
                        args=[client_socket, remote_host, remote_port, receive_first])

        proxy_thread.start()


def main():
    if len(sys.argv[1:]) != 5:
        print('Usage: ./proxy.py [localhost] [localport] [remotehost] [remoteport] [receive_first]')
        print('Example: ./proxy.py 127.0.0.1 9000 10.12.132.1 9000 True')
        sys.exit(1)

    local_host = sys.argv[1]
    local_port = int(sys.argv[2])

    remote_host = sys.argv[3]
    remote_port = int(sys.argv[4])

    receive_first = sys.argv[5]

    if 'True' in receive_first:
        receive_first = True
    else:
        receive_first = False

    server_loop(local_host, local_port, remote_host, remote_port, receive_first)

if __name__ == '__main__':
    main()

参考資料