Browse Source

[2003] log the event when continued TCP send failed.

JINMEI Tatuya 13 years ago
parent
commit
f79c74e2f7
3 changed files with 29 additions and 12 deletions
  1. 9 6
      src/bin/ddns/ddns.py.in
  2. 15 1
      src/bin/ddns/ddns_messages.mes
  3. 5 5
      src/bin/ddns/tests/ddns_test.py

+ 9 - 6
src/bin/ddns/ddns.py.in

@@ -197,7 +197,7 @@ class DDNSServer:
         #
         # DDNS Protocol handling class.
         self._UpdateSessionClass = isc.ddns.session.UpdateSession
-        # Outstanding TCP context: fileno=>context_obj
+        # Outstanding TCP context: fileno=>(context_obj, dst)
         self._tcp_ctxs = {}
 
     class InternalError(Exception):
@@ -409,13 +409,13 @@ class DDNSServer:
                 tcp_ctx = DNSTCPContext(sock)
                 send_result = tcp_ctx.send(data)
                 if send_result == DNSTCPContext.SENDING:
-                    self._tcp_ctxs[sock.fileno] = tcp_ctx
+                    self._tcp_ctxs[sock.fileno] = (tcp_ctx, dest)
                 elif send_result == DNSTCPContext.CLOSED:
                     raise socket.error("socket error in TCP send")
                 else:
                     tcp_ctx.close()
         except socket.error as ex:
-            logger.error(DDNS_RESPONSE_SOCKET_ERROR, ClientFormatter(dest), ex)
+            logger.warn(DDNS_RESPONSE_SOCKET_ERROR, ClientFormatter(dest), ex)
             return False
 
         return True
@@ -507,10 +507,13 @@ class DDNSServer:
                 else:
                     self.handle_session(fileno)
             for fileno in writes:
-                tcp_ctx = self._tcp_ctxs[fileno]
-                result = tcp_ctx.send_ready()
+                ctx = self._tcp_ctxs[fileno]
+                result = ctx[0].send_ready()
                 if result != DNSTCPContext.SENDING:
-                    tcp_ctx.close()
+                    if result == DNSTCPContext.CLOSED:
+                        logger.warn(DDNS_RESPONSE_TCP_SOCKET_ERROR,
+                                    ClientFormatter(ctx[1]))
+                    ctx[0].close()
                     del self._tcp_ctxs[fileno]
         self.shutdown_cleanup()
         logger.info(DDNS_STOPPED)

+ 15 - 1
src/bin/ddns/ddns_messages.mes

@@ -82,7 +82,7 @@ event is therefore logged at the error level, and the request is
 simply dropped.  Additional information of the error is also logged.
 
 % DDNS_RESPONSE_SOCKET_ERROR failed to send update response to %1: %2
-Network I/O error happens in sending an update request.  The
+Network I/O error happens in sending an update response.  The
 client's address that caused the error and error details are also
 logged.
 
@@ -135,3 +135,17 @@ needs to examine this message and takes an appropriate action.  In
 either case, this notification is generally expected to succeed; so
 the fact it fails itself means there's something wrong in the BIND 10
 system, and it would be advisable to check other log messages.
+
+% DDNS_RESPONSE_TCP_SOCKET_ERROR failed to complete sending update response to %1 over TCP
+b10-ddns had tried to send an update response over TCP, and it hadn't
+been completed at that time, and a followup attempt to complete the
+send operation failed due to some network I/O error.  While a network
+error can happen any time, this event is quite unexpected for two
+reasons.  First, since the size of a response to an update request
+should be generally small, it's unlikely that the initial attempt
+didn't fail but wasn't completed.  Second, since the first attempt
+succeeded and the TCP connection had been established in the first
+place, it's more likely for the subsequent attempt to succeed.  In any
+case, there may not be able to do anything to fix it at the server
+side, but the administrator may want to check the general reachability
+with the client address.

+ 5 - 5
src/bin/ddns/tests/ddns_test.py

@@ -631,7 +631,7 @@ class TestDDNSServer(unittest.TestCase):
     def __send_select_tcp(self, buflen, raise_after_select=False):
         '''Common subroutine for some TCP related tests below.'''
         fileno = self.__tcp_sock.fileno()
-        self.ddns_server._tcp_ctxs = {fileno: self.__tcp_ctx}
+        self.ddns_server._tcp_ctxs = {fileno: (self.__tcp_ctx, TEST_CLIENT6)}
 
         # make an initial, incomplete send via the test context
         self.__tcp_sock._send_buflen = buflen
@@ -671,8 +671,9 @@ class TestDDNSServer(unittest.TestCase):
         # watch it again).
         self.assertEqual(10, len(self.__tcp_sock._sent_data))
         self.assertEqual(0, self.__tcp_sock._close_called)
+        fileno = self.__tcp_sock.fileno()
         self.assertEqual(self.__tcp_ctx,
-                         self.ddns_server._tcp_ctxs[self.__tcp_sock.fileno()])
+                         self.ddns_server._tcp_ctxs[fileno][0])
 
     def test_select_send_continued_failed(self):
         '''Test continuation of sending a TCP response, which fails.'''
@@ -696,7 +697,7 @@ class TestDDNSServer(unittest.TestCase):
             # Use faked FD of 100, 101, 102 (again, arbitrary choice)
             s = FakeSocket(100 + i, proto=socket.IPPROTO_TCP)
             ctx = DNSTCPContext(s)
-            self.ddns_server._tcp_ctxs[s.fileno()] = ctx
+            self.ddns_server._tcp_ctxs[s.fileno()] = (ctx, TEST_CLIENT6)
             s._send_buflen = 7  # make sure it requires two send's
             self.assertEqual(DNSTCPContext.SENDING, ctx.send(self.__tcp_data))
             s.make_send_ready()
@@ -900,8 +901,7 @@ class TestDDNSSession(unittest.TestCase):
         # data should be the expected response.
         s.make_send_ready()
         self.assertEqual(DNSTCPContext.SEND_DONE,
-                         self.server._tcp_ctxs[s.fileno].send_ready())
-        self.server._tcp_ctxs[s.fileno].close() # explicit close per convention
+                         self.server._tcp_ctxs[s.fileno][0].send_ready())
         self.check_update_response(s._sent_data, Rcode.NOERROR(), tcp=True)
 
     def test_tcp_request_error(self):