xref: /openbsd-src/usr.sbin/nsd/packet.c (revision 50b7afb2c2c0993b0894d4e34bf857cb13ed9c80)
1 /*
2  * packet.c -- low-level DNS packet encoding and decoding functions.
3  *
4  * Copyright (c) 2001-2006, NLnet Labs. All rights reserved.
5  *
6  * See LICENSE for the license.
7  *
8  */
9 
10 #include "config.h"
11 
12 #include <string.h>
13 
14 #include "packet.h"
15 #include "query.h"
16 #include "rdata.h"
17 
18 static void
19 encode_dname(query_type *q, domain_type *domain)
20 {
21 	while (domain->parent && query_get_dname_offset(q, domain) == 0) {
22 		query_put_dname_offset(q, domain, buffer_position(q->packet));
23 		DEBUG(DEBUG_NAME_COMPRESSION, 2,
24 		      (LOG_INFO, "dname: %s, number: %lu, offset: %u\n",
25 		       domain_to_string(domain),
26 		       (unsigned long) domain->number,
27 		       query_get_dname_offset(q, domain)));
28 		buffer_write(q->packet, dname_name(domain_dname(domain)),
29 			     label_length(dname_name(domain_dname(domain))) + 1U);
30 		domain = domain->parent;
31 	}
32 	if (domain->parent) {
33 		DEBUG(DEBUG_NAME_COMPRESSION, 2,
34 		      (LOG_INFO, "dname: %s, number: %lu, pointer: %u\n",
35 		       domain_to_string(domain),
36 		       (unsigned long) domain->number,
37 		       query_get_dname_offset(q, domain)));
38 		assert(query_get_dname_offset(q, domain) <= MAX_COMPRESSION_OFFSET);
39 		buffer_write_u16(q->packet,
40 				 0xc000 | query_get_dname_offset(q, domain));
41 	} else {
42 		buffer_write_u8(q->packet, 0);
43 	}
44 }
45 
46 int
47 packet_encode_rr(query_type *q, domain_type *owner, rr_type *rr, uint32_t ttl)
48 {
49 	size_t truncation_mark;
50 	uint16_t rdlength = 0;
51 	size_t rdlength_pos;
52 	uint16_t j;
53 
54 	assert(q);
55 	assert(owner);
56 	assert(rr);
57 
58 	/*
59 	 * If the record does not in fit in the packet the packet size
60 	 * will be restored to the mark.
61 	 */
62 	truncation_mark = buffer_position(q->packet);
63 
64 	encode_dname(q, owner);
65 	buffer_write_u16(q->packet, rr->type);
66 	buffer_write_u16(q->packet, rr->klass);
67 	buffer_write_u32(q->packet, ttl);
68 
69 	/* Reserve space for rdlength. */
70 	rdlength_pos = buffer_position(q->packet);
71 	buffer_skip(q->packet, sizeof(rdlength));
72 
73 	for (j = 0; j < rr->rdata_count; ++j) {
74 		switch (rdata_atom_wireformat_type(rr->type, j)) {
75 		case RDATA_WF_COMPRESSED_DNAME:
76 			encode_dname(q, rdata_atom_domain(rr->rdatas[j]));
77 			break;
78 		case RDATA_WF_UNCOMPRESSED_DNAME:
79 		{
80 			const dname_type *dname = domain_dname(
81 				rdata_atom_domain(rr->rdatas[j]));
82 			buffer_write(q->packet,
83 				     dname_name(dname), dname->name_size);
84 			break;
85 		}
86 		default:
87 			buffer_write(q->packet,
88 				     rdata_atom_data(rr->rdatas[j]),
89 				     rdata_atom_size(rr->rdatas[j]));
90 			break;
91 		}
92 	}
93 
94 	if (!query_overflow(q)) {
95 		rdlength = (buffer_position(q->packet) - rdlength_pos
96 			    - sizeof(rdlength));
97 		buffer_write_u16_at(q->packet, rdlength_pos, rdlength);
98 		return 1;
99 	} else {
100 		buffer_set_position(q->packet, truncation_mark);
101 		query_clear_dname_offsets(q, truncation_mark);
102 		assert(!query_overflow(q));
103 		return 0;
104 	}
105 }
106 
107 int
108 packet_encode_rrset(query_type *query,
109 		    domain_type *owner,
110 		    rrset_type *rrset,
111 		    int section,
112 #ifdef MINIMAL_RESPONSES
113 		    size_t minimal_respsize,
114 		    int* done)
115 #else
116 		    size_t ATTR_UNUSED(minimal_respsize),
117 		    int* ATTR_UNUSED(done))
118 #endif
119 {
120 	uint16_t i;
121 	size_t truncation_mark;
122 	uint16_t added = 0;
123 	int all_added = 1;
124 #ifdef MINIMAL_RESPONSES
125 	int minimize_response = (section >= OPTIONAL_AUTHORITY_SECTION);
126 	int truncate_rrset = (section == ANSWER_SECTION ||
127 				section == AUTHORITY_SECTION);
128 #else
129 	int truncate_rrset = (section == ANSWER_SECTION ||
130 				section == AUTHORITY_SECTION ||
131 				section == OPTIONAL_AUTHORITY_SECTION);
132 #endif
133 	rrset_type *rrsig;
134 
135 	assert(rrset->rr_count > 0);
136 
137 	truncation_mark = buffer_position(query->packet);
138 
139 	for (i = 0; i < rrset->rr_count; ++i) {
140 		if (packet_encode_rr(query, owner, &rrset->rrs[i],
141 			rrset->rrs[i].ttl)) {
142 			++added;
143 		} else {
144 			all_added = 0;
145 			break;
146 		}
147 	}
148 
149 	if (all_added &&
150 	    query->edns.dnssec_ok &&
151 	    zone_is_secure(rrset->zone) &&
152 	    rrset_rrtype(rrset) != TYPE_RRSIG &&
153 	    (rrsig = domain_find_rrset(owner, rrset->zone, TYPE_RRSIG)))
154 	{
155 		for (i = 0; i < rrsig->rr_count; ++i) {
156 			if (rr_rrsig_type_covered(&rrsig->rrs[i])
157 			    == rrset_rrtype(rrset))
158 			{
159 				if (packet_encode_rr(query, owner,
160 					&rrsig->rrs[i],
161 					rrset_rrtype(rrset)==TYPE_SOA?rrset->rrs[0].ttl:rrsig->rrs[i].ttl))
162 				{
163 					++added;
164 				} else {
165 					all_added = 0;
166 					break;
167 				}
168 			}
169 		}
170 	}
171 
172 #ifdef MINIMAL_RESPONSES
173 	if ((!all_added || buffer_position(query->packet) > minimal_respsize)
174 	    && !query->tcp && minimize_response) {
175 		/* Truncate entire RRset. */
176 		buffer_set_position(query->packet, truncation_mark);
177 		query_clear_dname_offsets(query, truncation_mark);
178 		added = 0;
179 		*done = 1;
180 	}
181 #endif
182 
183 	if (!all_added && truncate_rrset) {
184 		/* Truncate entire RRset and set truncate flag. */
185 		buffer_set_position(query->packet, truncation_mark);
186 		query_clear_dname_offsets(query, truncation_mark);
187 		TC_SET(query->packet);
188 		added = 0;
189 	}
190 
191 	return added;
192 }
193 
194 int
195 packet_skip_dname(buffer_type *packet)
196 {
197 	while (1) {
198 		uint8_t label_size;
199 		if (!buffer_available(packet, 1))
200 			return 0;
201 
202 		label_size = buffer_read_u8(packet);
203 		if (label_size == 0) {
204 			return 1;
205 		} else if ((label_size & 0xc0) != 0) {
206 			if (!buffer_available(packet, 1))
207 				return 0;
208 			buffer_skip(packet, 1);
209 			return 1;
210 		} else if (!buffer_available(packet, label_size)) {
211 			return 0;
212 		} else {
213 			buffer_skip(packet, label_size);
214 		}
215 	}
216 }
217 
218 int
219 packet_skip_rr(buffer_type *packet, int question_section)
220 {
221 	if (!packet_skip_dname(packet))
222 		return 0;
223 
224 	if (question_section) {
225 		if (!buffer_available(packet, 4))
226 			return 0;
227 		buffer_skip(packet, 4);
228 	} else {
229 		uint16_t rdata_size;
230 		if (!buffer_available(packet, 10))
231 			return 0;
232 		buffer_skip(packet, 8);
233 		rdata_size = buffer_read_u16(packet);
234 		if (!buffer_available(packet, rdata_size))
235 			return 0;
236 		buffer_skip(packet, rdata_size);
237 	}
238 
239 	return 1;
240 }
241 
242 rr_type *
243 packet_read_rr(region_type *region, domain_table_type *owners,
244 	       buffer_type *packet, int question_section)
245 {
246 	const dname_type *owner;
247 	uint16_t rdlength;
248 	ssize_t rdata_count;
249 	rdata_atom_type *rdatas;
250 	rr_type *result = (rr_type *) region_alloc(region, sizeof(rr_type));
251 
252 	owner = dname_make_from_packet(region, packet, 1, 1);
253 	if (!owner || !buffer_available(packet, 2*sizeof(uint16_t))) {
254 		return NULL;
255 	}
256 
257 	result->owner = domain_table_insert(owners, owner);
258 	result->type = buffer_read_u16(packet);
259 	result->klass = buffer_read_u16(packet);
260 
261 	if (question_section) {
262 		result->ttl = 0;
263 		result->rdata_count = 0;
264 		result->rdatas = NULL;
265 		return result;
266 	} else if (!buffer_available(packet, sizeof(uint32_t) + sizeof(uint16_t))) {
267 		return NULL;
268 	}
269 
270 	result->ttl = buffer_read_u32(packet);
271 	rdlength = buffer_read_u16(packet);
272 
273 	if (!buffer_available(packet, rdlength)) {
274 		return NULL;
275 	}
276 
277 	rdata_count = rdata_wireformat_to_rdata_atoms(
278 		region, owners, result->type, rdlength, packet, &rdatas);
279 	if (rdata_count == -1) {
280 		return NULL;
281 	}
282 	result->rdata_count = rdata_count;
283 	result->rdatas = rdatas;
284 
285 	return result;
286 }
287 
288 int packet_read_query_section(buffer_type *packet,
289 	uint8_t* dst, uint16_t* qtype, uint16_t* qclass)
290 {
291 	uint8_t *query_name = buffer_current(packet);
292 	uint8_t *src = query_name;
293 	size_t len;
294 
295 	while (*src) {
296 		/*
297 		 * If we are out of buffer limits or we have a pointer
298 		 * in question dname or the domain name is longer than
299 		 * MAXDOMAINLEN ...
300 		 */
301 		if ((*src & 0xc0) ||
302 		    (src + *src + 2 > buffer_end(packet)) ||
303 		    (src + *src + 2 > query_name + MAXDOMAINLEN))
304 		{
305 			return 0;
306 		}
307 		memcpy(dst, src, *src + 1);
308 		dst += *src + 1;
309 		src += *src + 1;
310 	}
311 	*dst++ = *src++;
312 
313 	/* Make sure name is not too long or we have stripped packet... */
314 	len = src - query_name;
315 	if (len > MAXDOMAINLEN ||
316 	    (src + 2*sizeof(uint16_t) > buffer_end(packet)))
317 	{
318 		return 0;
319 	}
320 	buffer_set_position(packet, src - buffer_begin(packet));
321 
322 	*qtype = buffer_read_u16(packet);
323 	*qclass = buffer_read_u16(packet);
324 	return 1;
325 }
326