15185a700Sflorian /* 25185a700Sflorian * Copyright (C) Internet Systems Consortium, Inc. ("ISC") 35185a700Sflorian * 45185a700Sflorian * Permission to use, copy, modify, and/or distribute this software for any 55185a700Sflorian * purpose with or without fee is hereby granted, provided that the above 65185a700Sflorian * copyright notice and this permission notice appear in all copies. 75185a700Sflorian * 85185a700Sflorian * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 95185a700Sflorian * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 105185a700Sflorian * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 115185a700Sflorian * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 125185a700Sflorian * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 135185a700Sflorian * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 145185a700Sflorian * PERFORMANCE OF THIS SOFTWARE. 155185a700Sflorian */ 165185a700Sflorian 17*9835a5e1Sflorian /* $Id: message.c,v 1.22 2024/12/27 09:04:48 florian Exp $ */ 185185a700Sflorian 195185a700Sflorian /*! \file */ 205185a700Sflorian 215185a700Sflorian /*** 225185a700Sflorian *** Imports 235185a700Sflorian ***/ 245185a700Sflorian 2524ff385aSflorian #include <sys/socket.h> 2624ff385aSflorian #include <arpa/inet.h> 275185a700Sflorian 285185a700Sflorian #include <ctype.h> 295185a700Sflorian #include <stdlib.h> 3024ff385aSflorian #include <string.h> 315185a700Sflorian 325185a700Sflorian #include <isc/buffer.h> 335185a700Sflorian #include <isc/util.h> 345185a700Sflorian 355185a700Sflorian #include <dns/log.h> 365185a700Sflorian #include <dns/masterdump.h> 375185a700Sflorian #include <dns/message.h> 385185a700Sflorian #include <dns/rdata.h> 395185a700Sflorian #include <dns/rdatalist.h> 405185a700Sflorian #include <dns/rdataset.h> 415185a700Sflorian #include <dns/result.h> 425185a700Sflorian #include <dns/tsig.h> 435185a700Sflorian #include <dns/ttl.h> 445185a700Sflorian 455185a700Sflorian #define DNS_MESSAGE_OPCODE_MASK 0x7800U 465185a700Sflorian #define DNS_MESSAGE_OPCODE_SHIFT 11 475185a700Sflorian #define DNS_MESSAGE_RCODE_MASK 0x000fU 485185a700Sflorian #define DNS_MESSAGE_FLAG_MASK 0x8ff0U 495185a700Sflorian #define DNS_MESSAGE_EDNSRCODE_MASK 0xff000000U 505185a700Sflorian 515185a700Sflorian #define VALID_NAMED_SECTION(s) (((s) > DNS_SECTION_ANY) \ 525185a700Sflorian && ((s) < DNS_SECTION_MAX)) 535185a700Sflorian #define VALID_SECTION(s) (((s) >= DNS_SECTION_ANY) \ 545185a700Sflorian && ((s) < DNS_SECTION_MAX)) 555185a700Sflorian #define ADD_STRING(b, s) {if (strlen(s) >= \ 565185a700Sflorian isc_buffer_availablelength(b)) \ 575185a700Sflorian return(ISC_R_NOSPACE); else \ 585185a700Sflorian isc_buffer_putstr(b, s);} 595185a700Sflorian #define VALID_PSEUDOSECTION(s) (((s) >= DNS_PSEUDOSECTION_ANY) \ 605185a700Sflorian && ((s) < DNS_PSEUDOSECTION_MAX)) 615185a700Sflorian 625185a700Sflorian /*% 635185a700Sflorian * This is the size of each individual scratchpad buffer, and the numbers 645185a700Sflorian * of various block allocations used within the server. 655185a700Sflorian * XXXMLG These should come from a config setting. 665185a700Sflorian */ 675185a700Sflorian #define SCRATCHPAD_SIZE 512 685185a700Sflorian #define OFFSET_COUNT 4 695185a700Sflorian #define RDATA_COUNT 8 705185a700Sflorian #define RDATALIST_COUNT 8 715185a700Sflorian 725185a700Sflorian /*% 735185a700Sflorian * Text representation of the different items, for message_totext 745185a700Sflorian * functions. 755185a700Sflorian */ 765185a700Sflorian static const char *sectiontext[] = { 775185a700Sflorian "QUESTION", 785185a700Sflorian "ANSWER", 795185a700Sflorian "AUTHORITY", 805185a700Sflorian "ADDITIONAL" 815185a700Sflorian }; 825185a700Sflorian 835185a700Sflorian static const char *updsectiontext[] = { 845185a700Sflorian "ZONE", 855185a700Sflorian "PREREQUISITE", 865185a700Sflorian "UPDATE", 875185a700Sflorian "ADDITIONAL" 885185a700Sflorian }; 895185a700Sflorian 905185a700Sflorian /*% 915185a700Sflorian * "helper" type, which consists of a block of some type, and is linkable. 925185a700Sflorian * For it to work, sizeof(dns_msgblock_t) must be a multiple of the pointer 935185a700Sflorian * size, or the allocated elements will not be aligned correctly. 945185a700Sflorian */ 955185a700Sflorian struct dns_msgblock { 965185a700Sflorian unsigned int count; 975185a700Sflorian unsigned int remaining; 985185a700Sflorian ISC_LINK(dns_msgblock_t) link; 995185a700Sflorian }; /* dynamically sized */ 1005185a700Sflorian 1015185a700Sflorian static inline dns_msgblock_t * 1025185a700Sflorian msgblock_allocate(unsigned int, unsigned int); 1035185a700Sflorian 1045185a700Sflorian #define msgblock_get(block, type) \ 1055185a700Sflorian ((type *)msgblock_internalget(block, sizeof(type))) 1065185a700Sflorian 1075185a700Sflorian static inline void * 1085185a700Sflorian msgblock_internalget(dns_msgblock_t *, unsigned int); 1095185a700Sflorian 1105185a700Sflorian static inline void 1115185a700Sflorian msgblock_reset(dns_msgblock_t *); 1125185a700Sflorian 1135185a700Sflorian /* 1145185a700Sflorian * Allocate a new dns_msgblock_t, and return a pointer to it. If no memory 1155185a700Sflorian * is free, return NULL. 1165185a700Sflorian */ 1175185a700Sflorian static inline dns_msgblock_t * 1185185a700Sflorian msgblock_allocate(unsigned int sizeof_type, 1195185a700Sflorian unsigned int count) 1205185a700Sflorian { 1215185a700Sflorian dns_msgblock_t *block; 1225185a700Sflorian unsigned int length; 1235185a700Sflorian 1245185a700Sflorian length = sizeof(dns_msgblock_t) + (sizeof_type * count); 1255185a700Sflorian 1265185a700Sflorian block = malloc(length); 1275185a700Sflorian if (block == NULL) 1285185a700Sflorian return (NULL); 1295185a700Sflorian 1305185a700Sflorian block->count = count; 1315185a700Sflorian block->remaining = count; 1325185a700Sflorian 1335185a700Sflorian ISC_LINK_INIT(block, link); 1345185a700Sflorian 1355185a700Sflorian return (block); 1365185a700Sflorian } 1375185a700Sflorian 1385185a700Sflorian /* 1395185a700Sflorian * Return an element from the msgblock. If no more are available, return 1405185a700Sflorian * NULL. 1415185a700Sflorian */ 1425185a700Sflorian static inline void * 1435185a700Sflorian msgblock_internalget(dns_msgblock_t *block, unsigned int sizeof_type) { 1445185a700Sflorian void *ptr; 1455185a700Sflorian 1465185a700Sflorian if (block == NULL || block->remaining == 0) 1475185a700Sflorian return (NULL); 1485185a700Sflorian 1495185a700Sflorian block->remaining--; 1505185a700Sflorian 1515185a700Sflorian ptr = (((unsigned char *)block) 1525185a700Sflorian + sizeof(dns_msgblock_t) 1535185a700Sflorian + (sizeof_type * block->remaining)); 1545185a700Sflorian 1555185a700Sflorian return (ptr); 1565185a700Sflorian } 1575185a700Sflorian 1585185a700Sflorian static inline void 1595185a700Sflorian msgblock_reset(dns_msgblock_t *block) { 1605185a700Sflorian block->remaining = block->count; 1615185a700Sflorian } 1625185a700Sflorian 1635185a700Sflorian /* 1645185a700Sflorian * Allocate a new dynamic buffer, and attach it to this message as the 1655185a700Sflorian * "current" buffer. (which is always the last on the list, for our 1665185a700Sflorian * uses) 1675185a700Sflorian */ 1685185a700Sflorian static inline isc_result_t 1695185a700Sflorian newbuffer(dns_message_t *msg, unsigned int size) { 1705185a700Sflorian isc_result_t result; 1715185a700Sflorian isc_buffer_t *dynbuf; 1725185a700Sflorian 1735185a700Sflorian dynbuf = NULL; 1745185a700Sflorian result = isc_buffer_allocate(&dynbuf, size); 1755185a700Sflorian if (result != ISC_R_SUCCESS) 1765185a700Sflorian return (ISC_R_NOMEMORY); 1775185a700Sflorian 1785185a700Sflorian ISC_LIST_APPEND(msg->scratchpad, dynbuf, link); 1795185a700Sflorian return (ISC_R_SUCCESS); 1805185a700Sflorian } 1815185a700Sflorian 1825185a700Sflorian static inline isc_buffer_t * 1835185a700Sflorian currentbuffer(dns_message_t *msg) { 1845185a700Sflorian isc_buffer_t *dynbuf; 1855185a700Sflorian 1865185a700Sflorian dynbuf = ISC_LIST_TAIL(msg->scratchpad); 1875185a700Sflorian INSIST(dynbuf != NULL); 1885185a700Sflorian 1895185a700Sflorian return (dynbuf); 1905185a700Sflorian } 1915185a700Sflorian 1925185a700Sflorian static inline void 1935185a700Sflorian releaserdata(dns_message_t *msg, dns_rdata_t *rdata) { 1945185a700Sflorian ISC_LIST_PREPEND(msg->freerdata, rdata, link); 1955185a700Sflorian } 1965185a700Sflorian 1975185a700Sflorian static inline dns_rdata_t * 1985185a700Sflorian newrdata(dns_message_t *msg) { 1995185a700Sflorian dns_msgblock_t *msgblock; 2005185a700Sflorian dns_rdata_t *rdata; 2015185a700Sflorian 2025185a700Sflorian rdata = ISC_LIST_HEAD(msg->freerdata); 2035185a700Sflorian if (rdata != NULL) { 2045185a700Sflorian ISC_LIST_UNLINK(msg->freerdata, rdata, link); 2055185a700Sflorian return (rdata); 2065185a700Sflorian } 2075185a700Sflorian 2085185a700Sflorian msgblock = ISC_LIST_TAIL(msg->rdatas); 2095185a700Sflorian rdata = msgblock_get(msgblock, dns_rdata_t); 2105185a700Sflorian if (rdata == NULL) { 2115185a700Sflorian msgblock = msgblock_allocate(sizeof(dns_rdata_t), RDATA_COUNT); 2125185a700Sflorian if (msgblock == NULL) 2135185a700Sflorian return (NULL); 2145185a700Sflorian 2155185a700Sflorian ISC_LIST_APPEND(msg->rdatas, msgblock, link); 2165185a700Sflorian 2175185a700Sflorian rdata = msgblock_get(msgblock, dns_rdata_t); 2185185a700Sflorian } 2195185a700Sflorian 2205185a700Sflorian dns_rdata_init(rdata); 2215185a700Sflorian return (rdata); 2225185a700Sflorian } 2235185a700Sflorian 2245185a700Sflorian static inline void 2255185a700Sflorian releaserdatalist(dns_message_t *msg, dns_rdatalist_t *rdatalist) { 2265185a700Sflorian ISC_LIST_PREPEND(msg->freerdatalist, rdatalist, link); 2275185a700Sflorian } 2285185a700Sflorian 2295185a700Sflorian static inline dns_rdatalist_t * 2305185a700Sflorian newrdatalist(dns_message_t *msg) { 2315185a700Sflorian dns_msgblock_t *msgblock; 2325185a700Sflorian dns_rdatalist_t *rdatalist; 2335185a700Sflorian 2345185a700Sflorian rdatalist = ISC_LIST_HEAD(msg->freerdatalist); 2355185a700Sflorian if (rdatalist != NULL) { 2365185a700Sflorian ISC_LIST_UNLINK(msg->freerdatalist, rdatalist, link); 2375185a700Sflorian goto out; 2385185a700Sflorian } 2395185a700Sflorian 2405185a700Sflorian msgblock = ISC_LIST_TAIL(msg->rdatalists); 2415185a700Sflorian rdatalist = msgblock_get(msgblock, dns_rdatalist_t); 2425185a700Sflorian if (rdatalist == NULL) { 2435185a700Sflorian msgblock = msgblock_allocate(sizeof(dns_rdatalist_t), 2445185a700Sflorian RDATALIST_COUNT); 2455185a700Sflorian if (msgblock == NULL) 2465185a700Sflorian return (NULL); 2475185a700Sflorian 2485185a700Sflorian ISC_LIST_APPEND(msg->rdatalists, msgblock, link); 2495185a700Sflorian 2505185a700Sflorian rdatalist = msgblock_get(msgblock, dns_rdatalist_t); 2515185a700Sflorian } 2525185a700Sflorian out: 2535185a700Sflorian if (rdatalist != NULL) 2545185a700Sflorian dns_rdatalist_init(rdatalist); 2555185a700Sflorian 2565185a700Sflorian return (rdatalist); 2575185a700Sflorian } 2585185a700Sflorian 2595185a700Sflorian static inline dns_offsets_t * 2605185a700Sflorian newoffsets(dns_message_t *msg) { 2615185a700Sflorian dns_msgblock_t *msgblock; 2625185a700Sflorian dns_offsets_t *offsets; 2635185a700Sflorian 2645185a700Sflorian msgblock = ISC_LIST_TAIL(msg->offsets); 2655185a700Sflorian offsets = msgblock_get(msgblock, dns_offsets_t); 2665185a700Sflorian if (offsets == NULL) { 2675185a700Sflorian msgblock = msgblock_allocate(sizeof(dns_offsets_t), 2685185a700Sflorian OFFSET_COUNT); 2695185a700Sflorian if (msgblock == NULL) 2705185a700Sflorian return (NULL); 2715185a700Sflorian 2725185a700Sflorian ISC_LIST_APPEND(msg->offsets, msgblock, link); 2735185a700Sflorian 2745185a700Sflorian offsets = msgblock_get(msgblock, dns_offsets_t); 2755185a700Sflorian } 2765185a700Sflorian 2775185a700Sflorian return (offsets); 2785185a700Sflorian } 2795185a700Sflorian 2805185a700Sflorian static inline void 2815185a700Sflorian msginitheader(dns_message_t *m) { 2825185a700Sflorian m->id = 0; 2835185a700Sflorian m->flags = 0; 2845185a700Sflorian m->rcode = 0; 2855185a700Sflorian m->opcode = 0; 2865185a700Sflorian m->rdclass = 0; 2875185a700Sflorian } 2885185a700Sflorian 2895185a700Sflorian static inline void 2905185a700Sflorian msginitprivate(dns_message_t *m) { 2915185a700Sflorian unsigned int i; 2925185a700Sflorian 2935185a700Sflorian for (i = 0; i < DNS_SECTION_MAX; i++) { 2945185a700Sflorian m->cursors[i] = NULL; 2955185a700Sflorian m->counts[i] = 0; 2965185a700Sflorian } 2975185a700Sflorian m->opt = NULL; 2985185a700Sflorian m->sig0 = NULL; 2995185a700Sflorian m->sig0name = NULL; 3005185a700Sflorian m->tsig = NULL; 3015185a700Sflorian m->tsigname = NULL; 3025185a700Sflorian m->state = DNS_SECTION_ANY; /* indicate nothing parsed or rendered */ 3035185a700Sflorian m->opt_reserved = 0; 3045185a700Sflorian m->sig_reserved = 0; 3055185a700Sflorian m->reserved = 0; 3065185a700Sflorian m->buffer = NULL; 3075185a700Sflorian } 3085185a700Sflorian 3095185a700Sflorian static inline void 3105185a700Sflorian msginittsig(dns_message_t *m) { 3115185a700Sflorian m->tsigstatus = dns_rcode_noerror; 3125185a700Sflorian m->querytsigstatus = dns_rcode_noerror; 3135185a700Sflorian m->tsigkey = NULL; 3145185a700Sflorian m->tsigctx = NULL; 3155185a700Sflorian m->sigstart = -1; 3165185a700Sflorian m->sig0status = dns_rcode_noerror; 3175185a700Sflorian m->timeadjust = 0; 3185185a700Sflorian } 3195185a700Sflorian 3205185a700Sflorian /* 3215185a700Sflorian * Init elements to default state. Used both when allocating a new element 3225185a700Sflorian * and when resetting one. 3235185a700Sflorian */ 3245185a700Sflorian static inline void 3255185a700Sflorian msginit(dns_message_t *m) { 3265185a700Sflorian msginitheader(m); 3275185a700Sflorian msginitprivate(m); 3285185a700Sflorian msginittsig(m); 3295185a700Sflorian m->header_ok = 0; 3305185a700Sflorian m->question_ok = 0; 3315185a700Sflorian m->tcp_continuation = 0; 3325185a700Sflorian m->verified_sig = 0; 3335185a700Sflorian m->verify_attempted = 0; 3345185a700Sflorian m->query.base = NULL; 3355185a700Sflorian m->query.length = 0; 3365185a700Sflorian m->free_query = 0; 3375185a700Sflorian m->saved.base = NULL; 3385185a700Sflorian m->saved.length = 0; 3395185a700Sflorian m->free_saved = 0; 3405185a700Sflorian m->sitok = 0; 3415185a700Sflorian m->sitbad = 0; 3425185a700Sflorian m->tkey = 0; 3435185a700Sflorian m->rdclass_set = 0; 3445185a700Sflorian m->querytsig = NULL; 3455185a700Sflorian } 3465185a700Sflorian 3475185a700Sflorian static inline void 3485185a700Sflorian msgresetnames(dns_message_t *msg, unsigned int first_section) { 3495185a700Sflorian unsigned int i; 3505185a700Sflorian dns_name_t *name, *next_name; 3515185a700Sflorian dns_rdataset_t *rds, *next_rds; 3525185a700Sflorian 3535185a700Sflorian /* 3545185a700Sflorian * Clean up name lists by calling the rdataset disassociate function. 3555185a700Sflorian */ 3565185a700Sflorian for (i = first_section; i < DNS_SECTION_MAX; i++) { 3575185a700Sflorian name = ISC_LIST_HEAD(msg->sections[i]); 3585185a700Sflorian while (name != NULL) { 3595185a700Sflorian next_name = ISC_LIST_NEXT(name, link); 3605185a700Sflorian ISC_LIST_UNLINK(msg->sections[i], name, link); 3615185a700Sflorian 3625185a700Sflorian rds = ISC_LIST_HEAD(name->list); 3635185a700Sflorian while (rds != NULL) { 3645185a700Sflorian next_rds = ISC_LIST_NEXT(rds, link); 3655185a700Sflorian ISC_LIST_UNLINK(name->list, rds, link); 3665185a700Sflorian 3675185a700Sflorian INSIST(dns_rdataset_isassociated(rds)); 3685185a700Sflorian dns_rdataset_disassociate(rds); 3695185a700Sflorian free(rds); 3705185a700Sflorian rds = next_rds; 3715185a700Sflorian } 3725185a700Sflorian if (dns_name_dynamic(name)) 3735185a700Sflorian dns_name_free(name); 3745185a700Sflorian free(name); 3755185a700Sflorian name = next_name; 3765185a700Sflorian } 3775185a700Sflorian } 3785185a700Sflorian } 3795185a700Sflorian 3805185a700Sflorian static void 3815185a700Sflorian msgresetopt(dns_message_t *msg) 3825185a700Sflorian { 3835185a700Sflorian if (msg->opt != NULL) { 3845185a700Sflorian if (msg->opt_reserved > 0) { 3855185a700Sflorian dns_message_renderrelease(msg, msg->opt_reserved); 3865185a700Sflorian msg->opt_reserved = 0; 3875185a700Sflorian } 3885185a700Sflorian INSIST(dns_rdataset_isassociated(msg->opt)); 3895185a700Sflorian dns_rdataset_disassociate(msg->opt); 3905185a700Sflorian free(msg->opt); 3915185a700Sflorian msg->opt = NULL; 3925185a700Sflorian msg->sitok = 0; 3935185a700Sflorian msg->sitbad = 0; 3945185a700Sflorian } 3955185a700Sflorian } 3965185a700Sflorian 3975185a700Sflorian static void 3981fb015a8Sflorian msgresetsigs(dns_message_t *msg, int replying) { 3995185a700Sflorian if (msg->sig_reserved > 0) { 4005185a700Sflorian dns_message_renderrelease(msg, msg->sig_reserved); 4015185a700Sflorian msg->sig_reserved = 0; 4025185a700Sflorian } 4035185a700Sflorian if (msg->tsig != NULL) { 4045185a700Sflorian INSIST(dns_rdataset_isassociated(msg->tsig)); 4055185a700Sflorian if (replying) { 4065185a700Sflorian INSIST(msg->querytsig == NULL); 4075185a700Sflorian msg->querytsig = msg->tsig; 4085185a700Sflorian } else { 4095185a700Sflorian dns_rdataset_disassociate(msg->tsig); 4105185a700Sflorian free(msg->tsig); 4115185a700Sflorian if (msg->querytsig != NULL) { 4125185a700Sflorian dns_rdataset_disassociate(msg->querytsig); 4135185a700Sflorian free(msg->querytsig); 4145185a700Sflorian } 4155185a700Sflorian } 4165185a700Sflorian if (dns_name_dynamic(msg->tsigname)) 4175185a700Sflorian dns_name_free(msg->tsigname); 4185185a700Sflorian free(msg->tsigname); 4195185a700Sflorian msg->tsig = NULL; 4205185a700Sflorian msg->tsigname = NULL; 4215185a700Sflorian } else if (msg->querytsig != NULL && !replying) { 4225185a700Sflorian dns_rdataset_disassociate(msg->querytsig); 4235185a700Sflorian free(msg->querytsig); 4245185a700Sflorian msg->querytsig = NULL; 4255185a700Sflorian } 4265185a700Sflorian if (msg->sig0 != NULL) { 4275185a700Sflorian INSIST(dns_rdataset_isassociated(msg->sig0)); 4285185a700Sflorian dns_rdataset_disassociate(msg->sig0); 4295185a700Sflorian free(msg->sig0); 4305185a700Sflorian if (msg->sig0name != NULL) { 4315185a700Sflorian if (dns_name_dynamic(msg->sig0name)) 4325185a700Sflorian dns_name_free(msg->sig0name); 4335185a700Sflorian free(msg->sig0name); 4345185a700Sflorian } 4355185a700Sflorian msg->sig0 = NULL; 4365185a700Sflorian msg->sig0name = NULL; 4375185a700Sflorian } 4385185a700Sflorian } 4395185a700Sflorian 4405185a700Sflorian /* 4415185a700Sflorian * Free all but one (or everything) for this message. This is used by 4425185a700Sflorian * both dns_message_reset() and dns_message_destroy(). 4435185a700Sflorian */ 4445185a700Sflorian static void 4451fb015a8Sflorian msgreset(dns_message_t *msg, int everything) { 4465185a700Sflorian dns_msgblock_t *msgblock, *next_msgblock; 4475185a700Sflorian isc_buffer_t *dynbuf, *next_dynbuf; 4485185a700Sflorian dns_rdata_t *rdata; 4495185a700Sflorian dns_rdatalist_t *rdatalist; 4505185a700Sflorian 4515185a700Sflorian msgresetnames(msg, 0); 4525185a700Sflorian msgresetopt(msg); 4531fb015a8Sflorian msgresetsigs(msg, 0); 4545185a700Sflorian 4555185a700Sflorian /* 4565185a700Sflorian * Clean up linked lists. 4575185a700Sflorian */ 4585185a700Sflorian 4595185a700Sflorian /* 4605185a700Sflorian * Run through the free lists, and just unlink anything found there. 4615185a700Sflorian * The memory isn't lost since these are part of message blocks we 4625185a700Sflorian * have allocated. 4635185a700Sflorian */ 4645185a700Sflorian rdata = ISC_LIST_HEAD(msg->freerdata); 4655185a700Sflorian while (rdata != NULL) { 4665185a700Sflorian ISC_LIST_UNLINK(msg->freerdata, rdata, link); 4675185a700Sflorian rdata = ISC_LIST_HEAD(msg->freerdata); 4685185a700Sflorian } 4695185a700Sflorian rdatalist = ISC_LIST_HEAD(msg->freerdatalist); 4705185a700Sflorian while (rdatalist != NULL) { 4715185a700Sflorian ISC_LIST_UNLINK(msg->freerdatalist, rdatalist, link); 4725185a700Sflorian rdatalist = ISC_LIST_HEAD(msg->freerdatalist); 4735185a700Sflorian } 4745185a700Sflorian 4755185a700Sflorian dynbuf = ISC_LIST_HEAD(msg->scratchpad); 4765185a700Sflorian INSIST(dynbuf != NULL); 4775185a700Sflorian if (!everything) { 4785185a700Sflorian isc_buffer_clear(dynbuf); 4795185a700Sflorian dynbuf = ISC_LIST_NEXT(dynbuf, link); 4805185a700Sflorian } 4815185a700Sflorian while (dynbuf != NULL) { 4825185a700Sflorian next_dynbuf = ISC_LIST_NEXT(dynbuf, link); 4835185a700Sflorian ISC_LIST_UNLINK(msg->scratchpad, dynbuf, link); 4845185a700Sflorian isc_buffer_free(&dynbuf); 4855185a700Sflorian dynbuf = next_dynbuf; 4865185a700Sflorian } 4875185a700Sflorian 4885185a700Sflorian msgblock = ISC_LIST_HEAD(msg->rdatas); 4895185a700Sflorian if (!everything && msgblock != NULL) { 4905185a700Sflorian msgblock_reset(msgblock); 4915185a700Sflorian msgblock = ISC_LIST_NEXT(msgblock, link); 4925185a700Sflorian } 4935185a700Sflorian while (msgblock != NULL) { 4945185a700Sflorian next_msgblock = ISC_LIST_NEXT(msgblock, link); 4955185a700Sflorian ISC_LIST_UNLINK(msg->rdatas, msgblock, link); 496ad5cf538Sjung free(msgblock); 4975185a700Sflorian msgblock = next_msgblock; 4985185a700Sflorian } 4995185a700Sflorian 5005185a700Sflorian /* 5015185a700Sflorian * rdatalists could be empty. 5025185a700Sflorian */ 5035185a700Sflorian 5045185a700Sflorian msgblock = ISC_LIST_HEAD(msg->rdatalists); 5055185a700Sflorian if (!everything && msgblock != NULL) { 5065185a700Sflorian msgblock_reset(msgblock); 5075185a700Sflorian msgblock = ISC_LIST_NEXT(msgblock, link); 5085185a700Sflorian } 5095185a700Sflorian while (msgblock != NULL) { 5105185a700Sflorian next_msgblock = ISC_LIST_NEXT(msgblock, link); 5115185a700Sflorian ISC_LIST_UNLINK(msg->rdatalists, msgblock, link); 512ad5cf538Sjung free(msgblock); 5135185a700Sflorian msgblock = next_msgblock; 5145185a700Sflorian } 5155185a700Sflorian 5165185a700Sflorian msgblock = ISC_LIST_HEAD(msg->offsets); 5175185a700Sflorian if (!everything && msgblock != NULL) { 5185185a700Sflorian msgblock_reset(msgblock); 5195185a700Sflorian msgblock = ISC_LIST_NEXT(msgblock, link); 5205185a700Sflorian } 5215185a700Sflorian while (msgblock != NULL) { 5225185a700Sflorian next_msgblock = ISC_LIST_NEXT(msgblock, link); 5235185a700Sflorian ISC_LIST_UNLINK(msg->offsets, msgblock, link); 524ad5cf538Sjung free(msgblock); 5255185a700Sflorian msgblock = next_msgblock; 5265185a700Sflorian } 5275185a700Sflorian 5285185a700Sflorian if (msg->tsigkey != NULL) { 5295185a700Sflorian dns_tsigkey_detach(&msg->tsigkey); 5305185a700Sflorian msg->tsigkey = NULL; 5315185a700Sflorian } 5325185a700Sflorian 5335185a700Sflorian if (msg->tsigctx != NULL) 5345185a700Sflorian dst_context_destroy(&msg->tsigctx); 5355185a700Sflorian 5365185a700Sflorian if (msg->query.base != NULL) { 5375185a700Sflorian if (msg->free_query != 0) 5385185a700Sflorian free(msg->query.base); 5395185a700Sflorian msg->query.base = NULL; 5405185a700Sflorian msg->query.length = 0; 5415185a700Sflorian } 5425185a700Sflorian 5435185a700Sflorian if (msg->saved.base != NULL) { 5445185a700Sflorian if (msg->free_saved != 0) 5455185a700Sflorian free(msg->saved.base); 5465185a700Sflorian msg->saved.base = NULL; 5475185a700Sflorian msg->saved.length = 0; 5485185a700Sflorian } 5495185a700Sflorian 5505185a700Sflorian /* 5515185a700Sflorian * cleanup the buffer cleanup list 5525185a700Sflorian */ 5535185a700Sflorian dynbuf = ISC_LIST_HEAD(msg->cleanup); 5545185a700Sflorian while (dynbuf != NULL) { 5555185a700Sflorian next_dynbuf = ISC_LIST_NEXT(dynbuf, link); 5565185a700Sflorian ISC_LIST_UNLINK(msg->cleanup, dynbuf, link); 5575185a700Sflorian isc_buffer_free(&dynbuf); 5585185a700Sflorian dynbuf = next_dynbuf; 5595185a700Sflorian } 5605185a700Sflorian 5615185a700Sflorian /* 5625185a700Sflorian * Set other bits to normal default values. 5635185a700Sflorian */ 5645185a700Sflorian if (!everything) 5655185a700Sflorian msginit(msg); 5665185a700Sflorian } 5675185a700Sflorian 5685185a700Sflorian static unsigned int 5695185a700Sflorian spacefortsig(dns_tsigkey_t *key, int otherlen) { 5705185a700Sflorian isc_region_t r1, r2; 5715185a700Sflorian unsigned int x; 5725185a700Sflorian isc_result_t result; 5735185a700Sflorian 5745185a700Sflorian /* 5755185a700Sflorian * The space required for an TSIG record is: 5765185a700Sflorian * 5775185a700Sflorian * n1 bytes for the name 5785185a700Sflorian * 2 bytes for the type 5795185a700Sflorian * 2 bytes for the class 5805185a700Sflorian * 4 bytes for the ttl 5815185a700Sflorian * 2 bytes for the rdlength 5825185a700Sflorian * n2 bytes for the algorithm name 5835185a700Sflorian * 6 bytes for the time signed 5845185a700Sflorian * 2 bytes for the fudge 5855185a700Sflorian * 2 bytes for the MAC size 5865185a700Sflorian * x bytes for the MAC 5875185a700Sflorian * 2 bytes for the original id 5885185a700Sflorian * 2 bytes for the error 5895185a700Sflorian * 2 bytes for the other data length 5905185a700Sflorian * y bytes for the other data (at most) 5915185a700Sflorian * --------------------------------- 5925185a700Sflorian * 26 + n1 + n2 + x + y bytes 5935185a700Sflorian */ 5945185a700Sflorian 5955185a700Sflorian dns_name_toregion(&key->name, &r1); 5965185a700Sflorian dns_name_toregion(key->algorithm, &r2); 5975185a700Sflorian if (key->key == NULL) 5985185a700Sflorian x = 0; 5995185a700Sflorian else { 6005185a700Sflorian result = dst_key_sigsize(key->key, &x); 6015185a700Sflorian if (result != ISC_R_SUCCESS) 6025185a700Sflorian x = 0; 6035185a700Sflorian } 6045185a700Sflorian return (26 + r1.length + r2.length + x + otherlen); 6055185a700Sflorian } 6065185a700Sflorian 6075185a700Sflorian isc_result_t 6085185a700Sflorian dns_message_create(unsigned int intent, dns_message_t **msgp) 6095185a700Sflorian { 6105185a700Sflorian dns_message_t *m; 6115185a700Sflorian isc_result_t result; 6125185a700Sflorian isc_buffer_t *dynbuf; 6135185a700Sflorian unsigned int i; 6145185a700Sflorian 6155185a700Sflorian REQUIRE(msgp != NULL); 6165185a700Sflorian REQUIRE(*msgp == NULL); 6175185a700Sflorian REQUIRE(intent == DNS_MESSAGE_INTENTPARSE 6185185a700Sflorian || intent == DNS_MESSAGE_INTENTRENDER); 6195185a700Sflorian 6205185a700Sflorian m = malloc(sizeof(dns_message_t)); 6215185a700Sflorian if (m == NULL) 6225185a700Sflorian return (ISC_R_NOMEMORY); 6235185a700Sflorian 6245185a700Sflorian /* 6255185a700Sflorian * No allocations until further notice. Just initialize all lists 6265185a700Sflorian * and other members that are freed in the cleanup phase here. 6275185a700Sflorian */ 6285185a700Sflorian 6295185a700Sflorian m->from_to_wire = intent; 6305185a700Sflorian msginit(m); 6315185a700Sflorian 6325185a700Sflorian for (i = 0; i < DNS_SECTION_MAX; i++) 6335185a700Sflorian ISC_LIST_INIT(m->sections[i]); 6345185a700Sflorian 6355185a700Sflorian ISC_LIST_INIT(m->scratchpad); 6365185a700Sflorian ISC_LIST_INIT(m->cleanup); 6375185a700Sflorian ISC_LIST_INIT(m->rdatas); 6385185a700Sflorian ISC_LIST_INIT(m->rdatalists); 6395185a700Sflorian ISC_LIST_INIT(m->offsets); 6405185a700Sflorian ISC_LIST_INIT(m->freerdata); 6415185a700Sflorian ISC_LIST_INIT(m->freerdatalist); 6425185a700Sflorian 6435185a700Sflorian /* 6445185a700Sflorian * Ok, it is safe to allocate (and then "goto cleanup" if failure) 6455185a700Sflorian */ 6465185a700Sflorian 6475185a700Sflorian dynbuf = NULL; 6485185a700Sflorian result = isc_buffer_allocate(&dynbuf, SCRATCHPAD_SIZE); 6495185a700Sflorian if (result != ISC_R_SUCCESS) 6505185a700Sflorian goto cleanup; 6515185a700Sflorian ISC_LIST_APPEND(m->scratchpad, dynbuf, link); 6525185a700Sflorian 6535185a700Sflorian m->cctx = NULL; 6545185a700Sflorian 6555185a700Sflorian *msgp = m; 6565185a700Sflorian return (ISC_R_SUCCESS); 6575185a700Sflorian 6585185a700Sflorian /* 6595185a700Sflorian * Cleanup for error returns. 6605185a700Sflorian */ 6615185a700Sflorian cleanup: 6625185a700Sflorian dynbuf = ISC_LIST_HEAD(m->scratchpad); 6635185a700Sflorian if (dynbuf != NULL) { 6645185a700Sflorian ISC_LIST_UNLINK(m->scratchpad, dynbuf, link); 6655185a700Sflorian isc_buffer_free(&dynbuf); 6665185a700Sflorian } 6675185a700Sflorian free(m); 6685185a700Sflorian 6695185a700Sflorian return (ISC_R_NOMEMORY); 6705185a700Sflorian } 6715185a700Sflorian 6725185a700Sflorian void 6735185a700Sflorian dns_message_destroy(dns_message_t **msgp) { 6745185a700Sflorian dns_message_t *msg; 6755185a700Sflorian 6765185a700Sflorian REQUIRE(msgp != NULL); 6775185a700Sflorian 6785185a700Sflorian msg = *msgp; 6795185a700Sflorian *msgp = NULL; 6805185a700Sflorian 6811fb015a8Sflorian msgreset(msg, 1); 6825185a700Sflorian free(msg); 6835185a700Sflorian } 6845185a700Sflorian 6855185a700Sflorian static isc_result_t 6865185a700Sflorian findname(dns_name_t **foundname, dns_name_t *target, 6875185a700Sflorian dns_namelist_t *section) 6885185a700Sflorian { 6895185a700Sflorian dns_name_t *curr; 6905185a700Sflorian 6915185a700Sflorian for (curr = ISC_LIST_TAIL(*section); 6925185a700Sflorian curr != NULL; 6935185a700Sflorian curr = ISC_LIST_PREV(curr, link)) { 6945185a700Sflorian if (dns_name_equal(curr, target)) { 6955185a700Sflorian if (foundname != NULL) 6965185a700Sflorian *foundname = curr; 6975185a700Sflorian return (ISC_R_SUCCESS); 6985185a700Sflorian } 6995185a700Sflorian } 7005185a700Sflorian 7015185a700Sflorian return (ISC_R_NOTFOUND); 7025185a700Sflorian } 7035185a700Sflorian 7045185a700Sflorian isc_result_t 7055185a700Sflorian dns_message_find(dns_name_t *name, dns_rdataclass_t rdclass, 7065185a700Sflorian dns_rdatatype_t type, dns_rdatatype_t covers, 7075185a700Sflorian dns_rdataset_t **rdataset) 7085185a700Sflorian { 7095185a700Sflorian dns_rdataset_t *curr; 7105185a700Sflorian 7115185a700Sflorian REQUIRE(name != NULL); 7125185a700Sflorian REQUIRE(rdataset == NULL || *rdataset == NULL); 7135185a700Sflorian 7145185a700Sflorian for (curr = ISC_LIST_TAIL(name->list); 7155185a700Sflorian curr != NULL; 7165185a700Sflorian curr = ISC_LIST_PREV(curr, link)) { 7175185a700Sflorian if (curr->rdclass == rdclass && 7185185a700Sflorian curr->type == type && curr->covers == covers) { 7195185a700Sflorian if (rdataset != NULL) 7205185a700Sflorian *rdataset = curr; 7215185a700Sflorian return (ISC_R_SUCCESS); 7225185a700Sflorian } 7235185a700Sflorian } 7245185a700Sflorian 7255185a700Sflorian return (ISC_R_NOTFOUND); 7265185a700Sflorian } 7275185a700Sflorian 7285185a700Sflorian isc_result_t 7295185a700Sflorian dns_message_findtype(dns_name_t *name, dns_rdatatype_t type, 7305185a700Sflorian dns_rdatatype_t covers, dns_rdataset_t **rdataset) 7315185a700Sflorian { 7325185a700Sflorian dns_rdataset_t *curr; 7335185a700Sflorian 7345185a700Sflorian REQUIRE(name != NULL); 7355185a700Sflorian REQUIRE(rdataset == NULL || *rdataset == NULL); 7365185a700Sflorian 7375185a700Sflorian for (curr = ISC_LIST_TAIL(name->list); 7385185a700Sflorian curr != NULL; 7395185a700Sflorian curr = ISC_LIST_PREV(curr, link)) { 7405185a700Sflorian if (curr->type == type && curr->covers == covers) { 7415185a700Sflorian if (rdataset != NULL) 7425185a700Sflorian *rdataset = curr; 7435185a700Sflorian return (ISC_R_SUCCESS); 7445185a700Sflorian } 7455185a700Sflorian } 7465185a700Sflorian 7475185a700Sflorian return (ISC_R_NOTFOUND); 7485185a700Sflorian } 7495185a700Sflorian 7505185a700Sflorian /* 7515185a700Sflorian * Read a name from buffer "source". 7525185a700Sflorian */ 7535185a700Sflorian static isc_result_t 7545185a700Sflorian getname(dns_name_t *name, isc_buffer_t *source, dns_message_t *msg, 7555185a700Sflorian dns_decompress_t *dctx) 7565185a700Sflorian { 7575185a700Sflorian isc_buffer_t *scratch; 7585185a700Sflorian isc_result_t result; 7595185a700Sflorian unsigned int tries; 7605185a700Sflorian 7615185a700Sflorian scratch = currentbuffer(msg); 7625185a700Sflorian 7635185a700Sflorian /* 7645185a700Sflorian * First try: use current buffer. 7655185a700Sflorian * Second try: allocate a new buffer and use that. 7665185a700Sflorian */ 7675185a700Sflorian tries = 0; 7685185a700Sflorian while (tries < 2) { 7691fb015a8Sflorian result = dns_name_fromwire(name, source, dctx, 0, 7705185a700Sflorian scratch); 7715185a700Sflorian 7725185a700Sflorian if (result == ISC_R_NOSPACE) { 7735185a700Sflorian tries++; 7745185a700Sflorian 7755185a700Sflorian result = newbuffer(msg, SCRATCHPAD_SIZE); 7765185a700Sflorian if (result != ISC_R_SUCCESS) 7775185a700Sflorian return (result); 7785185a700Sflorian 7795185a700Sflorian scratch = currentbuffer(msg); 7805185a700Sflorian dns_name_reset(name); 7815185a700Sflorian } else { 7825185a700Sflorian return (result); 7835185a700Sflorian } 7845185a700Sflorian } 7855185a700Sflorian 7865185a700Sflorian INSIST(0); /* Cannot get here... */ 7875185a700Sflorian return (ISC_R_UNEXPECTED); 7885185a700Sflorian } 7895185a700Sflorian 7905185a700Sflorian static isc_result_t 7915185a700Sflorian getrdata(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, 7925185a700Sflorian dns_rdataclass_t rdclass, dns_rdatatype_t rdtype, 7935185a700Sflorian unsigned int rdatalen, dns_rdata_t *rdata) 7945185a700Sflorian { 7955185a700Sflorian isc_buffer_t *scratch; 7965185a700Sflorian isc_result_t result; 7975185a700Sflorian unsigned int tries; 7985185a700Sflorian unsigned int trysize; 7995185a700Sflorian 8005185a700Sflorian scratch = currentbuffer(msg); 8015185a700Sflorian 8025185a700Sflorian isc_buffer_setactive(source, rdatalen); 8035185a700Sflorian 8045185a700Sflorian /* 8055185a700Sflorian * First try: use current buffer. 8065185a700Sflorian * Second try: allocate a new buffer of size 8075185a700Sflorian * max(SCRATCHPAD_SIZE, 2 * compressed_rdatalen) 8085185a700Sflorian * (the data will fit if it was not more than 50% compressed) 8095185a700Sflorian * Subsequent tries: double buffer size on each try. 8105185a700Sflorian */ 8115185a700Sflorian tries = 0; 8125185a700Sflorian trysize = 0; 8135185a700Sflorian /* XXX possibly change this to a while (tries < 2) loop */ 8145185a700Sflorian for (;;) { 8155185a700Sflorian result = dns_rdata_fromwire(rdata, rdclass, rdtype, 8165185a700Sflorian source, dctx, 0, 8175185a700Sflorian scratch); 8185185a700Sflorian 8195185a700Sflorian if (result == ISC_R_NOSPACE) { 8205185a700Sflorian if (tries == 0) { 8215185a700Sflorian trysize = 2 * rdatalen; 8225185a700Sflorian if (trysize < SCRATCHPAD_SIZE) 8235185a700Sflorian trysize = SCRATCHPAD_SIZE; 8245185a700Sflorian } else { 8255185a700Sflorian INSIST(trysize != 0); 8265185a700Sflorian if (trysize >= 65535) 8275185a700Sflorian return (ISC_R_NOSPACE); 8285185a700Sflorian /* XXX DNS_R_RRTOOLONG? */ 8295185a700Sflorian trysize *= 2; 8305185a700Sflorian } 8315185a700Sflorian tries++; 8325185a700Sflorian result = newbuffer(msg, trysize); 8335185a700Sflorian if (result != ISC_R_SUCCESS) 8345185a700Sflorian return (result); 8355185a700Sflorian 8365185a700Sflorian scratch = currentbuffer(msg); 8375185a700Sflorian } else { 8385185a700Sflorian return (result); 8395185a700Sflorian } 8405185a700Sflorian } 8415185a700Sflorian } 8425185a700Sflorian 8435185a700Sflorian #define DO_FORMERR \ 8445185a700Sflorian do { \ 8455185a700Sflorian if (best_effort) \ 8461fb015a8Sflorian seen_problem = 1; \ 8475185a700Sflorian else { \ 8485185a700Sflorian result = DNS_R_FORMERR; \ 8495185a700Sflorian goto cleanup; \ 8505185a700Sflorian } \ 8515185a700Sflorian } while (0) 8525185a700Sflorian 8535185a700Sflorian static isc_result_t 8545185a700Sflorian getquestions(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, 8555185a700Sflorian unsigned int options) 8565185a700Sflorian { 8575185a700Sflorian isc_region_t r; 8585185a700Sflorian unsigned int count; 8595185a700Sflorian dns_name_t *name; 8605185a700Sflorian dns_name_t *name2; 8615185a700Sflorian dns_offsets_t *offsets; 8625185a700Sflorian dns_rdataset_t *rdataset; 8635185a700Sflorian dns_rdatalist_t *rdatalist; 8645185a700Sflorian isc_result_t result; 8655185a700Sflorian dns_rdatatype_t rdtype; 8665185a700Sflorian dns_rdataclass_t rdclass; 8675185a700Sflorian dns_namelist_t *section; 8681fb015a8Sflorian int free_name; 8691fb015a8Sflorian int best_effort; 8701fb015a8Sflorian int seen_problem; 8715185a700Sflorian 8725185a700Sflorian section = &msg->sections[DNS_SECTION_QUESTION]; 8735185a700Sflorian 8741fb015a8Sflorian best_effort = options & DNS_MESSAGEPARSE_BESTEFFORT; 8751fb015a8Sflorian seen_problem = 0; 8765185a700Sflorian 8775185a700Sflorian name = NULL; 8785185a700Sflorian rdataset = NULL; 8795185a700Sflorian rdatalist = NULL; 8805185a700Sflorian 8815185a700Sflorian for (count = 0; count < msg->counts[DNS_SECTION_QUESTION]; count++) { 8825185a700Sflorian name = malloc(sizeof(dns_name_t)); 8835185a700Sflorian if (name == NULL) 8845185a700Sflorian return (ISC_R_NOMEMORY); 8851fb015a8Sflorian free_name = 1; 8865185a700Sflorian 8875185a700Sflorian offsets = newoffsets(msg); 8885185a700Sflorian if (offsets == NULL) { 8895185a700Sflorian result = ISC_R_NOMEMORY; 8905185a700Sflorian goto cleanup; 8915185a700Sflorian } 8925185a700Sflorian dns_name_init(name, *offsets); 8935185a700Sflorian 8945185a700Sflorian /* 8955185a700Sflorian * Parse the name out of this packet. 8965185a700Sflorian */ 8975185a700Sflorian isc_buffer_remainingregion(source, &r); 8985185a700Sflorian isc_buffer_setactive(source, r.length); 8995185a700Sflorian result = getname(name, source, msg, dctx); 9005185a700Sflorian if (result != ISC_R_SUCCESS) 9015185a700Sflorian goto cleanup; 9025185a700Sflorian 9035185a700Sflorian /* 9045185a700Sflorian * Run through the section, looking to see if this name 9055185a700Sflorian * is already there. If it is found, put back the allocated 9065185a700Sflorian * name since we no longer need it, and set our name pointer 9075185a700Sflorian * to point to the name we found. 9085185a700Sflorian */ 9095185a700Sflorian result = findname(&name2, name, section); 9105185a700Sflorian 9115185a700Sflorian /* 9125185a700Sflorian * If it is the first name in the section, accept it. 9135185a700Sflorian * 9145185a700Sflorian * If it is not, but is not the same as the name already 9155185a700Sflorian * in the question section, append to the section. Note that 9165185a700Sflorian * here in the question section this is illegal, so return 9175185a700Sflorian * FORMERR. In the future, check the opcode to see if 9185185a700Sflorian * this should be legal or not. In either case we no longer 9195185a700Sflorian * need this name pointer. 9205185a700Sflorian */ 9215185a700Sflorian if (result != ISC_R_SUCCESS) { 9225185a700Sflorian if (!ISC_LIST_EMPTY(*section)) 9235185a700Sflorian DO_FORMERR; 9245185a700Sflorian ISC_LIST_APPEND(*section, name, link); 9251fb015a8Sflorian free_name = 0; 9265185a700Sflorian } else { 9275185a700Sflorian free(name); 9285185a700Sflorian name = name2; 9295185a700Sflorian name2 = NULL; 9301fb015a8Sflorian free_name = 0; 9315185a700Sflorian } 9325185a700Sflorian 9335185a700Sflorian /* 9345185a700Sflorian * Get type and class. 9355185a700Sflorian */ 9365185a700Sflorian isc_buffer_remainingregion(source, &r); 9375185a700Sflorian if (r.length < 4) { 9385185a700Sflorian result = ISC_R_UNEXPECTEDEND; 9395185a700Sflorian goto cleanup; 9405185a700Sflorian } 9415185a700Sflorian rdtype = isc_buffer_getuint16(source); 9425185a700Sflorian rdclass = isc_buffer_getuint16(source); 9435185a700Sflorian 9445185a700Sflorian /* 9455185a700Sflorian * If this class is different than the one we already read, 9465185a700Sflorian * this is an error. 9475185a700Sflorian */ 9485185a700Sflorian if (msg->rdclass_set == 0) { 9495185a700Sflorian msg->rdclass = rdclass; 9505185a700Sflorian msg->rdclass_set = 1; 9515185a700Sflorian } else if (msg->rdclass != rdclass) 9525185a700Sflorian DO_FORMERR; 9535185a700Sflorian 9545185a700Sflorian /* 9555185a700Sflorian * Is this a TKEY query? 9565185a700Sflorian */ 9575185a700Sflorian if (rdtype == dns_rdatatype_tkey) 9585185a700Sflorian msg->tkey = 1; 9595185a700Sflorian 9605185a700Sflorian /* 9615185a700Sflorian * Can't ask the same question twice. 9625185a700Sflorian */ 9635185a700Sflorian result = dns_message_find(name, rdclass, rdtype, 0, NULL); 9645185a700Sflorian if (result == ISC_R_SUCCESS) 9655185a700Sflorian DO_FORMERR; 9665185a700Sflorian 9675185a700Sflorian /* 9685185a700Sflorian * Allocate a new rdatalist. 9695185a700Sflorian */ 9705185a700Sflorian rdatalist = newrdatalist(msg); 9715185a700Sflorian if (rdatalist == NULL) { 9725185a700Sflorian result = ISC_R_NOMEMORY; 9735185a700Sflorian goto cleanup; 9745185a700Sflorian } 9755185a700Sflorian rdataset = malloc(sizeof(dns_rdataset_t)); 9765185a700Sflorian if (rdataset == NULL) { 9775185a700Sflorian result = ISC_R_NOMEMORY; 9785185a700Sflorian goto cleanup; 9795185a700Sflorian } 9805185a700Sflorian 9815185a700Sflorian /* 9825185a700Sflorian * Convert rdatalist to rdataset, and attach the latter to 9835185a700Sflorian * the name. 9845185a700Sflorian */ 9855185a700Sflorian rdatalist->type = rdtype; 9865185a700Sflorian rdatalist->covers = 0; 9875185a700Sflorian rdatalist->rdclass = rdclass; 9885185a700Sflorian rdatalist->ttl = 0; 9895185a700Sflorian ISC_LIST_INIT(rdatalist->rdata); 9905185a700Sflorian 9915185a700Sflorian dns_rdataset_init(rdataset); 9925185a700Sflorian result = dns_rdatalist_tordataset(rdatalist, rdataset); 9935185a700Sflorian if (result != ISC_R_SUCCESS) 9945185a700Sflorian goto cleanup; 9955185a700Sflorian 9965185a700Sflorian rdataset->attributes |= DNS_RDATASETATTR_QUESTION; 9975185a700Sflorian 9985185a700Sflorian ISC_LIST_APPEND(name->list, rdataset, link); 9995185a700Sflorian rdataset = NULL; 10005185a700Sflorian } 10015185a700Sflorian 10025185a700Sflorian if (seen_problem) 10035185a700Sflorian return (DNS_R_RECOVERABLE); 10045185a700Sflorian return (ISC_R_SUCCESS); 10055185a700Sflorian 10065185a700Sflorian cleanup: 10075185a700Sflorian if (rdataset != NULL) { 10085185a700Sflorian INSIST(!dns_rdataset_isassociated(rdataset)); 10095185a700Sflorian free(rdataset); 10105185a700Sflorian } 10115185a700Sflorian if (free_name) 10125185a700Sflorian free(name); 10135185a700Sflorian 10145185a700Sflorian return (result); 10155185a700Sflorian } 10165185a700Sflorian 10171fb015a8Sflorian static int 10185185a700Sflorian update(dns_section_t section, dns_rdataclass_t rdclass) { 10195185a700Sflorian if (section == DNS_SECTION_PREREQUISITE) 10201fb015a8Sflorian return (rdclass == dns_rdataclass_any || 10211fb015a8Sflorian rdclass == dns_rdataclass_none); 10225185a700Sflorian if (section == DNS_SECTION_UPDATE) 10231fb015a8Sflorian return (rdclass == dns_rdataclass_any); 10241fb015a8Sflorian return (0); 10255185a700Sflorian } 10265185a700Sflorian 10275185a700Sflorian static isc_result_t 10285185a700Sflorian getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, 10295185a700Sflorian dns_section_t sectionid, unsigned int options) 10305185a700Sflorian { 10315185a700Sflorian isc_region_t r; 10325185a700Sflorian unsigned int count, rdatalen; 10335185a700Sflorian dns_name_t *name = NULL; 10345185a700Sflorian dns_offsets_t *offsets; 10355185a700Sflorian dns_rdataset_t *rdataset; 10365185a700Sflorian dns_rdatalist_t *rdatalist; 10375185a700Sflorian isc_result_t result; 10385185a700Sflorian dns_rdatatype_t rdtype, covers; 10395185a700Sflorian dns_rdataclass_t rdclass; 10405185a700Sflorian dns_rdata_t *rdata; 10415185a700Sflorian dns_ttl_t ttl; 10425185a700Sflorian dns_namelist_t *section; 10431fb015a8Sflorian int free_name = 0, free_rdataset = 0; 10441fb015a8Sflorian int best_effort, seen_problem; 10451fb015a8Sflorian int issigzero; 10465185a700Sflorian 10471fb015a8Sflorian best_effort = options & DNS_MESSAGEPARSE_BESTEFFORT; 10481fb015a8Sflorian seen_problem = 0; 10495185a700Sflorian 10505185a700Sflorian section = &msg->sections[sectionid]; 10515185a700Sflorian 10525185a700Sflorian for (count = 0; count < msg->counts[sectionid]; count++) { 10535185a700Sflorian int recstart = source->current; 10541fb015a8Sflorian free_rdataset = 0; 10555185a700Sflorian 10565185a700Sflorian name = malloc(sizeof(dns_name_t)); 10575185a700Sflorian if (name == NULL) 10585185a700Sflorian return (ISC_R_NOMEMORY); 10591fb015a8Sflorian free_name = 1; 10605185a700Sflorian 10615185a700Sflorian offsets = newoffsets(msg); 10625185a700Sflorian if (offsets == NULL) { 10635185a700Sflorian result = ISC_R_NOMEMORY; 10645185a700Sflorian goto cleanup; 10655185a700Sflorian } 10665185a700Sflorian dns_name_init(name, *offsets); 10675185a700Sflorian 10685185a700Sflorian /* 10695185a700Sflorian * Parse the name out of this packet. 10705185a700Sflorian */ 10715185a700Sflorian isc_buffer_remainingregion(source, &r); 10725185a700Sflorian isc_buffer_setactive(source, r.length); 10735185a700Sflorian result = getname(name, source, msg, dctx); 10745185a700Sflorian if (result != ISC_R_SUCCESS) 10755185a700Sflorian goto cleanup; 10765185a700Sflorian 10775185a700Sflorian /* 10785185a700Sflorian * Get type, class, ttl, and rdatalen. Verify that at least 10795185a700Sflorian * rdatalen bytes remain. (Some of this is deferred to 10805185a700Sflorian * later.) 10815185a700Sflorian */ 10825185a700Sflorian isc_buffer_remainingregion(source, &r); 10835185a700Sflorian if (r.length < 2 + 2 + 4 + 2) { 10845185a700Sflorian result = ISC_R_UNEXPECTEDEND; 10855185a700Sflorian goto cleanup; 10865185a700Sflorian } 10875185a700Sflorian rdtype = isc_buffer_getuint16(source); 10885185a700Sflorian rdclass = isc_buffer_getuint16(source); 10895185a700Sflorian 10905185a700Sflorian /* 10915185a700Sflorian * If there was no question section, we may not yet have 10925185a700Sflorian * established a class. Do so now. 10935185a700Sflorian */ 10945185a700Sflorian if (msg->rdclass_set == 0 && 10955185a700Sflorian rdtype != dns_rdatatype_opt && /* class is UDP SIZE */ 10965185a700Sflorian rdtype != dns_rdatatype_tsig && /* class is ANY */ 10975185a700Sflorian rdtype != dns_rdatatype_tkey) { /* class is undefined */ 10985185a700Sflorian msg->rdclass = rdclass; 10995185a700Sflorian msg->rdclass_set = 1; 11005185a700Sflorian } 11015185a700Sflorian 11025185a700Sflorian /* 11035185a700Sflorian * If this class is different than the one in the question 11045185a700Sflorian * section, bail. 11055185a700Sflorian */ 11065185a700Sflorian if (msg->opcode != dns_opcode_update 11075185a700Sflorian && rdtype != dns_rdatatype_tsig 11085185a700Sflorian && rdtype != dns_rdatatype_opt 11095185a700Sflorian && rdtype != dns_rdatatype_key /* in a TKEY query */ 11105185a700Sflorian && rdtype != dns_rdatatype_sig /* SIG(0) */ 11115185a700Sflorian && rdtype != dns_rdatatype_tkey /* Win2000 TKEY */ 11125185a700Sflorian && msg->rdclass != dns_rdataclass_any 11135185a700Sflorian && msg->rdclass != rdclass) 11145185a700Sflorian DO_FORMERR; 11155185a700Sflorian 11165185a700Sflorian /* 11175185a700Sflorian * If this is not a TKEY query/response then the KEY 11185185a700Sflorian * record's class needs to match. 11195185a700Sflorian */ 11205185a700Sflorian if (msg->opcode != dns_opcode_update && !msg->tkey && 11215185a700Sflorian rdtype == dns_rdatatype_key && 11225185a700Sflorian msg->rdclass != dns_rdataclass_any && 11235185a700Sflorian msg->rdclass != rdclass) 11245185a700Sflorian DO_FORMERR; 11255185a700Sflorian 11265185a700Sflorian /* 11275185a700Sflorian * Special type handling for TSIG, OPT, and TKEY. 11285185a700Sflorian */ 11295185a700Sflorian if (rdtype == dns_rdatatype_tsig) { 11305185a700Sflorian /* 11315185a700Sflorian * If it is a tsig, verify that it is in the 11325185a700Sflorian * additional data section. 11335185a700Sflorian */ 11345185a700Sflorian if (sectionid != DNS_SECTION_ADDITIONAL || 11355185a700Sflorian rdclass != dns_rdataclass_any || 11365185a700Sflorian count != msg->counts[sectionid] - 1) 11375185a700Sflorian DO_FORMERR; 11385185a700Sflorian msg->sigstart = recstart; 11395185a700Sflorian } else if (rdtype == dns_rdatatype_opt) { 11405185a700Sflorian /* 11415185a700Sflorian * The name of an OPT record must be ".", it 11425185a700Sflorian * must be in the additional data section, and 11435185a700Sflorian * it must be the first OPT we've seen. 11445185a700Sflorian */ 11455185a700Sflorian if (!dns_name_equal(dns_rootname, name) || 11465185a700Sflorian sectionid != DNS_SECTION_ADDITIONAL || 11475185a700Sflorian msg->opt != NULL) 11485185a700Sflorian DO_FORMERR; 11495185a700Sflorian } else if (rdtype == dns_rdatatype_tkey) { 11505185a700Sflorian /* 11515185a700Sflorian * A TKEY must be in the additional section if this 11525185a700Sflorian * is a query, and the answer section if this is a 11535185a700Sflorian * response. Unless it's a Win2000 client. 11545185a700Sflorian * 11555185a700Sflorian * Its class is ignored. 11565185a700Sflorian */ 11575185a700Sflorian dns_section_t tkeysection; 11585185a700Sflorian 11595185a700Sflorian if ((msg->flags & DNS_MESSAGEFLAG_QR) == 0) 11605185a700Sflorian tkeysection = DNS_SECTION_ADDITIONAL; 11615185a700Sflorian else 11625185a700Sflorian tkeysection = DNS_SECTION_ANSWER; 11635185a700Sflorian if (sectionid != tkeysection && 11645185a700Sflorian sectionid != DNS_SECTION_ANSWER) 11655185a700Sflorian DO_FORMERR; 11665185a700Sflorian } 11675185a700Sflorian 11685185a700Sflorian /* 11695185a700Sflorian * ... now get ttl and rdatalen, and check buffer. 11705185a700Sflorian */ 11715185a700Sflorian ttl = isc_buffer_getuint32(source); 11725185a700Sflorian rdatalen = isc_buffer_getuint16(source); 11735185a700Sflorian r.length -= (2 + 2 + 4 + 2); 11745185a700Sflorian if (r.length < rdatalen) { 11755185a700Sflorian result = ISC_R_UNEXPECTEDEND; 11765185a700Sflorian goto cleanup; 11775185a700Sflorian } 11785185a700Sflorian 11795185a700Sflorian /* 11805185a700Sflorian * Read the rdata from the wire format. Interpret the 11815185a700Sflorian * rdata according to its actual class, even if it had a 11825185a700Sflorian * DynDNS meta-class in the packet (unless this is a TSIG). 11835185a700Sflorian * Then put the meta-class back into the finished rdata. 11845185a700Sflorian */ 11855185a700Sflorian rdata = newrdata(msg); 11865185a700Sflorian if (rdata == NULL) { 11875185a700Sflorian result = ISC_R_NOMEMORY; 11885185a700Sflorian goto cleanup; 11895185a700Sflorian } 11905185a700Sflorian if (msg->opcode == dns_opcode_update && 11915185a700Sflorian update(sectionid, rdclass)) { 11925185a700Sflorian if (rdatalen != 0) { 11935185a700Sflorian result = DNS_R_FORMERR; 11945185a700Sflorian goto cleanup; 11955185a700Sflorian } 11965185a700Sflorian /* 11975185a700Sflorian * When the rdata is empty, the data pointer is 11985185a700Sflorian * never dereferenced, but it must still be non-NULL. 11995185a700Sflorian * Casting 1 rather than "" avoids warnings about 12005185a700Sflorian * discarding the const attribute of a string, 12015185a700Sflorian * for compilers that would warn about such things. 12025185a700Sflorian */ 12035185a700Sflorian rdata->data = (unsigned char *)1; 12045185a700Sflorian rdata->length = 0; 12055185a700Sflorian rdata->rdclass = rdclass; 12065185a700Sflorian rdata->type = rdtype; 12075185a700Sflorian rdata->flags = DNS_RDATA_UPDATE; 12085185a700Sflorian result = ISC_R_SUCCESS; 12095185a700Sflorian } else if (rdclass == dns_rdataclass_none && 12105185a700Sflorian msg->opcode == dns_opcode_update && 12115185a700Sflorian sectionid == DNS_SECTION_UPDATE) { 12125185a700Sflorian result = getrdata(source, msg, dctx, msg->rdclass, 12135185a700Sflorian rdtype, rdatalen, rdata); 12145185a700Sflorian } else 12155185a700Sflorian result = getrdata(source, msg, dctx, rdclass, 12165185a700Sflorian rdtype, rdatalen, rdata); 12175185a700Sflorian if (result != ISC_R_SUCCESS) 12185185a700Sflorian goto cleanup; 12195185a700Sflorian rdata->rdclass = rdclass; 12201fb015a8Sflorian issigzero = 0; 12215185a700Sflorian if (rdtype == dns_rdatatype_rrsig && 12225185a700Sflorian rdata->flags == 0) { 12235185a700Sflorian covers = dns_rdata_covers(rdata); 12245185a700Sflorian if (covers == 0) 12255185a700Sflorian DO_FORMERR; 12265185a700Sflorian } else if (rdtype == dns_rdatatype_sig /* SIG(0) */ && 12275185a700Sflorian rdata->flags == 0) { 12285185a700Sflorian covers = dns_rdata_covers(rdata); 12295185a700Sflorian if (covers == 0) { 12305185a700Sflorian if (sectionid != DNS_SECTION_ADDITIONAL || 12315185a700Sflorian count != msg->counts[sectionid] - 1) 12325185a700Sflorian DO_FORMERR; 12335185a700Sflorian msg->sigstart = recstart; 12341fb015a8Sflorian issigzero = 1; 12355185a700Sflorian } else { 12365185a700Sflorian if (msg->rdclass != dns_rdataclass_any && 12375185a700Sflorian msg->rdclass != rdclass) 12385185a700Sflorian DO_FORMERR; 12395185a700Sflorian } 12405185a700Sflorian } else 12415185a700Sflorian covers = 0; 12425185a700Sflorian 12435185a700Sflorian /* 12445185a700Sflorian * Check the ownername of NSEC3 records 12455185a700Sflorian */ 12465185a700Sflorian if (rdtype == dns_rdatatype_nsec3 && 124766d1673bSflorian !dns_rdata_checkowner_nsec3(name, msg->rdclass, rdtype, 12481fb015a8Sflorian 0)) { 12495185a700Sflorian result = DNS_R_BADOWNERNAME; 12505185a700Sflorian goto cleanup; 12515185a700Sflorian } 12525185a700Sflorian 12535185a700Sflorian if (rdtype != dns_rdatatype_opt && 1254a9a8f5b3Sflorian rdtype != dns_rdatatype_tsig && !issigzero) { 12555185a700Sflorian ISC_LIST_APPEND(*section, name, link); 12561fb015a8Sflorian free_name = 0; 12575185a700Sflorian } 12585185a700Sflorian 12595185a700Sflorian rdataset = malloc(sizeof(dns_rdataset_t)); 12605185a700Sflorian if (rdataset == NULL) { 12615185a700Sflorian result = ISC_R_NOMEMORY; 12625185a700Sflorian goto cleanup; 12635185a700Sflorian } 12641fb015a8Sflorian free_rdataset = 1; 12655185a700Sflorian 12665185a700Sflorian rdatalist = newrdatalist(msg); 12675185a700Sflorian if (rdatalist == NULL) { 12685185a700Sflorian result = ISC_R_NOMEMORY; 12695185a700Sflorian goto cleanup; 12705185a700Sflorian } 12715185a700Sflorian 12725185a700Sflorian rdatalist->type = rdtype; 12735185a700Sflorian rdatalist->covers = covers; 12745185a700Sflorian rdatalist->rdclass = rdclass; 12755185a700Sflorian rdatalist->ttl = ttl; 12765185a700Sflorian ISC_LIST_INIT(rdatalist->rdata); 12775185a700Sflorian 12785185a700Sflorian dns_rdataset_init(rdataset); 12795185a700Sflorian RUNTIME_CHECK(dns_rdatalist_tordataset(rdatalist, 12805185a700Sflorian rdataset) 12815185a700Sflorian == ISC_R_SUCCESS); 12825185a700Sflorian 12835185a700Sflorian if (rdtype != dns_rdatatype_opt && 12845185a700Sflorian rdtype != dns_rdatatype_tsig && 12855185a700Sflorian !issigzero) 12865185a700Sflorian { 12875185a700Sflorian ISC_LIST_APPEND(name->list, rdataset, link); 12881fb015a8Sflorian free_rdataset = 0; 12895185a700Sflorian } 12905185a700Sflorian 12915185a700Sflorian /* 12925185a700Sflorian * Minimize TTLs. 12935185a700Sflorian * 12945185a700Sflorian * Section 5.2 of RFC2181 says we should drop 12955185a700Sflorian * nonauthoritative rrsets where the TTLs differ, but we 12965185a700Sflorian * currently treat them the as if they were authoritative and 12975185a700Sflorian * minimize them. 12985185a700Sflorian */ 12995185a700Sflorian if (ttl < rdataset->ttl) 13005185a700Sflorian rdataset->ttl = ttl; 13015185a700Sflorian 13025185a700Sflorian /* Append this rdata to the rdataset. */ 13035185a700Sflorian dns_rdatalist_fromrdataset(rdataset, &rdatalist); 13045185a700Sflorian ISC_LIST_APPEND(rdatalist->rdata, rdata, link); 13055185a700Sflorian 13065185a700Sflorian /* 13075185a700Sflorian * If this is an OPT, SIG(0) or TSIG record, remember it. 13085185a700Sflorian * Also, set the extended rcode for TSIG. 13095185a700Sflorian * 13105185a700Sflorian * Note msg->opt, msg->sig0 and msg->tsig will only be 13115185a700Sflorian * already set if best-effort parsing is enabled otherwise 13125185a700Sflorian * there will only be at most one of each. 13135185a700Sflorian */ 13145185a700Sflorian if (rdtype == dns_rdatatype_opt && msg->opt == NULL) { 13155185a700Sflorian dns_rcode_t ercode; 13165185a700Sflorian 13175185a700Sflorian msg->opt = rdataset; 13185185a700Sflorian rdataset = NULL; 13191fb015a8Sflorian free_rdataset = 0; 13205185a700Sflorian ercode = (dns_rcode_t) 13215185a700Sflorian ((msg->opt->ttl & DNS_MESSAGE_EDNSRCODE_MASK) 13225185a700Sflorian >> 20); 13235185a700Sflorian msg->rcode |= ercode; 13245185a700Sflorian free(name); 13251fb015a8Sflorian free_name = 0; 13265185a700Sflorian } else if (issigzero && msg->sig0 == NULL) { 13275185a700Sflorian msg->sig0 = rdataset; 13285185a700Sflorian msg->sig0name = name; 13295185a700Sflorian rdataset = NULL; 13301fb015a8Sflorian free_rdataset = 0; 13311fb015a8Sflorian free_name = 0; 13325185a700Sflorian } else if (rdtype == dns_rdatatype_tsig && msg->tsig == NULL) { 13335185a700Sflorian msg->tsig = rdataset; 13345185a700Sflorian msg->tsigname = name; 13355185a700Sflorian /* Windows doesn't like TSIG names to be compressed. */ 13365185a700Sflorian msg->tsigname->attributes |= DNS_NAMEATTR_NOCOMPRESS; 13375185a700Sflorian rdataset = NULL; 13381fb015a8Sflorian free_rdataset = 0; 13391fb015a8Sflorian free_name = 0; 13405185a700Sflorian } 13415185a700Sflorian 13425185a700Sflorian if (seen_problem) { 13435185a700Sflorian if (free_name) 13445185a700Sflorian free(name); 13455185a700Sflorian if (free_rdataset) 13465185a700Sflorian free(rdataset); 13471fb015a8Sflorian free_name = free_rdataset = 0; 13485185a700Sflorian } 13491fb015a8Sflorian INSIST(!free_name); 13501fb015a8Sflorian INSIST(!free_rdataset); 13515185a700Sflorian } 13525185a700Sflorian 13535185a700Sflorian if (seen_problem) 13545185a700Sflorian return (DNS_R_RECOVERABLE); 13555185a700Sflorian return (ISC_R_SUCCESS); 13565185a700Sflorian 13575185a700Sflorian cleanup: 13585185a700Sflorian if (free_name) 13595185a700Sflorian free(name); 13605185a700Sflorian if (free_rdataset) 13615185a700Sflorian free(rdataset); 13625185a700Sflorian 13635185a700Sflorian return (result); 13645185a700Sflorian } 13655185a700Sflorian 13665185a700Sflorian isc_result_t 13675185a700Sflorian dns_message_parse(dns_message_t *msg, isc_buffer_t *source, 13685185a700Sflorian unsigned int options) 13695185a700Sflorian { 13705185a700Sflorian isc_region_t r; 13715185a700Sflorian dns_decompress_t dctx; 13725185a700Sflorian isc_result_t ret; 13735185a700Sflorian uint16_t tmpflags; 13745185a700Sflorian isc_buffer_t origsource; 13751fb015a8Sflorian int seen_problem; 13761fb015a8Sflorian int ignore_tc; 13775185a700Sflorian 13785185a700Sflorian REQUIRE(source != NULL); 13795185a700Sflorian REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTPARSE); 13805185a700Sflorian 13811fb015a8Sflorian seen_problem = 0; 13821fb015a8Sflorian ignore_tc = options & DNS_MESSAGEPARSE_IGNORETRUNCATION; 13835185a700Sflorian 13845185a700Sflorian origsource = *source; 13855185a700Sflorian 13865185a700Sflorian msg->header_ok = 0; 13875185a700Sflorian msg->question_ok = 0; 13885185a700Sflorian 13895185a700Sflorian isc_buffer_remainingregion(source, &r); 13905185a700Sflorian if (r.length < DNS_MESSAGE_HEADERLEN) 13915185a700Sflorian return (ISC_R_UNEXPECTEDEND); 13925185a700Sflorian 13935185a700Sflorian msg->id = isc_buffer_getuint16(source); 13945185a700Sflorian tmpflags = isc_buffer_getuint16(source); 13955185a700Sflorian msg->opcode = ((tmpflags & DNS_MESSAGE_OPCODE_MASK) 13965185a700Sflorian >> DNS_MESSAGE_OPCODE_SHIFT); 13975185a700Sflorian msg->rcode = (dns_rcode_t)(tmpflags & DNS_MESSAGE_RCODE_MASK); 13985185a700Sflorian msg->flags = (tmpflags & DNS_MESSAGE_FLAG_MASK); 13995185a700Sflorian msg->counts[DNS_SECTION_QUESTION] = isc_buffer_getuint16(source); 14005185a700Sflorian msg->counts[DNS_SECTION_ANSWER] = isc_buffer_getuint16(source); 14015185a700Sflorian msg->counts[DNS_SECTION_AUTHORITY] = isc_buffer_getuint16(source); 14025185a700Sflorian msg->counts[DNS_SECTION_ADDITIONAL] = isc_buffer_getuint16(source); 14035185a700Sflorian 14045185a700Sflorian msg->header_ok = 1; 14055185a700Sflorian msg->state = DNS_SECTION_QUESTION; 14065185a700Sflorian 14075185a700Sflorian /* 14085185a700Sflorian * -1 means no EDNS. 14095185a700Sflorian */ 14105185a700Sflorian dns_decompress_init(&dctx, -1, DNS_DECOMPRESS_ANY); 14115185a700Sflorian 14125185a700Sflorian dns_decompress_setmethods(&dctx, DNS_COMPRESS_GLOBAL14); 14135185a700Sflorian 14145185a700Sflorian ret = getquestions(source, msg, &dctx, options); 14155185a700Sflorian if (ret == ISC_R_UNEXPECTEDEND && ignore_tc) 14165185a700Sflorian goto truncated; 14175185a700Sflorian if (ret == DNS_R_RECOVERABLE) { 14181fb015a8Sflorian seen_problem = 1; 14195185a700Sflorian ret = ISC_R_SUCCESS; 14205185a700Sflorian } 14215185a700Sflorian if (ret != ISC_R_SUCCESS) 14225185a700Sflorian return (ret); 14235185a700Sflorian msg->question_ok = 1; 14245185a700Sflorian 14255185a700Sflorian ret = getsection(source, msg, &dctx, DNS_SECTION_ANSWER, options); 14265185a700Sflorian if (ret == ISC_R_UNEXPECTEDEND && ignore_tc) 14275185a700Sflorian goto truncated; 14285185a700Sflorian if (ret == DNS_R_RECOVERABLE) { 14291fb015a8Sflorian seen_problem = 1; 14305185a700Sflorian ret = ISC_R_SUCCESS; 14315185a700Sflorian } 14325185a700Sflorian if (ret != ISC_R_SUCCESS) 14335185a700Sflorian return (ret); 14345185a700Sflorian 14355185a700Sflorian ret = getsection(source, msg, &dctx, DNS_SECTION_AUTHORITY, options); 14365185a700Sflorian if (ret == ISC_R_UNEXPECTEDEND && ignore_tc) 14375185a700Sflorian goto truncated; 14385185a700Sflorian if (ret == DNS_R_RECOVERABLE) { 14391fb015a8Sflorian seen_problem = 1; 14405185a700Sflorian ret = ISC_R_SUCCESS; 14415185a700Sflorian } 14425185a700Sflorian if (ret != ISC_R_SUCCESS) 14435185a700Sflorian return (ret); 14445185a700Sflorian 14455185a700Sflorian ret = getsection(source, msg, &dctx, DNS_SECTION_ADDITIONAL, options); 14465185a700Sflorian if (ret == ISC_R_UNEXPECTEDEND && ignore_tc) 14475185a700Sflorian goto truncated; 14485185a700Sflorian if (ret == DNS_R_RECOVERABLE) { 14491fb015a8Sflorian seen_problem = 1; 14505185a700Sflorian ret = ISC_R_SUCCESS; 14515185a700Sflorian } 14525185a700Sflorian if (ret != ISC_R_SUCCESS) 14535185a700Sflorian return (ret); 14545185a700Sflorian 14555185a700Sflorian isc_buffer_remainingregion(source, &r); 14565185a700Sflorian if (r.length != 0) { 14575185a700Sflorian isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL, 14585185a700Sflorian DNS_LOGMODULE_MESSAGE, ISC_LOG_DEBUG(3), 14595185a700Sflorian "message has %u byte(s) of trailing garbage", 14605185a700Sflorian r.length); 14615185a700Sflorian } 14625185a700Sflorian 14635185a700Sflorian truncated: 14645185a700Sflorian isc_buffer_usedregion(&origsource, &msg->saved); 14655185a700Sflorian 14665185a700Sflorian if (ret == ISC_R_UNEXPECTEDEND && ignore_tc) 14675185a700Sflorian return (DNS_R_RECOVERABLE); 14681fb015a8Sflorian if (seen_problem) 14695185a700Sflorian return (DNS_R_RECOVERABLE); 14705185a700Sflorian return (ISC_R_SUCCESS); 14715185a700Sflorian } 14725185a700Sflorian 14735185a700Sflorian isc_result_t 14745185a700Sflorian dns_message_renderbegin(dns_message_t *msg, dns_compress_t *cctx, 14755185a700Sflorian isc_buffer_t *buffer) 14765185a700Sflorian { 14775185a700Sflorian isc_region_t r; 14785185a700Sflorian 14795185a700Sflorian REQUIRE(buffer != NULL); 14805185a700Sflorian REQUIRE(msg->buffer == NULL); 14815185a700Sflorian REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER); 14825185a700Sflorian 14835185a700Sflorian msg->cctx = cctx; 14845185a700Sflorian 14855185a700Sflorian /* 14865185a700Sflorian * Erase the contents of this buffer. 14875185a700Sflorian */ 14885185a700Sflorian isc_buffer_clear(buffer); 14895185a700Sflorian 14905185a700Sflorian /* 14915185a700Sflorian * Make certain there is enough for at least the header in this 14925185a700Sflorian * buffer. 14935185a700Sflorian */ 14945185a700Sflorian isc_buffer_availableregion(buffer, &r); 14955185a700Sflorian if (r.length < DNS_MESSAGE_HEADERLEN) 14965185a700Sflorian return (ISC_R_NOSPACE); 14975185a700Sflorian 14985185a700Sflorian if (r.length - DNS_MESSAGE_HEADERLEN < msg->reserved) 14995185a700Sflorian return (ISC_R_NOSPACE); 15005185a700Sflorian 15015185a700Sflorian /* 15025185a700Sflorian * Reserve enough space for the header in this buffer. 15035185a700Sflorian */ 15045185a700Sflorian isc_buffer_add(buffer, DNS_MESSAGE_HEADERLEN); 15055185a700Sflorian 15065185a700Sflorian msg->buffer = buffer; 15075185a700Sflorian 15085185a700Sflorian return (ISC_R_SUCCESS); 15095185a700Sflorian } 15105185a700Sflorian 15115185a700Sflorian void 15125185a700Sflorian dns_message_renderrelease(dns_message_t *msg, unsigned int space) { 15135185a700Sflorian REQUIRE(space <= msg->reserved); 15145185a700Sflorian 15155185a700Sflorian msg->reserved -= space; 15165185a700Sflorian } 15175185a700Sflorian 15185185a700Sflorian isc_result_t 15195185a700Sflorian dns_message_renderreserve(dns_message_t *msg, unsigned int space) { 15205185a700Sflorian isc_region_t r; 15215185a700Sflorian 15225185a700Sflorian if (msg->buffer != NULL) { 15235185a700Sflorian isc_buffer_availableregion(msg->buffer, &r); 15245185a700Sflorian if (r.length < (space + msg->reserved)) 15255185a700Sflorian return (ISC_R_NOSPACE); 15265185a700Sflorian } 15275185a700Sflorian 15285185a700Sflorian msg->reserved += space; 15295185a700Sflorian 15305185a700Sflorian return (ISC_R_SUCCESS); 15315185a700Sflorian } 15325185a700Sflorian 15331fb015a8Sflorian static inline int 15347d5fc286Sflorian wrong_priority(dns_rdataset_t *rds, int pass) { 15355185a700Sflorian int pass_needed; 15365185a700Sflorian 15375185a700Sflorian /* 15385185a700Sflorian * If we are not rendering class IN, this ordering is bogus. 15395185a700Sflorian */ 15405185a700Sflorian if (rds->rdclass != dns_rdataclass_in) 15411fb015a8Sflorian return (0); 15425185a700Sflorian 15435185a700Sflorian switch (rds->type) { 15445185a700Sflorian case dns_rdatatype_a: 15455185a700Sflorian case dns_rdatatype_aaaa: 15465185a700Sflorian pass_needed = 3; 15475185a700Sflorian break; 15485185a700Sflorian case dns_rdatatype_rrsig: 15495185a700Sflorian case dns_rdatatype_dnskey: 15505185a700Sflorian pass_needed = 2; 15515185a700Sflorian break; 15525185a700Sflorian default: 15535185a700Sflorian pass_needed = 1; 15545185a700Sflorian } 15555185a700Sflorian 15565185a700Sflorian if (pass_needed >= pass) 15571fb015a8Sflorian return (0); 15585185a700Sflorian 15591fb015a8Sflorian return (1); 15605185a700Sflorian } 15615185a700Sflorian 15625185a700Sflorian static isc_result_t 15635185a700Sflorian renderset(dns_rdataset_t *rdataset, dns_name_t *owner_name, 15645185a700Sflorian dns_compress_t *cctx, isc_buffer_t *target, 1565ad5cf538Sjung unsigned int reserved, unsigned int *countp) 15665185a700Sflorian { 15675185a700Sflorian isc_result_t result; 15685185a700Sflorian 15695185a700Sflorian /* 15705185a700Sflorian * Shrink the space in the buffer by the reserved amount. 15715185a700Sflorian */ 15725185a700Sflorian if (target->length - target->used < reserved) 15735185a700Sflorian return (ISC_R_NOSPACE); 15745185a700Sflorian 15755185a700Sflorian target->length -= reserved; 15765185a700Sflorian result = dns_rdataset_towire(rdataset, owner_name, 1577ad5cf538Sjung cctx, target, countp); 15785185a700Sflorian target->length += reserved; 15795185a700Sflorian 15805185a700Sflorian return (result); 15815185a700Sflorian } 15825185a700Sflorian 15835185a700Sflorian static void 15845185a700Sflorian maybe_clear_ad(dns_message_t *msg, dns_section_t sectionid) { 15855185a700Sflorian if (msg->counts[sectionid] == 0 && 15865185a700Sflorian (sectionid == DNS_SECTION_ANSWER || 15875185a700Sflorian (sectionid == DNS_SECTION_AUTHORITY && 15885185a700Sflorian msg->counts[DNS_SECTION_ANSWER] == 0))) 15895185a700Sflorian msg->flags &= ~DNS_MESSAGEFLAG_AD; 15905185a700Sflorian } 15915185a700Sflorian 15925185a700Sflorian isc_result_t 15937d5fc286Sflorian dns_message_rendersection(dns_message_t *msg, dns_section_t sectionid) 15945185a700Sflorian { 15955185a700Sflorian dns_namelist_t *section; 15965185a700Sflorian dns_name_t *name, *next_name; 15975185a700Sflorian dns_rdataset_t *rdataset, *next_rdataset; 15985185a700Sflorian unsigned int count, total; 15995185a700Sflorian isc_result_t result; 16005185a700Sflorian isc_buffer_t st; /* for rollbacks */ 16015185a700Sflorian int pass; 16025185a700Sflorian 16035185a700Sflorian REQUIRE(msg->buffer != NULL); 16045185a700Sflorian REQUIRE(VALID_NAMED_SECTION(sectionid)); 16055185a700Sflorian 16065185a700Sflorian section = &msg->sections[sectionid]; 16075185a700Sflorian 16087d5fc286Sflorian if (sectionid == DNS_SECTION_ADDITIONAL) 16095185a700Sflorian pass = 3; 16107d5fc286Sflorian else 16115185a700Sflorian pass = 1; 16125185a700Sflorian 16135185a700Sflorian /* 16145185a700Sflorian * Shrink the space in the buffer by the reserved amount. 16155185a700Sflorian */ 16165185a700Sflorian if (msg->buffer->length - msg->buffer->used < msg->reserved) 16175185a700Sflorian return (ISC_R_NOSPACE); 16185185a700Sflorian msg->buffer->length -= msg->reserved; 16195185a700Sflorian 16205185a700Sflorian total = 0; 16215185a700Sflorian 16225185a700Sflorian do { 16235185a700Sflorian name = ISC_LIST_HEAD(*section); 16245185a700Sflorian if (name == NULL) { 16255185a700Sflorian msg->buffer->length += msg->reserved; 16265185a700Sflorian msg->counts[sectionid] += total; 16275185a700Sflorian return (ISC_R_SUCCESS); 16285185a700Sflorian } 16295185a700Sflorian 16305185a700Sflorian while (name != NULL) { 16315185a700Sflorian next_name = ISC_LIST_NEXT(name, link); 16325185a700Sflorian 16335185a700Sflorian rdataset = ISC_LIST_HEAD(name->list); 16345185a700Sflorian while (rdataset != NULL) { 16355185a700Sflorian next_rdataset = ISC_LIST_NEXT(rdataset, link); 16365185a700Sflorian 16375185a700Sflorian if ((rdataset->attributes & 16385185a700Sflorian DNS_RDATASETATTR_RENDERED) != 0) 16395185a700Sflorian goto next; 16405185a700Sflorian 16417d5fc286Sflorian if ((sectionid == DNS_SECTION_ADDITIONAL) 16427d5fc286Sflorian && wrong_priority(rdataset, pass)) 16435185a700Sflorian goto next; 16445185a700Sflorian 16455185a700Sflorian st = *(msg->buffer); 16465185a700Sflorian 16475185a700Sflorian count = 0; 16485185a700Sflorian result = dns_rdataset_towiresorted( 16495185a700Sflorian rdataset, 16505185a700Sflorian name, 16515185a700Sflorian msg->cctx, 16525185a700Sflorian msg->buffer, 16535185a700Sflorian &count); 16545185a700Sflorian 16555185a700Sflorian total += count; 16565185a700Sflorian 16575185a700Sflorian /* 16585185a700Sflorian * If out of space, record stats on what we 16595185a700Sflorian * rendered so far, and return that status. 16605185a700Sflorian * 16615185a700Sflorian */ 16625185a700Sflorian if (result != ISC_R_SUCCESS) { 16635185a700Sflorian INSIST(st.used < 65536); 16645185a700Sflorian dns_compress_rollback(msg->cctx, 16655185a700Sflorian (uint16_t)st.used); 16665185a700Sflorian *(msg->buffer) = st; /* rollback */ 16675185a700Sflorian msg->buffer->length += msg->reserved; 16685185a700Sflorian msg->counts[sectionid] += total; 16695185a700Sflorian maybe_clear_ad(msg, sectionid); 16705185a700Sflorian return (result); 16715185a700Sflorian } 16725185a700Sflorian 16735185a700Sflorian /* 16745185a700Sflorian * If we have rendered non-validated data, 16755185a700Sflorian * ensure that the AD bit is not set. 16765185a700Sflorian */ 167712363972Sflorian if ((sectionid == DNS_SECTION_ANSWER || 16785185a700Sflorian sectionid == DNS_SECTION_AUTHORITY)) 16795185a700Sflorian msg->flags &= ~DNS_MESSAGEFLAG_AD; 16805185a700Sflorian 16815185a700Sflorian rdataset->attributes |= 16825185a700Sflorian DNS_RDATASETATTR_RENDERED; 16835185a700Sflorian 16845185a700Sflorian next: 16855185a700Sflorian rdataset = next_rdataset; 16865185a700Sflorian } 16875185a700Sflorian 16885185a700Sflorian name = next_name; 16895185a700Sflorian } 16905185a700Sflorian } while (--pass != 0); 16915185a700Sflorian 16925185a700Sflorian msg->buffer->length += msg->reserved; 16935185a700Sflorian msg->counts[sectionid] += total; 16945185a700Sflorian 16955185a700Sflorian return (ISC_R_SUCCESS); 16965185a700Sflorian } 16975185a700Sflorian 16985185a700Sflorian void 16995185a700Sflorian dns_message_renderheader(dns_message_t *msg, isc_buffer_t *target) { 17005185a700Sflorian uint16_t tmp; 17015185a700Sflorian isc_region_t r; 17025185a700Sflorian 17035185a700Sflorian REQUIRE(target != NULL); 17045185a700Sflorian 17055185a700Sflorian isc_buffer_availableregion(target, &r); 17065185a700Sflorian REQUIRE(r.length >= DNS_MESSAGE_HEADERLEN); 17075185a700Sflorian 17085185a700Sflorian isc_buffer_putuint16(target, msg->id); 17095185a700Sflorian 17105185a700Sflorian tmp = ((msg->opcode << DNS_MESSAGE_OPCODE_SHIFT) 17115185a700Sflorian & DNS_MESSAGE_OPCODE_MASK); 17125185a700Sflorian tmp |= (msg->rcode & DNS_MESSAGE_RCODE_MASK); 17135185a700Sflorian tmp |= (msg->flags & DNS_MESSAGE_FLAG_MASK); 17145185a700Sflorian 17155185a700Sflorian INSIST(msg->counts[DNS_SECTION_QUESTION] < 65536 && 17165185a700Sflorian msg->counts[DNS_SECTION_ANSWER] < 65536 && 17175185a700Sflorian msg->counts[DNS_SECTION_AUTHORITY] < 65536 && 17185185a700Sflorian msg->counts[DNS_SECTION_ADDITIONAL] < 65536); 17195185a700Sflorian 17205185a700Sflorian isc_buffer_putuint16(target, tmp); 17215185a700Sflorian isc_buffer_putuint16(target, 17225185a700Sflorian (uint16_t)msg->counts[DNS_SECTION_QUESTION]); 17235185a700Sflorian isc_buffer_putuint16(target, 17245185a700Sflorian (uint16_t)msg->counts[DNS_SECTION_ANSWER]); 17255185a700Sflorian isc_buffer_putuint16(target, 17265185a700Sflorian (uint16_t)msg->counts[DNS_SECTION_AUTHORITY]); 17275185a700Sflorian isc_buffer_putuint16(target, 17285185a700Sflorian (uint16_t)msg->counts[DNS_SECTION_ADDITIONAL]); 17295185a700Sflorian } 17305185a700Sflorian 17315185a700Sflorian isc_result_t 17325185a700Sflorian dns_message_renderend(dns_message_t *msg) { 17335185a700Sflorian isc_buffer_t tmpbuf; 17345185a700Sflorian isc_region_t r; 17355185a700Sflorian int result; 17365185a700Sflorian unsigned int count; 17375185a700Sflorian 17385185a700Sflorian REQUIRE(msg->buffer != NULL); 17395185a700Sflorian 17405185a700Sflorian if ((msg->rcode & ~DNS_MESSAGE_RCODE_MASK) != 0 && msg->opt == NULL) { 17415185a700Sflorian /* 17425185a700Sflorian * We have an extended rcode but are not using EDNS. 17435185a700Sflorian */ 17445185a700Sflorian return (DNS_R_FORMERR); 17455185a700Sflorian } 17465185a700Sflorian 17475185a700Sflorian /* 17485185a700Sflorian * If we're adding a OPT, TSIG or SIG(0) to a truncated message, 17495185a700Sflorian * clear all rdatasets from the message except for the question 17505185a700Sflorian * before adding the OPT, TSIG or SIG(0). If the question doesn't 17515185a700Sflorian * fit, don't include it. 17525185a700Sflorian */ 17535185a700Sflorian if ((msg->tsigkey != NULL || msg->opt) && 17545185a700Sflorian (msg->flags & DNS_MESSAGEFLAG_TC) != 0) 17555185a700Sflorian { 17565185a700Sflorian isc_buffer_t *buf; 17575185a700Sflorian 17585185a700Sflorian msgresetnames(msg, DNS_SECTION_ANSWER); 17595185a700Sflorian buf = msg->buffer; 17605185a700Sflorian dns_message_renderreset(msg); 17615185a700Sflorian msg->buffer = buf; 17625185a700Sflorian isc_buffer_clear(msg->buffer); 17635185a700Sflorian isc_buffer_add(msg->buffer, DNS_MESSAGE_HEADERLEN); 17645185a700Sflorian dns_compress_rollback(msg->cctx, 0); 17657d5fc286Sflorian result = dns_message_rendersection(msg, DNS_SECTION_QUESTION); 17665185a700Sflorian if (result != ISC_R_SUCCESS && result != ISC_R_NOSPACE) 17675185a700Sflorian return (result); 17685185a700Sflorian } 17695185a700Sflorian 17705185a700Sflorian /* 17715185a700Sflorian * If we've got an OPT record, render it. 17725185a700Sflorian */ 17735185a700Sflorian if (msg->opt != NULL) { 17745185a700Sflorian dns_message_renderrelease(msg, msg->opt_reserved); 17755185a700Sflorian msg->opt_reserved = 0; 17765185a700Sflorian /* 17775185a700Sflorian * Set the extended rcode. 17785185a700Sflorian */ 17795185a700Sflorian msg->opt->ttl &= ~DNS_MESSAGE_EDNSRCODE_MASK; 17805185a700Sflorian msg->opt->ttl |= ((msg->rcode << 20) & 17815185a700Sflorian DNS_MESSAGE_EDNSRCODE_MASK); 17825185a700Sflorian /* 17835185a700Sflorian * Render. 17845185a700Sflorian */ 17855185a700Sflorian count = 0; 17865185a700Sflorian result = renderset(msg->opt, dns_rootname, msg->cctx, 1787ad5cf538Sjung msg->buffer, msg->reserved, &count); 17885185a700Sflorian msg->counts[DNS_SECTION_ADDITIONAL] += count; 17895185a700Sflorian if (result != ISC_R_SUCCESS) 17905185a700Sflorian return (result); 17915185a700Sflorian } 17925185a700Sflorian 17935185a700Sflorian /* 17945185a700Sflorian * If we're adding a TSIG record, generate and render it. 17955185a700Sflorian */ 17965185a700Sflorian if (msg->tsigkey != NULL) { 17975185a700Sflorian dns_message_renderrelease(msg, msg->sig_reserved); 17985185a700Sflorian msg->sig_reserved = 0; 17995185a700Sflorian result = dns_tsig_sign(msg); 18005185a700Sflorian if (result != ISC_R_SUCCESS) 18015185a700Sflorian return (result); 18025185a700Sflorian count = 0; 18035185a700Sflorian result = renderset(msg->tsig, msg->tsigname, msg->cctx, 1804ad5cf538Sjung msg->buffer, msg->reserved, &count); 18055185a700Sflorian msg->counts[DNS_SECTION_ADDITIONAL] += count; 18065185a700Sflorian if (result != ISC_R_SUCCESS) 18075185a700Sflorian return (result); 18085185a700Sflorian } 18095185a700Sflorian 18105185a700Sflorian isc_buffer_usedregion(msg->buffer, &r); 18115185a700Sflorian isc_buffer_init(&tmpbuf, r.base, r.length); 18125185a700Sflorian 18135185a700Sflorian dns_message_renderheader(msg, &tmpbuf); 18145185a700Sflorian 18155185a700Sflorian msg->buffer = NULL; /* forget about this buffer only on success XXX */ 18165185a700Sflorian 18175185a700Sflorian return (ISC_R_SUCCESS); 18185185a700Sflorian } 18195185a700Sflorian 18205185a700Sflorian void 18215185a700Sflorian dns_message_renderreset(dns_message_t *msg) { 18225185a700Sflorian unsigned int i; 18235185a700Sflorian dns_name_t *name; 18245185a700Sflorian dns_rdataset_t *rds; 18255185a700Sflorian 18265185a700Sflorian /* 18275185a700Sflorian * Reset the message so that it may be rendered again. 18285185a700Sflorian */ 18295185a700Sflorian 18305185a700Sflorian REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER); 18315185a700Sflorian 18325185a700Sflorian msg->buffer = NULL; 18335185a700Sflorian 18345185a700Sflorian for (i = 0; i < DNS_SECTION_MAX; i++) { 18355185a700Sflorian msg->cursors[i] = NULL; 18365185a700Sflorian msg->counts[i] = 0; 18375185a700Sflorian for (name = ISC_LIST_HEAD(msg->sections[i]); 18385185a700Sflorian name != NULL; 18395185a700Sflorian name = ISC_LIST_NEXT(name, link)) { 18405185a700Sflorian for (rds = ISC_LIST_HEAD(name->list); 18415185a700Sflorian rds != NULL; 18425185a700Sflorian rds = ISC_LIST_NEXT(rds, link)) { 18435185a700Sflorian rds->attributes &= ~DNS_RDATASETATTR_RENDERED; 18445185a700Sflorian } 18455185a700Sflorian } 18465185a700Sflorian } 18475185a700Sflorian if (msg->tsigname != NULL) 18485185a700Sflorian dns_message_puttempname(msg, &msg->tsigname); 18495185a700Sflorian if (msg->tsig != NULL) { 18505185a700Sflorian dns_rdataset_disassociate(msg->tsig); 18515185a700Sflorian dns_message_puttemprdataset(msg, &msg->tsig); 18525185a700Sflorian } 18535185a700Sflorian if (msg->sig0 != NULL) { 18545185a700Sflorian dns_rdataset_disassociate(msg->sig0); 18555185a700Sflorian dns_message_puttemprdataset(msg, &msg->sig0); 18565185a700Sflorian } 18575185a700Sflorian } 18585185a700Sflorian 18595185a700Sflorian isc_result_t 18605185a700Sflorian dns_message_firstname(dns_message_t *msg, dns_section_t section) { 18615185a700Sflorian REQUIRE(VALID_NAMED_SECTION(section)); 18625185a700Sflorian 18635185a700Sflorian msg->cursors[section] = ISC_LIST_HEAD(msg->sections[section]); 18645185a700Sflorian 18655185a700Sflorian if (msg->cursors[section] == NULL) 18665185a700Sflorian return (ISC_R_NOMORE); 18675185a700Sflorian 18685185a700Sflorian return (ISC_R_SUCCESS); 18695185a700Sflorian } 18705185a700Sflorian 18715185a700Sflorian isc_result_t 18725185a700Sflorian dns_message_nextname(dns_message_t *msg, dns_section_t section) { 18735185a700Sflorian REQUIRE(VALID_NAMED_SECTION(section)); 18745185a700Sflorian REQUIRE(msg->cursors[section] != NULL); 18755185a700Sflorian 18765185a700Sflorian msg->cursors[section] = ISC_LIST_NEXT(msg->cursors[section], link); 18775185a700Sflorian 18785185a700Sflorian if (msg->cursors[section] == NULL) 18795185a700Sflorian return (ISC_R_NOMORE); 18805185a700Sflorian 18815185a700Sflorian return (ISC_R_SUCCESS); 18825185a700Sflorian } 18835185a700Sflorian 18845185a700Sflorian void 18855185a700Sflorian dns_message_currentname(dns_message_t *msg, dns_section_t section, 18865185a700Sflorian dns_name_t **name) 18875185a700Sflorian { 18885185a700Sflorian REQUIRE(VALID_NAMED_SECTION(section)); 18895185a700Sflorian REQUIRE(name != NULL && *name == NULL); 18905185a700Sflorian REQUIRE(msg->cursors[section] != NULL); 18915185a700Sflorian 18925185a700Sflorian *name = msg->cursors[section]; 18935185a700Sflorian } 18945185a700Sflorian 18955185a700Sflorian isc_result_t 18965185a700Sflorian dns_message_findname(dns_message_t *msg, dns_section_t section, 18975185a700Sflorian dns_name_t *target, dns_rdatatype_t type, 18985185a700Sflorian dns_rdatatype_t covers, dns_name_t **name, 18995185a700Sflorian dns_rdataset_t **rdataset) 19005185a700Sflorian { 19015185a700Sflorian dns_name_t *foundname; 19025185a700Sflorian isc_result_t result; 19035185a700Sflorian 19045185a700Sflorian /* 19055185a700Sflorian * XXX These requirements are probably too intensive, especially 19065185a700Sflorian * where things can be NULL, but as they are they ensure that if 19075185a700Sflorian * something is NON-NULL, indicating that the caller expects it 19085185a700Sflorian * to be filled in, that we can in fact fill it in. 19095185a700Sflorian */ 19105185a700Sflorian REQUIRE(msg != NULL); 19115185a700Sflorian REQUIRE(VALID_SECTION(section)); 19125185a700Sflorian REQUIRE(target != NULL); 19135185a700Sflorian REQUIRE(name == NULL || *name == NULL); 19145185a700Sflorian 19155185a700Sflorian if (type == dns_rdatatype_any) { 19165185a700Sflorian REQUIRE(rdataset == NULL); 19175185a700Sflorian } else { 19185185a700Sflorian REQUIRE(rdataset == NULL || *rdataset == NULL); 19195185a700Sflorian } 19205185a700Sflorian 19215185a700Sflorian result = findname(&foundname, target, 19225185a700Sflorian &msg->sections[section]); 19235185a700Sflorian 19245185a700Sflorian if (result == ISC_R_NOTFOUND) 19255185a700Sflorian return (DNS_R_NXDOMAIN); 19265185a700Sflorian else if (result != ISC_R_SUCCESS) 19275185a700Sflorian return (result); 19285185a700Sflorian 19295185a700Sflorian if (name != NULL) 19305185a700Sflorian *name = foundname; 19315185a700Sflorian 19325185a700Sflorian /* 19335185a700Sflorian * And now look for the type. 19345185a700Sflorian */ 19355185a700Sflorian if (type == dns_rdatatype_any) 19365185a700Sflorian return (ISC_R_SUCCESS); 19375185a700Sflorian 19385185a700Sflorian result = dns_message_findtype(foundname, type, covers, rdataset); 19395185a700Sflorian if (result == ISC_R_NOTFOUND) 19405185a700Sflorian return (DNS_R_NXRRSET); 19415185a700Sflorian 19425185a700Sflorian return (result); 19435185a700Sflorian } 19445185a700Sflorian 19455185a700Sflorian void 19465185a700Sflorian dns_message_addname(dns_message_t *msg, dns_name_t *name, 19475185a700Sflorian dns_section_t section) 19485185a700Sflorian { 19495185a700Sflorian REQUIRE(msg != NULL); 19505185a700Sflorian REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER); 19515185a700Sflorian REQUIRE(name != NULL); 19525185a700Sflorian REQUIRE(VALID_NAMED_SECTION(section)); 19535185a700Sflorian 19545185a700Sflorian ISC_LIST_APPEND(msg->sections[section], name, link); 19555185a700Sflorian } 19565185a700Sflorian 19575185a700Sflorian isc_result_t 19585185a700Sflorian dns_message_gettempname(dns_message_t *msg, dns_name_t **item) { 19595185a700Sflorian REQUIRE(item != NULL && *item == NULL); 19605185a700Sflorian 1961ad5cf538Sjung UNUSED(msg); 1962ad5cf538Sjung 19635185a700Sflorian *item = malloc(sizeof(dns_name_t)); 19645185a700Sflorian if (*item == NULL) 19655185a700Sflorian return (ISC_R_NOMEMORY); 19665185a700Sflorian dns_name_init(*item, NULL); 19675185a700Sflorian 19685185a700Sflorian return (ISC_R_SUCCESS); 19695185a700Sflorian } 19705185a700Sflorian 19715185a700Sflorian isc_result_t 19725185a700Sflorian dns_message_gettemprdata(dns_message_t *msg, dns_rdata_t **item) { 19735185a700Sflorian REQUIRE(item != NULL && *item == NULL); 19745185a700Sflorian 19755185a700Sflorian *item = newrdata(msg); 19765185a700Sflorian if (*item == NULL) 19775185a700Sflorian return (ISC_R_NOMEMORY); 19785185a700Sflorian 19795185a700Sflorian return (ISC_R_SUCCESS); 19805185a700Sflorian } 19815185a700Sflorian 19825185a700Sflorian isc_result_t 19835185a700Sflorian dns_message_gettemprdataset(dns_message_t *msg, dns_rdataset_t **item) { 19845185a700Sflorian REQUIRE(item != NULL && *item == NULL); 19855185a700Sflorian 1986ad5cf538Sjung UNUSED(msg); 1987ad5cf538Sjung 19885185a700Sflorian *item = malloc(sizeof(dns_rdataset_t)); 19895185a700Sflorian if (*item == NULL) 19905185a700Sflorian return (ISC_R_NOMEMORY); 19915185a700Sflorian 19925185a700Sflorian dns_rdataset_init(*item); 19935185a700Sflorian 19945185a700Sflorian return (ISC_R_SUCCESS); 19955185a700Sflorian } 19965185a700Sflorian 19975185a700Sflorian isc_result_t 19985185a700Sflorian dns_message_gettemprdatalist(dns_message_t *msg, dns_rdatalist_t **item) { 19995185a700Sflorian REQUIRE(item != NULL && *item == NULL); 20005185a700Sflorian 20015185a700Sflorian *item = newrdatalist(msg); 20025185a700Sflorian if (*item == NULL) 20035185a700Sflorian return (ISC_R_NOMEMORY); 20045185a700Sflorian 20055185a700Sflorian return (ISC_R_SUCCESS); 20065185a700Sflorian } 20075185a700Sflorian 20085185a700Sflorian void 20095185a700Sflorian dns_message_puttempname(dns_message_t *msg, dns_name_t **item) { 20105185a700Sflorian REQUIRE(item != NULL && *item != NULL); 20115185a700Sflorian 2012ad5cf538Sjung UNUSED(msg); 2013ad5cf538Sjung 20145185a700Sflorian if (dns_name_dynamic(*item)) 20155185a700Sflorian dns_name_free(*item); 20165185a700Sflorian free(*item); 20175185a700Sflorian *item = NULL; 20185185a700Sflorian } 20195185a700Sflorian 20205185a700Sflorian void 20215185a700Sflorian dns_message_puttemprdata(dns_message_t *msg, dns_rdata_t **item) { 20225185a700Sflorian REQUIRE(item != NULL && *item != NULL); 20235185a700Sflorian 20245185a700Sflorian releaserdata(msg, *item); 20255185a700Sflorian *item = NULL; 20265185a700Sflorian } 20275185a700Sflorian 20285185a700Sflorian void 20295185a700Sflorian dns_message_puttemprdataset(dns_message_t *msg, dns_rdataset_t **item) { 20305185a700Sflorian REQUIRE(item != NULL && *item != NULL); 20315185a700Sflorian REQUIRE(!dns_rdataset_isassociated(*item)); 2032ad5cf538Sjung 2033ad5cf538Sjung UNUSED(msg); 2034ad5cf538Sjung 20355185a700Sflorian free(*item); 20365185a700Sflorian *item = NULL; 20375185a700Sflorian } 20385185a700Sflorian 20395185a700Sflorian void 20405185a700Sflorian dns_message_puttemprdatalist(dns_message_t *msg, dns_rdatalist_t **item) { 20415185a700Sflorian REQUIRE(item != NULL && *item != NULL); 20425185a700Sflorian 20435185a700Sflorian releaserdatalist(msg, *item); 20445185a700Sflorian *item = NULL; 20455185a700Sflorian } 20465185a700Sflorian 20475185a700Sflorian isc_result_t 20485185a700Sflorian dns_message_peekheader(isc_buffer_t *source, dns_messageid_t *idp, 20495185a700Sflorian unsigned int *flagsp) 20505185a700Sflorian { 20515185a700Sflorian isc_region_t r; 20525185a700Sflorian isc_buffer_t buffer; 20535185a700Sflorian dns_messageid_t id; 20545185a700Sflorian unsigned int flags; 20555185a700Sflorian 20565185a700Sflorian REQUIRE(source != NULL); 20575185a700Sflorian 20585185a700Sflorian buffer = *source; 20595185a700Sflorian 20605185a700Sflorian isc_buffer_remainingregion(&buffer, &r); 20615185a700Sflorian if (r.length < DNS_MESSAGE_HEADERLEN) 20625185a700Sflorian return (ISC_R_UNEXPECTEDEND); 20635185a700Sflorian 20645185a700Sflorian id = isc_buffer_getuint16(&buffer); 20655185a700Sflorian flags = isc_buffer_getuint16(&buffer); 20665185a700Sflorian flags &= DNS_MESSAGE_FLAG_MASK; 20675185a700Sflorian 20685185a700Sflorian if (flagsp != NULL) 20695185a700Sflorian *flagsp = flags; 20705185a700Sflorian if (idp != NULL) 20715185a700Sflorian *idp = id; 20725185a700Sflorian 20735185a700Sflorian return (ISC_R_SUCCESS); 20745185a700Sflorian } 20755185a700Sflorian 20765185a700Sflorian dns_rdataset_t * 20775185a700Sflorian dns_message_getopt(dns_message_t *msg) { 20785185a700Sflorian 20795185a700Sflorian /* 20805185a700Sflorian * Get the OPT record for 'msg'. 20815185a700Sflorian */ 20825185a700Sflorian return (msg->opt); 20835185a700Sflorian } 20845185a700Sflorian 20855185a700Sflorian isc_result_t 20865185a700Sflorian dns_message_setopt(dns_message_t *msg, dns_rdataset_t *opt) { 20875185a700Sflorian isc_result_t result; 20885185a700Sflorian dns_rdata_t rdata = DNS_RDATA_INIT; 20895185a700Sflorian 20905185a700Sflorian /* 20915185a700Sflorian * Set the OPT record for 'msg'. 20925185a700Sflorian */ 20935185a700Sflorian 20945185a700Sflorian /* 20955185a700Sflorian * The space required for an OPT record is: 20965185a700Sflorian * 20975185a700Sflorian * 1 byte for the name 20985185a700Sflorian * 2 bytes for the type 20995185a700Sflorian * 2 bytes for the class 21005185a700Sflorian * 4 bytes for the ttl 21015185a700Sflorian * 2 bytes for the rdata length 21025185a700Sflorian * --------------------------------- 21035185a700Sflorian * 11 bytes 21045185a700Sflorian * 21055185a700Sflorian * plus the length of the rdata. 21065185a700Sflorian */ 21075185a700Sflorian 21085185a700Sflorian REQUIRE(opt->type == dns_rdatatype_opt); 21095185a700Sflorian REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER); 21105185a700Sflorian REQUIRE(msg->state == DNS_SECTION_ANY); 21115185a700Sflorian 21125185a700Sflorian msgresetopt(msg); 21135185a700Sflorian 21145185a700Sflorian result = dns_rdataset_first(opt); 21155185a700Sflorian if (result != ISC_R_SUCCESS) 21165185a700Sflorian goto cleanup; 21175185a700Sflorian dns_rdataset_current(opt, &rdata); 21185185a700Sflorian msg->opt_reserved = 11 + rdata.length; 21195185a700Sflorian result = dns_message_renderreserve(msg, msg->opt_reserved); 21205185a700Sflorian if (result != ISC_R_SUCCESS) { 21215185a700Sflorian msg->opt_reserved = 0; 21225185a700Sflorian goto cleanup; 21235185a700Sflorian } 21245185a700Sflorian 21255185a700Sflorian msg->opt = opt; 21265185a700Sflorian 21275185a700Sflorian return (ISC_R_SUCCESS); 21285185a700Sflorian 21295185a700Sflorian cleanup: 21305185a700Sflorian dns_rdataset_disassociate(opt); 21315185a700Sflorian dns_message_puttemprdataset(msg, &opt); 21325185a700Sflorian return (result); 21335185a700Sflorian } 21345185a700Sflorian 21355185a700Sflorian dns_rdataset_t * 21365185a700Sflorian dns_message_gettsig(dns_message_t *msg, dns_name_t **owner) { 21375185a700Sflorian 21385185a700Sflorian /* 21395185a700Sflorian * Get the TSIG record and owner for 'msg'. 21405185a700Sflorian */ 21415185a700Sflorian 21425185a700Sflorian REQUIRE(owner == NULL || *owner == NULL); 21435185a700Sflorian 21445185a700Sflorian if (owner != NULL) 21455185a700Sflorian *owner = msg->tsigname; 21465185a700Sflorian return (msg->tsig); 21475185a700Sflorian } 21485185a700Sflorian 21495185a700Sflorian isc_result_t 21505185a700Sflorian dns_message_settsigkey(dns_message_t *msg, dns_tsigkey_t *key) { 21515185a700Sflorian isc_result_t result; 21525185a700Sflorian 21535185a700Sflorian /* 21545185a700Sflorian * Set the TSIG key for 'msg' 21555185a700Sflorian */ 21565185a700Sflorian 21575185a700Sflorian REQUIRE(msg->state == DNS_SECTION_ANY); 21585185a700Sflorian 21595185a700Sflorian if (key == NULL && msg->tsigkey != NULL) { 21605185a700Sflorian if (msg->sig_reserved != 0) { 21615185a700Sflorian dns_message_renderrelease(msg, msg->sig_reserved); 21625185a700Sflorian msg->sig_reserved = 0; 21635185a700Sflorian } 21645185a700Sflorian dns_tsigkey_detach(&msg->tsigkey); 21655185a700Sflorian } 21665185a700Sflorian if (key != NULL) { 21675185a700Sflorian REQUIRE(msg->tsigkey == NULL); 21685185a700Sflorian dns_tsigkey_attach(key, &msg->tsigkey); 21695185a700Sflorian if (msg->from_to_wire == DNS_MESSAGE_INTENTRENDER) { 21705185a700Sflorian msg->sig_reserved = spacefortsig(msg->tsigkey, 0); 21715185a700Sflorian result = dns_message_renderreserve(msg, 21725185a700Sflorian msg->sig_reserved); 21735185a700Sflorian if (result != ISC_R_SUCCESS) { 21745185a700Sflorian dns_tsigkey_detach(&msg->tsigkey); 21755185a700Sflorian msg->sig_reserved = 0; 21765185a700Sflorian return (result); 21775185a700Sflorian } 21785185a700Sflorian } 21795185a700Sflorian } 21805185a700Sflorian return (ISC_R_SUCCESS); 21815185a700Sflorian } 21825185a700Sflorian 21835185a700Sflorian dns_tsigkey_t * 21845185a700Sflorian dns_message_gettsigkey(dns_message_t *msg) { 21855185a700Sflorian 21865185a700Sflorian /* 21875185a700Sflorian * Get the TSIG key for 'msg' 21885185a700Sflorian */ 21895185a700Sflorian return (msg->tsigkey); 21905185a700Sflorian } 21915185a700Sflorian 21925185a700Sflorian isc_result_t 21935185a700Sflorian dns_message_setquerytsig(dns_message_t *msg, isc_buffer_t *querytsig) { 21945185a700Sflorian dns_rdata_t *rdata = NULL; 21955185a700Sflorian dns_rdatalist_t *list = NULL; 21965185a700Sflorian dns_rdataset_t *set = NULL; 21975185a700Sflorian isc_buffer_t *buf = NULL; 21985185a700Sflorian isc_region_t r; 21995185a700Sflorian isc_result_t result; 22005185a700Sflorian 22015185a700Sflorian REQUIRE(msg->querytsig == NULL); 22025185a700Sflorian 22035185a700Sflorian if (querytsig == NULL) 22045185a700Sflorian return (ISC_R_SUCCESS); 22055185a700Sflorian 22065185a700Sflorian result = dns_message_gettemprdata(msg, &rdata); 22075185a700Sflorian if (result != ISC_R_SUCCESS) 22085185a700Sflorian goto cleanup; 22095185a700Sflorian 22105185a700Sflorian result = dns_message_gettemprdatalist(msg, &list); 22115185a700Sflorian if (result != ISC_R_SUCCESS) 22125185a700Sflorian goto cleanup; 22135185a700Sflorian result = dns_message_gettemprdataset(msg, &set); 22145185a700Sflorian if (result != ISC_R_SUCCESS) 22155185a700Sflorian goto cleanup; 22165185a700Sflorian 22175185a700Sflorian isc_buffer_usedregion(querytsig, &r); 22185185a700Sflorian result = isc_buffer_allocate(&buf, r.length); 22195185a700Sflorian if (result != ISC_R_SUCCESS) 22205185a700Sflorian goto cleanup; 22215185a700Sflorian isc_buffer_putmem(buf, r.base, r.length); 22225185a700Sflorian isc_buffer_usedregion(buf, &r); 22235185a700Sflorian dns_rdata_init(rdata); 22245185a700Sflorian dns_rdata_fromregion(rdata, dns_rdataclass_any, dns_rdatatype_tsig, &r); 22255185a700Sflorian dns_message_takebuffer(msg, &buf); 22265185a700Sflorian ISC_LIST_APPEND(list->rdata, rdata, link); 22275185a700Sflorian result = dns_rdatalist_tordataset(list, set); 22285185a700Sflorian if (result != ISC_R_SUCCESS) 22295185a700Sflorian goto cleanup; 22305185a700Sflorian 22315185a700Sflorian msg->querytsig = set; 22325185a700Sflorian 22335185a700Sflorian return (result); 22345185a700Sflorian 22355185a700Sflorian cleanup: 22365185a700Sflorian if (rdata != NULL) 22375185a700Sflorian dns_message_puttemprdata(msg, &rdata); 22385185a700Sflorian if (list != NULL) 22395185a700Sflorian dns_message_puttemprdatalist(msg, &list); 22405185a700Sflorian if (set != NULL) 22415185a700Sflorian dns_message_puttemprdataset(msg, &set); 22425185a700Sflorian return (ISC_R_NOMEMORY); 22435185a700Sflorian } 22445185a700Sflorian 22455185a700Sflorian isc_result_t 22465185a700Sflorian dns_message_getquerytsig(dns_message_t *msg, isc_buffer_t **querytsig) { 22475185a700Sflorian isc_result_t result; 22485185a700Sflorian dns_rdata_t rdata = DNS_RDATA_INIT; 22495185a700Sflorian isc_region_t r; 22505185a700Sflorian 22515185a700Sflorian REQUIRE(querytsig != NULL && *querytsig == NULL); 22525185a700Sflorian 22535185a700Sflorian if (msg->tsig == NULL) 22545185a700Sflorian return (ISC_R_SUCCESS); 22555185a700Sflorian 22565185a700Sflorian result = dns_rdataset_first(msg->tsig); 22575185a700Sflorian if (result != ISC_R_SUCCESS) 22585185a700Sflorian return (result); 22595185a700Sflorian dns_rdataset_current(msg->tsig, &rdata); 22605185a700Sflorian dns_rdata_toregion(&rdata, &r); 22615185a700Sflorian 22625185a700Sflorian result = isc_buffer_allocate(querytsig, r.length); 22635185a700Sflorian if (result != ISC_R_SUCCESS) 22645185a700Sflorian return (result); 22655185a700Sflorian isc_buffer_putmem(*querytsig, r.base, r.length); 22665185a700Sflorian return (ISC_R_SUCCESS); 22675185a700Sflorian } 22685185a700Sflorian 22695185a700Sflorian dns_rdataset_t * 22705185a700Sflorian dns_message_getsig0(dns_message_t *msg, dns_name_t **owner) { 22715185a700Sflorian 22725185a700Sflorian /* 22735185a700Sflorian * Get the SIG(0) record for 'msg'. 22745185a700Sflorian */ 22755185a700Sflorian 22765185a700Sflorian REQUIRE(owner == NULL || *owner == NULL); 22775185a700Sflorian 22785185a700Sflorian if (msg->sig0 != NULL && owner != NULL) { 22795185a700Sflorian /* If dns_message_getsig0 is called on a rendered message 22805185a700Sflorian * after the SIG(0) has been applied, we need to return the 22815185a700Sflorian * root name, not NULL. 22825185a700Sflorian */ 22835185a700Sflorian if (msg->sig0name == NULL) 22845185a700Sflorian *owner = dns_rootname; 22855185a700Sflorian else 22865185a700Sflorian *owner = msg->sig0name; 22875185a700Sflorian } 22885185a700Sflorian return (msg->sig0); 22895185a700Sflorian } 22905185a700Sflorian 22915185a700Sflorian void 22925185a700Sflorian dns_message_takebuffer(dns_message_t *msg, isc_buffer_t **buffer) { 22935185a700Sflorian REQUIRE(buffer != NULL); 22945185a700Sflorian 22955185a700Sflorian ISC_LIST_APPEND(msg->cleanup, *buffer, link); 22965185a700Sflorian *buffer = NULL; 22975185a700Sflorian } 22985185a700Sflorian 22995185a700Sflorian isc_result_t 23005185a700Sflorian dns_message_sectiontotext(dns_message_t *msg, dns_section_t section, 23015185a700Sflorian const dns_master_style_t *style, 23025185a700Sflorian dns_messagetextflag_t flags, 23035185a700Sflorian isc_buffer_t *target) { 23045185a700Sflorian dns_name_t *name, empty_name; 23055185a700Sflorian dns_rdataset_t *rdataset; 23065185a700Sflorian isc_result_t result; 23071fb015a8Sflorian int seensoa = 0; 23085185a700Sflorian 23095185a700Sflorian REQUIRE(target != NULL); 23105185a700Sflorian REQUIRE(VALID_SECTION(section)); 23115185a700Sflorian 23125185a700Sflorian if (ISC_LIST_EMPTY(msg->sections[section])) 23135185a700Sflorian return (ISC_R_SUCCESS); 23145185a700Sflorian 23155185a700Sflorian if ((flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0) { 23165185a700Sflorian ADD_STRING(target, ";; "); 23175185a700Sflorian if (msg->opcode != dns_opcode_update) { 23185185a700Sflorian ADD_STRING(target, sectiontext[section]); 23195185a700Sflorian } else { 23205185a700Sflorian ADD_STRING(target, updsectiontext[section]); 23215185a700Sflorian } 23225185a700Sflorian ADD_STRING(target, " SECTION:\n"); 23235185a700Sflorian } 23245185a700Sflorian 23255185a700Sflorian dns_name_init(&empty_name, NULL); 23265185a700Sflorian result = dns_message_firstname(msg, section); 23275185a700Sflorian if (result != ISC_R_SUCCESS) { 23285185a700Sflorian return (result); 23295185a700Sflorian } 23305185a700Sflorian do { 23315185a700Sflorian name = NULL; 23325185a700Sflorian dns_message_currentname(msg, section, &name); 23335185a700Sflorian for (rdataset = ISC_LIST_HEAD(name->list); 23345185a700Sflorian rdataset != NULL; 23355185a700Sflorian rdataset = ISC_LIST_NEXT(rdataset, link)) { 23365185a700Sflorian if (section == DNS_SECTION_ANSWER && 23375185a700Sflorian rdataset->type == dns_rdatatype_soa) { 23385185a700Sflorian if ((flags & DNS_MESSAGETEXTFLAG_OMITSOA) != 0) 23395185a700Sflorian continue; 23405185a700Sflorian if (seensoa && 23415185a700Sflorian (flags & DNS_MESSAGETEXTFLAG_ONESOA) != 0) 23425185a700Sflorian continue; 23431fb015a8Sflorian seensoa = 1; 23445185a700Sflorian } 23455185a700Sflorian if (section == DNS_SECTION_QUESTION) { 23465185a700Sflorian ADD_STRING(target, ";"); 23475185a700Sflorian result = dns_master_questiontotext(name, 23485185a700Sflorian rdataset, 23495185a700Sflorian style, 23505185a700Sflorian target); 23515185a700Sflorian } else { 23525185a700Sflorian result = dns_master_rdatasettotext(name, 23535185a700Sflorian rdataset, 23545185a700Sflorian style, 23555185a700Sflorian target); 23565185a700Sflorian } 23575185a700Sflorian if (result != ISC_R_SUCCESS) 23585185a700Sflorian return (result); 23595185a700Sflorian } 23605185a700Sflorian result = dns_message_nextname(msg, section); 23615185a700Sflorian } while (result == ISC_R_SUCCESS); 23625185a700Sflorian if ((flags & DNS_MESSAGETEXTFLAG_NOHEADERS) == 0 && 23635185a700Sflorian (flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0) 23645185a700Sflorian ADD_STRING(target, "\n"); 23655185a700Sflorian if (result == ISC_R_NOMORE) 23665185a700Sflorian result = ISC_R_SUCCESS; 23675185a700Sflorian return (result); 23685185a700Sflorian } 23695185a700Sflorian 23705185a700Sflorian static isc_result_t 23715185a700Sflorian render_ecs(isc_buffer_t *ecsbuf, isc_buffer_t *target) { 23725185a700Sflorian int i; 23735185a700Sflorian char addr[16], addr_text[64]; 23745185a700Sflorian uint16_t family; 23755185a700Sflorian uint8_t addrlen, addrbytes, scopelen; 23765185a700Sflorian 23775185a700Sflorian /* 23785185a700Sflorian * Note: This routine needs to handle malformed ECS options. 23795185a700Sflorian */ 23805185a700Sflorian 23815185a700Sflorian if (isc_buffer_remaininglength(ecsbuf) < 4) 23825185a700Sflorian return (DNS_R_OPTERR); 23835185a700Sflorian family = isc_buffer_getuint16(ecsbuf); 23845185a700Sflorian addrlen = isc_buffer_getuint8(ecsbuf); 23855185a700Sflorian scopelen = isc_buffer_getuint8(ecsbuf); 23865185a700Sflorian 23875185a700Sflorian addrbytes = (addrlen + 7) / 8; 23885185a700Sflorian if (isc_buffer_remaininglength(ecsbuf) < addrbytes) 23895185a700Sflorian return (DNS_R_OPTERR); 23905185a700Sflorian 23915185a700Sflorian if (addrbytes > sizeof(addr)) 23925185a700Sflorian return (DNS_R_OPTERR); 23935185a700Sflorian 23945185a700Sflorian memset(addr, 0, sizeof(addr)); 23955185a700Sflorian for (i = 0; i < addrbytes; i ++) 23965185a700Sflorian addr[i] = isc_buffer_getuint8(ecsbuf); 23975185a700Sflorian 23985185a700Sflorian switch (family) { 23995185a700Sflorian case 0: 24005185a700Sflorian if (addrlen != 0U || scopelen != 0U) 24015185a700Sflorian return (DNS_R_OPTERR); 24025185a700Sflorian strlcpy(addr_text, "0", sizeof(addr_text)); 24035185a700Sflorian break; 24045185a700Sflorian case 1: 24055185a700Sflorian if (addrlen > 32 || scopelen > 32) 24065185a700Sflorian return (DNS_R_OPTERR); 24075185a700Sflorian inet_ntop(AF_INET, addr, addr_text, sizeof(addr_text)); 24085185a700Sflorian break; 24095185a700Sflorian case 2: 24105185a700Sflorian if (addrlen > 128 || scopelen > 128) 24115185a700Sflorian return (DNS_R_OPTERR); 24125185a700Sflorian inet_ntop(AF_INET6, addr, addr_text, sizeof(addr_text)); 24135185a700Sflorian break; 24145185a700Sflorian default: 24155185a700Sflorian return (DNS_R_OPTERR); 24165185a700Sflorian } 24175185a700Sflorian 24185185a700Sflorian ADD_STRING(target, ": "); 24195185a700Sflorian ADD_STRING(target, addr_text); 24205185a700Sflorian snprintf(addr_text, sizeof(addr_text), "/%d/%d", addrlen, scopelen); 24215185a700Sflorian ADD_STRING(target, addr_text); 24225185a700Sflorian return (ISC_R_SUCCESS); 24235185a700Sflorian } 24245185a700Sflorian 2425fabbbca0Sflorian static const char * 2426fabbbca0Sflorian ede_info_code2str(uint16_t info_code) 2427fabbbca0Sflorian { 2428fabbbca0Sflorian if (info_code > 49151) 2429fabbbca0Sflorian return "Private Use"; 2430fabbbca0Sflorian 2431fabbbca0Sflorian switch (info_code) { 2432fabbbca0Sflorian case 0: 2433fabbbca0Sflorian return "Other Error"; 2434fabbbca0Sflorian case 1: 2435fabbbca0Sflorian return "Unsupported DNSKEY Algorithm"; 2436fabbbca0Sflorian case 2: 2437fabbbca0Sflorian return "Unsupported DS Digest Type"; 2438fabbbca0Sflorian case 3: 2439fabbbca0Sflorian return "Stale Answer"; 2440fabbbca0Sflorian case 4: 2441fabbbca0Sflorian return "Forged Answer"; 2442fabbbca0Sflorian case 5: 2443fabbbca0Sflorian return "DNSSEC Indeterminate"; 2444fabbbca0Sflorian case 6: 2445fabbbca0Sflorian return "DNSSEC Bogus"; 2446fabbbca0Sflorian case 7: 2447fabbbca0Sflorian return "Signature Expired"; 2448fabbbca0Sflorian case 8: 2449fabbbca0Sflorian return "Signature Not Yet Valid"; 2450fabbbca0Sflorian case 9: 2451fabbbca0Sflorian return "DNSKEY Missing"; 2452fabbbca0Sflorian case 10: 2453fabbbca0Sflorian return "RRSIGs Missing"; 2454fabbbca0Sflorian case 11: 2455fabbbca0Sflorian return "No Zone Key Bit Set"; 2456fabbbca0Sflorian case 12: 2457fabbbca0Sflorian return "NSEC Missing"; 2458fabbbca0Sflorian case 13: 2459fabbbca0Sflorian return "Cached Error"; 2460fabbbca0Sflorian case 14: 2461fabbbca0Sflorian return "Not Ready"; 2462fabbbca0Sflorian case 15: 2463fabbbca0Sflorian return "Blocked"; 2464fabbbca0Sflorian case 16: 2465fabbbca0Sflorian return "Censored"; 2466fabbbca0Sflorian case 17: 2467fabbbca0Sflorian return "Filtered"; 2468fabbbca0Sflorian case 18: 2469fabbbca0Sflorian return "Prohibited"; 2470fabbbca0Sflorian case 19: 2471fabbbca0Sflorian return "Stale NXDomain Answer"; 2472fabbbca0Sflorian case 20: 2473fabbbca0Sflorian return "Not Authoritative"; 2474fabbbca0Sflorian case 21: 2475fabbbca0Sflorian return "Not Supported"; 2476fabbbca0Sflorian case 22: 2477fabbbca0Sflorian return "No Reachable Authority"; 2478fabbbca0Sflorian case 23: 2479fabbbca0Sflorian return "Network Error"; 2480fabbbca0Sflorian case 24: 2481fabbbca0Sflorian return "Invalid Data"; 2482fabbbca0Sflorian default: 2483fabbbca0Sflorian return "Unassigned"; 2484fabbbca0Sflorian } 2485fabbbca0Sflorian } 2486fabbbca0Sflorian 2487*9835a5e1Sflorian static const char * 2488*9835a5e1Sflorian zoneversion_zone(const char *zone, int labelcount) 2489*9835a5e1Sflorian { 2490*9835a5e1Sflorian size_t pos; 2491*9835a5e1Sflorian 2492*9835a5e1Sflorian if (zone == NULL || labelcount == 0) 2493*9835a5e1Sflorian return "."; 2494*9835a5e1Sflorian 2495*9835a5e1Sflorian pos = strlen(zone); 2496*9835a5e1Sflorian if (pos == 0) 2497*9835a5e1Sflorian return "."; 2498*9835a5e1Sflorian 2499*9835a5e1Sflorian pos--; /* go to last char in string */ 2500*9835a5e1Sflorian if (zone[pos] == '.') 2501*9835a5e1Sflorian pos--; /* the labelcount does not count the empty root label */ 2502*9835a5e1Sflorian 2503*9835a5e1Sflorian for (; pos > 0; pos--) { 2504*9835a5e1Sflorian if (zone[pos] == '.') { 2505*9835a5e1Sflorian labelcount--; 2506*9835a5e1Sflorian 2507*9835a5e1Sflorian if (labelcount == 0) { 2508*9835a5e1Sflorian pos++; 2509*9835a5e1Sflorian break; 2510*9835a5e1Sflorian } 2511*9835a5e1Sflorian } 2512*9835a5e1Sflorian } 2513*9835a5e1Sflorian 2514*9835a5e1Sflorian return (zone + pos); 2515*9835a5e1Sflorian } 2516*9835a5e1Sflorian 25175185a700Sflorian isc_result_t 25185185a700Sflorian dns_message_pseudosectiontotext(dns_message_t *msg, 25195185a700Sflorian dns_pseudosection_t section, 25205185a700Sflorian const dns_master_style_t *style, 25215185a700Sflorian dns_messagetextflag_t flags, 2522*9835a5e1Sflorian const char *textname, 25235185a700Sflorian isc_buffer_t *target) 25245185a700Sflorian { 25255185a700Sflorian dns_rdataset_t *ps = NULL; 25265185a700Sflorian dns_name_t *name = NULL; 25275185a700Sflorian isc_result_t result; 25285185a700Sflorian char buf[sizeof("1234567890")]; 25295185a700Sflorian uint32_t mbz; 25305185a700Sflorian dns_rdata_t rdata; 25315185a700Sflorian isc_buffer_t optbuf; 25325185a700Sflorian uint16_t optcode, optlen; 25335185a700Sflorian unsigned char *optdata; 25345185a700Sflorian 25355185a700Sflorian REQUIRE(target != NULL); 25365185a700Sflorian REQUIRE(VALID_PSEUDOSECTION(section)); 25375185a700Sflorian 25385185a700Sflorian switch (section) { 25395185a700Sflorian case DNS_PSEUDOSECTION_OPT: 25405185a700Sflorian ps = dns_message_getopt(msg); 25415185a700Sflorian if (ps == NULL) 25425185a700Sflorian return (ISC_R_SUCCESS); 25435185a700Sflorian if ((flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0) 25445185a700Sflorian ADD_STRING(target, ";; OPT PSEUDOSECTION:\n"); 25455185a700Sflorian ADD_STRING(target, "; EDNS: version: "); 25465185a700Sflorian snprintf(buf, sizeof(buf), "%u", 25475185a700Sflorian (unsigned int)((ps->ttl & 0x00ff0000) >> 16)); 25485185a700Sflorian ADD_STRING(target, buf); 25495185a700Sflorian ADD_STRING(target, ", flags:"); 25505185a700Sflorian if ((ps->ttl & DNS_MESSAGEEXTFLAG_DO) != 0) 25515185a700Sflorian ADD_STRING(target, " do"); 25525185a700Sflorian mbz = ps->ttl & 0xffff; 25535185a700Sflorian mbz &= ~DNS_MESSAGEEXTFLAG_DO; /* Known Flags. */ 25545185a700Sflorian if (mbz != 0) { 25555185a700Sflorian ADD_STRING(target, "; MBZ: "); 25565185a700Sflorian snprintf(buf, sizeof(buf), "0x%.4x", mbz); 25575185a700Sflorian ADD_STRING(target, buf); 25585185a700Sflorian ADD_STRING(target, ", udp: "); 25595185a700Sflorian } else 25605185a700Sflorian ADD_STRING(target, "; udp: "); 25615185a700Sflorian snprintf(buf, sizeof(buf), "%u\n", (unsigned int)ps->rdclass); 25625185a700Sflorian ADD_STRING(target, buf); 25635185a700Sflorian 25645185a700Sflorian result = dns_rdataset_first(ps); 25655185a700Sflorian if (result != ISC_R_SUCCESS) 25665185a700Sflorian return (ISC_R_SUCCESS); 25675185a700Sflorian 25685185a700Sflorian /* 25695185a700Sflorian * Print EDNS info, if any. 25705185a700Sflorian * 25715185a700Sflorian * WARNING: The option contents may be malformed as 25725185a700Sflorian * dig +ednsopt=value:<content> does not validity 25735185a700Sflorian * checking. 25745185a700Sflorian */ 25755185a700Sflorian dns_rdata_init(&rdata); 25765185a700Sflorian dns_rdataset_current(ps, &rdata); 25775185a700Sflorian 25785185a700Sflorian isc_buffer_init(&optbuf, rdata.data, rdata.length); 25795185a700Sflorian isc_buffer_add(&optbuf, rdata.length); 25805185a700Sflorian while (isc_buffer_remaininglength(&optbuf) != 0) { 25815185a700Sflorian INSIST(isc_buffer_remaininglength(&optbuf) >= 4U); 25825185a700Sflorian optcode = isc_buffer_getuint16(&optbuf); 25835185a700Sflorian optlen = isc_buffer_getuint16(&optbuf); 25845185a700Sflorian INSIST(isc_buffer_remaininglength(&optbuf) >= optlen); 25855185a700Sflorian 25865185a700Sflorian if (optcode == DNS_OPT_NSID) { 25875185a700Sflorian ADD_STRING(target, "; NSID"); 25885185a700Sflorian } else if (optcode == DNS_OPT_COOKIE) { 25895185a700Sflorian ADD_STRING(target, "; COOKIE"); 25905185a700Sflorian } else if (optcode == DNS_OPT_CLIENT_SUBNET) { 25915185a700Sflorian isc_buffer_t ecsbuf; 25925185a700Sflorian 25935185a700Sflorian ADD_STRING(target, "; CLIENT-SUBNET"); 25945185a700Sflorian isc_buffer_init(&ecsbuf, 25955185a700Sflorian isc_buffer_current(&optbuf), 25965185a700Sflorian optlen); 25975185a700Sflorian isc_buffer_add(&ecsbuf, optlen); 25985185a700Sflorian result = render_ecs(&ecsbuf, target); 25995185a700Sflorian if (result == ISC_R_NOSPACE) 26005185a700Sflorian return (result); 26015185a700Sflorian if (result == ISC_R_SUCCESS) { 26025185a700Sflorian isc_buffer_forward(&optbuf, optlen); 26035185a700Sflorian ADD_STRING(target, "\n"); 26045185a700Sflorian continue; 26055185a700Sflorian } 26065185a700Sflorian } else if (optcode == DNS_OPT_EXPIRE) { 26075185a700Sflorian if (optlen == 4) { 26085185a700Sflorian uint32_t secs; 26095185a700Sflorian secs = isc_buffer_getuint32(&optbuf); 26105185a700Sflorian ADD_STRING(target, "; EXPIRE: "); 26115185a700Sflorian snprintf(buf, sizeof(buf), "%u", secs); 26125185a700Sflorian ADD_STRING(target, buf); 26135185a700Sflorian ADD_STRING(target, " ("); 26145185a700Sflorian result = dns_ttl_totext(secs, 26151fb015a8Sflorian 1, 26165185a700Sflorian target); 26175185a700Sflorian if (result != ISC_R_SUCCESS) 26185185a700Sflorian return (result); 26195185a700Sflorian ADD_STRING(target, ")\n"); 26205185a700Sflorian continue; 26215185a700Sflorian } 26225185a700Sflorian ADD_STRING(target, "; EXPIRE"); 26235185a700Sflorian } else if (optcode == DNS_OPT_PAD) { 26245185a700Sflorian ADD_STRING(target, "; PAD"); 26255185a700Sflorian } else if (optcode == DNS_OPT_KEY_TAG) { 26265185a700Sflorian ADD_STRING(target, "; KEY-TAG"); 26275185a700Sflorian if (optlen > 0U && (optlen % 2U) == 0U) { 26285185a700Sflorian const char *sep = ": "; 26295185a700Sflorian uint16_t id; 26305185a700Sflorian while (optlen > 0U) { 26315185a700Sflorian id = isc_buffer_getuint16(&optbuf); 26325185a700Sflorian snprintf(buf, sizeof(buf), "%s%u", 26335185a700Sflorian sep, id); 26345185a700Sflorian ADD_STRING(target, buf); 26355185a700Sflorian sep = ", "; 26365185a700Sflorian optlen -= 2; 26375185a700Sflorian } 26385185a700Sflorian ADD_STRING(target, "\n"); 26395185a700Sflorian continue; 26405185a700Sflorian } 2641fabbbca0Sflorian } else if (optcode == DNS_OPT_EDE) { 2642fabbbca0Sflorian uint16_t info_code; 2643fabbbca0Sflorian ADD_STRING(target, "; EDE"); 2644fabbbca0Sflorian if (optlen >= 2) { 2645fabbbca0Sflorian info_code = 2646fabbbca0Sflorian isc_buffer_getuint16(&optbuf); 2647fabbbca0Sflorian optlen -= 2; 2648fabbbca0Sflorian snprintf(buf, sizeof(buf), ": %u (", 2649fabbbca0Sflorian info_code); 2650fabbbca0Sflorian ADD_STRING(target, buf); 2651fabbbca0Sflorian ADD_STRING(target, 2652fabbbca0Sflorian ede_info_code2str(info_code)); 2653fabbbca0Sflorian ADD_STRING(target, ")"); 2654fabbbca0Sflorian } 2655*9835a5e1Sflorian } else if (optcode == DNS_OPT_ZONEVERSION) { 2656*9835a5e1Sflorian int i; 2657*9835a5e1Sflorian 2658*9835a5e1Sflorian ADD_STRING(target, "; ZONEVERSION: "); 2659*9835a5e1Sflorian optdata = isc_buffer_current(&optbuf); 2660*9835a5e1Sflorian for (i = 0; i < optlen; i++) { 2661*9835a5e1Sflorian snprintf(buf, sizeof(buf), "%02x ", 2662*9835a5e1Sflorian optdata[i]); 2663*9835a5e1Sflorian ADD_STRING(target, buf); 2664*9835a5e1Sflorian } 2665*9835a5e1Sflorian 2666*9835a5e1Sflorian if (optlen >= 2) { 2667*9835a5e1Sflorian uint8_t labelcount, type; 2668*9835a5e1Sflorian const char *zone; 2669*9835a5e1Sflorian 2670*9835a5e1Sflorian labelcount = 2671*9835a5e1Sflorian isc_buffer_getuint8(&optbuf); 2672*9835a5e1Sflorian optlen -= 1; 2673*9835a5e1Sflorian type = isc_buffer_getuint8(&optbuf); 2674*9835a5e1Sflorian optlen -= 1; 2675*9835a5e1Sflorian zone = zoneversion_zone(textname, 2676*9835a5e1Sflorian labelcount); 2677*9835a5e1Sflorian 2678*9835a5e1Sflorian if (type == 0 && optlen == 4) { 2679*9835a5e1Sflorian uint32_t serial; 2680*9835a5e1Sflorian 2681*9835a5e1Sflorian serial = isc_buffer_getuint32( 2682*9835a5e1Sflorian &optbuf); 2683*9835a5e1Sflorian optlen -= 4; 2684*9835a5e1Sflorian ADD_STRING(target, 2685*9835a5e1Sflorian "(\"SOA-SERIAL: "); 2686*9835a5e1Sflorian snprintf(buf, sizeof(buf), "%u", 2687*9835a5e1Sflorian serial); 2688*9835a5e1Sflorian ADD_STRING(target, buf); 2689*9835a5e1Sflorian ADD_STRING(target, " ("); 2690*9835a5e1Sflorian ADD_STRING(target, zone); 2691*9835a5e1Sflorian ADD_STRING(target, ")"); 2692*9835a5e1Sflorian ADD_STRING(target, "\")"); 2693*9835a5e1Sflorian } 2694*9835a5e1Sflorian } 26955185a700Sflorian } else { 26965185a700Sflorian ADD_STRING(target, "; OPT="); 26975185a700Sflorian snprintf(buf, sizeof(buf), "%u", optcode); 26985185a700Sflorian ADD_STRING(target, buf); 26995185a700Sflorian } 27005185a700Sflorian 27015185a700Sflorian if (optlen != 0) { 27025185a700Sflorian int i; 27035185a700Sflorian ADD_STRING(target, ": "); 27045185a700Sflorian 27055185a700Sflorian optdata = isc_buffer_current(&optbuf); 27065185a700Sflorian for (i = 0; i < optlen; i++) { 27075185a700Sflorian const char *sep; 27085185a700Sflorian switch (optcode) { 27095185a700Sflorian case DNS_OPT_COOKIE: 27105185a700Sflorian sep = ""; 27115185a700Sflorian break; 27125185a700Sflorian default: 27135185a700Sflorian sep = " "; 27145185a700Sflorian break; 27155185a700Sflorian } 27165185a700Sflorian snprintf(buf, sizeof(buf), "%02x%s", 27175185a700Sflorian optdata[i], sep); 27185185a700Sflorian ADD_STRING(target, buf); 27195185a700Sflorian } 27205185a700Sflorian 27215185a700Sflorian isc_buffer_forward(&optbuf, optlen); 27225185a700Sflorian 27235185a700Sflorian if (optcode == DNS_OPT_COOKIE) { 27245185a700Sflorian if (msg->sitok) 27255185a700Sflorian ADD_STRING(target, " (good)"); 27265185a700Sflorian if (msg->sitbad) 27275185a700Sflorian ADD_STRING(target, " (bad)"); 27285185a700Sflorian ADD_STRING(target, "\n"); 27295185a700Sflorian continue; 27305185a700Sflorian } 27315185a700Sflorian 27325185a700Sflorian if (optcode == DNS_OPT_CLIENT_SUBNET) { 27335185a700Sflorian ADD_STRING(target, "\n"); 27345185a700Sflorian continue; 27355185a700Sflorian } 27365185a700Sflorian 27375185a700Sflorian /* 27385185a700Sflorian * For non-SIT options, add a printable 27395185a700Sflorian * version 27405185a700Sflorian */ 27415185a700Sflorian ADD_STRING(target, "(\""); 27425185a700Sflorian if (isc_buffer_availablelength(target) < optlen) 27435185a700Sflorian return (ISC_R_NOSPACE); 27445185a700Sflorian for (i = 0; i < optlen; i++) { 27455185a700Sflorian if (isprint(optdata[i])) 27465185a700Sflorian isc_buffer_putmem(target, 27475185a700Sflorian &optdata[i], 27485185a700Sflorian 1); 27495185a700Sflorian else 27505185a700Sflorian isc_buffer_putstr(target, "."); 27515185a700Sflorian } 27525185a700Sflorian ADD_STRING(target, "\")"); 27535185a700Sflorian } 27545185a700Sflorian ADD_STRING(target, "\n"); 27555185a700Sflorian } 27565185a700Sflorian return (ISC_R_SUCCESS); 27575185a700Sflorian case DNS_PSEUDOSECTION_TSIG: 27585185a700Sflorian ps = dns_message_gettsig(msg, &name); 27595185a700Sflorian if (ps == NULL) 27605185a700Sflorian return (ISC_R_SUCCESS); 27615185a700Sflorian if ((flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0) 27625185a700Sflorian ADD_STRING(target, ";; TSIG PSEUDOSECTION:\n"); 27635185a700Sflorian result = dns_master_rdatasettotext(name, ps, style, target); 27645185a700Sflorian if ((flags & DNS_MESSAGETEXTFLAG_NOHEADERS) == 0 && 27655185a700Sflorian (flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0) 27665185a700Sflorian ADD_STRING(target, "\n"); 27675185a700Sflorian return (result); 27685185a700Sflorian case DNS_PSEUDOSECTION_SIG0: 27695185a700Sflorian ps = dns_message_getsig0(msg, &name); 27705185a700Sflorian if (ps == NULL) 27715185a700Sflorian return (ISC_R_SUCCESS); 27725185a700Sflorian if ((flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0) 27735185a700Sflorian ADD_STRING(target, ";; SIG0 PSEUDOSECTION:\n"); 27745185a700Sflorian result = dns_master_rdatasettotext(name, ps, style, target); 27755185a700Sflorian if ((flags & DNS_MESSAGETEXTFLAG_NOHEADERS) == 0 && 27765185a700Sflorian (flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0) 27775185a700Sflorian ADD_STRING(target, "\n"); 27785185a700Sflorian return (result); 27795185a700Sflorian } 27805185a700Sflorian return (ISC_R_UNEXPECTED); 27815185a700Sflorian } 27825185a700Sflorian 27835185a700Sflorian isc_result_t 27845185a700Sflorian dns_message_buildopt(dns_message_t *message, dns_rdataset_t **rdatasetp, 27855185a700Sflorian unsigned int version, uint16_t udpsize, 27865185a700Sflorian unsigned int flags, dns_ednsopt_t *ednsopts, size_t count) 27875185a700Sflorian { 27885185a700Sflorian dns_rdataset_t *rdataset = NULL; 27895185a700Sflorian dns_rdatalist_t *rdatalist = NULL; 27905185a700Sflorian dns_rdata_t *rdata = NULL; 27915185a700Sflorian isc_result_t result; 27925185a700Sflorian unsigned int len = 0, i; 27935185a700Sflorian 27945185a700Sflorian REQUIRE(rdatasetp != NULL && *rdatasetp == NULL); 27955185a700Sflorian 27965185a700Sflorian result = dns_message_gettemprdatalist(message, &rdatalist); 27975185a700Sflorian if (result != ISC_R_SUCCESS) 27985185a700Sflorian return (result); 27995185a700Sflorian result = dns_message_gettemprdata(message, &rdata); 28005185a700Sflorian if (result != ISC_R_SUCCESS) 28015185a700Sflorian goto cleanup; 28025185a700Sflorian result = dns_message_gettemprdataset(message, &rdataset); 28035185a700Sflorian if (result != ISC_R_SUCCESS) 28045185a700Sflorian goto cleanup; 28055185a700Sflorian 28065185a700Sflorian rdatalist->type = dns_rdatatype_opt; 28075185a700Sflorian 28085185a700Sflorian /* 28095185a700Sflorian * Set Maximum UDP buffer size. 28105185a700Sflorian */ 28115185a700Sflorian rdatalist->rdclass = udpsize; 28125185a700Sflorian 28135185a700Sflorian /* 28145185a700Sflorian * Set EXTENDED-RCODE and Z to 0. 28155185a700Sflorian */ 28165185a700Sflorian rdatalist->ttl = (version << 16); 28175185a700Sflorian rdatalist->ttl |= (flags & 0xffff); 28185185a700Sflorian 28195185a700Sflorian /* 28205185a700Sflorian * Set EDNS options if applicable 28215185a700Sflorian */ 28225185a700Sflorian if (count != 0U) { 28235185a700Sflorian isc_buffer_t *buf = NULL; 28245185a700Sflorian for (i = 0; i < count; i++) 28255185a700Sflorian len += ednsopts[i].length + 4; 28265185a700Sflorian 28275185a700Sflorian if (len > 0xffffU) { 28285185a700Sflorian result = ISC_R_NOSPACE; 28295185a700Sflorian goto cleanup; 28305185a700Sflorian } 28315185a700Sflorian 28325185a700Sflorian result = isc_buffer_allocate(&buf, len); 28335185a700Sflorian if (result != ISC_R_SUCCESS) 28345185a700Sflorian goto cleanup; 28355185a700Sflorian 28365185a700Sflorian for (i = 0; i < count; i++) { 28375185a700Sflorian isc_buffer_putuint16(buf, ednsopts[i].code); 28385185a700Sflorian isc_buffer_putuint16(buf, ednsopts[i].length); 28395185a700Sflorian if (ednsopts[i].length != 0) { 28405185a700Sflorian isc_buffer_putmem(buf, ednsopts[i].value, 28415185a700Sflorian ednsopts[i].length); 28425185a700Sflorian } 28435185a700Sflorian } 28445185a700Sflorian rdata->data = isc_buffer_base(buf); 28455185a700Sflorian rdata->length = len; 28465185a700Sflorian dns_message_takebuffer(message, &buf); 28475185a700Sflorian } else { 28485185a700Sflorian rdata->data = NULL; 28495185a700Sflorian rdata->length = 0; 28505185a700Sflorian } 28515185a700Sflorian 28525185a700Sflorian rdata->rdclass = rdatalist->rdclass; 28535185a700Sflorian rdata->type = rdatalist->type; 28545185a700Sflorian rdata->flags = 0; 28555185a700Sflorian 28565185a700Sflorian ISC_LIST_APPEND(rdatalist->rdata, rdata, link); 28575185a700Sflorian result = dns_rdatalist_tordataset(rdatalist, rdataset); 28585185a700Sflorian RUNTIME_CHECK(result == ISC_R_SUCCESS); 28595185a700Sflorian 28605185a700Sflorian *rdatasetp = rdataset; 28615185a700Sflorian return (ISC_R_SUCCESS); 28625185a700Sflorian 28635185a700Sflorian cleanup: 28645185a700Sflorian if (rdata != NULL) 28655185a700Sflorian dns_message_puttemprdata(message, &rdata); 28665185a700Sflorian if (rdataset != NULL) 28675185a700Sflorian dns_message_puttemprdataset(message, &rdataset); 28685185a700Sflorian if (rdatalist != NULL) 28695185a700Sflorian dns_message_puttemprdatalist(message, &rdatalist); 28705185a700Sflorian return (result); 28715185a700Sflorian } 2872