123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309 |
- #!/usr/bin/python
- #$Id$
- import socket
- import struct
- import sys
- class _Enum:
- def __init__(self, start, names):
- self.nameOf = {}
- idx = start
- for name in names:
- setattr(self,name,idx)
- self.nameOf[idx] = name
- idx += 1
- 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",
- ])
- assert MSG_TYPE.SAVECONF = 0x0008
- assert MSG_TYPE.CLOSECIRCUIT = 0x0014
- EVENT_TYPE = _ENUM(0x0001,
- ["CIRCSTATUS",
- "STREAMSTATUS",
- "ORCONNSTATUS",
- "BANDWIDTH",
- "WARN",
- "NEWDESC"])
- 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):
- pass
- class ProtocolError(TorCtlError):
- 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 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?
- minPackets,minSlop = divmod(realLength+6,65535)
- minLength = (minPackets*(65535+4))+4+minSlop
- if len(msg) < minLength:
- return None, minLength, msg
- # Okay; optimistically try to build up the msg.
- soFar = [ body[6:] ]
- lenSoFarLen = len(body)-6
- while rest 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
- rest = rest[4+ln:]
- if lenSoFar == realLength:
- return realType, "".join(soFar), rest
- elif lenSoFar > realLength:
- raise ProtocolError("Bad fragmentation: message longer than declared")
- else:
- return None, len(msg)+(realLength-lenSoFar), msg
- 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(tp, 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])
- return body
- def listen_for_events(s):
- while(1):
- _,type,body = receive_message(s)
- print "event",type
- 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"])`
- 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.WARN,EVENT_TYPE.STREAMSTATUS])
- listen_for_events(s)
- return
- if __name__ == '__main__':
- if len(sys.argv) != 2:
- print "Syntax: tor-control.py torhost:torport"
- sys.exit(0)
- sh,sp = parseHostAndPort(sys.argv[1])
- do_main_loop(sh,sp)
|