[or-cvs] Oops. I guess I would probably get more comments about thi...

Nick Mathewson nickm at seul.org
Sat Jun 4 02:42:58 UTC 2005


Update of /home/or/cvsroot/control/python
In directory moria:/tmp/cvs-serv6608/python

Added Files:
	.cvsignore TorCtl.py TorExample.py 
Log Message:
Oops.  I guess I would probably get more comments about this clever controller API/demo stuff if I actually committed it to CVS.  Here then.

--- NEW FILE: .cvsignore ---
*.pyo
*.pyc

--- NEW FILE: TorCtl.py ---
#!/usr/bin/python
# TorCtl.py -- Python module to interface with Tor Control interface.
# Copyright 2005 Nick Mathewson -- See LICENSE for licensing information.
#$Id: TorCtl.py,v 1.1 2005/06/04 02:42:56 nickm Exp $

"""
TorCtl -- Library to control Tor processes.  See TorCtlDemo.py for example use.
"""

import binascii
import os
import sha
import socket
import struct
import sys
import threading
import Queue

__all__ = [
    "MSG_TYPE", "EVENT_TYPE", "CIRC_STATUS", "STREAM_STATUS",
    "OR_CONN_STATUS", "SIGNAL", "ERR_STATUS",
    "TorCtlError", "ProtocolError", "ErrorReply", "Connection", "EventHandler",
    "DebugEventHandler", "parseHostAndPort"
    ]

class _Enum:
    # Helper: define an ordered dense name-to-number 1-1 mapping.
    def __init__(self, start, names):
        self.nameOf = {}
        idx = start
        for name in names:
            setattr(self,name,idx)
            self.nameOf[idx] = name
            idx += 1

class _Enum2:
    # Helper: define an ordered sparse name-to-number 1-1 mapping.
    def __init__(self, **args):
        self.__dict__.update(args)
        self.nameOf = {}
        for k,v in args.items():
            self.nameOf[v] = k

# Message types that client or server can send.
MSG_TYPE = _Enum(0x0000,
                 ["ERROR",
                  "DONE",
                  "SETCONF",
                  "GETCONF",
                  "CONFVALUE",
                  "SETEVENTS",
                  "EVENT",
                  "AUTH",
                  "SAVECONF",
                  "SIGNAL",
                  "MAPADDRESS",
                  "GETINFO",
                  "INFOVALUE",
                  "EXTENDCIRCUIT",
                  "ATTACHSTREAM",
                  "POSTDESCRIPTOR",
                  "FRAGMENTHEADER",
                  "FRAGMENT",
                  "REDIRECTSTREAM",
                  "CLOSESTREAM",
                  "CLOSECIRCUIT",
                  ])

# Make sure that the enumeration code is working.
assert MSG_TYPE.SAVECONF == 0x0008
assert MSG_TYPE.CLOSECIRCUIT == 0x0014

# Types of "EVENT" message.
EVENT_TYPE = _Enum(0x0001,
                   ["CIRCSTATUS",
                    "STREAMSTATUS",
                    "ORCONNSTATUS",
                    "BANDWIDTH",
                    "OBSOLETE_LOG",
                    "NEWDESC",
                    "DEBUG_MSG",
                    "INFO_MSG",
                    "NOTICE_MSG",
                    "WARN_MSG",
                    "ERR_MSG",
                    ])

assert EVENT_TYPE.ERR_MSG == 0x000B
assert EVENT_TYPE.OBSOLETE_LOG == 0x0005

# Status codes for "CIRCSTATUS" events.
CIRC_STATUS = _Enum(0x00,
                    ["LAUNCHED",
                     "BUILT",
                     "EXTENDED",
                     "FAILED",
                     "CLOSED"])

# Status codes for "STREAMSTATUS" events
STREAM_STATUS = _Enum(0x00,
                      ["SENT_CONNECT",
                       "SENT_RESOLVE",
                       "SUCCEEDED",
                       "FAILED",
                       "CLOSED",
                       "NEW_CONNECT",
                       "NEW_RESOLVE",
                       "DETACHED"])

# Status codes for "ORCONNSTATUS" events
OR_CONN_STATUS = _Enum(0x00,
                       ["LAUNCHED","CONNECTED","FAILED","CLOSED"])

# Signal codes for "SIGNAL" events.
SIGNAL = _Enum2(HUP=0x01,INT=0x02,USR1=0x0A,USR2=0x0C,TERM=0x0F)

# Error codes for "ERROR" events.
ERR_CODES = {
  0x0000 : "Unspecified error",
  0x0001 : "Internal error",
  0x0002 : "Unrecognized message type",
  0x0003 : "Syntax error",
  0x0004 : "Unrecognized configuration key",
  0x0005 : "Invalid configuration value",
  0x0006 : "Unrecognized byte code",
  0x0007 : "Unauthorized",
  0x0008 : "Failed authentication attempt",
  0x0009 : "Resource exhausted",
  0x000A : "No such stream",
  0x000B : "No such circuit",
  0x000C : "No such OR"
}

class TorCtlError(Exception):
    "Generic error raised by TorControl code."
    pass

class ProtocolError(TorCtlError):
    "Raised on violations in Tor controller protocol"
    pass

class ErrorReply(TorCtlError):
    "Raised when Tor controller returns an error"
    pass

def parseHostAndPort(h):
    """Given a string of the form 'address:port' or 'address' or
       'port' or '', return a two-tuple of (address, port)
    """
    host, port = "localhost", 9100
    if ":" in h:
        i = h.index(":")
        host = h[:i]
        try:
            port = int(h[i+1:])
        except ValueError:
            print "Bad hostname %r"%h
            sys.exit(1)
    elif h:
        try:
            port = int(h)
        except ValueError:
            host = h

    return host, port

def _unpack_singleton_msg(msg):
    """Helper: unpack a single packet.  Return (None, minLength, body-so-far)
       on incomplete packet or (type,body,rest) on somplete packet
    """
    if len(msg) < 4:
        return None, 4, msg
    length,type = struct.unpack("!HH",msg)
    if len(msg) >= 4+length:
        return type,msg[4:4+length],msg[4+length:]
    else:
        return None,4+length,msg

def _minLengthToPack(bytes):
    """Return the minimum number of bytes needed to pack the message 'smg'"""
    whole,left = divmod(bytes,65535)
    if left:
        return whole*(65535+4)+4+left
    else:
        return whole*(65535+4)

def _unpack_msg(msg):
    "returns as for _unpack_singleton_msg"
    tp,body,rest = _unpack_singleton_msg(msg)
    if tp != MSG_TYPE.FRAGMENTHEADER:
        return tp, body, rest

    if len(body) < 6:
        raise ProtocolError("FRAGMENTHEADER message too short")

    realType,realLength = struct.unpack("!HL", body[:6])

    # Okay; could the message _possibly_ be here?
    minLength = _minLengthToPack(realLength+6)
    if len(msg) < minLength:
        return None,  minLength, msg

    # Okay; optimistically try to build up the msg.
    soFar = [ body[6:] ]
    lenSoFarLen = len(body)-6
    while len(rest)>=4 and lenSoFar < realLength:
        ln, tp = struct.unpack("!HH", rest[:4])
        if tp != MSG_TYPE.FRAGMENT:
            raise ProtocolError("Missing FRAGMENT message")
        soFar.append(rest[4:4+ln])
        lenSoFar += ln
        if 4+ln > len(rest):
            rest = ""
            leftInPacket = 4+ln-len(rest)
        else:
            rest = rest[4+ln:]
            leftInPacket=0

    if lenSoFar == realLength:
        return realType, "".join(soFar), rest
    elif lenSoFar > realLength:
        raise ProtocolError("Bad fragmentation: message longer than declared")
    else:
        inOtherPackets = realLength-lenSoFar-leftInPacket
        minLength = _minLengthToPack(inOtherPackets)
        return None, len(msg)+leftInPacket+inOtherPackets, msg

def _receive_singleton_msg(s):
    """Read a single packet from the socket s.
    """
    body = ""
    header = s.recv(4)
    length,type = struct.unpack("!HH",header)
    if length:
        while length > len(body):
            body += s.recv(length-len(body))
    return length,type,body

def _receive_message(s):
    """Read a single message (possibly multi-packet) from the socket s."""
    length, tp, body = _receive_singleton_msg(s)
    if tp != MSG_TYPE.FRAGMENTHEADER:
        return length, tp, body
    if length < 6:
        raise ProtocolError("FRAGMENTHEADER message too short")
    realType,realLength = struct.unpack("!HL", body[:6])
    data = [ body[6:] ]
    soFar = len(data[0])
    while 1:
        length, tp, body = _receive_singleton_msg(s)
        if tp != MSG_TYPE.FRAGMENT:
            raise ProtocolError("Missing FRAGMENT message")
        soFar += length
        data.append(body)
        if soFar == realLength:
            return realLength, realType, "".join(data)
        elif soFar > realLength:
            raise ProtocolError("FRAGMENT message too long!")

def pack_message(type, body=""):
    """Given a message type and optional message body, generate a set of
       packets to send.
    """
    length = len(body)
    if length < 65536:
        reqheader = struct.pack("!HH", length, type)
        return "%s%s"%(reqheader,body)

    fragheader = struct.pack("!HHHL",
                             65535, MSG_TYPE.FRAGMENTHEADER, type, length)
    msgs = [ fragheader, body[:65535-6] ]
    body = body[65535-6:]
    while body:
        if len(body) > 65535:
            fl = 65535
        else:
            fl = len(body)
        fragheader = struct.pack("!HH", MSG_TYPE.FRAGMENT, fl)
        msgs.append(fragheader)
        msgs.append(body[:fl])
        body = body[fl:]

    return "".join(msgs)

def _parseKV(body,sep=" ",term="\n"):
    """Helper: parse a key/value list of the form [key sep value term]* .
       Return a list of (k,v)."""
    res = []
    for line in body.split(term):
        if not line: continue
        k, v = line.split(sep,1)
        res.append((k,v))
    return res

def _unterminate(s):
    """Strip trailing NUL characters from s."""
    if s[-1] == '\0':
        return s[:-1]
    else:
        return s

class Connection:
    """A Connection represents a connection to the Tor process."""
    def __init__(self, sock):
        """Create a Connection to communicate with the Tor process over the
           socket 'sock'.
        """
        self._s = sock
        self._handler = None
        self._sendLock = threading.RLock()
        self._queue = Queue.Queue()
        self._thread = None

    def setEventHandler(self, handler):
        """Cause future events from the Tor process to be sent to 'handler'.
        """
        self._handler = handler

    def launchThread(self, daemon=1):
        """Launch a background thread to handle messages from the Tor process."""
        assert self._thread is None
        t = threading.Thread(target=self._loop)
        if daemon:
            t.setDaemon(daemon)
        t.start()
        self._thread = t
        return t

    def _send(self, type, body=""):
        """Helper: Deliver a command of type 'type' and body 'body' to Tor.
        """
        self._s.sendall(pack_message(type, body))

    def _loop(self):
        """Main subthread loop: Read commands from Tor, and handle them either
           as events or as responses to other commands.
        """
        while 1:
            try:
                length, tp, body = _receive_message(self._s)
            except OSError:
                if self._queue.get(timeout=0) != "CLOSE":
                    raise
            if tp == MSG_TYPE.EVENT:
                if self._handler is not None:
                    self._handler.handle(body)
            else:
                cb = self._queue.get()
                cb(tp, body)

    def _sendAndRecv(self, tp, msg="", expectedTypes=(MSG_TYPE.DONE,)):
        """Helper: Send a command of type 'tp' and body 'msg' to Tor,
           and wait for a command in response.  If the response type is
           in expectedTypes, return a (tp,body) tuple.  If it is an error,
           raise ErrorReply.  Otherwise, raise ProtocolError.
        """
        # This condition will get notified when we've got a result...
        condition = threading.Condition()
        # Here's where the result goes...
        result = []

        def cb(tp,body,condition=condition,result=result):
            condition.acquire()
            try:
                result.append((tp, body))
                condition.notify()
            finally:
                condition.release()

        # Sends a message to Tor...
        self._sendLock.acquire()
        try:
            self._queue.put(cb)
            self._send(tp, msg)
        finally:
            self._sendLock.release()

        # Now wait till the answer is in...
        condition.acquire()
        try:
            while not result:
                condition.wait()
        finally:
            condition.release()

        # ...And handle the answer appropriately.
        assert len(result) == 1
        tp, msg = result[0]
        if tp in expectedTypes:
            return tp, msg
        elif tp == MSG_TYPE.ERROR:
            if len(msg)<2:
                raise ProtocolError("(Truncated error message)")
            errCode, = struct.unpack("!H", msg[:2])
            raise ErrorReply((errCode,
                              ERR_CODES.get(errCode,"[unrecognized]"),
                              msg[2:]))
        else:
            raise ProtocolError("Unexpectd message type 0x%04x"%tp)

    def close(self):
        """Shut down this controller connection"""
        self._sendLock.acquire()
        try:
            self._queue.put("CLOSE")
            self._s.close()
        finally:
            self._sendLock.release()

    def authenticate(self, secret=""):
        """Send an authenticating secret to Tor.  You'll need to call this
           method before Tor can start.
        """
        self._sendAndRecv(MSG_TYPE.AUTH,secret)

    def get_option(self,name):
        """Return the value of the configuration option named 'name'.
        """
        if not isinstance(name, str):
            name = "".join(["%s\n"%s for s in name])
        tp,body = self._sendAndRecv(MSG_TYPE.GETCONF,name,[MSG_TYPE.CONFVALUE])
        return _parseKV(body)

    def set_option(self,key,value):
        """Set the value of the configuration option 'key' to the value 'value'.
        """
        self.set_options([key, value])

    def set_options(self,kvlist):
        """Given a list of [(key,value)] pairs, set them as configuration
           options.
        """
        msg = "".join(["%s %s\n" for k,v in kvlist])
        self._sendAndRecv(MSG_TYPE.SETCONF,msg)

    def get_info(self,name):
        """Return the value of the internal information field named 'named'.
        """
        if not isinstance(name, str):
            name = "".join(["%s\n"%s for s in name])
        tp, body = self._sendAndRecv(MSG_TYPE.GETINFO,name,[MSG_TYPE.INFOVALUE])
        kvs = body.split("\0")
        d = {}
        for i in xrange(0,len(kvs)-1,2):
            d[kvs[i]] = kvs[i+1]
        return d

    def set_events(self,events):
        """Change the list of events that the event handler is interested
           in to those in 'events', which is a list of EVENT_TYPE members.
        """
        self._sendAndRecv(MSG_TYPE.SETEVENTS,
                     "".join([struct.pack("!H", event) for event in events]))

    def save_conf(self):
        """Flush all configuration changes to disk.
        """
        self._sendAndRecv(s,MSG_TYPE.SAVECONF)

    def send_signal(self, sig):
        """Send the signal 'sig' to the Tor process; 'sig' must be a member of
           SIGNAL.
        """
        self._sendAndRecv(MSG_TYPE.SIGNAL,struct.pack("B",sig))

    def map_address(self, kvList):
        """Given a list of (old-address,new-address), have Tor redirect
           streams from old-address to new-address.  Old-address can be in a
           special "dont-care" form of "0.0.0.0" or ".".
        """
        msg = [ "%s %s\n"%(k,v) for k,v in kvList ]
        tp, body = self._sendAndRecv(MSG_TYPE.MAPADDRESS,"".join(msg))
        return _parseKV(body)

    def extend_circuit(self, circid, hops):
        """Tell Tor to extend the circuit identified by 'circid' through the
           servers named in the list "hops".
        """
        msg = struct.pack("!L",circid) + ",".join(hops) + "\0"
        tp, body = self._sendAndRecv(MSG_TYPE.EXTENDCIRCUIT,msg)
        if len(body) != 4:
            raise ProtocolError("Extendcircuit reply too short or long")
        return struct.unpack("!L",body)[0]

    def redirect_stream(self, streamid, newtarget):
        """Tell Tor to change the target address of the stream identified by
           'streamid' from its old value to 'newtarget'."""
        msg = struct.pack("!L",streamid) + newtarget + "\0"
        self._sendAndRecv(MSG_TYPE.REDIRECTSTREAM,msg)

    def attach_stream(self, streamid, circid):
        """Tell Tor To attach stream 'streamid' to circuit 'circid'."""
        msg = struct.pack("!LL",streamid, circid)
        self._sendAndRecv(MSG_TYPE.ATTACHSTREAM,msg)

    def close_stream(self, streamid, reason=0, flags=0):
        """Close the stream 'streamid'. """
        msg = struct.pack("!LBB",streamid,reason,flags)
        self._sendAndRecv(MSG_TYPE.CLOSESTREAM,msg)

    def close_circuit(self, circid, flags=0):
        """Close the circuit 'circid'."""
        msg = struct.pack("!LB",circid,flags)
        self._sendAndRecv(MSG_TYPE.CLOSECIRCUIT,msg)

    def post_descriptor(self, descriptor):
        """Tell Tor about a new descriptor in 'descriptor'."""
        self._sendAndRecv(MSG_TYPE.POSTDESCRIPTOR,descriptor)

class EventHandler:
    """An 'EventHandler' wraps callbacks for the events Tor can return."""
    def __init__(self):
        """Create a new EventHandler."""
        self._map = {
            EVENT_TYPE.CIRCSTATUS : self.circStatus,
            EVENT_TYPE.STREAMSTATUS : self.streamStatus,
            EVENT_TYPE.ORCONNSTATUS : self.orConnStatus,
            EVENT_TYPE.BANDWIDTH : self.circStatus,
            EVENT_TYPE.NEWDESC : self.newDesc,
            EVENT_TYPE.INFO_MSG : self.msg,
            EVENT_TYPE.NOTICE_MSG : self.msg,
            EVENT_TYPE.WARN_MSG : self.msg,
            EVENT_TYPE.ERR_MSG : self.msg,
            }

    def handle(self, evbody):
        """Dispatcher: called from Connection when an event is received."""
        evtype, args = self.decode(evbody)
        self._map.get(evtype, self.unknownEvent)(evtype, *args)

    def decode(self, body):
        """Unpack an event message into a type/arguments-tuple tuple."""
        if len(body)<2:
            raise ProtocolError("EVENT body too short.")
        evtype, = struct.unpack("!H", body[:2])
        body = body[2:]
        if evtype == EVENT_TYPE.CIRCSTATUS:
            if len(body)<5:
                raise ProtocolError("CIRCUITSTATUS event too short.")
            status,ident = struct.unpack("!BL", body[:5])
            path = _unterminate(body[5:]).split(",")
            args = status, ident, path
        elif evtype == EVENT_TYPE.STREAMSTATUS:
            if len(body)<5:
                raise ProtocolError("STREAMSTATUS event too short.")
            status,ident = struct.unpack("!BL", body[:5])
            target = _unterminate(body[5:])
            args = status, ident, target
        elif evtype == EVENT_TYPE.ORCONNSTATUS:
            if len(body)<2:
                raise ProtocolError("ORCONNSTATUS event too short.")
            status = ord(body[0])
            target = _unterminate(body[1:])
            args = status, target
        elif evtype == EVENT_TYPE.BANDWIDTH:
            if len(body)<8:
                raise ProtocolError("BANDWIDTH event too short.")
            read, written = struct.unpack("!LL",body[:8])
            args = read, written
        elif evtype == EVENT_TYPE.OBSOLETE_LOG:
            args = (_unterminate(body),)
        elif evtype == EVENT_TYPE.NEWDESC:
            args = (_unterminate(body).split(","),)
        elif EVENT_TYPE.DEBUG_MSG <= evtype <= EVENT_TYPE.ERR_MSG:
            args = (EVENT_TYPE.nameOf[evtype], _unterminate(body))
        else:
            args = (body,)

        return evtype, args

    def circStatus(self, status, circID, path):
        """Called when a circuit status changes if listening to CIRCSTATUS
           events.  'status' is a member of CIRC_STATUS; circID is a numeric
           circuit ID, and 'path' is the circuit's path so far as a list of
           names.
        """
        raise NotImplemented

    def streamStatus(self, status, circID, target):
        """Called when a stream status changes if listening to STREAMSTATUS
           events.  'status' is a member of STREAM_STATUS; streamID is a
           numeric stream ID, and 'target' is the destination of the stream.
        """
        raise NotImplemented

    def orConnStatus(self, status, target):
        """Called when an OR connection's status changes if listening to
           ORCONNSTATUS events. 'status' is a member of OR_CONN_STATUS; target
           is the OR in question.
        """
        raise NotImplemented

    def bandwidth(self, read, written):
        """Called once a second if listening to BANDWIDTH events.  'read' is
           the number of bytes read; 'written' is the number of bytes written.
        """
        raise NotImplemented

    def newDesc(self, identities):
        """Called when Tor learns a new server descriptor if listenting to
           NEWDESC events.
        """
        raise NotImplemented

    def msg(self, severity, message):
        """Called when a log message of a given severity arrives if listening
           to INFO_MSG, NOTICE_MSG, WARN_MSG, or ERR_MSG events."""
        raise NotImplemented

class DebugEventHandler(EventHandler):
    """Trivial event handler: dumps all events to stdout."""
    def __init__(self, out=None):
        if out is None:
            out = sys.stdout
        self._out = out

    def handle(self, body):
        evtype, args = self.decode(body)
        print >>self._out,EVENT_TYPE.nameOf[evtype],args

def secret_to_key(secret, s2k_specifier):
    c = ord(s2k_specifier[8])
    EXPBIAS = 6
    count = (16+(c&15)) << ((c>>4) + EXPBIAS)

    d = sha.new()
    tmp = s2k_specifier[:8]+secret
    slen = len(tmp)
    while count:
        if count > slen:
            d.update(tmp)
            count -= slen
        else:
            d.update(tmp[:count])
            count = 0
    return d.digest()

def urandom_rng(n):
    f = open('/dev/urandom', 'rb')
    try:
        return f.read(n)
    finally:
        f.close()

def s2k_gen(secret, rng):
    spec = "%s%s"%(rng(8), chr(96))
    return "16:%s"%(
        binascii.b2a_hex(spec + secret_to_key(secret, spec)))

def s2k_check(secret, k):
    assert k[:3] == "16:"

    k =  binascii.a2b_hex(k[3:])
    return secret_to_key(secret, k[:9]) == k[9:]

def run_example(host,port):
    print "host is %s:%d"%(host,port)
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((host,port))
    c = Connection(s)
    c.setEventHandler(DebugEventHandler())
    th = c.launchThread()
    c.authenticate()
    print "nick",`c.get_option("nickname")`
    print c.get_option("DirFetchPeriod\n")
    print `c.get_info("version")`
    #print `c.get_info("desc/name/moria1")`
    print `c.get_info("network-status")`
    print `c.get_info("addr-mappings/all")`
    print `c.get_info("addr-mappings/config")`
    print `c.get_info("addr-mappings/cache")`
    print `c.get_info("addr-mappings/control")`
    print `c.map_address([("0.0.0.0", "Foobar.com"),
                        ("1.2.3.4", "foobaz.com"),
                        ("frebnitz.com", "5.6.7.8"),
                        (".", "abacinator.onion")])`
    print `c.extend_circuit(0,["moria1"])`
    try:
        print `c.extend_circuit(0,[""])`
    except ErrorReply:
        print "got error. good."
    #send_signal(s,1)
    #save_conf(s)

    #set_option(s,"1")
    #set_option(s,"bandwidthburstbytes 100000")
    #set_option(s,"runasdaemon 1")
    #set_events(s,[EVENT_TYPE.WARN])
    c.set_events([EVENT_TYPE.ORCONNSTATUS, EVENT_TYPE.STREAMSTATUS,
                  EVENT_TYPE.CIRCSTATUS, EVENT_TYPE.INFO_MSG,
                  EVENT_TYPE.BANDWIDTH])

    th.join()
    return

if __name__ == '__main__':
    if len(sys.argv) > 2:
        print "Syntax: TorControl.py torhost:torport"
        sys.exit(0)
    else:
        sys.argv.append("localhost:9051")
    sh,sp = parseHostAndPort(sys.argv[1])
    run_example(sh,sp)


--- NEW FILE: TorExample.py ---
#!/usr/bin/python
# TorExample.py -- Python module to demonstrate tor controller functionality.
# Copyright 2005 Nick Mathewson -- See LICENSE for licensing information.
#$Id: TorExample.py,v 1.1 2005/06/04 02:42:56 nickm Exp $

import socket
import sys
from TorCtl import *

def getConnection(daemon=1):
    if sys.argv[1] == '--host':
        hostport = sys.argv[2]
        del sys.argv[1:3]
    elif sys.argv[1].startswith("--host="):
        hostport = sys.argv[1][7:]
        del sys.argv[1]
    else:
        hostport = "localhost:9100"
    host,port = parseHostAndPort(hostport)
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((host,port))
    conn = Connection(s)
    th = conn.launchThread(daemon)
    conn.authenticate("")
    return conn

def run():
    if len(sys.argv)<2:
        print "No command given."
    cmd = sys.argv[1].replace("-","_")
    del sys.argv[1]
    fn = globals().get("run_"+cmd)
    if fn is None:
        print "Unrecognized command:",cmd
    fn()

def run_set_config():
    conn = getConnection()
    if sys.argv[1] == '--save':
        save = 1
        del sys.argv[1]
    else:
        save = 0
    kvList = []
    for i in xrange(1, len(sys.argv), 2):
        kvList.append((sys.argv[i], sys.argv[i+1]))
    conn.set_options(kvList)
    if save:
        conn.save_conf()

def run_get_config():
    conn = getConnection()
    opts = conn.get_option(sys.argv[1:])
    for k in sys.argv[1:]:
        print "KEY:",k
        print "VALUE:",opts.get(k)

def run_get_info():
    conn = getConnection()
    opts = conn.get_option(sys.argv[1:])
    for k in sys.argv[1:]:
        print "KEY:",k
        print "VALUE:",opts.get(k)

def run_listen():
    m = { "circ": "CIRCSTATUS",
          "stream": "STREAMSTATUS",
          "orconn", "ORCONNSTATUS",
          "bw": "BANDWIDTH",
          "newdesc": "NEWDESC",
          "info": "INFO_MSG",
          "notice": "NOTICE_MSG",
          "warn": "WARN_MSG",
          "error": "ERR_MSG",
          }
    conn = getConnection(daemon=0)
    events = []
    for kw in sys.argv[1:]:
        try:
            events.append(EVENT_TYPE.get(m[kw]))
        except KeyError:
            print "Unrecognized event %r; recognized events are: %s"%(
                kw, ", ".join(m.keys()))
    if not events:
        return

    conn.setEventHandler(DebugEventHandler())
    conn.listenForEvents(events)

def run_signal():
    conn = getConnection()
    m = { 'reload': "HUP", 'shutdown': "INT", 'dump': "USR1",
          'debug': "USR2", 'halt': 'TERM' }
    if len(sys.argv)<2:
        print "Syntax: signal [signal]"
        return
    try:
        sig = SIGNAL.get(m[sys.argv[1]])
    except KeyError:
        print "Unrecognized signal %r. Options are %s"%(
            sys.argv[1], ", ".join(m.keys()))

    conn.signal(sig)

def run_authdemo():
    conn = getConnection()
    conn.close()
    #XXXX



More information about the tor-commits mailing list