TorNet.py 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. #!/usr/bin/python
  2. #
  3. # Copyright 2011 Nick Mathewson, Michael Stone
  4. #
  5. # You may do anything with this work that copyright law would normally
  6. # restrict, so long as you retain the above notice(s) and this license
  7. # in all redistributed copies and derived works. There is no warranty.
  8. from __future__ import with_statement
  9. import os
  10. import templating
  11. import subprocess
  12. import sys
  13. import re
  14. import errno
  15. def mkdir_p(d):
  16. try:
  17. os.makedirs(d)
  18. except OSError, e:
  19. if e.errno == errno.EEXIST:
  20. return
  21. raise
  22. class Node:
  23. ########
  24. # Users are expected to call these:
  25. def __init__(self, parent=None, **kwargs):
  26. self._parent = parent
  27. self._fields = self._createEnviron(parent, kwargs)
  28. def getN(self, N):
  29. return [ Node(self) for i in xrange(N) ]
  30. def specialize(self, **kwargs):
  31. return Node(parent=self, **kwargs)
  32. #######
  33. # Users are NOT expected to call these:
  34. def _createTorrcFile(self, checkOnly=False):
  35. template = self._getTorrcTemplate()
  36. env = self._fields
  37. fn_out = templating.Template("${torrc_fname}").format(env)
  38. output = template.format(env)
  39. if checkOnly:
  40. return
  41. with open(fn_out, 'w') as f:
  42. f.write(output)
  43. def _getTorrcTemplate(self):
  44. env = self._fields
  45. template_path = env['torrc_template_path']
  46. t = "$${include:$torrc}"
  47. return templating.Template(t, includePath=template_path)
  48. def _getFreeVars(self):
  49. template = self._getTorrcTemplate()
  50. env = self._fields
  51. return template.freevars(env)
  52. def _createEnviron(self, parent, argdict):
  53. if parent:
  54. parentfields = parent._fields
  55. else:
  56. parentfields = self._getDefaultFields()
  57. return TorEnviron(parentfields, **argdict)
  58. def _getDefaultFields(self):
  59. return _BASE_FIELDS
  60. def _checkConfig(self, net):
  61. self._createTorrcFile(checkOnly=True)
  62. def _preConfig(self, net):
  63. self._makeDataDir()
  64. if self._fields['authority']:
  65. self._genAuthorityKey()
  66. if self._fields['relay']:
  67. self._genRouterKey()
  68. def _config(self, net):
  69. self._createTorrcFile()
  70. #self._createScripts()
  71. def _postConfig(self, net):
  72. #self.net.addNode(self)
  73. pass
  74. def _setnodenum(self, num):
  75. self._fields['nodenum'] = num
  76. def _makeDataDir(self):
  77. env = self._fields
  78. datadir = env['dir']
  79. mkdir_p(os.path.join(datadir, 'keys'))
  80. def _genAuthorityKey(self):
  81. env = self._fields
  82. datadir = env['dir']
  83. tor_gencert = env['tor_gencert']
  84. lifetime = env['auth_cert_lifetime']
  85. idfile = os.path.join(datadir,'keys',"authority_identity_key")
  86. skfile = os.path.join(datadir,'keys',"authority_signing_key")
  87. certfile = os.path.join(datadir,'keys',"authority_certificate")
  88. addr = "%s:%s" % (env['ip'], env['dirport'])
  89. passphrase = env['auth_passphrase']
  90. if all(os.path.exists(f) for f in [idfile, skfile, certfile]):
  91. return
  92. cmdline = [
  93. tor_gencert,
  94. '--create-identity-key',
  95. '--passphrase-fd', '0',
  96. '-i', idfile,
  97. '-s', skfile,
  98. '-c', certfile,
  99. '-m', str(lifetime),
  100. '-a', addr]
  101. print "Creating identity key %s for %s with %s"%(idfile,env['nick']," ".join(cmdline))
  102. p = subprocess.Popen(cmdline, stdin=subprocess.PIPE)
  103. p.communicate(passphrase+"\n")
  104. assert p.returncode == 0 #XXXX BAD!
  105. def _genRouterKey(self):
  106. env = self._fields
  107. datadir = env['dir']
  108. tor = env['tor']
  109. idfile = os.path.join(datadir,'keys',"identity_key")
  110. cmdline = [
  111. tor,
  112. "--quiet",
  113. "--list-fingerprint",
  114. "--orport", "1",
  115. "--dirserver",
  116. "xyzzy 127.0.0.1:1 ffffffffffffffffffffffffffffffffffffffff",
  117. "--datadirectory", datadir ]
  118. p = subprocess.Popen(cmdline, stdout=subprocess.PIPE)
  119. stdout, stderr = p.communicate()
  120. fingerprint = "".join(stdout.split()[1:])
  121. assert re.match(r'^[A-F0-9]{40}$', fingerprint)
  122. env['fingerprint'] = fingerprint
  123. def _getDirServerLine(self):
  124. env = self._fields
  125. if not env['authority']:
  126. return ""
  127. datadir = env['dir']
  128. certfile = os.path.join(datadir,'keys',"authority_certificate")
  129. v3id = None
  130. with open(certfile, 'r') as f:
  131. for line in f:
  132. if line.startswith("fingerprint"):
  133. v3id = line.split()[1].strip()
  134. break
  135. assert v3id is not None
  136. return "DirServer %s v3ident=%s orport=%s %s %s:%s %s\n" %(
  137. env['nick'], v3id, env['orport'], env['dirserver_flags'],
  138. env['ip'], env['dirport'], env['fingerprint'])
  139. DEFAULTS = {
  140. 'authority' : False,
  141. 'relay' : False,
  142. 'connlimit' : 60,
  143. 'net_base_dir' : 'net',
  144. 'tor' : 'tor',
  145. 'auth_cert_lifetime' : 12,
  146. 'ip' : '127.0.0.1',
  147. 'dirserver_flags' : 'no-v2',
  148. 'privnet_dir' : '.',
  149. 'torrc_fname' : '${dir}/torrc',
  150. 'orport_base' : 6000,
  151. 'dirport_base' : 7000,
  152. 'controlport_base' : 8000,
  153. 'socksport_base' : 9000,
  154. 'dirservers' : "Dirserver bleargh bad torrc file!"
  155. }
  156. class TorEnviron(templating.Environ):
  157. def __init__(self,parent=None,**kwargs):
  158. templating.Environ.__init__(self, parent=parent, **kwargs)
  159. def _get_orport(self, me):
  160. return me['orport_base']+me['nodenum']
  161. def _get_controlport(self, me):
  162. return me['controlport_base']+me['nodenum']
  163. def _get_socksport(self, me):
  164. return me['socksport_base']+me['nodenum']
  165. def _get_dirport(self, me):
  166. return me['dirport_base']+me['nodenum']
  167. def _get_dir(self, me):
  168. return os.path.abspath(os.path.join(me['net_base_dir'],
  169. "nodes",
  170. me['nick']))
  171. def _get_nick(self, me):
  172. return "%s-%02d"%(me['tag'], me['nodenum'])
  173. def _get_tor_gencert(self, me):
  174. return me['tor']+"-gencert"
  175. def _get_auth_passphrase(self, me):
  176. return self['nick'] # OMG TEH SECURE!
  177. def _get_torrc_template_path(self, me):
  178. return [ os.path.join(me['privnet_dir'], 'torrc_templates') ]
  179. class Network:
  180. def __init__(self,defaultEnviron):
  181. self._nodes = []
  182. self._dfltEnv = defaultEnviron
  183. self._nextnodenum = 0
  184. def addNode(self, n):
  185. n._setnodenum(self._nextnodenum)
  186. self._nextnodenum += 1
  187. self._nodes.append(n)
  188. def configure(self):
  189. network = self
  190. dirserverlines = []
  191. for n in self._nodes:
  192. n._checkConfig(network)
  193. for n in self._nodes:
  194. n._preConfig(network)
  195. dirserverlines.append(n._getDirServerLine())
  196. self._dfltEnv['dirservers'] = "".join(dirserverlines)
  197. for n in self._nodes:
  198. n._config(network)
  199. for n in self._nodes:
  200. n._postConfig(network)
  201. def ConfigureNodes(nodelist):
  202. network = _THE_NETWORK
  203. for n in nodelist:
  204. network.addNode(n)
  205. def runConfigFile(f):
  206. global _BASE_FIELDS
  207. global _THE_NETWORK
  208. _BASE_FIELDS = TorEnviron(templating.Environ(**DEFAULTS))
  209. _THE_NETWORK = Network(_BASE_FIELDS)
  210. _GLOBALS = dict(_BASE_FIELDS= _BASE_FIELDS,
  211. Node=Node,
  212. ConfigureNodes=ConfigureNodes,
  213. _THE_NETWORK=_THE_NETWORK)
  214. exec f in _GLOBALS
  215. network = _GLOBALS['_THE_NETWORK']
  216. network.configure()
  217. if __name__ == '__main__':
  218. f = open(sys.argv[1])
  219. runConfigFile(f)