|
@@ -1,464 +0,0 @@
|
|
|
-#!/usr/bin/python
|
|
|
-# TorControl.py -- Python module to interface with Tor Control interface.
|
|
|
-# Copyright 2005 Nick Mathewson -- See LICENSE for licensing information.
|
|
|
-#$Id$
|
|
|
-
|
|
|
-# THIS MODULE IS OBSOLETE!
|
|
|
-#
|
|
|
-# There is a "TorCtl.py" module in the "control" directory in Tor CVS;
|
|
|
-# this new module supports the new ('version 1') Tor controller protocol
|
|
|
-# and has a far nicer and more object-oriented design than this module does.
|
|
|
-#
|
|
|
-# No further support will be done on this module.
|
|
|
-
|
|
|
-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)
|
|
|
-
|