test_cmdline_args.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. #!/usr/bin/python
  2. import binascii
  3. import hashlib
  4. import os
  5. import re
  6. import shutil
  7. import subprocess
  8. import sys
  9. import tempfile
  10. import unittest
  11. TOR = "./src/or/tor"
  12. TOP_SRCDIR = "."
  13. if len(sys.argv) > 1:
  14. TOR = sys.argv[1]
  15. del sys.argv[1]
  16. if len(sys.argv) > 1:
  17. TOP_SRCDIR = sys.argv[1]
  18. del sys.argv[1]
  19. class UnexpectedSuccess(Exception):
  20. pass
  21. class UnexpectedFailure(Exception):
  22. pass
  23. if sys.version < '3':
  24. def b2s(b):
  25. return b
  26. def s2b(s):
  27. return s
  28. def NamedTemporaryFile():
  29. return tempfile.NamedTemporaryFile(delete=False)
  30. else:
  31. def b2s(b):
  32. return str(b, 'ascii')
  33. def s2b(s):
  34. return s.encode('ascii')
  35. def NamedTemporaryFile():
  36. return tempfile.NamedTemporaryFile(mode="w",delete=False,encoding="ascii")
  37. def contents(fn):
  38. f = open(fn)
  39. try:
  40. return f.read()
  41. finally:
  42. f.close()
  43. def run_tor(args, failure=False, stdin=None):
  44. kwargs = {}
  45. if stdin != None:
  46. kwargs['stdin'] = subprocess.PIPE
  47. p = subprocess.Popen([TOR] + args, stdout=subprocess.PIPE, **kwargs)
  48. output, _ = p.communicate(input=stdin)
  49. result = p.poll()
  50. if result and not failure:
  51. raise UnexpectedFailure()
  52. elif not result and failure:
  53. raise UnexpectedSuccess()
  54. return b2s(output.replace('\r\n','\n'))
  55. def spaceify_fp(fp):
  56. for i in range(0, len(fp), 4):
  57. yield fp[i:i+4]
  58. def lines(s):
  59. out = s.splitlines()
  60. if out and out[-1] == '':
  61. del out[-1]
  62. return out
  63. def strip_log_junk(line):
  64. m = re.match(r'([^\[]+\[[a-z]*\] *)(.*)', line)
  65. if not m:
  66. return ""+line
  67. return m.group(2).strip()
  68. def randstring(entropy_bytes):
  69. s = os.urandom(entropy_bytes)
  70. return b2s(binascii.b2a_hex(s))
  71. def findLineContaining(lines, s):
  72. for ln in lines:
  73. if s in ln:
  74. return True
  75. return False
  76. class CmdlineTests(unittest.TestCase):
  77. def test_version(self):
  78. out = run_tor(["--version"])
  79. self.assertTrue(out.startswith("Tor version "))
  80. self.assertEqual(len(lines(out)), 1)
  81. def test_quiet(self):
  82. out = run_tor(["--quiet", "--quumblebluffin", "1"], failure=True)
  83. self.assertEqual(out, "")
  84. def test_help(self):
  85. out = run_tor(["--help"], failure=False)
  86. out2 = run_tor(["-h"], failure=False)
  87. self.assertTrue(out.startswith("Copyright (c) 2001"))
  88. self.assertTrue(out.endswith(
  89. "tor -f <torrc> [args]\n"
  90. "See man page for options, or https://www.torproject.org/ for documentation.\n"))
  91. self.assertTrue(out == out2)
  92. def test_hush(self):
  93. torrc = NamedTemporaryFile()
  94. torrc.close()
  95. try:
  96. out = run_tor(["--hush", "-f", torrc.name,
  97. "--quumblebluffin", "1"], failure=True)
  98. finally:
  99. os.unlink(torrc.name)
  100. self.assertEqual(len(lines(out)), 2)
  101. ln = [ strip_log_junk(l) for l in lines(out) ]
  102. self.assertEqual(ln[0], "Failed to parse/validate config: Unknown option 'quumblebluffin'. Failing.")
  103. self.assertEqual(ln[1], "Reading config failed--see warnings above.")
  104. def test_missing_argument(self):
  105. out = run_tor(["--hush", "--hash-password"], failure=True)
  106. self.assertEqual(len(lines(out)), 2)
  107. ln = [ strip_log_junk(l) for l in lines(out) ]
  108. self.assertEqual(ln[0], "Command-line option '--hash-password' with no value. Failing.")
  109. def test_hash_password(self):
  110. out = run_tor(["--hash-password", "woodwose"])
  111. result = lines(out)[-1]
  112. self.assertEqual(result[:3], "16:")
  113. self.assertEqual(len(result), 61)
  114. r = binascii.a2b_hex(result[3:])
  115. self.assertEqual(len(r), 29)
  116. salt, how, hashed = r[:8], r[8], r[9:]
  117. self.assertEqual(len(hashed), 20)
  118. if type(how) == type("A"):
  119. how = ord(how)
  120. count = (16 + (how & 15)) << ((how >> 4) + 6)
  121. stuff = salt + s2b("woodwose")
  122. repetitions = count // len(stuff) + 1
  123. inp = stuff * repetitions
  124. inp = inp[:count]
  125. self.assertEqual(hashlib.sha1(inp).digest(), hashed)
  126. def test_digests(self):
  127. main_c = os.path.join(TOP_SRCDIR, "src", "or", "main.c")
  128. if os.stat(TOR).st_mtime < os.stat(main_c).st_mtime:
  129. self.skipTest(TOR+" not up to date")
  130. out = run_tor(["--digests"])
  131. main_line = [ l for l in lines(out) if l.endswith("/main.c") or l.endswith(" main.c") ]
  132. digest, name = main_line[0].split()
  133. f = open(main_c, 'rb')
  134. actual = hashlib.sha1(f.read()).hexdigest()
  135. f.close()
  136. self.assertEqual(digest, actual)
  137. def test_dump_options(self):
  138. default_torrc = NamedTemporaryFile()
  139. torrc = NamedTemporaryFile()
  140. torrc.write("SocksPort 9999")
  141. torrc.close()
  142. default_torrc.write("SafeLogging 0")
  143. default_torrc.close()
  144. out_sh = out_nb = out_fl = None
  145. opts = [ "-f", torrc.name,
  146. "--defaults-torrc", default_torrc.name ]
  147. try:
  148. out_sh = run_tor(["--dump-config", "short"]+opts)
  149. out_nb = run_tor(["--dump-config", "non-builtin"]+opts)
  150. out_fl = run_tor(["--dump-config", "full"]+opts)
  151. out_nr = run_tor(["--dump-config", "bliznert"]+opts,
  152. failure=True)
  153. out_verif = run_tor(["--verify-config"]+opts)
  154. finally:
  155. os.unlink(torrc.name)
  156. os.unlink(default_torrc.name)
  157. self.assertEqual(len(lines(out_sh)), 2)
  158. self.assertTrue(lines(out_sh)[0].startswith("DataDirectory "))
  159. self.assertEqual(lines(out_sh)[1:],
  160. [ "SocksPort 9999" ])
  161. self.assertEqual(len(lines(out_nb)), 2)
  162. self.assertEqual(lines(out_nb),
  163. [ "SafeLogging 0",
  164. "SocksPort 9999" ])
  165. out_fl = lines(out_fl)
  166. self.assertTrue(len(out_fl) > 100)
  167. self.assertTrue("SocksPort 9999" in out_fl)
  168. self.assertTrue("SafeLogging 0" in out_fl)
  169. self.assertTrue("ClientOnly 0" in out_fl)
  170. self.assertTrue(out_verif.endswith("Configuration was valid\n"))
  171. def test_list_fingerprint(self):
  172. tmpdir = tempfile.mkdtemp(prefix='ttca_')
  173. torrc = NamedTemporaryFile()
  174. torrc.write("ORPort 9999\n")
  175. torrc.write("DataDirectory %s\n"%tmpdir)
  176. torrc.write("Nickname tippi")
  177. torrc.close()
  178. opts = ["-f", torrc.name]
  179. try:
  180. out = run_tor(["--list-fingerprint"]+opts)
  181. fp = contents(os.path.join(tmpdir, "fingerprint"))
  182. finally:
  183. os.unlink(torrc.name)
  184. shutil.rmtree(tmpdir)
  185. out = lines(out)
  186. lastlog = strip_log_junk(out[-2])
  187. lastline = out[-1]
  188. fp = fp.strip()
  189. nn_fp = fp.split()[0]
  190. space_fp = " ".join(spaceify_fp(fp.split()[1]))
  191. self.assertEqual(lastlog,
  192. "Your Tor server's identity key fingerprint is '%s'"%fp)
  193. self.assertEqual(lastline, "tippi %s"%space_fp)
  194. self.assertEqual(nn_fp, "tippi")
  195. def test_list_options(self):
  196. out = lines(run_tor(["--list-torrc-options"]))
  197. self.assertTrue(len(out)>100)
  198. self.assertTrue(out[0] <= 'AccountingMax')
  199. self.assertTrue("UseBridges" in out)
  200. self.assertTrue("SocksPort" in out)
  201. def test_cmdline_args(self):
  202. default_torrc = NamedTemporaryFile()
  203. torrc = NamedTemporaryFile()
  204. contents = ("SocksPort 9999\n"
  205. "SocksPort 9998\n"
  206. "ORPort 9000\n"
  207. "ORPort 9001\n"
  208. "Nickname eleventeen\n"
  209. "ControlPort 9500\n")
  210. torrc.write(contents)
  211. default_torrc.write("")
  212. default_torrc.close()
  213. torrc.close()
  214. out_sh = out_nb = out_fl = None
  215. opts_stdin = [ "-f", "-",
  216. "--defaults-torrc", default_torrc.name,
  217. "--dump-config", "short" ]
  218. opts = [ "-f", torrc.name,
  219. "--defaults-torrc", default_torrc.name,
  220. "--dump-config", "short" ]
  221. try:
  222. out_0 = run_tor(opts_stdin,stdin=contents)
  223. out_1 = run_tor(opts)
  224. out_2 = run_tor(opts+["+ORPort", "9003",
  225. "SocksPort", "9090",
  226. "/ControlPort",
  227. "/TransPort",
  228. "+ExtORPort", "9005"])
  229. finally:
  230. os.unlink(torrc.name)
  231. os.unlink(default_torrc.name)
  232. out_0 = [ l for l in lines(out_0) if not l.startswith("DataDir") ]
  233. out_1 = [ l for l in lines(out_1) if not l.startswith("DataDir") ]
  234. out_2 = [ l for l in lines(out_2) if not l.startswith("DataDir") ]
  235. self.assertEqual(out_0,
  236. ["ControlPort 9500",
  237. "Nickname eleventeen",
  238. "ORPort 9000",
  239. "ORPort 9001",
  240. "SocksPort 9999",
  241. "SocksPort 9998"])
  242. self.assertEqual(out_1,
  243. ["ControlPort 9500",
  244. "Nickname eleventeen",
  245. "ORPort 9000",
  246. "ORPort 9001",
  247. "SocksPort 9999",
  248. "SocksPort 9998"])
  249. self.assertEqual(out_2,
  250. ["ExtORPort 9005",
  251. "Nickname eleventeen",
  252. "ORPort 9000",
  253. "ORPort 9001",
  254. "ORPort 9003",
  255. "SocksPort 9090"])
  256. def test_missing_torrc(self):
  257. fname = "nonexistent_file_"+randstring(8)
  258. out = run_tor(["-f", fname, "--verify-config"], failure=True)
  259. ln = [ strip_log_junk(l) for l in lines(out) ]
  260. self.assertTrue("Unable to open configuration file" in ln[-2])
  261. self.assertTrue("Reading config failed" in ln[-1])
  262. out = run_tor(["-f", fname, "--verify-config", "--ignore-missing-torrc"])
  263. ln = [ strip_log_junk(l) for l in lines(out) ]
  264. self.assertTrue(findLineContaining(ln, ", using reasonable defaults"))
  265. self.assertTrue("Configuration was valid" in ln[-1])
  266. if __name__ == '__main__':
  267. unittest.main()