Skip to content

Instantly share code, notes, and snippets.

@xanoni
Forked from mrphs/testobfs4.py
Created November 2, 2021 00:03
Show Gist options
  • Save xanoni/8ec8e9dde34fe7c45d77d616bb0783b6 to your computer and use it in GitHub Desktop.
Save xanoni/8ec8e9dde34fe7c45d77d616bb0783b6 to your computer and use it in GitHub Desktop.
testing obfs4 performance
#!/usr/bin/env python
import csv
import datetime
import os
import os.path
import re
import sys
import time
import urllib2
import stem
import stem.control
import stem.process
import torsocks
OBFS4PROXY = "/usr/bin/obfs4proxy"
# 10 MB file
URL = "http://speedtest.wdc01.softlayer.com/downloads/test10.zip"
# https://globe.torproject.org/#/relay/B204DE75B37064EF6A4C6BAF955C5724578D0B32
# cry, NL
MIDDLE_NODE = "B204DE75B37064EF6A4C6BAF955C5724578D0B32"
# https://globe.torproject.org/#/relay/E1E922A20AF608728824A620BADC6EFC8CB8C2B8
# TorLand1, GB.
EXIT_NODE = "E1E922A20AF608728824A620BADC6EFC8CB8C2B8"
BASE_CONFIG = {
"SOCKSPort": "auto",
"ControlPort": "auto",
"DataDirectory": os.path.join(os.getcwd(), "datadir"),
"CookieAuthentication": "1",
"LearnCircuitBuildTimeout": "0",
"CircuitBuildTimeout": "300",
"__DisablePredictedCircuits": "1",
"__LeaveStreamsUnattached": "1",
"FetchHidServDescriptors": "0",
"UseBridges": "1",
"ClientTransportPlugin": "obfs4 exec " + OBFS4PROXY,
}
class Bridge(object):
def __init__(self, transport, addr, fingerprint, *rest):
self.transport = transport
self.addr = addr
self.fingerprint = fingerprint
self.rest = rest
def __str__(self):
parts = []
if self.transport is not None:
parts.append(self.transport)
parts.append(self.addr)
parts.append(self.fingerprint)
parts.extend(self.rest)
return " ".join(parts)
BRIDGES = (
Bridge("obfs4", "154.35.22.10:41835", "8FB9F4319E89E5C6223052AA525A192AFBC85D55", "cert=GGGS1TX4R81m3r0HBl79wKy1OtPPNR2CZUIrHjkRg65Vc2VR8fOyo64f9kmT1UAFG7j0HQ", "iat-mode=0"),
Bridge("obfs4", "154.35.22.11:49868", "A832D176ECD5C7C6B58825AE22FC4C90FA249637", "cert=YPbQqXPiqTUBfjGFLpm9JYEFTBvnzEJDKJxXG5Sxzrr/v2qrhGU4Jls9lHjLAhqpXaEfZw", "iat-mode=0"),
Bridge("obfs4", "154.35.22.12:80", "00DC6C4FA49A65BD1472993CF6730D54F11E0DBB", "cert=N86E9hKXXXVz6G7w2z8wFfhIDztDAzZ/3poxVePHEYjbKDWzjkRDccFMAnhK75fc65pYSg", "iat-mode=0"),
Bridge("obfs4", "154.35.22.13:443", "FE7840FE1E21FE0A0639ED176EDA00A3ECA1E34D", "cert=fKnzxr+m+jWXXQGCaXe4f2gGoPXMzbL+bTBbXMYXuK0tMotd+nXyS33y2mONZWU29l81CA", "iat-mode=0"),
)
def start_tor(config):
ports = {}
# Parse status messages and set socks_port and control_port.
def parse_msg(s):
print >> sys.stderr, s
m = re.search(r'Socks listener listening on port ([0-9]+)\.', s)
if m is not None:
ports["socks"] = int(m.group(1))
m = re.search(r'Control listener listening on port ([0-9]+)\.', s)
if m is not None:
ports["control"] = int(m.group(1))
tor = stem.process.launch_tor_with_config(config=config, timeout=300, take_ownership=True, completion_percent=80, init_msg_handler=parse_msg)
return tor, ports["socks"], ports["control"]
def download(url, csvw, run_id, fingerprint):
start_time = time.time()
count = 0
def logline(now=None, msg=None):
if now is None:
now = time.time()
if msg is None:
msg = str(count)
csvw.writerow([run_id, fingerprint, url, "%.2f" % (now-start_time), msg])
logline(start_time)
try:
f = urllib2.urlopen(url)
while True:
data = f.read(4096)
if not data:
break
count += len(data)
logline()
except Exception as e:
logline(msg=str(e))
else:
logline()
f.close()
csvw = csv.writer(sys.stdout)
run_id = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
for bridge in BRIDGES:
config = BASE_CONFIG.copy()
config["Bridge"] = str(bridge)
tor, socks_port, control_port = start_tor(config)
controller = stem.control.Controller.from_port(port=control_port)
controller.authenticate()
circ = controller.new_circuit([bridge.fingerprint, MIDDLE_NODE, EXIT_NODE], await_build=True)
def stream_event(event):
if event.status == stem.StreamStatus.NEW and event.purpose == stem.StreamPurpose.USER:
controller.attach_stream(event.id, circ)
controller.add_event_listener(stream_event, stem.control.EventType.STREAM)
torsocks.run_python_over_tor(socks_port)(download, URL, csvw, run_id, bridge.fingerprint)
tor.terminate()
tor.wait()
# Copyright 2015 Philipp Winter <phw@nymity.ch>
#
# This file is part of exitmap.
#
# exitmap is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# exitmap is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with exitmap. If not, see <http://www.gnu.org/licenses/>.
"""
Provide a Tor-specific SOCKSv5 interface.
"""
import struct
import socket
proxy_addr = None
proxy_port = None
orig_socket = socket.socket
class SOCKSv5Error(Exception):
pass
def set_default_proxy(ip_addr, port):
"""
Set the SOCKS proxy address and its port.
"""
global proxy_addr, proxy_port
proxy_addr, proxy_port = ip_addr, port
class torsocket(socket.socket):
"""
Provides a minimal, Tor-specific SOCKSv5 interface.
"""
def __init__(self, family=socket.AF_INET, type=socket.SOCK_STREAM,
proto=0, _sock=None):
self.sockfamily = family
self.socktype = type
super(torsocket, self).__init__(family, type, proto, _sock)
def _authenticate(self):
"""
Authenticate to our SOCKSv5 server.
"""
assert (proxy_addr is not None) and (proxy_port is not None)
# Connect to SOCKSv5 server. We use version 5 and one authentication
# method, which is "no authentication".
orig_socket.connect(self, (proxy_addr, proxy_port))
self.sendall("\x05\x01\x00")
resp = self.recv(2)
if resp != "\x05\x00":
raise SOCKSv5Error("Invalid server response: 0x%s" %
resp.encode("hex"))
def resolve(self, domain):
"""
Resolve the given domain using Tor's SOCKS resolution extension.
"""
domain_len = len(domain)
if domain_len > 255:
raise SOCKSv5Error("Domain must not be longer than 255 "
"characters, but %d given." % domain_len)
# Tor defines a new command value, \x0f, that is used for domain
# resolution.
self._authenticate()
self.sendall("\x05\xf0\x00\x03%s%s%s" %
(chr(domain_len), domain, "\x00\x00"))
resp = self.recv(10)
if resp[:2] != "\x05\x00":
raise SOCKSv5Error("Invalid server response: 0x%s" %
resp[1].encode("hex"))
return socket.inet_ntoa(resp[4:8])
def connect(self, addr_tuple):
"""
Tell SOCKS server to connect to our destination.
"""
dst_addr, dst_port = addr_tuple[0], int(addr_tuple[1])
self._authenticate()
# Tell SOCKS server to connect to destination.
self.sendall("\x05\x01\x00\x01%s%s" %
(socket.inet_aton(dst_addr), struct.pack(">H", dst_port)))
resp = self.recv(4)
if resp[1] != "\x00":
raise SOCKSv5Error("Connection failed. Server responded "
"with 0x%s." % resp[0].encode("hex"))
# Depending on address type, get address.
if resp[3] == "\x01":
self.recv(4)
elif resp[3] == "\x03":
length = self.recv(1)
self.recv(length)
else:
self.recv(16)
# Get port.
self.recv(2)
def run_python_over_tor(socks_port):
"""
Returns a closure to route a Python function's network traffic over Tor.
"""
def closure(func, *args):
"""
Route the given Python function's network traffic over Tor.
We temporarily monkey-patch socket.socket using our torsocks module and
reset it, once the function returns.
"""
set_default_proxy("127.0.0.1", socks_port)
orig_socket = socket.socket
socket.socket = torsocket
func(*args)
socket.socket = orig_socket
return closure
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment