]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
- auth zone notify work.
authorWouter Wijngaards <wouter@nlnetlabs.nl>
Mon, 16 Apr 2018 14:24:57 +0000 (14:24 +0000)
committerWouter Wijngaards <wouter@nlnetlabs.nl>
Mon, 16 Apr 2018 14:24:57 +0000 (14:24 +0000)
git-svn-id: file:///svn/unbound/trunk@4625 be551aaa-1e26-0410-a405-d3ace91eadb9

daemon/worker.c
doc/Changelog
services/authzone.c
services/authzone.h

index c186424f7a79a08b4c6c27d1bb7e38f5103a0f1e..32ebf45becc93d7017430bd38d7d3896e784bbc3 100644 (file)
@@ -963,7 +963,7 @@ answer_notify(struct worker* w, struct query_info* qinfo,
        int has_serial;
        if(!w->env.auth_zones) return;
        has_serial = auth_zone_parse_notify_serial(pkt, &serial);
-       if(auth_zones_notify(w->env.auth_zones, qinfo->qname,
+       if(auth_zones_notify(w->env.auth_zones, &w->env, qinfo->qname,
                qinfo->qname_len, qinfo->qclass, &repinfo->addr,
                repinfo->addrlen, has_serial, serial, &refused)) {
                rcode = LDNS_RCODE_NOERROR;
index 14b5c330af3548e8cfcfc62258b4c9633152f76f..826d2fcd1b97c976377abaed4297f4d86c352af0 100644 (file)
@@ -1,6 +1,7 @@
 16 April 2018: Wouter
        - Fix auth zone target lookup iterator.
-       - notify with prefix
+       - auth zone notify with prefix
+       - auth zone notify work.
 
 13 April 2018: Wouter
        - Fix for max include depth for authzones.
index 5803ab558d2eb4e173df6be7851f9595e27bfbfc..30e6850050d2b8c20ea05122444f2bdab9ffb713 100644 (file)
@@ -95,6 +95,10 @@ static void xfr_set_timeout(struct auth_xfer* xfr, struct module_env* env,
 /** move to sending the probe packets, next if fails. task_probe */
 static void xfr_probe_send_or_end(struct auth_xfer* xfr,
        struct module_env* env);
+/** pick up probe task with specified(or NULL) destination first,
+ * or transfer task if nothing to probe, or false if already in progress */
+static int xfr_start_probe(struct auth_xfer* xfr, struct module_env* env,
+       struct auth_master* spec);
 
 /** create new dns_msg */
 static struct dns_msg*
@@ -3244,25 +3248,36 @@ addr_in_list(struct auth_addr* list, struct sockaddr_storage* addr,
  * addresses in the addr list) */
 static int
 addr_matches_master(struct auth_master* master, struct sockaddr_storage* addr,
-       socklen_t addrlen)
+       socklen_t addrlen, struct auth_master** fromhost)
 {
        struct sockaddr_storage a;
        socklen_t alen = 0;
        int net = 0;
-       if(addr_in_list(master->list, addr, addrlen))
-               return 1;
+       if(addr_in_list(master->list, addr, addrlen)) {
+               *fromhost = master;
+               return 1;       
+       }
+       /* compare address (but not port number, that is the destination
+        * port of the master, the port number of the received notify is
+        * allowed to by any port on that master) */
        if(extstrtoaddr(master->host, &a, &alen) &&
-               sockaddr_cmp_addr(addr, addrlen, &a, alen)==0)
+               sockaddr_cmp_addr(addr, addrlen, &a, alen)==0) {
+               *fromhost = master;
                return 1;
+       }
        /* prefixes, addr/len, like 10.0.0.0/8 */
        /* not http and has a / and there is one / */
+       /* TODO: for allow-notify elements */
        if(!master->http && strchr(master->host, '/')!=NULL &&
                strchr(master->host, '/') == strrchr(master->host, '/') &&
                netblockstrtoaddr(master->host, UNBOUND_DNS_PORT, &a, &alen,
                &net) && alen == addrlen) {
                if(addr_in_common(addr, (addr_is_ip6(addr, addrlen)?128:32),
-                       &a, net, alen) >= net)
+                       &a, net, alen) >= net) {
+                       *fromhost = NULL; /* prefix does not have destination
+                               to send the probe or transfer with */
                        return 1; /* matches the netblock */
+               }
        }
        return 0;
 }
@@ -3270,34 +3285,74 @@ addr_matches_master(struct auth_master* master, struct sockaddr_storage* addr,
 /** check access list for notifies */
 static int
 az_xfr_allowed_notify(struct auth_xfer* xfr, struct sockaddr_storage* addr,
-       socklen_t addrlen)
+       socklen_t addrlen, struct auth_master** fromhost)
 {
        struct auth_master* p;
        for(p=xfr->allow_notify_list; p; p=p->next) {
-               if(addr_matches_master(p, addr, addrlen)) {
+               if(addr_matches_master(p, addr, addrlen, fromhost)) {
                        return 1;
                }
        }
        return 0;
 }
 
-/** process a notify serial, start new probe or note serial. xfr is locked */
+/** see if the serial means the zone has to be updated, i.e. the serial
+ * is newer than the zone serial, or we have no zone */
 static int
-xfr_process_notify(struct auth_xfer* xfr, int has_serial, uint32_t serial)
+xfr_serial_means_update(struct auth_xfer* xfr, uint32_t serial)
 {
+       if(!xfr->have_zone)
+               return 1; /* no zone, anything is better */
+       if(xfr->zone_expired)
+               return 1; /* expired, the sent serial is better than expired
+                       data */
+       if(compare_serial(xfr->serial, serial) < 0)
+               return 1; /* our serial is smaller than the sent serial,
+                       the data is newer, fetch it */
+       return 0;
+}
+
+/** process a notify serial, start new probe or note serial. xfr is locked */
+static void
+xfr_process_notify(struct auth_xfer* xfr, struct module_env* env,
+       int has_serial, uint32_t serial, struct auth_master* fromhost)
+{
+       /* if the serial of notify is older than we have, don't fetch
+        * a zone, we already have it */
+       if(has_serial && !xfr_serial_means_update(xfr, serial))
+               return;
        /* start new probe with this addr src, or note serial */
-       /* TODO */
-       (void)xfr;
-       (void)has_serial;
-       (void)serial;
-       return 1;
+       if(!xfr_start_probe(xfr, env, fromhost)) {
+               /* not started because already in progress, note the serial */
+               if(xfr->notify_received && xfr->notify_has_serial &&
+                       has_serial) {
+                       /* see if this serial is newer */
+                       if(compare_serial(xfr->notify_serial, serial) < 0)
+                               xfr->notify_serial = serial;
+               } else if(xfr->notify_received && xfr->notify_has_serial &&
+                       !has_serial) {
+                       /* remove serial, we have notify without serial */
+                       xfr->notify_has_serial = 0;
+                       xfr->notify_serial = 0;
+               } else if(xfr->notify_received && !xfr->notify_has_serial) {
+                       /* we already have notify without serial, keep it
+                        * that way; no serial check when current operation
+                        * is done */
+               } else {
+                       xfr->notify_received = 1;
+                       xfr->notify_has_serial = has_serial;
+                       xfr->notify_serial = serial;
+               }
+       }
 }
 
-int auth_zones_notify(struct auth_zones* az, uint8_t* nm, size_t nmlen,
-       uint16_t dclass, struct sockaddr_storage* addr, socklen_t addrlen,
-       int has_serial, uint32_t serial, int* refused)
+int auth_zones_notify(struct auth_zones* az, struct module_env* env,
+       uint8_t* nm, size_t nmlen, uint16_t dclass,
+       struct sockaddr_storage* addr, socklen_t addrlen, int has_serial,
+       uint32_t serial, int* refused)
 {
        struct auth_xfer* xfr;
+       struct auth_master* fromhost = NULL;
        /* see which zone this is */
        lock_rw_rdlock(&az->lock);
        xfr = auth_xfer_find(az, nm, nmlen, dclass);
@@ -3311,7 +3366,7 @@ int auth_zones_notify(struct auth_zones* az, uint8_t* nm, size_t nmlen,
        lock_rw_unlock(&az->lock);
        
        /* check access list for notifies */
-       if(!az_xfr_allowed_notify(xfr, addr, addrlen)) {
+       if(!az_xfr_allowed_notify(xfr, addr, addrlen, &fromhost)) {
                lock_basic_unlock(&xfr->lock);
                /* notify not allowed, refuse the notify */
                *refused = 1;
@@ -3319,13 +3374,7 @@ int auth_zones_notify(struct auth_zones* az, uint8_t* nm, size_t nmlen,
        }
 
        /* process the notify */
-       if(!xfr_process_notify(xfr, has_serial, serial)) {
-               lock_basic_unlock(&xfr->lock);
-               /* servfail */
-               *refused = 0;
-               return 0;
-       }
-
+       xfr_process_notify(xfr, env, has_serial, serial, fromhost);
        lock_basic_unlock(&xfr->lock);
        return 1;
 }
@@ -3828,22 +3877,6 @@ check_packet_ok(sldns_buffer* pkt, uint16_t qtype, struct auth_xfer* xfr,
        return 1;
 }
 
-/** see if the serial means the zone has to be updated, i.e. the serial
- * is newer than the zone serial, or we have no zone */
-static int
-xfr_serial_means_update(struct auth_xfer* xfr, uint32_t serial)
-{
-       if(!xfr->have_zone)
-               return 1; /* no zone, anything is better */
-       if(xfr->zone_expired)
-               return 1; /* expired, the sent serial is better than expired
-                       data */
-       if(compare_serial(xfr->serial, serial) < 0)
-               return 1; /* our serial is smaller than the sent serial,
-                       the data is newer, fetch it */
-       return 0;
-}
-
 /** read one line from chunks into buffer at current position */
 static int
 chunkline_get_line(struct auth_chunk** chunk, size_t* chunk_pos,
@@ -5362,8 +5395,22 @@ process_list_end_transfer(struct auth_xfer* xfr, struct module_env* env)
                /* we fetched the zone, move to wait task */
                xfr_transfer_disown(xfr);
 
-               /* pick up the nextprobe task and wait (normail wait time) */
-               xfr_set_timeout(xfr, env, 0);
+               if(xfr->notify_received && (!xfr->notify_has_serial ||
+                       (xfr->notify_has_serial && 
+                       xfr_serial_means_update(xfr, xfr->notify_serial)))) {
+                       /* we received a notify while probe/transfer was
+                        * in progress.  start a new probe and transfer */
+                       if(xfr_start_probe(xfr, env, NULL)) {
+                               /* probe started successfully, we can remove
+                                * the stored notify */
+                               xfr->notify_received = 0;
+                               xfr->notify_has_serial = 0;
+                               xfr->notify_serial = 0;
+                       }
+               } else {
+                       /* pick up the nextprobe task and wait (normail wait time) */
+                       xfr_set_timeout(xfr, env, 0);
+               }
                lock_basic_unlock(&xfr->lock);
                return;
        }
@@ -5925,6 +5972,19 @@ auth_xfer_timer(void* arg)
 
        xfr_nextprobe_disown(xfr);
 
+       if(!xfr_start_probe(xfr, env, NULL)) {
+               /* not started because already in progress */
+               lock_basic_unlock(&xfr->lock);
+       }
+}
+
+/** start task_probe if possible, if no masters for probe start task_transfer
+ * returns true if task has been started, and false if the task is already
+ * in progress. */
+static int
+xfr_start_probe(struct auth_xfer* xfr, struct module_env* env,
+       struct auth_master* spec)
+{
        /* see if we need to start a probe (or maybe it is already in
         * progress (due to notify)) */
        if(xfr->task_probe->worker == NULL) {
@@ -5932,12 +5992,11 @@ auth_xfer_timer(void* arg)
                        /* useless to pick up task_probe, no masters to
                         * probe. Instead attempt to pick up task transfer */
                        if(xfr->task_transfer->worker == NULL) {
-                               xfr_start_transfer(xfr, env, NULL);
-                       } else {
-                               /* task transfer already in progress */
-                               lock_basic_unlock(&xfr->lock);
+                               xfr_start_transfer(xfr, env, spec);
+                               return 1;
                        }
-                       return;
+                       /* task transfer already in progress */
+                       return 0;
                }
 
                /* pick up the probe task ourselves */
@@ -5946,15 +6005,17 @@ auth_xfer_timer(void* arg)
                xfr->task_probe->cp = NULL;
 
                /* start the task */
-               /* this was a timeout, so no specific first master to scan */
-               xfr_probe_start_list(xfr, NULL);
+               /* if this was a timeout, no specific first master to scan */
+               /* otherwise, spec is nonNULL the notified master, scan
+                * first and also transfer first from it */
+               xfr_probe_start_list(xfr, spec);
                /* setup to start the lookup of hostnames of masters afresh */
                xfr_probe_start_lookups(xfr);
                /* send the probe packet or next send, or end task */
                xfr_probe_send_or_end(xfr, env);
-       } else {
-               lock_basic_unlock(&xfr->lock);
+               return 1;
        }
+       return 0;
 }
 
 /** for task_nextprobe.
index 30ef204484c21498cf6c8142ae60682c6c1a8da7..b93a80fdead85a4847099ac671736349931cd56b 100644 (file)
@@ -218,6 +218,8 @@ struct auth_xfer {
         * Hold the lock to access this member (and the serial).
         */
        int notify_received;
+       /** true if the notify_received has a serial number */
+       int notify_has_serial;
        /** serial number of the notify */
        uint32_t notify_serial;
        /** the list of masters for checking notifies.  This list is
@@ -553,6 +555,9 @@ int auth_zones_can_fallback(struct auth_zones* az, uint8_t* nm, size_t nmlen,
  * first checks the access list.  Then processes the notify. This starts
  * the probe sequence or it notes the serial number (if any)
  * @param az: auth zones structure.
+ * @param env: module env of the worker that is handling the notify. it will
+ *     pick up the task probe (or transfer), unless already in progress by
+ *     another worker.
  * @param nm: name of the zone.  Uncompressed. from query.
  * @param nmlen: length of name.
  * @param dclass: class of zone.
@@ -564,9 +569,10 @@ int auth_zones_can_fallback(struct auth_zones* az, uint8_t* nm, size_t nmlen,
  * @return fail on failures (refused is false) and when access is
  *     denied (refused is true).  True when processed.
  */
-int auth_zones_notify(struct auth_zones* az, uint8_t* nm, size_t nmlen,
-       uint16_t dclass, struct sockaddr_storage* addr, socklen_t addrlen,
-       int has_serial, uint32_t serial, int* refused);
+int auth_zones_notify(struct auth_zones* az, struct module_env* env,
+       uint8_t* nm, size_t nmlen, uint16_t dclass,
+       struct sockaddr_storage* addr, socklen_t addrlen, int has_serial,
+       uint32_t serial, int* refused);
 
 /** process notify packet and read serial number from SOA.
  * returns 0 if no soa record in the notify */