# A tee for TCP, similar to `socal -v`. | |
# | |
# | server | |
# client ---| | |
# | stdout | |
import socket | |
from select import select | |
import sys | |
import logging | |
class TcpTee: | |
def __init__(self, source_port, destination): | |
self.destination = destination | |
self.teesock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | |
self.teesock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) | |
self.teesock.bind(('127.0.0.1', source_port)) | |
self.teesock.listen(200) | |
# Linked client/server sockets in both directions | |
self.channel = {} | |
def run(self): | |
while 1: | |
inputready, outputready, exceptready = select([self.teesock] + self.channel.keys(), [], []) | |
for s in inputready: | |
if s == self.teesock: | |
self.on_accept() | |
break | |
data = s.recv(4096) | |
if not data: | |
self.on_close(s) | |
break | |
self.on_recv(s, data) | |
def on_accept(self): | |
clientsock, clientaddr = self.teesock.accept() | |
serversock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | |
try: | |
serversock.connect(self.destination) | |
except Exception: | |
logging.exception('Could not connect to server %s. Closing connection to client %s' % (self.destination, clientaddr)) | |
clientsock.close() | |
else: | |
logging.info("%r has connected", clientaddr) | |
self.channel[clientsock] = serversock | |
self.channel[serversock] = clientsock | |
def on_close(self, sock): | |
logging.info("%s has disconnected", s.getpeername()) | |
othersock = self.channel[sock] | |
sock.close() | |
othersock.close() | |
del self.channel[sock] | |
del self.channel[othersock] | |
def on_recv(self, sock, data): | |
print data | |
self.channel[sock].send(data) | |
if __name__ == '__main__': | |
import argparse | |
parser = argparse.ArgumentParser() | |
parser.add_argument("listen_port", help="The port this process will listen on.", type=int) | |
parser.add_argument("server_host", help="The remote host to connect to.") | |
parser.add_argument("server_port", help="The remote port to connect to.", type=int) | |
args = parser.parse_args() | |
logging.basicConfig(stream=sys.stderr, level=logging.DEBUG) | |
tee = TcpTee(int(args.listen_port), (args.server_host, int(args.server_port))) | |
try: | |
tee.run() | |
except KeyboardInterrupt: | |
logging.info("Ctrl C - Good Bye") | |
sys.exit(1) |
Getting this error when I try to run your code
python3 testing.py 2001 4chan.org 80
Traceback (most recent call last):
File "testing.py", line 73, in
tee.run()
File "testing.py", line 21, in run
inputready, outputready, exceptready = select([self.teesock] + self.channel.keys(), [], [])
TypeError: can only concatenate list (not "dict_keys") to list
Fyi, I changed line 58 to print (data)
this is great. However i do have an odd question. Is it possible to combine this with scapy, as for a test i would like to print (as an example) for each received packet, not only the data but also the tcp headers (and after that some SSL data).
i've got it partly working:
at def on_recv(self): i've added
layer = IP(data)
layer.show()
but the output is totally incorrect
Thanks for writing this, very useful.
When I tried to run it in Python 3.4 I saw a couple of errors so I fixed those in a fork https://gist.github.com/shearichard/14c78eb4a8f1aea4791787ab1d14c8f9 - if you want to you can update yours.
Thanks again.
Based on http://voorloopnul.com/blog/a-python-proxy-in-less-than-100-lines-of-code/