Skip to content

Instantly share code, notes, and snippets.

@jwustrack
Last active August 2, 2022 13:04
Show Gist options
  • Save jwustrack/0c7cb063a28ce14766d421e8d8a12fcc to your computer and use it in GitHub Desktop.
Save jwustrack/0c7cb063a28ce14766d421e8d8a12fcc to your computer and use it in GitHub Desktop.
tcptee.py is a tee for TCP. It acts as a simple TCP proxy and dumps all traffic onto stdout.
# 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)
@jwustrack
Copy link
Author

@mr-fool
Copy link

mr-fool commented Oct 28, 2017

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)

@rstoutjesdijk
Copy link

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).

@rstoutjesdijk
Copy link

rstoutjesdijk commented Apr 14, 2018

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

@shearichard
Copy link

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment