diff --git a/dname.c b/dname.c index f1b3d95ad..52297a2c9 100644 --- a/dname.c +++ b/dname.c @@ -717,3 +717,41 @@ dname_make_from_packet_buffered(struct dname_buffer* dname, assert(wirelen == dname->dname.name_size); return wirelen; } + +dname_type* +label_plus_dname(const char* label, const dname_type* dname) +{ + static struct { + dname_type dname; + uint8_t bytes[MAXDOMAINLEN + 128 /* max number of labels */]; + } ATTR_PACKED name; + size_t i, ll; + + if (!label || !dname || dname->label_count > 127) + return NULL; + ll = strlen(label); + if ((int)dname->name_size + ll + 1 > MAXDOMAINLEN) + return NULL; + + /* In reversed order and first copy with memmove, so we can nest. + * i.e. label_plus_dname(label1, label_plus_dname(label2, dname)) + */ + memmove(name.bytes + dname->label_count + + 1 /* label_count increases by one */ + + 1 /* label type/length byte for label */ + ll, + ((char*)dname) + sizeof(dname_type) + dname->label_count, + dname->name_size); + memcpy(name.bytes + dname->label_count + + 1 /* label_count increases by one */ + + 1 /* label type/length byte for label */, label, ll); + name.bytes[dname->label_count + 1] = ll; /* label type/length byte */ + name.bytes[dname->label_count] = 0; /* first label follows last + * label_offsets element */ + for (i = 0; i < dname->label_count; i++) + name.bytes[i] = ((uint8_t*)(void*)dname)[sizeof(dname_type)+i] + + 1 /* label type/length byte for label */ + ll; + name.dname.label_count = dname->label_count + 1 /* label_count incr. */; + name.dname.name_size = dname->name_size + ll + + 1 /* label length */; + return &name.dname; +} diff --git a/dname.h b/dname.h index 82347064f..f3127519f 100644 --- a/dname.h +++ b/dname.h @@ -448,4 +448,7 @@ int dname_make_buffered(struct dname_buffer* dname, uint8_t *name, int dname_make_from_packet_buffered(struct dname_buffer* dname, buffer_type *packet, int allow_pointers, int normalize); +/** return (static) dname with label prepended to dname */ +dname_type* label_plus_dname(const char* label,const dname_type* dname); + #endif /* DNAME_H */ diff --git a/edns.c b/edns.c index d77a146ef..6ca005798 100644 --- a/edns.c +++ b/edns.c @@ -71,6 +71,8 @@ edns_init_record(edns_record_type *edns) edns->dnssec_ok = 0; edns->nsid = 0; edns->zoneversion = 0; + edns->backend_version_rr = NULL; + edns->backend_version_len = 0; edns->padding = 0; edns->cookie_status = COOKIE_NOT_PRESENT; edns->cookie_len = 0; diff --git a/edns.h b/edns.h index 66f30fae2..7fba8ba2a 100644 --- a/edns.h +++ b/edns.h @@ -13,6 +13,7 @@ #include "buffer.h" struct nsd; struct query; +struct rr; #define OPT_LEN 9U /* Length of the NSD EDNS response record minus 2 */ #define OPT_RDATA 2 /* holds the rdata length comes after OPT_LEN */ @@ -25,7 +26,8 @@ struct query; #define DNSSEC_OK_MASK 0x8000U /* do bit mask */ /* https://iana.org/assignments/dns-parameters/#zoneversion-type-values */ -#define ZONEVERSION_SOA_SERIAL 0 +#define ZONEVERSION_SOA_SERIAL 0 +#define ZONEVERSION_BACKEND_SERIAL 247 /* BACKEND-SERIAL ZONEVERSION type */ /* See RFC 8467 */ #define PADDING_BLOCK_SZ 468 @@ -68,6 +70,8 @@ struct edns_record int dnssec_ok; int nsid; int zoneversion; + struct rr* backend_version_rr; + size_t backend_version_len; int padding; cookie_status_type cookie_status; size_t cookie_len; diff --git a/query.c b/query.c index f94a503eb..f77fec4fc 100644 --- a/query.c +++ b/query.c @@ -1813,6 +1813,8 @@ void query_add_optional(query_type *q, nsd_type *nsd, uint32_t *now_p) { struct edns_data *edns = &nsd->edns_ipv4; + domain_type *match, *closest_encloser; + rrset_type *rrset; #if defined(INET6) if (q->client_addr.ss_family == AF_INET6) { edns = &nsd->edns_ipv6; @@ -1836,11 +1838,42 @@ query_add_optional(query_type *q, nsd_type *nsd, uint32_t *now_p) 6 + ( q->edns.ede_text_len ? q->edns.ede_text_len : 0); - if(q->edns.zoneversion - && q->zone - && q->zone->soa_rrset - && q->zone->soa_rrset->rr_count >= 1 - && q->zone->soa_rrset->rrs[0]->rdlength >= 20 /* 5x 32bit numbers */ +2*sizeof(void*) /* two pointers to domain names */) + if(!q->edns.zoneversion || !q->zone) + ; /* pass */ + else if(namedb_lookup( nsd->db + , label_plus_dname("_backend-version" + , domain_dname_const(q->zone->apex)) + , &match, &closest_encloser) + && (rrset = domain_find_rrset(match, q->zone, TYPE_TXT)) + && rrset->rr_count == 1) { + uint8_t *rdp = rrset->rrs[0]->rdata; + uint8_t *eo_rd = rrset->rrs[0]->rdata + + rrset->rrs[0]->rdlength; + while (rdp < eo_rd) { + q->edns.backend_version_len += *rdp; + rdp += *rdp + 1; + } + if(rdp == eo_rd) { + q->edns.backend_version_rr = rrset->rrs[0]; + q->edns.opt_reserved_space += sizeof(uint16_t) + + sizeof(uint16_t) + + sizeof(uint8_t) + + sizeof(uint8_t) + + q->edns.backend_version_len; + + } else if(q->zone->soa_rrset + && q->zone->soa_rrset->rr_count >= 1 + && q->zone->soa_rrset->rrs[0]->rdlength >= 20+2*sizeof(void*)) { + q->edns.opt_reserved_space += sizeof(uint16_t) + + sizeof(uint16_t) + + sizeof(uint8_t) + + sizeof(uint8_t) + + sizeof(uint32_t); + } + } + else if(q->zone->soa_rrset + && q->zone->soa_rrset->rr_count >= 1 + && q->zone->soa_rrset->rrs[0]->rdlength >= 20 /* 5x 32bit numbers */ +2*sizeof(void*) /* two pointers to domain names */) q->edns.opt_reserved_space += sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint8_t) @@ -1875,11 +1908,32 @@ query_add_optional(query_type *q, nsd_type *nsd, uint32_t *now_p) /* nsid payload */ buffer_write(q->packet, nsd->nsid, nsd->nsid_len); } - if(q->edns.zoneversion - && q->zone - && q->zone->soa_rrset - && q->zone->soa_rrset->rr_count >= 1 - && q->zone->soa_rrset->rrs[0]->rdlength >= 20+2*sizeof(void*) /* 5x4 bytes and 2 pointers to domains */ ) { + if(!q->edns.zoneversion || !q->zone) + ; /* pass */ + else if(q->edns.backend_version_rr) { + uint8_t *rdp = q->edns.backend_version_rr->rdata; + uint8_t *eo_rd = q->edns.backend_version_rr->rdata + + q->edns.backend_version_rr->rdlength; + + buffer_write_u16(q->packet, ZONEVERSION_CODE); + buffer_write_u16( q->packet + , sizeof(uint8_t) + + sizeof(uint8_t) + + q->edns.backend_version_len); + buffer_write_u8(q->packet, + domain_dname(q->zone->apex)->label_count - 1); + buffer_write_u8( q->packet + , ZONEVERSION_BACKEND_SERIAL); + while (rdp < eo_rd) { + buffer_write(q->packet, rdp+1, *rdp); + q->edns.backend_version_len += *rdp; + rdp += *rdp + 1; + } + assert(rdp == eo_rd); + + } else if(q->zone->soa_rrset + && q->zone->soa_rrset->rr_count >= 1 + && q->zone->soa_rrset->rrs[0]->rdlength >= 20+2*sizeof(void*) /* 5x4 bytes and 2 pointers to domains */ ) { uint32_t serial = 0; buffer_write_u16(q->packet, ZONEVERSION_CODE); buffer_write_u16( q->packet diff --git a/xfrd-catalog-zones.c b/xfrd-catalog-zones.c index a797ad181..fe3a337aa 100644 --- a/xfrd-catalog-zones.c +++ b/xfrd-catalog-zones.c @@ -27,9 +27,6 @@ static void vmake_catalog_consumer_invalid( struct xfrd_catalog_consumer_zone *consumer_zone, const char *format, va_list args); -/** return (static) dname with label prepended to dname */ -static dname_type* label_plus_dname(const char* label,const dname_type* dname); - /** delete the catalog member zone */ static void catalog_del_consumer_member_zone( struct xfrd_catalog_consumer_zone* consumer_zone, @@ -304,44 +301,6 @@ make_catalog_consumer_valid(struct xfrd_catalog_consumer_zone *consumer_zone) } } -static dname_type* -label_plus_dname(const char* label, const dname_type* dname) -{ - static struct { - dname_type dname; - uint8_t bytes[MAXDOMAINLEN + 128 /* max number of labels */]; - } ATTR_PACKED name; - size_t i, ll; - - if (!label || !dname || dname->label_count > 127) - return NULL; - ll = strlen(label); - if ((int)dname->name_size + ll + 1 > MAXDOMAINLEN) - return NULL; - - /* In reversed order and first copy with memmove, so we can nest. - * i.e. label_plus_dname(label1, label_plus_dname(label2, dname)) - */ - memmove(name.bytes + dname->label_count - + 1 /* label_count increases by one */ - + 1 /* label type/length byte for label */ + ll, - ((char*)dname) + sizeof(dname_type) + dname->label_count, - dname->name_size); - memcpy(name.bytes + dname->label_count - + 1 /* label_count increases by one */ - + 1 /* label type/length byte for label */, label, ll); - name.bytes[dname->label_count + 1] = ll; /* label type/length byte */ - name.bytes[dname->label_count] = 0; /* first label follows last - * label_offsets element */ - for (i = 0; i < dname->label_count; i++) - name.bytes[i] = ((uint8_t*)(void*)dname)[sizeof(dname_type)+i] - + 1 /* label type/length byte for label */ + ll; - name.dname.label_count = dname->label_count + 1 /* label_count incr. */; - name.dname.name_size = dname->name_size + ll - + 1 /* label length */; - return &name.dname; -} - static void catalog_del_consumer_member_zone( struct xfrd_catalog_consumer_zone* consumer_zone,