[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[tor-commits] [obfsproxy/master] Flesh tester.py.in out to subsume the existing non-automated integration tests. Note: all tests presently fail due to bugs introduced somewhere on this branch.
commit a99ca9dd19d3fc8e290b04ad3af9251a43caa4d1
Author: Zack Weinberg <zackw@xxxxxxxxx>
Date: Tue Jul 26 15:44:08 2011 -0700
Flesh tester.py.in out to subsume the existing non-automated integration tests. Note: all tests presently fail due to bugs introduced somewhere on this branch.
---
src/test/integration_test/alpha | 38 ----
src/test/integration_test/int_test.sh | 76 -------
src/test/test_socks_unsupported.py | 18 --
src/test/tester.py.in | 354 ++++++++++++++++++++++++++++++++-
4 files changed, 348 insertions(+), 138 deletions(-)
diff --git a/src/test/integration_test/alpha b/src/test/integration_test/alpha
deleted file mode 100644
index b6d68d6..0000000
--- a/src/test/integration_test/alpha
+++ /dev/null
@@ -1,38 +0,0 @@
-THIS IS A TEST FILE. IT'S USED BY THE INTEGRATION TESTS.
-THIS IS A TEST FILE. IT'S USED BY THE INTEGRATION TESTS.
-THIS IS A TEST FILE. IT'S USED BY THE INTEGRATION TESTS.
-THIS IS A TEST FILE. IT'S USED BY THE INTEGRATION TESTS.
-
-"Can entropy ever be reversed?"
-"THERE IS AS YET INSUFFICIENT DATA FOR A MEANINGFUL ANSWER."
-"Can entropy ever be reversed?"
-"THERE IS AS YET INSUFFICIENT DATA FOR A MEANINGFUL ANSWER."
-"Can entropy ever be reversed?"
-"THERE IS AS YET INSUFFICIENT DATA FOR A MEANINGFUL ANSWER."
-"Can entropy ever be reversed?"
-"THERE IS AS YET INSUFFICIENT DATA FOR A MEANINGFUL ANSWER."
-"Can entropy ever be reversed?"
-"THERE IS AS YET INSUFFICIENT DATA FOR A MEANINGFUL ANSWER."
-"Can entropy ever be reversed?"
-"THERE IS AS YET INSUFFICIENT DATA FOR A MEANINGFUL ANSWER."
-"Can entropy ever be reversed?"
-"THERE IS AS YET INSUFFICIENT DATA FOR A MEANINGFUL ANSWER."
-"Can entropy ever be reversed?"
-"THERE IS AS YET INSUFFICIENT DATA FOR A MEANINGFUL ANSWER."
-
- In obfuscatory age geeky warfare did I wage
- For hiding bits from nasty censors' sight
- I was hacker to my set in that dim dark age of net
- And I hacked from noon till three or four at night
-
- Then a rival from Helsinki said my protocol was dinky
- So I flamed him with a condescending laugh,
- Saying his designs for stego might as well be made of lego
- And that my bikeshed was prettier by half.
-
- But Claude Shannon saw my shame. From his noiseless channel came
- A message sent with not a wasted byte
- "There are nine and sixty ways to disguise communiques
- And RATHER MORE THAN ONE OF THEM IS RIGHT"
-
- (apologies to Rudyard Kipling.)
diff --git a/src/test/integration_test/int_test.sh b/src/test/integration_test/int_test.sh
deleted file mode 100644
index 9aa37ac..0000000
--- a/src/test/integration_test/int_test.sh
+++ /dev/null
@@ -1,76 +0,0 @@
-#!/bin/bash
-
-# replace this with your path to obfsproxy.
-OBFSPROXY="../../../obfsproxy"
-# replace this with your path to ncat.
-NCAT=ncat
-
-ENTRY_PORT=4999
-SERVER_PORT=5000
-NCAT_PORT=5001
-
-DIR=inttemp_temp
-FILE1=$DIR/test1
-FILE2=$DIR/test2
-
-mkdir -p $DIR ; :>$FILE1
-
-# TEST 1
-# We open a server and a client and transfer a file. Then we check if the output of the
-# server is the same as the file we sent.
-
-$NCAT -k -l -o $FILE1 -p $NCAT_PORT > /dev/null &
-ncat1_pid=$!
-
-
-$OBFSPROXY --log-min-severity=warn obfs2 --dest=127.0.0.1:$NCAT_PORT server 127.0.0.1:$SERVER_PORT \
- + obfs2 --dest=127.0.0.1:$SERVER_PORT client 127.0.0.1:$ENTRY_PORT &
-obfsproxy_pid=$!
-sleep 1
-
-
-$NCAT localhost $ENTRY_PORT < alpha &
-ncat2_pid=$!
-sleep 2
-
-if cmp -s alpha $FILE1
-then echo "GREAT SUCCESS 1!" ; rm $FILE1
-else echo "GREAT FAIL 1!"
-fi
-
-kill -9 $ncat1_pid
-kill -9 $obfsproxy_pid
-kill -9 $ncat2_pid
-
-sleep 2
-
-# TEST 2
-# We open an obfsproxy SOCKS server on the dummy protocol and an ncat listening.
-# Then we configure another ncat to use SOCKS4 and transfer a file to the other ncat.
-# Finally, we check if the file was sent correctly.
-
-:>$FILE2
-
-$NCAT -k -l -o $FILE2 -p $NCAT_PORT > /dev/null &
-ncat1_pid=$!
-
-$OBFSPROXY --log-min-severity=warn dummy socks 127.0.0.1:$SERVER_PORT &
-obfsproxy_pid=$!
-sleep 1
-
-$NCAT --proxy-type socks4 --proxy 127.0.0.1:$SERVER_PORT \
- 127.0.0.1 $NCAT_PORT < alpha &
-ncat2_pid=$!
-sleep 2
-
-if cmp -s alpha $FILE2
-then echo "GREAT SUCCESS 2!" ; rm $FILE2
-else echo "GREAT FAIL 2!"
-fi
-
-kill -9 $ncat1_pid
-kill -9 $obfsproxy_pid
-kill -9 $ncat2_pid
-
-rmdir $DIR
-
diff --git a/src/test/test_socks_unsupported.py b/src/test/test_socks_unsupported.py
deleted file mode 100644
index d1afd02..0000000
--- a/src/test/test_socks_unsupported.py
+++ /dev/null
@@ -1,18 +0,0 @@
-import socket,struct
-
-negot = struct.pack('BBB', 5, 1, 0)
-request = struct.pack('BBBBBBB', 5, 2, 0, 1, 1, 1, 1)
-
-PORT = 4500
-
-s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-s.connect(("127.0.0.1",PORT))
-s.send(negot)
-s.recv(1024)
-s.send(request)
-data = s.recv(1024)
-if (struct.unpack('BBBBih', data)[1] == 7):
- print "Works."
-else:
- print "Fail!"
-
diff --git a/src/test/tester.py.in b/src/test/tester.py.in
index d330e69..b0602ed 100644
--- a/src/test/tester.py.in
+++ b/src/test/tester.py.in
@@ -5,14 +5,356 @@
# The obfsproxy binary is assumed to exist in the current working
# directory, and you need to have Python 2.6 or better (but not 3).
# You need to be able to make connections to arbitrary high-numbered
-# TCP ports on the loopback interface (there is, however, an effort to
-# figure out which ones are already in use) and all IPv4 addresses in
-# 127.0.0/24 are assumed to be bound to the loopback interface.
+# TCP ports on the loopback interface.
+import errno
+import multiprocessing
+import Queue
+import signal
import socket
import struct
import subprocess
-import sys
+import time
+import unittest
-print "All tests successful."
-sys.exit(0)
+# Helper: Repeatedly try to connect to the specified server socket
+# until either it succeeds or one full second has elapsed. (Surely
+# there is a better way to do this?)
+
+def connect_with_retry(addr):
+ retry = 0
+ while True:
+ try:
+ return socket.create_connection(addr)
+ except socket.error, e:
+ if e.errno != errno.ECONNREFUSED: raise
+ if retry == 20: raise
+ retry += 1
+ time.sleep(0.05)
+
+# Helper: In a separate process (to avoid deadlock), listen on a
+# specified socket. The first time something connects to that socket,
+# read all available data, stick it in a string, and post the string
+# to the output queue. Then close both sockets and exit.
+
+class ReadWorker(object):
+ @staticmethod
+ def work(address, oq):
+ listener = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ listener.bind(address)
+ listener.listen(1)
+ (conn, remote) = listener.accept()
+ data = ""
+ try:
+ while True:
+ chunk = conn.recv(4096)
+ if chunk == "": break
+ data += chunk
+ except Exception, e:
+ data += "|RECV ERROR: " + e
+ conn.close()
+ listener.close()
+ oq.put(data)
+
+ def __init__(self, address):
+ self.oq = multiprocessing.Queue()
+ self.worker = multiprocessing.Process(target=self.work,
+ args=(address, self.oq))
+ self.worker.start()
+
+ def get(self):
+ rv = self.oq.get(timeout=1)
+ self.worker.join()
+ return rv
+
+ def stop(self):
+ if self.worker.is_alive(): self.worker.terminate()
+
+# Right now this is a direct translation of the former int_test.sh
+# (except that I have fleshed out the SOCKS test a bit).
+# It will be made more general and parametric Real Soon.
+
+ENTRY_PORT = 4999
+SERVER_PORT = 5000
+EXIT_PORT = 5001
+
+#
+# Test base classes. They do _not_ inherit from unittest.TestCase
+# so that they are not scanned directly for test functions (some of
+# them do provide test functions, but not in a usable state without
+# further code from subclasses).
+#
+
+class DirectTest(object):
+ def setUp(self):
+ self.output_reader = ReadWorker(("127.0.0.1", EXIT_PORT))
+ self.obfs = subprocess.Popen(
+ self.obfs_args,
+ stdin=open("/dev/null", "r"),
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+
+ self.input_chan = connect_with_retry(("127.0.0.1", ENTRY_PORT))
+ self.input_chan.settimeout(1.0)
+
+ def tearDown(self):
+ if self.obfs.returncode is None:
+ self.obfs.terminate()
+ self.output_reader.stop()
+
+ def checkSubprocesses(self):
+ if self.obfs.poll() is None:
+ self.obfs.send_signal(signal.SIGINT)
+
+ (out, err) = self.obfs.communicate()
+
+ if (out != "" or err != "" or self.obfs.returncode != 0):
+ self.fail("obfsproxy process failure:\n"
+ "\treturn code: %d\n"
+ "\tstdout: %s\n"
+ "\tstderr: %s\n"
+ % (self.obfs.returncode, out, err))
+
+ def test_direct_transfer(self):
+ # Open a server and a simple client (in the same process) and
+ # transfer a file. Then check whether the output is the same
+ # as the input.
+ self.input_chan.sendall(TEST_FILE)
+ self.input_chan.shutdown(socket.SHUT_WR)
+ try:
+ output = self.output_reader.get()
+ except Queue.Empty:
+ output = ""
+
+ self.checkSubprocesses()
+ self.assertEqual(output, TEST_FILE)
+
+# Same as above, but we use a socks client instead of a simple client,
+# and the server's a separate process.
+
+class SocksTest(object):
+ def setUp(self):
+ self.output_reader = ReadWorker(("127.0.0.1", EXIT_PORT))
+ self.obfs_server = subprocess.Popen(
+ self.server_args,
+ stdin=open("/dev/null", "r"),
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ self.obfs_client = subprocess.Popen(
+ self.client_args,
+ stdin=open("/dev/null", "r"),
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+
+ self.input_chan = connect_with_retry(("127.0.0.1", ENTRY_PORT))
+ self.input_chan.settimeout(1.0)
+
+ def tearDown(self):
+ if self.obfs_server.returncode is None:
+ self.obfs_server.terminate()
+ if self.obfs_client.returncode is None:
+ self.obfs_client.terminate()
+ self.output_reader.stop()
+
+ def checkSubprocesses(self):
+ if self.obfs_server.poll() is None:
+ self.obfs_server.send_signal(signal.SIGINT)
+ if self.obfs_client.poll() is None:
+ self.obfs_client.send_signal(signal.SIGINT)
+
+ (sout, serr) = self.obfs_server.communicate()
+ (cout, cerr) = self.obfs_client.communicate()
+
+ if (sout != "" or serr != "" or cout != "" or cerr != ""
+ or self.obfs_server.returncode != 0
+ or self.obfs_client.returncode != 0):
+ self.fail("obfsproxy process failures:\n"
+ "\tclient return code: %d\n"
+ "\tserver return code: %d\n"
+ "\tclient stdout: %s\n"
+ "\tclient stderr: %s\n"
+ "\tserver stdout: %s\n"
+ "\tserver stderr: %s\n"
+ % (self.obfs_client.returncode,
+ self.obfs_server.returncode,
+ cout, cerr, sout, serr))
+
+ # 'sequence' is a sequence of SOCKS[45] protocol messages
+ # which we will send or receive. Sends alternate with
+ # receives. Each entry may be a string, which is sent or
+ # received verbatim; a pair of a sequence of data items and a
+ # struct pack code, which is packed and then sent or received;
+ # or the constant False, which means the server is expected to
+ # drop the connection at that point. If we come to the end of
+ # the SOCKS sequence without the server having dropped the
+ # connection, we transmit the test file and expect to get it
+ # back from the far end.
+ def socksTest(self, sequence):
+ sending = True
+ good = True
+ for msg in sequence:
+ if msg is False:
+ self.input_chan.shutdown(socket.SHUT_WR)
+ # Expect either a clean closedown or a connection reset
+ # at this point.
+ got = ""
+ try:
+ got = self.input_chan.recv(4096)
+ except socket.error, e:
+ if e.errno != errno.ECONNRESET: raise
+ self.assertEqual(got, "")
+ good = False
+ break
+ elif isinstance(msg, str):
+ exp = msg
+ elif isinstance(msg, tuple):
+ exp = struct.pack(msg[1], *msg[0])
+ else:
+ raise TypeError("incomprehensible msg: " + repr(msg))
+ if sending:
+ self.input_chan.sendall(exp)
+ else:
+ got = ""
+ try:
+ got = self.input_chan.recv(4096)
+ except socket.error, e:
+ if e.errno != errno.ECONNRESET: raise
+ self.assertEqual(got, exp)
+ if good:
+ self.input_chan.sendall(TEST_FILE)
+ self.input_chan.shutdown(socket.SHUT_WR)
+ try:
+ output = self.output_reader.get()
+ except Queue.Empty:
+ output = ""
+ self.assertEqual(output, TEST_FILE)
+
+ self.input_chan.close()
+ self.checkSubprocesses()
+
+ def test_illformed(self):
+ # ill-formed socks message - server should drop connection
+ self.socksTest([ "GET / HTTP/1.1\r\nHost: 127.0.0.1\r\n"
+ "Connection: close\r\n\r\n",
+ False ])
+
+ def test_socks4_unsupported_methods(self):
+ # SOCKS4 bind request - should fail, presently just drops connection
+ self.socksTest([ ( (4, 2, SERVER_PORT, 127, 0, 0, 1, 0), "!BBH5B" ),
+ False ])
+
+ def test_socks4_connect(self):
+ # SOCKS4 connection request - should succeed
+ self.socksTest([ ( (4, 1, SERVER_PORT, 127, 0, 0, 1, 0), "!BBH5B" ),
+ ( (0, 90, SERVER_PORT, 127, 0, 0, 1), "!BBH4B" ) ])
+
+ def test_socks5_bad_handshakes(self):
+ self.socksTest([ "\x05", False ])
+ self.socksTest([ "\x05\x00", False ])
+ self.socksTest([ "\x05\x01\x01", "\x05\xFF", False ])
+ self.socksTest([ "\x05\x01\x080", "\x05\xFF", False ])
+ self.socksTest([ "\x05\x02\x01\x02", "\x05\xFF", False ])
+
+ def test_socks5_bad_requests(self):
+ self.socksTest([ "\x05\x01\x00", "\x05\x00", "\x05\x00",
+ "\x05\x07\x00", False ])
+ self.socksTest([ "\x05\x02\x00\x01", "\x05\x00", "\x05\x00",
+ "\x05\x07\x00", False ])
+
+ def test_socks5_unsupported_methods(self):
+ self.socksTest([ "\x05\x01\x00", "\x05\x00",
+ ( (5, 2, 0, 1, 127, 0, 0, 1, SERVER_PORT), "!8BH" ),
+ "\x05\x07\x00", False ])
+ self.socksTest([ "\x05\x01\x00", "\x05\x00",
+ ( (5, 3, 0, 1, 127, 0, 0, 1, SERVER_PORT), "!8BH" ),
+ "\x05\x07\x00", False ])
+
+ def test_socks5_connect(self):
+ self.socksTest([ "\x05\x01\x00", "\x05\x00",
+ ( (5, 1, 0, 1, 127, 0, 0, 1, SERVER_PORT), "!8BH" ),
+ ( (5, 0, 0, 1, 127, 0, 0, 1, SERVER_PORT), "!8BH" ) ])
+
+#
+# Concrete test classes specialize the above base classes for each protocol.
+#
+
+class DirectObfs2(DirectTest, unittest.TestCase):
+ obfs_args = ("./obfsproxy", "--log-min-severity=warn",
+ "obfs2",
+ "--dest=127.0.0.1:%d" % EXIT_PORT,
+ "server", "127.0.0.1:%d" % SERVER_PORT,
+ "+", "obfs2",
+ "--dest=127.0.0.1:%d" % SERVER_PORT,
+ "client", "127.0.0.1:%d" % ENTRY_PORT)
+
+class DirectDummy(DirectTest, unittest.TestCase):
+ obfs_args = ("./obfsproxy", "--log-min-severity=warn",
+ "dummy", "server",
+ "127.0.0.1:%d" % SERVER_PORT,
+ "127.0.0.1:%d" % EXIT_PORT,
+ "+", "dummy", "client",
+ "127.0.0.1:%d" % ENTRY_PORT,
+ "127.0.0.1:%d" % SERVER_PORT)
+
+class SocksObfs2(SocksTest, unittest.TestCase):
+ server_args = ("./obfsproxy", "--log-min-severity=warn",
+ "obfs2",
+ "--dest=127.0.0.1:%d" % EXIT_PORT,
+ "server", "127.0.0.1:%d" % SERVER_PORT)
+ client_args = ("./obfsproxy", "--log-min-severity=warn",
+ "obfs2",
+ "socks", "127.0.0.1:%d" % ENTRY_PORT)
+
+class SocksDummy(SocksTest, unittest.TestCase):
+ server_args = ("./obfsproxy", "--log-min-severity=warn",
+ "dummy", "server",
+ "127.0.0.1:%d" % SERVER_PORT,
+ "127.0.0.1:%d" % EXIT_PORT)
+ client_args = ("./obfsproxy", "--log-min-severity=warn",
+ "dummy", "socks",
+ "127.0.0.1:%d" % ENTRY_PORT)
+
+TEST_FILE = """\
+THIS IS A TEST FILE. IT'S USED BY THE INTEGRATION TESTS.
+THIS IS A TEST FILE. IT'S USED BY THE INTEGRATION TESTS.
+THIS IS A TEST FILE. IT'S USED BY THE INTEGRATION TESTS.
+THIS IS A TEST FILE. IT'S USED BY THE INTEGRATION TESTS.
+
+"Can entropy ever be reversed?"
+"THERE IS AS YET INSUFFICIENT DATA FOR A MEANINGFUL ANSWER."
+"Can entropy ever be reversed?"
+"THERE IS AS YET INSUFFICIENT DATA FOR A MEANINGFUL ANSWER."
+"Can entropy ever be reversed?"
+"THERE IS AS YET INSUFFICIENT DATA FOR A MEANINGFUL ANSWER."
+"Can entropy ever be reversed?"
+"THERE IS AS YET INSUFFICIENT DATA FOR A MEANINGFUL ANSWER."
+"Can entropy ever be reversed?"
+"THERE IS AS YET INSUFFICIENT DATA FOR A MEANINGFUL ANSWER."
+"Can entropy ever be reversed?"
+"THERE IS AS YET INSUFFICIENT DATA FOR A MEANINGFUL ANSWER."
+"Can entropy ever be reversed?"
+"THERE IS AS YET INSUFFICIENT DATA FOR A MEANINGFUL ANSWER."
+"Can entropy ever be reversed?"
+"THERE IS AS YET INSUFFICIENT DATA FOR A MEANINGFUL ANSWER."
+
+ In obfuscatory age geeky warfare did I wage
+ For hiding bits from nasty censors' sight
+ I was hacker to my set in that dim dark age of net
+ And I hacked from noon till three or four at night
+
+ Then a rival from Helsinki said my protocol was dinky
+ So I flamed him with a condescending laugh,
+ Saying his designs for stego might as well be made of lego
+ And that my bikeshed was prettier by half.
+
+ But Claude Shannon saw my shame. From his noiseless channel came
+ A message sent with not a wasted byte
+ "There are nine and sixty ways to disguise communiques
+ And RATHER MORE THAN ONE OF THEM IS RIGHT"
+
+ (apologies to Rudyard Kipling.)
+"""
+
+if __name__ == '__main__':
+ unittest.main()
_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits