TorControl.py 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. #!/usr/bin/python
  2. #$Id$
  3. import socket
  4. import struct
  5. import sys
  6. class _Enum:
  7. def __init__(self, start, names):
  8. self.nameOf = {}
  9. idx = start
  10. for name in names:
  11. setattr(self,name,idx)
  12. self.nameOf[idx] = name
  13. idx += 1
  14. MSG_TYPE = _Enum(0x0000,
  15. ["ERROR",
  16. "DONE",
  17. "SETCONF",
  18. "GETCONF",
  19. "CONFVALUE",
  20. "SETEVENTS",
  21. "EVENT",
  22. "AUTH",
  23. "SAVECONF",
  24. "SIGNAL",
  25. "MAPADDRESS",
  26. "GETINFO",
  27. "INFOVALUE",
  28. "EXTENDCIRCUIT",
  29. "ATTACHSTREAM",
  30. "POSTDESCRIPTOR",
  31. "FRAGMENTHEADER",
  32. "FRAGMENT",
  33. "REDIRECTSTREAM",
  34. "CLOSESTREAM",
  35. "CLOSECIRCUIT",
  36. ])
  37. assert MSG_TYPE.SAVECONF = 0x0008
  38. assert MSG_TYPE.CLOSECIRCUIT = 0x0014
  39. EVENT_TYPE = _ENUM(0x0001,
  40. ["CIRCSTATUS",
  41. "STREAMSTATUS",
  42. "ORCONNSTATUS",
  43. "BANDWIDTH",
  44. "WARN",
  45. "NEWDESC"])
  46. ERR_CODES = {
  47. 0x0000 : "Unspecified error",
  48. 0x0001 : "Internal error",
  49. 0x0002 : "Unrecognized message type",
  50. 0x0003 : "Syntax error",
  51. 0x0004 : "Unrecognized configuration key",
  52. 0x0005 : "Invalid configuration value",
  53. 0x0006 : "Unrecognized byte code",
  54. 0x0007 : "Unauthorized",
  55. 0x0008 : "Failed authentication attempt",
  56. 0x0009 : "Resource exhausted",
  57. 0x000A : "No such stream",
  58. 0x000B : "No such circuit",
  59. 0x000C : "No such OR"
  60. }
  61. class TorCtlError(Exception):
  62. pass
  63. class ProtocolError(TorCtlError):
  64. pass
  65. class ErrorReply(TorCtlError):
  66. pass
  67. def parseHostAndPort(h):
  68. host, port = "localhost", 9051
  69. if ":" in h:
  70. i = h.index(":")
  71. host = h[:i]
  72. try:
  73. port = int(h[i+1:])
  74. except ValueError:
  75. print "Bad hostname %r"%h
  76. sys.exit(1)
  77. elif h:
  78. try:
  79. port = int(h)
  80. except ValueError:
  81. host = h
  82. return host, port
  83. def _unpack_msg(msg):
  84. "return None, minLength, body or type,body,rest"
  85. if len(msg) < 4:
  86. return None, 4, msg
  87. length,type = struct.unpack("!HH",msg)
  88. if len(msg) >= 4+length:
  89. return type,msg[4:4+length],msg[4+length:]
  90. else:
  91. return None,4+length,msg
  92. def unpack_msg(msg):
  93. "returns as for _unpack_msg"
  94. tp,body,rest = _unpack_msg(msg)
  95. if tp != MSG_TYPE.FRAGMENTHEADER:
  96. return tp, body, rest
  97. if len(body) < 6:
  98. raise ProtocolError("FRAGMENTHEADER message too short")
  99. realType,realLength = struct.unpack("!HL", body[:6])
  100. # Okay; could the message _possibly_ be here?
  101. minPackets,minSlop = divmod(realLength+6,65535)
  102. minLength = (minPackets*(65535+4))+4+minSlop
  103. if len(msg) < minLength:
  104. return None, minLength, msg
  105. # Okay; optimistically try to build up the msg.
  106. soFar = [ body[6:] ]
  107. lenSoFarLen = len(body)-6
  108. while rest and lenSoFar < realLength:
  109. ln, tp = struct.unpack("!HH" rest[:4])
  110. if tp != MSG_TYPE.FRAGMENT:
  111. raise ProtocolError("Missing FRAGMENT message")
  112. soFar.append(rest[4:4+ln])
  113. lenSoFar += ln
  114. rest = rest[4+ln:]
  115. if lenSoFar == realLength:
  116. return realType, "".join(soFar), rest
  117. elif lenSoFar > realLength:
  118. raise ProtocolError("Bad fragmentation: message longer than declared")
  119. else:
  120. return None, len(msg)+(realLength-lenSoFar), msg
  121. def receive_message(s):
  122. length, tp, body = _receive_msg(s)
  123. if tp != MSG_TYPE.FRAGMENTHEADER:
  124. return length, tp, body
  125. if length < 6:
  126. raise ProtocolError("FRAGMENTHEADER message too short")
  127. realType,realLength = struct.unpack("!HL", body[:6])
  128. data = [ body[6:] ]
  129. soFar = len(data[0])
  130. while 1:
  131. length, tp, body = _receive_msg(s)
  132. if tp != MSG_TYPE.FRAGMENT:
  133. raise ProtocolError("Missing FRAGMENT message")
  134. soFar += length
  135. data.append(body)
  136. if soFar == realLength:
  137. return realLength, realType, "".join(data)
  138. elif soFar > realLengtH:
  139. raise ProtocolError("FRAGMENT message too long!")
  140. _event_handler = None
  141. def receive_reply(s, expected=None):
  142. while 1:
  143. _, tp, body = receive_message(s)
  144. if tp == MSG_TYPE.EVENT:
  145. if _event_handler is not None:
  146. _event_handler(tp, body)
  147. elif tp == MSG_TYPE.ERROR:
  148. if len(body)<2:
  149. raise ProtocolError("(Truncated error message)")
  150. errCode, = struct.unpack("!H", body[:2])
  151. raise ErrorReply((errCode,
  152. ERR_CODES.get(errCode,"[unrecognized]"),
  153. body[2:]))
  154. elif (expected is not None) and (tp not in expected):
  155. raise ProtocolError("Unexpected message type 0x%04x"%tp)
  156. else:
  157. return tp, body
  158. def pack_message(type, body=""):
  159. length = len(body)
  160. if length < 65536:
  161. reqheader = struct.pack("!HH", length, type)
  162. return "%s%s"%(reqheader,body)
  163. fragheader = struct.pack("!HHHL",
  164. 65535, MSG_TYPE.FRAGMENTHEADER, type, length)
  165. msgs = [ fragheader, body[:65535-6] ]
  166. body = body[65535-6:]
  167. while body:
  168. if len(body) > 65535:
  169. fl = 65535
  170. else:
  171. fl = len(body)
  172. fragheader = struct.pack("!HH", MSG_TYPE.FRAGMENT, fl)
  173. msgs.append(fragheader)
  174. msgs.append(body[:fl])
  175. body = body[fl:]
  176. return "".join(msgs)
  177. def send_message(s, type, body=""):
  178. s.sendall(pack_message(type, body))
  179. def authenticate(s):
  180. send_message(s,MSG_TYPE.AUTH)
  181. type,body = receive_reply(s)
  182. return
  183. def _parseKV(body,sep=" ",term="\n"):
  184. res = []
  185. for line in body.split(term):
  186. if not line: continue
  187. print repr(line)
  188. k, v = line.split(sep,1)
  189. res.append((k,v))
  190. return res
  191. def get_option(s,name):
  192. send_message(s,MSG_TYPE.GETCONF,name)
  193. tp,body = receive_reply(s,[MSG_TYPE.CONFVALUE])
  194. return _parseKV(body)
  195. def set_option(s,msg):
  196. send_message(s,MSG_TYPE.SETCONF,msg)
  197. tp,body = receive_reply(s,[MSG_TYPE.DONE])
  198. def get_info(s,name):
  199. send_message(s,MSG_TYPE.GETINFO,name)
  200. tp,body = receive_reply(s,[MSG_TYPE.INFOVALUE])
  201. kvs = body.split("\0")
  202. d = {}
  203. for i in xrange(0,len(kvs)-1,2):
  204. d[kvs[i]] = kvs[i+1]
  205. return d
  206. def set_events(s,events):
  207. send_message(s,MSG_TYPE.SETEVENTS,
  208. "".join([struct.pack("!H", event) for event in events]))
  209. type,body = receive_reply(s,[MSG_TYPE.DONE])
  210. return
  211. def save_conf(s):
  212. send_message(s,MSG_TYPE.SAVECONF)
  213. receive_reply(s,[MSG_TYPE.DONE])
  214. def send_signal(s, sig):
  215. send_message(s,MSG_TYPE.SIGNAL,struct.pack("B",sig))
  216. receive_reply(s,[MSG_TYPE.DONE])
  217. def map_address(s, kv):
  218. msg = [ "%s %s\n"%(k,v) for k,v in kv ]
  219. send_message(s,MSG_TYPE.MAPADDRESS,"".join(msg))
  220. tp, body = receive_reply(s,[MSG_TYPE.DONE])
  221. return _parseKV(body)
  222. def extend_circuit(s, circid, hops):
  223. msg = struct.pack("!L",circid) + ",".join(hops) + "\0"
  224. send_message(s,MSG_TYPE.EXTENDCIRCUIT,msg)
  225. tp, body = receive_reply(s,[MSG_TYPE.DONE])
  226. return body
  227. def listen_for_events(s):
  228. while(1):
  229. _,type,body = receive_message(s)
  230. print "event",type
  231. return
  232. def do_main_loop(host,port):
  233. print "host is %s:%d"%(host,port)
  234. s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  235. s.connect((host,port))
  236. authenticate(s)
  237. print "nick",`get_option(s,"nickname")`
  238. print get_option(s,"DirFetchPeriod\n")
  239. print `get_info(s,"version")`
  240. #print `get_info(s,"desc/name/moria1")`
  241. print `get_info(s,"network-status")`
  242. print `get_info(s,"addr-mappings/all")`
  243. print `get_info(s,"addr-mappings/config")`
  244. print `get_info(s,"addr-mappings/cache")`
  245. print `get_info(s,"addr-mappings/control")`
  246. print `map_address(s, [("0.0.0.0", "Foobar.com"),
  247. ("1.2.3.4", "foobaz.com"),
  248. ("frebnitz.com", "5.6.7.8"),
  249. (".", "abacinator.onion")])`
  250. print `extend_circuit(s,0,["moria1"])`
  251. send_signal(s,1)
  252. #save_conf(s)
  253. #set_option(s,"1")
  254. #set_option(s,"bandwidthburstbytes 100000")
  255. #set_option(s,"runasdaemon 1")
  256. #set_events(s,[EVENT_TYPE.WARN])
  257. set_events(s,[EVENT_TYPE.WARN,EVENT_TYPE.STREAMSTATUS])
  258. listen_for_events(s)
  259. return
  260. if __name__ == '__main__':
  261. if len(sys.argv) != 2:
  262. print "Syntax: tor-control.py torhost:torport"
  263. sys.exit(0)
  264. sh,sp = parseHostAndPort(sys.argv[1])
  265. do_main_loop(sh,sp)