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.
         # DDNS Protocol handling class.
         self._UpdateSessionClass = isc.ddns.session.UpdateSession
         self._UpdateSessionClass = isc.ddns.session.UpdateSession
-        # Outstanding TCP context: fileno=>context_obj
+        # Outstanding TCP context: fileno=>(context_obj, dst)
         self._tcp_ctxs = {}
         self._tcp_ctxs = {}
 
 
     class InternalError(Exception):
     class InternalError(Exception):
@@ -409,13 +409,13 @@ class DDNSServer:
                 tcp_ctx = DNSTCPContext(sock)
                 tcp_ctx = DNSTCPContext(sock)
                 send_result = tcp_ctx.send(data)
                 send_result = tcp_ctx.send(data)
                 if send_result == DNSTCPContext.SENDING:
                 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:
                 elif send_result == DNSTCPContext.CLOSED:
                     raise socket.error("socket error in TCP send")
                     raise socket.error("socket error in TCP send")
                 else:
                 else:
                     tcp_ctx.close()
                     tcp_ctx.close()
         except socket.error as ex:
         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 False
 
 
         return True
         return True
@@ -507,10 +507,13 @@ class DDNSServer:
                 else:
                 else:
                     self.handle_session(fileno)
                     self.handle_session(fileno)
             for fileno in writes:
             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:
                 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]
                     del self._tcp_ctxs[fileno]
         self.shutdown_cleanup()
         self.shutdown_cleanup()
         logger.info(DDNS_STOPPED)
         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.
 simply dropped.  Additional information of the error is also logged.
 
 
 % DDNS_RESPONSE_SOCKET_ERROR failed to send update response to %1: %2
 % 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
 client's address that caused the error and error details are also
 logged.
 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
 either case, this notification is generally expected to succeed; so
 the fact it fails itself means there's something wrong in the BIND 10
 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.
 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):
     def __send_select_tcp(self, buflen, raise_after_select=False):
         '''Common subroutine for some TCP related tests below.'''
         '''Common subroutine for some TCP related tests below.'''
         fileno = self.__tcp_sock.fileno()
         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
         # make an initial, incomplete send via the test context
         self.__tcp_sock._send_buflen = buflen
         self.__tcp_sock._send_buflen = buflen
@@ -671,8 +671,9 @@ class TestDDNSServer(unittest.TestCase):
         # watch it again).
         # watch it again).
         self.assertEqual(10, len(self.__tcp_sock._sent_data))
         self.assertEqual(10, len(self.__tcp_sock._sent_data))
         self.assertEqual(0, self.__tcp_sock._close_called)
         self.assertEqual(0, self.__tcp_sock._close_called)
+        fileno = self.__tcp_sock.fileno()
         self.assertEqual(self.__tcp_ctx,
         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):
     def test_select_send_continued_failed(self):
         '''Test continuation of sending a TCP response, which fails.'''
         '''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)
             # Use faked FD of 100, 101, 102 (again, arbitrary choice)
             s = FakeSocket(100 + i, proto=socket.IPPROTO_TCP)
             s = FakeSocket(100 + i, proto=socket.IPPROTO_TCP)
             ctx = DNSTCPContext(s)
             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
             s._send_buflen = 7  # make sure it requires two send's
             self.assertEqual(DNSTCPContext.SENDING, ctx.send(self.__tcp_data))
             self.assertEqual(DNSTCPContext.SENDING, ctx.send(self.__tcp_data))
             s.make_send_ready()
             s.make_send_ready()
@@ -900,8 +901,7 @@ class TestDDNSSession(unittest.TestCase):
         # data should be the expected response.
         # data should be the expected response.
         s.make_send_ready()
         s.make_send_ready()
         self.assertEqual(DNSTCPContext.SEND_DONE,
         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)
         self.check_update_response(s._sent_data, Rcode.NOERROR(), tcp=True)
 
 
     def test_tcp_request_error(self):
     def test_tcp_request_error(self):