#!/usr/bin/env python3

from datetime import datetime
import errno
import logging
import os
import re
import shutil
import subprocess
import tempfile
import time
import sys

import stem.process

BRIDGE_LINES = (
)

START_TOR_TIMEOUT = 3*60
CIRCUIT_BUILD_TIMEOUT = 3*60
SNOWFLAKE_PATH = "/usr/bin/snowflake"

def start_tcpdump(basename, interface):
    # http://packetlife.net/blog/2010/mar/19/sniffing-wireshark-non-root-user/
    # groupadd tcpdump
    # usermod -a -G tcpdump user
    # chgrp tcpdump /usr/sbin/tcpdump
    # setcap cap_net_raw,cap_net_admin=eip /usr/sbin/tcpdump
    p = subprocess.Popen(["/usr/sbin/tcpdump", "-i", interface, "-U", "-B", "4096", "-w", basename + "-%s.pcap" % interface],
        stderr=open(basename + ".tcpdump.err", "w"))
    return p

def download_file(socks_port):
    logging.info("Attempting to download large file")
    
    try:
        start = time.time()
        # Download a ~1MB file
        subprocess.run(["torsocks", "-P", str(socks_port), "wget", "-O", "/dev/null", "https://mirror.csclub.uwaterloo.ca/ubuntu/dists/xenial/main/binary-amd64/Packages.xz"], check=True)
        stop = time.time()
        download_time = stop - start
    except subprocess.CalledProcessError as e:
        logging.info("failed to download file: %s", e)


def start_tor(tor_config):
    assert "DataDirectory" in tor_config

    config = {
        "SOCKSPort": "auto",
        "ControlPort": "auto",
        "CookieAuthentication": "1",
        "LearnCircuitBuildTimeout": "0",
        "CircuitBuildTimeout": str(CIRCUIT_BUILD_TIMEOUT),
        "FetchHidServDescriptors": "0",
        "LogTimeGranularity": "1",
        "Log": "notice stdout",
    }
    config.update(tor_config)

    class Ports(object):
        socks = None
        control = None
    ports = Ports()
    socks_re = re.compile(r'\bSocks listener listening on port ([0-9]+)\.')
    control_re = re.compile(r'\bControl listener listening on port ([0-9]+)\.')
    def init_msg_handler(line):
        logging.info("tor: %s" % line.encode("unicode_escape"))
        m = socks_re.search(line)
        if m is not None:
            assert ports.socks is None
            ports.socks = int(m.group(1))
        m = control_re.search(line)
        if m is not None:
            assert ports.control is None
            ports.control = int(m.group(1))

    logging.info("starting tor with configuration %r" % config)

    proc = stem.process.launch_tor_with_config(
        config,
        timeout=START_TOR_TIMEOUT,
        take_ownership=True,
        init_msg_handler=init_msg_handler,
    )

    assert ports.socks is not None
    assert ports.control is not None

    return proc, ports.socks, ports.control

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s.%(msecs)03d %(message)s",
    datefmt="%Y-%m-%d %H:%M:%S",
)
logging.Formatter.converter = time.gmtime

# Set timezone to be inherited by tor processes.
os.environ["TZ"] = "UTC"
time.tzset()

logging.info("starting")

#Now do a set of probes of snowflake proxies
for x in range(0, 10):
    # Set up logs and datadir for probe
    nickname = "snowflake-probe-%(num)d" % {"num": x}
    datadir = tempfile.mkdtemp(prefix="datadir.", dir=".")
    logging.info("created temporary DataDirectory %r", datadir)
    tcpdump_lo_proc = None
    tcpdump_eth0_proc = None
    try:
        logging.info("starting tcpdump for bridge %r" % nickname)
        try:
            #tcpdump_lo_proc = start_tcpdump(nickname, "lo")
            tcpdump_eth0_proc = start_tcpdump(nickname, "eth0")
        except OSError as e:
            logging.info("failed to start tcpdump, stopping snowflake probe: %s", e)
            #these tests break if we can't find the proxy ip address
            break
	
        logging.info("starting tor for bridge %r" % nickname)
        tor_config = {
		"DataDirectory": datadir, 
		"Log": "notice file %s" % os.path.join(".", "%s-tor.log" % nickname),
		"UseBridges": "1",
		"ClientTransportPlugin": "snowflake exec {} \
					-url https://snowflake-broker.torproject.net.global.prod.fastly.net/ \
-front cdn.sstatic.net \
                    -log {}-client.log \
					-ice stun:stun.voip.blackberry.com:3478,stun:stun.altar.com.pl:3478,stun:stun.antisip.com:3478,stun:stun.bluesip.net:3478,stun:stun.dus.net:3478,stun:stun.epygi.com:3478,stun:stun.sonetel.com:3478,stun:stun.sonetel.net:3478,stun:stun.stunprotocol.org:3478,stun:stun.uls.co.za:3478,stun:stun.voipgate.com:3478,stun:stun.voys.nl:3478".format(SNOWFLAKE_PATH, nickname),
		"Bridge": "snowflake 192.0.2.3:1",
		}
        try:
            tor_proc, socks_port, _ = start_tor(tor_config)
            download_file(socks_port)
            tor_proc.terminate()
            tor_proc.wait()
        except OSError as err:
            logging.info("failed to start tor: %s" % err)
            continue
        finally:
            #give data to log file
            timestamp = datetime.utcnow().strftime("%b %d %H:%M:%S.%f")
            logging.info("Probed snowflake proxy %d time(s)" % (x+1))

    finally:
	#Extract the proxy ip
        logging.info("deleting temporary DataDirectory %r", datadir)
        shutil.rmtree(datadir)
        if tcpdump_lo_proc is not None:
            tcpdump_lo_proc.terminate()
        if tcpdump_eth0_proc is not None:
            tcpdump_eth0_proc.terminate()

