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

daemon/worker.c
doc/Changelog
services/authzone.c
services/authzone.h
util/data/msgparse.c
util/data/msgreply.c

index 389a1de530eccf925344702a117d854b6cc82499..4cbbb170b156200a3f2a5e56161ef8c553954570 100644 (file)
@@ -342,7 +342,8 @@ worker_check_request(sldns_buffer* pkt, struct worker* worker)
                verbose(VERB_QUERY, "request bad, has TC bit on");
                return worker_err_ratelimit(worker, LDNS_RCODE_FORMERR);
        }
-       if(LDNS_OPCODE_WIRE(sldns_buffer_begin(pkt)) != LDNS_PACKET_QUERY) {
+       if(LDNS_OPCODE_WIRE(sldns_buffer_begin(pkt)) != LDNS_PACKET_QUERY &&
+               LDNS_OPCODE_WIRE(sldns_buffer_begin(pkt)) != LDNS_PACKET_NOTIFY) {
                verbose(VERB_QUERY, "request unknown opcode %d", 
                        LDNS_OPCODE_WIRE(sldns_buffer_begin(pkt)));
                return worker_err_ratelimit(worker, LDNS_RCODE_NOTIMPL);
@@ -352,7 +353,9 @@ worker_check_request(sldns_buffer* pkt, struct worker* worker)
                        LDNS_QDCOUNT(sldns_buffer_begin(pkt)));
                return worker_err_ratelimit(worker, LDNS_RCODE_FORMERR);
        }
-       if(LDNS_ANCOUNT(sldns_buffer_begin(pkt)) != 0) {
+       if(LDNS_ANCOUNT(sldns_buffer_begin(pkt)) != 0 && 
+               (LDNS_ANCOUNT(sldns_buffer_begin(pkt)) != 1 ||
+               LDNS_OPCODE_WIRE(sldns_buffer_begin(pkt)) != LDNS_PACKET_NOTIFY)) {
                verbose(VERB_QUERY, "request wrong nr an=%d", 
                        LDNS_ANCOUNT(sldns_buffer_begin(pkt)));
                return worker_err_ratelimit(worker, LDNS_RCODE_FORMERR);
@@ -940,6 +943,64 @@ answer_chaos(struct worker* w, struct query_info* qinfo,
        return 0;
 }
 
+/**
+ * Answer notify queries.  These are notifies for authoritative zones,
+ * the reply is an ack that the notify has been received.  We need to check
+ * access permission here.
+ * @param w: worker
+ * @param qinfo: query info. Pointer into packet buffer.
+ * @param edns: edns info from query.
+ * @param pkt: packet buffer.
+ */
+static void
+answer_notify(struct worker* w, struct query_info* qinfo, 
+       struct edns_data* edns, sldns_buffer* pkt, struct comm_reply* repinfo)
+{
+       int refused = 0;
+       int rcode = LDNS_RCODE_NOERROR;
+       uint32_t serial = 0;
+       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,
+               qinfo->qname_len, qinfo->qclass, &repinfo->addr,
+               repinfo->addrlen, has_serial, serial, &refused)) {
+               rcode = LDNS_RCODE_NOERROR;
+       } else {
+               if(refused)
+                       rcode = LDNS_RCODE_REFUSED;
+               else    rcode = LDNS_RCODE_SERVFAIL;
+       }
+
+       if(verbosity >= VERB_DETAIL) {
+               char buf[380];
+               char zname[255+1];
+               char sr[25];
+               dname_str(qinfo->qname, zname);
+               sr[0]=0;
+               if(has_serial)
+                       snprintf(sr, sizeof(sr), "serial %u ",
+                               (unsigned)serial);
+               if(rcode == LDNS_RCODE_REFUSED)
+                       snprintf(buf, sizeof(buf),
+                               "refused NOTIFY %sfor %s from", sr, zname);
+               else if(rcode == LDNS_RCODE_SERVFAIL)
+                       snprintf(buf, sizeof(buf),
+                               "servfail for NOTIFY %sfor %s from", sr, zname);
+               else    snprintf(buf, sizeof(buf),
+                               "received NOTIFY %sfor %s from", sr, zname);
+               log_addr(VERB_DETAIL, buf, &repinfo->addr, repinfo->addrlen);
+       }
+       edns->edns_version = EDNS_ADVERTISED_VERSION;
+       edns->udp_size = EDNS_ADVERTISED_SIZE;
+       edns->ext_rcode = 0;
+       edns->bits &= EDNS_DO;
+       edns->opt_list = NULL;
+       error_encode(pkt, rcode, qinfo,
+               *(uint16_t*)(void *)sldns_buffer_begin(pkt),
+               sldns_buffer_read_u16_at(pkt, 2), edns);
+}
+
 static int
 deny_refuse(struct comm_point* c, enum acl_access acl,
        enum acl_access deny, enum acl_access refuse,
@@ -1238,6 +1299,12 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
                regional_free_all(worker->scratchpad);
                goto send_reply;
        }
+       if(LDNS_OPCODE_WIRE(sldns_buffer_begin(c->buffer)) ==
+               LDNS_PACKET_NOTIFY) {
+               answer_notify(worker, &qinfo, &edns, c->buffer, repinfo);
+               regional_free_all(worker->scratchpad);
+               goto send_reply;
+       }
        if(local_zones_answer(worker->daemon->local_zones, &worker->env, &qinfo,
                &edns, c->buffer, worker->scratchpad, repinfo, acladdr->taglist,
                acladdr->taglen, acladdr->tag_actions,
index f1d10ce62423c368b81cff4909d70132b0e37806..a3f6cd2a47345ca2d06d0983cea17ffcdc4f26fb 100644 (file)
@@ -4,6 +4,7 @@
 
 10 April 2018: Wouter
        - documentation for low-rtt and low-rtt-pct.
+       - auth zone notify work.
 
 9 April 2018: Wouter
        - Fix that flush_zone sets prefetch ttl expired, so that with
index 224f96a5bb14dbeb21441aef5ba6da9f32696cef..16e6439f425488951aea9cb64df09a819eee9abc 100644 (file)
@@ -3189,6 +3189,94 @@ int auth_zones_can_fallback(struct auth_zones* az, uint8_t* nm, size_t nmlen,
        return r;
 }
 
+int
+auth_zone_parse_notify_serial(sldns_buffer* pkt, uint32_t *serial)
+{
+       struct query_info q;
+       uint16_t rdlen;
+       memset(&q, 0, sizeof(q));
+       sldns_buffer_set_position(pkt, 0);
+       if(!query_info_parse(&q, pkt)) return 0;
+       if(LDNS_ANCOUNT(sldns_buffer_begin(pkt)) == 0) return 0;
+       /* skip name of RR in answer section */
+       if(sldns_buffer_remaining(pkt) < 1) return 0;
+       if(pkt_dname_len(pkt) == 0) return 0;
+       /* check type */
+       if(sldns_buffer_remaining(pkt) < 10 /* type,class,ttl,rdatalen*/)
+               return 0;
+       if(sldns_buffer_read_u16(pkt) != LDNS_RR_TYPE_SOA) return 0;
+       sldns_buffer_skip(pkt, 2); /* class */
+       sldns_buffer_skip(pkt, 4); /* ttl */
+       rdlen = sldns_buffer_read_u16(pkt); /* rdatalen */
+       if(sldns_buffer_remaining(pkt) < rdlen) return 0;
+       if(rdlen < 22) return 0; /* bad soa length */
+       sldns_buffer_skip(pkt, rdlen-20);
+       *serial = sldns_buffer_read_u32(pkt);
+       /* return true when has serial in answer section */
+       return 1;
+}
+
+/** check access list for notifies */
+static int
+az_xfr_allowed_notify(struct auth_xfer* xfr, struct sockaddr_storage* addr,
+       socklen_t addrlen)
+{
+       /* TODO */
+       (void)xfr;
+       (void)addr;
+       (void)addrlen;
+       return 0;
+}
+
+/** process a notify serial, start new probe or note serial. xfr is locked */
+static int
+xfr_process_notify(struct auth_xfer* xfr, int has_serial, uint32_t serial)
+{
+       /* start new probe with this addr src, or note serial */
+       /* TODO */
+       (void)xfr;
+       (void)has_serial;
+       (void)serial;
+       return 1;
+}
+
+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)
+{
+       struct auth_xfer* xfr;
+       /* see which zone this is */
+       lock_rw_rdlock(&az->lock);
+       xfr = auth_xfer_find(az, nm, nmlen, dclass);
+       if(!xfr) {
+               lock_rw_unlock(&az->lock);
+               /* no such zone, refuse the notify */
+               *refused = 1;
+               return 0;
+       }
+       lock_basic_lock(&xfr->lock);
+       lock_rw_unlock(&az->lock);
+       
+       /* check access list for notifies */
+       if(!az_xfr_allowed_notify(xfr, addr, addrlen)) {
+               lock_basic_unlock(&xfr->lock);
+               /* notify not allowed, refuse the notify */
+               *refused = 1;
+               return 0;
+       }
+
+       /* process the notify */
+       if(!xfr_process_notify(xfr, has_serial, serial)) {
+               lock_basic_unlock(&xfr->lock);
+               /* servfail */
+               *refused = 0;
+               return 0;
+       }
+
+       lock_basic_unlock(&xfr->lock);
+       return 1;
+}
+
 /** set a zone expired */
 static void
 auth_xfer_set_expired(struct auth_xfer* xfr, struct module_env* env,
index 258a1ebc370ace1d9c45f5c59f0674877ab1c682..0b45edb29e9770af63b953091854b42a966ab83c 100644 (file)
@@ -545,6 +545,29 @@ int auth_zone_set_fallback(struct auth_zone* z, char* fallbackstr);
 int auth_zones_can_fallback(struct auth_zones* az, uint8_t* nm, size_t nmlen,
        uint16_t dclass);
 
+/** process notify for auth zones.
+ * 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 nm: name of the zone.  Uncompressed. from query.
+ * @param nmlen: length of name.
+ * @param dclass: class of zone.
+ * @param addr: source address of notify
+ * @param addrlen: length of addr.
+ * @param has_serial: if true, the notify has a serial attached.
+ * @param serial: the serial number, if has_serial is true.
+ * @param refused: is set to true on failure to note refused access.
+ * @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);
+
+/** process notify packet and read serial number from SOA.
+ * returns 0 if no soa record in the notify */
+int auth_zone_parse_notify_serial(struct sldns_buffer* pkt, uint32_t *serial);
+
 /** read auth zone from zonefile. caller must lock zone. false on failure */
 int auth_zone_read_zonefile(struct auth_zone* z);
 
index 288720068b106351ca725d11d7ce48e405032da6..13cad8a266304f2b41c427b27c96ed6af5c21801 100644 (file)
@@ -1028,6 +1028,32 @@ parse_extract_edns(struct msg_parse* msg, struct edns_data* edns,
        return 0;
 }
 
+/** skip RR in packet */
+static int
+skip_pkt_rr(sldns_buffer* pkt)
+{
+       if(sldns_buffer_remaining(pkt) < 1) return 0;
+       if(!pkt_dname_len(pkt))
+               return 0;
+       if(sldns_buffer_remaining(pkt) < 4) return 0;
+       sldns_buffer_skip(pkt, 4); /* type and class */
+       if(!skip_ttl_rdata(pkt))
+               return 0;
+       return 1;
+}
+
+/** skip RRs from packet */
+static int
+skip_pkt_rrs(sldns_buffer* pkt, int num)
+{
+       int i;
+       for(i=0; i<num; i++) {
+               if(!skip_pkt_rr(pkt))
+                       return 0;
+       }
+       return 1;
+}
+
 int 
 parse_edns_from_pkt(sldns_buffer* pkt, struct edns_data* edns,
        struct regional* region)
@@ -1035,8 +1061,12 @@ parse_edns_from_pkt(sldns_buffer* pkt, struct edns_data* edns,
        size_t rdata_len;
        uint8_t* rdata_ptr;
        log_assert(LDNS_QDCOUNT(sldns_buffer_begin(pkt)) == 1);
-       log_assert(LDNS_ANCOUNT(sldns_buffer_begin(pkt)) == 0);
-       log_assert(LDNS_NSCOUNT(sldns_buffer_begin(pkt)) == 0);
+       if(LDNS_ANCOUNT(sldns_buffer_begin(pkt)) != 0 ||
+               LDNS_NSCOUNT(sldns_buffer_begin(pkt)) != 0) {
+               if(!skip_pkt_rrs(pkt, ((int)LDNS_ANCOUNT(sldns_buffer_begin(pkt)))+
+                       ((int)LDNS_NSCOUNT(sldns_buffer_begin(pkt)))))
+                       return 0;
+       }
        /* check edns section is present */
        if(LDNS_ARCOUNT(sldns_buffer_begin(pkt)) > 1) {
                return LDNS_RCODE_FORMERR;
index e25b42cc257ab90459a50536a09c5361d18c189d..772f5d1f10290f6c1f833fb9db67b95b9f63d1d1 100644 (file)
@@ -534,8 +534,9 @@ query_info_parse(struct query_info* m, sldns_buffer* query)
        /* minimum size: header + \0 + qtype + qclass */
        if(sldns_buffer_limit(query) < LDNS_HEADER_SIZE + 5)
                return 0;
-       if(LDNS_OPCODE_WIRE(q) != LDNS_PACKET_QUERY || 
-               LDNS_QDCOUNT(q) != 1 || sldns_buffer_position(query) != 0)
+       if((LDNS_OPCODE_WIRE(q) != LDNS_PACKET_QUERY && LDNS_OPCODE_WIRE(q) !=
+               LDNS_PACKET_NOTIFY) || LDNS_QDCOUNT(q) != 1 ||
+               sldns_buffer_position(query) != 0)
                return 0;
        sldns_buffer_skip(query, LDNS_HEADER_SIZE);
        m->qname = sldns_buffer_current(query);