diff options
author | Nick Mathewson <nickm@torproject.org> | 2005-03-25 20:52:51 +0000 |
---|---|---|
committer | Nick Mathewson <nickm@torproject.org> | 2005-03-25 20:52:51 +0000 |
commit | 5c0e6b587ae5813b65a2750ecfec8396e3f8b44f (patch) | |
tree | b2b9f8346ce5eeef2141f334d0b44ddfc6bcb061 /contrib | |
parent | c5845c17841d5844560af2535e6e77cbbcd36809 (diff) | |
download | tor-5c0e6b587ae5813b65a2750ecfec8396e3f8b44f.tar.gz tor-5c0e6b587ae5813b65a2750ecfec8396e3f8b44f.zip |
Rename tor-control.py to TorControl.py; begin making it into a useful library instead of a lame testing script.
svn:r3879
Diffstat (limited to 'contrib')
-rwxr-xr-x | contrib/TorControl.py | 309 | ||||
-rwxr-xr-x | contrib/tor-control.py | 255 |
2 files changed, 309 insertions, 255 deletions
diff --git a/contrib/TorControl.py b/contrib/TorControl.py new file mode 100755 index 0000000000..155603644d --- /dev/null +++ b/contrib/TorControl.py @@ -0,0 +1,309 @@ +#!/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) + diff --git a/contrib/tor-control.py b/contrib/tor-control.py deleted file mode 100755 index 72333a4c3e..0000000000 --- a/contrib/tor-control.py +++ /dev/null @@ -1,255 +0,0 @@ -#!/usr/bin/python2 -#$Id$ - -import socket -import struct -import sys - -MSG_TYPE_ERROR = 0x0000 -MSG_TYPE_DONE = 0x0001 -MSG_TYPE_SETCONF = 0x0002 -MSG_TYPE_GETCONF = 0x0003 -MSG_TYPE_CONFVALUE = 0x0004 -MSG_TYPE_SETEVENTS = 0x0005 -MSG_TYPE_EVENT = 0x0006 -MSG_TYPE_AUTH = 0x0007 -MSG_TYPE_SAVECONF = 0x0008 -MSG_TYPE_SIGNAL = 0x0009 -MSG_TYPE_MAPADDRESS = 0x000A -MSG_TYPE_GETINFO = 0x000B -MSG_TYPE_INFOVALUE = 0x000C -MSG_TYPE_EXTENDCIRCUIT = 0x000D -MSG_TYPE_ATTACHSTREAM = 0x000E -MSG_TYPE_POSTDESCRIPTOR = 0x000F -MSG_TYPE_FRAGMENTHEADER = 0x0010 -MSG_TYPE_FRAGMENT = 0x0011 -MSG_TYPE_REDIRECTSTREAM = 0x0012 -MSG_TYPE_CLOSESTREAM = 0x0013 -MSG_TYPE_CLOSECIRCUIT = 0x0014 - -EVENT_TYPE_CIRCSTATUS = 0x0001 -EVENT_TYPE_STREAMSTATUS = 0x0002 -EVENT_TYPE_ORCONNSTATUS = 0x0003 -EVENT_TYPE_BANDWIDTH = 0x0004 -EVENT_TYPE_WARN = 0x0005 -EVENT_TYPE_NEWDESC = 0x0006 - -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 _receive_msg(s): - body = "" - header = s.recv(4) - length,type = struct.unpack("!HH",header) - if length: - body = s.recv(length) - 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(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) - |