1 /* 2 * util/data/msgreply.c - store message and reply data. 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 a data structure to store a message and its reply. 40 */ 41 42 #include "config.h" 43 #include "util/data/msgreply.h" 44 #include "util/storage/lookup3.h" 45 #include "util/log.h" 46 #include "util/alloc.h" 47 #include "util/netevent.h" 48 #include "util/net_help.h" 49 #include "util/data/dname.h" 50 #include "util/regional.h" 51 #include "util/data/msgparse.h" 52 #include "util/data/msgencode.h" 53 #include "sldns/sbuffer.h" 54 #include "sldns/wire2str.h" 55 #include "util/module.h" 56 #include "util/fptr_wlist.h" 57 58 /** MAX TTL default for messages and rrsets */ 59 time_t MAX_TTL = 3600 * 24 * 10; /* ten days */ 60 /** MIN TTL default for messages and rrsets */ 61 time_t MIN_TTL = 0; 62 /** MAX Negative TTL, for SOA records in authority section */ 63 time_t MAX_NEG_TTL = 3600; /* one hour */ 64 /** Time to serve records after expiration */ 65 time_t SERVE_EXPIRED_TTL = 0; 66 67 /** allocate qinfo, return 0 on error */ 68 static int 69 parse_create_qinfo(sldns_buffer* pkt, struct msg_parse* msg, 70 struct query_info* qinf, struct regional* region) 71 { 72 if(msg->qname) { 73 if(region) 74 qinf->qname = (uint8_t*)regional_alloc(region, 75 msg->qname_len); 76 else qinf->qname = (uint8_t*)malloc(msg->qname_len); 77 if(!qinf->qname) return 0; 78 dname_pkt_copy(pkt, qinf->qname, msg->qname); 79 } else qinf->qname = 0; 80 qinf->qname_len = msg->qname_len; 81 qinf->qtype = msg->qtype; 82 qinf->qclass = msg->qclass; 83 qinf->local_alias = NULL; 84 return 1; 85 } 86 87 /** constructor for replyinfo */ 88 struct reply_info* 89 construct_reply_info_base(struct regional* region, uint16_t flags, size_t qd, 90 time_t ttl, time_t prettl, time_t expttl, size_t an, size_t ns, 91 size_t ar, size_t total, enum sec_status sec) 92 { 93 struct reply_info* rep; 94 /* rrset_count-1 because the first ref is part of the struct. */ 95 size_t s = sizeof(struct reply_info) - sizeof(struct rrset_ref) + 96 sizeof(struct ub_packed_rrset_key*) * total; 97 if(total >= RR_COUNT_MAX) return NULL; /* sanity check on numRRS*/ 98 if(region) 99 rep = (struct reply_info*)regional_alloc(region, s); 100 else rep = (struct reply_info*)malloc(s + 101 sizeof(struct rrset_ref) * (total)); 102 if(!rep) 103 return NULL; 104 rep->flags = flags; 105 rep->qdcount = qd; 106 rep->ttl = ttl; 107 rep->prefetch_ttl = prettl; 108 rep->serve_expired_ttl = expttl; 109 rep->an_numrrsets = an; 110 rep->ns_numrrsets = ns; 111 rep->ar_numrrsets = ar; 112 rep->rrset_count = total; 113 rep->security = sec; 114 rep->authoritative = 0; 115 /* array starts after the refs */ 116 if(region) 117 rep->rrsets = (struct ub_packed_rrset_key**)&(rep->ref[0]); 118 else rep->rrsets = (struct ub_packed_rrset_key**)&(rep->ref[total]); 119 /* zero the arrays to assist cleanup in case of malloc failure */ 120 memset( rep->rrsets, 0, sizeof(struct ub_packed_rrset_key*) * total); 121 if(!region) 122 memset( &rep->ref[0], 0, sizeof(struct rrset_ref) * total); 123 return rep; 124 } 125 126 /** allocate replyinfo, return 0 on error */ 127 static int 128 parse_create_repinfo(struct msg_parse* msg, struct reply_info** rep, 129 struct regional* region) 130 { 131 *rep = construct_reply_info_base(region, msg->flags, msg->qdcount, 0, 132 0, 0, msg->an_rrsets, msg->ns_rrsets, msg->ar_rrsets, 133 msg->rrset_count, sec_status_unchecked); 134 if(!*rep) 135 return 0; 136 return 1; 137 } 138 139 int 140 reply_info_alloc_rrset_keys(struct reply_info* rep, struct alloc_cache* alloc, 141 struct regional* region) 142 { 143 size_t i; 144 for(i=0; i<rep->rrset_count; i++) { 145 if(region) { 146 rep->rrsets[i] = (struct ub_packed_rrset_key*) 147 regional_alloc(region, 148 sizeof(struct ub_packed_rrset_key)); 149 if(rep->rrsets[i]) { 150 memset(rep->rrsets[i], 0, 151 sizeof(struct ub_packed_rrset_key)); 152 rep->rrsets[i]->entry.key = rep->rrsets[i]; 153 } 154 } 155 else rep->rrsets[i] = alloc_special_obtain(alloc); 156 if(!rep->rrsets[i]) 157 return 0; 158 rep->rrsets[i]->entry.data = NULL; 159 } 160 return 1; 161 } 162 163 /** find the minimumttl in the rdata of SOA record */ 164 static time_t 165 soa_find_minttl(struct rr_parse* rr) 166 { 167 uint16_t rlen = sldns_read_uint16(rr->ttl_data+4); 168 if(rlen < 20) 169 return 0; /* rdata too small for SOA (dname, dname, 5*32bit) */ 170 /* minimum TTL is the last 32bit value in the rdata of the record */ 171 /* at position ttl_data + 4(ttl) + 2(rdatalen) + rdatalen - 4(timeval)*/ 172 return (time_t)sldns_read_uint32(rr->ttl_data+6+rlen-4); 173 } 174 175 /** do the rdata copy */ 176 static int 177 rdata_copy(sldns_buffer* pkt, struct packed_rrset_data* data, uint8_t* to, 178 struct rr_parse* rr, time_t* rr_ttl, uint16_t type, 179 sldns_pkt_section section) 180 { 181 uint16_t pkt_len; 182 const sldns_rr_descriptor* desc; 183 184 *rr_ttl = sldns_read_uint32(rr->ttl_data); 185 /* RFC 2181 Section 8. if msb of ttl is set treat as if zero. */ 186 if(*rr_ttl & 0x80000000U) 187 *rr_ttl = 0; 188 if(type == LDNS_RR_TYPE_SOA && section == LDNS_SECTION_AUTHORITY) { 189 /* negative response. see if TTL of SOA record larger than the 190 * minimum-ttl in the rdata of the SOA record */ 191 if(*rr_ttl > soa_find_minttl(rr)) 192 *rr_ttl = soa_find_minttl(rr); 193 if(*rr_ttl > MAX_NEG_TTL) 194 *rr_ttl = MAX_NEG_TTL; 195 } 196 if(*rr_ttl < MIN_TTL) 197 *rr_ttl = MIN_TTL; 198 if(*rr_ttl > MAX_TTL) 199 *rr_ttl = MAX_TTL; 200 if(*rr_ttl < data->ttl) 201 data->ttl = *rr_ttl; 202 203 if(rr->outside_packet) { 204 /* uncompressed already, only needs copy */ 205 memmove(to, rr->ttl_data+sizeof(uint32_t), rr->size); 206 return 1; 207 } 208 209 sldns_buffer_set_position(pkt, (size_t) 210 (rr->ttl_data - sldns_buffer_begin(pkt) + sizeof(uint32_t))); 211 /* insert decompressed size into rdata len stored in memory */ 212 /* -2 because rdatalen bytes are not included. */ 213 pkt_len = htons(rr->size - 2); 214 memmove(to, &pkt_len, sizeof(uint16_t)); 215 to += 2; 216 /* read packet rdata len */ 217 pkt_len = sldns_buffer_read_u16(pkt); 218 if(sldns_buffer_remaining(pkt) < pkt_len) 219 return 0; 220 desc = sldns_rr_descript(type); 221 if(pkt_len > 0 && desc && desc->_dname_count > 0) { 222 int count = (int)desc->_dname_count; 223 int rdf = 0; 224 size_t len; 225 size_t oldpos; 226 /* decompress dnames. */ 227 while(pkt_len > 0 && count) { 228 switch(desc->_wireformat[rdf]) { 229 case LDNS_RDF_TYPE_DNAME: 230 oldpos = sldns_buffer_position(pkt); 231 dname_pkt_copy(pkt, to, 232 sldns_buffer_current(pkt)); 233 to += pkt_dname_len(pkt); 234 pkt_len -= sldns_buffer_position(pkt)-oldpos; 235 count--; 236 len = 0; 237 break; 238 case LDNS_RDF_TYPE_STR: 239 len = sldns_buffer_current(pkt)[0] + 1; 240 break; 241 default: 242 len = get_rdf_size(desc->_wireformat[rdf]); 243 break; 244 } 245 if(len) { 246 log_assert(len <= pkt_len); 247 memmove(to, sldns_buffer_current(pkt), len); 248 to += len; 249 sldns_buffer_skip(pkt, (ssize_t)len); 250 pkt_len -= len; 251 } 252 rdf++; 253 } 254 } 255 /* copy remaining rdata */ 256 if(pkt_len > 0) 257 memmove(to, sldns_buffer_current(pkt), pkt_len); 258 259 return 1; 260 } 261 262 /** copy over the data into packed rrset */ 263 static int 264 parse_rr_copy(sldns_buffer* pkt, struct rrset_parse* pset, 265 struct packed_rrset_data* data) 266 { 267 size_t i; 268 struct rr_parse* rr = pset->rr_first; 269 uint8_t* nextrdata; 270 size_t total = pset->rr_count + pset->rrsig_count; 271 data->ttl = MAX_TTL; 272 data->count = pset->rr_count; 273 data->rrsig_count = pset->rrsig_count; 274 data->trust = rrset_trust_none; 275 data->security = sec_status_unchecked; 276 /* layout: struct - rr_len - rr_data - rr_ttl - rdata - rrsig */ 277 data->rr_len = (size_t*)((uint8_t*)data + 278 sizeof(struct packed_rrset_data)); 279 data->rr_data = (uint8_t**)&(data->rr_len[total]); 280 data->rr_ttl = (time_t*)&(data->rr_data[total]); 281 nextrdata = (uint8_t*)&(data->rr_ttl[total]); 282 for(i=0; i<data->count; i++) { 283 data->rr_len[i] = rr->size; 284 data->rr_data[i] = nextrdata; 285 nextrdata += rr->size; 286 if(!rdata_copy(pkt, data, data->rr_data[i], rr, 287 &data->rr_ttl[i], pset->type, pset->section)) 288 return 0; 289 rr = rr->next; 290 } 291 /* if rrsig, its rdata is at nextrdata */ 292 rr = pset->rrsig_first; 293 for(i=data->count; i<total; i++) { 294 data->rr_len[i] = rr->size; 295 data->rr_data[i] = nextrdata; 296 nextrdata += rr->size; 297 if(!rdata_copy(pkt, data, data->rr_data[i], rr, 298 &data->rr_ttl[i], LDNS_RR_TYPE_RRSIG, pset->section)) 299 return 0; 300 rr = rr->next; 301 } 302 return 1; 303 } 304 305 /** create rrset return 0 on failure */ 306 static int 307 parse_create_rrset(sldns_buffer* pkt, struct rrset_parse* pset, 308 struct packed_rrset_data** data, struct regional* region) 309 { 310 /* allocate */ 311 size_t s; 312 if(pset->rr_count > RR_COUNT_MAX || pset->rrsig_count > RR_COUNT_MAX || 313 pset->size > RR_COUNT_MAX) 314 return 0; /* protect against integer overflow */ 315 s = sizeof(struct packed_rrset_data) + 316 (pset->rr_count + pset->rrsig_count) * 317 (sizeof(size_t)+sizeof(uint8_t*)+sizeof(time_t)) + 318 pset->size; 319 if(region) 320 *data = regional_alloc(region, s); 321 else *data = malloc(s); 322 if(!*data) 323 return 0; 324 /* copy & decompress */ 325 if(!parse_rr_copy(pkt, pset, *data)) { 326 if(!region) free(*data); 327 return 0; 328 } 329 return 1; 330 } 331 332 /** get trust value for rrset */ 333 static enum rrset_trust 334 get_rrset_trust(struct msg_parse* msg, struct rrset_parse* rrset) 335 { 336 uint16_t AA = msg->flags & BIT_AA; 337 if(rrset->section == LDNS_SECTION_ANSWER) { 338 if(AA) { 339 /* RFC2181 says remainder of CNAME chain is nonauth*/ 340 if(msg->rrset_first && 341 msg->rrset_first->section==LDNS_SECTION_ANSWER 342 && msg->rrset_first->type==LDNS_RR_TYPE_CNAME){ 343 if(rrset == msg->rrset_first) 344 return rrset_trust_ans_AA; 345 else return rrset_trust_ans_noAA; 346 } 347 if(msg->rrset_first && 348 msg->rrset_first->section==LDNS_SECTION_ANSWER 349 && msg->rrset_first->type==LDNS_RR_TYPE_DNAME){ 350 if(rrset == msg->rrset_first || 351 rrset == msg->rrset_first->rrset_all_next) 352 return rrset_trust_ans_AA; 353 else return rrset_trust_ans_noAA; 354 } 355 return rrset_trust_ans_AA; 356 } 357 else return rrset_trust_ans_noAA; 358 } else if(rrset->section == LDNS_SECTION_AUTHORITY) { 359 if(AA) return rrset_trust_auth_AA; 360 else return rrset_trust_auth_noAA; 361 } else { 362 /* addit section */ 363 if(AA) return rrset_trust_add_AA; 364 else return rrset_trust_add_noAA; 365 } 366 /* NOTREACHED */ 367 return rrset_trust_none; 368 } 369 370 int 371 parse_copy_decompress_rrset(sldns_buffer* pkt, struct msg_parse* msg, 372 struct rrset_parse *pset, struct regional* region, 373 struct ub_packed_rrset_key* pk) 374 { 375 struct packed_rrset_data* data; 376 pk->rk.flags = pset->flags; 377 pk->rk.dname_len = pset->dname_len; 378 if(region) 379 pk->rk.dname = (uint8_t*)regional_alloc( 380 region, pset->dname_len); 381 else pk->rk.dname = 382 (uint8_t*)malloc(pset->dname_len); 383 if(!pk->rk.dname) 384 return 0; 385 /** copy & decompress dname */ 386 dname_pkt_copy(pkt, pk->rk.dname, pset->dname); 387 /** copy over type and class */ 388 pk->rk.type = htons(pset->type); 389 pk->rk.rrset_class = pset->rrset_class; 390 /** read data part. */ 391 if(!parse_create_rrset(pkt, pset, &data, region)) 392 return 0; 393 pk->entry.data = (void*)data; 394 pk->entry.key = (void*)pk; 395 pk->entry.hash = pset->hash; 396 data->trust = get_rrset_trust(msg, pset); 397 return 1; 398 } 399 400 /** 401 * Copy and decompress rrs 402 * @param pkt: the packet for compression pointer resolution. 403 * @param msg: the parsed message 404 * @param rep: reply info to put rrs into. 405 * @param region: if not NULL, used for allocation. 406 * @return 0 on failure. 407 */ 408 static int 409 parse_copy_decompress(sldns_buffer* pkt, struct msg_parse* msg, 410 struct reply_info* rep, struct regional* region) 411 { 412 size_t i; 413 struct rrset_parse *pset = msg->rrset_first; 414 struct packed_rrset_data* data; 415 log_assert(rep); 416 rep->ttl = MAX_TTL; 417 rep->security = sec_status_unchecked; 418 if(rep->rrset_count == 0) 419 rep->ttl = NORR_TTL; 420 421 for(i=0; i<rep->rrset_count; i++) { 422 if(!parse_copy_decompress_rrset(pkt, msg, pset, region, 423 rep->rrsets[i])) 424 return 0; 425 data = (struct packed_rrset_data*)rep->rrsets[i]->entry.data; 426 if(data->ttl < rep->ttl) 427 rep->ttl = data->ttl; 428 429 pset = pset->rrset_all_next; 430 } 431 rep->prefetch_ttl = PREFETCH_TTL_CALC(rep->ttl); 432 rep->serve_expired_ttl = rep->ttl + SERVE_EXPIRED_TTL; 433 return 1; 434 } 435 436 int 437 parse_create_msg(sldns_buffer* pkt, struct msg_parse* msg, 438 struct alloc_cache* alloc, struct query_info* qinf, 439 struct reply_info** rep, struct regional* region) 440 { 441 log_assert(pkt && msg); 442 if(!parse_create_qinfo(pkt, msg, qinf, region)) 443 return 0; 444 if(!parse_create_repinfo(msg, rep, region)) 445 return 0; 446 if(!reply_info_alloc_rrset_keys(*rep, alloc, region)) { 447 if(!region) reply_info_parsedelete(*rep, alloc); 448 return 0; 449 } 450 if(!parse_copy_decompress(pkt, msg, *rep, region)) { 451 if(!region) reply_info_parsedelete(*rep, alloc); 452 return 0; 453 } 454 return 1; 455 } 456 457 int reply_info_parse(sldns_buffer* pkt, struct alloc_cache* alloc, 458 struct query_info* qinf, struct reply_info** rep, 459 struct regional* region, struct edns_data* edns) 460 { 461 /* use scratch pad region-allocator during parsing. */ 462 struct msg_parse* msg; 463 int ret; 464 465 qinf->qname = NULL; 466 qinf->local_alias = NULL; 467 *rep = NULL; 468 if(!(msg = regional_alloc(region, sizeof(*msg)))) { 469 return LDNS_RCODE_SERVFAIL; 470 } 471 memset(msg, 0, sizeof(*msg)); 472 473 sldns_buffer_set_position(pkt, 0); 474 if((ret = parse_packet(pkt, msg, region)) != 0) { 475 return ret; 476 } 477 if((ret = parse_extract_edns(msg, edns, region)) != 0) 478 return ret; 479 480 /* parse OK, allocate return structures */ 481 /* this also performs dname decompression */ 482 if(!parse_create_msg(pkt, msg, alloc, qinf, rep, NULL)) { 483 query_info_clear(qinf); 484 reply_info_parsedelete(*rep, alloc); 485 *rep = NULL; 486 return LDNS_RCODE_SERVFAIL; 487 } 488 return 0; 489 } 490 491 /** helper compare function to sort in lock order */ 492 static int 493 reply_info_sortref_cmp(const void* a, const void* b) 494 { 495 struct rrset_ref* x = (struct rrset_ref*)a; 496 struct rrset_ref* y = (struct rrset_ref*)b; 497 if(x->key < y->key) return -1; 498 if(x->key > y->key) return 1; 499 return 0; 500 } 501 502 void 503 reply_info_sortref(struct reply_info* rep) 504 { 505 qsort(&rep->ref[0], rep->rrset_count, sizeof(struct rrset_ref), 506 reply_info_sortref_cmp); 507 } 508 509 void 510 reply_info_set_ttls(struct reply_info* rep, time_t timenow) 511 { 512 size_t i, j; 513 rep->ttl += timenow; 514 rep->prefetch_ttl += timenow; 515 rep->serve_expired_ttl += timenow; 516 for(i=0; i<rep->rrset_count; i++) { 517 struct packed_rrset_data* data = (struct packed_rrset_data*) 518 rep->ref[i].key->entry.data; 519 if(i>0 && rep->ref[i].key == rep->ref[i-1].key) 520 continue; 521 data->ttl += timenow; 522 for(j=0; j<data->count + data->rrsig_count; j++) { 523 data->rr_ttl[j] += timenow; 524 } 525 } 526 } 527 528 void 529 reply_info_parsedelete(struct reply_info* rep, struct alloc_cache* alloc) 530 { 531 size_t i; 532 if(!rep) 533 return; 534 /* no need to lock, since not shared in hashtables. */ 535 for(i=0; i<rep->rrset_count; i++) { 536 ub_packed_rrset_parsedelete(rep->rrsets[i], alloc); 537 } 538 free(rep); 539 } 540 541 int 542 query_info_parse(struct query_info* m, sldns_buffer* query) 543 { 544 uint8_t* q = sldns_buffer_begin(query); 545 /* minimum size: header + \0 + qtype + qclass */ 546 if(sldns_buffer_limit(query) < LDNS_HEADER_SIZE + 5) 547 return 0; 548 if((LDNS_OPCODE_WIRE(q) != LDNS_PACKET_QUERY && LDNS_OPCODE_WIRE(q) != 549 LDNS_PACKET_NOTIFY) || LDNS_QDCOUNT(q) != 1 || 550 sldns_buffer_position(query) != 0) 551 return 0; 552 sldns_buffer_skip(query, LDNS_HEADER_SIZE); 553 m->qname = sldns_buffer_current(query); 554 if((m->qname_len = query_dname_len(query)) == 0) 555 return 0; /* parse error */ 556 if(sldns_buffer_remaining(query) < 4) 557 return 0; /* need qtype, qclass */ 558 m->qtype = sldns_buffer_read_u16(query); 559 m->qclass = sldns_buffer_read_u16(query); 560 m->local_alias = NULL; 561 return 1; 562 } 563 564 /** tiny subroutine for msgreply_compare */ 565 #define COMPARE_IT(x, y) \ 566 if( (x) < (y) ) return -1; \ 567 else if( (x) > (y) ) return +1; \ 568 log_assert( (x) == (y) ); 569 570 int 571 query_info_compare(void* m1, void* m2) 572 { 573 struct query_info* msg1 = (struct query_info*)m1; 574 struct query_info* msg2 = (struct query_info*)m2; 575 int mc; 576 /* from most different to least different for speed */ 577 COMPARE_IT(msg1->qtype, msg2->qtype); 578 if((mc = query_dname_compare(msg1->qname, msg2->qname)) != 0) 579 return mc; 580 log_assert(msg1->qname_len == msg2->qname_len); 581 COMPARE_IT(msg1->qclass, msg2->qclass); 582 return 0; 583 #undef COMPARE_IT 584 } 585 586 void 587 query_info_clear(struct query_info* m) 588 { 589 free(m->qname); 590 m->qname = NULL; 591 } 592 593 size_t 594 msgreply_sizefunc(void* k, void* d) 595 { 596 struct msgreply_entry* q = (struct msgreply_entry*)k; 597 struct reply_info* r = (struct reply_info*)d; 598 size_t s = sizeof(struct msgreply_entry) + sizeof(struct reply_info) 599 + q->key.qname_len + lock_get_mem(&q->entry.lock) 600 - sizeof(struct rrset_ref); 601 s += r->rrset_count * sizeof(struct rrset_ref); 602 s += r->rrset_count * sizeof(struct ub_packed_rrset_key*); 603 return s; 604 } 605 606 void 607 query_entry_delete(void *k, void* ATTR_UNUSED(arg)) 608 { 609 struct msgreply_entry* q = (struct msgreply_entry*)k; 610 lock_rw_destroy(&q->entry.lock); 611 query_info_clear(&q->key); 612 free(q); 613 } 614 615 void 616 reply_info_delete(void* d, void* ATTR_UNUSED(arg)) 617 { 618 struct reply_info* r = (struct reply_info*)d; 619 free(r); 620 } 621 622 hashvalue_type 623 query_info_hash(struct query_info *q, uint16_t flags) 624 { 625 hashvalue_type h = 0xab; 626 h = hashlittle(&q->qtype, sizeof(q->qtype), h); 627 if(q->qtype == LDNS_RR_TYPE_AAAA && (flags&BIT_CD)) 628 h++; 629 h = hashlittle(&q->qclass, sizeof(q->qclass), h); 630 h = dname_query_hash(q->qname, h); 631 return h; 632 } 633 634 struct msgreply_entry* 635 query_info_entrysetup(struct query_info* q, struct reply_info* r, 636 hashvalue_type h) 637 { 638 struct msgreply_entry* e = (struct msgreply_entry*)malloc( 639 sizeof(struct msgreply_entry)); 640 if(!e) return NULL; 641 memcpy(&e->key, q, sizeof(*q)); 642 e->entry.hash = h; 643 e->entry.key = e; 644 e->entry.data = r; 645 lock_rw_init(&e->entry.lock); 646 lock_protect(&e->entry.lock, &e->key.qname, sizeof(e->key.qname)); 647 lock_protect(&e->entry.lock, &e->key.qname_len, sizeof(e->key.qname_len)); 648 lock_protect(&e->entry.lock, &e->key.qtype, sizeof(e->key.qtype)); 649 lock_protect(&e->entry.lock, &e->key.qclass, sizeof(e->key.qclass)); 650 lock_protect(&e->entry.lock, &e->key.local_alias, sizeof(e->key.local_alias)); 651 lock_protect(&e->entry.lock, &e->entry.hash, sizeof(e->entry.hash)); 652 lock_protect(&e->entry.lock, &e->entry.key, sizeof(e->entry.key)); 653 lock_protect(&e->entry.lock, &e->entry.data, sizeof(e->entry.data)); 654 lock_protect(&e->entry.lock, e->key.qname, e->key.qname_len); 655 q->qname = NULL; 656 return e; 657 } 658 659 /** copy rrsets from replyinfo to dest replyinfo */ 660 static int 661 repinfo_copy_rrsets(struct reply_info* dest, struct reply_info* from, 662 struct regional* region) 663 { 664 size_t i, s; 665 struct packed_rrset_data* fd, *dd; 666 struct ub_packed_rrset_key* fk, *dk; 667 for(i=0; i<dest->rrset_count; i++) { 668 fk = from->rrsets[i]; 669 dk = dest->rrsets[i]; 670 fd = (struct packed_rrset_data*)fk->entry.data; 671 dk->entry.hash = fk->entry.hash; 672 dk->rk = fk->rk; 673 if(region) { 674 dk->id = fk->id; 675 dk->rk.dname = (uint8_t*)regional_alloc_init(region, 676 fk->rk.dname, fk->rk.dname_len); 677 } else 678 dk->rk.dname = (uint8_t*)memdup(fk->rk.dname, 679 fk->rk.dname_len); 680 if(!dk->rk.dname) 681 return 0; 682 s = packed_rrset_sizeof(fd); 683 if(region) 684 dd = (struct packed_rrset_data*)regional_alloc_init( 685 region, fd, s); 686 else dd = (struct packed_rrset_data*)memdup(fd, s); 687 if(!dd) 688 return 0; 689 packed_rrset_ptr_fixup(dd); 690 dk->entry.data = (void*)dd; 691 } 692 return 1; 693 } 694 695 struct reply_info* 696 reply_info_copy(struct reply_info* rep, struct alloc_cache* alloc, 697 struct regional* region) 698 { 699 struct reply_info* cp; 700 cp = construct_reply_info_base(region, rep->flags, rep->qdcount, 701 rep->ttl, rep->prefetch_ttl, rep->serve_expired_ttl, 702 rep->an_numrrsets, rep->ns_numrrsets, rep->ar_numrrsets, 703 rep->rrset_count, rep->security); 704 if(!cp) 705 return NULL; 706 /* allocate ub_key structures special or not */ 707 if(!reply_info_alloc_rrset_keys(cp, alloc, region)) { 708 if(!region) 709 reply_info_parsedelete(cp, alloc); 710 return NULL; 711 } 712 if(!repinfo_copy_rrsets(cp, rep, region)) { 713 if(!region) 714 reply_info_parsedelete(cp, alloc); 715 return NULL; 716 } 717 return cp; 718 } 719 720 uint8_t* 721 reply_find_final_cname_target(struct query_info* qinfo, struct reply_info* rep) 722 { 723 uint8_t* sname = qinfo->qname; 724 size_t snamelen = qinfo->qname_len; 725 size_t i; 726 for(i=0; i<rep->an_numrrsets; i++) { 727 struct ub_packed_rrset_key* s = rep->rrsets[i]; 728 /* follow CNAME chain (if any) */ 729 if(ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME && 730 ntohs(s->rk.rrset_class) == qinfo->qclass && 731 snamelen == s->rk.dname_len && 732 query_dname_compare(sname, s->rk.dname) == 0) { 733 get_cname_target(s, &sname, &snamelen); 734 } 735 } 736 if(sname != qinfo->qname) 737 return sname; 738 return NULL; 739 } 740 741 struct ub_packed_rrset_key* 742 reply_find_answer_rrset(struct query_info* qinfo, struct reply_info* rep) 743 { 744 uint8_t* sname = qinfo->qname; 745 size_t snamelen = qinfo->qname_len; 746 size_t i; 747 for(i=0; i<rep->an_numrrsets; i++) { 748 struct ub_packed_rrset_key* s = rep->rrsets[i]; 749 /* first match type, for query of qtype cname */ 750 if(ntohs(s->rk.type) == qinfo->qtype && 751 ntohs(s->rk.rrset_class) == qinfo->qclass && 752 snamelen == s->rk.dname_len && 753 query_dname_compare(sname, s->rk.dname) == 0) { 754 return s; 755 } 756 /* follow CNAME chain (if any) */ 757 if(ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME && 758 ntohs(s->rk.rrset_class) == qinfo->qclass && 759 snamelen == s->rk.dname_len && 760 query_dname_compare(sname, s->rk.dname) == 0) { 761 get_cname_target(s, &sname, &snamelen); 762 } 763 } 764 return NULL; 765 } 766 767 struct ub_packed_rrset_key* reply_find_rrset_section_an(struct reply_info* rep, 768 uint8_t* name, size_t namelen, uint16_t type, uint16_t dclass) 769 { 770 size_t i; 771 for(i=0; i<rep->an_numrrsets; i++) { 772 struct ub_packed_rrset_key* s = rep->rrsets[i]; 773 if(ntohs(s->rk.type) == type && 774 ntohs(s->rk.rrset_class) == dclass && 775 namelen == s->rk.dname_len && 776 query_dname_compare(name, s->rk.dname) == 0) { 777 return s; 778 } 779 } 780 return NULL; 781 } 782 783 struct ub_packed_rrset_key* reply_find_rrset_section_ns(struct reply_info* rep, 784 uint8_t* name, size_t namelen, uint16_t type, uint16_t dclass) 785 { 786 size_t i; 787 for(i=rep->an_numrrsets; i<rep->an_numrrsets+rep->ns_numrrsets; i++) { 788 struct ub_packed_rrset_key* s = rep->rrsets[i]; 789 if(ntohs(s->rk.type) == type && 790 ntohs(s->rk.rrset_class) == dclass && 791 namelen == s->rk.dname_len && 792 query_dname_compare(name, s->rk.dname) == 0) { 793 return s; 794 } 795 } 796 return NULL; 797 } 798 799 struct ub_packed_rrset_key* reply_find_rrset(struct reply_info* rep, 800 uint8_t* name, size_t namelen, uint16_t type, uint16_t dclass) 801 { 802 size_t i; 803 for(i=0; i<rep->rrset_count; i++) { 804 struct ub_packed_rrset_key* s = rep->rrsets[i]; 805 if(ntohs(s->rk.type) == type && 806 ntohs(s->rk.rrset_class) == dclass && 807 namelen == s->rk.dname_len && 808 query_dname_compare(name, s->rk.dname) == 0) { 809 return s; 810 } 811 } 812 return NULL; 813 } 814 815 void 816 log_dns_msg(const char* str, struct query_info* qinfo, struct reply_info* rep) 817 { 818 /* not particularly fast but flexible, make wireformat and print */ 819 sldns_buffer* buf = sldns_buffer_new(65535); 820 struct regional* region = regional_create(); 821 if(!reply_info_encode(qinfo, rep, 0, rep->flags, buf, 0, 822 region, 65535, 1, 0)) { 823 log_info("%s: log_dns_msg: out of memory", str); 824 } else { 825 char* s = sldns_wire2str_pkt(sldns_buffer_begin(buf), 826 sldns_buffer_limit(buf)); 827 if(!s) { 828 log_info("%s: log_dns_msg: ldns tostr failed", str); 829 } else { 830 log_info("%s %s", str, s); 831 } 832 free(s); 833 } 834 sldns_buffer_free(buf); 835 regional_destroy(region); 836 } 837 838 void 839 log_reply_info(enum verbosity_value v, struct query_info *qinf, 840 struct sockaddr_storage *addr, socklen_t addrlen, struct timeval dur, 841 int cached, struct sldns_buffer *rmsg) 842 { 843 char qname_buf[LDNS_MAX_DOMAINLEN+1]; 844 char clientip_buf[128]; 845 char rcode_buf[16]; 846 char type_buf[16]; 847 char class_buf[16]; 848 size_t pktlen; 849 uint16_t rcode = FLAGS_GET_RCODE(sldns_buffer_read_u16_at(rmsg, 2)); 850 851 if(verbosity < v) 852 return; 853 854 sldns_wire2str_rcode_buf((int)rcode, rcode_buf, sizeof(rcode_buf)); 855 addr_to_str(addr, addrlen, clientip_buf, sizeof(clientip_buf)); 856 if(rcode == LDNS_RCODE_FORMERR) 857 { 858 if(LOG_TAG_QUERYREPLY) 859 log_reply("%s - - - %s - - - ", clientip_buf, rcode_buf); 860 else log_info("%s - - - %s - - - ", clientip_buf, rcode_buf); 861 } else { 862 if(qinf->qname) 863 dname_str(qinf->qname, qname_buf); 864 else snprintf(qname_buf, sizeof(qname_buf), "null"); 865 pktlen = sldns_buffer_limit(rmsg); 866 sldns_wire2str_type_buf(qinf->qtype, type_buf, sizeof(type_buf)); 867 sldns_wire2str_class_buf(qinf->qclass, class_buf, sizeof(class_buf)); 868 if(LOG_TAG_QUERYREPLY) 869 log_reply("%s %s %s %s %s " ARG_LL "d.%6.6d %d %d", 870 clientip_buf, qname_buf, type_buf, class_buf, 871 rcode_buf, (long long)dur.tv_sec, (int)dur.tv_usec, cached, (int)pktlen); 872 else log_info("%s %s %s %s %s " ARG_LL "d.%6.6d %d %d", 873 clientip_buf, qname_buf, type_buf, class_buf, 874 rcode_buf, (long long)dur.tv_sec, (int)dur.tv_usec, cached, (int)pktlen); 875 } 876 } 877 878 void 879 log_query_info(enum verbosity_value v, const char* str, 880 struct query_info* qinf) 881 { 882 log_nametypeclass(v, str, qinf->qname, qinf->qtype, qinf->qclass); 883 } 884 885 int 886 reply_check_cname_chain(struct query_info* qinfo, struct reply_info* rep) 887 { 888 /* check only answer section rrs for matching cname chain. 889 * the cache may return changed rdata, but owner names are untouched.*/ 890 size_t i; 891 uint8_t* sname = qinfo->qname; 892 size_t snamelen = qinfo->qname_len; 893 for(i=0; i<rep->an_numrrsets; i++) { 894 uint16_t t = ntohs(rep->rrsets[i]->rk.type); 895 if(t == LDNS_RR_TYPE_DNAME) 896 continue; /* skip dnames; note TTL 0 not cached */ 897 /* verify that owner matches current sname */ 898 if(query_dname_compare(sname, rep->rrsets[i]->rk.dname) != 0){ 899 /* cname chain broken */ 900 return 0; 901 } 902 /* if this is a cname; move on */ 903 if(t == LDNS_RR_TYPE_CNAME) { 904 get_cname_target(rep->rrsets[i], &sname, &snamelen); 905 } 906 } 907 return 1; 908 } 909 910 int 911 reply_all_rrsets_secure(struct reply_info* rep) 912 { 913 size_t i; 914 for(i=0; i<rep->rrset_count; i++) { 915 if( ((struct packed_rrset_data*)rep->rrsets[i]->entry.data) 916 ->security != sec_status_secure ) 917 return 0; 918 } 919 return 1; 920 } 921 922 struct reply_info* 923 parse_reply_in_temp_region(sldns_buffer* pkt, struct regional* region, 924 struct query_info* qi) 925 { 926 struct reply_info* rep; 927 struct msg_parse* msg; 928 if(!(msg = regional_alloc(region, sizeof(*msg)))) { 929 return NULL; 930 } 931 memset(msg, 0, sizeof(*msg)); 932 sldns_buffer_set_position(pkt, 0); 933 if(parse_packet(pkt, msg, region) != 0){ 934 return 0; 935 } 936 if(!parse_create_msg(pkt, msg, NULL, qi, &rep, region)) { 937 return 0; 938 } 939 return rep; 940 } 941 942 int edns_opt_append(struct edns_data* edns, struct regional* region, 943 uint16_t code, size_t len, uint8_t* data) 944 { 945 struct edns_option** prevp; 946 struct edns_option* opt; 947 948 /* allocate new element */ 949 opt = (struct edns_option*)regional_alloc(region, sizeof(*opt)); 950 if(!opt) 951 return 0; 952 opt->next = NULL; 953 opt->opt_code = code; 954 opt->opt_len = len; 955 opt->opt_data = NULL; 956 if(len > 0) { 957 opt->opt_data = regional_alloc_init(region, data, len); 958 if(!opt->opt_data) 959 return 0; 960 } 961 962 /* append at end of list */ 963 prevp = &edns->opt_list; 964 while(*prevp != NULL) 965 prevp = &((*prevp)->next); 966 *prevp = opt; 967 return 1; 968 } 969 970 int edns_opt_list_append(struct edns_option** list, uint16_t code, size_t len, 971 uint8_t* data, struct regional* region) 972 { 973 struct edns_option** prevp; 974 struct edns_option* opt; 975 976 /* allocate new element */ 977 opt = (struct edns_option*)regional_alloc(region, sizeof(*opt)); 978 if(!opt) 979 return 0; 980 opt->next = NULL; 981 opt->opt_code = code; 982 opt->opt_len = len; 983 opt->opt_data = NULL; 984 if(len > 0) { 985 opt->opt_data = regional_alloc_init(region, data, len); 986 if(!opt->opt_data) 987 return 0; 988 } 989 990 /* append at end of list */ 991 prevp = list; 992 while(*prevp != NULL) { 993 prevp = &((*prevp)->next); 994 } 995 *prevp = opt; 996 return 1; 997 } 998 999 int edns_opt_list_remove(struct edns_option** list, uint16_t code) 1000 { 1001 /* The list should already be allocated in a region. Freeing the 1002 * allocated space in a region is not possible. We just unlink the 1003 * required elements and they will be freed together with the region. */ 1004 1005 struct edns_option* prev; 1006 struct edns_option* curr; 1007 if(!list || !(*list)) return 0; 1008 1009 /* Unlink and repoint if the element(s) are first in list */ 1010 while(list && *list && (*list)->opt_code == code) { 1011 *list = (*list)->next; 1012 } 1013 1014 if(!list || !(*list)) return 1; 1015 /* Unlink elements and reattach the list */ 1016 prev = *list; 1017 curr = (*list)->next; 1018 while(curr != NULL) { 1019 if(curr->opt_code == code) { 1020 prev->next = curr->next; 1021 curr = curr->next; 1022 } else { 1023 prev = curr; 1024 curr = curr->next; 1025 } 1026 } 1027 return 1; 1028 } 1029 1030 static int inplace_cb_reply_call_generic( 1031 struct inplace_cb* callback_list, enum inplace_cb_list_type type, 1032 struct query_info* qinfo, struct module_qstate* qstate, 1033 struct reply_info* rep, int rcode, struct edns_data* edns, 1034 struct comm_reply* repinfo, struct regional* region) 1035 { 1036 struct inplace_cb* cb; 1037 struct edns_option* opt_list_out = NULL; 1038 #if defined(EXPORT_ALL_SYMBOLS) 1039 (void)type; /* param not used when fptr_ok disabled */ 1040 #endif 1041 if(qstate) 1042 opt_list_out = qstate->edns_opts_front_out; 1043 for(cb=callback_list; cb; cb=cb->next) { 1044 fptr_ok(fptr_whitelist_inplace_cb_reply_generic( 1045 (inplace_cb_reply_func_type*)cb->cb, type)); 1046 (void)(*(inplace_cb_reply_func_type*)cb->cb)(qinfo, qstate, rep, 1047 rcode, edns, &opt_list_out, repinfo, region, cb->id, cb->cb_arg); 1048 } 1049 edns->opt_list = opt_list_out; 1050 return 1; 1051 } 1052 1053 int inplace_cb_reply_call(struct module_env* env, struct query_info* qinfo, 1054 struct module_qstate* qstate, struct reply_info* rep, int rcode, 1055 struct edns_data* edns, struct comm_reply* repinfo, struct regional* region) 1056 { 1057 return inplace_cb_reply_call_generic( 1058 env->inplace_cb_lists[inplace_cb_reply], inplace_cb_reply, qinfo, 1059 qstate, rep, rcode, edns, repinfo, region); 1060 } 1061 1062 int inplace_cb_reply_cache_call(struct module_env* env, 1063 struct query_info* qinfo, struct module_qstate* qstate, 1064 struct reply_info* rep, int rcode, struct edns_data* edns, 1065 struct comm_reply* repinfo, struct regional* region) 1066 { 1067 return inplace_cb_reply_call_generic( 1068 env->inplace_cb_lists[inplace_cb_reply_cache], inplace_cb_reply_cache, 1069 qinfo, qstate, rep, rcode, edns, repinfo, region); 1070 } 1071 1072 int inplace_cb_reply_local_call(struct module_env* env, 1073 struct query_info* qinfo, struct module_qstate* qstate, 1074 struct reply_info* rep, int rcode, struct edns_data* edns, 1075 struct comm_reply* repinfo, struct regional* region) 1076 { 1077 return inplace_cb_reply_call_generic( 1078 env->inplace_cb_lists[inplace_cb_reply_local], inplace_cb_reply_local, 1079 qinfo, qstate, rep, rcode, edns, repinfo, region); 1080 } 1081 1082 int inplace_cb_reply_servfail_call(struct module_env* env, 1083 struct query_info* qinfo, struct module_qstate* qstate, 1084 struct reply_info* rep, int rcode, struct edns_data* edns, 1085 struct comm_reply* repinfo, struct regional* region) 1086 { 1087 /* We are going to servfail. Remove any potential edns options. */ 1088 if(qstate) 1089 qstate->edns_opts_front_out = NULL; 1090 return inplace_cb_reply_call_generic( 1091 env->inplace_cb_lists[inplace_cb_reply_servfail], 1092 inplace_cb_reply_servfail, qinfo, qstate, rep, rcode, edns, repinfo, 1093 region); 1094 } 1095 1096 int inplace_cb_query_call(struct module_env* env, struct query_info* qinfo, 1097 uint16_t flags, struct sockaddr_storage* addr, socklen_t addrlen, 1098 uint8_t* zone, size_t zonelen, struct module_qstate* qstate, 1099 struct regional* region) 1100 { 1101 struct inplace_cb* cb = env->inplace_cb_lists[inplace_cb_query]; 1102 for(; cb; cb=cb->next) { 1103 fptr_ok(fptr_whitelist_inplace_cb_query( 1104 (inplace_cb_query_func_type*)cb->cb)); 1105 (void)(*(inplace_cb_query_func_type*)cb->cb)(qinfo, flags, 1106 qstate, addr, addrlen, zone, zonelen, region, 1107 cb->id, cb->cb_arg); 1108 } 1109 return 1; 1110 } 1111 1112 int inplace_cb_edns_back_parsed_call(struct module_env* env, 1113 struct module_qstate* qstate) 1114 { 1115 struct inplace_cb* cb = 1116 env->inplace_cb_lists[inplace_cb_edns_back_parsed]; 1117 for(; cb; cb=cb->next) { 1118 fptr_ok(fptr_whitelist_inplace_cb_edns_back_parsed( 1119 (inplace_cb_edns_back_parsed_func_type*)cb->cb)); 1120 (void)(*(inplace_cb_edns_back_parsed_func_type*)cb->cb)(qstate, 1121 cb->id, cb->cb_arg); 1122 } 1123 return 1; 1124 } 1125 1126 int inplace_cb_query_response_call(struct module_env* env, 1127 struct module_qstate* qstate, struct dns_msg* response) { 1128 struct inplace_cb* cb = 1129 env->inplace_cb_lists[inplace_cb_query_response]; 1130 for(; cb; cb=cb->next) { 1131 fptr_ok(fptr_whitelist_inplace_cb_query_response( 1132 (inplace_cb_query_response_func_type*)cb->cb)); 1133 (void)(*(inplace_cb_query_response_func_type*)cb->cb)(qstate, 1134 response, cb->id, cb->cb_arg); 1135 } 1136 return 1; 1137 } 1138 1139 struct edns_option* edns_opt_copy_region(struct edns_option* list, 1140 struct regional* region) 1141 { 1142 struct edns_option* result = NULL, *cur = NULL, *s; 1143 while(list) { 1144 /* copy edns option structure */ 1145 s = regional_alloc_init(region, list, sizeof(*list)); 1146 if(!s) return NULL; 1147 s->next = NULL; 1148 1149 /* copy option data */ 1150 if(s->opt_data) { 1151 s->opt_data = regional_alloc_init(region, s->opt_data, 1152 s->opt_len); 1153 if(!s->opt_data) 1154 return NULL; 1155 } 1156 1157 /* link into list */ 1158 if(cur) 1159 cur->next = s; 1160 else result = s; 1161 cur = s; 1162 1163 /* examine next element */ 1164 list = list->next; 1165 } 1166 return result; 1167 } 1168 1169 int edns_opt_compare(struct edns_option* p, struct edns_option* q) 1170 { 1171 if(!p && !q) return 0; 1172 if(!p) return -1; 1173 if(!q) return 1; 1174 log_assert(p && q); 1175 if(p->opt_code != q->opt_code) 1176 return (int)q->opt_code - (int)p->opt_code; 1177 if(p->opt_len != q->opt_len) 1178 return (int)q->opt_len - (int)p->opt_len; 1179 if(p->opt_len != 0) 1180 return memcmp(p->opt_data, q->opt_data, p->opt_len); 1181 return 0; 1182 } 1183 1184 int edns_opt_list_compare(struct edns_option* p, struct edns_option* q) 1185 { 1186 int r; 1187 while(p && q) { 1188 r = edns_opt_compare(p, q); 1189 if(r != 0) 1190 return r; 1191 p = p->next; 1192 q = q->next; 1193 } 1194 if(p || q) { 1195 /* uneven length lists */ 1196 if(p) return 1; 1197 if(q) return -1; 1198 } 1199 return 0; 1200 } 1201 1202 void edns_opt_list_free(struct edns_option* list) 1203 { 1204 struct edns_option* n; 1205 while(list) { 1206 free(list->opt_data); 1207 n = list->next; 1208 free(list); 1209 list = n; 1210 } 1211 } 1212 1213 struct edns_option* edns_opt_copy_alloc(struct edns_option* list) 1214 { 1215 struct edns_option* result = NULL, *cur = NULL, *s; 1216 while(list) { 1217 /* copy edns option structure */ 1218 s = memdup(list, sizeof(*list)); 1219 if(!s) { 1220 edns_opt_list_free(result); 1221 return NULL; 1222 } 1223 s->next = NULL; 1224 1225 /* copy option data */ 1226 if(s->opt_data) { 1227 s->opt_data = memdup(s->opt_data, s->opt_len); 1228 if(!s->opt_data) { 1229 free(s); 1230 edns_opt_list_free(result); 1231 return NULL; 1232 } 1233 } 1234 1235 /* link into list */ 1236 if(cur) 1237 cur->next = s; 1238 else result = s; 1239 cur = s; 1240 1241 /* examine next element */ 1242 list = list->next; 1243 } 1244 return result; 1245 } 1246 1247 struct edns_option* edns_opt_list_find(struct edns_option* list, uint16_t code) 1248 { 1249 struct edns_option* p; 1250 for(p=list; p; p=p->next) { 1251 if(p->opt_code == code) 1252 return p; 1253 } 1254 return NULL; 1255 } 1256