|
@@ -0,0 +1,253 @@
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+import os, signal, socket, unittest
|
|
|
|
+from socket import AF_INET, AF_INET6, SOCK_STREAM, SOCK_DGRAM, IPPROTO_UDP, \
|
|
|
|
+ IPPROTO_TCP
|
|
|
|
+from isc.util.io.socketsession import *
|
|
|
|
+
|
|
|
|
+TESTDATA_OBJDIR = os.getenv("TESTDATAOBJDIR")
|
|
|
|
+TEST_UNIX_FILE = TESTDATA_OBJDIR + '/ssessiontest.unix'
|
|
|
|
+TEST_DATA = b'BIND10 test'
|
|
|
|
+TEST_PORT = 53535
|
|
|
|
+
|
|
|
|
+class TestForwarder(unittest.TestCase):
|
|
|
|
+ '''In general, this is a straightforward port of the C++ counterpart.
|
|
|
|
+
|
|
|
|
+ In some cases test cases are simplified or have Python specific cases.
|
|
|
|
+
|
|
|
|
+ '''
|
|
|
|
+
|
|
|
|
+ def setUp(self):
|
|
|
|
+ self.forwarder = SocketSessionForwarder(TEST_UNIX_FILE)
|
|
|
|
+ if os.path.exists(TEST_UNIX_FILE):
|
|
|
|
+ os.unlink(TEST_UNIX_FILE)
|
|
|
|
+ self.large_text = b'a' * 65535
|
|
|
|
+
|
|
|
|
+ def tearDown(self):
|
|
|
|
+ if os.path.exists(TEST_UNIX_FILE):
|
|
|
|
+ os.unlink(TEST_UNIX_FILE)
|
|
|
|
+
|
|
|
|
+ def start_listen(self):
|
|
|
|
+ self.listen_sock = socket.socket(socket.AF_UNIX, SOCK_STREAM, 0)
|
|
|
|
+ self.listen_sock.bind(TEST_UNIX_FILE)
|
|
|
|
+ self.listen_sock.listen(10)
|
|
|
|
+
|
|
|
|
+ def accept_forwarder(self):
|
|
|
|
+ self.listen_sock.setblocking(False)
|
|
|
|
+ s, _ = self.listen_sock.accept()
|
|
|
|
+ s.setblocking(True)
|
|
|
|
+ return s
|
|
|
|
+
|
|
|
|
+ def test_init(self):
|
|
|
|
+
|
|
|
|
+ self.assertRaises(TypeError, SocketSessionForwarder, 1)
|
|
|
|
+ self.assertRaises(TypeError, SocketSessionForwarder,
|
|
|
|
+ 'test.unix', 'test.unix')
|
|
|
|
+
|
|
|
|
+ def test_badpush(self):
|
|
|
|
+
|
|
|
|
+ self.assertRaises(TypeError, self.forwarder.push, 1)
|
|
|
|
+ self.assertRaises(TypeError, self.forwarder.push, 0, AF_INET,
|
|
|
|
+ SOCK_DGRAM, IPPROTO_UDP, ('127.0.0.1', 53),
|
|
|
|
+ ('192.0.2.1', 5300), TEST_DATA, 0)
|
|
|
|
+
|
|
|
|
+ self.assertRaises(TypeError, self.forwarder.push, 0, 'AF_INET',
|
|
|
|
+ SOCK_DGRAM, IPPROTO_UDP, ('127.0.0.1', 53),
|
|
|
|
+ ('192.0.2.1', 5300), TEST_DATA)
|
|
|
|
+
|
|
|
|
+ self.assertRaises(TypeError, self.forwarder.push, 0, AF_INET,
|
|
|
|
+ SOCK_DGRAM, IPPROTO_UDP, ('127.0.0..1', 53),
|
|
|
|
+ ('192.0.2.1', 5300), TEST_DATA)
|
|
|
|
+ self.assertRaises(TypeError, self.forwarder.push, 0, AF_INET,
|
|
|
|
+ SOCK_DGRAM, IPPROTO_UDP, '127.0.0.1',
|
|
|
|
+ ('192.0.2.1', 5300), TEST_DATA)
|
|
|
|
+
|
|
|
|
+ self.assertRaises(TypeError, self.forwarder.push, 0, AF_INET6,
|
|
|
|
+ SOCK_DGRAM, IPPROTO_UDP, ('2001:db8::1', 53),
|
|
|
|
+ ('2001:db8:::3', 5300), TEST_DATA)
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ self.assertRaises(TypeError, self.forwarder.push, 0, AF_INET,
|
|
|
|
+ SOCK_DGRAM, IPPROTO_UDP, ('192.0.2.1', 53),
|
|
|
|
+ ('192.0.2.2', 53), TEST_DATA)
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ self.start_listen()
|
|
|
|
+ self.forwarder.connect_to_receiver()
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ self.assertRaises(TypeError, self.forwarder.push, 1, AF_INET,
|
|
|
|
+ SOCK_DGRAM, IPPROTO_UDP, ('2001:db8::1', 53, 0, 1),
|
|
|
|
+ ('192.0.2.2', 53), TEST_DATA)
|
|
|
|
+ self.assertRaises(TypeError, self.forwarder.push, 1, AF_INET6,
|
|
|
|
+ SOCK_DGRAM, IPPROTO_UDP, ('2001:db8::1', 53, 0, 1),
|
|
|
|
+ ('192.0.2.2', 53), TEST_DATA)
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ self.assertRaises(TypeError, self.forwarder.push, 1, AF_INET,
|
|
|
|
+ SOCK_DGRAM, IPPROTO_UDP, ('192.0.2.1', 53),
|
|
|
|
+ ('192.0.2.2', 53), b'')
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ self.assertRaises(TypeError, self.forwarder.push, 1, AF_INET,
|
|
|
|
+ SOCK_DGRAM, IPPROTO_UDP, ('192.0.2.1', 53),
|
|
|
|
+ ('192.0.2.2', 53), b'd' * 65536)
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ self.listen_sock.close()
|
|
|
|
+ self.assertRaises(SocketSessionError, self.forwarder.push, 1, AF_INET,
|
|
|
|
+ SOCK_DGRAM, IPPROTO_UDP, ('192.0.2.1', 53),
|
|
|
|
+ ('192.0.2.2', 53), TEST_DATA)
|
|
|
|
+
|
|
|
|
+ def create_socket(self, family, type, protocol, addr, do_listen):
|
|
|
|
+ s = socket.socket(family, type, protocol)
|
|
|
|
+ s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
|
|
+ s.bind(addr)
|
|
|
|
+ if do_listen and protocol == IPPROTO_TCP:
|
|
|
|
+ s.listen(1)
|
|
|
|
+ return s
|
|
|
|
+
|
|
|
|
+ def check_push_and_pop(self, family, type, protocol, local, remote,
|
|
|
|
+ data, new_connection):
|
|
|
|
+ sock = self.create_socket(family, type, protocol, local, True)
|
|
|
|
+ fwd_fd = sock.fileno()
|
|
|
|
+ if protocol == IPPROTO_TCP:
|
|
|
|
+ client_addr = ('::1', 0, 0, 0) if family == AF_INET6 \
|
|
|
|
+ else ('127.0.0.1', 0)
|
|
|
|
+ client_sock = self.create_socket(family, type, protocol,
|
|
|
|
+ client_addr, False)
|
|
|
|
+ client_sock.setblocking(False)
|
|
|
|
+ try:
|
|
|
|
+ client_sock.connect(local)
|
|
|
|
+ except socket.error:
|
|
|
|
+ pass
|
|
|
|
+ server_sock, _ = sock.accept()
|
|
|
|
+ fwd_fd = server_sock.fileno()
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ if new_connection:
|
|
|
|
+ self.start_listen()
|
|
|
|
+ self.forwarder.connect_to_receiver()
|
|
|
|
+ self.accept_sock = self.accept_forwarder()
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ self.forwarder.push(fwd_fd, family, type, protocol, local, remote,
|
|
|
|
+ data)
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ receiver = SocketSessionReceiver(self.accept_sock)
|
|
|
|
+ signal.alarm(1)
|
|
|
|
+ sock_session = receiver.pop()
|
|
|
|
+ signal.alarm(0)
|
|
|
|
+ passed_sock = sock_session[0]
|
|
|
|
+ self.assertNotEqual(fwd_fd, passed_sock.fileno())
|
|
|
|
+ self.assertEqual(family, passed_sock.family)
|
|
|
|
+ self.assertEqual(type, passed_sock.type)
|
|
|
|
+ self.assertEqual(protocol, passed_sock.proto)
|
|
|
|
+ self.assertEqual(local, sock_session[1])
|
|
|
|
+ self.assertEqual(remote, sock_session[2])
|
|
|
|
+ self.assertEqual(data, sock_session[3])
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ passed_sock.setblocking(True)
|
|
|
|
+ if protocol == IPPROTO_UDP:
|
|
|
|
+ self.assertEqual(len(TEST_DATA), passed_sock.sendto(TEST_DATA,
|
|
|
|
+ local))
|
|
|
|
+ sock.settimeout(10)
|
|
|
|
+ self.assertEqual(TEST_DATA, sock.recvfrom(len(TEST_DATA))[0])
|
|
|
|
+ else:
|
|
|
|
+ server_sock.close()
|
|
|
|
+ self.assertEqual(len(TEST_DATA), passed_sock.send(TEST_DATA))
|
|
|
|
+ client_sock.setblocking(True)
|
|
|
|
+ client_sock.settimeout(10)
|
|
|
|
+ self.assertEqual(TEST_DATA, client_sock.recv(len(TEST_DATA)))
|
|
|
|
+
|
|
|
|
+ def test_push_and_pop(self):
|
|
|
|
+
|
|
|
|
+ local6 = ('::1', TEST_PORT, 0, 0)
|
|
|
|
+ remote6 = ('2001:db8::1', 5300, 0, 0)
|
|
|
|
+ self.check_push_and_pop(AF_INET6, SOCK_DGRAM, IPPROTO_UDP,
|
|
|
|
+ local6, remote6, TEST_DATA, True)
|
|
|
|
+ self.check_push_and_pop(AF_INET6, SOCK_STREAM, IPPROTO_TCP,
|
|
|
|
+ local6, remote6, TEST_DATA, False)
|
|
|
|
+
|
|
|
|
+ local4 = ('127.0.0.1', TEST_PORT)
|
|
|
|
+ remote4 = ('192.0.2.2', 5300)
|
|
|
|
+ self.check_push_and_pop(AF_INET, SOCK_DGRAM, IPPROTO_UDP,
|
|
|
|
+ local4, remote4, TEST_DATA, False)
|
|
|
|
+ self.check_push_and_pop(AF_INET, SOCK_STREAM, IPPROTO_TCP,
|
|
|
|
+ local4, remote4, TEST_DATA, False)
|
|
|
|
+
|
|
|
|
+ self.check_push_and_pop(AF_INET6, SOCK_DGRAM, IPPROTO_UDP,
|
|
|
|
+ local6, remote6, self.large_text, False)
|
|
|
|
+ self.check_push_and_pop(AF_INET6, SOCK_STREAM, IPPROTO_TCP,
|
|
|
|
+ local6, remote6, self.large_text, False)
|
|
|
|
+ self.check_push_and_pop(AF_INET, SOCK_DGRAM, IPPROTO_UDP,
|
|
|
|
+ local4, remote4, self.large_text, False)
|
|
|
|
+ self.check_push_and_pop(AF_INET, SOCK_STREAM, IPPROTO_TCP,
|
|
|
|
+ local4, remote4, self.large_text, False)
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ scope6 = ('fe80::1', TEST_PORT, 0, 1)
|
|
|
|
+ self.check_push_and_pop(AF_INET6, SOCK_DGRAM, IPPROTO_UDP,
|
|
|
|
+ local6, scope6, TEST_DATA, False)
|
|
|
|
+
|
|
|
|
+ def test_push_too_fast(self):
|
|
|
|
+
|
|
|
|
+ def multi_push(forwarder, addr, data):
|
|
|
|
+ for i in range(0, 10):
|
|
|
|
+ forwarder.push(1, AF_INET, SOCK_DGRAM, IPPROTO_UDP, addr,
|
|
|
|
+ addr, data)
|
|
|
|
+ self.start_listen()
|
|
|
|
+ self.forwarder.connect_to_receiver()
|
|
|
|
+ self.assertRaises(SocketSessionError, multi_push, self.forwarder,
|
|
|
|
+ ('192.0.2.1', 53), self.large_text)
|
|
|
|
+
|
|
|
|
+ def test_bad_pop(self):
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ self.start_listen()
|
|
|
|
+ s = socket.socket(socket.AF_UNIX, SOCK_STREAM, 0)
|
|
|
|
+ s.setblocking(False)
|
|
|
|
+ s.connect(TEST_UNIX_FILE)
|
|
|
|
+ accept_sock = self.accept_forwarder()
|
|
|
|
+ receiver = SocketSessionReceiver(accept_sock)
|
|
|
|
+ s.close()
|
|
|
|
+ self.assertRaises(SocketSessionError, receiver.pop)
|
|
|
|
+
|
|
|
|
+class TestReceiver(unittest.TestCase):
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ def test_bad_init(self):
|
|
|
|
+ class FakeSocket:
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ def fileno(self):
|
|
|
|
+ return None
|
|
|
|
+ self.assertRaises(TypeError, SocketSessionReceiver, 1)
|
|
|
|
+ self.assertRaises(TypeError, SocketSessionReceiver, FakeSocket())
|
|
|
|
+
|
|
|
|
+if __name__ == '__main__':
|
|
|
|
+ unittest.main()
|