|
@@ -0,0 +1,241 @@
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+'''Tests for isc.server_common.dns_tcp'''
|
|
|
|
+
|
|
|
|
+import isc.log
|
|
|
|
+from isc.server_common.dns_tcp import *
|
|
|
|
+import socket
|
|
|
|
+import errno
|
|
|
|
+import unittest
|
|
|
|
+
|
|
|
|
+def check_length_field(assert_eq, len_data, expected_len):
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ len_high = (expected_len >> 8) & 0x00ff
|
|
|
|
+ len_low = (expected_len & 0x00ff)
|
|
|
|
+ assert_eq(2, len(len_data))
|
|
|
|
+ assert_eq(len_high, len_data[0])
|
|
|
|
+ assert_eq(len_low, len_data[1])
|
|
|
|
+
|
|
|
|
+class BufferTest(unittest.TestCase):
|
|
|
|
+ def check_length_field(self, buf, expected_len):
|
|
|
|
+ '''Common subtest for the main tests that checks the length buffer.'''
|
|
|
|
+ check_length_field(self.assertEqual, buf.get_data(0), expected_len)
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ self.assertEqual(1, len(buf.get_data(1)))
|
|
|
|
+ self.assertEqual(expected_len & 0x00ff, buf.get_data(1)[0])
|
|
|
|
+
|
|
|
|
+ def test_small_data(self):
|
|
|
|
+
|
|
|
|
+ expected_data = b'x' * 12
|
|
|
|
+ buf = DNSTCPSendBuffer(expected_data)
|
|
|
|
+ self.check_length_field(buf, 12)
|
|
|
|
+
|
|
|
|
+ self.assertEqual(expected_data, buf.get_data(2))
|
|
|
|
+ self.assertEqual(b'x' * 11, buf.get_data(3))
|
|
|
|
+ self.assertEqual(None, buf.get_data(14))
|
|
|
|
+
|
|
|
|
+ def test_large_data(self):
|
|
|
|
+
|
|
|
|
+ buf = DNSTCPSendBuffer(b'x' * 65534)
|
|
|
|
+ self.check_length_field(buf, 65534)
|
|
|
|
+ self.assertEqual(b'x' * 65534, buf.get_data(2))
|
|
|
|
+ self.assertEqual(b'x' * 2, buf.get_data(65534))
|
|
|
|
+ self.assertEqual(None, buf.get_data(65536))
|
|
|
|
+
|
|
|
|
+ def test_largest_data(self):
|
|
|
|
+
|
|
|
|
+ buf = DNSTCPSendBuffer(b'y' * 65535)
|
|
|
|
+ self.check_length_field(buf, 65535)
|
|
|
|
+ self.assertEqual(b'y', buf.get_data(65536))
|
|
|
|
+ self.assertEqual(None, buf.get_data(65537))
|
|
|
|
+
|
|
|
|
+ def test_too_large_data(self):
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ self.assertRaises(ValueError, DNSTCPSendBuffer, b'0' * 65536)
|
|
|
|
+
|
|
|
|
+ def test_empty_data(self):
|
|
|
|
+
|
|
|
|
+ buf = DNSTCPSendBuffer(b'')
|
|
|
|
+ self.check_length_field(buf, 0)
|
|
|
|
+ self.assertEqual(None, buf.get_data(2))
|
|
|
|
+
|
|
|
|
+class FakeSocket:
|
|
|
|
+ '''Emulating python socket w/o involving IO while allowing inspection.'''
|
|
|
|
+ def __init__(self, proto=socket.IPPROTO_TCP):
|
|
|
|
+ self._setblocking_val = None
|
|
|
|
+ self._closed = False
|
|
|
|
+ self._sent_data = []
|
|
|
|
+ self._send_buflen = None
|
|
|
|
+
|
|
|
|
+ self._send_cc = 0
|
|
|
|
+ self.proto = proto
|
|
|
|
+
|
|
|
|
+ def setblocking(self, on):
|
|
|
|
+ self._setblocking_val = on
|
|
|
|
+
|
|
|
|
+ def close(self):
|
|
|
|
+ self._closed = True
|
|
|
|
+
|
|
|
|
+ def send(self, data):
|
|
|
|
+
|
|
|
|
+ if self._send_buflen == -1:
|
|
|
|
+ raise socket.error(errno.EPIPE, "Broken pipe")
|
|
|
|
+ elif self._send_buflen is None:
|
|
|
|
+ available_space = len(data)
|
|
|
|
+ else:
|
|
|
|
+ available_space = self._send_buflen - self._send_cc
|
|
|
|
+ if available_space == 0:
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ raise socket.error(errno.EAGAIN,
|
|
|
|
+ "Resource temporarily unavailable")
|
|
|
|
+
|
|
|
|
+ cc = min(available_space, len(data))
|
|
|
|
+ self._sent_data.append(data[:cc])
|
|
|
|
+ self._send_cc += cc
|
|
|
|
+ return cc
|
|
|
|
+
|
|
|
|
+ def make_send_ready(self):
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ self._send_cc = 0
|
|
|
|
+
|
|
|
|
+ def getpeername(self):
|
|
|
|
+ '''Return faked remote address'''
|
|
|
|
+ return ('2001:db8::1', 53000, 0, 0)
|
|
|
|
+
|
|
|
|
+class ContextTest(unittest.TestCase):
|
|
|
|
+ def setUp(self):
|
|
|
|
+ self.__sock = FakeSocket()
|
|
|
|
+
|
|
|
|
+ self.assertEqual(None, self.__sock._setblocking_val)
|
|
|
|
+ self.__ctx = DNSTCPContext(self.__sock)
|
|
|
|
+
|
|
|
|
+ self.__test_data = b'x' * 12
|
|
|
|
+
|
|
|
|
+ def test_initialization(self):
|
|
|
|
+
|
|
|
|
+ self.assertFalse(self.__sock._setblocking_val)
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ self.assertRaises(DNSTCPContextError, DNSTCPContext,
|
|
|
|
+ FakeSocket(proto=socket.IPPROTO_UDP))
|
|
|
|
+
|
|
|
|
+ def test_send_all(self):
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ self.assertEqual(DNSTCPContext.SEND_DONE,
|
|
|
|
+ self.__ctx.send(self.__test_data))
|
|
|
|
+ self.assertEqual(2, len(self.__sock._sent_data))
|
|
|
|
+ check_length_field(self.assertEqual, self.__sock._sent_data[0],
|
|
|
|
+ len(self.__test_data))
|
|
|
|
+ self.assertEqual(self.__test_data, self.__sock._sent_data[1])
|
|
|
|
+
|
|
|
|
+ def test_send_divided(self):
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ self.__sock._send_buflen = 7
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ self.assertEqual(DNSTCPContext.SENDING,
|
|
|
|
+ self.__ctx.send(self.__test_data))
|
|
|
|
+ self.assertEqual(2, len(self.__sock._sent_data))
|
|
|
|
+ check_length_field(self.assertEqual, self.__sock._sent_data[0],
|
|
|
|
+ len(self.__test_data))
|
|
|
|
+ self.assertEqual(self.__test_data[:5], self.__sock._sent_data[1])
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ self.__sock.make_send_ready()
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ self.assertEqual(DNSTCPContext.SEND_DONE, self.__ctx.send_ready())
|
|
|
|
+ self.assertEqual(3, len(self.__sock._sent_data))
|
|
|
|
+ self.assertEqual(self.__test_data[5:], self.__sock._sent_data[2])
|
|
|
|
+
|
|
|
|
+ def test_send_multi(self):
|
|
|
|
+
|
|
|
|
+ for i in (0, 2):
|
|
|
|
+ self.assertEqual(DNSTCPContext.SEND_DONE,
|
|
|
|
+ self.__ctx.send(self.__test_data))
|
|
|
|
+ self.assertEqual(i + 2, len(self.__sock._sent_data))
|
|
|
|
+ check_length_field(self.assertEqual, self.__sock._sent_data[i],
|
|
|
|
+ len(self.__test_data))
|
|
|
|
+ self.assertEqual(self.__test_data, self.__sock._sent_data[i + 1])
|
|
|
|
+
|
|
|
|
+ def test_send_reset(self):
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ self.__sock._send_buflen = -1
|
|
|
|
+ self.assertEqual(DNSTCPContext.CLOSED,
|
|
|
|
+ self.__ctx.send(self.__test_data))
|
|
|
|
+ self.assertTrue(self.__sock._closed)
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ self.assertRaises(DNSTCPContextError, self.__ctx.send,
|
|
|
|
+ self.__test_data)
|
|
|
|
+
|
|
|
|
+ self.__ctx.close()
|
|
|
|
+
|
|
|
|
+ def test_send_divided_reset(self):
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ self.__sock._send_buflen = 7
|
|
|
|
+ self.assertEqual(DNSTCPContext.SENDING,
|
|
|
|
+ self.__ctx.send(self.__test_data))
|
|
|
|
+ self.__sock._send_buflen = -1
|
|
|
|
+ self.assertEqual(DNSTCPContext.CLOSED, self.__ctx.send_ready())
|
|
|
|
+ self.assertTrue(self.__sock._closed)
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ self.assertRaises(DNSTCPContextError, self.__ctx.send_ready)
|
|
|
|
+
|
|
|
|
+ def test_duplicate_send(self):
|
|
|
|
+
|
|
|
|
+ self.__sock._send_buflen = 7
|
|
|
|
+ self.assertEqual(DNSTCPContext.SENDING,
|
|
|
|
+ self.__ctx.send(self.__test_data))
|
|
|
|
+ self.assertRaises(DNSTCPContextError, self.__ctx.send,
|
|
|
|
+ self.__test_data)
|
|
|
|
+
|
|
|
|
+ def test_skip_send(self):
|
|
|
|
+
|
|
|
|
+ self.assertRaises(DNSTCPContextError, self.__ctx.send_ready)
|
|
|
|
+
|
|
|
|
+ def test_close(self):
|
|
|
|
+ self.assertEqual(DNSTCPContext.SEND_DONE,
|
|
|
|
+ self.__ctx.send(self.__test_data))
|
|
|
|
+ self.__ctx.close()
|
|
|
|
+ self.assertTrue(self.__sock._closed)
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ self.__ctx.close()
|
|
|
|
+ self.assertTrue(self.__sock._closed)
|
|
|
|
+
|
|
|
|
+if __name__ == "__main__":
|
|
|
|
+ isc.log.init("bind10")
|
|
|
|
+ isc.log.resetUnitTestRootLogger()
|
|
|
|
+ unittest.main()
|