Files
unbound/util/data/packed_rrset.c
T
W.C.A. Wijngaards b1d1dcb3b6 - Fix that dump_cache has a larger buffer for records,
and it checks that an owner name does not collide with BADRR
  on the input, and changes verbosity on the log of failure in
  rrset to string.  Thanks to Qifan Zhang, Palo Alto Networks,
  for the report.
2026-06-03 14:51:16 +02:00

427 lines
12 KiB
C

/*
* util/data/packed_rrset.c - data storage for a set of resource records.
*
* Copyright (c) 2007, NLnet Labs. All rights reserved.
*
* This software is open source.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the NLNET LABS nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \file
*
* This file contains the data storage for RRsets.
*/
#include "config.h"
#include "util/data/msgparse.h"
#include "util/data/packed_rrset.h"
#include "util/data/dname.h"
#include "util/storage/lookup3.h"
#include "util/log.h"
#include "util/alloc.h"
#include "util/regional.h"
#include "util/net_help.h"
#include "sldns/rrdef.h"
#include "sldns/sbuffer.h"
#include "sldns/wire2str.h"
void
ub_packed_rrset_parsedelete(struct ub_packed_rrset_key* pkey,
struct alloc_cache* alloc)
{
if(!pkey)
return;
free(pkey->entry.data);
pkey->entry.data = NULL;
free(pkey->rk.dname);
pkey->rk.dname = NULL;
pkey->id = 0;
alloc_special_release(alloc, pkey);
}
size_t
ub_rrset_sizefunc(void* key, void* data)
{
struct ub_packed_rrset_key* k = (struct ub_packed_rrset_key*)key;
struct packed_rrset_data* d = (struct packed_rrset_data*)data;
size_t s = sizeof(struct ub_packed_rrset_key) + k->rk.dname_len;
s += packed_rrset_sizeof(d) + lock_get_mem(&k->entry.lock);
return s;
}
size_t
packed_rrset_sizeof(struct packed_rrset_data* d)
{
size_t s;
if(d->rrsig_count > 0) {
s = ((uint8_t*)d->rr_data[d->count+d->rrsig_count-1] -
(uint8_t*)d) + d->rr_len[d->count+d->rrsig_count-1];
} else {
log_assert(d->count > 0);
s = ((uint8_t*)d->rr_data[d->count-1] - (uint8_t*)d) +
d->rr_len[d->count-1];
}
return s;
}
int
ub_rrset_compare(void* k1, void* k2)
{
struct ub_packed_rrset_key* key1 = (struct ub_packed_rrset_key*)k1;
struct ub_packed_rrset_key* key2 = (struct ub_packed_rrset_key*)k2;
int c;
if(key1 == key2)
return 0;
if(key1->rk.type != key2->rk.type) {
if(key1->rk.type < key2->rk.type)
return -1;
return 1;
}
if(key1->rk.dname_len != key2->rk.dname_len) {
if(key1->rk.dname_len < key2->rk.dname_len)
return -1;
return 1;
}
if((c=query_dname_compare(key1->rk.dname, key2->rk.dname)) != 0)
return c;
if(key1->rk.rrset_class != key2->rk.rrset_class) {
if(key1->rk.rrset_class < key2->rk.rrset_class)
return -1;
return 1;
}
if(key1->rk.flags != key2->rk.flags) {
if(key1->rk.flags < key2->rk.flags)
return -1;
return 1;
}
return 0;
}
void
ub_rrset_key_delete(void* key, void* userdata)
{
struct ub_packed_rrset_key* k = (struct ub_packed_rrset_key*)key;
struct alloc_cache* a = (struct alloc_cache*)userdata;
k->id = 0;
free(k->rk.dname);
k->rk.dname = NULL;
alloc_special_release(a, k);
}
void
rrset_data_delete(void* data, void* ATTR_UNUSED(userdata))
{
struct packed_rrset_data* d = (struct packed_rrset_data*)data;
free(d);
}
int
rrsetdata_equal(struct packed_rrset_data* d1, struct packed_rrset_data* d2)
{
size_t i;
size_t total;
if(d1->count != d2->count || d1->rrsig_count != d2->rrsig_count)
return 0;
total = d1->count + d1->rrsig_count;
for(i=0; i<total; i++) {
if(d1->rr_len[i] != d2->rr_len[i])
return 0;
if(memcmp(d1->rr_data[i], d2->rr_data[i], d1->rr_len[i]) != 0)
return 0;
}
return 1;
}
hashvalue_type
rrset_key_hash(struct packed_rrset_key* key)
{
/* type is hashed in host order */
uint16_t t = ntohs(key->type);
/* Note this MUST be identical to pkt_hash_rrset in msgparse.c */
/* this routine does not have a compressed name */
hashvalue_type h = 0xab;
h = dname_query_hash(key->dname, h);
h = hashlittle(&t, sizeof(t), h);
h = hashlittle(&key->rrset_class, sizeof(uint16_t), h);
h = hashlittle(&key->flags, sizeof(uint32_t), h);
return h;
}
void
packed_rrset_ptr_fixup(struct packed_rrset_data* data)
{
size_t i;
size_t total = data->count + data->rrsig_count;
uint8_t* nextrdata;
/* fixup pointers in packed rrset data */
data->rr_len = (size_t*)((uint8_t*)data +
sizeof(struct packed_rrset_data));
data->rr_data = (uint8_t**)&(data->rr_len[total]);
data->rr_ttl = (time_t*)&(data->rr_data[total]);
nextrdata = (uint8_t*)&(data->rr_ttl[total]);
for(i=0; i<total; i++) {
data->rr_data[i] = nextrdata;
nextrdata += data->rr_len[i];
}
}
void
get_cname_target(struct ub_packed_rrset_key* rrset, uint8_t** dname,
size_t* dname_len)
{
struct packed_rrset_data* d;
size_t len;
if(ntohs(rrset->rk.type) != LDNS_RR_TYPE_CNAME &&
ntohs(rrset->rk.type) != LDNS_RR_TYPE_DNAME)
return;
d = (struct packed_rrset_data*)rrset->entry.data;
if(d->count < 1)
return;
if(d->rr_len[0] < 3) /* at least rdatalen + 0byte root label */
return;
len = sldns_read_uint16(d->rr_data[0]);
if(len != d->rr_len[0] - sizeof(uint16_t))
return;
if(dname_valid(d->rr_data[0]+sizeof(uint16_t), len) != len)
return;
*dname = d->rr_data[0]+sizeof(uint16_t);
*dname_len = len;
}
void
packed_rrset_ttl_add(struct packed_rrset_data* data, time_t add)
{
size_t i;
size_t total = data->count + data->rrsig_count;
data->ttl_add = add;
data->ttl += add;
for(i=0; i<total; i++)
data->rr_ttl[i] += add;
}
const char*
rrset_trust_to_string(enum rrset_trust s)
{
switch(s) {
case rrset_trust_none: return "rrset_trust_none";
case rrset_trust_add_noAA: return "rrset_trust_add_noAA";
case rrset_trust_auth_noAA: return "rrset_trust_auth_noAA";
case rrset_trust_add_AA: return "rrset_trust_add_AA";
case rrset_trust_nonauth_ans_AA:return "rrset_trust_nonauth_ans_AA";
case rrset_trust_ans_noAA: return "rrset_trust_ans_noAA";
case rrset_trust_glue: return "rrset_trust_glue";
case rrset_trust_auth_AA: return "rrset_trust_auth_AA";
case rrset_trust_ans_AA: return "rrset_trust_ans_AA";
case rrset_trust_sec_noglue: return "rrset_trust_sec_noglue";
case rrset_trust_prim_noglue: return "rrset_trust_prim_noglue";
case rrset_trust_validated: return "rrset_trust_validated";
case rrset_trust_ultimate: return "rrset_trust_ultimate";
}
return "unknown_rrset_trust_value";
}
const char*
sec_status_to_string(enum sec_status s)
{
switch(s) {
case sec_status_unchecked: return "sec_status_unchecked";
case sec_status_bogus: return "sec_status_bogus";
case sec_status_indeterminate: return "sec_status_indeterminate";
case sec_status_insecure: return "sec_status_insecure";
case sec_status_secure_sentinel_fail: return "sec_status_secure_sentinel_fail";
case sec_status_secure: return "sec_status_secure";
}
return "unknown_sec_status_value";
}
void log_rrset_key(enum verbosity_value v, const char* str,
struct ub_packed_rrset_key* rrset)
{
if(verbosity >= v)
log_nametypeclass(v, str, rrset->rk.dname,
ntohs(rrset->rk.type), ntohs(rrset->rk.rrset_class));
}
int packed_rr_to_string(struct ub_packed_rrset_key* rrset, size_t i,
time_t now, char* dest, size_t dest_len)
{
struct packed_rrset_data* d = (struct packed_rrset_data*)rrset->
entry.data;
uint8_t rr[65535];
size_t wlen;
size_t rlen = rrset->rk.dname_len + 2 + 2 + 4 + d->rr_len[i];
time_t adjust = 0;
log_assert(dest_len > 0 && dest);
if(rlen > dest_len) {
dest[0] = 0;
return 0;
}
memmove(rr, rrset->rk.dname, rrset->rk.dname_len);
if(i < d->count)
memmove(rr+rrset->rk.dname_len, &rrset->rk.type, 2);
else sldns_write_uint16(rr+rrset->rk.dname_len, LDNS_RR_TYPE_RRSIG);
memmove(rr+rrset->rk.dname_len+2, &rrset->rk.rrset_class, 2);
adjust = SERVE_ORIGINAL_TTL ? d->ttl_add : now;
if (d->rr_ttl[i] < adjust) adjust = d->rr_ttl[i]; /* Prevent negative TTL overflow */
sldns_write_uint32(rr+rrset->rk.dname_len+4,
(uint32_t)(d->rr_ttl[i]-adjust));
memmove(rr+rrset->rk.dname_len+8, d->rr_data[i], d->rr_len[i]);
wlen = (size_t)sldns_wire2str_rr_buf(rr, rlen, dest, dest_len);
if(wlen >= dest_len) {
/* the output string was truncated */
verbose(VERB_ALGO, "rrbuf failure %d %s", (int)d->rr_len[i], dest);
dest[0] = 0;
return 0;
}
return 1;
}
void log_packed_rrset(enum verbosity_value v, const char* str,
struct ub_packed_rrset_key* rrset)
{
struct packed_rrset_data* d = (struct packed_rrset_data*)rrset->
entry.data;
char buf[65535];
size_t i;
if(verbosity < v)
return;
for(i=0; i<d->count+d->rrsig_count; i++) {
if(!packed_rr_to_string(rrset, i, 0, buf, sizeof(buf))) {
log_info("%s: rr %d wire2str-error", str, (int)i);
} else {
log_info("%s: %s", str, buf);
}
}
}
time_t
ub_packed_rrset_ttl(struct ub_packed_rrset_key* key)
{
struct packed_rrset_data* d = (struct packed_rrset_data*)key->
entry.data;
return d->ttl;
}
struct ub_packed_rrset_key*
packed_rrset_copy_region(struct ub_packed_rrset_key* key,
struct regional* region, time_t now)
{
struct ub_packed_rrset_key* ck = regional_alloc(region,
sizeof(struct ub_packed_rrset_key));
struct packed_rrset_data* d;
struct packed_rrset_data* data = key->entry.data;
size_t dsize, i;
time_t now_control;
if(!ck)
return NULL;
ck->id = key->id;
memset(&ck->entry, 0, sizeof(ck->entry));
ck->entry.hash = key->entry.hash;
ck->entry.key = ck;
ck->rk = key->rk;
ck->rk.dname = regional_alloc_init(region, key->rk.dname,
key->rk.dname_len);
if(!ck->rk.dname)
return NULL;
dsize = packed_rrset_sizeof(data);
d = regional_alloc_init(region, data, dsize);
if(!d)
return NULL;
ck->entry.data = d;
packed_rrset_ptr_fixup(d);
/* make TTLs relative - once per rr */
if(now > 0) {
/* NS RRSets may be here with ttl_add higher than now because
* of the novel ghost attack mitigation i.e., using the
* qstarttime for NS RRSets. In that case make sure that the
* returned TTL is not higher than the original one. */
/* For types other than type NS, auth zone and rpz code
* can have ttl_add values. Also time could conceivably move
* in reverse, due to operator action, and it is prudent
* to not assert on that here.
* So there is no assertion d->ttl_add <= now || type==NS */
now_control = SERVE_ORIGINAL_TTL ? data->ttl_add
: (d->ttl_add > now ? d->ttl_add : now );
for(i=0; i<d->count + d->rrsig_count; i++) {
if(TTL_IS_EXPIRED(d->rr_ttl[i], now_control)) {
d->rr_ttl[i] = EXPIRED_REPLY_TTL_CALC(d->rr_ttl[i], data->ttl_add);
} else d->rr_ttl[i] -= now_control;
}
if(TTL_IS_EXPIRED(d->ttl, now_control)) {
d->ttl = EXPIRED_REPLY_TTL_CALC(d->ttl, data->ttl_add);
} else d->ttl -= now_control;
d->ttl_add = 0; /* TTLs have been made relative */
}
return ck;
}
struct ub_packed_rrset_key*
packed_rrset_copy_alloc(struct ub_packed_rrset_key* key,
struct alloc_cache* alloc, time_t now)
{
struct packed_rrset_data* fd, *dd;
struct ub_packed_rrset_key* dk = alloc_special_obtain(alloc);
if(!dk) return NULL;
fd = (struct packed_rrset_data*)key->entry.data;
dk->entry.hash = key->entry.hash;
dk->rk = key->rk;
dk->rk.dname = (uint8_t*)memdup(key->rk.dname, key->rk.dname_len);
if(!dk->rk.dname) {
alloc_special_release(alloc, dk);
return NULL;
}
dd = (struct packed_rrset_data*)memdup(fd, packed_rrset_sizeof(fd));
if(!dd) {
free(dk->rk.dname);
alloc_special_release(alloc, dk);
return NULL;
}
packed_rrset_ptr_fixup(dd);
dk->entry.data = (void*)dd;
packed_rrset_ttl_add(dd, now);
return dk;
}
int
packed_rrset_find_rr(struct packed_rrset_data* d, uint8_t* rdata, size_t len,
size_t* index)
{
size_t i;
for(i=0; i<d->count; i++) {
if(d->rr_len[i] != len)
continue;
if(memcmp(d->rr_data[i], rdata, len) == 0) {
*index = i;
return 1;
}
}
return 0;
}