xref: /openbsd-src/usr.sbin/nsd/packet.c (revision 308d25095010cc66b1b67286e27e62e265360b59)
162ac0c33Sjakob /*
262ac0c33Sjakob  * packet.c -- low-level DNS packet encoding and decoding functions.
362ac0c33Sjakob  *
4d3fecca9Ssthen  * Copyright (c) 2001-2006, NLnet Labs. All rights reserved.
562ac0c33Sjakob  *
662ac0c33Sjakob  * See LICENSE for the license.
762ac0c33Sjakob  *
862ac0c33Sjakob  */
962ac0c33Sjakob 
102c1ae072Ssthen #include "config.h"
1162ac0c33Sjakob 
1262ac0c33Sjakob #include <string.h>
1362ac0c33Sjakob 
1462ac0c33Sjakob #include "packet.h"
1562ac0c33Sjakob #include "query.h"
1662ac0c33Sjakob #include "rdata.h"
1762ac0c33Sjakob 
1815ed76cbSbrad int round_robin = 0;
19db7d0d02Sflorian int minimal_responses = 0;
2015ed76cbSbrad 
2162ac0c33Sjakob static void
encode_dname(query_type * q,domain_type * domain)2262ac0c33Sjakob encode_dname(query_type *q, domain_type *domain)
2362ac0c33Sjakob {
2462ac0c33Sjakob 	while (domain->parent && query_get_dname_offset(q, domain) == 0) {
2562ac0c33Sjakob 		query_put_dname_offset(q, domain, buffer_position(q->packet));
2662ac0c33Sjakob 		DEBUG(DEBUG_NAME_COMPRESSION, 2,
2762ac0c33Sjakob 		      (LOG_INFO, "dname: %s, number: %lu, offset: %u\n",
28d3fecca9Ssthen 		       domain_to_string(domain),
2962ac0c33Sjakob 		       (unsigned long) domain->number,
3062ac0c33Sjakob 		       query_get_dname_offset(q, domain)));
3162ac0c33Sjakob 		buffer_write(q->packet, dname_name(domain_dname(domain)),
3262ac0c33Sjakob 			     label_length(dname_name(domain_dname(domain))) + 1U);
3362ac0c33Sjakob 		domain = domain->parent;
3462ac0c33Sjakob 	}
3562ac0c33Sjakob 	if (domain->parent) {
3662ac0c33Sjakob 		DEBUG(DEBUG_NAME_COMPRESSION, 2,
3762ac0c33Sjakob 		      (LOG_INFO, "dname: %s, number: %lu, pointer: %u\n",
38d3fecca9Ssthen 		       domain_to_string(domain),
3962ac0c33Sjakob 		       (unsigned long) domain->number,
4062ac0c33Sjakob 		       query_get_dname_offset(q, domain)));
4162ac0c33Sjakob 		assert(query_get_dname_offset(q, domain) <= MAX_COMPRESSION_OFFSET);
4262ac0c33Sjakob 		buffer_write_u16(q->packet,
4362ac0c33Sjakob 				 0xc000 | query_get_dname_offset(q, domain));
4462ac0c33Sjakob 	} else {
4562ac0c33Sjakob 		buffer_write_u8(q->packet, 0);
4662ac0c33Sjakob 	}
4762ac0c33Sjakob }
4862ac0c33Sjakob 
4962ac0c33Sjakob int
packet_encode_rr(query_type * q,domain_type * owner,rr_type * rr,uint32_t ttl)50cdb6bbddSbrad packet_encode_rr(query_type *q, domain_type *owner, rr_type *rr, uint32_t ttl)
5162ac0c33Sjakob {
5262ac0c33Sjakob 	size_t truncation_mark;
5362ac0c33Sjakob 	uint16_t rdlength = 0;
5462ac0c33Sjakob 	size_t rdlength_pos;
5562ac0c33Sjakob 	uint16_t j;
5662ac0c33Sjakob 
5762ac0c33Sjakob 	assert(q);
5862ac0c33Sjakob 	assert(owner);
5962ac0c33Sjakob 	assert(rr);
6062ac0c33Sjakob 
6162ac0c33Sjakob 	/*
6262ac0c33Sjakob 	 * If the record does not in fit in the packet the packet size
6362ac0c33Sjakob 	 * will be restored to the mark.
6462ac0c33Sjakob 	 */
6562ac0c33Sjakob 	truncation_mark = buffer_position(q->packet);
6662ac0c33Sjakob 
6762ac0c33Sjakob 	encode_dname(q, owner);
6862ac0c33Sjakob 	buffer_write_u16(q->packet, rr->type);
6962ac0c33Sjakob 	buffer_write_u16(q->packet, rr->klass);
70cdb6bbddSbrad 	buffer_write_u32(q->packet, ttl);
7162ac0c33Sjakob 
7262ac0c33Sjakob 	/* Reserve space for rdlength. */
7362ac0c33Sjakob 	rdlength_pos = buffer_position(q->packet);
7462ac0c33Sjakob 	buffer_skip(q->packet, sizeof(rdlength));
7562ac0c33Sjakob 
7662ac0c33Sjakob 	for (j = 0; j < rr->rdata_count; ++j) {
7762ac0c33Sjakob 		switch (rdata_atom_wireformat_type(rr->type, j)) {
7862ac0c33Sjakob 		case RDATA_WF_COMPRESSED_DNAME:
7962ac0c33Sjakob 			encode_dname(q, rdata_atom_domain(rr->rdatas[j]));
8062ac0c33Sjakob 			break;
8162ac0c33Sjakob 		case RDATA_WF_UNCOMPRESSED_DNAME:
8262ac0c33Sjakob 		{
8362ac0c33Sjakob 			const dname_type *dname = domain_dname(
8462ac0c33Sjakob 				rdata_atom_domain(rr->rdatas[j]));
8562ac0c33Sjakob 			buffer_write(q->packet,
8662ac0c33Sjakob 				     dname_name(dname), dname->name_size);
8762ac0c33Sjakob 			break;
8862ac0c33Sjakob 		}
8962ac0c33Sjakob 		default:
9062ac0c33Sjakob 			buffer_write(q->packet,
9162ac0c33Sjakob 				     rdata_atom_data(rr->rdatas[j]),
9262ac0c33Sjakob 				     rdata_atom_size(rr->rdatas[j]));
9362ac0c33Sjakob 			break;
9462ac0c33Sjakob 		}
9562ac0c33Sjakob 	}
9662ac0c33Sjakob 
9762ac0c33Sjakob 	if (!query_overflow(q)) {
9862ac0c33Sjakob 		rdlength = (buffer_position(q->packet) - rdlength_pos
9962ac0c33Sjakob 			    - sizeof(rdlength));
10062ac0c33Sjakob 		buffer_write_u16_at(q->packet, rdlength_pos, rdlength);
10162ac0c33Sjakob 		return 1;
10262ac0c33Sjakob 	} else {
10362ac0c33Sjakob 		buffer_set_position(q->packet, truncation_mark);
10462ac0c33Sjakob 		query_clear_dname_offsets(q, truncation_mark);
10562ac0c33Sjakob 		assert(!query_overflow(q));
10662ac0c33Sjakob 		return 0;
10762ac0c33Sjakob 	}
10862ac0c33Sjakob }
10962ac0c33Sjakob 
11062ac0c33Sjakob int
packet_encode_rrset(query_type * query,domain_type * owner,rrset_type * rrset,int section,size_t minimal_respsize,int * done)11162ac0c33Sjakob packet_encode_rrset(query_type *query,
11262ac0c33Sjakob 		    domain_type *owner,
11362ac0c33Sjakob 		    rrset_type *rrset,
1140c2b6c02Sjakob 		    int section,
1150c2b6c02Sjakob #ifdef MINIMAL_RESPONSES
1160c2b6c02Sjakob 		    size_t minimal_respsize,
1170c2b6c02Sjakob 		    int* done)
1180c2b6c02Sjakob #else
1190c2b6c02Sjakob 		    size_t ATTR_UNUSED(minimal_respsize),
1200c2b6c02Sjakob 		    int* ATTR_UNUSED(done))
1210c2b6c02Sjakob #endif
12262ac0c33Sjakob {
12362ac0c33Sjakob 	uint16_t i;
12462ac0c33Sjakob 	size_t truncation_mark;
12562ac0c33Sjakob 	uint16_t added = 0;
12662ac0c33Sjakob 	int all_added = 1;
1270c2b6c02Sjakob #ifdef MINIMAL_RESPONSES
1280c2b6c02Sjakob 	int minimize_response = (section >= OPTIONAL_AUTHORITY_SECTION);
12962ac0c33Sjakob 	int truncate_rrset = (section == ANSWER_SECTION ||
13062ac0c33Sjakob 				section == AUTHORITY_SECTION);
1310c2b6c02Sjakob #else
1320c2b6c02Sjakob 	int truncate_rrset = (section == ANSWER_SECTION ||
1330c2b6c02Sjakob 				section == AUTHORITY_SECTION ||
1340c2b6c02Sjakob 				section == OPTIONAL_AUTHORITY_SECTION);
1350c2b6c02Sjakob #endif
13615ed76cbSbrad 	static int round_robin_off = 0;
13715ed76cbSbrad 	int do_robin = (round_robin && section == ANSWER_SECTION &&
13815ed76cbSbrad 		query->qtype != TYPE_AXFR && query->qtype != TYPE_IXFR);
13915ed76cbSbrad 	uint16_t start;
14062ac0c33Sjakob 	rrset_type *rrsig;
14162ac0c33Sjakob 
14262ac0c33Sjakob 	assert(rrset->rr_count > 0);
14362ac0c33Sjakob 
14462ac0c33Sjakob 	truncation_mark = buffer_position(query->packet);
14562ac0c33Sjakob 
14615ed76cbSbrad 	if(do_robin && rrset->rr_count)
14715ed76cbSbrad 		start = (uint16_t)(round_robin_off++ % rrset->rr_count);
14815ed76cbSbrad 	else	start = 0;
14915ed76cbSbrad 	for (i = start; i < rrset->rr_count; ++i) {
15015ed76cbSbrad 		if (packet_encode_rr(query, owner, &rrset->rrs[i],
15115ed76cbSbrad 			rrset->rrs[i].ttl)) {
15215ed76cbSbrad 			++added;
15315ed76cbSbrad 		} else {
15415ed76cbSbrad 			all_added = 0;
15515ed76cbSbrad 			start = 0;
15615ed76cbSbrad 			break;
15715ed76cbSbrad 		}
15815ed76cbSbrad 	}
15915ed76cbSbrad 	for (i = 0; i < start; ++i) {
160cdb6bbddSbrad 		if (packet_encode_rr(query, owner, &rrset->rrs[i],
161cdb6bbddSbrad 			rrset->rrs[i].ttl)) {
16262ac0c33Sjakob 			++added;
16362ac0c33Sjakob 		} else {
16462ac0c33Sjakob 			all_added = 0;
16562ac0c33Sjakob 			break;
16662ac0c33Sjakob 		}
16762ac0c33Sjakob 	}
16862ac0c33Sjakob 
16962ac0c33Sjakob 	if (all_added &&
17062ac0c33Sjakob 	    query->edns.dnssec_ok &&
17162ac0c33Sjakob 	    zone_is_secure(rrset->zone) &&
17262ac0c33Sjakob 	    rrset_rrtype(rrset) != TYPE_RRSIG &&
17362ac0c33Sjakob 	    (rrsig = domain_find_rrset(owner, rrset->zone, TYPE_RRSIG)))
17462ac0c33Sjakob 	{
17562ac0c33Sjakob 		for (i = 0; i < rrsig->rr_count; ++i) {
17662ac0c33Sjakob 			if (rr_rrsig_type_covered(&rrsig->rrs[i])
17762ac0c33Sjakob 			    == rrset_rrtype(rrset))
17862ac0c33Sjakob 			{
17962ac0c33Sjakob 				if (packet_encode_rr(query, owner,
180cdb6bbddSbrad 					&rrsig->rrs[i],
181cdb6bbddSbrad 					rrset_rrtype(rrset)==TYPE_SOA?rrset->rrs[0].ttl:rrsig->rrs[i].ttl))
18262ac0c33Sjakob 				{
18362ac0c33Sjakob 					++added;
18462ac0c33Sjakob 				} else {
18562ac0c33Sjakob 					all_added = 0;
18662ac0c33Sjakob 					break;
18762ac0c33Sjakob 				}
18862ac0c33Sjakob 			}
18962ac0c33Sjakob 		}
19062ac0c33Sjakob 	}
19162ac0c33Sjakob 
1920c2b6c02Sjakob #ifdef MINIMAL_RESPONSES
1930c2b6c02Sjakob 	if ((!all_added || buffer_position(query->packet) > minimal_respsize)
1940c2b6c02Sjakob 	    && !query->tcp && minimize_response) {
1950c2b6c02Sjakob 		/* Truncate entire RRset. */
1960c2b6c02Sjakob 		buffer_set_position(query->packet, truncation_mark);
1970c2b6c02Sjakob 		query_clear_dname_offsets(query, truncation_mark);
1980c2b6c02Sjakob 		added = 0;
1990c2b6c02Sjakob 		*done = 1;
2000c2b6c02Sjakob 	}
2010c2b6c02Sjakob #endif
2020c2b6c02Sjakob 
20362ac0c33Sjakob 	if (!all_added && truncate_rrset) {
20462ac0c33Sjakob 		/* Truncate entire RRset and set truncate flag. */
20562ac0c33Sjakob 		buffer_set_position(query->packet, truncation_mark);
20662ac0c33Sjakob 		query_clear_dname_offsets(query, truncation_mark);
20762ac0c33Sjakob 		TC_SET(query->packet);
20862ac0c33Sjakob 		added = 0;
20962ac0c33Sjakob 	}
21062ac0c33Sjakob 
21162ac0c33Sjakob 	return added;
21262ac0c33Sjakob }
21362ac0c33Sjakob 
21462ac0c33Sjakob int
packet_skip_dname(buffer_type * packet)21562ac0c33Sjakob packet_skip_dname(buffer_type *packet)
21662ac0c33Sjakob {
21762ac0c33Sjakob 	while (1) {
21862ac0c33Sjakob 		uint8_t label_size;
21962ac0c33Sjakob 		if (!buffer_available(packet, 1))
22062ac0c33Sjakob 			return 0;
22162ac0c33Sjakob 
22262ac0c33Sjakob 		label_size = buffer_read_u8(packet);
22362ac0c33Sjakob 		if (label_size == 0) {
22462ac0c33Sjakob 			return 1;
22562ac0c33Sjakob 		} else if ((label_size & 0xc0) != 0) {
22662ac0c33Sjakob 			if (!buffer_available(packet, 1))
22762ac0c33Sjakob 				return 0;
22862ac0c33Sjakob 			buffer_skip(packet, 1);
22962ac0c33Sjakob 			return 1;
23062ac0c33Sjakob 		} else if (!buffer_available(packet, label_size)) {
23162ac0c33Sjakob 			return 0;
23262ac0c33Sjakob 		} else {
23362ac0c33Sjakob 			buffer_skip(packet, label_size);
23462ac0c33Sjakob 		}
23562ac0c33Sjakob 	}
23662ac0c33Sjakob }
23762ac0c33Sjakob 
23862ac0c33Sjakob int
packet_skip_rr(buffer_type * packet,int question_section)23962ac0c33Sjakob packet_skip_rr(buffer_type *packet, int question_section)
24062ac0c33Sjakob {
24162ac0c33Sjakob 	if (!packet_skip_dname(packet))
24262ac0c33Sjakob 		return 0;
24362ac0c33Sjakob 
24462ac0c33Sjakob 	if (question_section) {
24562ac0c33Sjakob 		if (!buffer_available(packet, 4))
24662ac0c33Sjakob 			return 0;
24762ac0c33Sjakob 		buffer_skip(packet, 4);
24862ac0c33Sjakob 	} else {
24962ac0c33Sjakob 		uint16_t rdata_size;
25062ac0c33Sjakob 		if (!buffer_available(packet, 10))
25162ac0c33Sjakob 			return 0;
25262ac0c33Sjakob 		buffer_skip(packet, 8);
25362ac0c33Sjakob 		rdata_size = buffer_read_u16(packet);
25462ac0c33Sjakob 		if (!buffer_available(packet, rdata_size))
25562ac0c33Sjakob 			return 0;
25662ac0c33Sjakob 		buffer_skip(packet, rdata_size);
25762ac0c33Sjakob 	}
25862ac0c33Sjakob 
25962ac0c33Sjakob 	return 1;
26062ac0c33Sjakob }
26162ac0c33Sjakob 
26262ac0c33Sjakob rr_type *
packet_read_rr(region_type * region,domain_table_type * owners,buffer_type * packet,int question_section)26362ac0c33Sjakob packet_read_rr(region_type *region, domain_table_type *owners,
26462ac0c33Sjakob 	       buffer_type *packet, int question_section)
26562ac0c33Sjakob {
26662ac0c33Sjakob 	const dname_type *owner;
26762ac0c33Sjakob 	uint16_t rdlength;
26862ac0c33Sjakob 	ssize_t rdata_count;
26962ac0c33Sjakob 	rdata_atom_type *rdatas;
27062ac0c33Sjakob 	rr_type *result = (rr_type *) region_alloc(region, sizeof(rr_type));
27162ac0c33Sjakob 
27262ac0c33Sjakob 	owner = dname_make_from_packet(region, packet, 1, 1);
27362ac0c33Sjakob 	if (!owner || !buffer_available(packet, 2*sizeof(uint16_t))) {
27462ac0c33Sjakob 		return NULL;
27562ac0c33Sjakob 	}
27662ac0c33Sjakob 
27762ac0c33Sjakob 	result->owner = domain_table_insert(owners, owner);
27862ac0c33Sjakob 	result->type = buffer_read_u16(packet);
27962ac0c33Sjakob 	result->klass = buffer_read_u16(packet);
28062ac0c33Sjakob 
28162ac0c33Sjakob 	if (question_section) {
28262ac0c33Sjakob 		result->ttl = 0;
28362ac0c33Sjakob 		result->rdata_count = 0;
28462ac0c33Sjakob 		result->rdatas = NULL;
28562ac0c33Sjakob 		return result;
28662ac0c33Sjakob 	} else if (!buffer_available(packet, sizeof(uint32_t) + sizeof(uint16_t))) {
28762ac0c33Sjakob 		return NULL;
28862ac0c33Sjakob 	}
28962ac0c33Sjakob 
29062ac0c33Sjakob 	result->ttl = buffer_read_u32(packet);
29162ac0c33Sjakob 	rdlength = buffer_read_u16(packet);
29262ac0c33Sjakob 
29362ac0c33Sjakob 	if (!buffer_available(packet, rdlength)) {
29462ac0c33Sjakob 		return NULL;
29562ac0c33Sjakob 	}
29662ac0c33Sjakob 
29762ac0c33Sjakob 	rdata_count = rdata_wireformat_to_rdata_atoms(
29862ac0c33Sjakob 		region, owners, result->type, rdlength, packet, &rdatas);
29962ac0c33Sjakob 	if (rdata_count == -1) {
30062ac0c33Sjakob 		return NULL;
30162ac0c33Sjakob 	}
30262ac0c33Sjakob 	result->rdata_count = rdata_count;
30362ac0c33Sjakob 	result->rdatas = rdatas;
30462ac0c33Sjakob 
30562ac0c33Sjakob 	return result;
30662ac0c33Sjakob }
30762ac0c33Sjakob 
packet_read_query_section(buffer_type * packet,uint8_t * dst,uint16_t * qtype,uint16_t * qclass)30862ac0c33Sjakob int packet_read_query_section(buffer_type *packet,
30962ac0c33Sjakob 	uint8_t* dst, uint16_t* qtype, uint16_t* qclass)
31062ac0c33Sjakob {
31162ac0c33Sjakob 	uint8_t *query_name = buffer_current(packet);
31262ac0c33Sjakob 	uint8_t *src = query_name;
31362ac0c33Sjakob 	size_t len;
31462ac0c33Sjakob 
31562ac0c33Sjakob 	while (*src) {
31662ac0c33Sjakob 		/*
31762ac0c33Sjakob 		 * If we are out of buffer limits or we have a pointer
31862ac0c33Sjakob 		 * in question dname or the domain name is longer than
31962ac0c33Sjakob 		 * MAXDOMAINLEN ...
32062ac0c33Sjakob 		 */
32162ac0c33Sjakob 		if ((*src & 0xc0) ||
32262ac0c33Sjakob 		    (src + *src + 2 > buffer_end(packet)) ||
32362ac0c33Sjakob 		    (src + *src + 2 > query_name + MAXDOMAINLEN))
32462ac0c33Sjakob 		{
32562ac0c33Sjakob 			return 0;
32662ac0c33Sjakob 		}
32762ac0c33Sjakob 		memcpy(dst, src, *src + 1);
32862ac0c33Sjakob 		dst += *src + 1;
32962ac0c33Sjakob 		src += *src + 1;
33062ac0c33Sjakob 	}
33162ac0c33Sjakob 	*dst++ = *src++;
33262ac0c33Sjakob 
33362ac0c33Sjakob 	/* Make sure name is not too long or we have stripped packet... */
33462ac0c33Sjakob 	len = src - query_name;
33562ac0c33Sjakob 	if (len > MAXDOMAINLEN ||
33662ac0c33Sjakob 	    (src + 2*sizeof(uint16_t) > buffer_end(packet)))
33762ac0c33Sjakob 	{
33862ac0c33Sjakob 		return 0;
33962ac0c33Sjakob 	}
34062ac0c33Sjakob 	buffer_set_position(packet, src - buffer_begin(packet));
34162ac0c33Sjakob 
34262ac0c33Sjakob 	*qtype = buffer_read_u16(packet);
34362ac0c33Sjakob 	*qclass = buffer_read_u16(packet);
34462ac0c33Sjakob 	return 1;
34562ac0c33Sjakob }
346c939baa4Ssthen 
packet_find_notify_serial(buffer_type * packet,uint32_t * serial)347c939baa4Ssthen int packet_find_notify_serial(buffer_type *packet, uint32_t* serial)
348c939baa4Ssthen {
349c939baa4Ssthen 	size_t saved_position = buffer_position(packet);
350c939baa4Ssthen 	/* count of further RRs after question section */
351*308d2509Sflorian 	size_t rrcount = (size_t)ANCOUNT(packet) + (size_t)NSCOUNT(packet) + (size_t)ARCOUNT(packet);
352*308d2509Sflorian 	size_t qcount = (size_t)QDCOUNT(packet);
353c939baa4Ssthen 	size_t i;
354c939baa4Ssthen 	buffer_set_position(packet, QHEADERSZ);
355*308d2509Sflorian 	if(qcount > 64 || rrcount > 65530) {
356*308d2509Sflorian 		/* query count 0 or 1 only, rr number limited by 64k packet,
357*308d2509Sflorian 		 * and should not be impossibly high, parse error */
358*308d2509Sflorian 		buffer_set_position(packet, saved_position);
359*308d2509Sflorian 		return 0;
360*308d2509Sflorian 	}
361c939baa4Ssthen 
362c939baa4Ssthen 	/* skip all question RRs */
363*308d2509Sflorian 	for (i = 0; i < qcount; ++i) {
364c939baa4Ssthen 		if (!packet_skip_rr(packet, 1)) {
365c939baa4Ssthen 			buffer_set_position(packet, saved_position);
366c939baa4Ssthen 			return 0;
367c939baa4Ssthen 		}
368c939baa4Ssthen 	}
369c939baa4Ssthen 
370c939baa4Ssthen 	/* Find the SOA RR */
371c939baa4Ssthen 	for(i = 0; i < rrcount; i++) {
372c939baa4Ssthen 		uint16_t rdata_size;
373c939baa4Ssthen 		if (!packet_skip_dname(packet))
374c939baa4Ssthen 			break;
375c939baa4Ssthen 		/* check length available for type,class,ttl,rdatalen */
376c939baa4Ssthen 		if (!buffer_available(packet, 10))
377c939baa4Ssthen 			break;
378c939baa4Ssthen 		/* check type, class */
379c939baa4Ssthen 		if(buffer_read_u16(packet) == TYPE_SOA) {
380c939baa4Ssthen 			if(buffer_read_u16(packet) != CLASS_IN)
381c939baa4Ssthen 				break;
382c939baa4Ssthen 			buffer_skip(packet, 4); /* skip ttl */
383c939baa4Ssthen 			rdata_size = buffer_read_u16(packet);
384c939baa4Ssthen 			if (!buffer_available(packet, rdata_size))
385c939baa4Ssthen 				break;
386c939baa4Ssthen 			/* skip two dnames, then serial */
387c939baa4Ssthen 			if (!packet_skip_dname(packet) ||
388c939baa4Ssthen 				!packet_skip_dname(packet))
389c939baa4Ssthen 				break;
390c939baa4Ssthen 			if (!buffer_available(packet, 4))
391c939baa4Ssthen 				break;
392c939baa4Ssthen 			*serial = buffer_read_u32(packet);
393c939baa4Ssthen 			buffer_set_position(packet, saved_position);
394c939baa4Ssthen 			return 1;
395c939baa4Ssthen 		}
396c939baa4Ssthen 		/* continue to next RR */
397c939baa4Ssthen 		buffer_skip(packet, 6);
398c939baa4Ssthen 		rdata_size = buffer_read_u16(packet);
399c939baa4Ssthen 		if (!buffer_available(packet, rdata_size))
400c939baa4Ssthen 			break;
401c939baa4Ssthen 		buffer_skip(packet, rdata_size);
402c939baa4Ssthen 	}
403c939baa4Ssthen 	/* failed to find SOA */
404c939baa4Ssthen 	buffer_set_position(packet, saved_position);
405c939baa4Ssthen 	return 0;
406c939baa4Ssthen }
407