Ticket #7549: detect_proxy_within_tor_in_tor_situation.patch

File detect_proxy_within_tor_in_tor_situation.patch, 7.4 KB (added by jct, 7 years ago)

A patch with the candidate code in order to detect that the public address of the Proxy belongs to a TOR Exit node

  • facilitator/fac.py

    From 0beacf0a9abab04fd9a7a1b20409fe137285005f Mon Sep 17 00:00:00 2001
    From: Jorge Couchet <jorge.couchet@gmail.com>
    Date: Mon, 26 Nov 2012 12:06:01 +0100
    Subject: [PATCH] The changes needed to detect a proxy in a Tor-in-Tor
     situation
    
    ---
     facilitator/fac.py      |    5 +++
     facilitator/facilitator |  109 ++++++++++++++++++++++++++++++++++++++++++++---
     proxy/flashproxy.js     |    7 +++
     3 files changed, 114 insertions(+), 7 deletions(-)
    
    diff --git a/facilitator/fac.py b/facilitator/fac.py
    index 9d77b95..ed38b24 100644
    a b def get_reg(facilitator_addr, proxy_addr): 
    221221        return {
    222222            "client": ""
    223223        }
     224    # The proxy address belong to a Tor Exit node.
     225    elif command == "ERROR":
     226        return {
     227            "error": "tor-in-tor"
     228        }
    224229    elif command == "OK":
    225230        client_spec = param_first("CLIENT", params)
    226231        relay_spec = param_first("RELAY", params)
  • facilitator/facilitator

    diff --git a/facilitator/facilitator b/facilitator/facilitator
    index 26e449d..f9c7576 100755
    a b import socket 
    88import sys
    99import threading
    1010import time
     11from urllib import urlopen
    1112
    1213import fac
    1314
    READLINE_MAX_LENGTH = 10240 
    2324
    2425LOG_DATE_FORMAT = "%Y-%m-%d %H:%M:%S"
    2526
     27# Constants needed for the Tor-in-Tor situation.
     28# ##############################################
     29URL_DIRECTORY_SERVERS= ['http://128.31.0.34:9031/tor/status/all', 'http://86.59.21.38:80/tor/status/all']
     30EXIT_NODE = 'Exit'
     31START_LINE_IP = 'r '
     32START_LINE_CODES = 's '
     33IP_POS_LINE = 6
     34# The time to set the interval in order to refresh the Tor Exits list.
     35# Time in seconds (3600 secs = 1 hour)
     36ELAPSED_REFRESHING_TIME = 3600
     37
    2638class options(object):
    2739    listen_port = DEFAULT_LISTEN_PORT
    2840    log_filename = DEFAULT_LOG_FILENAME
    2941    log_file = sys.stdout
     42    proxy = {}
    3043    relay_spec = None
    3144    daemonize = True
    3245    pid_filename = None
    again. Listen on 127.0.0.1 and port PORT (by default %(port)d). 
    4861  -l, --log FILENAME      write log to FILENAME (default \"%(log)s\").
    4962  -p, --port PORT         listen on PORT (by default %(port)d).
    5063      --pidfile FILENAME  write PID to FILENAME after daemonizing.
     64  -o, --proxy PROXY       If the facilitar is behind a proxy, then it is needed in order to query a Tor Directory Server for the Tor Exit nodes.
    5165  -r, --relay RELAY       send RELAY (host:port) to proxies as the relay to use.
    5266      --unsafe-logging    don't scrub IP addresses from logs.\
    5367""" % {
    class Handler(SocketServer.StreamRequestHandler): 
    225239        print >> self.wfile, "ERROR"
    226240
    227241    def do_GET(self, params):
    228         reg = REGS.fetch()
    229         if reg:
    230             log(u"proxy gets %s, relay %s (now %d)" %
    231                 (safe_str(unicode(reg)), options.relay_spec, len(REGS)))
    232             print >> self.wfile, fac.render_transaction("OK", ("CLIENT", str(reg)), ("RELAY", options.relay_spec))
     242        # It is checking if the proxy's address belongs to a Tor Exit node.
     243        if tor_exits.query(fac.param_first("FROM", params)):
     244            log(u"proxy is in Tor-in-Tor situation")
     245            print >> self.wfile, fac.render_transaction("ERROR")
    233246        else:
    234             log(u"proxy gets none")
    235             print >> self.wfile, fac.render_transaction("NONE")
     247            reg = REGS.fetch()
     248            if reg:
     249                log(u"proxy gets %s, relay %s (now %d)" %
     250                    (safe_str(unicode(reg)), options.relay_spec, len(REGS)))
     251                print >> self.wfile, fac.render_transaction("OK", ("CLIENT", str(reg)), ("RELAY", options.relay_spec))
     252            else:
     253                log(u"proxy gets none")
     254                print >> self.wfile, fac.render_transaction("NONE")
    236255        return True
    237256
    238257    def do_PUT(self, params):
    class Server(SocketServer.ThreadingMixIn, SocketServer.TCPServer): 
    266285
    267286REGS = RegSet()
    268287
     288# It is holding the list of Tor Exit nodes.
     289class TorExitsSet(object):
     290    def __init__(self):
     291        self.set = []
     292        self.cv = threading.Condition()
     293
     294    def replace(self, exits):
     295        self.cv.acquire()
     296        try:
     297            self.set = exits
     298            self.cv.notify()
     299        finally:
     300            self.cv.release()
     301
     302    def query(self, exit):
     303        self.cv.acquire()
     304        try:
     305            if (not self.set) or (len(self.set) == 0) or (not (exit in self.set)):
     306                return False
     307            else:
     308                return True
     309        finally:
     310            self.cv.release()
     311
     312# The local Tor Exit nodes list.
     313tor_exits = TorExitsSet()
     314
     315# It is querying a Tor Directory server for the list of Tor Exit nodes.
     316def get_exit_nodes():
     317    is_error = True
     318    error_list = []
     319    for url in URL_DIRECTORY_SERVERS:
     320        try:
     321            f = urlopen(url, proxies=options.proxy)
     322            is_error = False
     323            break
     324        except Exception:
     325            e = sys.exc_info()[1]
     326            error_list.append(e)
     327
     328    if not is_error:
     329        exits = []
     330        is_v = False
     331        for i in f:
     332            if is_v and i.startswith(START_LINE_CODES):
     333                if EXIT_NODE in i:
     334                    exits.append(ip)
     335                is_v = False
     336            elif i.startswith(START_LINE_IP):
     337                elements = i.split()
     338                ip = elements[IP_POS_LINE]
     339                is_v = True
     340        tor_exits.replace(exits)
     341    else:
     342        msg = ""
     343        for e in error_list:
     344            msg+= str(e)
     345        log(u"Not able to connect to any of the Directory servers: %s" % msg)
     346
     347
     348# It is the main function for the Thread in charge of refreshing the Tor Exit nodes list.
     349def refresh_tor_exits():
     350    while True:
     351        get_exit_nodes()
     352        log(u"Finished of retrieving the Tor Exit nodes list, going to sleep.")
     353        time.sleep(ELAPSED_REFRESHING_TIME)
     354
    269355def main():
    270356    opts, args = getopt.gnu_getopt(sys.argv[1:], "dhl:p:r:",
    271357        ["debug", "help", "log=", "port=", "pidfile=", "relay=", "unsafe-logging"])
    def main(): 
    282368            options.listen_port = int(a)
    283369        elif o == "--pidfile":
    284370            options.pid_filename = a
     371        elif o == "-o" or o == "--proxy":
     372            options.proxy = {'http': a}
    285373        elif o == "-r" or o == "--relay":
    286374            try:
    287375                options.set_relay_spec(a)
    The -r option is required. Give it the relay that will be sent to proxies. 
    322410                f.close()
    323411            sys.exit(0)
    324412
     413    # It is the Thread in charge of refreshing the Tor Exit nodes list.
     414    thread = threading.Thread(target = refresh_tor_exits)
     415    # It is necessary to set 'daemon' to 'True' so that the thread dies automatically when the main
     416    # program is terminated: http://stackoverflow.com/questions/190010/daemon-threads-explanation
     417    thread.daemon = True
     418    thread.start()
     419
    325420    try:
    326421        server.serve_forever()
    327422    except KeyboardInterrupt:
  • proxy/flashproxy.js

    diff --git a/proxy/flashproxy.js b/proxy/flashproxy.js
    index 13a77f5..6ed3a57 100644
    a b function FlashProxy() { 
    507507
    508508        response = parse_query_string(text);
    509509
     510        /* It is receiving an error from the Facilitator, thus it is disabling itself. */
     511        if (response.error) {
     512            puts("Error: " + repr(response.error) + ".");
     513            this.disable();
     514            return;
     515        }
     516
    510517        if (!response.client) {
    511518            puts("No clients.");
    512519            return;