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