From: Wouter Wijngaards Date: Tue, 10 Apr 2018 14:57:38 +0000 (+0000) Subject: - auth zone notify work. X-Git-Tag: release-1.7.1rc1~45 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=ad9784c5e8f70f2975f9b1eace3d09eee300daa7;p=thirdparty%2Funbound.git - auth zone notify work. git-svn-id: file:///svn/unbound/trunk@4619 be551aaa-1e26-0410-a405-d3ace91eadb9 --- diff --git a/daemon/worker.c b/daemon/worker.c index 389a1de53..4cbbb170b 100644 --- a/daemon/worker.c +++ b/daemon/worker.c @@ -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, diff --git a/doc/Changelog b/doc/Changelog index f1d10ce62..a3f6cd2a4 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -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 diff --git a/services/authzone.c b/services/authzone.c index 224f96a5b..16e6439f4 100644 --- a/services/authzone.c +++ b/services/authzone.c @@ -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, diff --git a/services/authzone.h b/services/authzone.h index 258a1ebc3..0b45edb29 100644 --- a/services/authzone.h +++ b/services/authzone.h @@ -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); diff --git a/util/data/msgparse.c b/util/data/msgparse.c index 288720068..13cad8a26 100644 --- a/util/data/msgparse.c +++ b/util/data/msgparse.c @@ -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 1) { return LDNS_RCODE_FORMERR; diff --git a/util/data/msgreply.c b/util/data/msgreply.c index e25b42cc2..772f5d1f1 100644 --- a/util/data/msgreply.c +++ b/util/data/msgreply.c @@ -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);