1/*
2 * util/data/packed_rrset.c - data storage for a set of resource records.
3 *
4 * Copyright (c) 2007, NLnet Labs. All rights reserved.
5 *
6 * This software is open source.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * Redistributions of source code must retain the above copyright notice,
13 * this list of conditions and the following disclaimer.
14 *
15 * Redistributions in binary form must reproduce the above copyright notice,
16 * this list of conditions and the following disclaimer in the documentation
17 * and/or other materials provided with the distribution.
18 *
19 * Neither the name of the NLNET LABS nor the names of its contributors may
20 * be used to endorse or promote products derived from this software without
21 * specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
29 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 */
35
36/**
37 * \file
38 *
39 * This file contains the data storage for RRsets.
40 */
41
42#include "config.h"
43#include "util/data/msgparse.h"
44#include "util/data/packed_rrset.h"
45#include "util/data/dname.h"
46#include "util/storage/lookup3.h"
47#include "util/log.h"
48#include "util/alloc.h"
49#include "util/regional.h"
50#include "util/net_help.h"
51#include "sldns/rrdef.h"
52#include "sldns/sbuffer.h"
53#include "sldns/wire2str.h"
54
55void
56ub_packed_rrset_parsedelete(struct ub_packed_rrset_key* pkey,
57        struct alloc_cache* alloc)
58{
59	if(!pkey)
60		return;
61	free(pkey->entry.data);
62	pkey->entry.data = NULL;
63	free(pkey->rk.dname);
64	pkey->rk.dname = NULL;
65	pkey->id = 0;
66	alloc_special_release(alloc, pkey);
67}
68
69size_t
70ub_rrset_sizefunc(void* key, void* data)
71{
72	struct ub_packed_rrset_key* k = (struct ub_packed_rrset_key*)key;
73	struct packed_rrset_data* d = (struct packed_rrset_data*)data;
74	size_t s = sizeof(struct ub_packed_rrset_key) + k->rk.dname_len;
75	s += packed_rrset_sizeof(d) + lock_get_mem(&k->entry.lock);
76	return s;
77}
78
79size_t
80packed_rrset_sizeof(struct packed_rrset_data* d)
81{
82	size_t s;
83	if(d->rrsig_count > 0) {
84		s = ((uint8_t*)d->rr_data[d->count+d->rrsig_count-1] -
85			(uint8_t*)d) + d->rr_len[d->count+d->rrsig_count-1];
86	} else {
87		log_assert(d->count > 0);
88		s = ((uint8_t*)d->rr_data[d->count-1] - (uint8_t*)d) +
89			d->rr_len[d->count-1];
90	}
91	return s;
92}
93
94int
95ub_rrset_compare(void* k1, void* k2)
96{
97	struct ub_packed_rrset_key* key1 = (struct ub_packed_rrset_key*)k1;
98	struct ub_packed_rrset_key* key2 = (struct ub_packed_rrset_key*)k2;
99	int c;
100	if(key1 == key2)
101		return 0;
102	if(key1->rk.type != key2->rk.type) {
103		if(key1->rk.type < key2->rk.type)
104			return -1;
105		return 1;
106	}
107	if(key1->rk.dname_len != key2->rk.dname_len) {
108		if(key1->rk.dname_len < key2->rk.dname_len)
109			return -1;
110		return 1;
111	}
112	if((c=query_dname_compare(key1->rk.dname, key2->rk.dname)) != 0)
113		return c;
114	if(key1->rk.rrset_class != key2->rk.rrset_class) {
115		if(key1->rk.rrset_class < key2->rk.rrset_class)
116			return -1;
117		return 1;
118	}
119	if(key1->rk.flags != key2->rk.flags) {
120		if(key1->rk.flags < key2->rk.flags)
121			return -1;
122		return 1;
123	}
124	return 0;
125}
126
127void
128ub_rrset_key_delete(void* key, void* userdata)
129{
130	struct ub_packed_rrset_key* k = (struct ub_packed_rrset_key*)key;
131	struct alloc_cache* a = (struct alloc_cache*)userdata;
132	k->id = 0;
133	free(k->rk.dname);
134	k->rk.dname = NULL;
135	alloc_special_release(a, k);
136}
137
138void
139rrset_data_delete(void* data, void* ATTR_UNUSED(userdata))
140{
141	struct packed_rrset_data* d = (struct packed_rrset_data*)data;
142	free(d);
143}
144
145int
146rrsetdata_equal(struct packed_rrset_data* d1, struct packed_rrset_data* d2)
147{
148	size_t i;
149	size_t total;
150	if(d1->count != d2->count || d1->rrsig_count != d2->rrsig_count)
151		return 0;
152	total = d1->count + d1->rrsig_count;
153	for(i=0; i<total; i++) {
154		if(d1->rr_len[i] != d2->rr_len[i])
155			return 0;
156		if(memcmp(d1->rr_data[i], d2->rr_data[i], d1->rr_len[i]) != 0)
157			return 0;
158	}
159	return 1;
160}
161
162hashvalue_type
163rrset_key_hash(struct packed_rrset_key* key)
164{
165	/* type is hashed in host order */
166	uint16_t t = ntohs(key->type);
167	/* Note this MUST be identical to pkt_hash_rrset in msgparse.c */
168	/* this routine does not have a compressed name */
169	hashvalue_type h = 0xab;
170	h = dname_query_hash(key->dname, h);
171	h = hashlittle(&t, sizeof(t), h);
172	h = hashlittle(&key->rrset_class, sizeof(uint16_t), h);
173	h = hashlittle(&key->flags, sizeof(uint32_t), h);
174	return h;
175}
176
177void
178packed_rrset_ptr_fixup(struct packed_rrset_data* data)
179{
180	size_t i;
181	size_t total = data->count + data->rrsig_count;
182	uint8_t* nextrdata;
183	/* fixup pointers in packed rrset data */
184	data->rr_len = (size_t*)((uint8_t*)data +
185		sizeof(struct packed_rrset_data));
186	data->rr_data = (uint8_t**)&(data->rr_len[total]);
187	data->rr_ttl = (time_t*)&(data->rr_data[total]);
188	nextrdata = (uint8_t*)&(data->rr_ttl[total]);
189	for(i=0; i<total; i++) {
190		data->rr_data[i] = nextrdata;
191		nextrdata += data->rr_len[i];
192	}
193}
194
195void
196get_cname_target(struct ub_packed_rrset_key* rrset, uint8_t** dname,
197	size_t* dname_len)
198{
199	struct packed_rrset_data* d;
200	size_t len;
201	if(ntohs(rrset->rk.type) != LDNS_RR_TYPE_CNAME &&
202		ntohs(rrset->rk.type) != LDNS_RR_TYPE_DNAME)
203		return;
204	d = (struct packed_rrset_data*)rrset->entry.data;
205	if(d->count < 1)
206		return;
207	if(d->rr_len[0] < 3) /* at least rdatalen + 0byte root label */
208		return;
209	len = sldns_read_uint16(d->rr_data[0]);
210	if(len != d->rr_len[0] - sizeof(uint16_t))
211		return;
212	if(dname_valid(d->rr_data[0]+sizeof(uint16_t), len) != len)
213		return;
214	*dname = d->rr_data[0]+sizeof(uint16_t);
215	*dname_len = len;
216}
217
218void
219packed_rrset_ttl_add(struct packed_rrset_data* data, time_t add)
220{
221	size_t i;
222	size_t total = data->count + data->rrsig_count;
223	data->ttl_add = add;
224	data->ttl += add;
225	for(i=0; i<total; i++)
226		data->rr_ttl[i] += add;
227}
228
229const char*
230rrset_trust_to_string(enum rrset_trust s)
231{
232	switch(s) {
233	case rrset_trust_none: 		return "rrset_trust_none";
234	case rrset_trust_add_noAA: 	return "rrset_trust_add_noAA";
235	case rrset_trust_auth_noAA: 	return "rrset_trust_auth_noAA";
236	case rrset_trust_add_AA: 	return "rrset_trust_add_AA";
237	case rrset_trust_nonauth_ans_AA:return "rrset_trust_nonauth_ans_AA";
238	case rrset_trust_ans_noAA: 	return "rrset_trust_ans_noAA";
239	case rrset_trust_glue: 		return "rrset_trust_glue";
240	case rrset_trust_auth_AA: 	return "rrset_trust_auth_AA";
241	case rrset_trust_ans_AA: 	return "rrset_trust_ans_AA";
242	case rrset_trust_sec_noglue: 	return "rrset_trust_sec_noglue";
243	case rrset_trust_prim_noglue: 	return "rrset_trust_prim_noglue";
244	case rrset_trust_validated: 	return "rrset_trust_validated";
245	case rrset_trust_ultimate: 	return "rrset_trust_ultimate";
246	}
247	return "unknown_rrset_trust_value";
248}
249
250const char*
251sec_status_to_string(enum sec_status s)
252{
253	switch(s) {
254	case sec_status_unchecked: 	return "sec_status_unchecked";
255	case sec_status_bogus: 		return "sec_status_bogus";
256	case sec_status_indeterminate: 	return "sec_status_indeterminate";
257	case sec_status_insecure: 	return "sec_status_insecure";
258	case sec_status_secure_sentinel_fail: 	return "sec_status_secure_sentinel_fail";
259	case sec_status_secure: 	return "sec_status_secure";
260	}
261	return "unknown_sec_status_value";
262}
263
264void log_rrset_key(enum verbosity_value v, const char* str,
265	struct ub_packed_rrset_key* rrset)
266{
267	if(verbosity >= v)
268		log_nametypeclass(v, str, rrset->rk.dname,
269			ntohs(rrset->rk.type), ntohs(rrset->rk.rrset_class));
270}
271
272int packed_rr_to_string(struct ub_packed_rrset_key* rrset, size_t i,
273	time_t now, char* dest, size_t dest_len)
274{
275	struct packed_rrset_data* d = (struct packed_rrset_data*)rrset->
276		entry.data;
277	uint8_t rr[65535];
278	size_t wlen;
279	size_t rlen = rrset->rk.dname_len + 2 + 2 + 4 + d->rr_len[i];
280	time_t adjust = 0;
281	log_assert(dest_len > 0 && dest);
282	if(rlen > dest_len) {
283		dest[0] = 0;
284		return 0;
285	}
286	memmove(rr, rrset->rk.dname, rrset->rk.dname_len);
287	if(i < d->count)
288		memmove(rr+rrset->rk.dname_len, &rrset->rk.type, 2);
289	else	sldns_write_uint16(rr+rrset->rk.dname_len, LDNS_RR_TYPE_RRSIG);
290	memmove(rr+rrset->rk.dname_len+2, &rrset->rk.rrset_class, 2);
291	adjust = SERVE_ORIGINAL_TTL ? d->ttl_add : now;
292	if (d->rr_ttl[i] < adjust) adjust = d->rr_ttl[i]; /* Prevent negative TTL overflow */
293	sldns_write_uint32(rr+rrset->rk.dname_len+4,
294		(uint32_t)(d->rr_ttl[i]-adjust));
295	memmove(rr+rrset->rk.dname_len+8, d->rr_data[i], d->rr_len[i]);
296	wlen = (size_t)sldns_wire2str_rr_buf(rr, rlen, dest, dest_len);
297	if(wlen >= dest_len) {
298		/* the output string was truncated */
299		log_info("rrbuf failure %d %s", (int)d->rr_len[i], dest);
300		dest[0] = 0;
301		return 0;
302	}
303	return 1;
304}
305
306void log_packed_rrset(enum verbosity_value v, const char* str,
307	struct ub_packed_rrset_key* rrset)
308{
309	struct packed_rrset_data* d = (struct packed_rrset_data*)rrset->
310		entry.data;
311	char buf[65535];
312	size_t i;
313	if(verbosity < v)
314		return;
315	for(i=0; i<d->count+d->rrsig_count; i++) {
316		if(!packed_rr_to_string(rrset, i, 0, buf, sizeof(buf))) {
317			log_info("%s: rr %d wire2str-error", str, (int)i);
318		} else {
319			log_info("%s: %s", str, buf);
320		}
321	}
322}
323
324time_t
325ub_packed_rrset_ttl(struct ub_packed_rrset_key* key)
326{
327	struct packed_rrset_data* d = (struct packed_rrset_data*)key->
328		entry.data;
329	return d->ttl;
330}
331
332struct ub_packed_rrset_key*
333packed_rrset_copy_region(struct ub_packed_rrset_key* key,
334	struct regional* region, time_t now)
335{
336	struct ub_packed_rrset_key* ck = regional_alloc(region,
337		sizeof(struct ub_packed_rrset_key));
338	struct packed_rrset_data* d;
339	struct packed_rrset_data* data = (struct packed_rrset_data*)
340		key->entry.data;
341	size_t dsize, i;
342	time_t adjust = 0;
343	if(!ck)
344		return NULL;
345	ck->id = key->id;
346	memset(&ck->entry, 0, sizeof(ck->entry));
347	ck->entry.hash = key->entry.hash;
348	ck->entry.key = ck;
349	ck->rk = key->rk;
350	ck->rk.dname = regional_alloc_init(region, key->rk.dname,
351		key->rk.dname_len);
352	if(!ck->rk.dname)
353		return NULL;
354	dsize = packed_rrset_sizeof(data);
355	d = (struct packed_rrset_data*)regional_alloc_init(region, data, dsize);
356	if(!d)
357		return NULL;
358	ck->entry.data = d;
359	packed_rrset_ptr_fixup(d);
360	/* make TTLs relative - once per rrset */
361	adjust = SERVE_ORIGINAL_TTL ? data->ttl_add : now;
362	for(i=0; i<d->count + d->rrsig_count; i++) {
363		if(d->rr_ttl[i] < adjust)
364			d->rr_ttl[i] = SERVE_EXPIRED?SERVE_EXPIRED_REPLY_TTL:0;
365		else	d->rr_ttl[i] -= adjust;
366	}
367	if(d->ttl < adjust)
368		d->ttl = SERVE_EXPIRED?SERVE_EXPIRED_REPLY_TTL:0;
369	else	d->ttl -= adjust;
370	d->ttl_add = 0; /* TTLs have been made relative */
371	return ck;
372}
373
374struct ub_packed_rrset_key*
375packed_rrset_copy_alloc(struct ub_packed_rrset_key* key,
376	struct alloc_cache* alloc, time_t now)
377{
378	struct packed_rrset_data* fd, *dd;
379	struct ub_packed_rrset_key* dk = alloc_special_obtain(alloc);
380	if(!dk) return NULL;
381	fd = (struct packed_rrset_data*)key->entry.data;
382	dk->entry.hash = key->entry.hash;
383	dk->rk = key->rk;
384	dk->rk.dname = (uint8_t*)memdup(key->rk.dname, key->rk.dname_len);
385	if(!dk->rk.dname) {
386		alloc_special_release(alloc, dk);
387		return NULL;
388	}
389	dd = (struct packed_rrset_data*)memdup(fd, packed_rrset_sizeof(fd));
390	if(!dd) {
391		free(dk->rk.dname);
392		alloc_special_release(alloc, dk);
393		return NULL;
394	}
395	packed_rrset_ptr_fixup(dd);
396	dk->entry.data = (void*)dd;
397	packed_rrset_ttl_add(dd, now);
398	return dk;
399}
400
401int
402packed_rrset_find_rr(struct packed_rrset_data* d, uint8_t* rdata, size_t len,
403	size_t* index)
404{
405	size_t i;
406	for(i=0; i<d->count; i++) {
407		if(d->rr_len[i] != len)
408			continue;
409		if(memcmp(d->rr_data[i], rdata, len) == 0) {
410			*index = i;
411			return 1;
412		}
413	}
414	return 0;
415}
416