diff --git a/daemon/remote.c b/daemon/remote.c index 00e7dd21d..2bcc4012f 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -1339,7 +1339,7 @@ perform_zone_add(RES* ssl, struct local_zones* zones, char* arg) return 1; } if(!local_zones_add_zone(zones, nm, nmlen, - nmlabs, LDNS_RR_CLASS_IN, t)) { + nmlabs, LDNS_RR_CLASS_IN, t, NULL)) { lock_rw_unlock(&zones->lock); ssl_printf(ssl, "error out of memory\n"); return 0; diff --git a/libunbound/libunbound.c b/libunbound/libunbound.c index 9c6a3e309..673ad6ec8 100644 --- a/libunbound/libunbound.c +++ b/libunbound/libunbound.c @@ -1385,7 +1385,7 @@ int ub_ctx_zone_add(struct ub_ctx* ctx, const char *zone_name, return UB_NOERROR; } if(!local_zones_add_zone(ctx->local_zones, nm, nmlen, nmlabs, - LDNS_RR_CLASS_IN, t)) { + LDNS_RR_CLASS_IN, t, NULL)) { lock_rw_unlock(&ctx->local_zones->lock); return UB_NOMEM; } diff --git a/respip/respip.c b/respip/respip.c index f39ce5805..83603bb42 100644 --- a/respip/respip.c +++ b/respip/respip.c @@ -102,7 +102,7 @@ respip_set_get_tree(struct respip_set* set) struct resp_addr* respip_sockaddr_find_or_create(struct respip_set* set, struct sockaddr_storage* addr, - socklen_t addrlen, int net, int create, const char* ipstr) + socklen_t addrlen, int net, int create) { struct resp_addr* node; log_assert(set); @@ -119,7 +119,10 @@ respip_sockaddr_find_or_create(struct respip_set* set, struct sockaddr_storage* addrlen, net)) { /* We know we didn't find it, so this should be * impossible. */ - log_warn("unexpected: duplicate address: %s", ipstr); + char a[128]; + addr_to_str(addr, addrlen, a, sizeof(a)); + log_warn("unexpected: duplicate address: %s/%d", + a, net); } } return node; @@ -154,8 +157,7 @@ respip_find_or_create(struct respip_set* set, const char* ipstr, int create) log_err("cannot parse netblock: '%s'", ipstr); return NULL; } - return respip_sockaddr_find_or_create(set, &addr, addrlen, net, create, - ipstr); + return respip_sockaddr_find_or_create(set, &addr, addrlen, net, create); } static int @@ -291,6 +293,49 @@ respip_enter_rr(struct regional* region, struct resp_addr* raddr, return rrset_insert_rr(region, pd, rdata, rdata_len, ttl, rrstr); } +int +respip_enter_rr_wol(struct regional* region, struct resp_addr* raddr, + uint16_t rrtype, uint16_t rrclass, time_t ttl, uint8_t* rdata_wol, + size_t rdata_len, const char* netblockstr) +{ + struct packed_rrset_data* pd; + struct sockaddr* sa; + sa = (struct sockaddr*)&raddr->node.addr; + if (rrtype == LDNS_RR_TYPE_CNAME && raddr->data) { + char* rrstr = dname_rdata_to_str(NULL, 0, rrtype, + rrclass, ttl, rdata_wol, rdata_len); + log_err("CNAME response-ip data (%s) can not co-exist with other " + "response-ip data for netblock %s", (rrstr?rrstr:""), netblockstr); + free(rrstr); + return 0; + } else if (raddr->data && + raddr->data->rk.type == htons(LDNS_RR_TYPE_CNAME)) { + char* rrstr = dname_rdata_to_str(NULL, 0, rrtype, + rrclass, ttl, rdata_wol, rdata_len); + log_err("response-ip data (%s) can not be added; CNAME response-ip " + "data already in place for netblock %s", (rrstr?rrstr:""), netblockstr); + free(rrstr); + return 0; + } else if((rrtype != LDNS_RR_TYPE_CNAME) && + ((sa->sa_family == AF_INET && rrtype != LDNS_RR_TYPE_A) || + (sa->sa_family == AF_INET6 && rrtype != LDNS_RR_TYPE_AAAA))) { + char* rrstr = dname_rdata_to_str(NULL, 0, rrtype, + rrclass, ttl, rdata_wol, rdata_len); + log_err("response-ip data %s record type does not correspond " + "to netblock %s address family", (rrstr?rrstr:""), netblockstr); + free(rrstr); + return 0; + } + + if(!raddr->data) { + raddr->data = new_rrset(region, rrtype, rrclass); + if(!raddr->data) + return 0; + } + pd = raddr->data->entry.data; + return rrset_insert_rr_wol(region, pd, rdata_wol, rdata_len, ttl); +} + static int respip_enter_rrstr(struct regional* region, struct resp_addr* raddr, const char* rrstr, const char* netblock) diff --git a/respip/respip.h b/respip/respip.h index 83b641473..bc5b2e36f 100644 --- a/respip/respip.h +++ b/respip/respip.h @@ -276,12 +276,11 @@ void respip_inform_print(struct respip_action_info* respip_actinfo, * @param addrlen: length of addr. * @param net: netblock to lookup. * @param create: create node if it does not exist when 1. - * @param ipstr: human readable ip string, for logging. * @return newly created of found node, not holding lock. */ struct resp_addr* respip_sockaddr_find_or_create(struct respip_set* set, struct sockaddr_storage* addr, - socklen_t addrlen, int net, int create, const char* ipstr); + socklen_t addrlen, int net, int create); /** * Add RR to resp_addr's RRset. Create RRset if not existing. @@ -301,6 +300,22 @@ respip_enter_rr(struct regional* region, struct resp_addr* raddr, uint16_t rrtype, uint16_t rrclass, time_t ttl, uint8_t* rdata, size_t rdata_len, const char* rrstr, const char* netblockstr); +/** + * Add RR to resp_addr's RRset. Create RRset if not existing. + * @param region: region to alloc RR(set). + * @param raddr: resp_addr containing RRset. Must hold write lock. + * @param rrtype: RR type. + * @param rrclass: RR class. + * @param ttl: TTL. + * @param rdata: RDATA. Without prefix len. + * @param rdata_len: length of rdata. + * @param netblockstr: netblock as string, for logging + * @return 0 on error + */ +int respip_enter_rr_wol(struct regional* region, struct resp_addr* raddr, + uint16_t rrtype, uint16_t rrclass, time_t ttl, uint8_t* rdata, + size_t rdata_len, const char* netblockstr); + /** * Delete resp_addr node from tree. * @param set: struct containing tree. Must hold write lock. diff --git a/services/authzone.c b/services/authzone.c index ce5f65fe0..b6705dd79 100644 --- a/services/authzone.c +++ b/services/authzone.c @@ -1189,8 +1189,7 @@ az_domain_add_rr(struct auth_data* node, uint16_t rr_type, uint32_t rr_ttl, static int az_insert_rr_as_rdata(struct auth_zone* z, uint8_t* dname, size_t dname_len, uint16_t rr_type, uint16_t rr_class, uint32_t rr_ttl, - uint8_t* rdata_wol, size_t rdatalen, int* duplicate, - uint8_t* rr, size_t rr_len) + uint8_t* rdata_wol, size_t rdatalen, int* duplicate) { struct auth_data* node; if(rr_class != z->dclass) { @@ -1207,35 +1206,9 @@ az_insert_rr_as_rdata(struct auth_zone* z, uint8_t* dname, size_t dname_len, return 0; } if(z->rpz) { - uint8_t* rdata_wl; - uint8_t buf[65536]; - if(rr == NULL) { - /* spool it into buffer. */ - log_assert(dname); - if(dname_len + 10 /* type, class, ttl, rdlength */ + - rdatalen > sizeof(buf)) { - char dstr[LDNS_MAX_DOMAINLEN], t[16], c[16]; - dname_str(dname, dstr); - sldns_wire2str_type_buf(rr_type, t, sizeof(t)); - sldns_wire2str_class_buf(rr_class, c, sizeof(c)); - log_err("record exceeds buffer length, %s %s %s", dstr, c, t); - return 0; - } - rr = buf; - rr_len = dname_len - + 10 /* type, class, ttl, rdlength */ + - rdatalen; - memcpy(buf, dname, dname_len); - sldns_write_uint16(buf+dname_len, rr_type); - sldns_write_uint16(buf+dname_len+2, rr_class); - sldns_write_uint32(buf+dname_len+4, rr_ttl); - sldns_write_uint16(buf+dname_len+8, rdatalen); - memmove(buf+dname_len+10, rdata_wol, rdatalen); - } - rdata_wl = sldns_wirerr_get_rdatawl(rr, rr_len, dname_len); if(!(rpz_insert_rr(z->rpz, z->name, z->namelen, dname, - dname_len, rr_type, rr_class, rr_ttl, rdata_wl, - rdatalen+2, rr, rr_len))) + dname_len, rr_type, rr_class, rr_ttl, rdata_wol, + rdatalen))) return 0; } return 1; @@ -1256,7 +1229,7 @@ az_insert_rr(struct auth_zone* z, uint8_t* rr, size_t rr_len, uint8_t* rdata_wol = sldns_wirerr_get_rdata(rr, rr_len, dname_len); return az_insert_rr_as_rdata(z, dname, dname_len, rr_type, rr_class, - rr_ttl, rdata_wol, rdatalen, duplicate, rr, rr_len); + rr_ttl, rdata_wol, rdatalen, duplicate); } /** Remove rr from node, ignores nonexisting RRs, @@ -1690,7 +1663,7 @@ az_parse_accept(zone_parser_t *parser, const zone_name_t *owner, /* Duplicates can be ignored, do not insert them twice. */ if(!az_insert_rr_as_rdata(state->z, (uint8_t*)owner->octets, owner->length, type, dclass, ttl, (uint8_t*)rdata, rdlength, - NULL, NULL, 0)) { + NULL)) { char dname[LDNS_MAX_DOMAINLEN], t[16], c[16]; dname_str((uint8_t*)owner->octets, dname); sldns_wire2str_type_buf(type, t, sizeof(t)); diff --git a/services/localzone.c b/services/localzone.c index 9ea98c250..cd15b0f24 100644 --- a/services/localzone.c +++ b/services/localzone.c @@ -41,6 +41,7 @@ #include "config.h" #include "services/localzone.h" #include "sldns/str2wire.h" +#include "sldns/wire2str.h" #include "util/regional.h" #include "util/config_file.h" #include "util/data/dname.h" @@ -356,6 +357,22 @@ rr_is_duplicate(struct packed_rrset_data* pd, uint8_t* rdata, size_t rdata_len) return 0; } +/** see if rdata is duplicate */ +static int +rr_is_duplicate_wol(struct packed_rrset_data* d, uint8_t* rdata_wol, size_t len) +{ + size_t i, rdatawl_len = len+2; + uint16_t len16 = htons(len); + for(i=0; icount + d->rrsig_count; i++) { + if(d->rr_len[i] != rdatawl_len) + continue; + if(memcmp(d->rr_data[i], &len16, 2) == 0 && + memcmp(d->rr_data[i]+2, rdata_wol, len) == 0) + return 1; + } + return 0; +} + /** new local_rrset */ static struct local_rrset* new_local_rrset(struct regional* region, struct local_data* node, @@ -393,6 +410,49 @@ new_local_rrset(struct regional* region, struct local_data* node, return rrset; } +/** insert RR into RRset data structure; Wastes a couple of bytes */ +int +rrset_insert_rr_wol(struct regional* region, struct packed_rrset_data* pd, + uint8_t* rdata_wol, size_t rdata_len, time_t ttl) +{ + size_t* oldlen = pd->rr_len; + time_t* oldttl = pd->rr_ttl; + uint8_t** olddata = pd->rr_data; + + /* add RR to rrset */ + if(pd->count > LOCALZONE_RRSET_COUNT_MAX) { + log_warn("RRset has more than %d records, record ignored", + LOCALZONE_RRSET_COUNT_MAX); + return 1; + } + pd->count++; + pd->rr_len = regional_alloc(region, sizeof(*pd->rr_len)*pd->count); + pd->rr_ttl = regional_alloc(region, sizeof(*pd->rr_ttl)*pd->count); + pd->rr_data = regional_alloc(region, sizeof(*pd->rr_data)*pd->count); + if(!pd->rr_len || !pd->rr_ttl || !pd->rr_data) { + log_err("out of memory"); + return 0; + } + if(pd->count > 1) { + memcpy(pd->rr_len+1, oldlen, + sizeof(*pd->rr_len)*(pd->count-1)); + memcpy(pd->rr_ttl+1, oldttl, + sizeof(*pd->rr_ttl)*(pd->count-1)); + memcpy(pd->rr_data+1, olddata, + sizeof(*pd->rr_data)*(pd->count-1)); + } + pd->rr_len[0] = rdata_len+2; + pd->rr_ttl[0] = ttl; + pd->rr_data[0] = regional_alloc(region, rdata_len+2); + if(!pd->rr_data[0]) { + log_err("out of memory"); + return 0; + } + sldns_write_uint16(pd->rr_data[0], rdata_len); + memmove(pd->rr_data[0]+2, rdata_wol, rdata_len); + return 1; +} + /** insert RR into RRset data structure; Wastes a couple of bytes */ int rrset_insert_rr(struct regional* region, struct packed_rrset_data* pd, @@ -503,6 +563,48 @@ lz_find_create_node(struct local_zone* z, uint8_t* nm, size_t nmlen, return 1; } +/* Mark the SOA record for the zone. This only marks the SOA rrset; the data + * for the RR is entered later on local_zone_enter_rr() as with the other + * records. An artificial soa_negative record with a modified TTL (minimum of + * the TTL and the SOA.MINIMUM) is also created and marked for usage with + * negative answers and to avoid allocations during those answers. */ +static int +lz_mark_soa_for_zone_wol(struct local_zone* z, struct ub_packed_rrset_key* soa_rrset, + uint8_t* rdata_wol, size_t rdata_len, time_t ttl) +{ + struct packed_rrset_data* pd = (struct packed_rrset_data*) + regional_alloc_zero(z->region, sizeof(*pd)); + struct ub_packed_rrset_key* rrset_negative = (struct ub_packed_rrset_key*) + regional_alloc_zero(z->region, sizeof(*rrset_negative)); + time_t minimum; + if(!rrset_negative||!pd) { + log_err("out of memory"); + return 0; + } + /* Mark the original SOA record and then continue with the negative one. */ + z->soa = soa_rrset; + rrset_negative->entry.key = rrset_negative; + pd->trust = rrset_trust_prim_noglue; + pd->security = sec_status_insecure; + rrset_negative->entry.data = pd; + rrset_negative->rk.dname = soa_rrset->rk.dname; + rrset_negative->rk.dname_len = soa_rrset->rk.dname_len; + rrset_negative->rk.type = soa_rrset->rk.type; + rrset_negative->rk.rrset_class = soa_rrset->rk.rrset_class; + if(!rrset_insert_rr_wol(z->region, pd, rdata_wol, rdata_len, ttl)) + return 0; + /* last 4 bytes are minimum ttl in network format */ + if(pd->count == 0 || pd->rr_len[0] < 2+4) + return 0; + minimum = (time_t)sldns_read_uint32(pd->rr_data[0]+(pd->rr_len[0]-4)); + minimum = ttlttl = minimum; + pd->rr_ttl[0] = minimum; + + z->soa_negative = rrset_negative; + return 1; +} + /* Mark the SOA record for the zone. This only marks the SOA rrset; the data * for the RR is entered later on local_zone_enter_rr() as with the other * records. An artificial soa_negative record with a modified TTL (minimum of @@ -545,6 +647,96 @@ lz_mark_soa_for_zone(struct local_zone* z, struct ub_packed_rrset_key* soa_rrset return 1; } +/** + * Convert dname, type, class, ttl, rdata to an rr string. + * rdata without prefixed length. returned string is malloced. + */ +char* dname_rdata_to_str(uint8_t* dname, size_t dnamelen, uint16_t rrtype, + uint16_t rrclass, uint32_t ttl, uint8_t* rdata, size_t rdata_len) +{ + char buf[65536], t[32], c[32], d[1024], result[65536+32+32+1024+1024]; + buf[0]=0; buf[sizeof(buf)-1]=0; + d[0]=0; d[sizeof(d)-1]=0; + (void)sldns_wire2str_rdata_buf(rdata, rdata_len, buf, sizeof(buf), + rrtype); + (void)sldns_wire2str_type_buf(rrtype, t, sizeof(t)); + (void)sldns_wire2str_class_buf(rrclass, c, sizeof(c)); + if(dname) + (void)sldns_wire2str_dname_buf(dname, dnamelen, d, sizeof(d)); + snprintf(result, sizeof(result), "%s%s%u %s %s %s", + dname, (dname?" ":""), (unsigned)ttl, c, t, d); + return strdup(result); +} + +int +local_zone_enter_rr_wol(struct local_zone* z, uint8_t* nm, size_t nmlen, + int nmlabs, uint16_t rrtype, uint16_t rrclass, time_t ttl, + uint8_t* rdata_wol, size_t rdata_len) +{ + struct local_data* node; + struct local_rrset* rrset; + struct packed_rrset_data* pd; + + if(!lz_find_create_node(z, nm, nmlen, nmlabs, &node)) { + return 0; + } + log_assert(node); + + /* Reject it if we would end up having CNAME and other data (including + * another CNAME) for a redirect zone. */ + if((z->type == local_zone_redirect || + z->type == local_zone_inform_redirect) && node->rrsets) { + const char* othertype = NULL; + if (rrtype == LDNS_RR_TYPE_CNAME) + othertype = "other"; + else if (node->rrsets->rrset->rk.type == + htons(LDNS_RR_TYPE_CNAME)) { + othertype = "CNAME"; + } + if(othertype) { + char* rrstr = dname_rdata_to_str(nm, nmlen, rrtype, + rrclass, ttl, rdata_wol, rdata_len); + log_err("local-data '%s' in redirect zone must not " + "coexist with %s local-data", (rrstr?rrstr:""), othertype); + free(rrstr); + return 0; + } + } + rrset = local_data_find_type(node, rrtype, 0); + if(!rrset) { + rrset = new_local_rrset(z->region, node, rrtype, rrclass); + if(!rrset) + return 0; + if(query_dname_compare(node->name, z->name) == 0) { + if(rrtype == LDNS_RR_TYPE_NSEC) + rrset->rrset->rk.flags = PACKED_RRSET_NSEC_AT_APEX; + if(rrtype == LDNS_RR_TYPE_SOA && + !lz_mark_soa_for_zone_wol(z, rrset->rrset, rdata_wol, rdata_len, ttl)) + return 0; + } + } + pd = (struct packed_rrset_data*)rrset->rrset->entry.data; + log_assert(rrset && pd); + + /* check for duplicate RR */ + if(rr_is_duplicate_wol(pd, rdata_wol, rdata_len)) { + char* rrstr = dname_rdata_to_str(nm, nmlen, rrtype, + rrclass, ttl, rdata_wol, rdata_len); + verbose(VERB_ALGO, "ignoring duplicate RR: %s", (rrstr?rrstr:"")); + free(rrstr); + return 1; + } + if(pd->count > LOCALZONE_RRSET_COUNT_MAX) { + char* rrstr = dname_rdata_to_str(nm, nmlen, rrtype, + rrclass, ttl, rdata_wol, rdata_len); + log_warn("RRset %s has more than %d records, record ignored", + (rrstr?rrstr:""), LOCALZONE_RRSET_COUNT_MAX); + free(rrstr); + return 1; + } + return rrset_insert_rr_wol(z->region, pd, rdata_wol, rdata_len, ttl); +} + int local_zone_enter_rr(struct local_zone* z, uint8_t* nm, size_t nmlen, int nmlabs, uint16_t rrtype, uint16_t rrclass, time_t ttl, @@ -2039,7 +2231,7 @@ set_kiddo_parents(struct local_zone* z, struct local_zone* match, struct local_zone* local_zones_add_zone(struct local_zones* zones, uint8_t* name, size_t len, int labs, uint16_t dclass, - enum localzone_type tp) + enum localzone_type tp, int* duplicate) { int exact; /* create */ @@ -2047,6 +2239,7 @@ struct local_zone* local_zones_add_zone(struct local_zones* zones, struct local_zone* z = local_zone_create(name, len, labs, tp, dclass); if(!z) { free(name); + if(duplicate) *duplicate = 0; return NULL; } lock_rw_wrlock(&z->lock); @@ -2060,8 +2253,14 @@ struct local_zone* local_zones_add_zone(struct local_zones* zones, if(exact||!rbtree_insert(&zones->ztree, &z->node)) { /* duplicate entry! */ lock_rw_unlock(&z->lock); + if(duplicate) { + *duplicate = 1; + z->name = NULL; /* Do not delete the name in + local_zone_delete. */ + } local_zone_delete(z); - log_err("internal: duplicate entry in local_zones_add_zone"); + if(duplicate == NULL) + log_err("internal: duplicate entry in local_zones_add_zone"); return NULL; } @@ -2105,7 +2304,7 @@ local_zones_add_RR(struct local_zones* zones, const char* rr) z = local_zones_lookup(zones, rr_name, len, labs, rr_class, rr_type); if(!z) { z = local_zones_add_zone(zones, rr_name, len, labs, rr_class, - local_zone_transparent); + local_zone_transparent, NULL); if(!z) { lock_rw_unlock(&zones->lock); return 0; diff --git a/services/localzone.h b/services/localzone.h index 66102fd98..3cecaff4a 100644 --- a/services/localzone.h +++ b/services/localzone.h @@ -403,11 +403,17 @@ local_zones_find_le(struct local_zones* zones, * @param labs: labelcount of name. * @param dclass: class to add. * @param tp: type. + * @param duplicate: Allows to check if a NULL return from the function is a + * memory error, or a duplicate entry. Pass NULL to have it not returned, + * the name is freed on errors, and for a duplicate a log message is + * printed. Pass not NULL, and when the error is a duplicate, the function + * returns NULL, and the variable is set true. The name is not freed + * when there is a duplicate, no error is printed by this function. * @return local_zone or NULL on error, caller must printout memory error. */ struct local_zone* local_zones_add_zone(struct local_zones* zones, uint8_t* name, size_t len, int labs, uint16_t dclass, - enum localzone_type tp); + enum localzone_type tp, int* duplicate); /** * Delete a zone. Caller must hold the zones lock. @@ -526,6 +532,18 @@ int rrstr_get_rr_content(const char* str, uint8_t** nm, uint16_t* type, int rrset_insert_rr(struct regional* region, struct packed_rrset_data* pd, uint8_t* rdata, size_t rdata_len, time_t ttl, const char* rrstr); +/** + * Insert specified rdata into the specified resource record. + * @param region: allocator + * @param pd: data portion of the destination resource record + * @param rdata_wol: source rdata, without prefix len. + * @param rdata_len: source rdata length + * @param ttl: time to live + * @return 1 on success; 0 otherwise. + */ +int rrset_insert_rr_wol(struct regional* region, struct packed_rrset_data* pd, + uint8_t* rdata_wol, size_t rdata_len, time_t ttl); + /** * Remove RR from rrset that is created using localzone's rrset_insert_rr. * @param pd: the RRset containing the RR to remove @@ -631,6 +649,24 @@ local_zone_enter_rr(struct local_zone* z, uint8_t* nm, size_t nmlen, int nmlabs, uint16_t rrtype, uint16_t rrclass, time_t ttl, uint8_t* rdata, size_t rdata_len, const char* rrstr); +/** + * Add RR to local zone. Without prefix length on rdata. + * @param z: local zone to add RR to + * @param nm: dname of RR + * @param nmlen: length of nm + * @param nmlabs: number of labels of nm + * @param rrtype: RR type + * @param rrclass: RR class + * @param ttl: TTL of RR to add + * @param rdata_wol: RDATA of RR to add, without prefix length. + * @param rdata_len: length of rdata, without prefix length. + * @return: 1 on success + */ +int +local_zone_enter_rr_wol(struct local_zone* z, uint8_t* nm, size_t nmlen, + int nmlabs, uint16_t rrtype, uint16_t rrclass, time_t ttl, + uint8_t* rdata_wol, size_t rdata_len); + /** * Find a data node by exact name for a local zone * @param z: local_zone containing data tree @@ -674,4 +710,12 @@ lz_enter_zone(struct local_zones* zones, const char* name, const char* type, */ void lz_init_parents(struct local_zones* zones); + +/** + * Convert dname, type, class, ttl, rdata to an rr string. + * rdata without prefixed length. returned string is malloced. + */ +char* dname_rdata_to_str(uint8_t* dname, size_t dnamelen, uint16_t rrtype, + uint16_t rrclass, uint32_t ttl, uint8_t* rdata, size_t rdata_len); + #endif /* SERVICES_LOCALZONE_H */ diff --git a/services/rpz.c b/services/rpz.c index f45cf6542..ed68e5f00 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -170,15 +170,14 @@ rpz_type_ignored(uint16_t rr_type) /** * Classify RPZ action for RR type/rdata * @param rr_type: the RR type - * @param rdatawl: RDATA with 2 bytes length - * @param rdatalen: the length of rdatawl (including its 2 bytes length) + * @param rdata: RDATA without 2 bytes length + * @param rdatalen: the length of rdatawl (not including its 2 bytes length) * @return: the RPZ action */ static enum rpz_action -rpz_rr_to_action(uint16_t rr_type, uint8_t* rdatawl, size_t rdatalen) +rpz_rr_to_action_wol(uint16_t rr_type, uint8_t* rdata, size_t rdatalen) { char* endptr; - uint8_t* rdata; int rdatalabs; uint8_t* tldlab = NULL; @@ -202,11 +201,10 @@ rpz_rr_to_action(uint16_t rr_type, uint8_t* rdatawl, size_t rdatalen) /* use CNAME target to determine RPZ action */ log_assert(rr_type == LDNS_RR_TYPE_CNAME); - if(rdatalen < 3) + if(rdatalen < 1) return RPZ_INVALID_ACTION; - rdata = rdatawl + 2; /* 2 bytes of rdata length */ - if(dname_valid(rdata, rdatalen-2) != rdatalen-2) + if(dname_valid(rdata, rdatalen) != rdatalen) return RPZ_INVALID_ACTION; rdatalabs = dname_count_labels(rdata); @@ -226,7 +224,7 @@ rpz_rr_to_action(uint16_t rr_type, uint8_t* rdatawl, size_t rdatalen) } /* all other TLDs starting with "rpz-" are invalid */ - tldlab = get_tld_label(rdata, rdatalen-2); + tldlab = get_tld_label(rdata, rdatalen); if(tldlab && dname_lab_startswith(tldlab, "rpz-", &endptr)) return RPZ_INVALID_ACTION; @@ -234,6 +232,21 @@ rpz_rr_to_action(uint16_t rr_type, uint8_t* rdatawl, size_t rdatalen) return RPZ_LOCAL_DATA_ACTION; } +/** + * Classify RPZ action for RR type/rdata + * @param rr_type: the RR type + * @param rdatawl: RDATA with 2 bytes length + * @param rdatalen: the length of rdatawl (including its 2 bytes length) + * @return: the RPZ action + */ +static enum rpz_action +rpz_rr_to_action(uint16_t rr_type, uint8_t* rdatawl, size_t rdatalen) +{ + if(rdatalen < 2) + return RPZ_INVALID_ACTION; + return rpz_rr_to_action_wol(rr_type, rdatawl+2, rdatalen-2); +} + static enum localzone_type rpz_action_to_localzone_type(enum rpz_action a) { @@ -658,7 +671,7 @@ strip_dname_origin(uint8_t* dname, size_t dnamelen, size_t originlen, static void rpz_insert_local_zones_trigger(struct local_zones* lz, uint8_t* dname, size_t dnamelen, enum rpz_action a, uint16_t rrtype, uint16_t rrclass, - uint32_t ttl, uint8_t* rdata, size_t rdata_len, uint8_t* rr, size_t rr_len) + uint32_t ttl, uint8_t* rdata, size_t rdata_len) { struct local_zone* z; enum localzone_type tp = local_zone_always_transparent; @@ -685,20 +698,41 @@ rpz_insert_local_zones_trigger(struct local_zones* lz, uint8_t* dname, return; } + /* For not a local-data action. + * Insert the zone, then detect a duplicate, instead of find it first, + * for speed of searching the tree once. */ + if(a != RPZ_LOCAL_DATA_ACTION) { + int duplicate = 0; + lock_rw_wrlock(&lz->lock); + tp = rpz_action_to_localzone_type(a); + z = local_zones_add_zone(lz, dname, dnamelen, + dnamelabs, rrclass, tp, &duplicate); + if(z == NULL) { + if(duplicate) { + char* rrstr = dname_rdata_to_str(dname, dnamelen, rrtype, + rrclass, ttl, rdata, rdata_len); + verbose(VERB_ALGO, "rpz: skipping duplicate record: %s", (rrstr?rrstr:"")); + free(rrstr); + free(dname); + lock_rw_unlock(&lz->lock); + return; + } + log_warn("rpz: create failed, out of memory"); + lock_rw_unlock(&lz->lock); + /* dname will be free'd in failed local_zone_create() */ + return; + } + lock_rw_unlock(&lz->lock); + return; + } + lock_rw_wrlock(&lz->lock); /* exact match */ z = local_zones_find(lz, dname, dnamelen, dnamelabs, LDNS_RR_CLASS_IN); if(z != NULL && a != RPZ_LOCAL_DATA_ACTION) { - char* rrstr = sldns_wire2str_rr(rr, rr_len); - if(rrstr == NULL) { - log_err("malloc error while inserting rpz nsdname trigger"); - free(dname); - lock_rw_unlock(&lz->lock); - return; - } - if(rrstr[0]) - rrstr[strlen(rrstr)-1]=0; /* remove newline */ - verbose(VERB_ALGO, "rpz: skipping duplicate record: '%s'", rrstr); + char* rrstr = dname_rdata_to_str(dname, dnamelen, rrtype, + rrclass, ttl, rdata, rdata_len); + verbose(VERB_ALGO, "rpz: skipping duplicate record: %s", (rrstr?rrstr:"")); free(rrstr); free(dname); lock_rw_unlock(&lz->lock); @@ -707,7 +741,7 @@ rpz_insert_local_zones_trigger(struct local_zones* lz, uint8_t* dname, if(z == NULL) { tp = rpz_action_to_localzone_type(a); z = local_zones_add_zone(lz, dname, dnamelen, - dnamelabs, rrclass, tp); + dnamelabs, rrclass, tp, NULL); if(z == NULL) { log_warn("rpz: create failed"); lock_rw_unlock(&lz->lock); @@ -717,18 +751,10 @@ rpz_insert_local_zones_trigger(struct local_zones* lz, uint8_t* dname, newzone = 1; } if(a == RPZ_LOCAL_DATA_ACTION) { - char* rrstr = sldns_wire2str_rr(rr, rr_len); - if(rrstr == NULL) { - log_err("malloc error while inserting rpz nsdname trigger"); - free(dname); - lock_rw_unlock(&lz->lock); - return; - } lock_rw_wrlock(&z->lock); - local_zone_enter_rr(z, dname, dnamelen, dnamelabs, rrtype, - rrclass, ttl, rdata, rdata_len, rrstr); + local_zone_enter_rr_wol(z, dname, dnamelen, dnamelabs, rrtype, + rrclass, ttl, rdata, rdata_len); lock_rw_unlock(&z->lock); - free(rrstr); } if(!newzone) { free(dname); @@ -748,7 +774,7 @@ rpz_log_dname(char const* msg, uint8_t* dname, size_t dname_len) static void rpz_insert_qname_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, enum rpz_action a, uint16_t rrtype, uint16_t rrclass, uint32_t ttl, - uint8_t* rdata, size_t rdata_len, uint8_t* rr, size_t rr_len) + uint8_t* rdata, size_t rdata_len) { if(a == RPZ_INVALID_ACTION) { verbose(VERB_ALGO, "rpz: skipping invalid action"); @@ -757,7 +783,7 @@ rpz_insert_qname_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, } rpz_insert_local_zones_trigger(r->local_zones, dname, dnamelen, a, rrtype, - rrclass, ttl, rdata, rdata_len, rr, rr_len); + rrclass, ttl, rdata, rdata_len); } static int @@ -799,7 +825,7 @@ rpz_strip_nsdname_suffix(uint8_t* dname, size_t maxdnamelen, static void rpz_insert_nsdname_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, enum rpz_action a, uint16_t rrtype, uint16_t rrclass, uint32_t ttl, - uint8_t* rdata, size_t rdata_len, uint8_t* rr, size_t rr_len) + uint8_t* rdata, size_t rdata_len) { uint8_t* dname_stripped = NULL; size_t dnamelen_stripped = 0; @@ -814,32 +840,22 @@ rpz_insert_nsdname_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, /* dname_stripped is consumed or freed by the insert routine */ rpz_insert_local_zones_trigger(r->nsdname_zones, dname_stripped, - dnamelen_stripped, a, rrtype, rrclass, ttl, rdata, rdata_len, - rr, rr_len); + dnamelen_stripped, a, rrtype, rrclass, ttl, rdata, rdata_len); } static int rpz_insert_ipaddr_based_trigger(struct respip_set* set, struct sockaddr_storage* addr, socklen_t addrlen, int net, enum rpz_action a, uint16_t rrtype, - uint16_t rrclass, uint32_t ttl, uint8_t* rdata, size_t rdata_len, - uint8_t* rr, size_t rr_len) + uint16_t rrclass, uint32_t ttl, uint8_t* rdata, size_t rdata_len) { struct resp_addr* node; - char* rrstr; enum respip_action respa = rpz_action_to_respip_action(a); lock_rw_wrlock(&set->lock); - rrstr = sldns_wire2str_rr(rr, rr_len); - if(rrstr == NULL) { - log_err("malloc error while inserting rpz ipaddr based trigger"); - lock_rw_unlock(&set->lock); - return 0; - } - node = respip_sockaddr_find_or_create(set, addr, addrlen, net, 1, rrstr); + node = respip_sockaddr_find_or_create(set, addr, addrlen, net, 1); if(node == NULL) { lock_rw_unlock(&set->lock); - free(rrstr); return 0; } @@ -849,12 +865,11 @@ rpz_insert_ipaddr_based_trigger(struct respip_set* set, struct sockaddr_storage* node->action = respa; if(a == RPZ_LOCAL_DATA_ACTION) { - respip_enter_rr(set->region, node, rrtype, - rrclass, ttl, rdata, rdata_len, rrstr, ""); + respip_enter_rr_wol(set->region, node, rrtype, + rrclass, ttl, rdata, rdata_len, ""); } lock_rw_unlock(&node->lock); - free(rrstr); return 1; } @@ -890,17 +905,6 @@ rpz_clientip_ensure_entry(struct clientip_synthesized_rrset* set, return node; } -static void -rpz_report_rrset_error(const char* msg, uint8_t* rr, size_t rr_len) { - char* rrstr = sldns_wire2str_rr(rr, rr_len); - if(rrstr == NULL) { - log_err("malloc error while inserting rpz clientip based record"); - return; - } - log_err("rpz: unexpected: unable to insert %s: %s", msg, rrstr); - free(rrstr); -} - /* from localzone.c; difference is we don't have a dname */ static struct local_rrset* rpz_clientip_new_rrset(struct regional* region, @@ -958,14 +962,13 @@ rpz_clientip_enter_rr(struct regional* region, struct clientip_synthesized_rr* r return 0; } - return rrset_insert_rr(region, rrset->rrset->entry.data, rdata, rdata_len, ttl, ""); + return rrset_insert_rr_wol(region, rrset->rrset->entry.data, rdata, rdata_len, ttl); } static int rpz_clientip_insert_trigger_rr(struct clientip_synthesized_rrset* set, struct sockaddr_storage* addr, socklen_t addrlen, int net, enum rpz_action a, uint16_t rrtype, - uint16_t rrclass, uint32_t ttl, uint8_t* rdata, size_t rdata_len, - uint8_t* rr, size_t rr_len) + uint16_t rrclass, uint32_t ttl, uint8_t* rdata, size_t rdata_len) { struct clientip_synthesized_rr* node; @@ -973,8 +976,14 @@ rpz_clientip_insert_trigger_rr(struct clientip_synthesized_rrset* set, struct so node = rpz_clientip_ensure_entry(set, addr, addrlen, net); if(node == NULL) { + char as[64], *rrstr; lock_rw_unlock(&set->lock); - rpz_report_rrset_error("client ip address", rr, rr_len); + addr_to_str(addr, addrlen, as, sizeof(as)); + rrstr = dname_rdata_to_str(NULL, 0, rrtype, rrclass, ttl, + rdata, rdata_len); + log_err("rpz: unexpected: unable to insert %s: %s/%d %s", + "client ip address", as, net, (rrstr?rrstr:"")); + free(rrstr); return 0; } @@ -1000,7 +1009,7 @@ rpz_clientip_insert_trigger_rr(struct clientip_synthesized_rrset* set, struct so static int rpz_insert_clientip_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, enum rpz_action a, uint16_t rrtype, uint16_t rrclass, uint32_t ttl, - uint8_t* rdata, size_t rdata_len, uint8_t* rr, size_t rr_len) + uint8_t* rdata, size_t rdata_len) { struct sockaddr_storage addr; socklen_t addrlen; @@ -1016,13 +1025,13 @@ rpz_insert_clientip_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, } return rpz_clientip_insert_trigger_rr(r->client_set, &addr, addrlen, net, - a, rrtype, rrclass, ttl, rdata, rdata_len, rr, rr_len); + a, rrtype, rrclass, ttl, rdata, rdata_len); } static int rpz_insert_nsip_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, enum rpz_action a, uint16_t rrtype, uint16_t rrclass, uint32_t ttl, - uint8_t* rdata, size_t rdata_len, uint8_t* rr, size_t rr_len) + uint8_t* rdata, size_t rdata_len) { struct sockaddr_storage addr; socklen_t addrlen; @@ -1038,14 +1047,14 @@ rpz_insert_nsip_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, } return rpz_clientip_insert_trigger_rr(r->ns_set, &addr, addrlen, net, - a, rrtype, rrclass, ttl, rdata, rdata_len, rr, rr_len); + a, rrtype, rrclass, ttl, rdata, rdata_len); } /** Insert RR into RPZ's respip_set */ static int rpz_insert_response_ip_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, enum rpz_action a, uint16_t rrtype, uint16_t rrclass, uint32_t ttl, - uint8_t* rdata, size_t rdata_len, uint8_t* rr, size_t rr_len) + uint8_t* rdata, size_t rdata_len) { struct sockaddr_storage addr; socklen_t addrlen; @@ -1070,13 +1079,13 @@ rpz_insert_response_ip_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, } return rpz_insert_ipaddr_based_trigger(r->respip_set, &addr, addrlen, net, - a, rrtype, rrclass, ttl, rdata, rdata_len, rr, rr_len); + a, rrtype, rrclass, ttl, rdata, rdata_len); } int rpz_insert_rr(struct rpz* r, uint8_t* azname, size_t aznamelen, uint8_t* dname, size_t dnamelen, uint16_t rr_type, uint16_t rr_class, uint32_t rr_ttl, - uint8_t* rdatawl, size_t rdatalen, uint8_t* rr, size_t rr_len) + uint8_t* rdatawol, size_t rdatalen) { size_t policydnamelen; /* name is free'd in local_zone delete */ @@ -1110,7 +1119,7 @@ rpz_insert_rr(struct rpz* r, uint8_t* azname, size_t aznamelen, uint8_t* dname, return 0; } - a = rpz_rr_to_action(rr_type, rdatawl, rdatalen); + a = rpz_rr_to_action_wol(rr_type, rdatawol, rdatalen); if(!(policydnamelen = strip_dname_origin(dname, dnamelen, aznamelen, policydname, (dnamelen-aznamelen)+1))) { free(policydname); @@ -1125,27 +1134,22 @@ rpz_insert_rr(struct rpz* r, uint8_t* azname, size_t aznamelen, uint8_t* dname, if(t == RPZ_QNAME_TRIGGER) { /* policydname will be consumed, no free */ rpz_insert_qname_trigger(r, policydname, policydnamelen, - a, rr_type, rr_class, rr_ttl, rdatawl, rdatalen, rr, - rr_len); + a, rr_type, rr_class, rr_ttl, rdatawol, rdatalen); } else if(t == RPZ_RESPONSE_IP_TRIGGER) { rpz_insert_response_ip_trigger(r, policydname, policydnamelen, - a, rr_type, rr_class, rr_ttl, rdatawl, rdatalen, rr, - rr_len); + a, rr_type, rr_class, rr_ttl, rdatawol, rdatalen); free(policydname); } else if(t == RPZ_CLIENT_IP_TRIGGER) { rpz_insert_clientip_trigger(r, policydname, policydnamelen, - a, rr_type, rr_class, rr_ttl, rdatawl, rdatalen, rr, - rr_len); + a, rr_type, rr_class, rr_ttl, rdatawol, rdatalen); free(policydname); } else if(t == RPZ_NSIP_TRIGGER) { rpz_insert_nsip_trigger(r, policydname, policydnamelen, - a, rr_type, rr_class, rr_ttl, rdatawl, rdatalen, rr, - rr_len); + a, rr_type, rr_class, rr_ttl, rdatawol, rdatalen); free(policydname); } else if(t == RPZ_NSDNAME_TRIGGER) { rpz_insert_nsdname_trigger(r, policydname, policydnamelen, - a, rr_type, rr_class, rr_ttl, rdatawl, rdatalen, rr, - rr_len); + a, rr_type, rr_class, rr_ttl, rdatawol, rdatalen); free(policydname); } else { free(policydname); diff --git a/services/rpz.h b/services/rpz.h index 6b5f17d1e..f3fb6e02e 100644 --- a/services/rpz.h +++ b/services/rpz.h @@ -136,15 +136,13 @@ struct rpz { * @param rr_type: RR type of the RR * @param rr_class: RR class of the RR * @param rr_ttl: TTL of the RR - * @param rdatawl: rdata of the RR, prepended with the rdata size - * @param rdatalen: length if the RR, including the prepended rdata size - * @param rr: the complete RR, for logging purposes - * @param rr_len: the length of the complete RR + * @param rdatawol: rdata of the RR, not prepended with the rdata size + * @param rdatalen: length if the RR, not including the prepended rdata size * @return: 0 on error */ int rpz_insert_rr(struct rpz* r, uint8_t* azname, size_t aznamelen, uint8_t* dname, size_t dnamelen, uint16_t rr_type, uint16_t rr_class, uint32_t rr_ttl, - uint8_t* rdatawl, size_t rdatalen, uint8_t* rr, size_t rr_len); + uint8_t* rdatawol, size_t rdatalen); /** * Delete policy matching RR, used for IXFR. diff --git a/testcode/unitmain.c b/testcode/unitmain.c index 79ce45f39..bbbf6750b 100644 --- a/testcode/unitmain.c +++ b/testcode/unitmain.c @@ -1265,7 +1265,7 @@ static void localzone_parents_test(void) nmlabs = dname_count_size_labels(nm, &nmlen); lock_rw_wrlock(&z2->lock); local_zones_add_zone(z2, nm, nmlen, nmlabs, LDNS_RR_CLASS_IN, - local_zone_always_nxdomain); + local_zone_always_nxdomain, NULL); lock_rw_unlock(&z2->lock); } /* The trees should be the same, iterate and check the nodes */ diff --git a/util/data/dname.c b/util/data/dname.c index 5370aa6f9..902b752e8 100644 --- a/util/data/dname.c +++ b/util/data/dname.c @@ -100,6 +100,7 @@ int query_dname_compare(register uint8_t* d1, register uint8_t* d2) { register uint8_t lab1, lab2; + register uint8_t ld1, ld2; log_assert(d1 && d2); lab1 = *d1++; lab2 = *d2++; @@ -116,8 +117,8 @@ query_dname_compare(register uint8_t* d1, register uint8_t* d2) while(lab1--) { /* compare bytes first for speed */ if(*d1 != *d2 && - tolower((unsigned char)*d1) != tolower((unsigned char)*d2)) { - if(tolower((unsigned char)*d1) < tolower((unsigned char)*d2)) + (ld1=tolower((unsigned char)*d1)) != (ld2=tolower((unsigned char)*d2))) { + if(ld1 < ld2) return -1; return 1; } @@ -233,6 +234,7 @@ int dname_pkt_compare(sldns_buffer* pkt, uint8_t* d1, uint8_t* d2) { uint8_t len1, len2; + register uint8_t ld1, ld2; int count1 = 0, count2 = 0; log_assert(pkt && d1 && d2); len1 = *d1++; @@ -269,8 +271,8 @@ dname_pkt_compare(sldns_buffer* pkt, uint8_t* d1, uint8_t* d2) log_assert(len1 == len2 && len1 != 0); /* compare labels */ while(len1--) { - if(tolower((unsigned char)*d1) != tolower((unsigned char)*d2)) { - if(tolower((unsigned char)*d1) < tolower((unsigned char)*d2)) + if((ld1=tolower((unsigned char)*d1)) != (ld2=tolower((unsigned char)*d2))) { + if(ld1 < ld2) return -1; return 1; } @@ -470,9 +472,10 @@ dname_count_size_labels(uint8_t* dname, size_t* size) static int memlowercmp(uint8_t* p1, uint8_t* p2, uint8_t len) { + register uint8_t lp1, lp2; while(len--) { - if(*p1 != *p2 && tolower((unsigned char)*p1) != tolower((unsigned char)*p2)) { - if(tolower((unsigned char)*p1) < tolower((unsigned char)*p2)) + if(*p1 != *p2 && (lp1=tolower((unsigned char)*p1)) != (lp2=tolower((unsigned char)*p2))) { + if(lp1 < lp2) return -1; return 1; } @@ -523,15 +526,15 @@ dname_lab_cmp(uint8_t* d1, int labs1, uint8_t* d2, int labs2, int* mlabs) d1 += len1; d2 += len2; } else { + register uint8_t ld1, ld2; /* memlowercmp is inlined here; or just like * if((c=memlowercmp(d1, d2, len1)) != 0) { * lastdiff = c; * lastmlabs = atlabel; } apart from d1++,d2++ */ while(len1) { - if(*d1 != *d2 && tolower((unsigned char)*d1) - != tolower((unsigned char)*d2)) { - if(tolower((unsigned char)*d1) < - tolower((unsigned char)*d2)) { + if(*d1 != *d2 && (ld1=tolower((unsigned char)*d1)) + != (ld2=tolower((unsigned char)*d2))) { + if(ld1 < ld2) { lastdiff = -1; lastmlabs = atlabel; d1 += len1;