2 # TorControl.py -- Python module to interface with Tor Control interface.
3 # Copyright 2005 Nick Mathewson -- See LICENSE for licensing information.
10 #__all__ = [ "MSG_TYPE", "" ]
13 # Helper: define an ordered dense name-to-number 1-1 mapping.
14 def __init__(self
, start
, names
):
18 setattr(self
,name
,idx
)
19 self
.nameOf
[idx
] = name
22 # Helper: define an ordered sparse name-to-number 1-1 mapping.
23 def __init__(self
, **args
):
24 self
.__dict
__.update(args
)
26 for k
,v
in args
.items():
29 # Message types that client or server can send.
30 MSG_TYPE
= _Enum(0x0000,
54 # Make sure that the enumeration code is working.
55 assert MSG_TYPE
.SAVECONF
== 0x0008
56 assert MSG_TYPE
.CLOSECIRCUIT
== 0x0014
58 # Types of "EVENT" message.
59 EVENT_TYPE
= _Enum(0x0001,
73 assert EVENT_TYPE
.ERR_MSG
== 0x000B
74 assert EVENT_TYPE
.OBSOLETE_LOG
== 0x0005
76 # Status codes for "CIRCSTATUS" events.
77 CIRC_STATUS
= _Enum(0x00,
84 # Status codes for "STREAMSTATUS" events
85 STREAM_STATUS
= _Enum(0x00,
95 # Status codes for "ORCONNSTATUS" events
96 OR_CONN_STATUS
= _Enum(0x00,
97 ["LAUNCHED","CONNECTED","FAILED","CLOSED"])
99 # Signal codes for "SIGNAL" events.
100 SIGNAL
= _Enum2(HUP
=0x01,INT
=0x02,USR1
=0x0A,USR2
=0x0C,TERM
=0x0F)
102 # Error codes for "ERROR" events.
104 0x0000 : "Unspecified error",
105 0x0001 : "Internal error",
106 0x0002 : "Unrecognized message type",
107 0x0003 : "Syntax error",
108 0x0004 : "Unrecognized configuration key",
109 0x0005 : "Invalid configuration value",
110 0x0006 : "Unrecognized byte code",
111 0x0007 : "Unauthorized",
112 0x0008 : "Failed authentication attempt",
113 0x0009 : "Resource exhausted",
114 0x000A : "No such stream",
115 0x000B : "No such circuit",
116 0x000C : "No such OR"
119 class TorCtlError(Exception):
120 "Generic error raised by TorControl code."
123 class ProtocolError(TorCtlError
):
124 "Raised on violations in Tor controller protocol"
127 class ErrorReply(TorCtlError
):
131 def parseHostAndPort(h
):
132 host
, port
= "localhost", 9051
139 print "Bad hostname %r"%h
149 def _unpack_msg(msg
):
150 "return None, minLength, body or type,body,rest"
153 length
,type = struct
.unpack("!HH",msg
)
154 if len(msg
) >= 4+length
:
155 return type,msg
[4:4+length
],msg
[4+length
:]
157 return None,4+length
,msg
159 def _minLengthToPack(bytes
):
160 whole
,left
= divmod(bytes
,65535)
162 return whole
*(65535+4)+4+left
164 return whole
*(65535+4)
167 "returns as for _unpack_msg"
168 tp
,body
,rest
= _unpack_msg(msg
)
169 if tp
!= MSG_TYPE
.FRAGMENTHEADER
:
170 return tp
, body
, rest
173 raise ProtocolError("FRAGMENTHEADER message too short")
175 realType
,realLength
= struct
.unpack("!HL", body
[:6])
177 # Okay; could the message _possibly_ be here?
178 minLength
= _minLengthToPack(realLength
+6)
179 if len(msg
) < minLength
:
180 return None, minLength
, msg
182 # Okay; optimistically try to build up the msg.
184 lenSoFarLen
= len(body
)-6
185 while len(rest
)>=4 and lenSoFar
< realLength
:
186 ln
, tp
= struct
.unpack("!HH", rest
[:4])
187 if tp
!= MSG_TYPE
.FRAGMENT
:
188 raise ProtocolError("Missing FRAGMENT message")
189 soFar
.append(rest
[4:4+ln
])
193 leftInPacket
= 4+ln
-len(rest
)
198 if lenSoFar
== realLength
:
199 return realType
, "".join(soFar
), rest
200 elif lenSoFar
> realLength
:
201 raise ProtocolError("Bad fragmentation: message longer than declared")
203 inOtherPackets
= realLength
-lenSoFar
-leftInPacket
204 minLength
= _minLengthToPack(inOtherPackets
)
205 return None, len(msg
)+leftInPacket
+inOtherPackets
, msg
210 length
,type = struct
.unpack("!HH",header
)
212 body
= s
.recv(length
)
213 return length
,type,body
215 def receive_message(s
):
216 length
, tp
, body
= _receive_msg(s
)
217 if tp
!= MSG_TYPE
.FRAGMENTHEADER
:
218 return length
, tp
, body
220 raise ProtocolError("FRAGMENTHEADER message too short")
221 realType
,realLength
= struct
.unpack("!HL", body
[:6])
225 length
, tp
, body
= _receive_msg(s
)
226 if tp
!= MSG_TYPE
.FRAGMENT
:
227 raise ProtocolError("Missing FRAGMENT message")
230 if soFar
== realLength
:
231 return realLength
, realType
, "".join(data
)
232 elif soFar
> realLengtH
:
233 raise ProtocolError("FRAGMENT message too long!")
235 _event_handler
= None
236 def receive_reply(s
, expected
=None):
238 _
, tp
, body
= receive_message(s
)
239 if tp
== MSG_TYPE
.EVENT
:
240 if _event_handler
is not None:
242 elif tp
== MSG_TYPE
.ERROR
:
244 raise ProtocolError("(Truncated error message)")
245 errCode
, = struct
.unpack("!H", body
[:2])
246 raise ErrorReply((errCode
,
247 ERR_CODES
.get(errCode
,"[unrecognized]"),
249 elif (expected
is not None) and (tp
not in expected
):
250 raise ProtocolError("Unexpected message type 0x%04x"%tp
)
254 def pack_message(type, body
=""):
257 reqheader
= struct
.pack("!HH", length
, type)
258 return "%s%s"%(reqheader
,body
)
260 fragheader
= struct
.pack("!HHHL",
261 65535, MSG_TYPE
.FRAGMENTHEADER
, type, length
)
262 msgs
= [ fragheader
, body
[:65535-6] ]
263 body
= body
[65535-6:]
265 if len(body
) > 65535:
269 fragheader
= struct
.pack("!HH", MSG_TYPE
.FRAGMENT
, fl
)
270 msgs
.append(fragheader
)
271 msgs
.append(body
[:fl
])
276 def send_message(s
, type, body
=""):
277 s
.sendall(pack_message(type, body
))
280 send_message(s
,MSG_TYPE
.AUTH
)
281 type,body
= receive_reply(s
)
284 def _parseKV(body
,sep
=" ",term
="\n"):
286 for line
in body
.split(term
):
287 if not line
: continue
289 k
, v
= line
.split(sep
,1)
293 def get_option(s
,name
):
294 send_message(s
,MSG_TYPE
.GETCONF
,name
)
295 tp
,body
= receive_reply(s
,[MSG_TYPE
.CONFVALUE
])
296 return _parseKV(body
)
298 def set_option(s
,msg
):
299 send_message(s
,MSG_TYPE
.SETCONF
,msg
)
300 tp
,body
= receive_reply(s
,[MSG_TYPE
.DONE
])
302 def get_info(s
,name
):
303 send_message(s
,MSG_TYPE
.GETINFO
,name
)
304 tp
,body
= receive_reply(s
,[MSG_TYPE
.INFOVALUE
])
305 kvs
= body
.split("\0")
307 for i
in xrange(0,len(kvs
)-1,2):
311 def set_events(s
,events
):
312 send_message(s
,MSG_TYPE
.SETEVENTS
,
313 "".join([struct
.pack("!H", event
) for event
in events
]))
314 type,body
= receive_reply(s
,[MSG_TYPE
.DONE
])
318 send_message(s
,MSG_TYPE
.SAVECONF
)
319 receive_reply(s
,[MSG_TYPE
.DONE
])
321 def send_signal(s
, sig
):
322 send_message(s
,MSG_TYPE
.SIGNAL
,struct
.pack("B",sig
))
323 receive_reply(s
,[MSG_TYPE
.DONE
])
325 def map_address(s
, kv
):
326 msg
= [ "%s %s\n"%(k
,v
) for k
,v
in kv
]
327 send_message(s
,MSG_TYPE
.MAPADDRESS
,"".join(msg
))
328 tp
, body
= receive_reply(s
,[MSG_TYPE
.DONE
])
329 return _parseKV(body
)
331 def extend_circuit(s
, circid
, hops
):
332 msg
= struct
.pack("!L",circid
) + ",".join(hops
) + "\0"
333 send_message(s
,MSG_TYPE
.EXTENDCIRCUIT
,msg
)
334 tp
, body
= receive_reply(s
,[MSG_TYPE
.DONE
])
336 raise ProtocolError("Extendcircuit reply too short or long")
337 return struct
.unpack("!L",body
)[0]
339 def redirect_stream(s
, streamid
, newtarget
):
340 msg
= struct
.pack("!L",streamid
) + newtarget
+ "\0"
341 send_message(s
,MSG_TYPE
.REDIRECTSTREAM
,msg
)
342 tp
,body
= receive_reply(s
,[MSG_TYPE
.DONE
])
344 def attach_stream(s
, streamid
, circid
):
345 msg
= struct
.pack("!LL",streamid
, circid
)
346 send_message(s
,MSG_TYPE
.ATTACHSTREAM
,msg
)
347 tp
,body
= receive_reply(s
,[MSG_TYPE
.DONE
])
349 def close_stream(s
, streamid
, reason
=0, flags
=0):
350 msg
= struct
.pack("!LBB",streamid
,reason
,flags
)
351 send_message(s
,MSG_TYPE
.CLOSESTREAM
,msg
)
352 tp
,body
= receive_reply(s
,[MSG_TYPE
.DONE
])
354 def close_circuit(s
, circid
, flags
=0):
355 msg
= struct
.pack("!LB",circid
,flags
)
356 send_message(s
,MSG_TYPE
.CLOSECIRCUIT
,msg
)
357 tp
,body
= receive_reply(s
,[MSG_TYPE
.DONE
])
359 def post_descriptor(s
, descriptor
):
360 send_message(s
,MSG_TYPE
.POSTDESCRIPTOR
,descriptor
)
361 tp
,body
= receive_reply(s
,[MSG_TYPE
.DONE
])
370 def unpack_event(body
):
372 raise ProtocolError("EVENT body too short.")
373 evtype
, = struct
.unpack("!H", body
[:2])
375 if evtype
== EVENT_TYPE
.CIRCSTATUS
:
377 raise ProtocolError("CIRCUITSTATUS event too short.")
378 status
,ident
= struct
.unpack("!BL", body
[:5])
379 path
= _unterminate(body
[5:]).split(",")
380 args
= status
, ident
, path
381 elif evtype
== EVENT_TYPE
.STREAMSTATUS
:
383 raise ProtocolError("CIRCUITSTATUS event too short.")
384 status
,ident
= struct
.unpack("!BL", body
[:5])
385 target
= _unterminate(body
[5:])
386 args
= status
, ident
, target
387 elif evtype
== EVENT_TYPE
.ORCONNSTATUS
:
389 raise ProtocolError("CIRCUITSTATUS event too short.")
390 status
= ord(body
[0])
391 target
= _unterminate(body
[1:])
392 args
= status
, target
393 elif evtype
== EVENT_TYPE
.BANDWIDTH
:
395 raise ProtocolError("BANDWIDTH event too short.")
396 read
, written
= struct
.unpack("!LL",body
[:8])
398 elif evtype
== EVENT_TYPE
.OBSOLETE_LOG
:
399 args
= (_unterminate(body
),)
400 elif evtype
== EVENT_TYPE
.NEWDESC
:
401 args
= (_unterminate(body
).split(","),)
402 elif EVENT_TYPE
.DEBUG_MSG
<= evtype
<= EVENT_TYPE
.ERR_MSG
:
403 args
= (EVENT_TYPE
.nameOf(evtype
), _unterminate(body
))
409 def listen_for_events(s
):
411 _
,type,body
= receive_message(s
)
412 print unpack_event(body
)
415 def do_main_loop(host
,port
):
416 print "host is %s:%d"%(host
,port
)
417 s
= socket
.socket(socket
.AF_INET
, socket
.SOCK_STREAM
)
418 s
.connect((host
,port
))
420 print "nick",`
get_option(s
,"nickname")`
421 print get_option(s
,"DirFetchPeriod\n")
422 print `
get_info(s
,"version")`
423 #print `get_info(s,"desc/name/moria1")`
424 print `
get_info(s
,"network-status")`
425 print `
get_info(s
,"addr-mappings/all")`
426 print `
get_info(s
,"addr-mappings/config")`
427 print `
get_info(s
,"addr-mappings/cache")`
428 print `
get_info(s
,"addr-mappings/control")`
429 print `
map_address(s
, [("0.0.0.0", "Foobar.com"),
430 ("1.2.3.4", "foobaz.com"),
431 ("frebnitz.com", "5.6.7.8"),
432 (".", "abacinator.onion")])`
433 print `
extend_circuit(s
,0,["moria1"])`
439 #set_option(s,"bandwidthburstbytes 100000")
440 #set_option(s,"runasdaemon 1")
441 #set_events(s,[EVENT_TYPE.WARN])
442 set_events(s
,[EVENT_TYPE
.OBSOLETE_LOG
])
448 if __name__
== '__main__':
449 if len(sys
.argv
) != 2:
450 print "Syntax: tor-control.py torhost:torport"
452 sh
,sp
= parseHostAndPort(sys
.argv
[1])