Skip to content

Instantly share code, notes, and snippets.

@gkbrk
Created January 21, 2018 20:36
Show Gist options
  • Save gkbrk/fcd6ada16505e74bb678fd1a073a76eb to your computer and use it in GitHub Desktop.
Save gkbrk/fcd6ada16505e74bb678fd1a073a76eb to your computer and use it in GitHub Desktop.
A SOCKS-over-HTTP tunnel for evading censorship/network filters
#!/usr/bin/env python3
import socket
import threading
import random
import time
class EvadereSocket:
def __init__(self):
self.conn = None
self.last_activity = time.time()
self.id = random.randint(99999, 99999999)
self.should_clean = False
def connect(self, host, port):
self.conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.conn.connect((host, port))
def update_timestamp(self):
self.last_activity = time.time()
class VpnServer:
def __init__(self):
self.proxy_sockets = []
self.vpn_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
def bind(self, ip="0.0.0.0", port=80):
self.vpn_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.vpn_socket.bind((ip, port))
self.vpn_socket.listen(15)
def handle_connection(self, conn):
conn.settimeout(15)
conn_file = conn.makefile("rwb")
headers = {}
method, url, version = conn_file.readline().decode("utf-8").strip().split()
print(method, url, version)
if method.upper() == "GET" and version.startswith("HTTP/1"):
command, *args = url.split("/")[1:]
while True:
line = conn_file.readline().decode("utf-8").strip()
if line == "":
break
name, value = line.split(":", 1)
headers[name.strip()] = value.strip()
if command == "connect" and len(args) == 2:
# TODO: Exclude local ip addresses
print("Creating connection to {}:{}...".format(args[0], args[1]))
s = EvadereSocket()
s.connect(args[0], int(args[1]))
self.proxy_sockets.append(s)
conn_file.write(b"HTTP/1.1 200 OK\n")
conn_file.write(b"Content-Type: text/html\n")
conn_file.write("Content-Length: {}\n\n".format(len(str(s.id))).encode("utf-8"))
conn_file.write("{}".format(s.id).encode("utf-8"))
conn_file.flush()
elif command == "send" and len(args) == 1:
for s in self.proxy_sockets:
if s.id == int(args[0]):
data = conn_file.read(int(headers["Content-Length"]))
s.conn.sendall(data)
s.update_timestamp()
conn_file.write(b"HTTP/1.1 200 OK\n")
conn_file.write(b"Content-Type: text/html\n")
conn_file.write(b"Content-Length: 4\n\n")
conn_file.write(b"DONE")
conn_file.flush()
elif command == "recv" and len(args) == 1:
for s in self.proxy_sockets:
if s.id == int(args[0]):
data = s.conn.recv(16384)
if len(data) == 0:
s.should_clean = True
s.update_timestamp()
conn_file.write(b"HTTP/1.1 200 OK\n")
conn_file.write(b"Content-Type: text/html\n")
conn_file.write("Content-Length: {}\n\n".format(len(data)).encode("utf-8"))
conn_file.write(data)
conn_file.flush()
elif command == "stats":
conn_file.write(b"HTTP/1.1 200 OK\n")
conn_file.write("X-Socket-Count: {}\n\n".format(len(self.proxy_sockets)).encode("utf-8"))
conn_file.flush()
conn.close()
def run_threaded(self):
while True:
try:
sock, addr = self.vpn_socket.accept()
self.clean_connections()
threading.Thread(target=self.handle_connection, args=(sock,)).start()
except socket.error as e:
print("Socket error: {}".format(e))
except KeyboardInterrupt:
break
except Exception as e:
print("Error: {}".format(e))
def clean_connections(self):
current_time = time.time()
for s in list(self.proxy_sockets):
if current_time - s.last_activity > 2*60:
print("Closing EvadereSocket id {} (TIMEOUT)".format(s.id))
s.conn.close()
self.proxy_sockets.remove(s)
for s in list(self.proxy_sockets):
if s.should_clean:
s.conn.close()
self.proxy_sockets.remove(s)
if __name__ == "__main__":
server = VpnServer()
server.bind(port=1235)
server.run_threaded()
print("Goodbye!")
server.vpn_socket.shutdown(socket.SHUT_RDWR)
server.vpn_socket.close()
#!/usr/bin/env python3
import socket
import threading
import struct
import requests
headers = {"Host": "www.meb.gov.tr", "User-Agent": "Firefox"}
class EvadereClient:
def __init__(self, vpn):
self.vpn = vpn
self.sock_id = None
def connect(self, host, port):
self.sock_id = int(requests.get("http://{}/connect/{}/{}".format(self.vpn, host, port), headers=headers).text)
def send(self, data):
requests.get("http://{}/send/{}".format(self.vpn, self.sock_id), data=data, headers=headers).text
def recv(self):
return requests.get("http://{}/recv/{}".format(self.vpn, self.sock_id), headers=headers).content
def handle_connection(conn):
socks_version = conn.recv(1)[0]
if socks_version == 0x04:
command = conn.recv(1)[0]
if command == 0x01:
port = struct.unpack(">H", conn.recv(2))[0]
ip = conn.recv(4)
if ip[0] + ip[1] + ip[2] == 0 and ip[3] != 0:
b = None
while b != 0x00:
b = conn.recv(1)[0]
hostname = ""
while True:
b = conn.recv(1)
if b[0] == 0x00:
break
hostname += b.decode("utf-8")
conn.sendall(b"\x00\x5A\x00\x60\x01\x01\x01\x01")
print("{}:{}".format(hostname, port))
vpn = EvadereClient("gkbrk.com:80")
vpn.connect(hostname, port)
print(vpn.sock_id)
def recv_thread():
while True:
data = vpn.recv()
if len(data) == 0:
break
conn.sendall(data)
def send_thread():
while True:
data = conn.recv(16384)
if len(data) == 0:
break
vpn.send(data)
threading.Thread(target=recv_thread).start()
threading.Thread(target=send_thread).start()
else:
ip = "{}.{}.{}.{}".format(*conn.recv(4))
print(ip)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(("0.0.0.0", 1080))
s.listen(15)
while True:
try:
conn, addr = s.accept()
threading.Thread(target=handle_connection, args=(conn,)).start()
except KeyboardInterrupt:
break
except Exception as e:
print("Error: {}".format(e))
print("Goodbye!")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment