Browse Source

[2003] supported a simple case with TCP response.

it only handles the case where the initial send() completes the transmission.
JINMEI Tatuya 13 years ago
parent
commit
37883d293d
2 changed files with 40 additions and 10 deletions
  1. 7 3
      src/bin/ddns/ddns.py.in
  2. 33 7
      src/bin/ddns/tests/ddns_test.py

+ 7 - 3
src/bin/ddns/ddns.py.in

@@ -30,6 +30,7 @@ import isc.util.process
 import isc.util.cio.socketsession
 import isc.util.cio.socketsession
 from isc.notify.notify_out import ZONE_NEW_DATA_READY_CMD
 from isc.notify.notify_out import ZONE_NEW_DATA_READY_CMD
 import isc.server_common.tsig_keyring
 import isc.server_common.tsig_keyring
+from isc.server_common.dns_tcp import DNSTCPContext
 from isc.datasrc import DataSourceClient
 from isc.datasrc import DataSourceClient
 import select
 import select
 import errno
 import errno
@@ -342,8 +343,6 @@ class DDNSServer:
         # or dropped by the sender, so if such error is detected we treat it
         # or dropped by the sender, so if such error is detected we treat it
         # as an internal error and don't bother to respond.
         # as an internal error and don't bother to respond.
         try:
         try:
-            if sock.proto == socket.IPPROTO_TCP:
-                raise self.InternalError('TCP requests are not yet supported')
             self.__request_msg.clear(Message.PARSE)
             self.__request_msg.clear(Message.PARSE)
             # specify PRESERVE_ORDER as we need to handle each RR separately.
             # specify PRESERVE_ORDER as we need to handle each RR separately.
             self.__request_msg.from_wire(req_data, Message.PRESERVE_ORDER)
             self.__request_msg.from_wire(req_data, Message.PRESERVE_ORDER)
@@ -402,7 +401,12 @@ class DDNSServer:
 
 
         '''
         '''
         try:
         try:
-            sock.sendto(data, dest)
+            if sock.proto == socket.IPPROTO_UDP:
+                sock.sendto(data, dest)
+            else:
+                tcp_ctx = DNSTCPContext(sock)
+                tcp_ctx.send(data)
+                tcp_ctx.close()
         except socket.error as ex:
         except socket.error as ex:
             logger.error(DDNS_RESPONSE_SOCKET_ERROR, ClientFormatter(dest), ex)
             logger.error(DDNS_RESPONSE_SOCKET_ERROR, ClientFormatter(dest), ex)
             return False
             return False

+ 33 - 7
src/bin/ddns/tests/ddns_test.py

@@ -64,12 +64,16 @@ class FakeSocket:
         self.__fileno = fileno
         self.__fileno = fileno
         self._sent_data = None
         self._sent_data = None
         self._sent_addr = None
         self._sent_addr = None
+        self._close_called = 0  # number of calls to close()
         # customizable by tests; if set to True, sendto() will throw after
         # customizable by tests; if set to True, sendto() will throw after
         # recording the parameters.
         # recording the parameters.
         self._raise_on_send = False
         self._raise_on_send = False
     def fileno(self):
     def fileno(self):
         return self.__fileno
         return self.__fileno
     def getpeername(self):
     def getpeername(self):
+        if self.proto == socket.IPPROTO_UDP or \
+                self.proto == socket.IPPROTO_TCP:
+            return TEST_CLIENT4
         return "fake_unix_socket"
         return "fake_unix_socket"
     def accept(self):
     def accept(self):
         return FakeSocket(self.__fileno + 1), '/dummy/path'
         return FakeSocket(self.__fileno + 1), '/dummy/path'
@@ -78,6 +82,17 @@ class FakeSocket:
         self._sent_addr = addr
         self._sent_addr = addr
         if self._raise_on_send:
         if self._raise_on_send:
             raise socket.error('test socket failure')
             raise socket.error('test socket failure')
+    def send(self, data):
+        if self._sent_data is None:
+            self._sent_data = data
+        else:
+            self._sent_data += data
+        return len(data)
+    def setblocking(self, on):
+        # We only need a faked NO-OP implementation.
+        pass
+    def close(self):
+        self._close_called += 1
     def clear(self):
     def clear(self):
         '''Clear internal instrumental data.'''
         '''Clear internal instrumental data.'''
         self._sent_data = None
         self._sent_data = None
@@ -633,7 +648,7 @@ class TestDDNSSession(unittest.TestCase):
                                  self.__faked_result)
                                  self.__faked_result)
 
 
     def check_update_response(self, resp_wire, expected_rcode=Rcode.NOERROR(),
     def check_update_response(self, resp_wire, expected_rcode=Rcode.NOERROR(),
-                              tsig_ctx=None):
+                              tsig_ctx=None, tcp=False):
         '''Check if given wire data are valid form of update response.
         '''Check if given wire data are valid form of update response.
 
 
         In this implementation, zone/prerequisite/update sections should be
         In this implementation, zone/prerequisite/update sections should be
@@ -643,7 +658,15 @@ class TestDDNSSession(unittest.TestCase):
         be TSIG signed and the signature should be verifiable with the context
         be TSIG signed and the signature should be verifiable with the context
         that has signed the corresponding request.
         that has signed the corresponding request.
 
 
+        if tcp is True, the wire data are expected to be prepended with
+        a 2-byte length field.
+
         '''
         '''
+        if tcp:
+            data_len = resp_wire[0] * 256 + resp_wire[1]
+            resp_wire = resp_wire[2:]
+            self.assertEqual(len(resp_wire), data_len)
+
         msg = Message(Message.PARSE)
         msg = Message(Message.PARSE)
         msg.from_wire(resp_wire)
         msg.from_wire(resp_wire)
         if tsig_ctx is not None:
         if tsig_ctx is not None:
@@ -716,19 +739,22 @@ class TestDDNSSession(unittest.TestCase):
         self.__sock._raise_on_send = True
         self.__sock._raise_on_send = True
         # handle_request indicates the failure
         # handle_request indicates the failure
         self.assertFalse(self.server.handle_request((self.__sock, TEST_SERVER6,
         self.assertFalse(self.server.handle_request((self.__sock, TEST_SERVER6,
-                                                     TEST_SERVER4,
+                                                     TEST_CLIENT6,
                                                      create_msg())))
                                                      create_msg())))
         # this check ensures sendto() was really attempted.
         # this check ensures sendto() was really attempted.
         self.check_update_response(self.__sock._sent_data, Rcode.NOERROR())
         self.check_update_response(self.__sock._sent_data, Rcode.NOERROR())
 
 
     def test_tcp_request(self):
     def test_tcp_request(self):
-        # Right now TCP request is not supported.
+        # A simple case using TCP: all resopnse data are sent out at once.
         s = self.__sock
         s = self.__sock
         s.proto = socket.IPPROTO_TCP
         s.proto = socket.IPPROTO_TCP
-        self.assertFalse(self.server.handle_request((s, TEST_SERVER6,
-                                                     TEST_SERVER4,
-                                                     create_msg())))
-        self.assertEqual((None, None), (s._sent_data, s._sent_addr))
+        self.assertTrue(self.server.handle_request((s, TEST_SERVER6,
+                                                    TEST_CLIENT6,
+                                                    create_msg())))
+        self.check_update_response(s._sent_data, Rcode.NOERROR(), tcp=True)
+        # In the current implementation, the socket should be closed
+        # immedidately after a successful send.
+        self.assertEqual(1, s._close_called)
 
 
     def test_request_message(self):
     def test_request_message(self):
         '''Test if the request message stores RRs separately.'''
         '''Test if the request message stores RRs separately.'''