| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458 | 
							- #!/usr/bin/python
 
- # TorControl.py -- Python module to interface with Tor Control interface.
 
- # Copyright 2005 Nick Mathewson -- See LICENSE for licensing information.
 
- #$Id$
 
- import socket
 
- import struct
 
- import sys
 
- #__all__ = [ "MSG_TYPE", "" ]
 
- 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):
 
-     ""
 
-     pass
 
- def parseHostAndPort(h):
 
-     host, port = "localhost", 9051
 
-     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_msg(msg):
 
-     "return None, minLength, body or type,body,rest"
 
-     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):
 
-     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_msg"
 
-     tp,body,rest = _unpack_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_msg(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):
 
-     length, tp, body = _receive_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_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!")
 
- _event_handler = None
 
- def receive_reply(s, expected=None):
 
-     while 1:
 
-         _, tp, body = receive_message(s)
 
-         if tp == MSG_TYPE.EVENT:
 
-             if _event_handler is not None:
 
-                 _event_handler(body)
 
-         elif tp == MSG_TYPE.ERROR:
 
-             if len(body)<2:
 
-                 raise ProtocolError("(Truncated error message)")
 
-             errCode, = struct.unpack("!H", body[:2])
 
-             raise ErrorReply((errCode,
 
-                               ERR_CODES.get(errCode,"[unrecognized]"),
 
-                               body[2:]))
 
-         elif (expected is not None) and (tp not in expected):
 
-             raise ProtocolError("Unexpected message type 0x%04x"%tp)
 
-         else:
 
-             return tp, body
 
- def pack_message(type, body=""):
 
-     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 send_message(s, type, body=""):
 
-     s.sendall(pack_message(type, body))
 
- def authenticate(s):
 
-     send_message(s,MSG_TYPE.AUTH)
 
-     type,body = receive_reply(s)
 
-     return
 
- def _parseKV(body,sep=" ",term="\n"):
 
-     res = []
 
-     for line in body.split(term):
 
-         if not line: continue
 
-         print repr(line)
 
-         k, v = line.split(sep,1)
 
-         res.append((k,v))
 
-     return res
 
- def get_option(s,name):
 
-     send_message(s,MSG_TYPE.GETCONF,name)
 
-     tp,body = receive_reply(s,[MSG_TYPE.CONFVALUE])
 
-     return _parseKV(body)
 
- def set_option(s,msg):
 
-     send_message(s,MSG_TYPE.SETCONF,msg)
 
-     tp,body = receive_reply(s,[MSG_TYPE.DONE])
 
- def get_info(s,name):
 
-     send_message(s,MSG_TYPE.GETINFO,name)
 
-     tp,body = receive_reply(s,[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(s,events):
 
-     send_message(s,MSG_TYPE.SETEVENTS,
 
-                  "".join([struct.pack("!H", event) for event in events]))
 
-     type,body = receive_reply(s,[MSG_TYPE.DONE])
 
-     return
 
- def save_conf(s):
 
-     send_message(s,MSG_TYPE.SAVECONF)
 
-     receive_reply(s,[MSG_TYPE.DONE])
 
- def send_signal(s, sig):
 
-     send_message(s,MSG_TYPE.SIGNAL,struct.pack("B",sig))
 
-     receive_reply(s,[MSG_TYPE.DONE])
 
- def map_address(s, kv):
 
-     msg = [ "%s %s\n"%(k,v) for k,v in kv ]
 
-     send_message(s,MSG_TYPE.MAPADDRESS,"".join(msg))
 
-     tp, body = receive_reply(s,[MSG_TYPE.DONE])
 
-     return _parseKV(body)
 
- def extend_circuit(s, circid, hops):
 
-     msg = struct.pack("!L",circid) + ",".join(hops) + "\0"
 
-     send_message(s,MSG_TYPE.EXTENDCIRCUIT,msg)
 
-     tp, body = receive_reply(s,[MSG_TYPE.DONE])
 
-     if len(body) != 4:
 
-         raise ProtocolError("Extendcircuit reply too short or long")
 
-     return struct.unpack("!L",body)[0]
 
- def redirect_stream(s, streamid, newtarget):
 
-     msg = struct.pack("!L",streamid) + newtarget + "\0"
 
-     send_message(s,MSG_TYPE.REDIRECTSTREAM,msg)
 
-     tp,body = receive_reply(s,[MSG_TYPE.DONE])
 
- def attach_stream(s, streamid, circid):
 
-     msg = struct.pack("!LL",streamid, circid)
 
-     send_message(s,MSG_TYPE.ATTACHSTREAM,msg)
 
-     tp,body = receive_reply(s,[MSG_TYPE.DONE])
 
- def close_stream(s, streamid, reason=0, flags=0):
 
-     msg = struct.pack("!LBB",streamid,reason,flags)
 
-     send_message(s,MSG_TYPE.CLOSESTREAM,msg)
 
-     tp,body = receive_reply(s,[MSG_TYPE.DONE])
 
- def close_circuit(s, circid, flags=0):
 
-     msg = struct.pack("!LB",circid,flags)
 
-     send_message(s,MSG_TYPE.CLOSECIRCUIT,msg)
 
-     tp,body = receive_reply(s,[MSG_TYPE.DONE])
 
- def post_descriptor(s, descriptor):
 
-     send_message(s,MSG_TYPE.POSTDESCRIPTOR,descriptor)
 
-     tp,body = receive_reply(s,[MSG_TYPE.DONE])
 
- def _unterminate(s):
 
-     if s[-1] == '\0':
 
-         return s[:-1]
 
-     else:
 
-         return s
 
- def unpack_event(body):
 
-     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("CIRCUITSTATUS 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("CIRCUITSTATUS 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 listen_for_events(s):
 
-     while(1):
 
-         _,type,body = receive_message(s)
 
-         print unpack_event(body)
 
-     return
 
- def do_main_loop(host,port):
 
-     print "host is %s:%d"%(host,port)
 
-     s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 
-     s.connect((host,port))
 
-     authenticate(s)
 
-     print "nick",`get_option(s,"nickname")`
 
-     print get_option(s,"DirFetchPeriod\n")
 
-     print `get_info(s,"version")`
 
-     #print `get_info(s,"desc/name/moria1")`
 
-     print `get_info(s,"network-status")`
 
-     print `get_info(s,"addr-mappings/all")`
 
-     print `get_info(s,"addr-mappings/config")`
 
-     print `get_info(s,"addr-mappings/cache")`
 
-     print `get_info(s,"addr-mappings/control")`
 
-     print `map_address(s, [("0.0.0.0", "Foobar.com"),
 
-                            ("1.2.3.4", "foobaz.com"),
 
-                            ("frebnitz.com", "5.6.7.8"),
 
-                            (".", "abacinator.onion")])`
 
-     print `extend_circuit(s,0,["moria1"])`
 
-     print '========'
 
-     print `extend_circuit(s,0,[""])`
 
-     print '========'
 
-     #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])
 
-     set_events(s,[EVENT_TYPE.OBSOLETE_LOG])
 
-     listen_for_events(s)
 
-     return
 
- if __name__ == '__main__':
 
-     if len(sys.argv) != 2:
 
-         print "Syntax: TorControl.py torhost:torport"
 
-         sys.exit(0)
 
-     sh,sp = parseHostAndPort(sys.argv[1])
 
-     do_main_loop(sh,sp)
 
 
  |