Browse Source

Refactor the code of xfrin and add config item 'masters' to xfrin, make sure when xfrin get the notify command from zone manager, the zone transfer will be triggered.

git-svn-id: svn://bind10.isc.org/svn/bind10/branches/trac289@2628 e5f2f494-b856-4b98-b285-d166d9295462
Likun Zhang 14 years ago
parent
commit
c029db4fbf
2 changed files with 86 additions and 70 deletions
  1. 66 68
      src/bin/xfrin/xfrin.py.in
  2. 20 2
      src/bin/xfrin/xfrin.spec.pre.in

+ 66 - 68
src/bin/xfrin/xfrin.py.in

@@ -362,18 +362,19 @@ class XfrinRecorder:
 
 
 class Xfrin:
 class Xfrin:
     def __init__(self, verbose = False):
     def __init__(self, verbose = False):
-        self._cc_setup()
         self._max_transfers_in = 10
         self._max_transfers_in = 10
+        #TODO, this is the temp way to set the zone's master.
+        self._masters = {} 
+        self._cc_setup()
         self.recorder = XfrinRecorder()
         self.recorder = XfrinRecorder()
         self._shutdown_event = threading.Event()
         self._shutdown_event = threading.Event()
         self._verbose = verbose
         self._verbose = verbose
 
 
     def _cc_setup(self):
     def _cc_setup(self):
-        '''
-This method is used only as part of initialization, but is implemented
-separately for convenience of unit tests; by letting the test code override
-this method we can test most of this class without requiring a command channel.
-'''
+        '''This method is used only as part of initialization, but is 
+        implemented separately for convenience of unit tests; by letting 
+        the test code override this method we can test most of this class 
+        without requiring a command channel.'''
         # Create one session for sending command to other modules, because the 
         # Create one session for sending command to other modules, because the 
         # listening session will block the send operation.
         # listening session will block the send operation.
         self._send_cc_session = isc.cc.Session()
         self._send_cc_session = isc.cc.Session()
@@ -381,16 +382,32 @@ this method we can test most of this class without requiring a command channel.
                                               self.config_handler,
                                               self.config_handler,
                                               self.command_handler)
                                               self.command_handler)
         self._module_cc.start()
         self._module_cc.start()
+        config_data = self._module_cc.get_full_config()
+        self._max_transfers_in = config_data.get("transfers_in")
+        self._masters = config_data.get('masters')
 
 
     def _cc_check_command(self):
     def _cc_check_command(self):
-        '''
-This is a straightforward wrapper for cc.check_command, but provided as
-a separate method for the convenience of unit tests.
-'''
+        '''This is a straightforward wrapper for cc.check_command, 
+        but provided as a separate method for the convenience 
+        of unit tests.'''
         self._module_cc.check_command()
         self._module_cc.check_command()
 
 
     def config_handler(self, new_config):
     def config_handler(self, new_config):
-        # TODO, process new config data
+        self._max_transfers_in = new_config.get("transfers_in") or self._max_transfers_in
+        new_master = new_config.get('masters')
+        if new_master:
+            # Check if the new master is valid, there should be library for check it.
+            # and user should change the port and address together.
+            try:
+                new_master['address'] = new_master.get('address') or self._masters.get('address')
+                new_master['port'] = new_master.get('port') or self._masters.get('port')
+                check_addr_port(new_master.get('address'), new_master.get('port'))
+                self._masters = new_master
+            except:
+                errmsg = "bad format for zone's master: " + str(new_master)
+                log_error(errmsg)
+                return create_answer(1, errmsg)
+
         return create_answer(0)
         return create_answer(0)
 
 
     def shutdown(self):
     def shutdown(self):
@@ -404,67 +421,32 @@ a separate method for the convenience of unit tests.
                 continue
                 continue
             th.join()
             th.join()
 
 
-
     def command_handler(self, command, args):
     def command_handler(self, command, args):
         answer = create_answer(0)
         answer = create_answer(0)
         try:
         try:
             if command == 'shutdown':
             if command == 'shutdown':
                 self._shutdown_event.set()
                 self._shutdown_event.set()
-            elif command == 'retransfer' or command == 'refresh':
-                (zone_name, rrclass,
-                 master_addr, db_file) = self._parse_cmd_params(args)
-                ret = self.xfrin_start(zone_name, rrclass, db_file,
-                                       master_addr,
-                                   False if command == 'retransfer' else True)
-                answer = create_answer(ret[0], ret[1])
-            elif command == 'notify':
-                # This is the temporary implementation for notify.
-                # actually the notfiy command should be sent to the
-                # Zone Manager module.  Being temporary, we separate this case
-                # from refresh/retransfer while we could (and should otherwise)
-                # share the code.
-                (zone_name, rrclass,
-                 master_addr, db_file) = self._parse_cmd_params(args)
-
-                # XXX: master_addr is the sender of the notify message.
-                # It's very dangerous to naively trust it as the source of
-                # subsequent zone transfer; any remote node can easily exploit
-                # it to mount zone poisoning or DoS attacks.  We should
-                # locally identify the appropriate set of master servers.
-                # For now, we disable the code below.
-                master_is_valid = False 
-
-                if master_is_valid:
-                    ret = self.xfrin_start(zone_name, rrclass, db_file,
-                                           master_addr, True)
-                else:
-                    errmsg = 'Failed to validate the master address ('
-                    errmsg += args['master'] + '), ignoring notify'
-                    ret = [1, errmsg]
+            elif command == 'notify' or \
+                 command == 'retransfer' or \
+                 command == 'refresh':
+                # Xfrin maybe receives the refresh/notify command from zone manager, or 
+                # the retransfer/refresh from cmdctl(sent by bindctl).
+                # If the command has specified master address, do transfer from the 
+                # master address, or else do transfer from the configured masters.                
+                # notify command maybe has the parameters which 
+                # specify the notifyfrom address and port, according the RFC1996, zone
+                # transfer should starts first from the notifyfrom, but now, let 'TODO' it.
+                (zone_name, rrclass, master_addr, db_file) = self._parse_cmd_params(args)
+                ret = self.xfrin_start(zone_name, rrclass, 
+                                       db_file, master_addr,
+                                       (False if command == 'retransfer' else True))
                 answer = create_answer(ret[0], ret[1])
                 answer = create_answer(ret[0], ret[1])
             else:
             else:
                 answer = create_answer(1, 'unknown command: ' + command)
                 answer = create_answer(1, 'unknown command: ' + command)
-
         except XfrinException as err:
         except XfrinException as err:
             answer = create_answer(1, str(err))
             answer = create_answer(1, str(err))
-
         return answer
         return answer
 
 
-    def publish_xfrin_news(self, zone_name, xfr_result):
-        '''Send command to xfrout/zone manager module.
-        If xfrin has finished successfully for one zone, tell the good 
-        news(command: zone_new_data_ready) to zone manager and xfrout.
-        if xfrin failed, just tell the bad news to zone manager, so that 
-        it can reset the refresh timer for that zone. '''
-        param = {'zone_name': zone_name}
-        if xfr_result == XFRIN_OK:
-            msg = create_command(notify_out.ZONE_NEW_DATA_READY_CMD, param)
-            self._send_cc_session.group_sendmsg(msg, XFROUT_MODULE_NAME)
-            self._send_cc_session.group_sendmsg(msg, ZONE_MANAGER_MODULE_NAME)
-        else:
-            msg = create_command(ZONE_XFRIN_FAILED, param)
-            self._send_cc_session.group_sendmsg(msg, ZONE_MANAGER_MODULE_NAME)
-
     def _parse_cmd_params(self, args):
     def _parse_cmd_params(self, args):
         zone_name = args.get('zone_name')
         zone_name = args.get('zone_name')
         if not zone_name:
         if not zone_name:
@@ -472,9 +454,6 @@ a separate method for the convenience of unit tests.
 
 
         rrclass = args.get('zone_class')
         rrclass = args.get('zone_class')
         if not rrclass:
         if not rrclass:
-            # The default RR class is IN.  We should fix this so that
-            # the class is always passed in the command arg (where we specify
-            # the default)
             rrclass = RRClass.IN()
             rrclass = RRClass.IN()
         else:
         else:
             try:
             try:
@@ -482,15 +461,19 @@ a separate method for the convenience of unit tests.
             except InvalidRRClass as e:
             except InvalidRRClass as e:
                 raise XfrinException('invalid RRClass: ' + rrclass)
                 raise XfrinException('invalid RRClass: ' + rrclass)
 
 
-        master = args.get('master')
-        if not master:
-            raise XfrinException('master address should be provided')
-
         port_str = args.get('port')
         port_str = args.get('port')
         if not port_str:
         if not port_str:
             port_str = DEFAULT_MASTER_PORT
             port_str = DEFAULT_MASTER_PORT
-        master_addrinfo = check_addr_port(master, port_str)
 
 
+        master = args.get('master')
+        if not master:
+            if len(self._masters) > 0:
+                master = self._masters.get('address')
+                port_str = self._masters.get('port')
+            else:
+                raise XfrinException("zone's master should be provided")
+
+        master_addrinfo = check_addr_port(master, port_str)
         db_file = args.get('db_file')
         db_file = args.get('db_file')
         if not db_file:
         if not db_file:
             #TODO, the db file path should be got in auth server's configuration
             #TODO, the db file path should be got in auth server's configuration
@@ -509,6 +492,21 @@ a separate method for the convenience of unit tests.
 
 
         return (zone_name, rrclass, master_addrinfo, db_file)
         return (zone_name, rrclass, master_addrinfo, db_file)
 
 
+    def publish_xfrin_news(self, zone_name, xfr_result):
+        '''Send command to xfrout/zone manager module.
+        If xfrin has finished successfully for one zone, tell the good 
+        news(command: zone_new_data_ready) to zone manager and xfrout.
+        if xfrin failed, just tell the bad news to zone manager, so that 
+        it can reset the refresh timer for that zone. '''
+        param = {'zone_name': zone_name}
+        if xfr_result == XFRIN_OK:
+            msg = create_command(notify_out.ZONE_NEW_DATA_READY_CMD, param)
+            self._send_cc_session.group_sendmsg(msg, XFROUT_MODULE_NAME)
+            self._send_cc_session.group_sendmsg(msg, ZONE_MANAGER_MODULE_NAME)
+        else:
+            msg = create_command(ZONE_XFRIN_FAILED, param)
+            self._send_cc_session.group_sendmsg(msg, ZONE_MANAGER_MODULE_NAME)
+
     def startup(self):
     def startup(self):
         while not self._shutdown_event.is_set():
         while not self._shutdown_event.is_set():
             self._cc_check_command()
             self._cc_check_command()

+ 20 - 2
src/bin/xfrin/xfrin.spec.pre.in

@@ -8,7 +8,25 @@
         "item_type": "integer",
         "item_type": "integer",
         "item_optional": false,
         "item_optional": false,
         "item_default": 10
         "item_default": 10
-      }
+      },
+      {
+        "item_name": "masters",
+        "item_type": "map",
+        "item_optional": true,
+        "item_default": {},
+        "map_item_spec": [
+           { "item_name": "address",
+             "item_type": "string",
+             "item_optional": false,
+             "item_default": ""
+           },
+           { "item_name": "port",
+             "item_type": "integer",
+             "item_optional": false,
+             "item_default": 53
+           }
+         ]
+       }
     ],
     ],
     "commands": [
     "commands": [
      {
      {
@@ -29,7 +47,7 @@
           {
           {
             "item_name": "master",
             "item_name": "master",
             "item_type": "string",
             "item_type": "string",
-            "item_optional": false,
+            "item_optional": true,
             "item_default": ""
             "item_default": ""
           },
           },
           {
           {