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 dname_to_string(domain_dname(domain), NULL), 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 dname_to_string(domain_dname(domain), NULL), 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) 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, rr->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 { 113 uint16_t i; 114 size_t truncation_mark; 115 uint16_t added = 0; 116 int all_added = 1; 117 int truncate_rrset = (section == ANSWER_SECTION || 118 section == AUTHORITY_SECTION); 119 rrset_type *rrsig; 120 121 assert(rrset->rr_count > 0); 122 123 truncation_mark = buffer_position(query->packet); 124 125 for (i = 0; i < rrset->rr_count; ++i) { 126 if (packet_encode_rr(query, owner, &rrset->rrs[i])) { 127 ++added; 128 } else { 129 all_added = 0; 130 break; 131 } 132 } 133 134 if (all_added && 135 query->edns.dnssec_ok && 136 zone_is_secure(rrset->zone) && 137 rrset_rrtype(rrset) != TYPE_RRSIG && 138 (rrsig = domain_find_rrset(owner, rrset->zone, TYPE_RRSIG))) 139 { 140 for (i = 0; i < rrsig->rr_count; ++i) { 141 if (rr_rrsig_type_covered(&rrsig->rrs[i]) 142 == rrset_rrtype(rrset)) 143 { 144 if (packet_encode_rr(query, owner, 145 &rrsig->rrs[i])) 146 { 147 ++added; 148 } else { 149 all_added = 0; 150 break; 151 } 152 } 153 } 154 } 155 156 if (!all_added && truncate_rrset) { 157 /* Truncate entire RRset and set truncate flag. */ 158 buffer_set_position(query->packet, truncation_mark); 159 query_clear_dname_offsets(query, truncation_mark); 160 TC_SET(query->packet); 161 added = 0; 162 } 163 164 return added; 165 } 166 167 int 168 packet_skip_dname(buffer_type *packet) 169 { 170 while (1) { 171 uint8_t label_size; 172 if (!buffer_available(packet, 1)) 173 return 0; 174 175 label_size = buffer_read_u8(packet); 176 if (label_size == 0) { 177 return 1; 178 } else if ((label_size & 0xc0) != 0) { 179 if (!buffer_available(packet, 1)) 180 return 0; 181 buffer_skip(packet, 1); 182 return 1; 183 } else if (!buffer_available(packet, label_size)) { 184 return 0; 185 } else { 186 buffer_skip(packet, label_size); 187 } 188 } 189 } 190 191 int 192 packet_skip_rr(buffer_type *packet, int question_section) 193 { 194 if (!packet_skip_dname(packet)) 195 return 0; 196 197 if (question_section) { 198 if (!buffer_available(packet, 4)) 199 return 0; 200 buffer_skip(packet, 4); 201 } else { 202 uint16_t rdata_size; 203 if (!buffer_available(packet, 10)) 204 return 0; 205 buffer_skip(packet, 8); 206 rdata_size = buffer_read_u16(packet); 207 if (!buffer_available(packet, rdata_size)) 208 return 0; 209 buffer_skip(packet, rdata_size); 210 } 211 212 return 1; 213 } 214 215 rr_type * 216 packet_read_rr(region_type *region, domain_table_type *owners, 217 buffer_type *packet, int question_section) 218 { 219 const dname_type *owner; 220 uint16_t rdlength; 221 ssize_t rdata_count; 222 rdata_atom_type *rdatas; 223 rr_type *result = (rr_type *) region_alloc(region, sizeof(rr_type)); 224 225 owner = dname_make_from_packet(region, packet, 1, 1); 226 if (!owner || !buffer_available(packet, 2*sizeof(uint16_t))) { 227 return NULL; 228 } 229 230 result->owner = domain_table_insert(owners, owner); 231 result->type = buffer_read_u16(packet); 232 result->klass = buffer_read_u16(packet); 233 234 if (question_section) { 235 result->ttl = 0; 236 result->rdata_count = 0; 237 result->rdatas = NULL; 238 return result; 239 } else if (!buffer_available(packet, sizeof(uint32_t) + sizeof(uint16_t))) { 240 return NULL; 241 } 242 243 result->ttl = buffer_read_u32(packet); 244 rdlength = buffer_read_u16(packet); 245 246 if (!buffer_available(packet, rdlength)) { 247 return NULL; 248 } 249 250 rdata_count = rdata_wireformat_to_rdata_atoms( 251 region, owners, result->type, rdlength, packet, &rdatas); 252 if (rdata_count == -1) { 253 return NULL; 254 } 255 result->rdata_count = rdata_count; 256 result->rdatas = rdatas; 257 258 return result; 259 } 260 261 int packet_read_query_section(buffer_type *packet, 262 uint8_t* dst, uint16_t* qtype, uint16_t* qclass) 263 { 264 uint8_t *query_name = buffer_current(packet); 265 uint8_t *src = query_name; 266 size_t len; 267 268 while (*src) { 269 /* 270 * If we are out of buffer limits or we have a pointer 271 * in question dname or the domain name is longer than 272 * MAXDOMAINLEN ... 273 */ 274 if ((*src & 0xc0) || 275 (src + *src + 2 > buffer_end(packet)) || 276 (src + *src + 2 > query_name + MAXDOMAINLEN)) 277 { 278 return 0; 279 } 280 memcpy(dst, src, *src + 1); 281 dst += *src + 1; 282 src += *src + 1; 283 } 284 *dst++ = *src++; 285 286 /* Make sure name is not too long or we have stripped packet... */ 287 len = src - query_name; 288 if (len > MAXDOMAINLEN || 289 (src + 2*sizeof(uint16_t) > buffer_end(packet))) 290 { 291 return 0; 292 } 293 buffer_set_position(packet, src - buffer_begin(packet)); 294 295 *qtype = buffer_read_u16(packet); 296 *qclass = buffer_read_u16(packet); 297 return 1; 298 } 299