message.c revision 292321
1135446Strhodes/* 2262706Serwin * Copyright (C) 2004-2014 Internet Systems Consortium, Inc. ("ISC") 3135446Strhodes * Copyright (C) 1999-2003 Internet Software Consortium. 4135446Strhodes * 5174187Sdougb * Permission to use, copy, modify, and/or distribute this software for any 6135446Strhodes * purpose with or without fee is hereby granted, provided that the above 7135446Strhodes * copyright notice and this permission notice appear in all copies. 8135446Strhodes * 9135446Strhodes * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 10135446Strhodes * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 11135446Strhodes * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 12135446Strhodes * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13135446Strhodes * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 14135446Strhodes * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15135446Strhodes * PERFORMANCE OF THIS SOFTWARE. 16135446Strhodes */ 17135446Strhodes 18234010Sdougb/* $Id$ */ 19135446Strhodes 20170222Sdougb/*! \file */ 21170222Sdougb 22135446Strhodes/*** 23135446Strhodes *** Imports 24135446Strhodes ***/ 25135446Strhodes 26135446Strhodes#include <config.h> 27193149Sdougb#include <ctype.h> 28135446Strhodes 29135446Strhodes#include <isc/buffer.h> 30135446Strhodes#include <isc/mem.h> 31135446Strhodes#include <isc/print.h> 32135446Strhodes#include <isc/string.h> /* Required for HP/UX (and others?) */ 33135446Strhodes#include <isc/util.h> 34135446Strhodes 35135446Strhodes#include <dns/dnssec.h> 36135446Strhodes#include <dns/keyvalues.h> 37135446Strhodes#include <dns/log.h> 38135446Strhodes#include <dns/masterdump.h> 39135446Strhodes#include <dns/message.h> 40135446Strhodes#include <dns/opcode.h> 41135446Strhodes#include <dns/rdata.h> 42135446Strhodes#include <dns/rdatalist.h> 43135446Strhodes#include <dns/rdataset.h> 44135446Strhodes#include <dns/rdatastruct.h> 45135446Strhodes#include <dns/result.h> 46135446Strhodes#include <dns/tsig.h> 47135446Strhodes#include <dns/view.h> 48135446Strhodes 49193149Sdougb#ifdef SKAN_MSG_DEBUG 50193149Sdougbstatic void 51193149Sdougbhexdump(const char *msg, const char *msg2, void *base, size_t len) { 52193149Sdougb unsigned char *p; 53193149Sdougb unsigned int cnt; 54193149Sdougb 55193149Sdougb p = base; 56193149Sdougb cnt = 0; 57193149Sdougb 58193149Sdougb printf("*** %s [%s] (%u bytes @ %p)\n", msg, msg2, len, base); 59193149Sdougb 60193149Sdougb while (cnt < len) { 61193149Sdougb if (cnt % 16 == 0) 62193149Sdougb printf("%p: ", p); 63193149Sdougb else if (cnt % 8 == 0) 64193149Sdougb printf(" |"); 65193149Sdougb printf(" %02x %c", *p, (isprint(*p) ? *p : ' ')); 66193149Sdougb p++; 67193149Sdougb cnt++; 68193149Sdougb 69193149Sdougb if (cnt % 16 == 0) 70193149Sdougb printf("\n"); 71193149Sdougb } 72193149Sdougb 73193149Sdougb if (cnt % 16 != 0) 74193149Sdougb printf("\n"); 75193149Sdougb} 76193149Sdougb#endif 77193149Sdougb 78135446Strhodes#define DNS_MESSAGE_OPCODE_MASK 0x7800U 79135446Strhodes#define DNS_MESSAGE_OPCODE_SHIFT 11 80135446Strhodes#define DNS_MESSAGE_RCODE_MASK 0x000fU 81135446Strhodes#define DNS_MESSAGE_FLAG_MASK 0x8ff0U 82135446Strhodes#define DNS_MESSAGE_EDNSRCODE_MASK 0xff000000U 83135446Strhodes#define DNS_MESSAGE_EDNSRCODE_SHIFT 24 84135446Strhodes#define DNS_MESSAGE_EDNSVERSION_MASK 0x00ff0000U 85135446Strhodes#define DNS_MESSAGE_EDNSVERSION_SHIFT 16 86135446Strhodes 87135446Strhodes#define VALID_NAMED_SECTION(s) (((s) > DNS_SECTION_ANY) \ 88135446Strhodes && ((s) < DNS_SECTION_MAX)) 89135446Strhodes#define VALID_SECTION(s) (((s) >= DNS_SECTION_ANY) \ 90135446Strhodes && ((s) < DNS_SECTION_MAX)) 91135446Strhodes#define ADD_STRING(b, s) {if (strlen(s) >= \ 92135446Strhodes isc_buffer_availablelength(b)) \ 93135446Strhodes return(ISC_R_NOSPACE); else \ 94135446Strhodes isc_buffer_putstr(b, s);} 95135446Strhodes#define VALID_PSEUDOSECTION(s) (((s) >= DNS_PSEUDOSECTION_ANY) \ 96135446Strhodes && ((s) < DNS_PSEUDOSECTION_MAX)) 97135446Strhodes 98193149Sdougb#define OPTOUT(x) (((x)->attributes & DNS_RDATASETATTR_OPTOUT) != 0) 99193149Sdougb 100170222Sdougb/*% 101135446Strhodes * This is the size of each individual scratchpad buffer, and the numbers 102135446Strhodes * of various block allocations used within the server. 103135446Strhodes * XXXMLG These should come from a config setting. 104135446Strhodes */ 105135446Strhodes#define SCRATCHPAD_SIZE 512 106135446Strhodes#define NAME_COUNT 8 107135446Strhodes#define OFFSET_COUNT 4 108135446Strhodes#define RDATA_COUNT 8 109135446Strhodes#define RDATALIST_COUNT 8 110135446Strhodes#define RDATASET_COUNT RDATALIST_COUNT 111135446Strhodes 112170222Sdougb/*% 113135446Strhodes * Text representation of the different items, for message_totext 114135446Strhodes * functions. 115135446Strhodes */ 116135446Strhodesstatic const char *sectiontext[] = { 117135446Strhodes "QUESTION", 118135446Strhodes "ANSWER", 119135446Strhodes "AUTHORITY", 120135446Strhodes "ADDITIONAL" 121135446Strhodes}; 122135446Strhodes 123135446Strhodesstatic const char *updsectiontext[] = { 124135446Strhodes "ZONE", 125135446Strhodes "PREREQUISITE", 126135446Strhodes "UPDATE", 127135446Strhodes "ADDITIONAL" 128135446Strhodes}; 129135446Strhodes 130135446Strhodesstatic const char *opcodetext[] = { 131135446Strhodes "QUERY", 132135446Strhodes "IQUERY", 133135446Strhodes "STATUS", 134135446Strhodes "RESERVED3", 135135446Strhodes "NOTIFY", 136135446Strhodes "UPDATE", 137135446Strhodes "RESERVED6", 138135446Strhodes "RESERVED7", 139135446Strhodes "RESERVED8", 140135446Strhodes "RESERVED9", 141135446Strhodes "RESERVED10", 142135446Strhodes "RESERVED11", 143135446Strhodes "RESERVED12", 144135446Strhodes "RESERVED13", 145135446Strhodes "RESERVED14", 146135446Strhodes "RESERVED15" 147135446Strhodes}; 148135446Strhodes 149135446Strhodesstatic const char *rcodetext[] = { 150135446Strhodes "NOERROR", 151135446Strhodes "FORMERR", 152135446Strhodes "SERVFAIL", 153135446Strhodes "NXDOMAIN", 154135446Strhodes "NOTIMP", 155135446Strhodes "REFUSED", 156135446Strhodes "YXDOMAIN", 157135446Strhodes "YXRRSET", 158135446Strhodes "NXRRSET", 159135446Strhodes "NOTAUTH", 160135446Strhodes "NOTZONE", 161135446Strhodes "RESERVED11", 162135446Strhodes "RESERVED12", 163135446Strhodes "RESERVED13", 164135446Strhodes "RESERVED14", 165135446Strhodes "RESERVED15", 166135446Strhodes "BADVERS" 167135446Strhodes}; 168135446Strhodes 169135446Strhodes 170170222Sdougb/*% 171135446Strhodes * "helper" type, which consists of a block of some type, and is linkable. 172135446Strhodes * For it to work, sizeof(dns_msgblock_t) must be a multiple of the pointer 173193149Sdougb * size, or the allocated elements will not be aligned correctly. 174135446Strhodes */ 175135446Strhodesstruct dns_msgblock { 176135446Strhodes unsigned int count; 177135446Strhodes unsigned int remaining; 178135446Strhodes ISC_LINK(dns_msgblock_t) link; 179135446Strhodes}; /* dynamically sized */ 180135446Strhodes 181135446Strhodesstatic inline dns_msgblock_t * 182135446Strhodesmsgblock_allocate(isc_mem_t *, unsigned int, unsigned int); 183135446Strhodes 184135446Strhodes#define msgblock_get(block, type) \ 185135446Strhodes ((type *)msgblock_internalget(block, sizeof(type))) 186135446Strhodes 187135446Strhodesstatic inline void * 188135446Strhodesmsgblock_internalget(dns_msgblock_t *, unsigned int); 189135446Strhodes 190135446Strhodesstatic inline void 191135446Strhodesmsgblock_reset(dns_msgblock_t *); 192135446Strhodes 193135446Strhodesstatic inline void 194135446Strhodesmsgblock_free(isc_mem_t *, dns_msgblock_t *, unsigned int); 195135446Strhodes 196135446Strhodes/* 197135446Strhodes * Allocate a new dns_msgblock_t, and return a pointer to it. If no memory 198135446Strhodes * is free, return NULL. 199135446Strhodes */ 200135446Strhodesstatic inline dns_msgblock_t * 201135446Strhodesmsgblock_allocate(isc_mem_t *mctx, unsigned int sizeof_type, 202135446Strhodes unsigned int count) 203135446Strhodes{ 204135446Strhodes dns_msgblock_t *block; 205135446Strhodes unsigned int length; 206135446Strhodes 207135446Strhodes length = sizeof(dns_msgblock_t) + (sizeof_type * count); 208135446Strhodes 209135446Strhodes block = isc_mem_get(mctx, length); 210135446Strhodes if (block == NULL) 211135446Strhodes return (NULL); 212135446Strhodes 213135446Strhodes block->count = count; 214135446Strhodes block->remaining = count; 215135446Strhodes 216135446Strhodes ISC_LINK_INIT(block, link); 217135446Strhodes 218135446Strhodes return (block); 219135446Strhodes} 220135446Strhodes 221135446Strhodes/* 222135446Strhodes * Return an element from the msgblock. If no more are available, return 223135446Strhodes * NULL. 224135446Strhodes */ 225135446Strhodesstatic inline void * 226135446Strhodesmsgblock_internalget(dns_msgblock_t *block, unsigned int sizeof_type) { 227135446Strhodes void *ptr; 228135446Strhodes 229135446Strhodes if (block == NULL || block->remaining == 0) 230135446Strhodes return (NULL); 231135446Strhodes 232135446Strhodes block->remaining--; 233135446Strhodes 234135446Strhodes ptr = (((unsigned char *)block) 235135446Strhodes + sizeof(dns_msgblock_t) 236135446Strhodes + (sizeof_type * block->remaining)); 237135446Strhodes 238135446Strhodes return (ptr); 239135446Strhodes} 240135446Strhodes 241135446Strhodesstatic inline void 242135446Strhodesmsgblock_reset(dns_msgblock_t *block) { 243135446Strhodes block->remaining = block->count; 244135446Strhodes} 245135446Strhodes 246135446Strhodes/* 247135446Strhodes * Release memory associated with a message block. 248135446Strhodes */ 249135446Strhodesstatic inline void 250135446Strhodesmsgblock_free(isc_mem_t *mctx, dns_msgblock_t *block, unsigned int sizeof_type) 251135446Strhodes{ 252135446Strhodes unsigned int length; 253135446Strhodes 254135446Strhodes length = sizeof(dns_msgblock_t) + (sizeof_type * block->count); 255135446Strhodes 256135446Strhodes isc_mem_put(mctx, block, length); 257135446Strhodes} 258135446Strhodes 259135446Strhodes/* 260135446Strhodes * Allocate a new dynamic buffer, and attach it to this message as the 261135446Strhodes * "current" buffer. (which is always the last on the list, for our 262135446Strhodes * uses) 263135446Strhodes */ 264135446Strhodesstatic inline isc_result_t 265135446Strhodesnewbuffer(dns_message_t *msg, unsigned int size) { 266135446Strhodes isc_result_t result; 267135446Strhodes isc_buffer_t *dynbuf; 268135446Strhodes 269135446Strhodes dynbuf = NULL; 270135446Strhodes result = isc_buffer_allocate(msg->mctx, &dynbuf, size); 271135446Strhodes if (result != ISC_R_SUCCESS) 272135446Strhodes return (ISC_R_NOMEMORY); 273135446Strhodes 274135446Strhodes ISC_LIST_APPEND(msg->scratchpad, dynbuf, link); 275135446Strhodes return (ISC_R_SUCCESS); 276135446Strhodes} 277135446Strhodes 278135446Strhodesstatic inline isc_buffer_t * 279135446Strhodescurrentbuffer(dns_message_t *msg) { 280135446Strhodes isc_buffer_t *dynbuf; 281135446Strhodes 282135446Strhodes dynbuf = ISC_LIST_TAIL(msg->scratchpad); 283135446Strhodes INSIST(dynbuf != NULL); 284135446Strhodes 285135446Strhodes return (dynbuf); 286135446Strhodes} 287135446Strhodes 288135446Strhodesstatic inline void 289135446Strhodesreleaserdata(dns_message_t *msg, dns_rdata_t *rdata) { 290135446Strhodes ISC_LIST_PREPEND(msg->freerdata, rdata, link); 291135446Strhodes} 292135446Strhodes 293135446Strhodesstatic inline dns_rdata_t * 294135446Strhodesnewrdata(dns_message_t *msg) { 295135446Strhodes dns_msgblock_t *msgblock; 296135446Strhodes dns_rdata_t *rdata; 297135446Strhodes 298135446Strhodes rdata = ISC_LIST_HEAD(msg->freerdata); 299135446Strhodes if (rdata != NULL) { 300135446Strhodes ISC_LIST_UNLINK(msg->freerdata, rdata, link); 301135446Strhodes return (rdata); 302135446Strhodes } 303135446Strhodes 304135446Strhodes msgblock = ISC_LIST_TAIL(msg->rdatas); 305135446Strhodes rdata = msgblock_get(msgblock, dns_rdata_t); 306135446Strhodes if (rdata == NULL) { 307135446Strhodes msgblock = msgblock_allocate(msg->mctx, sizeof(dns_rdata_t), 308135446Strhodes RDATA_COUNT); 309135446Strhodes if (msgblock == NULL) 310135446Strhodes return (NULL); 311135446Strhodes 312135446Strhodes ISC_LIST_APPEND(msg->rdatas, msgblock, link); 313135446Strhodes 314135446Strhodes rdata = msgblock_get(msgblock, dns_rdata_t); 315135446Strhodes } 316135446Strhodes 317135446Strhodes dns_rdata_init(rdata); 318135446Strhodes return (rdata); 319135446Strhodes} 320135446Strhodes 321135446Strhodesstatic inline void 322135446Strhodesreleaserdatalist(dns_message_t *msg, dns_rdatalist_t *rdatalist) { 323135446Strhodes ISC_LIST_PREPEND(msg->freerdatalist, rdatalist, link); 324135446Strhodes} 325135446Strhodes 326135446Strhodesstatic inline dns_rdatalist_t * 327135446Strhodesnewrdatalist(dns_message_t *msg) { 328135446Strhodes dns_msgblock_t *msgblock; 329135446Strhodes dns_rdatalist_t *rdatalist; 330135446Strhodes 331135446Strhodes rdatalist = ISC_LIST_HEAD(msg->freerdatalist); 332135446Strhodes if (rdatalist != NULL) { 333135446Strhodes ISC_LIST_UNLINK(msg->freerdatalist, rdatalist, link); 334135446Strhodes return (rdatalist); 335135446Strhodes } 336135446Strhodes 337135446Strhodes msgblock = ISC_LIST_TAIL(msg->rdatalists); 338135446Strhodes rdatalist = msgblock_get(msgblock, dns_rdatalist_t); 339135446Strhodes if (rdatalist == NULL) { 340135446Strhodes msgblock = msgblock_allocate(msg->mctx, 341135446Strhodes sizeof(dns_rdatalist_t), 342135446Strhodes RDATALIST_COUNT); 343135446Strhodes if (msgblock == NULL) 344135446Strhodes return (NULL); 345135446Strhodes 346135446Strhodes ISC_LIST_APPEND(msg->rdatalists, msgblock, link); 347135446Strhodes 348135446Strhodes rdatalist = msgblock_get(msgblock, dns_rdatalist_t); 349135446Strhodes } 350135446Strhodes 351135446Strhodes return (rdatalist); 352135446Strhodes} 353135446Strhodes 354135446Strhodesstatic inline dns_offsets_t * 355135446Strhodesnewoffsets(dns_message_t *msg) { 356135446Strhodes dns_msgblock_t *msgblock; 357135446Strhodes dns_offsets_t *offsets; 358135446Strhodes 359135446Strhodes msgblock = ISC_LIST_TAIL(msg->offsets); 360135446Strhodes offsets = msgblock_get(msgblock, dns_offsets_t); 361135446Strhodes if (offsets == NULL) { 362135446Strhodes msgblock = msgblock_allocate(msg->mctx, 363135446Strhodes sizeof(dns_offsets_t), 364135446Strhodes OFFSET_COUNT); 365135446Strhodes if (msgblock == NULL) 366135446Strhodes return (NULL); 367135446Strhodes 368135446Strhodes ISC_LIST_APPEND(msg->offsets, msgblock, link); 369135446Strhodes 370135446Strhodes offsets = msgblock_get(msgblock, dns_offsets_t); 371135446Strhodes } 372135446Strhodes 373135446Strhodes return (offsets); 374135446Strhodes} 375135446Strhodes 376135446Strhodesstatic inline void 377135446Strhodesmsginitheader(dns_message_t *m) { 378135446Strhodes m->id = 0; 379135446Strhodes m->flags = 0; 380135446Strhodes m->rcode = 0; 381135446Strhodes m->opcode = 0; 382135446Strhodes m->rdclass = 0; 383135446Strhodes} 384135446Strhodes 385135446Strhodesstatic inline void 386135446Strhodesmsginitprivate(dns_message_t *m) { 387135446Strhodes unsigned int i; 388135446Strhodes 389135446Strhodes for (i = 0; i < DNS_SECTION_MAX; i++) { 390135446Strhodes m->cursors[i] = NULL; 391135446Strhodes m->counts[i] = 0; 392135446Strhodes } 393135446Strhodes m->opt = NULL; 394135446Strhodes m->sig0 = NULL; 395135446Strhodes m->sig0name = NULL; 396135446Strhodes m->tsig = NULL; 397135446Strhodes m->tsigname = NULL; 398135446Strhodes m->state = DNS_SECTION_ANY; /* indicate nothing parsed or rendered */ 399135446Strhodes m->opt_reserved = 0; 400135446Strhodes m->sig_reserved = 0; 401135446Strhodes m->reserved = 0; 402135446Strhodes m->buffer = NULL; 403135446Strhodes} 404135446Strhodes 405135446Strhodesstatic inline void 406135446Strhodesmsginittsig(dns_message_t *m) { 407135446Strhodes m->tsigstatus = dns_rcode_noerror; 408135446Strhodes m->querytsigstatus = dns_rcode_noerror; 409135446Strhodes m->tsigkey = NULL; 410135446Strhodes m->tsigctx = NULL; 411135446Strhodes m->sigstart = -1; 412135446Strhodes m->sig0key = NULL; 413135446Strhodes m->sig0status = dns_rcode_noerror; 414135446Strhodes m->timeadjust = 0; 415135446Strhodes} 416135446Strhodes 417135446Strhodes/* 418135446Strhodes * Init elements to default state. Used both when allocating a new element 419135446Strhodes * and when resetting one. 420135446Strhodes */ 421135446Strhodesstatic inline void 422135446Strhodesmsginit(dns_message_t *m) { 423135446Strhodes msginitheader(m); 424135446Strhodes msginitprivate(m); 425135446Strhodes msginittsig(m); 426135446Strhodes m->header_ok = 0; 427135446Strhodes m->question_ok = 0; 428135446Strhodes m->tcp_continuation = 0; 429135446Strhodes m->verified_sig = 0; 430135446Strhodes m->verify_attempted = 0; 431135446Strhodes m->order = NULL; 432135446Strhodes m->order_arg = NULL; 433135446Strhodes m->query.base = NULL; 434135446Strhodes m->query.length = 0; 435135446Strhodes m->free_query = 0; 436135446Strhodes m->saved.base = NULL; 437135446Strhodes m->saved.length = 0; 438135446Strhodes m->free_saved = 0; 439292321Sdelphij m->tkey = 0; 440292321Sdelphij m->rdclass_set = 0; 441135446Strhodes m->querytsig = NULL; 442135446Strhodes} 443135446Strhodes 444135446Strhodesstatic inline void 445135446Strhodesmsgresetnames(dns_message_t *msg, unsigned int first_section) { 446135446Strhodes unsigned int i; 447135446Strhodes dns_name_t *name, *next_name; 448135446Strhodes dns_rdataset_t *rds, *next_rds; 449135446Strhodes 450135446Strhodes /* 451135446Strhodes * Clean up name lists by calling the rdataset disassociate function. 452135446Strhodes */ 453135446Strhodes for (i = first_section; i < DNS_SECTION_MAX; i++) { 454135446Strhodes name = ISC_LIST_HEAD(msg->sections[i]); 455135446Strhodes while (name != NULL) { 456135446Strhodes next_name = ISC_LIST_NEXT(name, link); 457135446Strhodes ISC_LIST_UNLINK(msg->sections[i], name, link); 458135446Strhodes 459135446Strhodes rds = ISC_LIST_HEAD(name->list); 460135446Strhodes while (rds != NULL) { 461135446Strhodes next_rds = ISC_LIST_NEXT(rds, link); 462135446Strhodes ISC_LIST_UNLINK(name->list, rds, link); 463135446Strhodes 464135446Strhodes INSIST(dns_rdataset_isassociated(rds)); 465135446Strhodes dns_rdataset_disassociate(rds); 466135446Strhodes isc_mempool_put(msg->rdspool, rds); 467135446Strhodes rds = next_rds; 468135446Strhodes } 469135446Strhodes if (dns_name_dynamic(name)) 470135446Strhodes dns_name_free(name, msg->mctx); 471135446Strhodes isc_mempool_put(msg->namepool, name); 472135446Strhodes name = next_name; 473135446Strhodes } 474135446Strhodes } 475135446Strhodes} 476135446Strhodes 477135446Strhodesstatic void 478135446Strhodesmsgresetopt(dns_message_t *msg) 479135446Strhodes{ 480135446Strhodes if (msg->opt != NULL) { 481135446Strhodes if (msg->opt_reserved > 0) { 482135446Strhodes dns_message_renderrelease(msg, msg->opt_reserved); 483135446Strhodes msg->opt_reserved = 0; 484135446Strhodes } 485135446Strhodes INSIST(dns_rdataset_isassociated(msg->opt)); 486135446Strhodes dns_rdataset_disassociate(msg->opt); 487135446Strhodes isc_mempool_put(msg->rdspool, msg->opt); 488135446Strhodes msg->opt = NULL; 489135446Strhodes } 490135446Strhodes} 491135446Strhodes 492135446Strhodesstatic void 493135446Strhodesmsgresetsigs(dns_message_t *msg, isc_boolean_t replying) { 494135446Strhodes if (msg->sig_reserved > 0) { 495135446Strhodes dns_message_renderrelease(msg, msg->sig_reserved); 496135446Strhodes msg->sig_reserved = 0; 497135446Strhodes } 498135446Strhodes if (msg->tsig != NULL) { 499135446Strhodes INSIST(dns_rdataset_isassociated(msg->tsig)); 500135446Strhodes INSIST(msg->namepool != NULL); 501135446Strhodes if (replying) { 502135446Strhodes INSIST(msg->querytsig == NULL); 503135446Strhodes msg->querytsig = msg->tsig; 504135446Strhodes } else { 505135446Strhodes dns_rdataset_disassociate(msg->tsig); 506135446Strhodes isc_mempool_put(msg->rdspool, msg->tsig); 507135446Strhodes if (msg->querytsig != NULL) { 508135446Strhodes dns_rdataset_disassociate(msg->querytsig); 509135446Strhodes isc_mempool_put(msg->rdspool, msg->querytsig); 510135446Strhodes } 511135446Strhodes } 512135446Strhodes if (dns_name_dynamic(msg->tsigname)) 513135446Strhodes dns_name_free(msg->tsigname, msg->mctx); 514135446Strhodes isc_mempool_put(msg->namepool, msg->tsigname); 515135446Strhodes msg->tsig = NULL; 516135446Strhodes msg->tsigname = NULL; 517135446Strhodes } else if (msg->querytsig != NULL && !replying) { 518135446Strhodes dns_rdataset_disassociate(msg->querytsig); 519135446Strhodes isc_mempool_put(msg->rdspool, msg->querytsig); 520135446Strhodes msg->querytsig = NULL; 521135446Strhodes } 522135446Strhodes if (msg->sig0 != NULL) { 523135446Strhodes INSIST(dns_rdataset_isassociated(msg->sig0)); 524135446Strhodes dns_rdataset_disassociate(msg->sig0); 525135446Strhodes isc_mempool_put(msg->rdspool, msg->sig0); 526135446Strhodes if (msg->sig0name != NULL) { 527135446Strhodes if (dns_name_dynamic(msg->sig0name)) 528135446Strhodes dns_name_free(msg->sig0name, msg->mctx); 529135446Strhodes isc_mempool_put(msg->namepool, msg->sig0name); 530135446Strhodes } 531135446Strhodes msg->sig0 = NULL; 532135446Strhodes msg->sig0name = NULL; 533135446Strhodes } 534135446Strhodes} 535135446Strhodes 536135446Strhodes/* 537135446Strhodes * Free all but one (or everything) for this message. This is used by 538135446Strhodes * both dns_message_reset() and dns_message_destroy(). 539135446Strhodes */ 540135446Strhodesstatic void 541135446Strhodesmsgreset(dns_message_t *msg, isc_boolean_t everything) { 542135446Strhodes dns_msgblock_t *msgblock, *next_msgblock; 543135446Strhodes isc_buffer_t *dynbuf, *next_dynbuf; 544135446Strhodes dns_rdata_t *rdata; 545135446Strhodes dns_rdatalist_t *rdatalist; 546135446Strhodes 547135446Strhodes msgresetnames(msg, 0); 548135446Strhodes msgresetopt(msg); 549135446Strhodes msgresetsigs(msg, ISC_FALSE); 550135446Strhodes 551135446Strhodes /* 552135446Strhodes * Clean up linked lists. 553135446Strhodes */ 554135446Strhodes 555135446Strhodes /* 556135446Strhodes * Run through the free lists, and just unlink anything found there. 557135446Strhodes * The memory isn't lost since these are part of message blocks we 558135446Strhodes * have allocated. 559135446Strhodes */ 560135446Strhodes rdata = ISC_LIST_HEAD(msg->freerdata); 561135446Strhodes while (rdata != NULL) { 562135446Strhodes ISC_LIST_UNLINK(msg->freerdata, rdata, link); 563135446Strhodes rdata = ISC_LIST_HEAD(msg->freerdata); 564135446Strhodes } 565135446Strhodes rdatalist = ISC_LIST_HEAD(msg->freerdatalist); 566135446Strhodes while (rdatalist != NULL) { 567135446Strhodes ISC_LIST_UNLINK(msg->freerdatalist, rdatalist, link); 568135446Strhodes rdatalist = ISC_LIST_HEAD(msg->freerdatalist); 569135446Strhodes } 570135446Strhodes 571135446Strhodes dynbuf = ISC_LIST_HEAD(msg->scratchpad); 572135446Strhodes INSIST(dynbuf != NULL); 573135446Strhodes if (!everything) { 574135446Strhodes isc_buffer_clear(dynbuf); 575135446Strhodes dynbuf = ISC_LIST_NEXT(dynbuf, link); 576135446Strhodes } 577135446Strhodes while (dynbuf != NULL) { 578135446Strhodes next_dynbuf = ISC_LIST_NEXT(dynbuf, link); 579135446Strhodes ISC_LIST_UNLINK(msg->scratchpad, dynbuf, link); 580135446Strhodes isc_buffer_free(&dynbuf); 581135446Strhodes dynbuf = next_dynbuf; 582135446Strhodes } 583135446Strhodes 584135446Strhodes msgblock = ISC_LIST_HEAD(msg->rdatas); 585135446Strhodes if (!everything && msgblock != NULL) { 586135446Strhodes msgblock_reset(msgblock); 587135446Strhodes msgblock = ISC_LIST_NEXT(msgblock, link); 588135446Strhodes } 589135446Strhodes while (msgblock != NULL) { 590135446Strhodes next_msgblock = ISC_LIST_NEXT(msgblock, link); 591135446Strhodes ISC_LIST_UNLINK(msg->rdatas, msgblock, link); 592135446Strhodes msgblock_free(msg->mctx, msgblock, sizeof(dns_rdata_t)); 593135446Strhodes msgblock = next_msgblock; 594135446Strhodes } 595135446Strhodes 596135446Strhodes /* 597135446Strhodes * rdatalists could be empty. 598135446Strhodes */ 599135446Strhodes 600135446Strhodes msgblock = ISC_LIST_HEAD(msg->rdatalists); 601135446Strhodes if (!everything && msgblock != NULL) { 602135446Strhodes msgblock_reset(msgblock); 603135446Strhodes msgblock = ISC_LIST_NEXT(msgblock, link); 604135446Strhodes } 605135446Strhodes while (msgblock != NULL) { 606135446Strhodes next_msgblock = ISC_LIST_NEXT(msgblock, link); 607135446Strhodes ISC_LIST_UNLINK(msg->rdatalists, msgblock, link); 608135446Strhodes msgblock_free(msg->mctx, msgblock, sizeof(dns_rdatalist_t)); 609135446Strhodes msgblock = next_msgblock; 610135446Strhodes } 611135446Strhodes 612135446Strhodes msgblock = ISC_LIST_HEAD(msg->offsets); 613135446Strhodes if (!everything && msgblock != NULL) { 614135446Strhodes msgblock_reset(msgblock); 615135446Strhodes msgblock = ISC_LIST_NEXT(msgblock, link); 616135446Strhodes } 617135446Strhodes while (msgblock != NULL) { 618135446Strhodes next_msgblock = ISC_LIST_NEXT(msgblock, link); 619135446Strhodes ISC_LIST_UNLINK(msg->offsets, msgblock, link); 620135446Strhodes msgblock_free(msg->mctx, msgblock, sizeof(dns_offsets_t)); 621135446Strhodes msgblock = next_msgblock; 622135446Strhodes } 623135446Strhodes 624135446Strhodes if (msg->tsigkey != NULL) { 625135446Strhodes dns_tsigkey_detach(&msg->tsigkey); 626135446Strhodes msg->tsigkey = NULL; 627135446Strhodes } 628135446Strhodes 629186462Sdougb if (msg->tsigctx != NULL) 630186462Sdougb dst_context_destroy(&msg->tsigctx); 631186462Sdougb 632135446Strhodes if (msg->query.base != NULL) { 633135446Strhodes if (msg->free_query != 0) 634135446Strhodes isc_mem_put(msg->mctx, msg->query.base, 635135446Strhodes msg->query.length); 636135446Strhodes msg->query.base = NULL; 637135446Strhodes msg->query.length = 0; 638135446Strhodes } 639135446Strhodes 640135446Strhodes if (msg->saved.base != NULL) { 641135446Strhodes if (msg->free_saved != 0) 642135446Strhodes isc_mem_put(msg->mctx, msg->saved.base, 643135446Strhodes msg->saved.length); 644135446Strhodes msg->saved.base = NULL; 645135446Strhodes msg->saved.length = 0; 646135446Strhodes } 647135446Strhodes 648135446Strhodes /* 649135446Strhodes * cleanup the buffer cleanup list 650135446Strhodes */ 651135446Strhodes dynbuf = ISC_LIST_HEAD(msg->cleanup); 652135446Strhodes while (dynbuf != NULL) { 653135446Strhodes next_dynbuf = ISC_LIST_NEXT(dynbuf, link); 654135446Strhodes ISC_LIST_UNLINK(msg->cleanup, dynbuf, link); 655135446Strhodes isc_buffer_free(&dynbuf); 656135446Strhodes dynbuf = next_dynbuf; 657135446Strhodes } 658135446Strhodes 659135446Strhodes /* 660135446Strhodes * Set other bits to normal default values. 661135446Strhodes */ 662135446Strhodes if (!everything) 663135446Strhodes msginit(msg); 664135446Strhodes 665135446Strhodes ENSURE(isc_mempool_getallocated(msg->namepool) == 0); 666135446Strhodes ENSURE(isc_mempool_getallocated(msg->rdspool) == 0); 667135446Strhodes} 668135446Strhodes 669135446Strhodesstatic unsigned int 670135446Strhodesspacefortsig(dns_tsigkey_t *key, int otherlen) { 671135446Strhodes isc_region_t r1, r2; 672135446Strhodes unsigned int x; 673135446Strhodes isc_result_t result; 674135446Strhodes 675135446Strhodes /* 676135446Strhodes * The space required for an TSIG record is: 677135446Strhodes * 678135446Strhodes * n1 bytes for the name 679135446Strhodes * 2 bytes for the type 680135446Strhodes * 2 bytes for the class 681135446Strhodes * 4 bytes for the ttl 682135446Strhodes * 2 bytes for the rdlength 683135446Strhodes * n2 bytes for the algorithm name 684135446Strhodes * 6 bytes for the time signed 685135446Strhodes * 2 bytes for the fudge 686135446Strhodes * 2 bytes for the MAC size 687135446Strhodes * x bytes for the MAC 688135446Strhodes * 2 bytes for the original id 689135446Strhodes * 2 bytes for the error 690135446Strhodes * 2 bytes for the other data length 691135446Strhodes * y bytes for the other data (at most) 692135446Strhodes * --------------------------------- 693135446Strhodes * 26 + n1 + n2 + x + y bytes 694135446Strhodes */ 695135446Strhodes 696135446Strhodes dns_name_toregion(&key->name, &r1); 697135446Strhodes dns_name_toregion(key->algorithm, &r2); 698135446Strhodes if (key->key == NULL) 699135446Strhodes x = 0; 700135446Strhodes else { 701135446Strhodes result = dst_key_sigsize(key->key, &x); 702135446Strhodes if (result != ISC_R_SUCCESS) 703135446Strhodes x = 0; 704135446Strhodes } 705135446Strhodes return (26 + r1.length + r2.length + x + otherlen); 706135446Strhodes} 707135446Strhodes 708135446Strhodesisc_result_t 709135446Strhodesdns_message_create(isc_mem_t *mctx, unsigned int intent, dns_message_t **msgp) 710135446Strhodes{ 711135446Strhodes dns_message_t *m; 712135446Strhodes isc_result_t result; 713135446Strhodes isc_buffer_t *dynbuf; 714135446Strhodes unsigned int i; 715135446Strhodes 716135446Strhodes REQUIRE(mctx != NULL); 717135446Strhodes REQUIRE(msgp != NULL); 718135446Strhodes REQUIRE(*msgp == NULL); 719135446Strhodes REQUIRE(intent == DNS_MESSAGE_INTENTPARSE 720135446Strhodes || intent == DNS_MESSAGE_INTENTRENDER); 721135446Strhodes 722135446Strhodes m = isc_mem_get(mctx, sizeof(dns_message_t)); 723135446Strhodes if (m == NULL) 724135446Strhodes return (ISC_R_NOMEMORY); 725135446Strhodes 726135446Strhodes /* 727135446Strhodes * No allocations until further notice. Just initialize all lists 728135446Strhodes * and other members that are freed in the cleanup phase here. 729135446Strhodes */ 730135446Strhodes 731135446Strhodes m->magic = DNS_MESSAGE_MAGIC; 732135446Strhodes m->from_to_wire = intent; 733135446Strhodes msginit(m); 734135446Strhodes 735135446Strhodes for (i = 0; i < DNS_SECTION_MAX; i++) 736135446Strhodes ISC_LIST_INIT(m->sections[i]); 737135446Strhodes 738254897Serwin m->mctx = NULL; 739254897Serwin isc_mem_attach(mctx, &m->mctx); 740254897Serwin 741135446Strhodes ISC_LIST_INIT(m->scratchpad); 742135446Strhodes ISC_LIST_INIT(m->cleanup); 743135446Strhodes m->namepool = NULL; 744135446Strhodes m->rdspool = NULL; 745135446Strhodes ISC_LIST_INIT(m->rdatas); 746135446Strhodes ISC_LIST_INIT(m->rdatalists); 747135446Strhodes ISC_LIST_INIT(m->offsets); 748135446Strhodes ISC_LIST_INIT(m->freerdata); 749135446Strhodes ISC_LIST_INIT(m->freerdatalist); 750135446Strhodes 751135446Strhodes /* 752135446Strhodes * Ok, it is safe to allocate (and then "goto cleanup" if failure) 753135446Strhodes */ 754135446Strhodes 755135446Strhodes result = isc_mempool_create(m->mctx, sizeof(dns_name_t), &m->namepool); 756135446Strhodes if (result != ISC_R_SUCCESS) 757135446Strhodes goto cleanup; 758135446Strhodes isc_mempool_setfreemax(m->namepool, NAME_COUNT); 759135446Strhodes isc_mempool_setname(m->namepool, "msg:names"); 760135446Strhodes 761135446Strhodes result = isc_mempool_create(m->mctx, sizeof(dns_rdataset_t), 762135446Strhodes &m->rdspool); 763135446Strhodes if (result != ISC_R_SUCCESS) 764135446Strhodes goto cleanup; 765135446Strhodes isc_mempool_setfreemax(m->rdspool, NAME_COUNT); 766135446Strhodes isc_mempool_setname(m->rdspool, "msg:rdataset"); 767135446Strhodes 768135446Strhodes dynbuf = NULL; 769135446Strhodes result = isc_buffer_allocate(mctx, &dynbuf, SCRATCHPAD_SIZE); 770135446Strhodes if (result != ISC_R_SUCCESS) 771135446Strhodes goto cleanup; 772135446Strhodes ISC_LIST_APPEND(m->scratchpad, dynbuf, link); 773135446Strhodes 774135446Strhodes m->cctx = NULL; 775135446Strhodes 776135446Strhodes *msgp = m; 777135446Strhodes return (ISC_R_SUCCESS); 778135446Strhodes 779135446Strhodes /* 780135446Strhodes * Cleanup for error returns. 781135446Strhodes */ 782135446Strhodes cleanup: 783135446Strhodes dynbuf = ISC_LIST_HEAD(m->scratchpad); 784135446Strhodes if (dynbuf != NULL) { 785135446Strhodes ISC_LIST_UNLINK(m->scratchpad, dynbuf, link); 786135446Strhodes isc_buffer_free(&dynbuf); 787135446Strhodes } 788135446Strhodes if (m->namepool != NULL) 789135446Strhodes isc_mempool_destroy(&m->namepool); 790135446Strhodes if (m->rdspool != NULL) 791135446Strhodes isc_mempool_destroy(&m->rdspool); 792135446Strhodes m->magic = 0; 793254897Serwin isc_mem_putanddetach(&mctx, m, sizeof(dns_message_t)); 794135446Strhodes 795135446Strhodes return (ISC_R_NOMEMORY); 796135446Strhodes} 797135446Strhodes 798135446Strhodesvoid 799135446Strhodesdns_message_reset(dns_message_t *msg, unsigned int intent) { 800135446Strhodes REQUIRE(DNS_MESSAGE_VALID(msg)); 801135446Strhodes REQUIRE(intent == DNS_MESSAGE_INTENTPARSE 802135446Strhodes || intent == DNS_MESSAGE_INTENTRENDER); 803135446Strhodes 804135446Strhodes msgreset(msg, ISC_FALSE); 805135446Strhodes msg->from_to_wire = intent; 806135446Strhodes} 807135446Strhodes 808135446Strhodesvoid 809135446Strhodesdns_message_destroy(dns_message_t **msgp) { 810135446Strhodes dns_message_t *msg; 811135446Strhodes 812135446Strhodes REQUIRE(msgp != NULL); 813135446Strhodes REQUIRE(DNS_MESSAGE_VALID(*msgp)); 814135446Strhodes 815135446Strhodes msg = *msgp; 816135446Strhodes *msgp = NULL; 817135446Strhodes 818135446Strhodes msgreset(msg, ISC_TRUE); 819135446Strhodes isc_mempool_destroy(&msg->namepool); 820135446Strhodes isc_mempool_destroy(&msg->rdspool); 821135446Strhodes msg->magic = 0; 822254897Serwin isc_mem_putanddetach(&msg->mctx, msg, sizeof(dns_message_t)); 823135446Strhodes} 824135446Strhodes 825135446Strhodesstatic isc_result_t 826135446Strhodesfindname(dns_name_t **foundname, dns_name_t *target, 827135446Strhodes dns_namelist_t *section) 828135446Strhodes{ 829135446Strhodes dns_name_t *curr; 830135446Strhodes 831135446Strhodes for (curr = ISC_LIST_TAIL(*section); 832135446Strhodes curr != NULL; 833135446Strhodes curr = ISC_LIST_PREV(curr, link)) { 834135446Strhodes if (dns_name_equal(curr, target)) { 835135446Strhodes if (foundname != NULL) 836135446Strhodes *foundname = curr; 837135446Strhodes return (ISC_R_SUCCESS); 838135446Strhodes } 839135446Strhodes } 840135446Strhodes 841135446Strhodes return (ISC_R_NOTFOUND); 842135446Strhodes} 843135446Strhodes 844135446Strhodesisc_result_t 845165071Sdougbdns_message_find(dns_name_t *name, dns_rdataclass_t rdclass, 846165071Sdougb dns_rdatatype_t type, dns_rdatatype_t covers, 847165071Sdougb dns_rdataset_t **rdataset) 848165071Sdougb{ 849165071Sdougb dns_rdataset_t *curr; 850165071Sdougb 851165071Sdougb if (rdataset != NULL) { 852165071Sdougb REQUIRE(*rdataset == NULL); 853165071Sdougb } 854165071Sdougb 855165071Sdougb for (curr = ISC_LIST_TAIL(name->list); 856165071Sdougb curr != NULL; 857165071Sdougb curr = ISC_LIST_PREV(curr, link)) { 858165071Sdougb if (curr->rdclass == rdclass && 859165071Sdougb curr->type == type && curr->covers == covers) { 860165071Sdougb if (rdataset != NULL) 861165071Sdougb *rdataset = curr; 862165071Sdougb return (ISC_R_SUCCESS); 863165071Sdougb } 864165071Sdougb } 865165071Sdougb 866165071Sdougb return (ISC_R_NOTFOUND); 867165071Sdougb} 868165071Sdougb 869165071Sdougbisc_result_t 870135446Strhodesdns_message_findtype(dns_name_t *name, dns_rdatatype_t type, 871135446Strhodes dns_rdatatype_t covers, dns_rdataset_t **rdataset) 872135446Strhodes{ 873135446Strhodes dns_rdataset_t *curr; 874135446Strhodes 875165071Sdougb REQUIRE(name != NULL); 876135446Strhodes if (rdataset != NULL) { 877135446Strhodes REQUIRE(*rdataset == NULL); 878135446Strhodes } 879135446Strhodes 880135446Strhodes for (curr = ISC_LIST_TAIL(name->list); 881135446Strhodes curr != NULL; 882135446Strhodes curr = ISC_LIST_PREV(curr, link)) { 883135446Strhodes if (curr->type == type && curr->covers == covers) { 884135446Strhodes if (rdataset != NULL) 885135446Strhodes *rdataset = curr; 886135446Strhodes return (ISC_R_SUCCESS); 887135446Strhodes } 888135446Strhodes } 889135446Strhodes 890135446Strhodes return (ISC_R_NOTFOUND); 891135446Strhodes} 892135446Strhodes 893135446Strhodes/* 894135446Strhodes * Read a name from buffer "source". 895135446Strhodes */ 896135446Strhodesstatic isc_result_t 897135446Strhodesgetname(dns_name_t *name, isc_buffer_t *source, dns_message_t *msg, 898135446Strhodes dns_decompress_t *dctx) 899135446Strhodes{ 900135446Strhodes isc_buffer_t *scratch; 901135446Strhodes isc_result_t result; 902135446Strhodes unsigned int tries; 903135446Strhodes 904135446Strhodes scratch = currentbuffer(msg); 905135446Strhodes 906135446Strhodes /* 907135446Strhodes * First try: use current buffer. 908135446Strhodes * Second try: allocate a new buffer and use that. 909135446Strhodes */ 910135446Strhodes tries = 0; 911135446Strhodes while (tries < 2) { 912135446Strhodes result = dns_name_fromwire(name, source, dctx, ISC_FALSE, 913135446Strhodes scratch); 914135446Strhodes 915135446Strhodes if (result == ISC_R_NOSPACE) { 916135446Strhodes tries++; 917135446Strhodes 918135446Strhodes result = newbuffer(msg, SCRATCHPAD_SIZE); 919135446Strhodes if (result != ISC_R_SUCCESS) 920135446Strhodes return (result); 921135446Strhodes 922135446Strhodes scratch = currentbuffer(msg); 923135446Strhodes dns_name_reset(name); 924135446Strhodes } else { 925135446Strhodes return (result); 926135446Strhodes } 927135446Strhodes } 928135446Strhodes 929135446Strhodes INSIST(0); /* Cannot get here... */ 930135446Strhodes return (ISC_R_UNEXPECTED); 931135446Strhodes} 932135446Strhodes 933135446Strhodesstatic isc_result_t 934135446Strhodesgetrdata(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, 935135446Strhodes dns_rdataclass_t rdclass, dns_rdatatype_t rdtype, 936135446Strhodes unsigned int rdatalen, dns_rdata_t *rdata) 937135446Strhodes{ 938135446Strhodes isc_buffer_t *scratch; 939135446Strhodes isc_result_t result; 940135446Strhodes unsigned int tries; 941135446Strhodes unsigned int trysize; 942135446Strhodes 943135446Strhodes scratch = currentbuffer(msg); 944135446Strhodes 945135446Strhodes isc_buffer_setactive(source, rdatalen); 946135446Strhodes 947135446Strhodes /* 948135446Strhodes * First try: use current buffer. 949135446Strhodes * Second try: allocate a new buffer of size 950135446Strhodes * max(SCRATCHPAD_SIZE, 2 * compressed_rdatalen) 951135446Strhodes * (the data will fit if it was not more than 50% compressed) 952135446Strhodes * Subsequent tries: double buffer size on each try. 953135446Strhodes */ 954135446Strhodes tries = 0; 955135446Strhodes trysize = 0; 956135446Strhodes /* XXX possibly change this to a while (tries < 2) loop */ 957135446Strhodes for (;;) { 958135446Strhodes result = dns_rdata_fromwire(rdata, rdclass, rdtype, 959135446Strhodes source, dctx, 0, 960135446Strhodes scratch); 961135446Strhodes 962135446Strhodes if (result == ISC_R_NOSPACE) { 963135446Strhodes if (tries == 0) { 964135446Strhodes trysize = 2 * rdatalen; 965135446Strhodes if (trysize < SCRATCHPAD_SIZE) 966135446Strhodes trysize = SCRATCHPAD_SIZE; 967135446Strhodes } else { 968135446Strhodes INSIST(trysize != 0); 969135446Strhodes if (trysize >= 65535) 970135446Strhodes return (ISC_R_NOSPACE); 971135446Strhodes /* XXX DNS_R_RRTOOLONG? */ 972135446Strhodes trysize *= 2; 973135446Strhodes } 974135446Strhodes tries++; 975135446Strhodes result = newbuffer(msg, trysize); 976135446Strhodes if (result != ISC_R_SUCCESS) 977135446Strhodes return (result); 978135446Strhodes 979135446Strhodes scratch = currentbuffer(msg); 980135446Strhodes } else { 981135446Strhodes return (result); 982135446Strhodes } 983135446Strhodes } 984135446Strhodes} 985135446Strhodes 986135446Strhodes#define DO_FORMERR \ 987135446Strhodes do { \ 988135446Strhodes if (best_effort) \ 989135446Strhodes seen_problem = ISC_TRUE; \ 990135446Strhodes else { \ 991135446Strhodes result = DNS_R_FORMERR; \ 992135446Strhodes goto cleanup; \ 993135446Strhodes } \ 994135446Strhodes } while (0) 995135446Strhodes 996135446Strhodesstatic isc_result_t 997135446Strhodesgetquestions(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, 998135446Strhodes unsigned int options) 999135446Strhodes{ 1000135446Strhodes isc_region_t r; 1001135446Strhodes unsigned int count; 1002135446Strhodes dns_name_t *name; 1003135446Strhodes dns_name_t *name2; 1004135446Strhodes dns_offsets_t *offsets; 1005135446Strhodes dns_rdataset_t *rdataset; 1006135446Strhodes dns_rdatalist_t *rdatalist; 1007135446Strhodes isc_result_t result; 1008135446Strhodes dns_rdatatype_t rdtype; 1009135446Strhodes dns_rdataclass_t rdclass; 1010135446Strhodes dns_namelist_t *section; 1011135446Strhodes isc_boolean_t free_name; 1012135446Strhodes isc_boolean_t best_effort; 1013135446Strhodes isc_boolean_t seen_problem; 1014135446Strhodes 1015135446Strhodes section = &msg->sections[DNS_SECTION_QUESTION]; 1016135446Strhodes 1017135446Strhodes best_effort = ISC_TF(options & DNS_MESSAGEPARSE_BESTEFFORT); 1018135446Strhodes seen_problem = ISC_FALSE; 1019135446Strhodes 1020135446Strhodes name = NULL; 1021135446Strhodes rdataset = NULL; 1022135446Strhodes rdatalist = NULL; 1023135446Strhodes 1024135446Strhodes for (count = 0; count < msg->counts[DNS_SECTION_QUESTION]; count++) { 1025135446Strhodes name = isc_mempool_get(msg->namepool); 1026135446Strhodes if (name == NULL) 1027135446Strhodes return (ISC_R_NOMEMORY); 1028135446Strhodes free_name = ISC_TRUE; 1029186462Sdougb 1030135446Strhodes offsets = newoffsets(msg); 1031135446Strhodes if (offsets == NULL) { 1032135446Strhodes result = ISC_R_NOMEMORY; 1033135446Strhodes goto cleanup; 1034135446Strhodes } 1035135446Strhodes dns_name_init(name, *offsets); 1036135446Strhodes 1037135446Strhodes /* 1038135446Strhodes * Parse the name out of this packet. 1039135446Strhodes */ 1040135446Strhodes isc_buffer_remainingregion(source, &r); 1041135446Strhodes isc_buffer_setactive(source, r.length); 1042135446Strhodes result = getname(name, source, msg, dctx); 1043135446Strhodes if (result != ISC_R_SUCCESS) 1044135446Strhodes goto cleanup; 1045135446Strhodes 1046135446Strhodes /* 1047135446Strhodes * Run through the section, looking to see if this name 1048135446Strhodes * is already there. If it is found, put back the allocated 1049135446Strhodes * name since we no longer need it, and set our name pointer 1050135446Strhodes * to point to the name we found. 1051135446Strhodes */ 1052135446Strhodes result = findname(&name2, name, section); 1053135446Strhodes 1054135446Strhodes /* 1055135446Strhodes * If it is the first name in the section, accept it. 1056135446Strhodes * 1057135446Strhodes * If it is not, but is not the same as the name already 1058135446Strhodes * in the question section, append to the section. Note that 1059135446Strhodes * here in the question section this is illegal, so return 1060135446Strhodes * FORMERR. In the future, check the opcode to see if 1061135446Strhodes * this should be legal or not. In either case we no longer 1062135446Strhodes * need this name pointer. 1063135446Strhodes */ 1064135446Strhodes if (result != ISC_R_SUCCESS) { 1065135446Strhodes if (!ISC_LIST_EMPTY(*section)) 1066135446Strhodes DO_FORMERR; 1067135446Strhodes ISC_LIST_APPEND(*section, name, link); 1068135446Strhodes free_name = ISC_FALSE; 1069135446Strhodes } else { 1070135446Strhodes isc_mempool_put(msg->namepool, name); 1071135446Strhodes name = name2; 1072135446Strhodes name2 = NULL; 1073135446Strhodes free_name = ISC_FALSE; 1074135446Strhodes } 1075135446Strhodes 1076135446Strhodes /* 1077135446Strhodes * Get type and class. 1078135446Strhodes */ 1079135446Strhodes isc_buffer_remainingregion(source, &r); 1080135446Strhodes if (r.length < 4) { 1081135446Strhodes result = ISC_R_UNEXPECTEDEND; 1082135446Strhodes goto cleanup; 1083135446Strhodes } 1084135446Strhodes rdtype = isc_buffer_getuint16(source); 1085135446Strhodes rdclass = isc_buffer_getuint16(source); 1086135446Strhodes 1087135446Strhodes /* 1088135446Strhodes * If this class is different than the one we already read, 1089135446Strhodes * this is an error. 1090135446Strhodes */ 1091292321Sdelphij if (msg->rdclass_set == 0) { 1092135446Strhodes msg->rdclass = rdclass; 1093292321Sdelphij msg->rdclass_set = 1; 1094135446Strhodes } else if (msg->rdclass != rdclass) 1095135446Strhodes DO_FORMERR; 1096135446Strhodes 1097135446Strhodes /* 1098292321Sdelphij * Is this a TKEY query? 1099292321Sdelphij */ 1100292321Sdelphij if (rdtype == dns_rdatatype_tkey) 1101292321Sdelphij msg->tkey = 1; 1102292321Sdelphij 1103292321Sdelphij /* 1104135446Strhodes * Can't ask the same question twice. 1105135446Strhodes */ 1106165071Sdougb result = dns_message_find(name, rdclass, rdtype, 0, NULL); 1107135446Strhodes if (result == ISC_R_SUCCESS) 1108135446Strhodes DO_FORMERR; 1109135446Strhodes 1110135446Strhodes /* 1111135446Strhodes * Allocate a new rdatalist. 1112135446Strhodes */ 1113135446Strhodes rdatalist = newrdatalist(msg); 1114135446Strhodes if (rdatalist == NULL) { 1115135446Strhodes result = ISC_R_NOMEMORY; 1116135446Strhodes goto cleanup; 1117135446Strhodes } 1118135446Strhodes rdataset = isc_mempool_get(msg->rdspool); 1119135446Strhodes if (rdataset == NULL) { 1120135446Strhodes result = ISC_R_NOMEMORY; 1121135446Strhodes goto cleanup; 1122135446Strhodes } 1123135446Strhodes 1124135446Strhodes /* 1125135446Strhodes * Convert rdatalist to rdataset, and attach the latter to 1126135446Strhodes * the name. 1127135446Strhodes */ 1128135446Strhodes rdatalist->type = rdtype; 1129135446Strhodes rdatalist->covers = 0; 1130135446Strhodes rdatalist->rdclass = rdclass; 1131135446Strhodes rdatalist->ttl = 0; 1132135446Strhodes ISC_LIST_INIT(rdatalist->rdata); 1133135446Strhodes 1134135446Strhodes dns_rdataset_init(rdataset); 1135135446Strhodes result = dns_rdatalist_tordataset(rdatalist, rdataset); 1136135446Strhodes if (result != ISC_R_SUCCESS) 1137135446Strhodes goto cleanup; 1138135446Strhodes 1139135446Strhodes rdataset->attributes |= DNS_RDATASETATTR_QUESTION; 1140135446Strhodes 1141135446Strhodes ISC_LIST_APPEND(name->list, rdataset, link); 1142135446Strhodes rdataset = NULL; 1143135446Strhodes } 1144135446Strhodes 1145135446Strhodes if (seen_problem) 1146135446Strhodes return (DNS_R_RECOVERABLE); 1147135446Strhodes return (ISC_R_SUCCESS); 1148135446Strhodes 1149135446Strhodes cleanup: 1150135446Strhodes if (rdataset != NULL) { 1151135446Strhodes INSIST(!dns_rdataset_isassociated(rdataset)); 1152135446Strhodes isc_mempool_put(msg->rdspool, rdataset); 1153135446Strhodes } 1154135446Strhodes#if 0 1155135446Strhodes if (rdatalist != NULL) 1156135446Strhodes isc_mempool_put(msg->rdlpool, rdatalist); 1157135446Strhodes#endif 1158135446Strhodes if (free_name) 1159135446Strhodes isc_mempool_put(msg->namepool, name); 1160135446Strhodes 1161135446Strhodes return (result); 1162135446Strhodes} 1163135446Strhodes 1164135446Strhodesstatic isc_boolean_t 1165135446Strhodesupdate(dns_section_t section, dns_rdataclass_t rdclass) { 1166135446Strhodes if (section == DNS_SECTION_PREREQUISITE) 1167135446Strhodes return (ISC_TF(rdclass == dns_rdataclass_any || 1168135446Strhodes rdclass == dns_rdataclass_none)); 1169135446Strhodes if (section == DNS_SECTION_UPDATE) 1170135446Strhodes return (ISC_TF(rdclass == dns_rdataclass_any)); 1171135446Strhodes return (ISC_FALSE); 1172135446Strhodes} 1173135446Strhodes 1174135446Strhodesstatic isc_result_t 1175135446Strhodesgetsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, 1176135446Strhodes dns_section_t sectionid, unsigned int options) 1177135446Strhodes{ 1178135446Strhodes isc_region_t r; 1179135446Strhodes unsigned int count, rdatalen; 1180135446Strhodes dns_name_t *name; 1181135446Strhodes dns_name_t *name2; 1182135446Strhodes dns_offsets_t *offsets; 1183135446Strhodes dns_rdataset_t *rdataset; 1184135446Strhodes dns_rdatalist_t *rdatalist; 1185135446Strhodes isc_result_t result; 1186135446Strhodes dns_rdatatype_t rdtype, covers; 1187135446Strhodes dns_rdataclass_t rdclass; 1188135446Strhodes dns_rdata_t *rdata; 1189135446Strhodes dns_ttl_t ttl; 1190135446Strhodes dns_namelist_t *section; 1191135446Strhodes isc_boolean_t free_name, free_rdataset; 1192135446Strhodes isc_boolean_t preserve_order, best_effort, seen_problem; 1193135446Strhodes isc_boolean_t issigzero; 1194135446Strhodes 1195135446Strhodes preserve_order = ISC_TF(options & DNS_MESSAGEPARSE_PRESERVEORDER); 1196135446Strhodes best_effort = ISC_TF(options & DNS_MESSAGEPARSE_BESTEFFORT); 1197135446Strhodes seen_problem = ISC_FALSE; 1198135446Strhodes 1199135446Strhodes for (count = 0; count < msg->counts[sectionid]; count++) { 1200135446Strhodes int recstart = source->current; 1201135446Strhodes isc_boolean_t skip_name_search, skip_type_search; 1202135446Strhodes 1203135446Strhodes section = &msg->sections[sectionid]; 1204135446Strhodes 1205135446Strhodes skip_name_search = ISC_FALSE; 1206135446Strhodes skip_type_search = ISC_FALSE; 1207135446Strhodes free_rdataset = ISC_FALSE; 1208135446Strhodes 1209135446Strhodes name = isc_mempool_get(msg->namepool); 1210135446Strhodes if (name == NULL) 1211135446Strhodes return (ISC_R_NOMEMORY); 1212135446Strhodes free_name = ISC_TRUE; 1213135446Strhodes 1214135446Strhodes offsets = newoffsets(msg); 1215135446Strhodes if (offsets == NULL) { 1216135446Strhodes result = ISC_R_NOMEMORY; 1217135446Strhodes goto cleanup; 1218135446Strhodes } 1219135446Strhodes dns_name_init(name, *offsets); 1220135446Strhodes 1221135446Strhodes /* 1222135446Strhodes * Parse the name out of this packet. 1223135446Strhodes */ 1224135446Strhodes isc_buffer_remainingregion(source, &r); 1225135446Strhodes isc_buffer_setactive(source, r.length); 1226135446Strhodes result = getname(name, source, msg, dctx); 1227135446Strhodes if (result != ISC_R_SUCCESS) 1228135446Strhodes goto cleanup; 1229135446Strhodes 1230135446Strhodes /* 1231135446Strhodes * Get type, class, ttl, and rdatalen. Verify that at least 1232135446Strhodes * rdatalen bytes remain. (Some of this is deferred to 1233135446Strhodes * later.) 1234135446Strhodes */ 1235135446Strhodes isc_buffer_remainingregion(source, &r); 1236135446Strhodes if (r.length < 2 + 2 + 4 + 2) { 1237135446Strhodes result = ISC_R_UNEXPECTEDEND; 1238135446Strhodes goto cleanup; 1239135446Strhodes } 1240135446Strhodes rdtype = isc_buffer_getuint16(source); 1241135446Strhodes rdclass = isc_buffer_getuint16(source); 1242135446Strhodes 1243135446Strhodes /* 1244135446Strhodes * If there was no question section, we may not yet have 1245135446Strhodes * established a class. Do so now. 1246135446Strhodes */ 1247292321Sdelphij if (msg->rdclass_set == 0 && 1248135446Strhodes rdtype != dns_rdatatype_opt && /* class is UDP SIZE */ 1249135446Strhodes rdtype != dns_rdatatype_tsig && /* class is ANY */ 1250135446Strhodes rdtype != dns_rdatatype_tkey) { /* class is undefined */ 1251135446Strhodes msg->rdclass = rdclass; 1252292321Sdelphij msg->rdclass_set = 1; 1253135446Strhodes } 1254135446Strhodes 1255135446Strhodes /* 1256135446Strhodes * If this class is different than the one in the question 1257135446Strhodes * section, bail. 1258135446Strhodes */ 1259135446Strhodes if (msg->opcode != dns_opcode_update 1260135446Strhodes && rdtype != dns_rdatatype_tsig 1261135446Strhodes && rdtype != dns_rdatatype_opt 1262292321Sdelphij && rdtype != dns_rdatatype_key /* in a TKEY query */ 1263135446Strhodes && rdtype != dns_rdatatype_sig /* SIG(0) */ 1264135446Strhodes && rdtype != dns_rdatatype_tkey /* Win2000 TKEY */ 1265165071Sdougb && msg->rdclass != dns_rdataclass_any 1266135446Strhodes && msg->rdclass != rdclass) 1267135446Strhodes DO_FORMERR; 1268135446Strhodes 1269135446Strhodes /* 1270292321Sdelphij * If this is not a TKEY query/response then the KEY 1271292321Sdelphij * record's class needs to match. 1272292321Sdelphij */ 1273292321Sdelphij if (msg->opcode != dns_opcode_update && !msg->tkey && 1274292321Sdelphij rdtype == dns_rdatatype_key && 1275292321Sdelphij msg->rdclass != dns_rdataclass_any && 1276292321Sdelphij msg->rdclass != rdclass) 1277292321Sdelphij DO_FORMERR; 1278292321Sdelphij 1279292321Sdelphij /* 1280135446Strhodes * Special type handling for TSIG, OPT, and TKEY. 1281135446Strhodes */ 1282135446Strhodes if (rdtype == dns_rdatatype_tsig) { 1283135446Strhodes /* 1284135446Strhodes * If it is a tsig, verify that it is in the 1285135446Strhodes * additional data section. 1286135446Strhodes */ 1287135446Strhodes if (sectionid != DNS_SECTION_ADDITIONAL || 1288135446Strhodes rdclass != dns_rdataclass_any || 1289135446Strhodes count != msg->counts[sectionid] - 1) 1290135446Strhodes DO_FORMERR; 1291135446Strhodes msg->sigstart = recstart; 1292135446Strhodes skip_name_search = ISC_TRUE; 1293135446Strhodes skip_type_search = ISC_TRUE; 1294135446Strhodes } else if (rdtype == dns_rdatatype_opt) { 1295135446Strhodes /* 1296135446Strhodes * The name of an OPT record must be ".", it 1297135446Strhodes * must be in the additional data section, and 1298135446Strhodes * it must be the first OPT we've seen. 1299135446Strhodes */ 1300135446Strhodes if (!dns_name_equal(dns_rootname, name) || 1301135446Strhodes msg->opt != NULL) 1302135446Strhodes DO_FORMERR; 1303135446Strhodes skip_name_search = ISC_TRUE; 1304135446Strhodes skip_type_search = ISC_TRUE; 1305135446Strhodes } else if (rdtype == dns_rdatatype_tkey) { 1306135446Strhodes /* 1307135446Strhodes * A TKEY must be in the additional section if this 1308135446Strhodes * is a query, and the answer section if this is a 1309135446Strhodes * response. Unless it's a Win2000 client. 1310135446Strhodes * 1311135446Strhodes * Its class is ignored. 1312135446Strhodes */ 1313135446Strhodes dns_section_t tkeysection; 1314135446Strhodes 1315135446Strhodes if ((msg->flags & DNS_MESSAGEFLAG_QR) == 0) 1316135446Strhodes tkeysection = DNS_SECTION_ADDITIONAL; 1317135446Strhodes else 1318135446Strhodes tkeysection = DNS_SECTION_ANSWER; 1319135446Strhodes if (sectionid != tkeysection && 1320135446Strhodes sectionid != DNS_SECTION_ANSWER) 1321135446Strhodes DO_FORMERR; 1322135446Strhodes } 1323135446Strhodes 1324135446Strhodes /* 1325135446Strhodes * ... now get ttl and rdatalen, and check buffer. 1326135446Strhodes */ 1327135446Strhodes ttl = isc_buffer_getuint32(source); 1328135446Strhodes rdatalen = isc_buffer_getuint16(source); 1329135446Strhodes r.length -= (2 + 2 + 4 + 2); 1330135446Strhodes if (r.length < rdatalen) { 1331135446Strhodes result = ISC_R_UNEXPECTEDEND; 1332135446Strhodes goto cleanup; 1333135446Strhodes } 1334135446Strhodes 1335135446Strhodes /* 1336135446Strhodes * Read the rdata from the wire format. Interpret the 1337135446Strhodes * rdata according to its actual class, even if it had a 1338135446Strhodes * DynDNS meta-class in the packet (unless this is a TSIG). 1339135446Strhodes * Then put the meta-class back into the finished rdata. 1340135446Strhodes */ 1341135446Strhodes rdata = newrdata(msg); 1342135446Strhodes if (rdata == NULL) { 1343135446Strhodes result = ISC_R_NOMEMORY; 1344135446Strhodes goto cleanup; 1345135446Strhodes } 1346135446Strhodes if (msg->opcode == dns_opcode_update && 1347135446Strhodes update(sectionid, rdclass)) { 1348135446Strhodes if (rdatalen != 0) { 1349135446Strhodes result = DNS_R_FORMERR; 1350135446Strhodes goto cleanup; 1351135446Strhodes } 1352135446Strhodes /* 1353135446Strhodes * When the rdata is empty, the data pointer is 1354186462Sdougb * never dereferenced, but it must still be non-NULL. 1355135446Strhodes * Casting 1 rather than "" avoids warnings about 1356135446Strhodes * discarding the const attribute of a string, 1357135446Strhodes * for compilers that would warn about such things. 1358135446Strhodes */ 1359135446Strhodes rdata->data = (unsigned char *)1; 1360135446Strhodes rdata->length = 0; 1361135446Strhodes rdata->rdclass = rdclass; 1362135446Strhodes rdata->type = rdtype; 1363135446Strhodes rdata->flags = DNS_RDATA_UPDATE; 1364135446Strhodes result = ISC_R_SUCCESS; 1365174187Sdougb } else if (rdclass == dns_rdataclass_none && 1366174187Sdougb msg->opcode == dns_opcode_update && 1367174187Sdougb sectionid == DNS_SECTION_UPDATE) { 1368174187Sdougb result = getrdata(source, msg, dctx, msg->rdclass, 1369174187Sdougb rdtype, rdatalen, rdata); 1370165071Sdougb } else 1371135446Strhodes result = getrdata(source, msg, dctx, rdclass, 1372135446Strhodes rdtype, rdatalen, rdata); 1373135446Strhodes if (result != ISC_R_SUCCESS) 1374135446Strhodes goto cleanup; 1375135446Strhodes rdata->rdclass = rdclass; 1376135446Strhodes issigzero = ISC_FALSE; 1377135446Strhodes if (rdtype == dns_rdatatype_rrsig && 1378135446Strhodes rdata->flags == 0) { 1379135446Strhodes covers = dns_rdata_covers(rdata); 1380135446Strhodes if (covers == 0) 1381135446Strhodes DO_FORMERR; 1382135446Strhodes } else if (rdtype == dns_rdatatype_sig /* SIG(0) */ && 1383135446Strhodes rdata->flags == 0) { 1384135446Strhodes covers = dns_rdata_covers(rdata); 1385135446Strhodes if (covers == 0) { 1386135446Strhodes if (sectionid != DNS_SECTION_ADDITIONAL || 1387135446Strhodes count != msg->counts[sectionid] - 1) 1388135446Strhodes DO_FORMERR; 1389135446Strhodes msg->sigstart = recstart; 1390135446Strhodes skip_name_search = ISC_TRUE; 1391135446Strhodes skip_type_search = ISC_TRUE; 1392135446Strhodes issigzero = ISC_TRUE; 1393292321Sdelphij } else { 1394292321Sdelphij if (msg->rdclass != dns_rdataclass_any && 1395292321Sdelphij msg->rdclass != rdclass) 1396292321Sdelphij DO_FORMERR; 1397135446Strhodes } 1398135446Strhodes } else 1399135446Strhodes covers = 0; 1400135446Strhodes 1401135446Strhodes /* 1402135446Strhodes * If we are doing a dynamic update or this is a meta-type, 1403135446Strhodes * don't bother searching for a name, just append this one 1404135446Strhodes * to the end of the message. 1405135446Strhodes */ 1406135446Strhodes if (preserve_order || msg->opcode == dns_opcode_update || 1407135446Strhodes skip_name_search) { 1408135446Strhodes if (rdtype != dns_rdatatype_opt && 1409135446Strhodes rdtype != dns_rdatatype_tsig && 1410135446Strhodes !issigzero) 1411135446Strhodes { 1412135446Strhodes ISC_LIST_APPEND(*section, name, link); 1413135446Strhodes free_name = ISC_FALSE; 1414135446Strhodes } 1415135446Strhodes } else { 1416135446Strhodes /* 1417135446Strhodes * Run through the section, looking to see if this name 1418135446Strhodes * is already there. If it is found, put back the 1419135446Strhodes * allocated name since we no longer need it, and set 1420135446Strhodes * our name pointer to point to the name we found. 1421135446Strhodes */ 1422135446Strhodes result = findname(&name2, name, section); 1423135446Strhodes 1424135446Strhodes /* 1425135446Strhodes * If it is a new name, append to the section. 1426135446Strhodes */ 1427135446Strhodes if (result == ISC_R_SUCCESS) { 1428135446Strhodes isc_mempool_put(msg->namepool, name); 1429135446Strhodes name = name2; 1430135446Strhodes } else { 1431135446Strhodes ISC_LIST_APPEND(*section, name, link); 1432135446Strhodes } 1433135446Strhodes free_name = ISC_FALSE; 1434135446Strhodes } 1435135446Strhodes 1436135446Strhodes /* 1437135446Strhodes * Search name for the particular type and class. 1438135446Strhodes * Skip this stage if in update mode or this is a meta-type. 1439135446Strhodes */ 1440135446Strhodes if (preserve_order || msg->opcode == dns_opcode_update || 1441135446Strhodes skip_type_search) 1442135446Strhodes result = ISC_R_NOTFOUND; 1443135446Strhodes else { 1444135446Strhodes /* 1445135446Strhodes * If this is a type that can only occur in 1446135446Strhodes * the question section, fail. 1447135446Strhodes */ 1448135446Strhodes if (dns_rdatatype_questiononly(rdtype)) 1449135446Strhodes DO_FORMERR; 1450135446Strhodes 1451135446Strhodes rdataset = NULL; 1452165071Sdougb result = dns_message_find(name, rdclass, rdtype, 1453165071Sdougb covers, &rdataset); 1454135446Strhodes } 1455135446Strhodes 1456135446Strhodes /* 1457135446Strhodes * If we found an rdataset that matches, we need to 1458135446Strhodes * append this rdata to that set. If we did not, we need 1459135446Strhodes * to create a new rdatalist, store the important bits there, 1460135446Strhodes * convert it to an rdataset, and link the latter to the name. 1461135446Strhodes * Yuck. When appending, make certain that the type isn't 1462135446Strhodes * a singleton type, such as SOA or CNAME. 1463135446Strhodes * 1464135446Strhodes * Note that this check will be bypassed when preserving order, 1465135446Strhodes * the opcode is an update, or the type search is skipped. 1466135446Strhodes */ 1467135446Strhodes if (result == ISC_R_SUCCESS) { 1468254402Serwin if (dns_rdatatype_issingleton(rdtype)) { 1469254402Serwin dns_rdata_t *first; 1470254402Serwin dns_rdatalist_fromrdataset(rdataset, 1471254402Serwin &rdatalist); 1472254402Serwin first = ISC_LIST_HEAD(rdatalist->rdata); 1473254402Serwin INSIST(first != NULL); 1474254402Serwin if (dns_rdata_compare(rdata, first) != 0) 1475254402Serwin DO_FORMERR; 1476254402Serwin } 1477135446Strhodes } 1478135446Strhodes 1479135446Strhodes if (result == ISC_R_NOTFOUND) { 1480135446Strhodes rdataset = isc_mempool_get(msg->rdspool); 1481135446Strhodes if (rdataset == NULL) { 1482135446Strhodes result = ISC_R_NOMEMORY; 1483135446Strhodes goto cleanup; 1484135446Strhodes } 1485135446Strhodes free_rdataset = ISC_TRUE; 1486135446Strhodes 1487135446Strhodes rdatalist = newrdatalist(msg); 1488135446Strhodes if (rdatalist == NULL) { 1489135446Strhodes result = ISC_R_NOMEMORY; 1490135446Strhodes goto cleanup; 1491135446Strhodes } 1492135446Strhodes 1493135446Strhodes rdatalist->type = rdtype; 1494135446Strhodes rdatalist->covers = covers; 1495135446Strhodes rdatalist->rdclass = rdclass; 1496135446Strhodes rdatalist->ttl = ttl; 1497135446Strhodes ISC_LIST_INIT(rdatalist->rdata); 1498135446Strhodes 1499135446Strhodes dns_rdataset_init(rdataset); 1500135446Strhodes RUNTIME_CHECK(dns_rdatalist_tordataset(rdatalist, 1501135446Strhodes rdataset) 1502135446Strhodes == ISC_R_SUCCESS); 1503135446Strhodes 1504186462Sdougb if (rdtype != dns_rdatatype_opt && 1505135446Strhodes rdtype != dns_rdatatype_tsig && 1506135446Strhodes !issigzero) 1507135446Strhodes { 1508135446Strhodes ISC_LIST_APPEND(name->list, rdataset, link); 1509135446Strhodes free_rdataset = ISC_FALSE; 1510135446Strhodes } 1511135446Strhodes } 1512135446Strhodes 1513135446Strhodes /* 1514135446Strhodes * Minimize TTLs. 1515135446Strhodes * 1516170222Sdougb * Section 5.2 of RFC2181 says we should drop 1517135446Strhodes * nonauthoritative rrsets where the TTLs differ, but we 1518135446Strhodes * currently treat them the as if they were authoritative and 1519135446Strhodes * minimize them. 1520135446Strhodes */ 1521135446Strhodes if (ttl != rdataset->ttl) { 1522135446Strhodes rdataset->attributes |= DNS_RDATASETATTR_TTLADJUSTED; 1523135446Strhodes if (ttl < rdataset->ttl) 1524135446Strhodes rdataset->ttl = ttl; 1525135446Strhodes } 1526135446Strhodes 1527193149Sdougb /* Append this rdata to the rdataset. */ 1528193149Sdougb dns_rdatalist_fromrdataset(rdataset, &rdatalist); 1529135446Strhodes ISC_LIST_APPEND(rdatalist->rdata, rdata, link); 1530135446Strhodes 1531135446Strhodes /* 1532135446Strhodes * If this is an OPT record, remember it. Also, set 1533135446Strhodes * the extended rcode. Note that msg->opt will only be set 1534135446Strhodes * if best-effort parsing is enabled. 1535135446Strhodes */ 1536135446Strhodes if (rdtype == dns_rdatatype_opt && msg->opt == NULL) { 1537135446Strhodes dns_rcode_t ercode; 1538135446Strhodes 1539135446Strhodes msg->opt = rdataset; 1540135446Strhodes rdataset = NULL; 1541135446Strhodes free_rdataset = ISC_FALSE; 1542135446Strhodes ercode = (dns_rcode_t) 1543135446Strhodes ((msg->opt->ttl & DNS_MESSAGE_EDNSRCODE_MASK) 1544135446Strhodes >> 20); 1545135446Strhodes msg->rcode |= ercode; 1546135446Strhodes isc_mempool_put(msg->namepool, name); 1547135446Strhodes free_name = ISC_FALSE; 1548135446Strhodes } 1549135446Strhodes 1550135446Strhodes /* 1551135446Strhodes * If this is an SIG(0) or TSIG record, remember it. Note 1552135446Strhodes * that msg->sig0 or msg->tsig will only be set if best-effort 1553135446Strhodes * parsing is enabled. 1554135446Strhodes */ 1555135446Strhodes if (issigzero && msg->sig0 == NULL) { 1556135446Strhodes msg->sig0 = rdataset; 1557135446Strhodes msg->sig0name = name; 1558135446Strhodes rdataset = NULL; 1559135446Strhodes free_rdataset = ISC_FALSE; 1560135446Strhodes free_name = ISC_FALSE; 1561135446Strhodes } else if (rdtype == dns_rdatatype_tsig && msg->tsig == NULL) { 1562135446Strhodes msg->tsig = rdataset; 1563135446Strhodes msg->tsigname = name; 1564218384Sdougb /* Windows doesn't like TSIG names to be compressed. */ 1565218384Sdougb msg->tsigname->attributes |= DNS_NAMEATTR_NOCOMPRESS; 1566135446Strhodes rdataset = NULL; 1567135446Strhodes free_rdataset = ISC_FALSE; 1568135446Strhodes free_name = ISC_FALSE; 1569135446Strhodes } 1570135446Strhodes 1571153816Sdougb if (seen_problem) { 1572153816Sdougb if (free_name) 1573153816Sdougb isc_mempool_put(msg->namepool, name); 1574153816Sdougb if (free_rdataset) 1575153816Sdougb isc_mempool_put(msg->rdspool, rdataset); 1576153816Sdougb free_name = free_rdataset = ISC_FALSE; 1577153816Sdougb } 1578135446Strhodes INSIST(free_name == ISC_FALSE); 1579135446Strhodes INSIST(free_rdataset == ISC_FALSE); 1580135446Strhodes } 1581135446Strhodes 1582135446Strhodes if (seen_problem) 1583135446Strhodes return (DNS_R_RECOVERABLE); 1584135446Strhodes return (ISC_R_SUCCESS); 1585135446Strhodes 1586135446Strhodes cleanup: 1587135446Strhodes if (free_name) 1588135446Strhodes isc_mempool_put(msg->namepool, name); 1589135446Strhodes if (free_rdataset) 1590135446Strhodes isc_mempool_put(msg->rdspool, rdataset); 1591135446Strhodes 1592135446Strhodes return (result); 1593135446Strhodes} 1594135446Strhodes 1595135446Strhodesisc_result_t 1596135446Strhodesdns_message_parse(dns_message_t *msg, isc_buffer_t *source, 1597135446Strhodes unsigned int options) 1598135446Strhodes{ 1599135446Strhodes isc_region_t r; 1600135446Strhodes dns_decompress_t dctx; 1601135446Strhodes isc_result_t ret; 1602135446Strhodes isc_uint16_t tmpflags; 1603135446Strhodes isc_buffer_t origsource; 1604135446Strhodes isc_boolean_t seen_problem; 1605135446Strhodes isc_boolean_t ignore_tc; 1606135446Strhodes 1607135446Strhodes REQUIRE(DNS_MESSAGE_VALID(msg)); 1608135446Strhodes REQUIRE(source != NULL); 1609135446Strhodes REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTPARSE); 1610135446Strhodes 1611135446Strhodes seen_problem = ISC_FALSE; 1612135446Strhodes ignore_tc = ISC_TF(options & DNS_MESSAGEPARSE_IGNORETRUNCATION); 1613135446Strhodes 1614135446Strhodes origsource = *source; 1615135446Strhodes 1616135446Strhodes msg->header_ok = 0; 1617135446Strhodes msg->question_ok = 0; 1618135446Strhodes 1619135446Strhodes isc_buffer_remainingregion(source, &r); 1620135446Strhodes if (r.length < DNS_MESSAGE_HEADERLEN) 1621135446Strhodes return (ISC_R_UNEXPECTEDEND); 1622135446Strhodes 1623135446Strhodes msg->id = isc_buffer_getuint16(source); 1624135446Strhodes tmpflags = isc_buffer_getuint16(source); 1625135446Strhodes msg->opcode = ((tmpflags & DNS_MESSAGE_OPCODE_MASK) 1626135446Strhodes >> DNS_MESSAGE_OPCODE_SHIFT); 1627135446Strhodes msg->rcode = (dns_rcode_t)(tmpflags & DNS_MESSAGE_RCODE_MASK); 1628135446Strhodes msg->flags = (tmpflags & DNS_MESSAGE_FLAG_MASK); 1629135446Strhodes msg->counts[DNS_SECTION_QUESTION] = isc_buffer_getuint16(source); 1630135446Strhodes msg->counts[DNS_SECTION_ANSWER] = isc_buffer_getuint16(source); 1631135446Strhodes msg->counts[DNS_SECTION_AUTHORITY] = isc_buffer_getuint16(source); 1632135446Strhodes msg->counts[DNS_SECTION_ADDITIONAL] = isc_buffer_getuint16(source); 1633135446Strhodes 1634135446Strhodes msg->header_ok = 1; 1635292321Sdelphij msg->state = DNS_SECTION_QUESTION; 1636135446Strhodes 1637135446Strhodes /* 1638135446Strhodes * -1 means no EDNS. 1639135446Strhodes */ 1640135446Strhodes dns_decompress_init(&dctx, -1, DNS_DECOMPRESS_ANY); 1641135446Strhodes 1642135446Strhodes dns_decompress_setmethods(&dctx, DNS_COMPRESS_GLOBAL14); 1643135446Strhodes 1644135446Strhodes ret = getquestions(source, msg, &dctx, options); 1645135446Strhodes if (ret == ISC_R_UNEXPECTEDEND && ignore_tc) 1646135446Strhodes goto truncated; 1647135446Strhodes if (ret == DNS_R_RECOVERABLE) { 1648135446Strhodes seen_problem = ISC_TRUE; 1649135446Strhodes ret = ISC_R_SUCCESS; 1650135446Strhodes } 1651135446Strhodes if (ret != ISC_R_SUCCESS) 1652135446Strhodes return (ret); 1653135446Strhodes msg->question_ok = 1; 1654135446Strhodes 1655135446Strhodes ret = getsection(source, msg, &dctx, DNS_SECTION_ANSWER, options); 1656135446Strhodes if (ret == ISC_R_UNEXPECTEDEND && ignore_tc) 1657135446Strhodes goto truncated; 1658135446Strhodes if (ret == DNS_R_RECOVERABLE) { 1659135446Strhodes seen_problem = ISC_TRUE; 1660135446Strhodes ret = ISC_R_SUCCESS; 1661135446Strhodes } 1662135446Strhodes if (ret != ISC_R_SUCCESS) 1663135446Strhodes return (ret); 1664135446Strhodes 1665135446Strhodes ret = getsection(source, msg, &dctx, DNS_SECTION_AUTHORITY, options); 1666135446Strhodes if (ret == ISC_R_UNEXPECTEDEND && ignore_tc) 1667135446Strhodes goto truncated; 1668135446Strhodes if (ret == DNS_R_RECOVERABLE) { 1669135446Strhodes seen_problem = ISC_TRUE; 1670135446Strhodes ret = ISC_R_SUCCESS; 1671135446Strhodes } 1672135446Strhodes if (ret != ISC_R_SUCCESS) 1673135446Strhodes return (ret); 1674135446Strhodes 1675135446Strhodes ret = getsection(source, msg, &dctx, DNS_SECTION_ADDITIONAL, options); 1676135446Strhodes if (ret == ISC_R_UNEXPECTEDEND && ignore_tc) 1677135446Strhodes goto truncated; 1678135446Strhodes if (ret == DNS_R_RECOVERABLE) { 1679135446Strhodes seen_problem = ISC_TRUE; 1680135446Strhodes ret = ISC_R_SUCCESS; 1681135446Strhodes } 1682135446Strhodes if (ret != ISC_R_SUCCESS) 1683135446Strhodes return (ret); 1684135446Strhodes 1685135446Strhodes isc_buffer_remainingregion(source, &r); 1686135446Strhodes if (r.length != 0) { 1687135446Strhodes isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL, 1688135446Strhodes DNS_LOGMODULE_MESSAGE, ISC_LOG_DEBUG(3), 1689135446Strhodes "message has %u byte(s) of trailing garbage", 1690135446Strhodes r.length); 1691135446Strhodes } 1692135446Strhodes 1693135446Strhodes truncated: 1694135446Strhodes if ((options & DNS_MESSAGEPARSE_CLONEBUFFER) == 0) 1695135446Strhodes isc_buffer_usedregion(&origsource, &msg->saved); 1696135446Strhodes else { 1697135446Strhodes msg->saved.length = isc_buffer_usedlength(&origsource); 1698135446Strhodes msg->saved.base = isc_mem_get(msg->mctx, msg->saved.length); 1699135446Strhodes if (msg->saved.base == NULL) 1700135446Strhodes return (ISC_R_NOMEMORY); 1701262706Serwin memmove(msg->saved.base, isc_buffer_base(&origsource), 1702262706Serwin msg->saved.length); 1703135446Strhodes msg->free_saved = 1; 1704135446Strhodes } 1705135446Strhodes 1706135446Strhodes if (ret == ISC_R_UNEXPECTEDEND && ignore_tc) 1707135446Strhodes return (DNS_R_RECOVERABLE); 1708135446Strhodes if (seen_problem == ISC_TRUE) 1709135446Strhodes return (DNS_R_RECOVERABLE); 1710135446Strhodes return (ISC_R_SUCCESS); 1711135446Strhodes} 1712135446Strhodes 1713135446Strhodesisc_result_t 1714135446Strhodesdns_message_renderbegin(dns_message_t *msg, dns_compress_t *cctx, 1715135446Strhodes isc_buffer_t *buffer) 1716135446Strhodes{ 1717135446Strhodes isc_region_t r; 1718135446Strhodes 1719135446Strhodes REQUIRE(DNS_MESSAGE_VALID(msg)); 1720135446Strhodes REQUIRE(buffer != NULL); 1721135446Strhodes REQUIRE(msg->buffer == NULL); 1722135446Strhodes REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER); 1723135446Strhodes 1724135446Strhodes msg->cctx = cctx; 1725135446Strhodes 1726135446Strhodes /* 1727135446Strhodes * Erase the contents of this buffer. 1728135446Strhodes */ 1729135446Strhodes isc_buffer_clear(buffer); 1730135446Strhodes 1731135446Strhodes /* 1732135446Strhodes * Make certain there is enough for at least the header in this 1733135446Strhodes * buffer. 1734135446Strhodes */ 1735135446Strhodes isc_buffer_availableregion(buffer, &r); 1736135446Strhodes if (r.length < DNS_MESSAGE_HEADERLEN) 1737135446Strhodes return (ISC_R_NOSPACE); 1738135446Strhodes 1739135446Strhodes if (r.length < msg->reserved) 1740135446Strhodes return (ISC_R_NOSPACE); 1741135446Strhodes 1742135446Strhodes /* 1743135446Strhodes * Reserve enough space for the header in this buffer. 1744135446Strhodes */ 1745135446Strhodes isc_buffer_add(buffer, DNS_MESSAGE_HEADERLEN); 1746135446Strhodes 1747135446Strhodes msg->buffer = buffer; 1748135446Strhodes 1749135446Strhodes return (ISC_R_SUCCESS); 1750135446Strhodes} 1751135446Strhodes 1752135446Strhodesisc_result_t 1753135446Strhodesdns_message_renderchangebuffer(dns_message_t *msg, isc_buffer_t *buffer) { 1754135446Strhodes isc_region_t r, rn; 1755135446Strhodes 1756135446Strhodes REQUIRE(DNS_MESSAGE_VALID(msg)); 1757135446Strhodes REQUIRE(buffer != NULL); 1758135446Strhodes REQUIRE(msg->buffer != NULL); 1759135446Strhodes 1760135446Strhodes /* 1761135446Strhodes * Ensure that the new buffer is empty, and has enough space to 1762135446Strhodes * hold the current contents. 1763135446Strhodes */ 1764135446Strhodes isc_buffer_clear(buffer); 1765135446Strhodes 1766135446Strhodes isc_buffer_availableregion(buffer, &rn); 1767135446Strhodes isc_buffer_usedregion(msg->buffer, &r); 1768135446Strhodes REQUIRE(rn.length > r.length); 1769135446Strhodes 1770135446Strhodes /* 1771135446Strhodes * Copy the contents from the old to the new buffer. 1772135446Strhodes */ 1773135446Strhodes isc_buffer_add(buffer, r.length); 1774262706Serwin memmove(rn.base, r.base, r.length); 1775135446Strhodes 1776135446Strhodes msg->buffer = buffer; 1777135446Strhodes 1778135446Strhodes return (ISC_R_SUCCESS); 1779135446Strhodes} 1780135446Strhodes 1781135446Strhodesvoid 1782135446Strhodesdns_message_renderrelease(dns_message_t *msg, unsigned int space) { 1783135446Strhodes REQUIRE(DNS_MESSAGE_VALID(msg)); 1784135446Strhodes REQUIRE(space <= msg->reserved); 1785135446Strhodes 1786135446Strhodes msg->reserved -= space; 1787135446Strhodes} 1788135446Strhodes 1789135446Strhodesisc_result_t 1790135446Strhodesdns_message_renderreserve(dns_message_t *msg, unsigned int space) { 1791135446Strhodes isc_region_t r; 1792135446Strhodes 1793135446Strhodes REQUIRE(DNS_MESSAGE_VALID(msg)); 1794135446Strhodes 1795135446Strhodes if (msg->buffer != NULL) { 1796135446Strhodes isc_buffer_availableregion(msg->buffer, &r); 1797135446Strhodes if (r.length < (space + msg->reserved)) 1798135446Strhodes return (ISC_R_NOSPACE); 1799135446Strhodes } 1800135446Strhodes 1801135446Strhodes msg->reserved += space; 1802135446Strhodes 1803135446Strhodes return (ISC_R_SUCCESS); 1804135446Strhodes} 1805135446Strhodes 1806135446Strhodesstatic inline isc_boolean_t 1807135446Strhodeswrong_priority(dns_rdataset_t *rds, int pass, dns_rdatatype_t preferred_glue) { 1808135446Strhodes int pass_needed; 1809135446Strhodes 1810135446Strhodes /* 1811135446Strhodes * If we are not rendering class IN, this ordering is bogus. 1812135446Strhodes */ 1813135446Strhodes if (rds->rdclass != dns_rdataclass_in) 1814135446Strhodes return (ISC_FALSE); 1815135446Strhodes 1816135446Strhodes switch (rds->type) { 1817135446Strhodes case dns_rdatatype_a: 1818135446Strhodes case dns_rdatatype_aaaa: 1819135446Strhodes if (preferred_glue == rds->type) 1820135446Strhodes pass_needed = 4; 1821135446Strhodes else 1822135446Strhodes pass_needed = 3; 1823135446Strhodes break; 1824135446Strhodes case dns_rdatatype_rrsig: 1825135446Strhodes case dns_rdatatype_dnskey: 1826135446Strhodes pass_needed = 2; 1827135446Strhodes break; 1828135446Strhodes default: 1829135446Strhodes pass_needed = 1; 1830135446Strhodes } 1831135446Strhodes 1832135446Strhodes if (pass_needed >= pass) 1833135446Strhodes return (ISC_FALSE); 1834135446Strhodes 1835135446Strhodes return (ISC_TRUE); 1836135446Strhodes} 1837135446Strhodes 1838224092Sdougb#ifdef ALLOW_FILTER_AAAA_ON_V4 1839224092Sdougb/* 1840224092Sdougb * Decide whether to not answer with an AAAA record and its RRSIG 1841224092Sdougb */ 1842224092Sdougbstatic inline isc_boolean_t 1843224092Sdougbnorender_rdataset(const dns_rdataset_t *rdataset, unsigned int options) 1844224092Sdougb{ 1845224092Sdougb switch (rdataset->type) { 1846224092Sdougb case dns_rdatatype_aaaa: 1847224092Sdougb if ((options & DNS_MESSAGERENDER_FILTER_AAAA) == 0) 1848224092Sdougb return (ISC_FALSE); 1849224092Sdougb break; 1850224092Sdougb 1851224092Sdougb case dns_rdatatype_rrsig: 1852224092Sdougb if ((options & DNS_MESSAGERENDER_FILTER_AAAA) == 0 || 1853224092Sdougb rdataset->covers != dns_rdatatype_aaaa) 1854224092Sdougb return (ISC_FALSE); 1855224092Sdougb break; 1856224092Sdougb 1857224092Sdougb default: 1858224092Sdougb return (ISC_FALSE); 1859224092Sdougb } 1860224092Sdougb 1861224092Sdougb if (rdataset->rdclass != dns_rdataclass_in) 1862224092Sdougb return (ISC_FALSE); 1863224092Sdougb 1864224092Sdougb return (ISC_TRUE); 1865224092Sdougb} 1866224092Sdougb 1867224092Sdougb#endif 1868135446Strhodesisc_result_t 1869135446Strhodesdns_message_rendersection(dns_message_t *msg, dns_section_t sectionid, 1870135446Strhodes unsigned int options) 1871135446Strhodes{ 1872135446Strhodes dns_namelist_t *section; 1873135446Strhodes dns_name_t *name, *next_name; 1874135446Strhodes dns_rdataset_t *rdataset, *next_rdataset; 1875135446Strhodes unsigned int count, total; 1876135446Strhodes isc_result_t result; 1877135446Strhodes isc_buffer_t st; /* for rollbacks */ 1878135446Strhodes int pass; 1879135446Strhodes isc_boolean_t partial = ISC_FALSE; 1880135446Strhodes unsigned int rd_options; 1881135446Strhodes dns_rdatatype_t preferred_glue = 0; 1882135446Strhodes 1883135446Strhodes REQUIRE(DNS_MESSAGE_VALID(msg)); 1884135446Strhodes REQUIRE(msg->buffer != NULL); 1885135446Strhodes REQUIRE(VALID_NAMED_SECTION(sectionid)); 1886135446Strhodes 1887135446Strhodes section = &msg->sections[sectionid]; 1888135446Strhodes 1889135446Strhodes if ((sectionid == DNS_SECTION_ADDITIONAL) 1890135446Strhodes && (options & DNS_MESSAGERENDER_ORDERED) == 0) { 1891135446Strhodes if ((options & DNS_MESSAGERENDER_PREFER_A) != 0) { 1892135446Strhodes preferred_glue = dns_rdatatype_a; 1893135446Strhodes pass = 4; 1894135446Strhodes } else if ((options & DNS_MESSAGERENDER_PREFER_AAAA) != 0) { 1895135446Strhodes preferred_glue = dns_rdatatype_aaaa; 1896135446Strhodes pass = 4; 1897135446Strhodes } else 1898135446Strhodes pass = 3; 1899135446Strhodes } else 1900135446Strhodes pass = 1; 1901135446Strhodes 1902135446Strhodes if ((options & DNS_MESSAGERENDER_OMITDNSSEC) == 0) 1903135446Strhodes rd_options = 0; 1904135446Strhodes else 1905135446Strhodes rd_options = DNS_RDATASETTOWIRE_OMITDNSSEC; 1906135446Strhodes 1907135446Strhodes /* 1908135446Strhodes * Shrink the space in the buffer by the reserved amount. 1909135446Strhodes */ 1910135446Strhodes msg->buffer->length -= msg->reserved; 1911135446Strhodes 1912135446Strhodes total = 0; 1913135446Strhodes if (msg->reserved == 0 && (options & DNS_MESSAGERENDER_PARTIAL) != 0) 1914135446Strhodes partial = ISC_TRUE; 1915135446Strhodes 1916153816Sdougb /* 1917153816Sdougb * Render required glue first. Set TC if it won't fit. 1918153816Sdougb */ 1919153816Sdougb name = ISC_LIST_HEAD(*section); 1920153816Sdougb if (name != NULL) { 1921153816Sdougb rdataset = ISC_LIST_HEAD(name->list); 1922153816Sdougb if (rdataset != NULL && 1923153816Sdougb (rdataset->attributes & DNS_RDATASETATTR_REQUIREDGLUE) != 0 && 1924153816Sdougb (rdataset->attributes & DNS_RDATASETATTR_RENDERED) == 0) { 1925165071Sdougb const void *order_arg = msg->order_arg; 1926153816Sdougb st = *(msg->buffer); 1927153816Sdougb count = 0; 1928153816Sdougb if (partial) 1929153816Sdougb result = dns_rdataset_towirepartial(rdataset, 1930153816Sdougb name, 1931153816Sdougb msg->cctx, 1932153816Sdougb msg->buffer, 1933153816Sdougb msg->order, 1934153816Sdougb order_arg, 1935153816Sdougb rd_options, 1936153816Sdougb &count, 1937153816Sdougb NULL); 1938153816Sdougb else 1939153816Sdougb result = dns_rdataset_towiresorted(rdataset, 1940153816Sdougb name, 1941153816Sdougb msg->cctx, 1942153816Sdougb msg->buffer, 1943153816Sdougb msg->order, 1944153816Sdougb order_arg, 1945153816Sdougb rd_options, 1946153816Sdougb &count); 1947153816Sdougb total += count; 1948153816Sdougb if (partial && result == ISC_R_NOSPACE) { 1949153816Sdougb msg->flags |= DNS_MESSAGEFLAG_TC; 1950153816Sdougb msg->buffer->length += msg->reserved; 1951153816Sdougb msg->counts[sectionid] += total; 1952153816Sdougb return (result); 1953153816Sdougb } 1954204619Sdougb if (result == ISC_R_NOSPACE) 1955204619Sdougb msg->flags |= DNS_MESSAGEFLAG_TC; 1956153816Sdougb if (result != ISC_R_SUCCESS) { 1957153816Sdougb INSIST(st.used < 65536); 1958153816Sdougb dns_compress_rollback(msg->cctx, 1959153816Sdougb (isc_uint16_t)st.used); 1960153816Sdougb *(msg->buffer) = st; /* rollback */ 1961153816Sdougb msg->buffer->length += msg->reserved; 1962153816Sdougb msg->counts[sectionid] += total; 1963153816Sdougb return (result); 1964153816Sdougb } 1965153816Sdougb rdataset->attributes |= DNS_RDATASETATTR_RENDERED; 1966153816Sdougb } 1967153816Sdougb } 1968153816Sdougb 1969135446Strhodes do { 1970135446Strhodes name = ISC_LIST_HEAD(*section); 1971135446Strhodes if (name == NULL) { 1972135446Strhodes msg->buffer->length += msg->reserved; 1973135446Strhodes msg->counts[sectionid] += total; 1974135446Strhodes return (ISC_R_SUCCESS); 1975135446Strhodes } 1976135446Strhodes 1977135446Strhodes while (name != NULL) { 1978135446Strhodes next_name = ISC_LIST_NEXT(name, link); 1979135446Strhodes 1980135446Strhodes rdataset = ISC_LIST_HEAD(name->list); 1981135446Strhodes while (rdataset != NULL) { 1982135446Strhodes next_rdataset = ISC_LIST_NEXT(rdataset, link); 1983135446Strhodes 1984135446Strhodes if ((rdataset->attributes & 1985135446Strhodes DNS_RDATASETATTR_RENDERED) != 0) 1986135446Strhodes goto next; 1987135446Strhodes 1988135446Strhodes if (((options & DNS_MESSAGERENDER_ORDERED) 1989135446Strhodes == 0) 1990135446Strhodes && (sectionid == DNS_SECTION_ADDITIONAL) 1991135446Strhodes && wrong_priority(rdataset, pass, 1992135446Strhodes preferred_glue)) 1993135446Strhodes goto next; 1994135446Strhodes 1995224092Sdougb#ifdef ALLOW_FILTER_AAAA_ON_V4 1996224092Sdougb /* 1997224092Sdougb * Suppress AAAAs if asked and we are 1998224092Sdougb * not doing DNSSEC or are breaking DNSSEC. 1999224092Sdougb * Say so in the AD bit if we break DNSSEC. 2000224092Sdougb */ 2001224092Sdougb if (norender_rdataset(rdataset, options) && 2002224092Sdougb sectionid != DNS_SECTION_QUESTION) { 2003224092Sdougb if (sectionid == DNS_SECTION_ANSWER || 2004224092Sdougb sectionid == DNS_SECTION_AUTHORITY) 2005224092Sdougb msg->flags &= ~DNS_MESSAGEFLAG_AD; 2006224092Sdougb if (OPTOUT(rdataset)) 2007224092Sdougb msg->flags &= ~DNS_MESSAGEFLAG_AD; 2008224092Sdougb goto next; 2009224092Sdougb } 2010224092Sdougb 2011224092Sdougb#endif 2012135446Strhodes st = *(msg->buffer); 2013135446Strhodes 2014135446Strhodes count = 0; 2015135446Strhodes if (partial) 2016135446Strhodes result = dns_rdataset_towirepartial( 2017135446Strhodes rdataset, 2018135446Strhodes name, 2019135446Strhodes msg->cctx, 2020135446Strhodes msg->buffer, 2021135446Strhodes msg->order, 2022135446Strhodes msg->order_arg, 2023135446Strhodes rd_options, 2024135446Strhodes &count, 2025135446Strhodes NULL); 2026135446Strhodes else 2027135446Strhodes result = dns_rdataset_towiresorted( 2028135446Strhodes rdataset, 2029135446Strhodes name, 2030135446Strhodes msg->cctx, 2031135446Strhodes msg->buffer, 2032135446Strhodes msg->order, 2033135446Strhodes msg->order_arg, 2034135446Strhodes rd_options, 2035135446Strhodes &count); 2036135446Strhodes 2037135446Strhodes total += count; 2038135446Strhodes 2039135446Strhodes /* 2040135446Strhodes * If out of space, record stats on what we 2041135446Strhodes * rendered so far, and return that status. 2042135446Strhodes * 2043135446Strhodes * XXXMLG Need to change this when 2044135446Strhodes * dns_rdataset_towire() can render partial 2045193149Sdougb * sets starting at some arbitrary point in the 2046135446Strhodes * set. This will include setting a bit in the 2047135446Strhodes * rdataset to indicate that a partial 2048135446Strhodes * rendering was done, and some state saved 2049135446Strhodes * somewhere (probably in the message struct) 2050135446Strhodes * to indicate where to continue from. 2051135446Strhodes */ 2052135446Strhodes if (partial && result == ISC_R_NOSPACE) { 2053135446Strhodes msg->buffer->length += msg->reserved; 2054135446Strhodes msg->counts[sectionid] += total; 2055135446Strhodes return (result); 2056135446Strhodes } 2057135446Strhodes if (result != ISC_R_SUCCESS) { 2058135446Strhodes INSIST(st.used < 65536); 2059135446Strhodes dns_compress_rollback(msg->cctx, 2060135446Strhodes (isc_uint16_t)st.used); 2061135446Strhodes *(msg->buffer) = st; /* rollback */ 2062135446Strhodes msg->buffer->length += msg->reserved; 2063135446Strhodes msg->counts[sectionid] += total; 2064135446Strhodes return (result); 2065135446Strhodes } 2066135446Strhodes 2067135446Strhodes /* 2068135446Strhodes * If we have rendered non-validated data, 2069135446Strhodes * ensure that the AD bit is not set. 2070135446Strhodes */ 2071135446Strhodes if (rdataset->trust != dns_trust_secure && 2072135446Strhodes (sectionid == DNS_SECTION_ANSWER || 2073135446Strhodes sectionid == DNS_SECTION_AUTHORITY)) 2074135446Strhodes msg->flags &= ~DNS_MESSAGEFLAG_AD; 2075193149Sdougb if (OPTOUT(rdataset)) 2076193149Sdougb msg->flags &= ~DNS_MESSAGEFLAG_AD; 2077135446Strhodes 2078135446Strhodes rdataset->attributes |= 2079135446Strhodes DNS_RDATASETATTR_RENDERED; 2080135446Strhodes 2081135446Strhodes next: 2082135446Strhodes rdataset = next_rdataset; 2083135446Strhodes } 2084135446Strhodes 2085135446Strhodes name = next_name; 2086135446Strhodes } 2087135446Strhodes } while (--pass != 0); 2088135446Strhodes 2089135446Strhodes msg->buffer->length += msg->reserved; 2090135446Strhodes msg->counts[sectionid] += total; 2091135446Strhodes 2092135446Strhodes return (ISC_R_SUCCESS); 2093135446Strhodes} 2094135446Strhodes 2095135446Strhodesvoid 2096135446Strhodesdns_message_renderheader(dns_message_t *msg, isc_buffer_t *target) { 2097135446Strhodes isc_uint16_t tmp; 2098135446Strhodes isc_region_t r; 2099135446Strhodes 2100135446Strhodes REQUIRE(DNS_MESSAGE_VALID(msg)); 2101135446Strhodes REQUIRE(target != NULL); 2102135446Strhodes 2103135446Strhodes isc_buffer_availableregion(target, &r); 2104135446Strhodes REQUIRE(r.length >= DNS_MESSAGE_HEADERLEN); 2105135446Strhodes 2106135446Strhodes isc_buffer_putuint16(target, msg->id); 2107135446Strhodes 2108135446Strhodes tmp = ((msg->opcode << DNS_MESSAGE_OPCODE_SHIFT) 2109135446Strhodes & DNS_MESSAGE_OPCODE_MASK); 2110135446Strhodes tmp |= (msg->rcode & DNS_MESSAGE_RCODE_MASK); 2111135446Strhodes tmp |= (msg->flags & DNS_MESSAGE_FLAG_MASK); 2112135446Strhodes 2113135446Strhodes INSIST(msg->counts[DNS_SECTION_QUESTION] < 65536 && 2114135446Strhodes msg->counts[DNS_SECTION_ANSWER] < 65536 && 2115135446Strhodes msg->counts[DNS_SECTION_AUTHORITY] < 65536 && 2116135446Strhodes msg->counts[DNS_SECTION_ADDITIONAL] < 65536); 2117135446Strhodes 2118135446Strhodes isc_buffer_putuint16(target, tmp); 2119135446Strhodes isc_buffer_putuint16(target, 2120135446Strhodes (isc_uint16_t)msg->counts[DNS_SECTION_QUESTION]); 2121135446Strhodes isc_buffer_putuint16(target, 2122135446Strhodes (isc_uint16_t)msg->counts[DNS_SECTION_ANSWER]); 2123135446Strhodes isc_buffer_putuint16(target, 2124135446Strhodes (isc_uint16_t)msg->counts[DNS_SECTION_AUTHORITY]); 2125135446Strhodes isc_buffer_putuint16(target, 2126135446Strhodes (isc_uint16_t)msg->counts[DNS_SECTION_ADDITIONAL]); 2127135446Strhodes} 2128135446Strhodes 2129135446Strhodesisc_result_t 2130135446Strhodesdns_message_renderend(dns_message_t *msg) { 2131135446Strhodes isc_buffer_t tmpbuf; 2132135446Strhodes isc_region_t r; 2133135446Strhodes int result; 2134135446Strhodes unsigned int count; 2135135446Strhodes 2136135446Strhodes REQUIRE(DNS_MESSAGE_VALID(msg)); 2137135446Strhodes REQUIRE(msg->buffer != NULL); 2138135446Strhodes 2139135446Strhodes if ((msg->rcode & ~DNS_MESSAGE_RCODE_MASK) != 0 && msg->opt == NULL) { 2140135446Strhodes /* 2141135446Strhodes * We have an extended rcode but are not using EDNS. 2142135446Strhodes */ 2143135446Strhodes return (DNS_R_FORMERR); 2144135446Strhodes } 2145135446Strhodes 2146135446Strhodes /* 2147254402Serwin * If we're adding a OPT, TSIG or SIG(0) to a truncated message, 2148254402Serwin * clear all rdatasets from the message except for the question 2149254402Serwin * before adding the OPT, TSIG or SIG(0). If the question doesn't 2150254402Serwin * fit, don't include it. 2151254402Serwin */ 2152254402Serwin if ((msg->tsigkey != NULL || msg->sig0key != NULL || msg->opt) && 2153254402Serwin (msg->flags & DNS_MESSAGEFLAG_TC) != 0) 2154254402Serwin { 2155254402Serwin isc_buffer_t *buf; 2156254402Serwin 2157254402Serwin msgresetnames(msg, DNS_SECTION_ANSWER); 2158254402Serwin buf = msg->buffer; 2159254402Serwin dns_message_renderreset(msg); 2160254402Serwin msg->buffer = buf; 2161254402Serwin isc_buffer_clear(msg->buffer); 2162254402Serwin isc_buffer_add(msg->buffer, DNS_MESSAGE_HEADERLEN); 2163254402Serwin dns_compress_rollback(msg->cctx, 0); 2164254402Serwin result = dns_message_rendersection(msg, DNS_SECTION_QUESTION, 2165254402Serwin 0); 2166254402Serwin if (result != ISC_R_SUCCESS && result != ISC_R_NOSPACE) 2167254402Serwin return (result); 2168254402Serwin } 2169254402Serwin 2170254402Serwin /* 2171135446Strhodes * If we've got an OPT record, render it. 2172135446Strhodes */ 2173135446Strhodes if (msg->opt != NULL) { 2174135446Strhodes dns_message_renderrelease(msg, msg->opt_reserved); 2175135446Strhodes msg->opt_reserved = 0; 2176135446Strhodes /* 2177135446Strhodes * Set the extended rcode. 2178135446Strhodes */ 2179135446Strhodes msg->opt->ttl &= ~DNS_MESSAGE_EDNSRCODE_MASK; 2180135446Strhodes msg->opt->ttl |= ((msg->rcode << 20) & 2181135446Strhodes DNS_MESSAGE_EDNSRCODE_MASK); 2182135446Strhodes /* 2183135446Strhodes * Render. 2184135446Strhodes */ 2185135446Strhodes count = 0; 2186135446Strhodes result = dns_rdataset_towire(msg->opt, dns_rootname, 2187135446Strhodes msg->cctx, msg->buffer, 0, 2188135446Strhodes &count); 2189135446Strhodes msg->counts[DNS_SECTION_ADDITIONAL] += count; 2190135446Strhodes if (result != ISC_R_SUCCESS) 2191135446Strhodes return (result); 2192135446Strhodes } 2193135446Strhodes 2194135446Strhodes /* 2195135446Strhodes * If we're adding a TSIG record, generate and render it. 2196135446Strhodes */ 2197135446Strhodes if (msg->tsigkey != NULL) { 2198135446Strhodes dns_message_renderrelease(msg, msg->sig_reserved); 2199135446Strhodes msg->sig_reserved = 0; 2200135446Strhodes result = dns_tsig_sign(msg); 2201135446Strhodes if (result != ISC_R_SUCCESS) 2202135446Strhodes return (result); 2203135446Strhodes count = 0; 2204135446Strhodes result = dns_rdataset_towire(msg->tsig, msg->tsigname, 2205135446Strhodes msg->cctx, msg->buffer, 0, 2206135446Strhodes &count); 2207135446Strhodes msg->counts[DNS_SECTION_ADDITIONAL] += count; 2208135446Strhodes if (result != ISC_R_SUCCESS) 2209135446Strhodes return (result); 2210135446Strhodes } 2211135446Strhodes 2212135446Strhodes /* 2213135446Strhodes * If we're adding a SIG(0) record, generate and render it. 2214135446Strhodes */ 2215135446Strhodes if (msg->sig0key != NULL) { 2216135446Strhodes dns_message_renderrelease(msg, msg->sig_reserved); 2217135446Strhodes msg->sig_reserved = 0; 2218135446Strhodes result = dns_dnssec_signmessage(msg, msg->sig0key); 2219135446Strhodes if (result != ISC_R_SUCCESS) 2220135446Strhodes return (result); 2221135446Strhodes count = 0; 2222135446Strhodes /* 2223135446Strhodes * Note: dns_rootname is used here, not msg->sig0name, since 2224135446Strhodes * the owner name of a SIG(0) is irrelevant, and will not 2225135446Strhodes * be set in a message being rendered. 2226135446Strhodes */ 2227135446Strhodes result = dns_rdataset_towire(msg->sig0, dns_rootname, 2228135446Strhodes msg->cctx, msg->buffer, 0, 2229135446Strhodes &count); 2230135446Strhodes msg->counts[DNS_SECTION_ADDITIONAL] += count; 2231135446Strhodes if (result != ISC_R_SUCCESS) 2232135446Strhodes return (result); 2233135446Strhodes } 2234135446Strhodes 2235135446Strhodes isc_buffer_usedregion(msg->buffer, &r); 2236135446Strhodes isc_buffer_init(&tmpbuf, r.base, r.length); 2237135446Strhodes 2238135446Strhodes dns_message_renderheader(msg, &tmpbuf); 2239135446Strhodes 2240135446Strhodes msg->buffer = NULL; /* forget about this buffer only on success XXX */ 2241135446Strhodes 2242135446Strhodes return (ISC_R_SUCCESS); 2243135446Strhodes} 2244135446Strhodes 2245135446Strhodesvoid 2246135446Strhodesdns_message_renderreset(dns_message_t *msg) { 2247135446Strhodes unsigned int i; 2248135446Strhodes dns_name_t *name; 2249135446Strhodes dns_rdataset_t *rds; 2250135446Strhodes 2251135446Strhodes /* 2252135446Strhodes * Reset the message so that it may be rendered again. 2253135446Strhodes */ 2254135446Strhodes 2255135446Strhodes REQUIRE(DNS_MESSAGE_VALID(msg)); 2256135446Strhodes REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER); 2257135446Strhodes 2258135446Strhodes msg->buffer = NULL; 2259135446Strhodes 2260135446Strhodes for (i = 0; i < DNS_SECTION_MAX; i++) { 2261135446Strhodes msg->cursors[i] = NULL; 2262135446Strhodes msg->counts[i] = 0; 2263135446Strhodes for (name = ISC_LIST_HEAD(msg->sections[i]); 2264135446Strhodes name != NULL; 2265135446Strhodes name = ISC_LIST_NEXT(name, link)) { 2266135446Strhodes for (rds = ISC_LIST_HEAD(name->list); 2267135446Strhodes rds != NULL; 2268135446Strhodes rds = ISC_LIST_NEXT(rds, link)) { 2269135446Strhodes rds->attributes &= ~DNS_RDATASETATTR_RENDERED; 2270135446Strhodes } 2271135446Strhodes } 2272135446Strhodes } 2273135446Strhodes if (msg->tsigname != NULL) 2274135446Strhodes dns_message_puttempname(msg, &msg->tsigname); 2275135446Strhodes if (msg->tsig != NULL) { 2276135446Strhodes dns_rdataset_disassociate(msg->tsig); 2277135446Strhodes dns_message_puttemprdataset(msg, &msg->tsig); 2278135446Strhodes } 2279135446Strhodes if (msg->sig0 != NULL) { 2280135446Strhodes dns_rdataset_disassociate(msg->sig0); 2281135446Strhodes dns_message_puttemprdataset(msg, &msg->sig0); 2282135446Strhodes } 2283135446Strhodes} 2284135446Strhodes 2285135446Strhodesisc_result_t 2286135446Strhodesdns_message_firstname(dns_message_t *msg, dns_section_t section) { 2287135446Strhodes REQUIRE(DNS_MESSAGE_VALID(msg)); 2288135446Strhodes REQUIRE(VALID_NAMED_SECTION(section)); 2289135446Strhodes 2290135446Strhodes msg->cursors[section] = ISC_LIST_HEAD(msg->sections[section]); 2291135446Strhodes 2292135446Strhodes if (msg->cursors[section] == NULL) 2293135446Strhodes return (ISC_R_NOMORE); 2294135446Strhodes 2295135446Strhodes return (ISC_R_SUCCESS); 2296135446Strhodes} 2297135446Strhodes 2298135446Strhodesisc_result_t 2299135446Strhodesdns_message_nextname(dns_message_t *msg, dns_section_t section) { 2300135446Strhodes REQUIRE(DNS_MESSAGE_VALID(msg)); 2301135446Strhodes REQUIRE(VALID_NAMED_SECTION(section)); 2302135446Strhodes REQUIRE(msg->cursors[section] != NULL); 2303135446Strhodes 2304135446Strhodes msg->cursors[section] = ISC_LIST_NEXT(msg->cursors[section], link); 2305135446Strhodes 2306135446Strhodes if (msg->cursors[section] == NULL) 2307135446Strhodes return (ISC_R_NOMORE); 2308135446Strhodes 2309135446Strhodes return (ISC_R_SUCCESS); 2310135446Strhodes} 2311135446Strhodes 2312135446Strhodesvoid 2313135446Strhodesdns_message_currentname(dns_message_t *msg, dns_section_t section, 2314135446Strhodes dns_name_t **name) 2315135446Strhodes{ 2316135446Strhodes REQUIRE(DNS_MESSAGE_VALID(msg)); 2317135446Strhodes REQUIRE(VALID_NAMED_SECTION(section)); 2318135446Strhodes REQUIRE(name != NULL && *name == NULL); 2319135446Strhodes REQUIRE(msg->cursors[section] != NULL); 2320135446Strhodes 2321135446Strhodes *name = msg->cursors[section]; 2322135446Strhodes} 2323135446Strhodes 2324135446Strhodesisc_result_t 2325135446Strhodesdns_message_findname(dns_message_t *msg, dns_section_t section, 2326135446Strhodes dns_name_t *target, dns_rdatatype_t type, 2327135446Strhodes dns_rdatatype_t covers, dns_name_t **name, 2328135446Strhodes dns_rdataset_t **rdataset) 2329135446Strhodes{ 2330135446Strhodes dns_name_t *foundname; 2331135446Strhodes isc_result_t result; 2332135446Strhodes 2333135446Strhodes /* 2334135446Strhodes * XXX These requirements are probably too intensive, especially 2335135446Strhodes * where things can be NULL, but as they are they ensure that if 2336135446Strhodes * something is NON-NULL, indicating that the caller expects it 2337135446Strhodes * to be filled in, that we can in fact fill it in. 2338135446Strhodes */ 2339135446Strhodes REQUIRE(msg != NULL); 2340135446Strhodes REQUIRE(VALID_SECTION(section)); 2341135446Strhodes REQUIRE(target != NULL); 2342135446Strhodes if (name != NULL) 2343135446Strhodes REQUIRE(*name == NULL); 2344135446Strhodes if (type == dns_rdatatype_any) { 2345135446Strhodes REQUIRE(rdataset == NULL); 2346135446Strhodes } else { 2347135446Strhodes if (rdataset != NULL) 2348135446Strhodes REQUIRE(*rdataset == NULL); 2349135446Strhodes } 2350135446Strhodes 2351135446Strhodes result = findname(&foundname, target, 2352135446Strhodes &msg->sections[section]); 2353135446Strhodes 2354135446Strhodes if (result == ISC_R_NOTFOUND) 2355135446Strhodes return (DNS_R_NXDOMAIN); 2356135446Strhodes else if (result != ISC_R_SUCCESS) 2357135446Strhodes return (result); 2358135446Strhodes 2359135446Strhodes if (name != NULL) 2360135446Strhodes *name = foundname; 2361135446Strhodes 2362135446Strhodes /* 2363135446Strhodes * And now look for the type. 2364135446Strhodes */ 2365135446Strhodes if (type == dns_rdatatype_any) 2366135446Strhodes return (ISC_R_SUCCESS); 2367135446Strhodes 2368135446Strhodes result = dns_message_findtype(foundname, type, covers, rdataset); 2369135446Strhodes if (result == ISC_R_NOTFOUND) 2370135446Strhodes return (DNS_R_NXRRSET); 2371135446Strhodes 2372135446Strhodes return (result); 2373135446Strhodes} 2374135446Strhodes 2375135446Strhodesvoid 2376135446Strhodesdns_message_movename(dns_message_t *msg, dns_name_t *name, 2377135446Strhodes dns_section_t fromsection, 2378135446Strhodes dns_section_t tosection) 2379135446Strhodes{ 2380135446Strhodes REQUIRE(msg != NULL); 2381135446Strhodes REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER); 2382135446Strhodes REQUIRE(name != NULL); 2383135446Strhodes REQUIRE(VALID_NAMED_SECTION(fromsection)); 2384135446Strhodes REQUIRE(VALID_NAMED_SECTION(tosection)); 2385135446Strhodes 2386135446Strhodes /* 2387135446Strhodes * Unlink the name from the old section 2388135446Strhodes */ 2389135446Strhodes ISC_LIST_UNLINK(msg->sections[fromsection], name, link); 2390135446Strhodes ISC_LIST_APPEND(msg->sections[tosection], name, link); 2391135446Strhodes} 2392135446Strhodes 2393135446Strhodesvoid 2394135446Strhodesdns_message_addname(dns_message_t *msg, dns_name_t *name, 2395135446Strhodes dns_section_t section) 2396135446Strhodes{ 2397135446Strhodes REQUIRE(msg != NULL); 2398135446Strhodes REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER); 2399135446Strhodes REQUIRE(name != NULL); 2400135446Strhodes REQUIRE(VALID_NAMED_SECTION(section)); 2401135446Strhodes 2402135446Strhodes ISC_LIST_APPEND(msg->sections[section], name, link); 2403135446Strhodes} 2404135446Strhodes 2405170222Sdougbvoid 2406170222Sdougbdns_message_removename(dns_message_t *msg, dns_name_t *name, 2407170222Sdougb dns_section_t section) 2408170222Sdougb{ 2409170222Sdougb REQUIRE(msg != NULL); 2410170222Sdougb REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER); 2411170222Sdougb REQUIRE(name != NULL); 2412170222Sdougb REQUIRE(VALID_NAMED_SECTION(section)); 2413170222Sdougb 2414170222Sdougb ISC_LIST_UNLINK(msg->sections[section], name, link); 2415170222Sdougb} 2416170222Sdougb 2417135446Strhodesisc_result_t 2418135446Strhodesdns_message_gettempname(dns_message_t *msg, dns_name_t **item) { 2419135446Strhodes REQUIRE(DNS_MESSAGE_VALID(msg)); 2420135446Strhodes REQUIRE(item != NULL && *item == NULL); 2421135446Strhodes 2422135446Strhodes *item = isc_mempool_get(msg->namepool); 2423135446Strhodes if (*item == NULL) 2424135446Strhodes return (ISC_R_NOMEMORY); 2425135446Strhodes dns_name_init(*item, NULL); 2426135446Strhodes 2427135446Strhodes return (ISC_R_SUCCESS); 2428135446Strhodes} 2429135446Strhodes 2430135446Strhodesisc_result_t 2431135446Strhodesdns_message_gettempoffsets(dns_message_t *msg, dns_offsets_t **item) { 2432135446Strhodes REQUIRE(DNS_MESSAGE_VALID(msg)); 2433135446Strhodes REQUIRE(item != NULL && *item == NULL); 2434135446Strhodes 2435135446Strhodes *item = newoffsets(msg); 2436135446Strhodes if (*item == NULL) 2437135446Strhodes return (ISC_R_NOMEMORY); 2438135446Strhodes 2439135446Strhodes return (ISC_R_SUCCESS); 2440135446Strhodes} 2441135446Strhodes 2442135446Strhodesisc_result_t 2443135446Strhodesdns_message_gettemprdata(dns_message_t *msg, dns_rdata_t **item) { 2444135446Strhodes REQUIRE(DNS_MESSAGE_VALID(msg)); 2445135446Strhodes REQUIRE(item != NULL && *item == NULL); 2446135446Strhodes 2447135446Strhodes *item = newrdata(msg); 2448135446Strhodes if (*item == NULL) 2449135446Strhodes return (ISC_R_NOMEMORY); 2450135446Strhodes 2451135446Strhodes return (ISC_R_SUCCESS); 2452135446Strhodes} 2453135446Strhodes 2454135446Strhodesisc_result_t 2455135446Strhodesdns_message_gettemprdataset(dns_message_t *msg, dns_rdataset_t **item) { 2456135446Strhodes REQUIRE(DNS_MESSAGE_VALID(msg)); 2457135446Strhodes REQUIRE(item != NULL && *item == NULL); 2458135446Strhodes 2459135446Strhodes *item = isc_mempool_get(msg->rdspool); 2460135446Strhodes if (*item == NULL) 2461135446Strhodes return (ISC_R_NOMEMORY); 2462135446Strhodes 2463135446Strhodes dns_rdataset_init(*item); 2464135446Strhodes 2465135446Strhodes return (ISC_R_SUCCESS); 2466135446Strhodes} 2467135446Strhodes 2468135446Strhodesisc_result_t 2469135446Strhodesdns_message_gettemprdatalist(dns_message_t *msg, dns_rdatalist_t **item) { 2470135446Strhodes REQUIRE(DNS_MESSAGE_VALID(msg)); 2471135446Strhodes REQUIRE(item != NULL && *item == NULL); 2472135446Strhodes 2473135446Strhodes *item = newrdatalist(msg); 2474135446Strhodes if (*item == NULL) 2475135446Strhodes return (ISC_R_NOMEMORY); 2476135446Strhodes 2477135446Strhodes return (ISC_R_SUCCESS); 2478135446Strhodes} 2479135446Strhodes 2480135446Strhodesvoid 2481135446Strhodesdns_message_puttempname(dns_message_t *msg, dns_name_t **item) { 2482135446Strhodes REQUIRE(DNS_MESSAGE_VALID(msg)); 2483135446Strhodes REQUIRE(item != NULL && *item != NULL); 2484135446Strhodes 2485135446Strhodes if (dns_name_dynamic(*item)) 2486135446Strhodes dns_name_free(*item, msg->mctx); 2487135446Strhodes isc_mempool_put(msg->namepool, *item); 2488135446Strhodes *item = NULL; 2489135446Strhodes} 2490135446Strhodes 2491135446Strhodesvoid 2492135446Strhodesdns_message_puttemprdata(dns_message_t *msg, dns_rdata_t **item) { 2493135446Strhodes REQUIRE(DNS_MESSAGE_VALID(msg)); 2494135446Strhodes REQUIRE(item != NULL && *item != NULL); 2495135446Strhodes 2496135446Strhodes releaserdata(msg, *item); 2497135446Strhodes *item = NULL; 2498135446Strhodes} 2499135446Strhodes 2500135446Strhodesvoid 2501135446Strhodesdns_message_puttemprdataset(dns_message_t *msg, dns_rdataset_t **item) { 2502135446Strhodes REQUIRE(DNS_MESSAGE_VALID(msg)); 2503135446Strhodes REQUIRE(item != NULL && *item != NULL); 2504135446Strhodes 2505135446Strhodes REQUIRE(!dns_rdataset_isassociated(*item)); 2506135446Strhodes isc_mempool_put(msg->rdspool, *item); 2507135446Strhodes *item = NULL; 2508135446Strhodes} 2509135446Strhodes 2510135446Strhodesvoid 2511135446Strhodesdns_message_puttemprdatalist(dns_message_t *msg, dns_rdatalist_t **item) { 2512135446Strhodes REQUIRE(DNS_MESSAGE_VALID(msg)); 2513135446Strhodes REQUIRE(item != NULL && *item != NULL); 2514135446Strhodes 2515135446Strhodes releaserdatalist(msg, *item); 2516135446Strhodes *item = NULL; 2517135446Strhodes} 2518135446Strhodes 2519135446Strhodesisc_result_t 2520135446Strhodesdns_message_peekheader(isc_buffer_t *source, dns_messageid_t *idp, 2521135446Strhodes unsigned int *flagsp) 2522135446Strhodes{ 2523135446Strhodes isc_region_t r; 2524135446Strhodes isc_buffer_t buffer; 2525135446Strhodes dns_messageid_t id; 2526135446Strhodes unsigned int flags; 2527135446Strhodes 2528135446Strhodes REQUIRE(source != NULL); 2529135446Strhodes 2530135446Strhodes buffer = *source; 2531135446Strhodes 2532135446Strhodes isc_buffer_remainingregion(&buffer, &r); 2533135446Strhodes if (r.length < DNS_MESSAGE_HEADERLEN) 2534135446Strhodes return (ISC_R_UNEXPECTEDEND); 2535135446Strhodes 2536135446Strhodes id = isc_buffer_getuint16(&buffer); 2537135446Strhodes flags = isc_buffer_getuint16(&buffer); 2538135446Strhodes flags &= DNS_MESSAGE_FLAG_MASK; 2539135446Strhodes 2540135446Strhodes if (flagsp != NULL) 2541135446Strhodes *flagsp = flags; 2542135446Strhodes if (idp != NULL) 2543135446Strhodes *idp = id; 2544135446Strhodes 2545135446Strhodes return (ISC_R_SUCCESS); 2546135446Strhodes} 2547135446Strhodes 2548135446Strhodesisc_result_t 2549135446Strhodesdns_message_reply(dns_message_t *msg, isc_boolean_t want_question_section) { 2550223812Sdougb unsigned int clear_after; 2551135446Strhodes isc_result_t result; 2552135446Strhodes 2553135446Strhodes REQUIRE(DNS_MESSAGE_VALID(msg)); 2554135446Strhodes REQUIRE((msg->flags & DNS_MESSAGEFLAG_QR) == 0); 2555135446Strhodes 2556135446Strhodes if (!msg->header_ok) 2557135446Strhodes return (DNS_R_FORMERR); 2558135446Strhodes if (msg->opcode != dns_opcode_query && 2559135446Strhodes msg->opcode != dns_opcode_notify) 2560135446Strhodes want_question_section = ISC_FALSE; 2561218384Sdougb if (msg->opcode == dns_opcode_update) 2562223812Sdougb clear_after = DNS_SECTION_PREREQUISITE; 2563218384Sdougb else if (want_question_section) { 2564135446Strhodes if (!msg->question_ok) 2565135446Strhodes return (DNS_R_FORMERR); 2566223812Sdougb clear_after = DNS_SECTION_ANSWER; 2567135446Strhodes } else 2568223812Sdougb clear_after = DNS_SECTION_QUESTION; 2569135446Strhodes msg->from_to_wire = DNS_MESSAGE_INTENTRENDER; 2570223812Sdougb msgresetnames(msg, clear_after); 2571135446Strhodes msgresetopt(msg); 2572135446Strhodes msgresetsigs(msg, ISC_TRUE); 2573135446Strhodes msginitprivate(msg); 2574135446Strhodes /* 2575135446Strhodes * We now clear most flags and then set QR, ensuring that the 2576135446Strhodes * reply's flags will be in a reasonable state. 2577135446Strhodes */ 2578135446Strhodes msg->flags &= DNS_MESSAGE_REPLYPRESERVE; 2579135446Strhodes msg->flags |= DNS_MESSAGEFLAG_QR; 2580135446Strhodes 2581135446Strhodes /* 2582135446Strhodes * This saves the query TSIG status, if the query was signed, and 2583135446Strhodes * reserves space in the reply for the TSIG. 2584135446Strhodes */ 2585135446Strhodes if (msg->tsigkey != NULL) { 2586135446Strhodes unsigned int otherlen = 0; 2587135446Strhodes msg->querytsigstatus = msg->tsigstatus; 2588135446Strhodes msg->tsigstatus = dns_rcode_noerror; 2589135446Strhodes if (msg->querytsigstatus == dns_tsigerror_badtime) 2590135446Strhodes otherlen = 6; 2591135446Strhodes msg->sig_reserved = spacefortsig(msg->tsigkey, otherlen); 2592135446Strhodes result = dns_message_renderreserve(msg, msg->sig_reserved); 2593135446Strhodes if (result != ISC_R_SUCCESS) { 2594135446Strhodes msg->sig_reserved = 0; 2595135446Strhodes return (result); 2596135446Strhodes } 2597135446Strhodes } 2598135446Strhodes if (msg->saved.base != NULL) { 2599135446Strhodes msg->query.base = msg->saved.base; 2600135446Strhodes msg->query.length = msg->saved.length; 2601135446Strhodes msg->free_query = msg->free_saved; 2602135446Strhodes msg->saved.base = NULL; 2603135446Strhodes msg->saved.length = 0; 2604135446Strhodes msg->free_saved = 0; 2605135446Strhodes } 2606135446Strhodes 2607135446Strhodes return (ISC_R_SUCCESS); 2608135446Strhodes} 2609135446Strhodes 2610135446Strhodesdns_rdataset_t * 2611135446Strhodesdns_message_getopt(dns_message_t *msg) { 2612135446Strhodes 2613135446Strhodes /* 2614135446Strhodes * Get the OPT record for 'msg'. 2615135446Strhodes */ 2616135446Strhodes 2617135446Strhodes REQUIRE(DNS_MESSAGE_VALID(msg)); 2618135446Strhodes 2619135446Strhodes return (msg->opt); 2620135446Strhodes} 2621135446Strhodes 2622135446Strhodesisc_result_t 2623135446Strhodesdns_message_setopt(dns_message_t *msg, dns_rdataset_t *opt) { 2624135446Strhodes isc_result_t result; 2625135446Strhodes dns_rdata_t rdata = DNS_RDATA_INIT; 2626135446Strhodes 2627135446Strhodes /* 2628135446Strhodes * Set the OPT record for 'msg'. 2629135446Strhodes */ 2630135446Strhodes 2631135446Strhodes /* 2632135446Strhodes * The space required for an OPT record is: 2633135446Strhodes * 2634135446Strhodes * 1 byte for the name 2635135446Strhodes * 2 bytes for the type 2636135446Strhodes * 2 bytes for the class 2637135446Strhodes * 4 bytes for the ttl 2638135446Strhodes * 2 bytes for the rdata length 2639135446Strhodes * --------------------------------- 2640135446Strhodes * 11 bytes 2641135446Strhodes * 2642135446Strhodes * plus the length of the rdata. 2643135446Strhodes */ 2644135446Strhodes 2645135446Strhodes REQUIRE(DNS_MESSAGE_VALID(msg)); 2646135446Strhodes REQUIRE(opt->type == dns_rdatatype_opt); 2647135446Strhodes REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER); 2648135446Strhodes REQUIRE(msg->state == DNS_SECTION_ANY); 2649135446Strhodes 2650135446Strhodes msgresetopt(msg); 2651135446Strhodes 2652135446Strhodes result = dns_rdataset_first(opt); 2653135446Strhodes if (result != ISC_R_SUCCESS) 2654135446Strhodes goto cleanup; 2655135446Strhodes dns_rdataset_current(opt, &rdata); 2656135446Strhodes msg->opt_reserved = 11 + rdata.length; 2657135446Strhodes result = dns_message_renderreserve(msg, msg->opt_reserved); 2658135446Strhodes if (result != ISC_R_SUCCESS) { 2659135446Strhodes msg->opt_reserved = 0; 2660135446Strhodes goto cleanup; 2661135446Strhodes } 2662135446Strhodes 2663135446Strhodes msg->opt = opt; 2664135446Strhodes 2665135446Strhodes return (ISC_R_SUCCESS); 2666135446Strhodes 2667135446Strhodes cleanup: 2668254402Serwin dns_rdataset_disassociate(opt); 2669135446Strhodes dns_message_puttemprdataset(msg, &opt); 2670135446Strhodes return (result); 2671135446Strhodes} 2672135446Strhodes 2673135446Strhodesdns_rdataset_t * 2674135446Strhodesdns_message_gettsig(dns_message_t *msg, dns_name_t **owner) { 2675135446Strhodes 2676135446Strhodes /* 2677135446Strhodes * Get the TSIG record and owner for 'msg'. 2678135446Strhodes */ 2679135446Strhodes 2680135446Strhodes REQUIRE(DNS_MESSAGE_VALID(msg)); 2681135446Strhodes REQUIRE(owner == NULL || *owner == NULL); 2682135446Strhodes 2683135446Strhodes if (owner != NULL) 2684135446Strhodes *owner = msg->tsigname; 2685135446Strhodes return (msg->tsig); 2686135446Strhodes} 2687135446Strhodes 2688135446Strhodesisc_result_t 2689135446Strhodesdns_message_settsigkey(dns_message_t *msg, dns_tsigkey_t *key) { 2690135446Strhodes isc_result_t result; 2691135446Strhodes 2692135446Strhodes /* 2693135446Strhodes * Set the TSIG key for 'msg' 2694135446Strhodes */ 2695135446Strhodes 2696135446Strhodes REQUIRE(DNS_MESSAGE_VALID(msg)); 2697135446Strhodes REQUIRE(msg->state == DNS_SECTION_ANY); 2698135446Strhodes 2699135446Strhodes if (key == NULL && msg->tsigkey != NULL) { 2700135446Strhodes if (msg->sig_reserved != 0) { 2701135446Strhodes dns_message_renderrelease(msg, msg->sig_reserved); 2702135446Strhodes msg->sig_reserved = 0; 2703135446Strhodes } 2704135446Strhodes dns_tsigkey_detach(&msg->tsigkey); 2705135446Strhodes } 2706135446Strhodes if (key != NULL) { 2707135446Strhodes REQUIRE(msg->tsigkey == NULL && msg->sig0key == NULL); 2708135446Strhodes dns_tsigkey_attach(key, &msg->tsigkey); 2709135446Strhodes if (msg->from_to_wire == DNS_MESSAGE_INTENTRENDER) { 2710135446Strhodes msg->sig_reserved = spacefortsig(msg->tsigkey, 0); 2711135446Strhodes result = dns_message_renderreserve(msg, 2712135446Strhodes msg->sig_reserved); 2713135446Strhodes if (result != ISC_R_SUCCESS) { 2714135446Strhodes dns_tsigkey_detach(&msg->tsigkey); 2715135446Strhodes msg->sig_reserved = 0; 2716135446Strhodes return (result); 2717135446Strhodes } 2718135446Strhodes } 2719135446Strhodes } 2720135446Strhodes return (ISC_R_SUCCESS); 2721135446Strhodes} 2722135446Strhodes 2723135446Strhodesdns_tsigkey_t * 2724135446Strhodesdns_message_gettsigkey(dns_message_t *msg) { 2725135446Strhodes 2726135446Strhodes /* 2727135446Strhodes * Get the TSIG key for 'msg' 2728135446Strhodes */ 2729135446Strhodes 2730135446Strhodes REQUIRE(DNS_MESSAGE_VALID(msg)); 2731135446Strhodes 2732135446Strhodes return (msg->tsigkey); 2733135446Strhodes} 2734135446Strhodes 2735135446Strhodesisc_result_t 2736135446Strhodesdns_message_setquerytsig(dns_message_t *msg, isc_buffer_t *querytsig) { 2737135446Strhodes dns_rdata_t *rdata = NULL; 2738135446Strhodes dns_rdatalist_t *list = NULL; 2739135446Strhodes dns_rdataset_t *set = NULL; 2740135446Strhodes isc_buffer_t *buf = NULL; 2741135446Strhodes isc_region_t r; 2742135446Strhodes isc_result_t result; 2743135446Strhodes 2744135446Strhodes REQUIRE(DNS_MESSAGE_VALID(msg)); 2745135446Strhodes REQUIRE(msg->querytsig == NULL); 2746135446Strhodes 2747135446Strhodes if (querytsig == NULL) 2748135446Strhodes return (ISC_R_SUCCESS); 2749135446Strhodes 2750135446Strhodes result = dns_message_gettemprdata(msg, &rdata); 2751135446Strhodes if (result != ISC_R_SUCCESS) 2752135446Strhodes goto cleanup; 2753135446Strhodes 2754135446Strhodes result = dns_message_gettemprdatalist(msg, &list); 2755135446Strhodes if (result != ISC_R_SUCCESS) 2756135446Strhodes goto cleanup; 2757135446Strhodes result = dns_message_gettemprdataset(msg, &set); 2758135446Strhodes if (result != ISC_R_SUCCESS) 2759135446Strhodes goto cleanup; 2760135446Strhodes 2761135446Strhodes isc_buffer_usedregion(querytsig, &r); 2762135446Strhodes result = isc_buffer_allocate(msg->mctx, &buf, r.length); 2763135446Strhodes if (result != ISC_R_SUCCESS) 2764135446Strhodes goto cleanup; 2765135446Strhodes isc_buffer_putmem(buf, r.base, r.length); 2766135446Strhodes isc_buffer_usedregion(buf, &r); 2767135446Strhodes dns_rdata_init(rdata); 2768135446Strhodes dns_rdata_fromregion(rdata, dns_rdataclass_any, dns_rdatatype_tsig, &r); 2769135446Strhodes dns_message_takebuffer(msg, &buf); 2770135446Strhodes ISC_LIST_INIT(list->rdata); 2771135446Strhodes ISC_LIST_APPEND(list->rdata, rdata, link); 2772135446Strhodes result = dns_rdatalist_tordataset(list, set); 2773135446Strhodes if (result != ISC_R_SUCCESS) 2774135446Strhodes goto cleanup; 2775135446Strhodes 2776135446Strhodes msg->querytsig = set; 2777135446Strhodes 2778135446Strhodes return (result); 2779135446Strhodes 2780135446Strhodes cleanup: 2781135446Strhodes if (rdata != NULL) 2782135446Strhodes dns_message_puttemprdata(msg, &rdata); 2783135446Strhodes if (list != NULL) 2784135446Strhodes dns_message_puttemprdatalist(msg, &list); 2785135446Strhodes if (set != NULL) 2786135446Strhodes dns_message_puttemprdataset(msg, &set); 2787135446Strhodes return (ISC_R_NOMEMORY); 2788135446Strhodes} 2789135446Strhodes 2790135446Strhodesisc_result_t 2791135446Strhodesdns_message_getquerytsig(dns_message_t *msg, isc_mem_t *mctx, 2792135446Strhodes isc_buffer_t **querytsig) { 2793135446Strhodes isc_result_t result; 2794135446Strhodes dns_rdata_t rdata = DNS_RDATA_INIT; 2795135446Strhodes isc_region_t r; 2796135446Strhodes 2797135446Strhodes REQUIRE(DNS_MESSAGE_VALID(msg)); 2798135446Strhodes REQUIRE(mctx != NULL); 2799135446Strhodes REQUIRE(querytsig != NULL && *querytsig == NULL); 2800135446Strhodes 2801135446Strhodes if (msg->tsig == NULL) 2802135446Strhodes return (ISC_R_SUCCESS); 2803135446Strhodes 2804135446Strhodes result = dns_rdataset_first(msg->tsig); 2805135446Strhodes if (result != ISC_R_SUCCESS) 2806135446Strhodes return (result); 2807135446Strhodes dns_rdataset_current(msg->tsig, &rdata); 2808135446Strhodes dns_rdata_toregion(&rdata, &r); 2809135446Strhodes 2810135446Strhodes result = isc_buffer_allocate(mctx, querytsig, r.length); 2811135446Strhodes if (result != ISC_R_SUCCESS) 2812135446Strhodes return (result); 2813135446Strhodes isc_buffer_putmem(*querytsig, r.base, r.length); 2814135446Strhodes return (ISC_R_SUCCESS); 2815135446Strhodes} 2816135446Strhodes 2817135446Strhodesdns_rdataset_t * 2818135446Strhodesdns_message_getsig0(dns_message_t *msg, dns_name_t **owner) { 2819135446Strhodes 2820135446Strhodes /* 2821135446Strhodes * Get the SIG(0) record for 'msg'. 2822135446Strhodes */ 2823135446Strhodes 2824135446Strhodes REQUIRE(DNS_MESSAGE_VALID(msg)); 2825135446Strhodes REQUIRE(owner == NULL || *owner == NULL); 2826135446Strhodes 2827135446Strhodes if (msg->sig0 != NULL && owner != NULL) { 2828135446Strhodes /* If dns_message_getsig0 is called on a rendered message 2829135446Strhodes * after the SIG(0) has been applied, we need to return the 2830135446Strhodes * root name, not NULL. 2831135446Strhodes */ 2832135446Strhodes if (msg->sig0name == NULL) 2833135446Strhodes *owner = dns_rootname; 2834135446Strhodes else 2835135446Strhodes *owner = msg->sig0name; 2836135446Strhodes } 2837135446Strhodes return (msg->sig0); 2838135446Strhodes} 2839135446Strhodes 2840135446Strhodesisc_result_t 2841135446Strhodesdns_message_setsig0key(dns_message_t *msg, dst_key_t *key) { 2842135446Strhodes isc_region_t r; 2843135446Strhodes unsigned int x; 2844135446Strhodes isc_result_t result; 2845135446Strhodes 2846135446Strhodes /* 2847135446Strhodes * Set the SIG(0) key for 'msg' 2848135446Strhodes */ 2849135446Strhodes 2850135446Strhodes /* 2851135446Strhodes * The space required for an SIG(0) record is: 2852135446Strhodes * 2853135446Strhodes * 1 byte for the name 2854135446Strhodes * 2 bytes for the type 2855135446Strhodes * 2 bytes for the class 2856135446Strhodes * 4 bytes for the ttl 2857135446Strhodes * 2 bytes for the type covered 2858135446Strhodes * 1 byte for the algorithm 2859135446Strhodes * 1 bytes for the labels 2860135446Strhodes * 4 bytes for the original ttl 2861135446Strhodes * 4 bytes for the signature expiration 2862135446Strhodes * 4 bytes for the signature inception 2863135446Strhodes * 2 bytes for the key tag 2864135446Strhodes * n bytes for the signer's name 2865135446Strhodes * x bytes for the signature 2866135446Strhodes * --------------------------------- 2867135446Strhodes * 27 + n + x bytes 2868135446Strhodes */ 2869135446Strhodes REQUIRE(DNS_MESSAGE_VALID(msg)); 2870135446Strhodes REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER); 2871135446Strhodes REQUIRE(msg->state == DNS_SECTION_ANY); 2872135446Strhodes 2873135446Strhodes if (key != NULL) { 2874135446Strhodes REQUIRE(msg->sig0key == NULL && msg->tsigkey == NULL); 2875135446Strhodes dns_name_toregion(dst_key_name(key), &r); 2876135446Strhodes result = dst_key_sigsize(key, &x); 2877135446Strhodes if (result != ISC_R_SUCCESS) { 2878135446Strhodes msg->sig_reserved = 0; 2879135446Strhodes return (result); 2880135446Strhodes } 2881135446Strhodes msg->sig_reserved = 27 + r.length + x; 2882135446Strhodes result = dns_message_renderreserve(msg, msg->sig_reserved); 2883135446Strhodes if (result != ISC_R_SUCCESS) { 2884135446Strhodes msg->sig_reserved = 0; 2885135446Strhodes return (result); 2886135446Strhodes } 2887135446Strhodes msg->sig0key = key; 2888135446Strhodes } 2889135446Strhodes return (ISC_R_SUCCESS); 2890135446Strhodes} 2891135446Strhodes 2892135446Strhodesdst_key_t * 2893135446Strhodesdns_message_getsig0key(dns_message_t *msg) { 2894135446Strhodes 2895135446Strhodes /* 2896135446Strhodes * Get the SIG(0) key for 'msg' 2897135446Strhodes */ 2898135446Strhodes 2899135446Strhodes REQUIRE(DNS_MESSAGE_VALID(msg)); 2900135446Strhodes 2901135446Strhodes return (msg->sig0key); 2902135446Strhodes} 2903135446Strhodes 2904135446Strhodesvoid 2905135446Strhodesdns_message_takebuffer(dns_message_t *msg, isc_buffer_t **buffer) { 2906135446Strhodes REQUIRE(DNS_MESSAGE_VALID(msg)); 2907135446Strhodes REQUIRE(buffer != NULL); 2908135446Strhodes REQUIRE(ISC_BUFFER_VALID(*buffer)); 2909135446Strhodes 2910135446Strhodes ISC_LIST_APPEND(msg->cleanup, *buffer, link); 2911135446Strhodes *buffer = NULL; 2912135446Strhodes} 2913135446Strhodes 2914135446Strhodesisc_result_t 2915135446Strhodesdns_message_signer(dns_message_t *msg, dns_name_t *signer) { 2916135446Strhodes isc_result_t result = ISC_R_SUCCESS; 2917135446Strhodes dns_rdata_t rdata = DNS_RDATA_INIT; 2918135446Strhodes 2919135446Strhodes REQUIRE(DNS_MESSAGE_VALID(msg)); 2920135446Strhodes REQUIRE(signer != NULL); 2921135446Strhodes REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTPARSE); 2922135446Strhodes 2923135446Strhodes if (msg->tsig == NULL && msg->sig0 == NULL) 2924135446Strhodes return (ISC_R_NOTFOUND); 2925135446Strhodes 2926135446Strhodes if (msg->verify_attempted == 0) 2927135446Strhodes return (DNS_R_NOTVERIFIEDYET); 2928135446Strhodes 2929135446Strhodes if (!dns_name_hasbuffer(signer)) { 2930135446Strhodes isc_buffer_t *dynbuf = NULL; 2931135446Strhodes result = isc_buffer_allocate(msg->mctx, &dynbuf, 512); 2932135446Strhodes if (result != ISC_R_SUCCESS) 2933135446Strhodes return (result); 2934135446Strhodes dns_name_setbuffer(signer, dynbuf); 2935135446Strhodes dns_message_takebuffer(msg, &dynbuf); 2936135446Strhodes } 2937135446Strhodes 2938135446Strhodes if (msg->sig0 != NULL) { 2939135446Strhodes dns_rdata_sig_t sig; 2940135446Strhodes 2941135446Strhodes result = dns_rdataset_first(msg->sig0); 2942135446Strhodes INSIST(result == ISC_R_SUCCESS); 2943135446Strhodes dns_rdataset_current(msg->sig0, &rdata); 2944135446Strhodes 2945135446Strhodes result = dns_rdata_tostruct(&rdata, &sig, NULL); 2946135446Strhodes if (result != ISC_R_SUCCESS) 2947135446Strhodes return (result); 2948135446Strhodes 2949135446Strhodes if (msg->verified_sig && msg->sig0status == dns_rcode_noerror) 2950135446Strhodes result = ISC_R_SUCCESS; 2951135446Strhodes else 2952135446Strhodes result = DNS_R_SIGINVALID; 2953135446Strhodes dns_name_clone(&sig.signer, signer); 2954135446Strhodes dns_rdata_freestruct(&sig); 2955135446Strhodes } else { 2956135446Strhodes dns_name_t *identity; 2957135446Strhodes dns_rdata_any_tsig_t tsig; 2958135446Strhodes 2959135446Strhodes result = dns_rdataset_first(msg->tsig); 2960135446Strhodes INSIST(result == ISC_R_SUCCESS); 2961135446Strhodes dns_rdataset_current(msg->tsig, &rdata); 2962135446Strhodes 2963135446Strhodes result = dns_rdata_tostruct(&rdata, &tsig, NULL); 2964225361Sdougb INSIST(result == ISC_R_SUCCESS); 2965135446Strhodes if (msg->tsigstatus != dns_rcode_noerror) 2966135446Strhodes result = DNS_R_TSIGVERIFYFAILURE; 2967135446Strhodes else if (tsig.error != dns_rcode_noerror) 2968135446Strhodes result = DNS_R_TSIGERRORSET; 2969135446Strhodes else 2970135446Strhodes result = ISC_R_SUCCESS; 2971135446Strhodes dns_rdata_freestruct(&tsig); 2972135446Strhodes 2973135446Strhodes if (msg->tsigkey == NULL) { 2974135446Strhodes /* 2975135446Strhodes * If msg->tsigstatus & tsig.error are both 2976135446Strhodes * dns_rcode_noerror, the message must have been 2977135446Strhodes * verified, which means msg->tsigkey will be 2978135446Strhodes * non-NULL. 2979135446Strhodes */ 2980135446Strhodes INSIST(result != ISC_R_SUCCESS); 2981135446Strhodes } else { 2982135446Strhodes identity = dns_tsigkey_identity(msg->tsigkey); 2983135446Strhodes if (identity == NULL) { 2984135446Strhodes if (result == ISC_R_SUCCESS) 2985135446Strhodes result = DNS_R_NOIDENTITY; 2986135446Strhodes identity = &msg->tsigkey->name; 2987135446Strhodes } 2988135446Strhodes dns_name_clone(identity, signer); 2989135446Strhodes } 2990135446Strhodes } 2991135446Strhodes 2992135446Strhodes return (result); 2993135446Strhodes} 2994135446Strhodes 2995135446Strhodesvoid 2996135446Strhodesdns_message_resetsig(dns_message_t *msg) { 2997135446Strhodes REQUIRE(DNS_MESSAGE_VALID(msg)); 2998135446Strhodes msg->verified_sig = 0; 2999135446Strhodes msg->verify_attempted = 0; 3000135446Strhodes msg->tsigstatus = dns_rcode_noerror; 3001135446Strhodes msg->sig0status = dns_rcode_noerror; 3002135446Strhodes msg->timeadjust = 0; 3003135446Strhodes if (msg->tsigkey != NULL) { 3004135446Strhodes dns_tsigkey_detach(&msg->tsigkey); 3005135446Strhodes msg->tsigkey = NULL; 3006135446Strhodes } 3007135446Strhodes} 3008135446Strhodes 3009135446Strhodesisc_result_t 3010135446Strhodesdns_message_rechecksig(dns_message_t *msg, dns_view_t *view) { 3011135446Strhodes dns_message_resetsig(msg); 3012135446Strhodes return (dns_message_checksig(msg, view)); 3013135446Strhodes} 3014135446Strhodes 3015193149Sdougb#ifdef SKAN_MSG_DEBUG 3016193149Sdougbvoid 3017193149Sdougbdns_message_dumpsig(dns_message_t *msg, char *txt1) { 3018193149Sdougb dns_rdata_t querytsigrdata = DNS_RDATA_INIT; 3019193149Sdougb dns_rdata_any_tsig_t querytsig; 3020193149Sdougb isc_result_t result; 3021193149Sdougb 3022193149Sdougb if (msg->tsig != NULL) { 3023193149Sdougb result = dns_rdataset_first(msg->tsig); 3024193149Sdougb RUNTIME_CHECK(result == ISC_R_SUCCESS); 3025193149Sdougb dns_rdataset_current(msg->tsig, &querytsigrdata); 3026193149Sdougb result = dns_rdata_tostruct(&querytsigrdata, &querytsig, NULL); 3027193149Sdougb RUNTIME_CHECK(result == ISC_R_SUCCESS); 3028193149Sdougb hexdump(txt1, "TSIG", querytsig.signature, 3029193149Sdougb querytsig.siglen); 3030193149Sdougb } 3031193149Sdougb 3032193149Sdougb if (msg->querytsig != NULL) { 3033193149Sdougb result = dns_rdataset_first(msg->querytsig); 3034193149Sdougb RUNTIME_CHECK(result == ISC_R_SUCCESS); 3035193149Sdougb dns_rdataset_current(msg->querytsig, &querytsigrdata); 3036193149Sdougb result = dns_rdata_tostruct(&querytsigrdata, &querytsig, NULL); 3037193149Sdougb RUNTIME_CHECK(result == ISC_R_SUCCESS); 3038193149Sdougb hexdump(txt1, "QUERYTSIG", querytsig.signature, 3039193149Sdougb querytsig.siglen); 3040193149Sdougb } 3041193149Sdougb} 3042193149Sdougb#endif 3043193149Sdougb 3044135446Strhodesisc_result_t 3045135446Strhodesdns_message_checksig(dns_message_t *msg, dns_view_t *view) { 3046135446Strhodes isc_buffer_t b, msgb; 3047135446Strhodes 3048135446Strhodes REQUIRE(DNS_MESSAGE_VALID(msg)); 3049135446Strhodes 3050135446Strhodes if (msg->tsigkey == NULL && msg->tsig == NULL && msg->sig0 == NULL) 3051135446Strhodes return (ISC_R_SUCCESS); 3052193149Sdougb 3053135446Strhodes INSIST(msg->saved.base != NULL); 3054135446Strhodes isc_buffer_init(&msgb, msg->saved.base, msg->saved.length); 3055135446Strhodes isc_buffer_add(&msgb, msg->saved.length); 3056135446Strhodes if (msg->tsigkey != NULL || msg->tsig != NULL) { 3057193149Sdougb#ifdef SKAN_MSG_DEBUG 3058193149Sdougb dns_message_dumpsig(msg, "dns_message_checksig#1"); 3059193149Sdougb#endif 3060135446Strhodes if (view != NULL) 3061135446Strhodes return (dns_view_checksig(view, &msgb, msg)); 3062135446Strhodes else 3063135446Strhodes return (dns_tsig_verify(&msgb, msg, NULL, NULL)); 3064135446Strhodes } else { 3065135446Strhodes dns_rdata_t rdata = DNS_RDATA_INIT; 3066135446Strhodes dns_rdata_sig_t sig; 3067135446Strhodes dns_rdataset_t keyset; 3068135446Strhodes isc_result_t result; 3069135446Strhodes 3070135446Strhodes result = dns_rdataset_first(msg->sig0); 3071135446Strhodes INSIST(result == ISC_R_SUCCESS); 3072135446Strhodes dns_rdataset_current(msg->sig0, &rdata); 3073135446Strhodes 3074135446Strhodes /* 3075135446Strhodes * This can occur when the message is a dynamic update, since 3076135446Strhodes * the rdata length checking is relaxed. This should not 3077135446Strhodes * happen in a well-formed message, since the SIG(0) is only 3078135446Strhodes * looked for in the additional section, and the dynamic update 3079135446Strhodes * meta-records are in the prerequisite and update sections. 3080135446Strhodes */ 3081135446Strhodes if (rdata.length == 0) 3082135446Strhodes return (ISC_R_UNEXPECTEDEND); 3083135446Strhodes 3084135446Strhodes result = dns_rdata_tostruct(&rdata, &sig, msg->mctx); 3085135446Strhodes if (result != ISC_R_SUCCESS) 3086135446Strhodes return (result); 3087135446Strhodes 3088135446Strhodes dns_rdataset_init(&keyset); 3089135446Strhodes if (view == NULL) 3090135446Strhodes return (DNS_R_KEYUNAUTHORIZED); 3091135446Strhodes result = dns_view_simplefind(view, &sig.signer, 3092135446Strhodes dns_rdatatype_key /* SIG(0) */, 3093135446Strhodes 0, 0, ISC_FALSE, &keyset, NULL); 3094135446Strhodes 3095135446Strhodes if (result != ISC_R_SUCCESS) { 3096135446Strhodes /* XXXBEW Should possibly create a fetch here */ 3097135446Strhodes result = DNS_R_KEYUNAUTHORIZED; 3098135446Strhodes goto freesig; 3099135446Strhodes } else if (keyset.trust < dns_trust_secure) { 3100135446Strhodes /* XXXBEW Should call a validator here */ 3101135446Strhodes result = DNS_R_KEYUNAUTHORIZED; 3102135446Strhodes goto freesig; 3103135446Strhodes } 3104135446Strhodes result = dns_rdataset_first(&keyset); 3105135446Strhodes INSIST(result == ISC_R_SUCCESS); 3106135446Strhodes for (; 3107135446Strhodes result == ISC_R_SUCCESS; 3108135446Strhodes result = dns_rdataset_next(&keyset)) 3109135446Strhodes { 3110135446Strhodes dst_key_t *key = NULL; 3111135446Strhodes 3112193149Sdougb dns_rdata_reset(&rdata); 3113135446Strhodes dns_rdataset_current(&keyset, &rdata); 3114135446Strhodes isc_buffer_init(&b, rdata.data, rdata.length); 3115135446Strhodes isc_buffer_add(&b, rdata.length); 3116135446Strhodes 3117135446Strhodes result = dst_key_fromdns(&sig.signer, rdata.rdclass, 3118135446Strhodes &b, view->mctx, &key); 3119135446Strhodes if (result != ISC_R_SUCCESS) 3120135446Strhodes continue; 3121135446Strhodes if (dst_key_alg(key) != sig.algorithm || 3122135446Strhodes dst_key_id(key) != sig.keyid || 3123135446Strhodes !(dst_key_proto(key) == DNS_KEYPROTO_DNSSEC || 3124135446Strhodes dst_key_proto(key) == DNS_KEYPROTO_ANY)) 3125135446Strhodes { 3126135446Strhodes dst_key_free(&key); 3127135446Strhodes continue; 3128135446Strhodes } 3129135446Strhodes result = dns_dnssec_verifymessage(&msgb, msg, key); 3130135446Strhodes dst_key_free(&key); 3131135446Strhodes if (result == ISC_R_SUCCESS) 3132135446Strhodes break; 3133135446Strhodes } 3134135446Strhodes if (result == ISC_R_NOMORE) 3135135446Strhodes result = DNS_R_KEYUNAUTHORIZED; 3136135446Strhodes 3137135446Strhodes freesig: 3138135446Strhodes if (dns_rdataset_isassociated(&keyset)) 3139135446Strhodes dns_rdataset_disassociate(&keyset); 3140135446Strhodes dns_rdata_freestruct(&sig); 3141135446Strhodes return (result); 3142135446Strhodes } 3143135446Strhodes} 3144135446Strhodes 3145135446Strhodesisc_result_t 3146135446Strhodesdns_message_sectiontotext(dns_message_t *msg, dns_section_t section, 3147135446Strhodes const dns_master_style_t *style, 3148135446Strhodes dns_messagetextflag_t flags, 3149135446Strhodes isc_buffer_t *target) { 3150135446Strhodes dns_name_t *name, empty_name; 3151135446Strhodes dns_rdataset_t *rdataset; 3152135446Strhodes isc_result_t result; 3153224092Sdougb isc_boolean_t seensoa = ISC_FALSE; 3154135446Strhodes 3155135446Strhodes REQUIRE(DNS_MESSAGE_VALID(msg)); 3156135446Strhodes REQUIRE(target != NULL); 3157135446Strhodes REQUIRE(VALID_SECTION(section)); 3158135446Strhodes 3159135446Strhodes if (ISC_LIST_EMPTY(msg->sections[section])) 3160135446Strhodes return (ISC_R_SUCCESS); 3161135446Strhodes 3162135446Strhodes if ((flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0) { 3163135446Strhodes ADD_STRING(target, ";; "); 3164135446Strhodes if (msg->opcode != dns_opcode_update) { 3165135446Strhodes ADD_STRING(target, sectiontext[section]); 3166174187Sdougb } else { 3167135446Strhodes ADD_STRING(target, updsectiontext[section]); 3168135446Strhodes } 3169135446Strhodes ADD_STRING(target, " SECTION:\n"); 3170135446Strhodes } 3171135446Strhodes 3172135446Strhodes dns_name_init(&empty_name, NULL); 3173135446Strhodes result = dns_message_firstname(msg, section); 3174135446Strhodes if (result != ISC_R_SUCCESS) { 3175135446Strhodes return (result); 3176135446Strhodes } 3177135446Strhodes do { 3178135446Strhodes name = NULL; 3179135446Strhodes dns_message_currentname(msg, section, &name); 3180135446Strhodes for (rdataset = ISC_LIST_HEAD(name->list); 3181135446Strhodes rdataset != NULL; 3182135446Strhodes rdataset = ISC_LIST_NEXT(rdataset, link)) { 3183224092Sdougb if (section == DNS_SECTION_ANSWER && 3184224092Sdougb rdataset->type == dns_rdatatype_soa) { 3185224092Sdougb if ((flags & DNS_MESSAGETEXTFLAG_OMITSOA) != 0) 3186224092Sdougb continue; 3187224092Sdougb if (seensoa && 3188224092Sdougb (flags & DNS_MESSAGETEXTFLAG_ONESOA) != 0) 3189224092Sdougb continue; 3190224092Sdougb seensoa = ISC_TRUE; 3191224092Sdougb } 3192135446Strhodes if (section == DNS_SECTION_QUESTION) { 3193135446Strhodes ADD_STRING(target, ";"); 3194135446Strhodes result = dns_master_questiontotext(name, 3195135446Strhodes rdataset, 3196135446Strhodes style, 3197135446Strhodes target); 3198135446Strhodes } else { 3199135446Strhodes result = dns_master_rdatasettotext(name, 3200135446Strhodes rdataset, 3201135446Strhodes style, 3202135446Strhodes target); 3203135446Strhodes } 3204135446Strhodes if (result != ISC_R_SUCCESS) 3205135446Strhodes return (result); 3206135446Strhodes } 3207135446Strhodes result = dns_message_nextname(msg, section); 3208135446Strhodes } while (result == ISC_R_SUCCESS); 3209135446Strhodes if ((flags & DNS_MESSAGETEXTFLAG_NOHEADERS) == 0 && 3210135446Strhodes (flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0) 3211135446Strhodes ADD_STRING(target, "\n"); 3212135446Strhodes if (result == ISC_R_NOMORE) 3213135446Strhodes result = ISC_R_SUCCESS; 3214135446Strhodes return (result); 3215135446Strhodes} 3216135446Strhodes 3217135446Strhodesisc_result_t 3218135446Strhodesdns_message_pseudosectiontotext(dns_message_t *msg, 3219135446Strhodes dns_pseudosection_t section, 3220135446Strhodes const dns_master_style_t *style, 3221135446Strhodes dns_messagetextflag_t flags, 3222135446Strhodes isc_buffer_t *target) { 3223135446Strhodes dns_rdataset_t *ps = NULL; 3224135446Strhodes dns_name_t *name = NULL; 3225135446Strhodes isc_result_t result; 3226135446Strhodes char buf[sizeof("1234567890")]; 3227135446Strhodes isc_uint32_t mbz; 3228193149Sdougb dns_rdata_t rdata; 3229193149Sdougb isc_buffer_t optbuf; 3230193149Sdougb isc_uint16_t optcode, optlen; 3231193149Sdougb unsigned char *optdata; 3232135446Strhodes 3233135446Strhodes REQUIRE(DNS_MESSAGE_VALID(msg)); 3234135446Strhodes REQUIRE(target != NULL); 3235135446Strhodes REQUIRE(VALID_PSEUDOSECTION(section)); 3236135446Strhodes 3237135446Strhodes switch (section) { 3238135446Strhodes case DNS_PSEUDOSECTION_OPT: 3239135446Strhodes ps = dns_message_getopt(msg); 3240135446Strhodes if (ps == NULL) 3241135446Strhodes return (ISC_R_SUCCESS); 3242135446Strhodes if ((flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0) 3243135446Strhodes ADD_STRING(target, ";; OPT PSEUDOSECTION:\n"); 3244135446Strhodes ADD_STRING(target, "; EDNS: version: "); 3245135446Strhodes snprintf(buf, sizeof(buf), "%u", 3246135446Strhodes (unsigned int)((ps->ttl & 0x00ff0000) >> 16)); 3247135446Strhodes ADD_STRING(target, buf); 3248135446Strhodes ADD_STRING(target, ", flags:"); 3249135446Strhodes if ((ps->ttl & DNS_MESSAGEEXTFLAG_DO) != 0) 3250135446Strhodes ADD_STRING(target, " do"); 3251218384Sdougb mbz = ps->ttl & 0xffff; 3252218384Sdougb mbz &= ~DNS_MESSAGEEXTFLAG_DO; /* Known Flags. */ 3253135446Strhodes if (mbz != 0) { 3254135446Strhodes ADD_STRING(target, "; MBZ: "); 3255135446Strhodes snprintf(buf, sizeof(buf), "%.4x ", mbz); 3256135446Strhodes ADD_STRING(target, buf); 3257135446Strhodes ADD_STRING(target, ", udp: "); 3258135446Strhodes } else 3259135446Strhodes ADD_STRING(target, "; udp: "); 3260135446Strhodes snprintf(buf, sizeof(buf), "%u\n", (unsigned int)ps->rdclass); 3261135446Strhodes ADD_STRING(target, buf); 3262193149Sdougb 3263193149Sdougb result = dns_rdataset_first(ps); 3264193149Sdougb if (result != ISC_R_SUCCESS) 3265193149Sdougb return (ISC_R_SUCCESS); 3266193149Sdougb 3267193149Sdougb /* Print EDNS info, if any */ 3268193149Sdougb dns_rdata_init(&rdata); 3269193149Sdougb dns_rdataset_current(ps, &rdata); 3270193149Sdougb 3271193149Sdougb isc_buffer_init(&optbuf, rdata.data, rdata.length); 3272193149Sdougb isc_buffer_add(&optbuf, rdata.length); 3273218384Sdougb while (isc_buffer_remaininglength(&optbuf) != 0) { 3274218384Sdougb INSIST(isc_buffer_remaininglength(&optbuf) >= 4U); 3275218384Sdougb optcode = isc_buffer_getuint16(&optbuf); 3276218384Sdougb optlen = isc_buffer_getuint16(&optbuf); 3277218384Sdougb INSIST(isc_buffer_remaininglength(&optbuf) >= optlen); 3278193149Sdougb 3279218384Sdougb if (optcode == DNS_OPT_NSID) { 3280218384Sdougb ADD_STRING(target, "; NSID"); 3281218384Sdougb } else { 3282218384Sdougb ADD_STRING(target, "; OPT="); 3283218384Sdougb sprintf(buf, "%u", optcode); 3284218384Sdougb ADD_STRING(target, buf); 3285218384Sdougb } 3286193149Sdougb 3287218384Sdougb if (optlen != 0) { 3288218384Sdougb int i; 3289218384Sdougb ADD_STRING(target, ": "); 3290193149Sdougb 3291218384Sdougb optdata = isc_buffer_current(&optbuf); 3292218384Sdougb for (i = 0; i < optlen; i++) { 3293218384Sdougb sprintf(buf, "%02x ", optdata[i]); 3294218384Sdougb ADD_STRING(target, buf); 3295218384Sdougb } 3296218384Sdougb for (i = 0; i < optlen; i++) { 3297218384Sdougb ADD_STRING(target, " ("); 3298218384Sdougb if (isprint(optdata[i])) 3299218384Sdougb isc_buffer_putmem(target, 3300218384Sdougb &optdata[i], 3301218384Sdougb 1); 3302218384Sdougb else 3303218384Sdougb isc_buffer_putstr(target, "."); 3304218384Sdougb ADD_STRING(target, ")"); 3305218384Sdougb } 3306218384Sdougb isc_buffer_forward(&optbuf, optlen); 3307193149Sdougb } 3308218384Sdougb ADD_STRING(target, "\n"); 3309193149Sdougb } 3310135446Strhodes return (ISC_R_SUCCESS); 3311135446Strhodes case DNS_PSEUDOSECTION_TSIG: 3312135446Strhodes ps = dns_message_gettsig(msg, &name); 3313135446Strhodes if (ps == NULL) 3314135446Strhodes return (ISC_R_SUCCESS); 3315135446Strhodes if ((flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0) 3316135446Strhodes ADD_STRING(target, ";; TSIG PSEUDOSECTION:\n"); 3317135446Strhodes result = dns_master_rdatasettotext(name, ps, style, target); 3318135446Strhodes if ((flags & DNS_MESSAGETEXTFLAG_NOHEADERS) == 0 && 3319135446Strhodes (flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0) 3320135446Strhodes ADD_STRING(target, "\n"); 3321135446Strhodes return (result); 3322135446Strhodes case DNS_PSEUDOSECTION_SIG0: 3323135446Strhodes ps = dns_message_getsig0(msg, &name); 3324135446Strhodes if (ps == NULL) 3325135446Strhodes return (ISC_R_SUCCESS); 3326135446Strhodes if ((flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0) 3327135446Strhodes ADD_STRING(target, ";; SIG0 PSEUDOSECTION:\n"); 3328135446Strhodes result = dns_master_rdatasettotext(name, ps, style, target); 3329135446Strhodes if ((flags & DNS_MESSAGETEXTFLAG_NOHEADERS) == 0 && 3330135446Strhodes (flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0) 3331135446Strhodes ADD_STRING(target, "\n"); 3332135446Strhodes return (result); 3333135446Strhodes } 3334135446Strhodes return (ISC_R_UNEXPECTED); 3335135446Strhodes} 3336135446Strhodes 3337135446Strhodesisc_result_t 3338135446Strhodesdns_message_totext(dns_message_t *msg, const dns_master_style_t *style, 3339135446Strhodes dns_messagetextflag_t flags, isc_buffer_t *target) { 3340135446Strhodes char buf[sizeof("1234567890")]; 3341135446Strhodes isc_result_t result; 3342135446Strhodes 3343135446Strhodes REQUIRE(DNS_MESSAGE_VALID(msg)); 3344135446Strhodes REQUIRE(target != NULL); 3345135446Strhodes 3346135446Strhodes if ((flags & DNS_MESSAGETEXTFLAG_NOHEADERS) == 0) { 3347135446Strhodes ADD_STRING(target, ";; ->>HEADER<<- opcode: "); 3348135446Strhodes ADD_STRING(target, opcodetext[msg->opcode]); 3349135446Strhodes ADD_STRING(target, ", status: "); 3350174187Sdougb if (msg->rcode < (sizeof(rcodetext)/sizeof(rcodetext[0]))) { 3351174187Sdougb ADD_STRING(target, rcodetext[msg->rcode]); 3352174187Sdougb } else { 3353174187Sdougb snprintf(buf, sizeof(buf), "%4u", msg->rcode); 3354174187Sdougb ADD_STRING(target, buf); 3355174187Sdougb } 3356135446Strhodes ADD_STRING(target, ", id: "); 3357135446Strhodes snprintf(buf, sizeof(buf), "%6u", msg->id); 3358135446Strhodes ADD_STRING(target, buf); 3359218384Sdougb ADD_STRING(target, "\n;; flags:"); 3360135446Strhodes if ((msg->flags & DNS_MESSAGEFLAG_QR) != 0) 3361218384Sdougb ADD_STRING(target, " qr"); 3362135446Strhodes if ((msg->flags & DNS_MESSAGEFLAG_AA) != 0) 3363218384Sdougb ADD_STRING(target, " aa"); 3364135446Strhodes if ((msg->flags & DNS_MESSAGEFLAG_TC) != 0) 3365218384Sdougb ADD_STRING(target, " tc"); 3366135446Strhodes if ((msg->flags & DNS_MESSAGEFLAG_RD) != 0) 3367218384Sdougb ADD_STRING(target, " rd"); 3368135446Strhodes if ((msg->flags & DNS_MESSAGEFLAG_RA) != 0) 3369218384Sdougb ADD_STRING(target, " ra"); 3370135446Strhodes if ((msg->flags & DNS_MESSAGEFLAG_AD) != 0) 3371218384Sdougb ADD_STRING(target, " ad"); 3372135446Strhodes if ((msg->flags & DNS_MESSAGEFLAG_CD) != 0) 3373218384Sdougb ADD_STRING(target, " cd"); 3374218384Sdougb /* 3375218384Sdougb * The final unnamed flag must be zero. 3376218384Sdougb */ 3377218384Sdougb if ((msg->flags & 0x0040U) != 0) 3378218384Sdougb ADD_STRING(target, "; MBZ: 0x4"); 3379135446Strhodes if (msg->opcode != dns_opcode_update) { 3380135446Strhodes ADD_STRING(target, "; QUESTION: "); 3381135446Strhodes } else { 3382135446Strhodes ADD_STRING(target, "; ZONE: "); 3383135446Strhodes } 3384135446Strhodes snprintf(buf, sizeof(buf), "%1u", 3385135446Strhodes msg->counts[DNS_SECTION_QUESTION]); 3386135446Strhodes ADD_STRING(target, buf); 3387135446Strhodes if (msg->opcode != dns_opcode_update) { 3388135446Strhodes ADD_STRING(target, ", ANSWER: "); 3389135446Strhodes } else { 3390135446Strhodes ADD_STRING(target, ", PREREQ: "); 3391135446Strhodes } 3392135446Strhodes snprintf(buf, sizeof(buf), "%1u", 3393135446Strhodes msg->counts[DNS_SECTION_ANSWER]); 3394135446Strhodes ADD_STRING(target, buf); 3395135446Strhodes if (msg->opcode != dns_opcode_update) { 3396135446Strhodes ADD_STRING(target, ", AUTHORITY: "); 3397135446Strhodes } else { 3398135446Strhodes ADD_STRING(target, ", UPDATE: "); 3399135446Strhodes } 3400135446Strhodes snprintf(buf, sizeof(buf), "%1u", 3401135446Strhodes msg->counts[DNS_SECTION_AUTHORITY]); 3402135446Strhodes ADD_STRING(target, buf); 3403135446Strhodes ADD_STRING(target, ", ADDITIONAL: "); 3404135446Strhodes snprintf(buf, sizeof(buf), "%1u", 3405135446Strhodes msg->counts[DNS_SECTION_ADDITIONAL]); 3406135446Strhodes ADD_STRING(target, buf); 3407135446Strhodes ADD_STRING(target, "\n"); 3408135446Strhodes } 3409135446Strhodes result = dns_message_pseudosectiontotext(msg, 3410135446Strhodes DNS_PSEUDOSECTION_OPT, 3411135446Strhodes style, flags, target); 3412135446Strhodes if (result != ISC_R_SUCCESS) 3413135446Strhodes return (result); 3414135446Strhodes 3415135446Strhodes result = dns_message_sectiontotext(msg, DNS_SECTION_QUESTION, 3416135446Strhodes style, flags, target); 3417135446Strhodes if (result != ISC_R_SUCCESS) 3418135446Strhodes return (result); 3419135446Strhodes result = dns_message_sectiontotext(msg, DNS_SECTION_ANSWER, 3420135446Strhodes style, flags, target); 3421135446Strhodes if (result != ISC_R_SUCCESS) 3422135446Strhodes return (result); 3423135446Strhodes result = dns_message_sectiontotext(msg, DNS_SECTION_AUTHORITY, 3424135446Strhodes style, flags, target); 3425135446Strhodes if (result != ISC_R_SUCCESS) 3426135446Strhodes return (result); 3427135446Strhodes result = dns_message_sectiontotext(msg, DNS_SECTION_ADDITIONAL, 3428135446Strhodes style, flags, target); 3429135446Strhodes if (result != ISC_R_SUCCESS) 3430135446Strhodes return (result); 3431135446Strhodes 3432135446Strhodes result = dns_message_pseudosectiontotext(msg, 3433135446Strhodes DNS_PSEUDOSECTION_TSIG, 3434135446Strhodes style, flags, target); 3435135446Strhodes if (result != ISC_R_SUCCESS) 3436135446Strhodes return (result); 3437135446Strhodes 3438135446Strhodes result = dns_message_pseudosectiontotext(msg, 3439135446Strhodes DNS_PSEUDOSECTION_SIG0, 3440135446Strhodes style, flags, target); 3441135446Strhodes if (result != ISC_R_SUCCESS) 3442135446Strhodes return (result); 3443135446Strhodes 3444135446Strhodes return (ISC_R_SUCCESS); 3445135446Strhodes} 3446135446Strhodes 3447135446Strhodesisc_region_t * 3448135446Strhodesdns_message_getrawmessage(dns_message_t *msg) { 3449135446Strhodes REQUIRE(DNS_MESSAGE_VALID(msg)); 3450135446Strhodes return (&msg->saved); 3451135446Strhodes} 3452135446Strhodes 3453135446Strhodesvoid 3454135446Strhodesdns_message_setsortorder(dns_message_t *msg, dns_rdatasetorderfunc_t order, 3455165071Sdougb const void *order_arg) 3456135446Strhodes{ 3457135446Strhodes REQUIRE(DNS_MESSAGE_VALID(msg)); 3458135446Strhodes msg->order = order; 3459135446Strhodes msg->order_arg = order_arg; 3460135446Strhodes} 3461135446Strhodes 3462135446Strhodesvoid 3463135446Strhodesdns_message_settimeadjust(dns_message_t *msg, int timeadjust) { 3464135446Strhodes REQUIRE(DNS_MESSAGE_VALID(msg)); 3465135446Strhodes msg->timeadjust = timeadjust; 3466135446Strhodes} 3467135446Strhodes 3468135446Strhodesint 3469135446Strhodesdns_message_gettimeadjust(dns_message_t *msg) { 3470135446Strhodes REQUIRE(DNS_MESSAGE_VALID(msg)); 3471135446Strhodes return (msg->timeadjust); 3472135446Strhodes} 3473135446Strhodes 3474135446Strhodesisc_result_t 3475135446Strhodesdns_opcode_totext(dns_opcode_t opcode, isc_buffer_t *target) { 3476135446Strhodes 3477135446Strhodes REQUIRE(opcode < 16); 3478135446Strhodes 3479135446Strhodes if (isc_buffer_availablelength(target) < strlen(opcodetext[opcode])) 3480135446Strhodes return (ISC_R_NOSPACE); 3481135446Strhodes isc_buffer_putstr(target, opcodetext[opcode]); 3482135446Strhodes return (ISC_R_SUCCESS); 3483135446Strhodes} 3484254402Serwin 3485254402Serwinisc_result_t 3486254402Serwindns_message_buildopt(dns_message_t *message, dns_rdataset_t **rdatasetp, 3487254402Serwin unsigned int version, isc_uint16_t udpsize, 3488254402Serwin unsigned int flags, dns_ednsopt_t *ednsopts, size_t count) 3489254402Serwin{ 3490254402Serwin dns_rdataset_t *rdataset = NULL; 3491254402Serwin dns_rdatalist_t *rdatalist = NULL; 3492254402Serwin dns_rdata_t *rdata = NULL; 3493254402Serwin isc_result_t result; 3494262706Serwin unsigned int len = 0, i; 3495254402Serwin 3496254402Serwin REQUIRE(DNS_MESSAGE_VALID(message)); 3497254402Serwin REQUIRE(rdatasetp != NULL && *rdatasetp == NULL); 3498254402Serwin 3499254402Serwin result = dns_message_gettemprdatalist(message, &rdatalist); 3500254402Serwin if (result != ISC_R_SUCCESS) 3501254402Serwin return (result); 3502254402Serwin result = dns_message_gettemprdata(message, &rdata); 3503254402Serwin if (result != ISC_R_SUCCESS) 3504254402Serwin goto cleanup; 3505254402Serwin result = dns_message_gettemprdataset(message, &rdataset); 3506254402Serwin if (result != ISC_R_SUCCESS) 3507254402Serwin goto cleanup; 3508254402Serwin dns_rdataset_init(rdataset); 3509254402Serwin 3510254402Serwin rdatalist->type = dns_rdatatype_opt; 3511254402Serwin rdatalist->covers = 0; 3512254402Serwin 3513254402Serwin /* 3514254402Serwin * Set Maximum UDP buffer size. 3515254402Serwin */ 3516254402Serwin rdatalist->rdclass = udpsize; 3517254402Serwin 3518254402Serwin /* 3519254402Serwin * Set EXTENDED-RCODE and Z to 0. 3520254402Serwin */ 3521254402Serwin rdatalist->ttl = (version << 16); 3522254402Serwin rdatalist->ttl |= (flags & 0xffff); 3523254402Serwin 3524254402Serwin /* 3525254402Serwin * Set EDNS options if applicable 3526254402Serwin */ 3527254402Serwin if (count != 0U) { 3528254402Serwin isc_buffer_t *buf = NULL; 3529254402Serwin for (i = 0; i < count; i++) 3530254402Serwin len += ednsopts[i].length + 4; 3531254402Serwin 3532254402Serwin if (len > 0xffffU) { 3533254402Serwin result = ISC_R_NOSPACE; 3534254402Serwin goto cleanup; 3535254402Serwin } 3536254402Serwin 3537254402Serwin result = isc_buffer_allocate(message->mctx, &buf, len); 3538254402Serwin if (result != ISC_R_SUCCESS) 3539254402Serwin goto cleanup; 3540254402Serwin 3541254402Serwin for (i = 0; i < count; i++) { 3542254402Serwin isc_buffer_putuint16(buf, ednsopts[i].code); 3543254402Serwin isc_buffer_putuint16(buf, ednsopts[i].length); 3544254402Serwin isc_buffer_putmem(buf, ednsopts[i].value, 3545254402Serwin ednsopts[i].length); 3546254402Serwin } 3547254402Serwin rdata->data = isc_buffer_base(buf); 3548254402Serwin rdata->length = len; 3549254402Serwin dns_message_takebuffer(message, &buf); 3550254402Serwin } else { 3551254402Serwin rdata->data = NULL; 3552254402Serwin rdata->length = 0; 3553254402Serwin } 3554254402Serwin 3555254402Serwin rdata->rdclass = rdatalist->rdclass; 3556254402Serwin rdata->type = rdatalist->type; 3557254402Serwin rdata->flags = 0; 3558254402Serwin 3559254402Serwin ISC_LIST_INIT(rdatalist->rdata); 3560254402Serwin ISC_LIST_APPEND(rdatalist->rdata, rdata, link); 3561254402Serwin result = dns_rdatalist_tordataset(rdatalist, rdataset); 3562254402Serwin RUNTIME_CHECK(result == ISC_R_SUCCESS); 3563254402Serwin 3564254402Serwin *rdatasetp = rdataset; 3565254402Serwin return (ISC_R_SUCCESS); 3566254402Serwin 3567254402Serwin cleanup: 3568254402Serwin if (rdata != NULL) 3569254402Serwin dns_message_puttemprdata(message, &rdata); 3570254402Serwin if (rdataset != NULL) 3571254402Serwin dns_message_puttemprdataset(message, &rdataset); 3572254402Serwin if (rdatalist != NULL) 3573254402Serwin dns_message_puttemprdatalist(message, &rdatalist); 3574254402Serwin return (result); 3575254402Serwin} 3576292321Sdelphij 3577292321Sdelphijvoid 3578292321Sdelphijdns_message_setclass(dns_message_t *msg, dns_rdataclass_t rdclass) { 3579292321Sdelphij 3580292321Sdelphij REQUIRE(DNS_MESSAGE_VALID(msg)); 3581292321Sdelphij REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTPARSE); 3582292321Sdelphij REQUIRE(msg->state == DNS_SECTION_ANY); 3583292321Sdelphij REQUIRE(msg->rdclass_set == 0); 3584292321Sdelphij 3585292321Sdelphij msg->rdclass = rdclass; 3586292321Sdelphij msg->rdclass_set = 1; 3587292321Sdelphij} 3588