message.c revision b66b333f59cf51ef87f973084a5023acd9317fb2
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt/*
45c5f403619029a363cf089e0a4b1bb44425dd84Tinderbox User * Copyright (C) 2004-2015 Internet Systems Consortium, Inc. ("ISC")
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt * Copyright (C) 1999-2003 Internet Software Consortium.
0c27b3fe77ac1d5094ba3521e8142d9e7973133fMark Andrews *
0c27b3fe77ac1d5094ba3521e8142d9e7973133fMark Andrews * Permission to use, copy, modify, and/or distribute this software for any
0c27b3fe77ac1d5094ba3521e8142d9e7973133fMark Andrews * purpose with or without fee is hereby granted, provided that the above
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt * copyright notice and this permission notice appear in all copies.
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt *
2f4561bc9cd5e5cdc58e29e600303c812f6902eeAutomatic Updater * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt * PERFORMANCE OF THIS SOFTWARE.
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt */
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt/*! \file */
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt
99f6179191e583d23f3c5567d3c00b57b64eb52dEvan Hunt/***
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt *** Imports
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt ***/
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt#include <config.h>
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt#include <ctype.h>
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt#include <isc/buffer.h>
39f2d1a96a7c7494b1db0ea0f45e063a6a5ef9bbEvan Hunt#include <isc/mem.h>
99f6179191e583d23f3c5567d3c00b57b64eb52dEvan Hunt#include <isc/print.h>
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt#include <isc/string.h> /* Required for HP/UX (and others?) */
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt#include <isc/util.h>
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt#include <dns/dnssec.h>
4eb998928b9aef0ceda42d7529980d658138698aEvan Hunt#include <dns/keyvalues.h>
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt#include <dns/log.h>
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt#include <dns/masterdump.h>
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt#include <dns/message.h>
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt#include <dns/opcode.h>
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt#include <dns/rdata.h>
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt#include <dns/rdatalist.h>
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt#include <dns/rdataset.h>
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt#include <dns/rdatastruct.h>
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt#include <dns/result.h>
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt#include <dns/tsig.h>
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt#include <dns/ttl.h>
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt#include <dns/view.h>
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt#ifdef SKAN_MSG_DEBUG
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Huntstatic void
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunthexdump(const char *msg, const char *msg2, void *base, size_t len) {
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt unsigned char *p;
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt unsigned int cnt;
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt p = base;
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt cnt = 0;
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt printf("*** %s [%s] (%u bytes @ %p)\n", msg, msg2, len, base);
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt while (cnt < len) {
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt if (cnt % 16 == 0)
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt printf("%p: ", p);
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt else if (cnt % 8 == 0)
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt printf(" |");
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt printf(" %02x %c", *p, (isprint(*p) ? *p : ' '));
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt p++;
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt cnt++;
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt if (cnt % 16 == 0)
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt printf("\n");
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt }
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt if (cnt % 16 != 0)
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt printf("\n");
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt}
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt#endif
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt
79ce3a9e82384cc31fd6b86be8f3d1474fcfd9f4Evan Hunt#define DNS_MESSAGE_OPCODE_MASK 0x7800U
79ce3a9e82384cc31fd6b86be8f3d1474fcfd9f4Evan Hunt#define DNS_MESSAGE_OPCODE_SHIFT 11
79ce3a9e82384cc31fd6b86be8f3d1474fcfd9f4Evan Hunt#define DNS_MESSAGE_RCODE_MASK 0x000fU
79ce3a9e82384cc31fd6b86be8f3d1474fcfd9f4Evan Hunt#define DNS_MESSAGE_FLAG_MASK 0x8ff0U
79ce3a9e82384cc31fd6b86be8f3d1474fcfd9f4Evan Hunt#define DNS_MESSAGE_EDNSRCODE_MASK 0xff000000U
79ce3a9e82384cc31fd6b86be8f3d1474fcfd9f4Evan Hunt#define DNS_MESSAGE_EDNSRCODE_SHIFT 24
79ce3a9e82384cc31fd6b86be8f3d1474fcfd9f4Evan Hunt#define DNS_MESSAGE_EDNSVERSION_MASK 0x00ff0000U
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt#define DNS_MESSAGE_EDNSVERSION_SHIFT 16
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt#define VALID_NAMED_SECTION(s) (((s) > DNS_SECTION_ANY) \
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt && ((s) < DNS_SECTION_MAX))
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt#define VALID_SECTION(s) (((s) >= DNS_SECTION_ANY) \
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt && ((s) < DNS_SECTION_MAX))
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt#define ADD_STRING(b, s) {if (strlen(s) >= \
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt isc_buffer_availablelength(b)) \
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt return(ISC_R_NOSPACE); else \
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt isc_buffer_putstr(b, s);}
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt#define VALID_PSEUDOSECTION(s) (((s) >= DNS_PSEUDOSECTION_ANY) \
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt && ((s) < DNS_PSEUDOSECTION_MAX))
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt#define OPTOUT(x) (((x)->attributes & DNS_RDATASETATTR_OPTOUT) != 0)
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt/*%
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt * This is the size of each individual scratchpad buffer, and the numbers
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt * of various block allocations used within the server.
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt * XXXMLG These should come from a config setting.
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt */
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt#define SCRATCHPAD_SIZE 512
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt#define NAME_COUNT 8
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt#define OFFSET_COUNT 4
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt#define RDATA_COUNT 8
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt#define RDATALIST_COUNT 8
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt#define RDATASET_COUNT RDATALIST_COUNT
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt/*%
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt * Text representation of the different items, for message_totext
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt * functions.
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt */
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Huntstatic const char *sectiontext[] = {
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt "QUESTION",
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt "ANSWER",
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt "AUTHORITY",
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt "ADDITIONAL"
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt};
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Huntstatic const char *updsectiontext[] = {
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt "ZONE",
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt "PREREQUISITE",
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt "UPDATE",
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt "ADDITIONAL"
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt};
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Huntstatic const char *opcodetext[] = {
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt "QUERY",
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt "IQUERY",
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt "STATUS",
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt "RESERVED3",
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt "NOTIFY",
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt "UPDATE",
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt "RESERVED6",
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt "RESERVED7",
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt "RESERVED8",
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt "RESERVED9",
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt "RESERVED10",
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt "RESERVED11",
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt "RESERVED12",
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt "RESERVED13",
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt "RESERVED14",
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt "RESERVED15"
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt};
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Huntstatic const char *rcodetext[] = {
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt "NOERROR",
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt "FORMERR",
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt "SERVFAIL",
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt "NXDOMAIN",
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt "NOTIMP",
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt "REFUSED",
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt "YXDOMAIN",
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt "YXRRSET",
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt "NXRRSET",
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt "NOTAUTH",
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt "NOTZONE",
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt "RESERVED11",
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt "RESERVED12",
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt "RESERVED13",
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt "RESERVED14",
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt "RESERVED15",
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt "BADVERS"
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt};
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt
5ae2eac4c16bdbbef032544bd9fc86f47e7bdc2cMark Andrews
5ae2eac4c16bdbbef032544bd9fc86f47e7bdc2cMark Andrews/*%
5ae2eac4c16bdbbef032544bd9fc86f47e7bdc2cMark Andrews * "helper" type, which consists of a block of some type, and is linkable.
5ae2eac4c16bdbbef032544bd9fc86f47e7bdc2cMark Andrews * For it to work, sizeof(dns_msgblock_t) must be a multiple of the pointer
2c089bf6d24936de631a57b4958ba6b8b5e3b23dMark Andrews * size, or the allocated elements will not be aligned correctly.
5ae2eac4c16bdbbef032544bd9fc86f47e7bdc2cMark Andrews */
5ae2eac4c16bdbbef032544bd9fc86f47e7bdc2cMark Andrewsstruct dns_msgblock {
5ae2eac4c16bdbbef032544bd9fc86f47e7bdc2cMark Andrews unsigned int count;
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt unsigned int remaining;
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt ISC_LINK(dns_msgblock_t) link;
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt}; /* dynamically sized */
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Huntstatic inline dns_msgblock_t *
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Huntmsgblock_allocate(isc_mem_t *, unsigned int, unsigned int);
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt
bbedadf76ab670b01887fb9b41097120ea4fdf14Evan Hunt#define msgblock_get(block, type) \
bbedadf76ab670b01887fb9b41097120ea4fdf14Evan Hunt ((type *)msgblock_internalget(block, sizeof(type)))
bbedadf76ab670b01887fb9b41097120ea4fdf14Evan Hunt
bbedadf76ab670b01887fb9b41097120ea4fdf14Evan Huntstatic inline void *
bbedadf76ab670b01887fb9b41097120ea4fdf14Evan Huntmsgblock_internalget(dns_msgblock_t *, unsigned int);
bbedadf76ab670b01887fb9b41097120ea4fdf14Evan Hunt
bbedadf76ab670b01887fb9b41097120ea4fdf14Evan Huntstatic inline void
61bcc232038f0a2cb77ed6269675fdc288f5ec98Evan Huntmsgblock_reset(dns_msgblock_t *);
61bcc232038f0a2cb77ed6269675fdc288f5ec98Evan Hunt
61bcc232038f0a2cb77ed6269675fdc288f5ec98Evan Huntstatic inline void
61bcc232038f0a2cb77ed6269675fdc288f5ec98Evan Huntmsgblock_free(isc_mem_t *, dns_msgblock_t *, unsigned int);
61bcc232038f0a2cb77ed6269675fdc288f5ec98Evan Hunt
61bcc232038f0a2cb77ed6269675fdc288f5ec98Evan Huntstatic void
61bcc232038f0a2cb77ed6269675fdc288f5ec98Evan Huntlogfmtpacket(dns_message_t *message, const char *description,
61bcc232038f0a2cb77ed6269675fdc288f5ec98Evan Hunt isc_sockaddr_t *address, isc_logcategory_t *category,
61bcc232038f0a2cb77ed6269675fdc288f5ec98Evan Hunt isc_logmodule_t *module, const dns_master_style_t *style,
61bcc232038f0a2cb77ed6269675fdc288f5ec98Evan Hunt int level, isc_mem_t *mctx);
61bcc232038f0a2cb77ed6269675fdc288f5ec98Evan Hunt
61bcc232038f0a2cb77ed6269675fdc288f5ec98Evan Hunt/*
61bcc232038f0a2cb77ed6269675fdc288f5ec98Evan Hunt * Allocate a new dns_msgblock_t, and return a pointer to it. If no memory
61bcc232038f0a2cb77ed6269675fdc288f5ec98Evan Hunt * is free, return NULL.
61bcc232038f0a2cb77ed6269675fdc288f5ec98Evan Hunt */
61bcc232038f0a2cb77ed6269675fdc288f5ec98Evan Huntstatic inline dns_msgblock_t *
61bcc232038f0a2cb77ed6269675fdc288f5ec98Evan Huntmsgblock_allocate(isc_mem_t *mctx, unsigned int sizeof_type,
61bcc232038f0a2cb77ed6269675fdc288f5ec98Evan Hunt unsigned int count)
61bcc232038f0a2cb77ed6269675fdc288f5ec98Evan Hunt{
61bcc232038f0a2cb77ed6269675fdc288f5ec98Evan Hunt dns_msgblock_t *block;
61bcc232038f0a2cb77ed6269675fdc288f5ec98Evan Hunt unsigned int length;
61bcc232038f0a2cb77ed6269675fdc288f5ec98Evan Hunt
61bcc232038f0a2cb77ed6269675fdc288f5ec98Evan Hunt length = sizeof(dns_msgblock_t) + (sizeof_type * count);
61bcc232038f0a2cb77ed6269675fdc288f5ec98Evan Hunt
61bcc232038f0a2cb77ed6269675fdc288f5ec98Evan Hunt block = isc_mem_get(mctx, length);
61bcc232038f0a2cb77ed6269675fdc288f5ec98Evan Hunt if (block == NULL)
61bcc232038f0a2cb77ed6269675fdc288f5ec98Evan Hunt return (NULL);
61bcc232038f0a2cb77ed6269675fdc288f5ec98Evan Hunt
319b8a14881a95996af3a9ba4a20f144eb766b31Evan Hunt block->count = count;
319b8a14881a95996af3a9ba4a20f144eb766b31Evan Hunt block->remaining = count;
319b8a14881a95996af3a9ba4a20f144eb766b31Evan Hunt
319b8a14881a95996af3a9ba4a20f144eb766b31Evan Hunt ISC_LINK_INIT(block, link);
319b8a14881a95996af3a9ba4a20f144eb766b31Evan Hunt
319b8a14881a95996af3a9ba4a20f144eb766b31Evan Hunt return (block);
b47c020d5c635b662ac57e5485d266fd62c796c0Evan Hunt}
b47c020d5c635b662ac57e5485d266fd62c796c0Evan Hunt
b47c020d5c635b662ac57e5485d266fd62c796c0Evan Hunt/*
b47c020d5c635b662ac57e5485d266fd62c796c0Evan Hunt * Return an element from the msgblock. If no more are available, return
b47c020d5c635b662ac57e5485d266fd62c796c0Evan Hunt * NULL.
b47c020d5c635b662ac57e5485d266fd62c796c0Evan Hunt */
b47c020d5c635b662ac57e5485d266fd62c796c0Evan Huntstatic inline void *
b47c020d5c635b662ac57e5485d266fd62c796c0Evan Huntmsgblock_internalget(dns_msgblock_t *block, unsigned int sizeof_type) {
b47c020d5c635b662ac57e5485d266fd62c796c0Evan Hunt void *ptr;
b47c020d5c635b662ac57e5485d266fd62c796c0Evan Hunt
b47c020d5c635b662ac57e5485d266fd62c796c0Evan Hunt if (block == NULL || block->remaining == 0)
b47c020d5c635b662ac57e5485d266fd62c796c0Evan Hunt return (NULL);
b47c020d5c635b662ac57e5485d266fd62c796c0Evan Hunt
b47c020d5c635b662ac57e5485d266fd62c796c0Evan Hunt block->remaining--;
e939674d53a127ddeeaf4b41fd72933f0b493308Mark Andrews
e939674d53a127ddeeaf4b41fd72933f0b493308Mark Andrews ptr = (((unsigned char *)block)
e939674d53a127ddeeaf4b41fd72933f0b493308Mark Andrews + sizeof(dns_msgblock_t)
e939674d53a127ddeeaf4b41fd72933f0b493308Mark Andrews + (sizeof_type * block->remaining));
e939674d53a127ddeeaf4b41fd72933f0b493308Mark Andrews
e939674d53a127ddeeaf4b41fd72933f0b493308Mark Andrews return (ptr);
e939674d53a127ddeeaf4b41fd72933f0b493308Mark Andrews}
e939674d53a127ddeeaf4b41fd72933f0b493308Mark Andrews
0d6328ce5f6b799f8e7c6cbbb3b965cf29bfb7baMark Andrewsstatic inline void
0d6328ce5f6b799f8e7c6cbbb3b965cf29bfb7baMark Andrewsmsgblock_reset(dns_msgblock_t *block) {
0d6328ce5f6b799f8e7c6cbbb3b965cf29bfb7baMark Andrews block->remaining = block->count;
0d6328ce5f6b799f8e7c6cbbb3b965cf29bfb7baMark Andrews}
0d6328ce5f6b799f8e7c6cbbb3b965cf29bfb7baMark Andrews
0d6328ce5f6b799f8e7c6cbbb3b965cf29bfb7baMark Andrews/*
0d6328ce5f6b799f8e7c6cbbb3b965cf29bfb7baMark Andrews * Release memory associated with a message block.
0d6328ce5f6b799f8e7c6cbbb3b965cf29bfb7baMark Andrews */
0d6328ce5f6b799f8e7c6cbbb3b965cf29bfb7baMark Andrewsstatic inline void
0d6328ce5f6b799f8e7c6cbbb3b965cf29bfb7baMark Andrewsmsgblock_free(isc_mem_t *mctx, dns_msgblock_t *block, unsigned int sizeof_type)
0d6328ce5f6b799f8e7c6cbbb3b965cf29bfb7baMark Andrews{
0d6328ce5f6b799f8e7c6cbbb3b965cf29bfb7baMark Andrews unsigned int length;
0d6328ce5f6b799f8e7c6cbbb3b965cf29bfb7baMark Andrews
0d6328ce5f6b799f8e7c6cbbb3b965cf29bfb7baMark Andrews length = sizeof(dns_msgblock_t) + (sizeof_type * block->count);
0d6328ce5f6b799f8e7c6cbbb3b965cf29bfb7baMark Andrews
0d6328ce5f6b799f8e7c6cbbb3b965cf29bfb7baMark Andrews isc_mem_put(mctx, block, length);
677f507de7c546c187c1505c48bc7b440545485cMark Andrews}
677f507de7c546c187c1505c48bc7b440545485cMark Andrews
677f507de7c546c187c1505c48bc7b440545485cMark Andrews/*
677f507de7c546c187c1505c48bc7b440545485cMark Andrews * Allocate a new dynamic buffer, and attach it to this message as the
677f507de7c546c187c1505c48bc7b440545485cMark Andrews * "current" buffer. (which is always the last on the list, for our
677f507de7c546c187c1505c48bc7b440545485cMark Andrews * uses)
677f507de7c546c187c1505c48bc7b440545485cMark Andrews */
e01ef6f01c7e8f80122cd80a2e011425a0135489Mark Andrewsstatic inline isc_result_t
e01ef6f01c7e8f80122cd80a2e011425a0135489Mark Andrewsnewbuffer(dns_message_t *msg, unsigned int size) {
e01ef6f01c7e8f80122cd80a2e011425a0135489Mark Andrews isc_result_t result;
e01ef6f01c7e8f80122cd80a2e011425a0135489Mark Andrews isc_buffer_t *dynbuf;
e01ef6f01c7e8f80122cd80a2e011425a0135489Mark Andrews
e01ef6f01c7e8f80122cd80a2e011425a0135489Mark Andrews dynbuf = NULL;
e01ef6f01c7e8f80122cd80a2e011425a0135489Mark Andrews result = isc_buffer_allocate(msg->mctx, &dynbuf, size);
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt if (result != ISC_R_SUCCESS)
return (ISC_R_NOMEMORY);
ISC_LIST_APPEND(msg->scratchpad, dynbuf, link);
return (ISC_R_SUCCESS);
}
static inline isc_buffer_t *
currentbuffer(dns_message_t *msg) {
isc_buffer_t *dynbuf;
dynbuf = ISC_LIST_TAIL(msg->scratchpad);
INSIST(dynbuf != NULL);
return (dynbuf);
}
static inline void
releaserdata(dns_message_t *msg, dns_rdata_t *rdata) {
ISC_LIST_PREPEND(msg->freerdata, rdata, link);
}
static inline dns_rdata_t *
newrdata(dns_message_t *msg) {
dns_msgblock_t *msgblock;
dns_rdata_t *rdata;
rdata = ISC_LIST_HEAD(msg->freerdata);
if (rdata != NULL) {
ISC_LIST_UNLINK(msg->freerdata, rdata, link);
return (rdata);
}
msgblock = ISC_LIST_TAIL(msg->rdatas);
rdata = msgblock_get(msgblock, dns_rdata_t);
if (rdata == NULL) {
msgblock = msgblock_allocate(msg->mctx, sizeof(dns_rdata_t),
RDATA_COUNT);
if (msgblock == NULL)
return (NULL);
ISC_LIST_APPEND(msg->rdatas, msgblock, link);
rdata = msgblock_get(msgblock, dns_rdata_t);
}
dns_rdata_init(rdata);
return (rdata);
}
static inline void
releaserdatalist(dns_message_t *msg, dns_rdatalist_t *rdatalist) {
ISC_LIST_PREPEND(msg->freerdatalist, rdatalist, link);
}
static inline dns_rdatalist_t *
newrdatalist(dns_message_t *msg) {
dns_msgblock_t *msgblock;
dns_rdatalist_t *rdatalist;
rdatalist = ISC_LIST_HEAD(msg->freerdatalist);
if (rdatalist != NULL) {
ISC_LIST_UNLINK(msg->freerdatalist, rdatalist, link);
goto out;
}
msgblock = ISC_LIST_TAIL(msg->rdatalists);
rdatalist = msgblock_get(msgblock, dns_rdatalist_t);
if (rdatalist == NULL) {
msgblock = msgblock_allocate(msg->mctx,
sizeof(dns_rdatalist_t),
RDATALIST_COUNT);
if (msgblock == NULL)
return (NULL);
ISC_LIST_APPEND(msg->rdatalists, msgblock, link);
rdatalist = msgblock_get(msgblock, dns_rdatalist_t);
}
out:
if (rdatalist != NULL)
dns_rdatalist_init(rdatalist);
return (rdatalist);
}
static inline dns_offsets_t *
newoffsets(dns_message_t *msg) {
dns_msgblock_t *msgblock;
dns_offsets_t *offsets;
msgblock = ISC_LIST_TAIL(msg->offsets);
offsets = msgblock_get(msgblock, dns_offsets_t);
if (offsets == NULL) {
msgblock = msgblock_allocate(msg->mctx,
sizeof(dns_offsets_t),
OFFSET_COUNT);
if (msgblock == NULL)
return (NULL);
ISC_LIST_APPEND(msg->offsets, msgblock, link);
offsets = msgblock_get(msgblock, dns_offsets_t);
}
return (offsets);
}
static inline void
msginitheader(dns_message_t *m) {
m->id = 0;
m->flags = 0;
m->rcode = 0;
m->opcode = 0;
m->rdclass = 0;
}
static inline void
msginitprivate(dns_message_t *m) {
unsigned int i;
for (i = 0; i < DNS_SECTION_MAX; i++) {
m->cursors[i] = NULL;
m->counts[i] = 0;
}
m->opt = NULL;
m->sig0 = NULL;
m->sig0name = NULL;
m->tsig = NULL;
m->tsigname = NULL;
m->state = DNS_SECTION_ANY; /* indicate nothing parsed or rendered */
m->opt_reserved = 0;
m->sig_reserved = 0;
m->reserved = 0;
m->buffer = NULL;
}
static inline void
msginittsig(dns_message_t *m) {
m->tsigstatus = dns_rcode_noerror;
m->querytsigstatus = dns_rcode_noerror;
m->tsigkey = NULL;
m->tsigctx = NULL;
m->sigstart = -1;
m->sig0key = NULL;
m->sig0status = dns_rcode_noerror;
m->timeadjust = 0;
}
/*
* Init elements to default state. Used both when allocating a new element
* and when resetting one.
*/
static inline void
msginit(dns_message_t *m) {
msginitheader(m);
msginitprivate(m);
msginittsig(m);
m->header_ok = 0;
m->question_ok = 0;
m->tcp_continuation = 0;
m->verified_sig = 0;
m->verify_attempted = 0;
m->order = NULL;
m->order_arg = NULL;
m->query.base = NULL;
m->query.length = 0;
m->free_query = 0;
m->saved.base = NULL;
m->saved.length = 0;
m->free_saved = 0;
m->cc_ok = 0;
m->cc_bad = 0;
m->querytsig = NULL;
}
static inline void
msgresetnames(dns_message_t *msg, unsigned int first_section) {
unsigned int i;
dns_name_t *name, *next_name;
dns_rdataset_t *rds, *next_rds;
/*
* Clean up name lists by calling the rdataset disassociate function.
*/
for (i = first_section; i < DNS_SECTION_MAX; i++) {
name = ISC_LIST_HEAD(msg->sections[i]);
while (name != NULL) {
next_name = ISC_LIST_NEXT(name, link);
ISC_LIST_UNLINK(msg->sections[i], name, link);
rds = ISC_LIST_HEAD(name->list);
while (rds != NULL) {
next_rds = ISC_LIST_NEXT(rds, link);
ISC_LIST_UNLINK(name->list, rds, link);
INSIST(dns_rdataset_isassociated(rds));
dns_rdataset_disassociate(rds);
isc_mempool_put(msg->rdspool, rds);
rds = next_rds;
}
if (dns_name_dynamic(name))
dns_name_free(name, msg->mctx);
isc_mempool_put(msg->namepool, name);
name = next_name;
}
}
}
static void
msgresetopt(dns_message_t *msg)
{
if (msg->opt != NULL) {
if (msg->opt_reserved > 0) {
dns_message_renderrelease(msg, msg->opt_reserved);
msg->opt_reserved = 0;
}
INSIST(dns_rdataset_isassociated(msg->opt));
dns_rdataset_disassociate(msg->opt);
isc_mempool_put(msg->rdspool, msg->opt);
msg->opt = NULL;
msg->cc_ok = 0;
msg->cc_bad = 0;
}
}
static void
msgresetsigs(dns_message_t *msg, isc_boolean_t replying) {
if (msg->sig_reserved > 0) {
dns_message_renderrelease(msg, msg->sig_reserved);
msg->sig_reserved = 0;
}
if (msg->tsig != NULL) {
INSIST(dns_rdataset_isassociated(msg->tsig));
INSIST(msg->namepool != NULL);
if (replying) {
INSIST(msg->querytsig == NULL);
msg->querytsig = msg->tsig;
} else {
dns_rdataset_disassociate(msg->tsig);
isc_mempool_put(msg->rdspool, msg->tsig);
if (msg->querytsig != NULL) {
dns_rdataset_disassociate(msg->querytsig);
isc_mempool_put(msg->rdspool, msg->querytsig);
}
}
if (dns_name_dynamic(msg->tsigname))
dns_name_free(msg->tsigname, msg->mctx);
isc_mempool_put(msg->namepool, msg->tsigname);
msg->tsig = NULL;
msg->tsigname = NULL;
} else if (msg->querytsig != NULL && !replying) {
dns_rdataset_disassociate(msg->querytsig);
isc_mempool_put(msg->rdspool, msg->querytsig);
msg->querytsig = NULL;
}
if (msg->sig0 != NULL) {
INSIST(dns_rdataset_isassociated(msg->sig0));
dns_rdataset_disassociate(msg->sig0);
isc_mempool_put(msg->rdspool, msg->sig0);
if (msg->sig0name != NULL) {
if (dns_name_dynamic(msg->sig0name))
dns_name_free(msg->sig0name, msg->mctx);
isc_mempool_put(msg->namepool, msg->sig0name);
}
msg->sig0 = NULL;
msg->sig0name = NULL;
}
}
/*
* Free all but one (or everything) for this message. This is used by
* both dns_message_reset() and dns_message_destroy().
*/
static void
msgreset(dns_message_t *msg, isc_boolean_t everything) {
dns_msgblock_t *msgblock, *next_msgblock;
isc_buffer_t *dynbuf, *next_dynbuf;
dns_rdata_t *rdata;
dns_rdatalist_t *rdatalist;
msgresetnames(msg, 0);
msgresetopt(msg);
msgresetsigs(msg, ISC_FALSE);
/*
* Clean up linked lists.
*/
/*
* Run through the free lists, and just unlink anything found there.
* The memory isn't lost since these are part of message blocks we
* have allocated.
*/
rdata = ISC_LIST_HEAD(msg->freerdata);
while (rdata != NULL) {
ISC_LIST_UNLINK(msg->freerdata, rdata, link);
rdata = ISC_LIST_HEAD(msg->freerdata);
}
rdatalist = ISC_LIST_HEAD(msg->freerdatalist);
while (rdatalist != NULL) {
ISC_LIST_UNLINK(msg->freerdatalist, rdatalist, link);
rdatalist = ISC_LIST_HEAD(msg->freerdatalist);
}
dynbuf = ISC_LIST_HEAD(msg->scratchpad);
INSIST(dynbuf != NULL);
if (!everything) {
isc_buffer_clear(dynbuf);
dynbuf = ISC_LIST_NEXT(dynbuf, link);
}
while (dynbuf != NULL) {
next_dynbuf = ISC_LIST_NEXT(dynbuf, link);
ISC_LIST_UNLINK(msg->scratchpad, dynbuf, link);
isc_buffer_free(&dynbuf);
dynbuf = next_dynbuf;
}
msgblock = ISC_LIST_HEAD(msg->rdatas);
if (!everything && msgblock != NULL) {
msgblock_reset(msgblock);
msgblock = ISC_LIST_NEXT(msgblock, link);
}
while (msgblock != NULL) {
next_msgblock = ISC_LIST_NEXT(msgblock, link);
ISC_LIST_UNLINK(msg->rdatas, msgblock, link);
msgblock_free(msg->mctx, msgblock, sizeof(dns_rdata_t));
msgblock = next_msgblock;
}
/*
* rdatalists could be empty.
*/
msgblock = ISC_LIST_HEAD(msg->rdatalists);
if (!everything && msgblock != NULL) {
msgblock_reset(msgblock);
msgblock = ISC_LIST_NEXT(msgblock, link);
}
while (msgblock != NULL) {
next_msgblock = ISC_LIST_NEXT(msgblock, link);
ISC_LIST_UNLINK(msg->rdatalists, msgblock, link);
msgblock_free(msg->mctx, msgblock, sizeof(dns_rdatalist_t));
msgblock = next_msgblock;
}
msgblock = ISC_LIST_HEAD(msg->offsets);
if (!everything && msgblock != NULL) {
msgblock_reset(msgblock);
msgblock = ISC_LIST_NEXT(msgblock, link);
}
while (msgblock != NULL) {
next_msgblock = ISC_LIST_NEXT(msgblock, link);
ISC_LIST_UNLINK(msg->offsets, msgblock, link);
msgblock_free(msg->mctx, msgblock, sizeof(dns_offsets_t));
msgblock = next_msgblock;
}
if (msg->tsigkey != NULL) {
dns_tsigkey_detach(&msg->tsigkey);
msg->tsigkey = NULL;
}
if (msg->tsigctx != NULL)
dst_context_destroy(&msg->tsigctx);
if (msg->query.base != NULL) {
if (msg->free_query != 0)
isc_mem_put(msg->mctx, msg->query.base,
msg->query.length);
msg->query.base = NULL;
msg->query.length = 0;
}
if (msg->saved.base != NULL) {
if (msg->free_saved != 0)
isc_mem_put(msg->mctx, msg->saved.base,
msg->saved.length);
msg->saved.base = NULL;
msg->saved.length = 0;
}
/*
* cleanup the buffer cleanup list
*/
dynbuf = ISC_LIST_HEAD(msg->cleanup);
while (dynbuf != NULL) {
next_dynbuf = ISC_LIST_NEXT(dynbuf, link);
ISC_LIST_UNLINK(msg->cleanup, dynbuf, link);
isc_buffer_free(&dynbuf);
dynbuf = next_dynbuf;
}
/*
* Set other bits to normal default values.
*/
if (!everything)
msginit(msg);
ENSURE(isc_mempool_getallocated(msg->namepool) == 0);
ENSURE(isc_mempool_getallocated(msg->rdspool) == 0);
}
static unsigned int
spacefortsig(dns_tsigkey_t *key, int otherlen) {
isc_region_t r1, r2;
unsigned int x;
isc_result_t result;
/*
* The space required for an TSIG record is:
*
* n1 bytes for the name
* 2 bytes for the type
* 2 bytes for the class
* 4 bytes for the ttl
* 2 bytes for the rdlength
* n2 bytes for the algorithm name
* 6 bytes for the time signed
* 2 bytes for the fudge
* 2 bytes for the MAC size
* x bytes for the MAC
* 2 bytes for the original id
* 2 bytes for the error
* 2 bytes for the other data length
* y bytes for the other data (at most)
* ---------------------------------
* 26 + n1 + n2 + x + y bytes
*/
dns_name_toregion(&key->name, &r1);
dns_name_toregion(key->algorithm, &r2);
if (key->key == NULL)
x = 0;
else {
result = dst_key_sigsize(key->key, &x);
if (result != ISC_R_SUCCESS)
x = 0;
}
return (26 + r1.length + r2.length + x + otherlen);
}
isc_result_t
dns_message_create(isc_mem_t *mctx, unsigned int intent, dns_message_t **msgp)
{
dns_message_t *m;
isc_result_t result;
isc_buffer_t *dynbuf;
unsigned int i;
REQUIRE(mctx != NULL);
REQUIRE(msgp != NULL);
REQUIRE(*msgp == NULL);
REQUIRE(intent == DNS_MESSAGE_INTENTPARSE
|| intent == DNS_MESSAGE_INTENTRENDER);
m = isc_mem_get(mctx, sizeof(dns_message_t));
if (m == NULL)
return (ISC_R_NOMEMORY);
/*
* No allocations until further notice. Just initialize all lists
* and other members that are freed in the cleanup phase here.
*/
m->magic = DNS_MESSAGE_MAGIC;
m->from_to_wire = intent;
msginit(m);
for (i = 0; i < DNS_SECTION_MAX; i++)
ISC_LIST_INIT(m->sections[i]);
m->mctx = NULL;
isc_mem_attach(mctx, &m->mctx);
ISC_LIST_INIT(m->scratchpad);
ISC_LIST_INIT(m->cleanup);
m->namepool = NULL;
m->rdspool = NULL;
ISC_LIST_INIT(m->rdatas);
ISC_LIST_INIT(m->rdatalists);
ISC_LIST_INIT(m->offsets);
ISC_LIST_INIT(m->freerdata);
ISC_LIST_INIT(m->freerdatalist);
/*
* Ok, it is safe to allocate (and then "goto cleanup" if failure)
*/
result = isc_mempool_create(m->mctx, sizeof(dns_name_t), &m->namepool);
if (result != ISC_R_SUCCESS)
goto cleanup;
isc_mempool_setfreemax(m->namepool, NAME_COUNT);
isc_mempool_setname(m->namepool, "msg:names");
result = isc_mempool_create(m->mctx, sizeof(dns_rdataset_t),
&m->rdspool);
if (result != ISC_R_SUCCESS)
goto cleanup;
isc_mempool_setfreemax(m->rdspool, NAME_COUNT);
isc_mempool_setname(m->rdspool, "msg:rdataset");
dynbuf = NULL;
result = isc_buffer_allocate(mctx, &dynbuf, SCRATCHPAD_SIZE);
if (result != ISC_R_SUCCESS)
goto cleanup;
ISC_LIST_APPEND(m->scratchpad, dynbuf, link);
m->cctx = NULL;
*msgp = m;
return (ISC_R_SUCCESS);
/*
* Cleanup for error returns.
*/
cleanup:
dynbuf = ISC_LIST_HEAD(m->scratchpad);
if (dynbuf != NULL) {
ISC_LIST_UNLINK(m->scratchpad, dynbuf, link);
isc_buffer_free(&dynbuf);
}
if (m->namepool != NULL)
isc_mempool_destroy(&m->namepool);
if (m->rdspool != NULL)
isc_mempool_destroy(&m->rdspool);
m->magic = 0;
isc_mem_putanddetach(&mctx, m, sizeof(dns_message_t));
return (ISC_R_NOMEMORY);
}
void
dns_message_reset(dns_message_t *msg, unsigned int intent) {
REQUIRE(DNS_MESSAGE_VALID(msg));
REQUIRE(intent == DNS_MESSAGE_INTENTPARSE
|| intent == DNS_MESSAGE_INTENTRENDER);
msgreset(msg, ISC_FALSE);
msg->from_to_wire = intent;
}
void
dns_message_destroy(dns_message_t **msgp) {
dns_message_t *msg;
REQUIRE(msgp != NULL);
REQUIRE(DNS_MESSAGE_VALID(*msgp));
msg = *msgp;
*msgp = NULL;
msgreset(msg, ISC_TRUE);
isc_mempool_destroy(&msg->namepool);
isc_mempool_destroy(&msg->rdspool);
msg->magic = 0;
isc_mem_putanddetach(&msg->mctx, msg, sizeof(dns_message_t));
}
static isc_result_t
findname(dns_name_t **foundname, dns_name_t *target,
dns_namelist_t *section)
{
dns_name_t *curr;
for (curr = ISC_LIST_TAIL(*section);
curr != NULL;
curr = ISC_LIST_PREV(curr, link)) {
if (dns_name_equal(curr, target)) {
if (foundname != NULL)
*foundname = curr;
return (ISC_R_SUCCESS);
}
}
return (ISC_R_NOTFOUND);
}
isc_result_t
dns_message_find(dns_name_t *name, dns_rdataclass_t rdclass,
dns_rdatatype_t type, dns_rdatatype_t covers,
dns_rdataset_t **rdataset)
{
dns_rdataset_t *curr;
if (rdataset != NULL) {
REQUIRE(*rdataset == NULL);
}
for (curr = ISC_LIST_TAIL(name->list);
curr != NULL;
curr = ISC_LIST_PREV(curr, link)) {
if (curr->rdclass == rdclass &&
curr->type == type && curr->covers == covers) {
if (rdataset != NULL)
*rdataset = curr;
return (ISC_R_SUCCESS);
}
}
return (ISC_R_NOTFOUND);
}
isc_result_t
dns_message_findtype(dns_name_t *name, dns_rdatatype_t type,
dns_rdatatype_t covers, dns_rdataset_t **rdataset)
{
dns_rdataset_t *curr;
REQUIRE(name != NULL);
if (rdataset != NULL) {
REQUIRE(*rdataset == NULL);
}
for (curr = ISC_LIST_TAIL(name->list);
curr != NULL;
curr = ISC_LIST_PREV(curr, link)) {
if (curr->type == type && curr->covers == covers) {
if (rdataset != NULL)
*rdataset = curr;
return (ISC_R_SUCCESS);
}
}
return (ISC_R_NOTFOUND);
}
/*
* Read a name from buffer "source".
*/
static isc_result_t
getname(dns_name_t *name, isc_buffer_t *source, dns_message_t *msg,
dns_decompress_t *dctx)
{
isc_buffer_t *scratch;
isc_result_t result;
unsigned int tries;
scratch = currentbuffer(msg);
/*
* First try: use current buffer.
* Second try: allocate a new buffer and use that.
*/
tries = 0;
while (tries < 2) {
result = dns_name_fromwire(name, source, dctx, ISC_FALSE,
scratch);
if (result == ISC_R_NOSPACE) {
tries++;
result = newbuffer(msg, SCRATCHPAD_SIZE);
if (result != ISC_R_SUCCESS)
return (result);
scratch = currentbuffer(msg);
dns_name_reset(name);
} else {
return (result);
}
}
INSIST(0); /* Cannot get here... */
return (ISC_R_UNEXPECTED);
}
static isc_result_t
getrdata(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
dns_rdataclass_t rdclass, dns_rdatatype_t rdtype,
unsigned int rdatalen, dns_rdata_t *rdata)
{
isc_buffer_t *scratch;
isc_result_t result;
unsigned int tries;
unsigned int trysize;
scratch = currentbuffer(msg);
isc_buffer_setactive(source, rdatalen);
/*
* First try: use current buffer.
* Second try: allocate a new buffer of size
* max(SCRATCHPAD_SIZE, 2 * compressed_rdatalen)
* (the data will fit if it was not more than 50% compressed)
* Subsequent tries: double buffer size on each try.
*/
tries = 0;
trysize = 0;
/* XXX possibly change this to a while (tries < 2) loop */
for (;;) {
result = dns_rdata_fromwire(rdata, rdclass, rdtype,
source, dctx, 0,
scratch);
if (result == ISC_R_NOSPACE) {
if (tries == 0) {
trysize = 2 * rdatalen;
if (trysize < SCRATCHPAD_SIZE)
trysize = SCRATCHPAD_SIZE;
} else {
INSIST(trysize != 0);
if (trysize >= 65535)
return (ISC_R_NOSPACE);
/* XXX DNS_R_RRTOOLONG? */
trysize *= 2;
}
tries++;
result = newbuffer(msg, trysize);
if (result != ISC_R_SUCCESS)
return (result);
scratch = currentbuffer(msg);
} else {
return (result);
}
}
}
#define DO_FORMERR \
do { \
if (best_effort) \
seen_problem = ISC_TRUE; \
else { \
result = DNS_R_FORMERR; \
goto cleanup; \
} \
} while (0)
static isc_result_t
getquestions(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
unsigned int options)
{
isc_region_t r;
unsigned int count;
dns_name_t *name;
dns_name_t *name2;
dns_offsets_t *offsets;
dns_rdataset_t *rdataset;
dns_rdatalist_t *rdatalist;
isc_result_t result;
dns_rdatatype_t rdtype;
dns_rdataclass_t rdclass;
dns_namelist_t *section;
isc_boolean_t free_name;
isc_boolean_t best_effort;
isc_boolean_t seen_problem;
section = &msg->sections[DNS_SECTION_QUESTION];
best_effort = ISC_TF(options & DNS_MESSAGEPARSE_BESTEFFORT);
seen_problem = ISC_FALSE;
name = NULL;
rdataset = NULL;
rdatalist = NULL;
for (count = 0; count < msg->counts[DNS_SECTION_QUESTION]; count++) {
name = isc_mempool_get(msg->namepool);
if (name == NULL)
return (ISC_R_NOMEMORY);
free_name = ISC_TRUE;
offsets = newoffsets(msg);
if (offsets == NULL) {
result = ISC_R_NOMEMORY;
goto cleanup;
}
dns_name_init(name, *offsets);
/*
* Parse the name out of this packet.
*/
isc_buffer_remainingregion(source, &r);
isc_buffer_setactive(source, r.length);
result = getname(name, source, msg, dctx);
if (result != ISC_R_SUCCESS)
goto cleanup;
/*
* Run through the section, looking to see if this name
* is already there. If it is found, put back the allocated
* name since we no longer need it, and set our name pointer
* to point to the name we found.
*/
result = findname(&name2, name, section);
/*
* If it is the first name in the section, accept it.
*
* If it is not, but is not the same as the name already
* in the question section, append to the section. Note that
* here in the question section this is illegal, so return
* FORMERR. In the future, check the opcode to see if
* this should be legal or not. In either case we no longer
* need this name pointer.
*/
if (result != ISC_R_SUCCESS) {
if (!ISC_LIST_EMPTY(*section))
DO_FORMERR;
ISC_LIST_APPEND(*section, name, link);
free_name = ISC_FALSE;
} else {
isc_mempool_put(msg->namepool, name);
name = name2;
name2 = NULL;
free_name = ISC_FALSE;
}
/*
* Get type and class.
*/
isc_buffer_remainingregion(source, &r);
if (r.length < 4) {
result = ISC_R_UNEXPECTEDEND;
goto cleanup;
}
rdtype = isc_buffer_getuint16(source);
rdclass = isc_buffer_getuint16(source);
/*
* If this class is different than the one we already read,
* this is an error.
*/
if (msg->state == DNS_SECTION_ANY) {
msg->state = DNS_SECTION_QUESTION;
msg->rdclass = rdclass;
} else if (msg->rdclass != rdclass)
DO_FORMERR;
/*
* Can't ask the same question twice.
*/
result = dns_message_find(name, rdclass, rdtype, 0, NULL);
if (result == ISC_R_SUCCESS)
DO_FORMERR;
/*
* Allocate a new rdatalist.
*/
rdatalist = newrdatalist(msg);
if (rdatalist == NULL) {
result = ISC_R_NOMEMORY;
goto cleanup;
}
rdataset = isc_mempool_get(msg->rdspool);
if (rdataset == NULL) {
result = ISC_R_NOMEMORY;
goto cleanup;
}
/*
* Convert rdatalist to rdataset, and attach the latter to
* the name.
*/
rdatalist->type = rdtype;
rdatalist->rdclass = rdclass;
dns_rdataset_init(rdataset);
result = dns_rdatalist_tordataset(rdatalist, rdataset);
if (result != ISC_R_SUCCESS)
goto cleanup;
rdataset->attributes |= DNS_RDATASETATTR_QUESTION;
ISC_LIST_APPEND(name->list, rdataset, link);
rdataset = NULL;
}
if (seen_problem)
return (DNS_R_RECOVERABLE);
return (ISC_R_SUCCESS);
cleanup:
if (rdataset != NULL) {
INSIST(!dns_rdataset_isassociated(rdataset));
isc_mempool_put(msg->rdspool, rdataset);
}
#if 0
if (rdatalist != NULL)
isc_mempool_put(msg->rdlpool, rdatalist);
#endif
if (free_name)
isc_mempool_put(msg->namepool, name);
return (result);
}
static isc_boolean_t
update(dns_section_t section, dns_rdataclass_t rdclass) {
if (section == DNS_SECTION_PREREQUISITE)
return (ISC_TF(rdclass == dns_rdataclass_any ||
rdclass == dns_rdataclass_none));
if (section == DNS_SECTION_UPDATE)
return (ISC_TF(rdclass == dns_rdataclass_any));
return (ISC_FALSE);
}
static isc_result_t
getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
dns_section_t sectionid, unsigned int options)
{
isc_region_t r;
unsigned int count, rdatalen;
dns_name_t *name;
dns_name_t *name2;
dns_offsets_t *offsets;
dns_rdataset_t *rdataset;
dns_rdatalist_t *rdatalist;
isc_result_t result;
dns_rdatatype_t rdtype, covers;
dns_rdataclass_t rdclass;
dns_rdata_t *rdata;
dns_ttl_t ttl;
dns_namelist_t *section;
isc_boolean_t free_name, free_rdataset;
isc_boolean_t preserve_order, best_effort, seen_problem;
isc_boolean_t issigzero;
preserve_order = ISC_TF(options & DNS_MESSAGEPARSE_PRESERVEORDER);
best_effort = ISC_TF(options & DNS_MESSAGEPARSE_BESTEFFORT);
seen_problem = ISC_FALSE;
for (count = 0; count < msg->counts[sectionid]; count++) {
int recstart = source->current;
isc_boolean_t skip_name_search, skip_type_search;
section = &msg->sections[sectionid];
skip_name_search = ISC_FALSE;
skip_type_search = ISC_FALSE;
free_rdataset = ISC_FALSE;
name = isc_mempool_get(msg->namepool);
if (name == NULL)
return (ISC_R_NOMEMORY);
free_name = ISC_TRUE;
offsets = newoffsets(msg);
if (offsets == NULL) {
result = ISC_R_NOMEMORY;
goto cleanup;
}
dns_name_init(name, *offsets);
/*
* Parse the name out of this packet.
*/
isc_buffer_remainingregion(source, &r);
isc_buffer_setactive(source, r.length);
result = getname(name, source, msg, dctx);
if (result != ISC_R_SUCCESS)
goto cleanup;
/*
* Get type, class, ttl, and rdatalen. Verify that at least
* rdatalen bytes remain. (Some of this is deferred to
* later.)
*/
isc_buffer_remainingregion(source, &r);
if (r.length < 2 + 2 + 4 + 2) {
result = ISC_R_UNEXPECTEDEND;
goto cleanup;
}
rdtype = isc_buffer_getuint16(source);
rdclass = isc_buffer_getuint16(source);
/*
* If there was no question section, we may not yet have
* established a class. Do so now.
*/
if (msg->state == DNS_SECTION_ANY &&
rdtype != dns_rdatatype_opt && /* class is UDP SIZE */
rdtype != dns_rdatatype_tsig && /* class is ANY */
rdtype != dns_rdatatype_tkey) { /* class is undefined */
msg->rdclass = rdclass;
msg->state = DNS_SECTION_QUESTION;
}
/*
* If this class is different than the one in the question
* section, bail.
*/
if (msg->opcode != dns_opcode_update
&& rdtype != dns_rdatatype_tsig
&& rdtype != dns_rdatatype_opt
&& rdtype != dns_rdatatype_dnskey /* in a TKEY query */
&& rdtype != dns_rdatatype_sig /* SIG(0) */
&& rdtype != dns_rdatatype_tkey /* Win2000 TKEY */
&& msg->rdclass != dns_rdataclass_any
&& msg->rdclass != rdclass)
DO_FORMERR;
/*
* Special type handling for TSIG, OPT, and TKEY.
*/
if (rdtype == dns_rdatatype_tsig) {
/*
* If it is a tsig, verify that it is in the
* additional data section.
*/
if (sectionid != DNS_SECTION_ADDITIONAL ||
rdclass != dns_rdataclass_any ||
count != msg->counts[sectionid] - 1)
DO_FORMERR;
msg->sigstart = recstart;
skip_name_search = ISC_TRUE;
skip_type_search = ISC_TRUE;
} else if (rdtype == dns_rdatatype_opt) {
/*
* The name of an OPT record must be ".", it
* must be in the additional data section, and
* it must be the first OPT we've seen.
*/
if (!dns_name_equal(dns_rootname, name) ||
sectionid != DNS_SECTION_ADDITIONAL ||
msg->opt != NULL)
DO_FORMERR;
skip_name_search = ISC_TRUE;
skip_type_search = ISC_TRUE;
} else if (rdtype == dns_rdatatype_tkey) {
/*
* A TKEY must be in the additional section if this
* is a query, and the answer section if this is a
* response. Unless it's a Win2000 client.
*
* Its class is ignored.
*/
dns_section_t tkeysection;
if ((msg->flags & DNS_MESSAGEFLAG_QR) == 0)
tkeysection = DNS_SECTION_ADDITIONAL;
else
tkeysection = DNS_SECTION_ANSWER;
if (sectionid != tkeysection &&
sectionid != DNS_SECTION_ANSWER)
DO_FORMERR;
}
/*
* ... now get ttl and rdatalen, and check buffer.
*/
ttl = isc_buffer_getuint32(source);
rdatalen = isc_buffer_getuint16(source);
r.length -= (2 + 2 + 4 + 2);
if (r.length < rdatalen) {
result = ISC_R_UNEXPECTEDEND;
goto cleanup;
}
/*
* Read the rdata from the wire format. Interpret the
* rdata according to its actual class, even if it had a
* DynDNS meta-class in the packet (unless this is a TSIG).
* Then put the meta-class back into the finished rdata.
*/
rdata = newrdata(msg);
if (rdata == NULL) {
result = ISC_R_NOMEMORY;
goto cleanup;
}
if (msg->opcode == dns_opcode_update &&
update(sectionid, rdclass)) {
if (rdatalen != 0) {
result = DNS_R_FORMERR;
goto cleanup;
}
/*
* When the rdata is empty, the data pointer is
* never dereferenced, but it must still be non-NULL.
* Casting 1 rather than "" avoids warnings about
* discarding the const attribute of a string,
* for compilers that would warn about such things.
*/
rdata->data = (unsigned char *)1;
rdata->length = 0;
rdata->rdclass = rdclass;
rdata->type = rdtype;
rdata->flags = DNS_RDATA_UPDATE;
result = ISC_R_SUCCESS;
} else if (rdclass == dns_rdataclass_none &&
msg->opcode == dns_opcode_update &&
sectionid == DNS_SECTION_UPDATE) {
result = getrdata(source, msg, dctx, msg->rdclass,
rdtype, rdatalen, rdata);
} else
result = getrdata(source, msg, dctx, rdclass,
rdtype, rdatalen, rdata);
if (result != ISC_R_SUCCESS)
goto cleanup;
rdata->rdclass = rdclass;
issigzero = ISC_FALSE;
if (rdtype == dns_rdatatype_rrsig &&
rdata->flags == 0) {
covers = dns_rdata_covers(rdata);
if (covers == 0)
DO_FORMERR;
} else if (rdtype == dns_rdatatype_sig /* SIG(0) */ &&
rdata->flags == 0) {
covers = dns_rdata_covers(rdata);
if (covers == 0) {
if (sectionid != DNS_SECTION_ADDITIONAL ||
count != msg->counts[sectionid] - 1)
DO_FORMERR;
msg->sigstart = recstart;
skip_name_search = ISC_TRUE;
skip_type_search = ISC_TRUE;
issigzero = ISC_TRUE;
}
} else
covers = 0;
/*
* Check the ownername of NSEC3 records
*/
if (rdtype == dns_rdatatype_nsec3 &&
!dns_rdata_checkowner(name, msg->rdclass, rdtype,
ISC_FALSE)) {
result = DNS_R_BADOWNERNAME;
goto cleanup;
}
/*
* If we are doing a dynamic update or this is a meta-type,
* don't bother searching for a name, just append this one
* to the end of the message.
*/
if (preserve_order || msg->opcode == dns_opcode_update ||
skip_name_search) {
if (rdtype != dns_rdatatype_opt &&
rdtype != dns_rdatatype_tsig &&
!issigzero)
{
ISC_LIST_APPEND(*section, name, link);
free_name = ISC_FALSE;
}
} else {
/*
* Run through the section, looking to see if this name
* is already there. If it is found, put back the
* allocated name since we no longer need it, and set
* our name pointer to point to the name we found.
*/
result = findname(&name2, name, section);
/*
* If it is a new name, append to the section.
*/
if (result == ISC_R_SUCCESS) {
isc_mempool_put(msg->namepool, name);
name = name2;
} else {
ISC_LIST_APPEND(*section, name, link);
}
free_name = ISC_FALSE;
}
/*
* Search name for the particular type and class.
* Skip this stage if in update mode or this is a meta-type.
*/
if (preserve_order || msg->opcode == dns_opcode_update ||
skip_type_search)
result = ISC_R_NOTFOUND;
else {
/*
* If this is a type that can only occur in
* the question section, fail.
*/
if (dns_rdatatype_questiononly(rdtype))
DO_FORMERR;
rdataset = NULL;
result = dns_message_find(name, rdclass, rdtype,
covers, &rdataset);
}
/*
* If we found an rdataset that matches, we need to
* append this rdata to that set. If we did not, we need
* to create a new rdatalist, store the important bits there,
* convert it to an rdataset, and link the latter to the name.
* Yuck. When appending, make certain that the type isn't
* a singleton type, such as SOA or CNAME.
*
* Note that this check will be bypassed when preserving order,
* the opcode is an update, or the type search is skipped.
*/
if (result == ISC_R_SUCCESS) {
if (dns_rdatatype_issingleton(rdtype)) {
dns_rdata_t *first;
dns_rdatalist_fromrdataset(rdataset,
&rdatalist);
first = ISC_LIST_HEAD(rdatalist->rdata);
INSIST(first != NULL);
if (dns_rdata_compare(rdata, first) != 0)
DO_FORMERR;
}
}
if (result == ISC_R_NOTFOUND) {
rdataset = isc_mempool_get(msg->rdspool);
if (rdataset == NULL) {
result = ISC_R_NOMEMORY;
goto cleanup;
}
free_rdataset = ISC_TRUE;
rdatalist = newrdatalist(msg);
if (rdatalist == NULL) {
result = ISC_R_NOMEMORY;
goto cleanup;
}
rdatalist->type = rdtype;
rdatalist->covers = covers;
rdatalist->rdclass = rdclass;
rdatalist->ttl = ttl;
dns_rdataset_init(rdataset);
RUNTIME_CHECK(dns_rdatalist_tordataset(rdatalist,
rdataset)
== ISC_R_SUCCESS);
dns_rdataset_setownercase(rdataset, name);
if (rdtype != dns_rdatatype_opt &&
rdtype != dns_rdatatype_tsig &&
!issigzero)
{
ISC_LIST_APPEND(name->list, rdataset, link);
free_rdataset = ISC_FALSE;
}
}
/*
* Minimize TTLs.
*
* Section 5.2 of RFC2181 says we should drop
* nonauthoritative rrsets where the TTLs differ, but we
* currently treat them the as if they were authoritative and
* minimize them.
*/
if (ttl != rdataset->ttl) {
rdataset->attributes |= DNS_RDATASETATTR_TTLADJUSTED;
if (ttl < rdataset->ttl)
rdataset->ttl = ttl;
}
/* Append this rdata to the rdataset. */
dns_rdatalist_fromrdataset(rdataset, &rdatalist);
ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
/*
* If this is an OPT, SIG(0) or TSIG record, remember it.
* Also, set the extended rcode for TSIG.
*
* Note msg->opt, msg->sig0 and msg->tsig will only be
* already set if best-effort parsing is enabled otherwise
* there will only be at most one of each.
*/
if (rdtype == dns_rdatatype_opt && msg->opt == NULL) {
dns_rcode_t ercode;
msg->opt = rdataset;
rdataset = NULL;
free_rdataset = ISC_FALSE;
ercode = (dns_rcode_t)
((msg->opt->ttl & DNS_MESSAGE_EDNSRCODE_MASK)
>> 20);
msg->rcode |= ercode;
isc_mempool_put(msg->namepool, name);
free_name = ISC_FALSE;
} else if (issigzero && msg->sig0 == NULL) {
msg->sig0 = rdataset;
msg->sig0name = name;
rdataset = NULL;
free_rdataset = ISC_FALSE;
free_name = ISC_FALSE;
} else if (rdtype == dns_rdatatype_tsig && msg->tsig == NULL) {
msg->tsig = rdataset;
msg->tsigname = name;
/* Windows doesn't like TSIG names to be compressed. */
msg->tsigname->attributes |= DNS_NAMEATTR_NOCOMPRESS;
rdataset = NULL;
free_rdataset = ISC_FALSE;
free_name = ISC_FALSE;
}
if (seen_problem) {
if (free_name)
isc_mempool_put(msg->namepool, name);
if (free_rdataset)
isc_mempool_put(msg->rdspool, rdataset);
free_name = free_rdataset = ISC_FALSE;
}
INSIST(free_name == ISC_FALSE);
INSIST(free_rdataset == ISC_FALSE);
}
if (seen_problem)
return (DNS_R_RECOVERABLE);
return (ISC_R_SUCCESS);
cleanup:
if (free_name)
isc_mempool_put(msg->namepool, name);
if (free_rdataset)
isc_mempool_put(msg->rdspool, rdataset);
return (result);
}
isc_result_t
dns_message_parse(dns_message_t *msg, isc_buffer_t *source,
unsigned int options)
{
isc_region_t r;
dns_decompress_t dctx;
isc_result_t ret;
isc_uint16_t tmpflags;
isc_buffer_t origsource;
isc_boolean_t seen_problem;
isc_boolean_t ignore_tc;
REQUIRE(DNS_MESSAGE_VALID(msg));
REQUIRE(source != NULL);
REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTPARSE);
seen_problem = ISC_FALSE;
ignore_tc = ISC_TF(options & DNS_MESSAGEPARSE_IGNORETRUNCATION);
origsource = *source;
msg->header_ok = 0;
msg->question_ok = 0;
isc_buffer_remainingregion(source, &r);
if (r.length < DNS_MESSAGE_HEADERLEN)
return (ISC_R_UNEXPECTEDEND);
msg->id = isc_buffer_getuint16(source);
tmpflags = isc_buffer_getuint16(source);
msg->opcode = ((tmpflags & DNS_MESSAGE_OPCODE_MASK)
>> DNS_MESSAGE_OPCODE_SHIFT);
msg->rcode = (dns_rcode_t)(tmpflags & DNS_MESSAGE_RCODE_MASK);
msg->flags = (tmpflags & DNS_MESSAGE_FLAG_MASK);
msg->counts[DNS_SECTION_QUESTION] = isc_buffer_getuint16(source);
msg->counts[DNS_SECTION_ANSWER] = isc_buffer_getuint16(source);
msg->counts[DNS_SECTION_AUTHORITY] = isc_buffer_getuint16(source);
msg->counts[DNS_SECTION_ADDITIONAL] = isc_buffer_getuint16(source);
msg->header_ok = 1;
/*
* -1 means no EDNS.
*/
dns_decompress_init(&dctx, -1, DNS_DECOMPRESS_ANY);
dns_decompress_setmethods(&dctx, DNS_COMPRESS_GLOBAL14);
ret = getquestions(source, msg, &dctx, options);
if (ret == ISC_R_UNEXPECTEDEND && ignore_tc)
goto truncated;
if (ret == DNS_R_RECOVERABLE) {
seen_problem = ISC_TRUE;
ret = ISC_R_SUCCESS;
}
if (ret != ISC_R_SUCCESS)
return (ret);
msg->question_ok = 1;
ret = getsection(source, msg, &dctx, DNS_SECTION_ANSWER, options);
if (ret == ISC_R_UNEXPECTEDEND && ignore_tc)
goto truncated;
if (ret == DNS_R_RECOVERABLE) {
seen_problem = ISC_TRUE;
ret = ISC_R_SUCCESS;
}
if (ret != ISC_R_SUCCESS)
return (ret);
ret = getsection(source, msg, &dctx, DNS_SECTION_AUTHORITY, options);
if (ret == ISC_R_UNEXPECTEDEND && ignore_tc)
goto truncated;
if (ret == DNS_R_RECOVERABLE) {
seen_problem = ISC_TRUE;
ret = ISC_R_SUCCESS;
}
if (ret != ISC_R_SUCCESS)
return (ret);
ret = getsection(source, msg, &dctx, DNS_SECTION_ADDITIONAL, options);
if (ret == ISC_R_UNEXPECTEDEND && ignore_tc)
goto truncated;
if (ret == DNS_R_RECOVERABLE) {
seen_problem = ISC_TRUE;
ret = ISC_R_SUCCESS;
}
if (ret != ISC_R_SUCCESS)
return (ret);
isc_buffer_remainingregion(source, &r);
if (r.length != 0) {
isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
DNS_LOGMODULE_MESSAGE, ISC_LOG_DEBUG(3),
"message has %u byte(s) of trailing garbage",
r.length);
}
truncated:
if ((options & DNS_MESSAGEPARSE_CLONEBUFFER) == 0)
isc_buffer_usedregion(&origsource, &msg->saved);
else {
msg->saved.length = isc_buffer_usedlength(&origsource);
msg->saved.base = isc_mem_get(msg->mctx, msg->saved.length);
if (msg->saved.base == NULL)
return (ISC_R_NOMEMORY);
memmove(msg->saved.base, isc_buffer_base(&origsource),
msg->saved.length);
msg->free_saved = 1;
}
if (ret == ISC_R_UNEXPECTEDEND && ignore_tc)
return (DNS_R_RECOVERABLE);
if (seen_problem == ISC_TRUE)
return (DNS_R_RECOVERABLE);
return (ISC_R_SUCCESS);
}
isc_result_t
dns_message_renderbegin(dns_message_t *msg, dns_compress_t *cctx,
isc_buffer_t *buffer)
{
isc_region_t r;
REQUIRE(DNS_MESSAGE_VALID(msg));
REQUIRE(buffer != NULL);
REQUIRE(msg->buffer == NULL);
REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER);
msg->cctx = cctx;
/*
* Erase the contents of this buffer.
*/
isc_buffer_clear(buffer);
/*
* Make certain there is enough for at least the header in this
* buffer.
*/
isc_buffer_availableregion(buffer, &r);
if (r.length < DNS_MESSAGE_HEADERLEN)
return (ISC_R_NOSPACE);
if (r.length < msg->reserved)
return (ISC_R_NOSPACE);
/*
* Reserve enough space for the header in this buffer.
*/
isc_buffer_add(buffer, DNS_MESSAGE_HEADERLEN);
msg->buffer = buffer;
return (ISC_R_SUCCESS);
}
isc_result_t
dns_message_renderchangebuffer(dns_message_t *msg, isc_buffer_t *buffer) {
isc_region_t r, rn;
REQUIRE(DNS_MESSAGE_VALID(msg));
REQUIRE(buffer != NULL);
REQUIRE(msg->buffer != NULL);
/*
* Ensure that the new buffer is empty, and has enough space to
* hold the current contents.
*/
isc_buffer_clear(buffer);
isc_buffer_availableregion(buffer, &rn);
isc_buffer_usedregion(msg->buffer, &r);
REQUIRE(rn.length > r.length);
/*
* Copy the contents from the old to the new buffer.
*/
isc_buffer_add(buffer, r.length);
memmove(rn.base, r.base, r.length);
msg->buffer = buffer;
return (ISC_R_SUCCESS);
}
void
dns_message_renderrelease(dns_message_t *msg, unsigned int space) {
REQUIRE(DNS_MESSAGE_VALID(msg));
REQUIRE(space <= msg->reserved);
msg->reserved -= space;
}
isc_result_t
dns_message_renderreserve(dns_message_t *msg, unsigned int space) {
isc_region_t r;
REQUIRE(DNS_MESSAGE_VALID(msg));
if (msg->buffer != NULL) {
isc_buffer_availableregion(msg->buffer, &r);
if (r.length < (space + msg->reserved))
return (ISC_R_NOSPACE);
}
msg->reserved += space;
return (ISC_R_SUCCESS);
}
static inline isc_boolean_t
wrong_priority(dns_rdataset_t *rds, int pass, dns_rdatatype_t preferred_glue) {
int pass_needed;
/*
* If we are not rendering class IN, this ordering is bogus.
*/
if (rds->rdclass != dns_rdataclass_in)
return (ISC_FALSE);
switch (rds->type) {
case dns_rdatatype_a:
case dns_rdatatype_aaaa:
if (preferred_glue == rds->type)
pass_needed = 4;
else
pass_needed = 3;
break;
case dns_rdatatype_rrsig:
case dns_rdatatype_dnskey:
pass_needed = 2;
break;
default:
pass_needed = 1;
}
if (pass_needed >= pass)
return (ISC_FALSE);
return (ISC_TRUE);
}
#ifdef ALLOW_FILTER_AAAA
/*
* Decide whether to not answer with an AAAA record and its RRSIG
*/
static inline isc_boolean_t
norender_rdataset(const dns_rdataset_t *rdataset, unsigned int options,
dns_section_t sectionid)
{
if (sectionid == DNS_SECTION_QUESTION)
return (ISC_FALSE);
switch (rdataset->type) {
case dns_rdatatype_ns:
if ((options & DNS_MESSAGERENDER_FILTER_AAAA) == 0 ||
sectionid != DNS_SECTION_AUTHORITY)
return (ISC_FALSE);
break;
case dns_rdatatype_aaaa:
if ((options & DNS_MESSAGERENDER_FILTER_AAAA) == 0)
return (ISC_FALSE);
break;
case dns_rdatatype_rrsig:
if ((options & DNS_MESSAGERENDER_FILTER_AAAA) == 0 ||
(rdataset->covers != dns_rdatatype_ns &&
rdataset->covers != dns_rdatatype_aaaa))
return (ISC_FALSE);
if ((rdataset->covers == dns_rdatatype_ns) &&
(sectionid != DNS_SECTION_AUTHORITY))
return (ISC_FALSE);
break;
default:
return (ISC_FALSE);
}
if (rdataset->rdclass != dns_rdataclass_in)
return (ISC_FALSE);
return (ISC_TRUE);
}
#endif
isc_result_t
dns_message_rendersection(dns_message_t *msg, dns_section_t sectionid,
unsigned int options)
{
dns_namelist_t *section;
dns_name_t *name, *next_name;
dns_rdataset_t *rdataset, *next_rdataset;
unsigned int count, total;
isc_result_t result;
isc_buffer_t st; /* for rollbacks */
int pass;
isc_boolean_t partial = ISC_FALSE;
unsigned int rd_options;
dns_rdatatype_t preferred_glue = 0;
REQUIRE(DNS_MESSAGE_VALID(msg));
REQUIRE(msg->buffer != NULL);
REQUIRE(VALID_NAMED_SECTION(sectionid));
section = &msg->sections[sectionid];
if ((sectionid == DNS_SECTION_ADDITIONAL)
&& (options & DNS_MESSAGERENDER_ORDERED) == 0) {
if ((options & DNS_MESSAGERENDER_PREFER_A) != 0) {
preferred_glue = dns_rdatatype_a;
pass = 4;
} else if ((options & DNS_MESSAGERENDER_PREFER_AAAA) != 0) {
preferred_glue = dns_rdatatype_aaaa;
pass = 4;
} else
pass = 3;
} else
pass = 1;
if ((options & DNS_MESSAGERENDER_OMITDNSSEC) == 0)
rd_options = 0;
else
rd_options = DNS_RDATASETTOWIRE_OMITDNSSEC;
/*
* Shrink the space in the buffer by the reserved amount.
*/
msg->buffer->length -= msg->reserved;
total = 0;
if (msg->reserved == 0 && (options & DNS_MESSAGERENDER_PARTIAL) != 0)
partial = ISC_TRUE;
/*
* Render required glue first. Set TC if it won't fit.
*/
name = ISC_LIST_HEAD(*section);
if (name != NULL) {
rdataset = ISC_LIST_HEAD(name->list);
if (rdataset != NULL &&
(rdataset->attributes & DNS_RDATASETATTR_REQUIREDGLUE) != 0 &&
(rdataset->attributes & DNS_RDATASETATTR_RENDERED) == 0) {
const void *order_arg = msg->order_arg;
st = *(msg->buffer);
count = 0;
if (partial)
result = dns_rdataset_towirepartial(rdataset,
name,
msg->cctx,
msg->buffer,
msg->order,
order_arg,
rd_options,
&count,
NULL);
else
result = dns_rdataset_towiresorted(rdataset,
name,
msg->cctx,
msg->buffer,
msg->order,
order_arg,
rd_options,
&count);
total += count;
if (partial && result == ISC_R_NOSPACE) {
msg->flags |= DNS_MESSAGEFLAG_TC;
msg->buffer->length += msg->reserved;
msg->counts[sectionid] += total;
return (result);
}
if (result == ISC_R_NOSPACE)
msg->flags |= DNS_MESSAGEFLAG_TC;
if (result != ISC_R_SUCCESS) {
INSIST(st.used < 65536);
dns_compress_rollback(msg->cctx,
(isc_uint16_t)st.used);
*(msg->buffer) = st; /* rollback */
msg->buffer->length += msg->reserved;
msg->counts[sectionid] += total;
return (result);
}
rdataset->attributes |= DNS_RDATASETATTR_RENDERED;
}
}
do {
name = ISC_LIST_HEAD(*section);
if (name == NULL) {
msg->buffer->length += msg->reserved;
msg->counts[sectionid] += total;
return (ISC_R_SUCCESS);
}
while (name != NULL) {
next_name = ISC_LIST_NEXT(name, link);
rdataset = ISC_LIST_HEAD(name->list);
while (rdataset != NULL) {
next_rdataset = ISC_LIST_NEXT(rdataset, link);
if ((rdataset->attributes &
DNS_RDATASETATTR_RENDERED) != 0)
goto next;
if (((options & DNS_MESSAGERENDER_ORDERED)
== 0)
&& (sectionid == DNS_SECTION_ADDITIONAL)
&& wrong_priority(rdataset, pass,
preferred_glue))
goto next;
#ifdef ALLOW_FILTER_AAAA
/*
* Suppress AAAAs if asked and we are
* not doing DNSSEC or are breaking DNSSEC.
* Say so in the AD bit if we break DNSSEC.
*/
if (norender_rdataset(rdataset, options, sectionid)) {
if (sectionid == DNS_SECTION_ANSWER ||
sectionid == DNS_SECTION_AUTHORITY)
msg->flags &= ~DNS_MESSAGEFLAG_AD;
if (OPTOUT(rdataset))
msg->flags &= ~DNS_MESSAGEFLAG_AD;
goto next;
}
#endif
st = *(msg->buffer);
count = 0;
if (partial)
result = dns_rdataset_towirepartial(
rdataset,
name,
msg->cctx,
msg->buffer,
msg->order,
msg->order_arg,
rd_options,
&count,
NULL);
else
result = dns_rdataset_towiresorted(
rdataset,
name,
msg->cctx,
msg->buffer,
msg->order,
msg->order_arg,
rd_options,
&count);
total += count;
/*
* If out of space, record stats on what we
* rendered so far, and return that status.
*
* XXXMLG Need to change this when
* dns_rdataset_towire() can render partial
* sets starting at some arbitrary point in the
* set. This will include setting a bit in the
* rdataset to indicate that a partial
* rendering was done, and some state saved
* somewhere (probably in the message struct)
* to indicate where to continue from.
*/
if (partial && result == ISC_R_NOSPACE) {
msg->buffer->length += msg->reserved;
msg->counts[sectionid] += total;
return (result);
}
if (result != ISC_R_SUCCESS) {
INSIST(st.used < 65536);
dns_compress_rollback(msg->cctx,
(isc_uint16_t)st.used);
*(msg->buffer) = st; /* rollback */
msg->buffer->length += msg->reserved;
msg->counts[sectionid] += total;
return (result);
}
/*
* If we have rendered non-validated data,
* ensure that the AD bit is not set.
*/
if (rdataset->trust != dns_trust_secure &&
(sectionid == DNS_SECTION_ANSWER ||
sectionid == DNS_SECTION_AUTHORITY))
msg->flags &= ~DNS_MESSAGEFLAG_AD;
if (OPTOUT(rdataset))
msg->flags &= ~DNS_MESSAGEFLAG_AD;
rdataset->attributes |=
DNS_RDATASETATTR_RENDERED;
next:
rdataset = next_rdataset;
}
name = next_name;
}
} while (--pass != 0);
msg->buffer->length += msg->reserved;
msg->counts[sectionid] += total;
return (ISC_R_SUCCESS);
}
void
dns_message_renderheader(dns_message_t *msg, isc_buffer_t *target) {
isc_uint16_t tmp;
isc_region_t r;
REQUIRE(DNS_MESSAGE_VALID(msg));
REQUIRE(target != NULL);
isc_buffer_availableregion(target, &r);
REQUIRE(r.length >= DNS_MESSAGE_HEADERLEN);
isc_buffer_putuint16(target, msg->id);
tmp = ((msg->opcode << DNS_MESSAGE_OPCODE_SHIFT)
& DNS_MESSAGE_OPCODE_MASK);
tmp |= (msg->rcode & DNS_MESSAGE_RCODE_MASK);
tmp |= (msg->flags & DNS_MESSAGE_FLAG_MASK);
INSIST(msg->counts[DNS_SECTION_QUESTION] < 65536 &&
msg->counts[DNS_SECTION_ANSWER] < 65536 &&
msg->counts[DNS_SECTION_AUTHORITY] < 65536 &&
msg->counts[DNS_SECTION_ADDITIONAL] < 65536);
isc_buffer_putuint16(target, tmp);
isc_buffer_putuint16(target,
(isc_uint16_t)msg->counts[DNS_SECTION_QUESTION]);
isc_buffer_putuint16(target,
(isc_uint16_t)msg->counts[DNS_SECTION_ANSWER]);
isc_buffer_putuint16(target,
(isc_uint16_t)msg->counts[DNS_SECTION_AUTHORITY]);
isc_buffer_putuint16(target,
(isc_uint16_t)msg->counts[DNS_SECTION_ADDITIONAL]);
}
isc_result_t
dns_message_renderend(dns_message_t *msg) {
isc_buffer_t tmpbuf;
isc_region_t r;
int result;
unsigned int count;
REQUIRE(DNS_MESSAGE_VALID(msg));
REQUIRE(msg->buffer != NULL);
if ((msg->rcode & ~DNS_MESSAGE_RCODE_MASK) != 0 && msg->opt == NULL) {
/*
* We have an extended rcode but are not using EDNS.
*/
return (DNS_R_FORMERR);
}
/*
* If we're adding a OPT, TSIG or SIG(0) to a truncated message,
* clear all rdatasets from the message except for the question
* before adding the OPT, TSIG or SIG(0). If the question doesn't
* fit, don't include it.
*/
if ((msg->tsigkey != NULL || msg->sig0key != NULL || msg->opt) &&
(msg->flags & DNS_MESSAGEFLAG_TC) != 0)
{
isc_buffer_t *buf;
msgresetnames(msg, DNS_SECTION_ANSWER);
buf = msg->buffer;
dns_message_renderreset(msg);
msg->buffer = buf;
isc_buffer_clear(msg->buffer);
isc_buffer_add(msg->buffer, DNS_MESSAGE_HEADERLEN);
dns_compress_rollback(msg->cctx, 0);
result = dns_message_rendersection(msg, DNS_SECTION_QUESTION,
0);
if (result != ISC_R_SUCCESS && result != ISC_R_NOSPACE)
return (result);
}
/*
* If we've got an OPT record, render it.
*/
if (msg->opt != NULL) {
dns_message_renderrelease(msg, msg->opt_reserved);
msg->opt_reserved = 0;
/*
* Set the extended rcode.
*/
msg->opt->ttl &= ~DNS_MESSAGE_EDNSRCODE_MASK;
msg->opt->ttl |= ((msg->rcode << 20) &
DNS_MESSAGE_EDNSRCODE_MASK);
/*
* Render.
*/
count = 0;
result = dns_rdataset_towire(msg->opt, dns_rootname,
msg->cctx, msg->buffer, 0,
&count);
msg->counts[DNS_SECTION_ADDITIONAL] += count;
if (result != ISC_R_SUCCESS)
return (result);
}
/*
* If we're adding a TSIG record, generate and render it.
*/
if (msg->tsigkey != NULL) {
dns_message_renderrelease(msg, msg->sig_reserved);
msg->sig_reserved = 0;
result = dns_tsig_sign(msg);
if (result != ISC_R_SUCCESS)
return (result);
count = 0;
result = dns_rdataset_towire(msg->tsig, msg->tsigname,
msg->cctx, msg->buffer, 0,
&count);
msg->counts[DNS_SECTION_ADDITIONAL] += count;
if (result != ISC_R_SUCCESS)
return (result);
}
/*
* If we're adding a SIG(0) record, generate and render it.
*/
if (msg->sig0key != NULL) {
dns_message_renderrelease(msg, msg->sig_reserved);
msg->sig_reserved = 0;
result = dns_dnssec_signmessage(msg, msg->sig0key);
if (result != ISC_R_SUCCESS)
return (result);
count = 0;
/*
* Note: dns_rootname is used here, not msg->sig0name, since
* the owner name of a SIG(0) is irrelevant, and will not
* be set in a message being rendered.
*/
result = dns_rdataset_towire(msg->sig0, dns_rootname,
msg->cctx, msg->buffer, 0,
&count);
msg->counts[DNS_SECTION_ADDITIONAL] += count;
if (result != ISC_R_SUCCESS)
return (result);
}
isc_buffer_usedregion(msg->buffer, &r);
isc_buffer_init(&tmpbuf, r.base, r.length);
dns_message_renderheader(msg, &tmpbuf);
msg->buffer = NULL; /* forget about this buffer only on success XXX */
return (ISC_R_SUCCESS);
}
void
dns_message_renderreset(dns_message_t *msg) {
unsigned int i;
dns_name_t *name;
dns_rdataset_t *rds;
/*
* Reset the message so that it may be rendered again.
*/
REQUIRE(DNS_MESSAGE_VALID(msg));
REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER);
msg->buffer = NULL;
for (i = 0; i < DNS_SECTION_MAX; i++) {
msg->cursors[i] = NULL;
msg->counts[i] = 0;
for (name = ISC_LIST_HEAD(msg->sections[i]);
name != NULL;
name = ISC_LIST_NEXT(name, link)) {
for (rds = ISC_LIST_HEAD(name->list);
rds != NULL;
rds = ISC_LIST_NEXT(rds, link)) {
rds->attributes &= ~DNS_RDATASETATTR_RENDERED;
}
}
}
if (msg->tsigname != NULL)
dns_message_puttempname(msg, &msg->tsigname);
if (msg->tsig != NULL) {
dns_rdataset_disassociate(msg->tsig);
dns_message_puttemprdataset(msg, &msg->tsig);
}
if (msg->sig0 != NULL) {
dns_rdataset_disassociate(msg->sig0);
dns_message_puttemprdataset(msg, &msg->sig0);
}
}
isc_result_t
dns_message_firstname(dns_message_t *msg, dns_section_t section) {
REQUIRE(DNS_MESSAGE_VALID(msg));
REQUIRE(VALID_NAMED_SECTION(section));
msg->cursors[section] = ISC_LIST_HEAD(msg->sections[section]);
if (msg->cursors[section] == NULL)
return (ISC_R_NOMORE);
return (ISC_R_SUCCESS);
}
isc_result_t
dns_message_nextname(dns_message_t *msg, dns_section_t section) {
REQUIRE(DNS_MESSAGE_VALID(msg));
REQUIRE(VALID_NAMED_SECTION(section));
REQUIRE(msg->cursors[section] != NULL);
msg->cursors[section] = ISC_LIST_NEXT(msg->cursors[section], link);
if (msg->cursors[section] == NULL)
return (ISC_R_NOMORE);
return (ISC_R_SUCCESS);
}
void
dns_message_currentname(dns_message_t *msg, dns_section_t section,
dns_name_t **name)
{
REQUIRE(DNS_MESSAGE_VALID(msg));
REQUIRE(VALID_NAMED_SECTION(section));
REQUIRE(name != NULL && *name == NULL);
REQUIRE(msg->cursors[section] != NULL);
*name = msg->cursors[section];
}
isc_result_t
dns_message_findname(dns_message_t *msg, dns_section_t section,
dns_name_t *target, dns_rdatatype_t type,
dns_rdatatype_t covers, dns_name_t **name,
dns_rdataset_t **rdataset)
{
dns_name_t *foundname;
isc_result_t result;
/*
* XXX These requirements are probably too intensive, especially
* where things can be NULL, but as they are they ensure that if
* something is NON-NULL, indicating that the caller expects it
* to be filled in, that we can in fact fill it in.
*/
REQUIRE(msg != NULL);
REQUIRE(VALID_SECTION(section));
REQUIRE(target != NULL);
if (name != NULL)
REQUIRE(*name == NULL);
if (type == dns_rdatatype_any) {
REQUIRE(rdataset == NULL);
} else {
if (rdataset != NULL)
REQUIRE(*rdataset == NULL);
}
result = findname(&foundname, target,
&msg->sections[section]);
if (result == ISC_R_NOTFOUND)
return (DNS_R_NXDOMAIN);
else if (result != ISC_R_SUCCESS)
return (result);
if (name != NULL)
*name = foundname;
/*
* And now look for the type.
*/
if (type == dns_rdatatype_any)
return (ISC_R_SUCCESS);
result = dns_message_findtype(foundname, type, covers, rdataset);
if (result == ISC_R_NOTFOUND)
return (DNS_R_NXRRSET);
return (result);
}
void
dns_message_movename(dns_message_t *msg, dns_name_t *name,
dns_section_t fromsection,
dns_section_t tosection)
{
REQUIRE(msg != NULL);
REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER);
REQUIRE(name != NULL);
REQUIRE(VALID_NAMED_SECTION(fromsection));
REQUIRE(VALID_NAMED_SECTION(tosection));
/*
* Unlink the name from the old section
*/
ISC_LIST_UNLINK(msg->sections[fromsection], name, link);
ISC_LIST_APPEND(msg->sections[tosection], name, link);
}
void
dns_message_addname(dns_message_t *msg, dns_name_t *name,
dns_section_t section)
{
REQUIRE(msg != NULL);
REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER);
REQUIRE(name != NULL);
REQUIRE(VALID_NAMED_SECTION(section));
ISC_LIST_APPEND(msg->sections[section], name, link);
}
void
dns_message_removename(dns_message_t *msg, dns_name_t *name,
dns_section_t section)
{
REQUIRE(msg != NULL);
REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER);
REQUIRE(name != NULL);
REQUIRE(VALID_NAMED_SECTION(section));
ISC_LIST_UNLINK(msg->sections[section], name, link);
}
isc_result_t
dns_message_gettempname(dns_message_t *msg, dns_name_t **item) {
REQUIRE(DNS_MESSAGE_VALID(msg));
REQUIRE(item != NULL && *item == NULL);
*item = isc_mempool_get(msg->namepool);
if (*item == NULL)
return (ISC_R_NOMEMORY);
dns_name_init(*item, NULL);
return (ISC_R_SUCCESS);
}
isc_result_t
dns_message_gettempoffsets(dns_message_t *msg, dns_offsets_t **item) {
REQUIRE(DNS_MESSAGE_VALID(msg));
REQUIRE(item != NULL && *item == NULL);
*item = newoffsets(msg);
if (*item == NULL)
return (ISC_R_NOMEMORY);
return (ISC_R_SUCCESS);
}
isc_result_t
dns_message_gettemprdata(dns_message_t *msg, dns_rdata_t **item) {
REQUIRE(DNS_MESSAGE_VALID(msg));
REQUIRE(item != NULL && *item == NULL);
*item = newrdata(msg);
if (*item == NULL)
return (ISC_R_NOMEMORY);
return (ISC_R_SUCCESS);
}
isc_result_t
dns_message_gettemprdataset(dns_message_t *msg, dns_rdataset_t **item) {
REQUIRE(DNS_MESSAGE_VALID(msg));
REQUIRE(item != NULL && *item == NULL);
*item = isc_mempool_get(msg->rdspool);
if (*item == NULL)
return (ISC_R_NOMEMORY);
dns_rdataset_init(*item);
return (ISC_R_SUCCESS);
}
isc_result_t
dns_message_gettemprdatalist(dns_message_t *msg, dns_rdatalist_t **item) {
REQUIRE(DNS_MESSAGE_VALID(msg));
REQUIRE(item != NULL && *item == NULL);
*item = newrdatalist(msg);
if (*item == NULL)
return (ISC_R_NOMEMORY);
return (ISC_R_SUCCESS);
}
void
dns_message_puttempname(dns_message_t *msg, dns_name_t **item) {
REQUIRE(DNS_MESSAGE_VALID(msg));
REQUIRE(item != NULL && *item != NULL);
if (dns_name_dynamic(*item))
dns_name_free(*item, msg->mctx);
isc_mempool_put(msg->namepool, *item);
*item = NULL;
}
void
dns_message_puttemprdata(dns_message_t *msg, dns_rdata_t **item) {
REQUIRE(DNS_MESSAGE_VALID(msg));
REQUIRE(item != NULL && *item != NULL);
releaserdata(msg, *item);
*item = NULL;
}
void
dns_message_puttemprdataset(dns_message_t *msg, dns_rdataset_t **item) {
REQUIRE(DNS_MESSAGE_VALID(msg));
REQUIRE(item != NULL && *item != NULL);
REQUIRE(!dns_rdataset_isassociated(*item));
isc_mempool_put(msg->rdspool, *item);
*item = NULL;
}
void
dns_message_puttemprdatalist(dns_message_t *msg, dns_rdatalist_t **item) {
REQUIRE(DNS_MESSAGE_VALID(msg));
REQUIRE(item != NULL && *item != NULL);
releaserdatalist(msg, *item);
*item = NULL;
}
isc_result_t
dns_message_peekheader(isc_buffer_t *source, dns_messageid_t *idp,
unsigned int *flagsp)
{
isc_region_t r;
isc_buffer_t buffer;
dns_messageid_t id;
unsigned int flags;
REQUIRE(source != NULL);
buffer = *source;
isc_buffer_remainingregion(&buffer, &r);
if (r.length < DNS_MESSAGE_HEADERLEN)
return (ISC_R_UNEXPECTEDEND);
id = isc_buffer_getuint16(&buffer);
flags = isc_buffer_getuint16(&buffer);
flags &= DNS_MESSAGE_FLAG_MASK;
if (flagsp != NULL)
*flagsp = flags;
if (idp != NULL)
*idp = id;
return (ISC_R_SUCCESS);
}
isc_result_t
dns_message_reply(dns_message_t *msg, isc_boolean_t want_question_section) {
unsigned int clear_from;
isc_result_t result;
REQUIRE(DNS_MESSAGE_VALID(msg));
REQUIRE((msg->flags & DNS_MESSAGEFLAG_QR) == 0);
if (!msg->header_ok)
return (DNS_R_FORMERR);
if (msg->opcode != dns_opcode_query &&
msg->opcode != dns_opcode_notify)
want_question_section = ISC_FALSE;
if (msg->opcode == dns_opcode_update)
clear_from = DNS_SECTION_PREREQUISITE;
else if (want_question_section) {
if (!msg->question_ok)
return (DNS_R_FORMERR);
clear_from = DNS_SECTION_ANSWER;
} else
clear_from = DNS_SECTION_QUESTION;
msg->from_to_wire = DNS_MESSAGE_INTENTRENDER;
msgresetnames(msg, clear_from);
msgresetopt(msg);
msgresetsigs(msg, ISC_TRUE);
msginitprivate(msg);
/*
* We now clear most flags and then set QR, ensuring that the
* reply's flags will be in a reasonable state.
*/
msg->flags &= DNS_MESSAGE_REPLYPRESERVE;
msg->flags |= DNS_MESSAGEFLAG_QR;
/*
* This saves the query TSIG status, if the query was signed, and
* reserves space in the reply for the TSIG.
*/
if (msg->tsigkey != NULL) {
unsigned int otherlen = 0;
msg->querytsigstatus = msg->tsigstatus;
msg->tsigstatus = dns_rcode_noerror;
if (msg->querytsigstatus == dns_tsigerror_badtime)
otherlen = 6;
msg->sig_reserved = spacefortsig(msg->tsigkey, otherlen);
result = dns_message_renderreserve(msg, msg->sig_reserved);
if (result != ISC_R_SUCCESS) {
msg->sig_reserved = 0;
return (result);
}
}
if (msg->saved.base != NULL) {
msg->query.base = msg->saved.base;
msg->query.length = msg->saved.length;
msg->free_query = msg->free_saved;
msg->saved.base = NULL;
msg->saved.length = 0;
msg->free_saved = 0;
}
return (ISC_R_SUCCESS);
}
dns_rdataset_t *
dns_message_getopt(dns_message_t *msg) {
/*
* Get the OPT record for 'msg'.
*/
REQUIRE(DNS_MESSAGE_VALID(msg));
return (msg->opt);
}
isc_result_t
dns_message_setopt(dns_message_t *msg, dns_rdataset_t *opt) {
isc_result_t result;
dns_rdata_t rdata = DNS_RDATA_INIT;
/*
* Set the OPT record for 'msg'.
*/
/*
* The space required for an OPT record is:
*
* 1 byte for the name
* 2 bytes for the type
* 2 bytes for the class
* 4 bytes for the ttl
* 2 bytes for the rdata length
* ---------------------------------
* 11 bytes
*
* plus the length of the rdata.
*/
REQUIRE(DNS_MESSAGE_VALID(msg));
REQUIRE(opt->type == dns_rdatatype_opt);
REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER);
REQUIRE(msg->state == DNS_SECTION_ANY);
msgresetopt(msg);
result = dns_rdataset_first(opt);
if (result != ISC_R_SUCCESS)
goto cleanup;
dns_rdataset_current(opt, &rdata);
msg->opt_reserved = 11 + rdata.length;
result = dns_message_renderreserve(msg, msg->opt_reserved);
if (result != ISC_R_SUCCESS) {
msg->opt_reserved = 0;
goto cleanup;
}
msg->opt = opt;
return (ISC_R_SUCCESS);
cleanup:
dns_rdataset_disassociate(opt);
dns_message_puttemprdataset(msg, &opt);
return (result);
}
dns_rdataset_t *
dns_message_gettsig(dns_message_t *msg, dns_name_t **owner) {
/*
* Get the TSIG record and owner for 'msg'.
*/
REQUIRE(DNS_MESSAGE_VALID(msg));
REQUIRE(owner == NULL || *owner == NULL);
if (owner != NULL)
*owner = msg->tsigname;
return (msg->tsig);
}
isc_result_t
dns_message_settsigkey(dns_message_t *msg, dns_tsigkey_t *key) {
isc_result_t result;
/*
* Set the TSIG key for 'msg'
*/
REQUIRE(DNS_MESSAGE_VALID(msg));
REQUIRE(msg->state == DNS_SECTION_ANY);
if (key == NULL && msg->tsigkey != NULL) {
if (msg->sig_reserved != 0) {
dns_message_renderrelease(msg, msg->sig_reserved);
msg->sig_reserved = 0;
}
dns_tsigkey_detach(&msg->tsigkey);
}
if (key != NULL) {
REQUIRE(msg->tsigkey == NULL && msg->sig0key == NULL);
dns_tsigkey_attach(key, &msg->tsigkey);
if (msg->from_to_wire == DNS_MESSAGE_INTENTRENDER) {
msg->sig_reserved = spacefortsig(msg->tsigkey, 0);
result = dns_message_renderreserve(msg,
msg->sig_reserved);
if (result != ISC_R_SUCCESS) {
dns_tsigkey_detach(&msg->tsigkey);
msg->sig_reserved = 0;
return (result);
}
}
}
return (ISC_R_SUCCESS);
}
dns_tsigkey_t *
dns_message_gettsigkey(dns_message_t *msg) {
/*
* Get the TSIG key for 'msg'
*/
REQUIRE(DNS_MESSAGE_VALID(msg));
return (msg->tsigkey);
}
isc_result_t
dns_message_setquerytsig(dns_message_t *msg, isc_buffer_t *querytsig) {
dns_rdata_t *rdata = NULL;
dns_rdatalist_t *list = NULL;
dns_rdataset_t *set = NULL;
isc_buffer_t *buf = NULL;
isc_region_t r;
isc_result_t result;
REQUIRE(DNS_MESSAGE_VALID(msg));
REQUIRE(msg->querytsig == NULL);
if (querytsig == NULL)
return (ISC_R_SUCCESS);
result = dns_message_gettemprdata(msg, &rdata);
if (result != ISC_R_SUCCESS)
goto cleanup;
result = dns_message_gettemprdatalist(msg, &list);
if (result != ISC_R_SUCCESS)
goto cleanup;
result = dns_message_gettemprdataset(msg, &set);
if (result != ISC_R_SUCCESS)
goto cleanup;
isc_buffer_usedregion(querytsig, &r);
result = isc_buffer_allocate(msg->mctx, &buf, r.length);
if (result != ISC_R_SUCCESS)
goto cleanup;
isc_buffer_putmem(buf, r.base, r.length);
isc_buffer_usedregion(buf, &r);
dns_rdata_init(rdata);
dns_rdata_fromregion(rdata, dns_rdataclass_any, dns_rdatatype_tsig, &r);
dns_message_takebuffer(msg, &buf);
ISC_LIST_APPEND(list->rdata, rdata, link);
result = dns_rdatalist_tordataset(list, set);
if (result != ISC_R_SUCCESS)
goto cleanup;
msg->querytsig = set;
return (result);
cleanup:
if (rdata != NULL)
dns_message_puttemprdata(msg, &rdata);
if (list != NULL)
dns_message_puttemprdatalist(msg, &list);
if (set != NULL)
dns_message_puttemprdataset(msg, &set);
return (ISC_R_NOMEMORY);
}
isc_result_t
dns_message_getquerytsig(dns_message_t *msg, isc_mem_t *mctx,
isc_buffer_t **querytsig) {
isc_result_t result;
dns_rdata_t rdata = DNS_RDATA_INIT;
isc_region_t r;
REQUIRE(DNS_MESSAGE_VALID(msg));
REQUIRE(mctx != NULL);
REQUIRE(querytsig != NULL && *querytsig == NULL);
if (msg->tsig == NULL)
return (ISC_R_SUCCESS);
result = dns_rdataset_first(msg->tsig);
if (result != ISC_R_SUCCESS)
return (result);
dns_rdataset_current(msg->tsig, &rdata);
dns_rdata_toregion(&rdata, &r);
result = isc_buffer_allocate(mctx, querytsig, r.length);
if (result != ISC_R_SUCCESS)
return (result);
isc_buffer_putmem(*querytsig, r.base, r.length);
return (ISC_R_SUCCESS);
}
dns_rdataset_t *
dns_message_getsig0(dns_message_t *msg, dns_name_t **owner) {
/*
* Get the SIG(0) record for 'msg'.
*/
REQUIRE(DNS_MESSAGE_VALID(msg));
REQUIRE(owner == NULL || *owner == NULL);
if (msg->sig0 != NULL && owner != NULL) {
/* If dns_message_getsig0 is called on a rendered message
* after the SIG(0) has been applied, we need to return the
* root name, not NULL.
*/
if (msg->sig0name == NULL)
*owner = dns_rootname;
else
*owner = msg->sig0name;
}
return (msg->sig0);
}
isc_result_t
dns_message_setsig0key(dns_message_t *msg, dst_key_t *key) {
isc_region_t r;
unsigned int x;
isc_result_t result;
/*
* Set the SIG(0) key for 'msg'
*/
/*
* The space required for an SIG(0) record is:
*
* 1 byte for the name
* 2 bytes for the type
* 2 bytes for the class
* 4 bytes for the ttl
* 2 bytes for the type covered
* 1 byte for the algorithm
* 1 bytes for the labels
* 4 bytes for the original ttl
* 4 bytes for the signature expiration
* 4 bytes for the signature inception
* 2 bytes for the key tag
* n bytes for the signer's name
* x bytes for the signature
* ---------------------------------
* 27 + n + x bytes
*/
REQUIRE(DNS_MESSAGE_VALID(msg));
REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER);
REQUIRE(msg->state == DNS_SECTION_ANY);
if (key != NULL) {
REQUIRE(msg->sig0key == NULL && msg->tsigkey == NULL);
dns_name_toregion(dst_key_name(key), &r);
result = dst_key_sigsize(key, &x);
if (result != ISC_R_SUCCESS) {
msg->sig_reserved = 0;
return (result);
}
msg->sig_reserved = 27 + r.length + x;
result = dns_message_renderreserve(msg, msg->sig_reserved);
if (result != ISC_R_SUCCESS) {
msg->sig_reserved = 0;
return (result);
}
msg->sig0key = key;
}
return (ISC_R_SUCCESS);
}
dst_key_t *
dns_message_getsig0key(dns_message_t *msg) {
/*
* Get the SIG(0) key for 'msg'
*/
REQUIRE(DNS_MESSAGE_VALID(msg));
return (msg->sig0key);
}
void
dns_message_takebuffer(dns_message_t *msg, isc_buffer_t **buffer) {
REQUIRE(DNS_MESSAGE_VALID(msg));
REQUIRE(buffer != NULL);
REQUIRE(ISC_BUFFER_VALID(*buffer));
ISC_LIST_APPEND(msg->cleanup, *buffer, link);
*buffer = NULL;
}
isc_result_t
dns_message_signer(dns_message_t *msg, dns_name_t *signer) {
isc_result_t result = ISC_R_SUCCESS;
dns_rdata_t rdata = DNS_RDATA_INIT;
REQUIRE(DNS_MESSAGE_VALID(msg));
REQUIRE(signer != NULL);
REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTPARSE);
if (msg->tsig == NULL && msg->sig0 == NULL)
return (ISC_R_NOTFOUND);
if (msg->verify_attempted == 0)
return (DNS_R_NOTVERIFIEDYET);
if (!dns_name_hasbuffer(signer)) {
isc_buffer_t *dynbuf = NULL;
result = isc_buffer_allocate(msg->mctx, &dynbuf, 512);
if (result != ISC_R_SUCCESS)
return (result);
dns_name_setbuffer(signer, dynbuf);
dns_message_takebuffer(msg, &dynbuf);
}
if (msg->sig0 != NULL) {
dns_rdata_sig_t sig;
result = dns_rdataset_first(msg->sig0);
INSIST(result == ISC_R_SUCCESS);
dns_rdataset_current(msg->sig0, &rdata);
result = dns_rdata_tostruct(&rdata, &sig, NULL);
if (result != ISC_R_SUCCESS)
return (result);
if (msg->verified_sig && msg->sig0status == dns_rcode_noerror)
result = ISC_R_SUCCESS;
else
result = DNS_R_SIGINVALID;
dns_name_clone(&sig.signer, signer);
dns_rdata_freestruct(&sig);
} else {
dns_name_t *identity;
dns_rdata_any_tsig_t tsig;
result = dns_rdataset_first(msg->tsig);
INSIST(result == ISC_R_SUCCESS);
dns_rdataset_current(msg->tsig, &rdata);
result = dns_rdata_tostruct(&rdata, &tsig, NULL);
INSIST(result == ISC_R_SUCCESS);
if (msg->tsigstatus != dns_rcode_noerror)
result = DNS_R_TSIGVERIFYFAILURE;
else if (tsig.error != dns_rcode_noerror)
result = DNS_R_TSIGERRORSET;
else
result = ISC_R_SUCCESS;
dns_rdata_freestruct(&tsig);
if (msg->tsigkey == NULL) {
/*
* If msg->tsigstatus & tsig.error are both
* dns_rcode_noerror, the message must have been
* verified, which means msg->tsigkey will be
* non-NULL.
*/
INSIST(result != ISC_R_SUCCESS);
} else {
identity = dns_tsigkey_identity(msg->tsigkey);
if (identity == NULL) {
if (result == ISC_R_SUCCESS)
result = DNS_R_NOIDENTITY;
identity = &msg->tsigkey->name;
}
dns_name_clone(identity, signer);
}
}
return (result);
}
void
dns_message_resetsig(dns_message_t *msg) {
REQUIRE(DNS_MESSAGE_VALID(msg));
msg->verified_sig = 0;
msg->verify_attempted = 0;
msg->tsigstatus = dns_rcode_noerror;
msg->sig0status = dns_rcode_noerror;
msg->timeadjust = 0;
if (msg->tsigkey != NULL) {
dns_tsigkey_detach(&msg->tsigkey);
msg->tsigkey = NULL;
}
}
isc_result_t
dns_message_rechecksig(dns_message_t *msg, dns_view_t *view) {
dns_message_resetsig(msg);
return (dns_message_checksig(msg, view));
}
#ifdef SKAN_MSG_DEBUG
void
dns_message_dumpsig(dns_message_t *msg, char *txt1) {
dns_rdata_t querytsigrdata = DNS_RDATA_INIT;
dns_rdata_any_tsig_t querytsig;
isc_result_t result;
if (msg->tsig != NULL) {
result = dns_rdataset_first(msg->tsig);
RUNTIME_CHECK(result == ISC_R_SUCCESS);
dns_rdataset_current(msg->tsig, &querytsigrdata);
result = dns_rdata_tostruct(&querytsigrdata, &querytsig, NULL);
RUNTIME_CHECK(result == ISC_R_SUCCESS);
hexdump(txt1, "TSIG", querytsig.signature,
querytsig.siglen);
}
if (msg->querytsig != NULL) {
result = dns_rdataset_first(msg->querytsig);
RUNTIME_CHECK(result == ISC_R_SUCCESS);
dns_rdataset_current(msg->querytsig, &querytsigrdata);
result = dns_rdata_tostruct(&querytsigrdata, &querytsig, NULL);
RUNTIME_CHECK(result == ISC_R_SUCCESS);
hexdump(txt1, "QUERYTSIG", querytsig.signature,
querytsig.siglen);
}
}
#endif
isc_result_t
dns_message_checksig(dns_message_t *msg, dns_view_t *view) {
isc_buffer_t b, msgb;
REQUIRE(DNS_MESSAGE_VALID(msg));
if (msg->tsigkey == NULL && msg->tsig == NULL && msg->sig0 == NULL)
return (ISC_R_SUCCESS);
INSIST(msg->saved.base != NULL);
isc_buffer_init(&msgb, msg->saved.base, msg->saved.length);
isc_buffer_add(&msgb, msg->saved.length);
if (msg->tsigkey != NULL || msg->tsig != NULL) {
#ifdef SKAN_MSG_DEBUG
dns_message_dumpsig(msg, "dns_message_checksig#1");
#endif
if (view != NULL)
return (dns_view_checksig(view, &msgb, msg));
else
return (dns_tsig_verify(&msgb, msg, NULL, NULL));
} else {
dns_rdata_t rdata = DNS_RDATA_INIT;
dns_rdata_sig_t sig;
dns_rdataset_t keyset;
isc_result_t result;
result = dns_rdataset_first(msg->sig0);
INSIST(result == ISC_R_SUCCESS);
dns_rdataset_current(msg->sig0, &rdata);
/*
* This can occur when the message is a dynamic update, since
* the rdata length checking is relaxed. This should not
* happen in a well-formed message, since the SIG(0) is only
* looked for in the additional section, and the dynamic update
* meta-records are in the prerequisite and update sections.
*/
if (rdata.length == 0)
return (ISC_R_UNEXPECTEDEND);
result = dns_rdata_tostruct(&rdata, &sig, msg->mctx);
if (result != ISC_R_SUCCESS)
return (result);
dns_rdataset_init(&keyset);
if (view == NULL)
return (DNS_R_KEYUNAUTHORIZED);
result = dns_view_simplefind(view, &sig.signer,
dns_rdatatype_key /* SIG(0) */,
0, 0, ISC_FALSE, &keyset, NULL);
if (result != ISC_R_SUCCESS) {
/* XXXBEW Should possibly create a fetch here */
result = DNS_R_KEYUNAUTHORIZED;
goto freesig;
} else if (keyset.trust < dns_trust_secure) {
/* XXXBEW Should call a validator here */
result = DNS_R_KEYUNAUTHORIZED;
goto freesig;
}
result = dns_rdataset_first(&keyset);
INSIST(result == ISC_R_SUCCESS);
for (;
result == ISC_R_SUCCESS;
result = dns_rdataset_next(&keyset))
{
dst_key_t *key = NULL;
dns_rdata_reset(&rdata);
dns_rdataset_current(&keyset, &rdata);
isc_buffer_init(&b, rdata.data, rdata.length);
isc_buffer_add(&b, rdata.length);
result = dst_key_fromdns(&sig.signer, rdata.rdclass,
&b, view->mctx, &key);
if (result != ISC_R_SUCCESS)
continue;
if (dst_key_alg(key) != sig.algorithm ||
dst_key_id(key) != sig.keyid ||
!(dst_key_proto(key) == DNS_KEYPROTO_DNSSEC ||
dst_key_proto(key) == DNS_KEYPROTO_ANY))
{
dst_key_free(&key);
continue;
}
result = dns_dnssec_verifymessage(&msgb, msg, key);
dst_key_free(&key);
if (result == ISC_R_SUCCESS)
break;
}
if (result == ISC_R_NOMORE)
result = DNS_R_KEYUNAUTHORIZED;
freesig:
if (dns_rdataset_isassociated(&keyset))
dns_rdataset_disassociate(&keyset);
dns_rdata_freestruct(&sig);
return (result);
}
}
isc_result_t
dns_message_sectiontotext(dns_message_t *msg, dns_section_t section,
const dns_master_style_t *style,
dns_messagetextflag_t flags,
isc_buffer_t *target) {
dns_name_t *name, empty_name;
dns_rdataset_t *rdataset;
isc_result_t result;
isc_boolean_t seensoa = ISC_FALSE;
unsigned int sflags = dns_master_styleflags(style);
REQUIRE(DNS_MESSAGE_VALID(msg));
REQUIRE(target != NULL);
REQUIRE(VALID_SECTION(section));
if (ISC_LIST_EMPTY(msg->sections[section]))
return (ISC_R_SUCCESS);
if ((flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0) {
if ((sflags & DNS_STYLEFLAG_INDENT) != 0)
ADD_STRING(target, dns_master_indentstr);
ADD_STRING(target, ";; ");
if (msg->opcode != dns_opcode_update) {
ADD_STRING(target, sectiontext[section]);
} else {
ADD_STRING(target, updsectiontext[section]);
}
ADD_STRING(target, " SECTION:\n");
}
dns_name_init(&empty_name, NULL);
result = dns_message_firstname(msg, section);
if (result != ISC_R_SUCCESS) {
return (result);
}
do {
name = NULL;
dns_message_currentname(msg, section, &name);
for (rdataset = ISC_LIST_HEAD(name->list);
rdataset != NULL;
rdataset = ISC_LIST_NEXT(rdataset, link)) {
if (section == DNS_SECTION_ANSWER &&
rdataset->type == dns_rdatatype_soa) {
if ((flags & DNS_MESSAGETEXTFLAG_OMITSOA) != 0)
continue;
if (seensoa &&
(flags & DNS_MESSAGETEXTFLAG_ONESOA) != 0)
continue;
seensoa = ISC_TRUE;
}
if (section == DNS_SECTION_QUESTION) {
if ((sflags & DNS_STYLEFLAG_INDENT) != 0)
ADD_STRING(target,
dns_master_indentstr);
ADD_STRING(target, ";");
result = dns_master_questiontotext(name,
rdataset,
style,
target);
} else {
result = dns_master_rdatasettotext(name,
rdataset,
style,
target);
}
if (result != ISC_R_SUCCESS)
return (result);
}
result = dns_message_nextname(msg, section);
} while (result == ISC_R_SUCCESS);
if ((flags & DNS_MESSAGETEXTFLAG_NOHEADERS) == 0 &&
(flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0) {
if ((sflags & DNS_STYLEFLAG_INDENT) != 0)
ADD_STRING(target, dns_master_indentstr);
ADD_STRING(target, "\n");
}
if (result == ISC_R_NOMORE)
result = ISC_R_SUCCESS;
return (result);
}
static isc_result_t
render_ecs(isc_buffer_t *optbuf, isc_buffer_t *target) {
int i;
char addr[16], addr_text[64];
isc_uint16_t family;
isc_uint8_t addrlen, addrbytes, scopelen;
/*
* Note: This routine needs to handle malformed ECS options.
*/
if (isc_buffer_remaininglength(optbuf) < 4)
return (DNS_R_OPTERR);
family = isc_buffer_getuint16(optbuf);
addrlen = isc_buffer_getuint8(optbuf);
scopelen = isc_buffer_getuint8(optbuf);
addrbytes = (addrlen + 7) / 8;
if (isc_buffer_remaininglength(optbuf) < addrbytes)
return (DNS_R_OPTERR);
ADD_STRING(target, ": ");
memset(addr, 0, sizeof(addr));
for (i = 0; i < addrbytes; i ++)
addr[i] = isc_buffer_getuint8(optbuf);
if (family == 1)
inet_ntop(AF_INET, addr, addr_text, sizeof(addr_text));
else if (family == 2)
inet_ntop(AF_INET6, addr, addr_text, sizeof(addr_text));
else {
snprintf(addr_text, sizeof(addr_text),
"Unsupported family %u", family);
ADD_STRING(target, addr_text);
return (ISC_R_SUCCESS);
}
ADD_STRING(target, addr_text);
snprintf(addr_text, sizeof(addr_text), "/%d/%d", addrlen, scopelen);
ADD_STRING(target, addr_text);
return (ISC_R_SUCCESS);
}
isc_result_t
dns_message_pseudosectiontotext(dns_message_t *msg,
dns_pseudosection_t section,
const dns_master_style_t *style,
dns_messagetextflag_t flags,
isc_buffer_t *target)
{
dns_rdataset_t *ps = NULL;
dns_name_t *name = NULL;
isc_result_t result;
char buf[sizeof("1234567890")];
isc_uint32_t mbz;
dns_rdata_t rdata;
isc_buffer_t optbuf;
isc_uint16_t optcode, optlen;
unsigned char *optdata;
unsigned int sflags = dns_master_styleflags(style);
REQUIRE(DNS_MESSAGE_VALID(msg));
REQUIRE(target != NULL);
REQUIRE(VALID_PSEUDOSECTION(section));
switch (section) {
case DNS_PSEUDOSECTION_OPT:
ps = dns_message_getopt(msg);
if (ps == NULL)
return (ISC_R_SUCCESS);
if ((flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0) {
if ((sflags & DNS_STYLEFLAG_INDENT) != 0)
ADD_STRING(target, dns_master_indentstr);
ADD_STRING(target, ";; OPT PSEUDOSECTION:\n");
}
if ((sflags & DNS_STYLEFLAG_INDENT) != 0)
ADD_STRING(target, dns_master_indentstr);
ADD_STRING(target, "; EDNS: version: ");
snprintf(buf, sizeof(buf), "%u",
(unsigned int)((ps->ttl & 0x00ff0000) >> 16));
ADD_STRING(target, buf);
ADD_STRING(target, ", flags:");
if ((ps->ttl & DNS_MESSAGEEXTFLAG_DO) != 0)
ADD_STRING(target, " do");
mbz = ps->ttl & 0xffff;
mbz &= ~DNS_MESSAGEEXTFLAG_DO; /* Known Flags. */
if (mbz != 0) {
ADD_STRING(target, "; MBZ: ");
snprintf(buf, sizeof(buf), "%.4x ", mbz);
ADD_STRING(target, buf);
ADD_STRING(target, ", udp: ");
} else
ADD_STRING(target, "; udp: ");
snprintf(buf, sizeof(buf), "%u\n", (unsigned int)ps->rdclass);
ADD_STRING(target, buf);
result = dns_rdataset_first(ps);
if (result != ISC_R_SUCCESS)
return (ISC_R_SUCCESS);
/*
* Print EDNS info, if any.
*
* WARNING: The option contents may be malformed as
* dig +ednsopt=value:<content> does not validity
* checking.
*/
dns_rdata_init(&rdata);
dns_rdataset_current(ps, &rdata);
isc_buffer_init(&optbuf, rdata.data, rdata.length);
isc_buffer_add(&optbuf, rdata.length);
while (isc_buffer_remaininglength(&optbuf) != 0) {
INSIST(isc_buffer_remaininglength(&optbuf) >= 4U);
optcode = isc_buffer_getuint16(&optbuf);
optlen = isc_buffer_getuint16(&optbuf);
INSIST(isc_buffer_remaininglength(&optbuf) >= optlen);
if ((sflags & DNS_STYLEFLAG_INDENT) != 0)
ADD_STRING(target, dns_master_indentstr);
if (optcode == DNS_OPT_NSID) {
ADD_STRING(target, "; NSID");
} else if (optcode == DNS_OPT_COOKIE) {
ADD_STRING(target, "; COOKIE");
} else if (optcode == DNS_OPT_CLIENT_SUBNET) {
ADD_STRING(target, "; CLIENT-SUBNET");
result = render_ecs(&optbuf, target);
if (result == ISC_R_SUCCESS) {
ADD_STRING(target, "\n");
continue;
}
} else if (optcode == DNS_OPT_EXPIRE) {
if (optlen == 4) {
isc_uint32_t secs;
secs = isc_buffer_getuint32(&optbuf);
ADD_STRING(target, "; EXPIRE: ");
snprintf(buf, sizeof(buf), "%u", secs);
ADD_STRING(target, buf);
ADD_STRING(target, " (");
dns_ttl_totext(secs, ISC_TRUE, target);
ADD_STRING(target, ")\n");
continue;
}
ADD_STRING(target, "; EXPIRE");
} else {
ADD_STRING(target, "; OPT=");
snprintf(buf, sizeof(buf), "%u", optcode);
ADD_STRING(target, buf);
}
if (optlen != 0) {
int i;
ADD_STRING(target, ": ");
optdata = isc_buffer_current(&optbuf);
for (i = 0; i < optlen; i++) {
const char *sep;
switch (optcode) {
case DNS_OPT_COOKIE:
sep = "";
break;
default:
sep = " ";
break;
}
snprintf(buf, sizeof(buf), "%02x%s",
optdata[i], sep);
ADD_STRING(target, buf);
}
isc_buffer_forward(&optbuf, optlen);
if (optcode == DNS_OPT_COOKIE) {
if (msg->cc_ok)
ADD_STRING(target, " (good)");
if (msg->cc_bad)
ADD_STRING(target, " (bad)");
ADD_STRING(target, "\n");
continue;
}
if (optcode == DNS_OPT_CLIENT_SUBNET) {
ADD_STRING(target, "\n");
continue;
}
/*
* For non-COOKIE options, add a printable
* version
*/
ADD_STRING(target, "(\"");
if (isc_buffer_availablelength(target) < optlen)
return (ISC_R_NOSPACE);
for (i = 0; i < optlen; i++) {
if (isprint(optdata[i]))
isc_buffer_putmem(target,
&optdata[i],
1);
else
isc_buffer_putstr(target, ".");
}
ADD_STRING(target, "\")");
}
ADD_STRING(target, "\n");
}
return (ISC_R_SUCCESS);
case DNS_PSEUDOSECTION_TSIG:
ps = dns_message_gettsig(msg, &name);
if (ps == NULL)
return (ISC_R_SUCCESS);
if ((sflags & DNS_STYLEFLAG_INDENT) != 0)
ADD_STRING(target, dns_master_indentstr);
if ((flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0)
ADD_STRING(target, ";; TSIG PSEUDOSECTION:\n");
result = dns_master_rdatasettotext(name, ps, style, target);
if ((flags & DNS_MESSAGETEXTFLAG_NOHEADERS) == 0 &&
(flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0)
ADD_STRING(target, "\n");
return (result);
case DNS_PSEUDOSECTION_SIG0:
ps = dns_message_getsig0(msg, &name);
if (ps == NULL)
return (ISC_R_SUCCESS);
if ((sflags & DNS_STYLEFLAG_INDENT) != 0)
ADD_STRING(target, dns_master_indentstr);
if ((flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0)
ADD_STRING(target, ";; SIG0 PSEUDOSECTION:\n");
result = dns_master_rdatasettotext(name, ps, style, target);
if ((flags & DNS_MESSAGETEXTFLAG_NOHEADERS) == 0 &&
(flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0)
ADD_STRING(target, "\n");
return (result);
}
return (ISC_R_UNEXPECTED);
}
isc_result_t
dns_message_totext(dns_message_t *msg, const dns_master_style_t *style,
dns_messagetextflag_t flags, isc_buffer_t *target) {
unsigned int sflags = dns_master_styleflags(style);
char buf[sizeof("1234567890")];
isc_result_t result;
REQUIRE(DNS_MESSAGE_VALID(msg));
REQUIRE(target != NULL);
if ((flags & DNS_MESSAGETEXTFLAG_NOHEADERS) == 0) {
if ((sflags & DNS_STYLEFLAG_INDENT) != 0)
ADD_STRING(target, dns_master_indentstr);
ADD_STRING(target, ";; ->>HEADER<<- opcode: ");
ADD_STRING(target, opcodetext[msg->opcode]);
ADD_STRING(target, ", status: ");
if (msg->rcode < (sizeof(rcodetext)/sizeof(rcodetext[0]))) {
ADD_STRING(target, rcodetext[msg->rcode]);
} else {
snprintf(buf, sizeof(buf), "%4u", msg->rcode);
ADD_STRING(target, buf);
}
ADD_STRING(target, ", id: ");
snprintf(buf, sizeof(buf), "%6u", msg->id);
ADD_STRING(target, buf);
ADD_STRING(target, "\n");
if ((sflags & DNS_STYLEFLAG_INDENT) != 0)
ADD_STRING(target, dns_master_indentstr);
ADD_STRING(target, ";; flags:");
if ((msg->flags & DNS_MESSAGEFLAG_QR) != 0)
ADD_STRING(target, " qr");
if ((msg->flags & DNS_MESSAGEFLAG_AA) != 0)
ADD_STRING(target, " aa");
if ((msg->flags & DNS_MESSAGEFLAG_TC) != 0)
ADD_STRING(target, " tc");
if ((msg->flags & DNS_MESSAGEFLAG_RD) != 0)
ADD_STRING(target, " rd");
if ((msg->flags & DNS_MESSAGEFLAG_RA) != 0)
ADD_STRING(target, " ra");
if ((msg->flags & DNS_MESSAGEFLAG_AD) != 0)
ADD_STRING(target, " ad");
if ((msg->flags & DNS_MESSAGEFLAG_CD) != 0)
ADD_STRING(target, " cd");
/*
* The final unnamed flag must be zero.
*/
if ((msg->flags & 0x0040U) != 0) {
if ((sflags & DNS_STYLEFLAG_INDENT) != 0)
ADD_STRING(target, dns_master_indentstr);
ADD_STRING(target, "; MBZ: 0x4");
}
if (msg->opcode != dns_opcode_update) {
if ((sflags & DNS_STYLEFLAG_INDENT) != 0)
ADD_STRING(target, dns_master_indentstr);
ADD_STRING(target, "; QUESTION: ");
} else {
if ((sflags & DNS_STYLEFLAG_INDENT) != 0)
ADD_STRING(target, dns_master_indentstr);
ADD_STRING(target, "; ZONE: ");
}
snprintf(buf, sizeof(buf), "%1u",
msg->counts[DNS_SECTION_QUESTION]);
ADD_STRING(target, buf);
if (msg->opcode != dns_opcode_update) {
ADD_STRING(target, ", ANSWER: ");
} else {
ADD_STRING(target, ", PREREQ: ");
}
snprintf(buf, sizeof(buf), "%1u",
msg->counts[DNS_SECTION_ANSWER]);
ADD_STRING(target, buf);
if (msg->opcode != dns_opcode_update) {
ADD_STRING(target, ", AUTHORITY: ");
} else {
ADD_STRING(target, ", UPDATE: ");
}
snprintf(buf, sizeof(buf), "%1u",
msg->counts[DNS_SECTION_AUTHORITY]);
ADD_STRING(target, buf);
ADD_STRING(target, ", ADDITIONAL: ");
snprintf(buf, sizeof(buf), "%1u",
msg->counts[DNS_SECTION_ADDITIONAL]);
ADD_STRING(target, buf);
ADD_STRING(target, "\n");
}
result = dns_message_pseudosectiontotext(msg,
DNS_PSEUDOSECTION_OPT,
style, flags, target);
if (result != ISC_R_SUCCESS)
return (result);
result = dns_message_sectiontotext(msg, DNS_SECTION_QUESTION,
style, flags, target);
if (result != ISC_R_SUCCESS)
return (result);
result = dns_message_sectiontotext(msg, DNS_SECTION_ANSWER,
style, flags, target);
if (result != ISC_R_SUCCESS)
return (result);
result = dns_message_sectiontotext(msg, DNS_SECTION_AUTHORITY,
style, flags, target);
if (result != ISC_R_SUCCESS)
return (result);
result = dns_message_sectiontotext(msg, DNS_SECTION_ADDITIONAL,
style, flags, target);
if (result != ISC_R_SUCCESS)
return (result);
result = dns_message_pseudosectiontotext(msg,
DNS_PSEUDOSECTION_TSIG,
style, flags, target);
if (result != ISC_R_SUCCESS)
return (result);
result = dns_message_pseudosectiontotext(msg,
DNS_PSEUDOSECTION_SIG0,
style, flags, target);
if (result != ISC_R_SUCCESS)
return (result);
return (ISC_R_SUCCESS);
}
isc_region_t *
dns_message_getrawmessage(dns_message_t *msg) {
REQUIRE(DNS_MESSAGE_VALID(msg));
return (&msg->saved);
}
void
dns_message_setsortorder(dns_message_t *msg, dns_rdatasetorderfunc_t order,
const void *order_arg)
{
REQUIRE(DNS_MESSAGE_VALID(msg));
msg->order = order;
msg->order_arg = order_arg;
}
void
dns_message_settimeadjust(dns_message_t *msg, int timeadjust) {
REQUIRE(DNS_MESSAGE_VALID(msg));
msg->timeadjust = timeadjust;
}
int
dns_message_gettimeadjust(dns_message_t *msg) {
REQUIRE(DNS_MESSAGE_VALID(msg));
return (msg->timeadjust);
}
isc_result_t
dns_opcode_totext(dns_opcode_t opcode, isc_buffer_t *target) {
REQUIRE(opcode < 16);
if (isc_buffer_availablelength(target) < strlen(opcodetext[opcode]))
return (ISC_R_NOSPACE);
isc_buffer_putstr(target, opcodetext[opcode]);
return (ISC_R_SUCCESS);
}
void
dns_message_logpacket(dns_message_t *message, const char *description,
isc_logcategory_t *category, isc_logmodule_t *module,
int level, isc_mem_t *mctx)
{
logfmtpacket(message, description, NULL, category, module,
&dns_master_style_debug, level, mctx);
}
void
dns_message_logpacket2(dns_message_t *message,
const char *description, isc_sockaddr_t *address,
isc_logcategory_t *category, isc_logmodule_t *module,
int level, isc_mem_t *mctx)
{
REQUIRE(address != NULL);
logfmtpacket(message, description, address, category, module,
&dns_master_style_debug, level, mctx);
}
void
dns_message_logfmtpacket(dns_message_t *message, const char *description,
isc_logcategory_t *category, isc_logmodule_t *module,
const dns_master_style_t *style, int level,
isc_mem_t *mctx)
{
logfmtpacket(message, description, NULL, category, module, style,
level, mctx);
}
void
dns_message_logfmtpacket2(dns_message_t *message,
const char *description, isc_sockaddr_t *address,
isc_logcategory_t *category, isc_logmodule_t *module,
const dns_master_style_t *style, int level,
isc_mem_t *mctx)
{
REQUIRE(address != NULL);
logfmtpacket(message, description, address, category, module, style,
level, mctx);
}
static void
logfmtpacket(dns_message_t *message, const char *description,
isc_sockaddr_t *address, isc_logcategory_t *category,
isc_logmodule_t *module, const dns_master_style_t *style,
int level, isc_mem_t *mctx)
{
char addrbuf[ISC_SOCKADDR_FORMATSIZE] = { 0 };
const char *newline = "\n";
const char *space = " ";
isc_buffer_t buffer;
char *buf = NULL;
int len = 1024;
isc_result_t result;
if (! isc_log_wouldlog(dns_lctx, level))
return;
/*
* Note that these are multiline debug messages. We want a newline
* to appear in the log after each message.
*/
if (address != NULL)
isc_sockaddr_format(address, addrbuf, sizeof(addrbuf));
else
newline = space = "";
do {
buf = isc_mem_get(mctx, len);
if (buf == NULL)
break;
isc_buffer_init(&buffer, buf, len);
result = dns_message_totext(message, style, 0, &buffer);
if (result == ISC_R_NOSPACE) {
isc_mem_put(mctx, buf, len);
len += 1024;
} else if (result == ISC_R_SUCCESS)
isc_log_write(dns_lctx, category, module, level,
"%s%s%s%s%.*s", description, space,
addrbuf, newline,
(int)isc_buffer_usedlength(&buffer),
buf);
} while (result == ISC_R_NOSPACE);
if (buf != NULL)
isc_mem_put(mctx, buf, len);
}
isc_result_t
dns_message_buildopt(dns_message_t *message, dns_rdataset_t **rdatasetp,
unsigned int version, isc_uint16_t udpsize,
unsigned int flags, dns_ednsopt_t *ednsopts, size_t count)
{
dns_rdataset_t *rdataset = NULL;
dns_rdatalist_t *rdatalist = NULL;
dns_rdata_t *rdata = NULL;
isc_result_t result;
unsigned int len = 0, i;
REQUIRE(DNS_MESSAGE_VALID(message));
REQUIRE(rdatasetp != NULL && *rdatasetp == NULL);
result = dns_message_gettemprdatalist(message, &rdatalist);
if (result != ISC_R_SUCCESS)
return (result);
result = dns_message_gettemprdata(message, &rdata);
if (result != ISC_R_SUCCESS)
goto cleanup;
result = dns_message_gettemprdataset(message, &rdataset);
if (result != ISC_R_SUCCESS)
goto cleanup;
rdatalist->type = dns_rdatatype_opt;
/*
* Set Maximum UDP buffer size.
*/
rdatalist->rdclass = udpsize;
/*
* Set EXTENDED-RCODE and Z to 0.
*/
rdatalist->ttl = (version << 16);
rdatalist->ttl |= (flags & 0xffff);
/*
* Set EDNS options if applicable
*/
if (count != 0U) {
isc_buffer_t *buf = NULL;
for (i = 0; i < count; i++)
len += ednsopts[i].length + 4;
if (len > 0xffffU) {
result = ISC_R_NOSPACE;
goto cleanup;
}
result = isc_buffer_allocate(message->mctx, &buf, len);
if (result != ISC_R_SUCCESS)
goto cleanup;
for (i = 0; i < count; i++) {
isc_buffer_putuint16(buf, ednsopts[i].code);
isc_buffer_putuint16(buf, ednsopts[i].length);
isc_buffer_putmem(buf, ednsopts[i].value,
ednsopts[i].length);
}
rdata->data = isc_buffer_base(buf);
rdata->length = len;
dns_message_takebuffer(message, &buf);
} else {
rdata->data = NULL;
rdata->length = 0;
}
rdata->rdclass = rdatalist->rdclass;
rdata->type = rdatalist->type;
rdata->flags = 0;
ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
result = dns_rdatalist_tordataset(rdatalist, rdataset);
RUNTIME_CHECK(result == ISC_R_SUCCESS);
*rdatasetp = rdataset;
return (ISC_R_SUCCESS);
cleanup:
if (rdata != NULL)
dns_message_puttemprdata(message, &rdata);
if (rdataset != NULL)
dns_message_puttemprdataset(message, &rdataset);
if (rdatalist != NULL)
dns_message_puttemprdatalist(message, &rdatalist);
return (result);
}