message.c revision c7868e2262d57451c7f0ce246be5f44e8c33f1e0
53f0234c3e3a845245042affb1f20a189d8791b9Automatic Updater/*
dafcb997e390efa4423883dafd100c975c4095d6Mark Andrews * Copyright (C) 1999, 2000 Internet Software Consortium.
40f53fa8d9c6a4fc38c0014495e7a42b08f52481David Lawrence *
ec5347e2c775f027573ce5648b910361aa926c01Automatic Updater * Permission to use, copy, modify, and distribute this software for any
c46bcd7c6908745e19b0b1fe18a23bbaaee553edBob Halley * purpose with or without fee is hereby granted, provided that the above
c46bcd7c6908745e19b0b1fe18a23bbaaee553edBob Halley * copyright notice and this permission notice appear in all copies.
40f53fa8d9c6a4fc38c0014495e7a42b08f52481David Lawrence *
dafcb997e390efa4423883dafd100c975c4095d6Mark Andrews * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
dafcb997e390efa4423883dafd100c975c4095d6Mark Andrews * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
dafcb997e390efa4423883dafd100c975c4095d6Mark Andrews * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
dafcb997e390efa4423883dafd100c975c4095d6Mark Andrews * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
dafcb997e390efa4423883dafd100c975c4095d6Mark Andrews * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
dafcb997e390efa4423883dafd100c975c4095d6Mark Andrews * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
dafcb997e390efa4423883dafd100c975c4095d6Mark Andrews * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
a5d43b72413db3edd6b36a58f9bdf2cf6ff692f2Bob Halley * SOFTWARE.
a27fe4c990f96bd792f2a07ca4d38c78d5b9df2cTatuya JINMEI 神明達哉 */
9c3531d72aeaad6c5f01efe6a1c82023e1379e4dDavid Lawrence
f41f183f628a148860a6d1f0070208cddd45b0c6Bob Halley/***
f41f183f628a148860a6d1f0070208cddd45b0c6Bob Halley *** Imports
f41f183f628a148860a6d1f0070208cddd45b0c6Bob Halley ***/
f41f183f628a148860a6d1f0070208cddd45b0c6Bob Halley
c2bc56dc65b4b103a5600565680eb5f33fa4c90bMark Andrews#include <config.h>
51917258dbb23cfe6069ae1cf2b7fc5aefc1e0c2Bob Halley
5d9cd26d79773c0014554a70972a24bc76aa4796Andreas Gustafsson#include <isc/mem.h>
5d9cd26d79773c0014554a70972a24bc76aa4796Andreas Gustafsson#include <isc/string.h> /* Required for HP/UX (and others?) */
6d5dcd0dc9bdbd679282b1ffc47987d24c3a1346Bob Halley#include <isc/util.h>
307d2084502eddc7ce921e5ce439aec3531d90e0Tatuya JINMEI 神明達哉#include <isc/buffer.h>
a5d43b72413db3edd6b36a58f9bdf2cf6ff692f2Bob Halley
a5d43b72413db3edd6b36a58f9bdf2cf6ff692f2Bob Halley#include <dns/dnssec.h>
0e40083fdd5445703bd30e46e5bfe7d047bced12Brian Wellington#include <dns/keyvalues.h>
c1d7e0562f6a72ecc07ab5140cf2b88183adbd08Francis Dupont#include <dns/message.h>
a27fe4c990f96bd792f2a07ca4d38c78d5b9df2cTatuya JINMEI 神明達哉#include <dns/rdata.h>
5d9cd26d79773c0014554a70972a24bc76aa4796Andreas Gustafsson#include <dns/rdatalist.h>
a3ab70dae26d009bf78b0594b2ab5eb9208f4b91Michael Graff#include <dns/rdataset.h>
60804eec9b2e36ead801e6ff7ad46586774ad828Michael Graff#include <dns/result.h>
64e41159a919b0711321fe688ca5da4f4d1b7d80Bob Halley#include <dns/tsig.h>
64e41159a919b0711321fe688ca5da4f4d1b7d80Bob Halley#include <dns/view.h>
a27fe4c990f96bd792f2a07ca4d38c78d5b9df2cTatuya JINMEI 神明達哉
5d9cd26d79773c0014554a70972a24bc76aa4796Andreas Gustafsson#define DNS_MESSAGE_OPCODE_MASK 0x7800U
a3ab70dae26d009bf78b0594b2ab5eb9208f4b91Michael Graff#define DNS_MESSAGE_OPCODE_SHIFT 11
60804eec9b2e36ead801e6ff7ad46586774ad828Michael Graff#define DNS_MESSAGE_RCODE_MASK 0x000fU
60804eec9b2e36ead801e6ff7ad46586774ad828Michael Graff#define DNS_MESSAGE_FLAG_MASK 0x8ff0U
a5d43b72413db3edd6b36a58f9bdf2cf6ff692f2Bob Halley#define DNS_MESSAGE_EDNSRCODE_MASK 0xff000000U
0111b7c5e12837bca4b97d2dd0e3989348a6a85dMichael Graff#define DNS_MESSAGE_EDNSRCODE_SHIFT 24
fc80027fb54b501cdd88461bf879d078259e0226David Lawrence#define DNS_MESSAGE_EDNSVERSION_MASK 0x00ff0000U
ca42dcc0686fefd3db202edab80e5adb09d6add5Evan Hunt#define DNS_MESSAGE_EDNSVERSION_SHIFT 16
ca42dcc0686fefd3db202edab80e5adb09d6add5Evan Hunt
113b83a9e26c1f0b9d99d9b659306907f5621750Andreas Gustafsson#define VALID_NAMED_SECTION(s) (((s) > DNS_SECTION_ANY) \
fc80027fb54b501cdd88461bf879d078259e0226David Lawrence && ((s) < DNS_SECTION_MAX))
ca42dcc0686fefd3db202edab80e5adb09d6add5Evan Hunt#define VALID_SECTION(s) (((s) >= DNS_SECTION_ANY) \
113b83a9e26c1f0b9d99d9b659306907f5621750Andreas Gustafsson && ((s) < DNS_SECTION_MAX))
113b83a9e26c1f0b9d99d9b659306907f5621750Andreas Gustafsson#define ADD_STRING(b, s) {if (strlen(s) >= \
42654cd6bbf5bc40c4374d3737e98a3dc05176f2Andreas Gustafsson isc_buffer_availablelength(b)) \
113b83a9e26c1f0b9d99d9b659306907f5621750Andreas Gustafsson return(ISC_R_NOSPACE); else \
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews isc_buffer_putstr(b, s);}
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews#define VALID_PSEUDOSECTION(s) (((s) >= DNS_PSEUDOSECTION_ANY) \
a27fe4c990f96bd792f2a07ca4d38c78d5b9df2cTatuya JINMEI 神明達哉 && ((s) < DNS_PSEUDOSECTION_MAX))
a27fe4c990f96bd792f2a07ca4d38c78d5b9df2cTatuya JINMEI 神明達哉
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews/*
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews * This is the size of each individual scratchpad buffer, and the numbers
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews * of various block allocations used within the server.
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews * XXXMLG These should come from a config setting.
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews */
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews#define SCRATCHPAD_SIZE 512
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews#define NAME_COUNT 8
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews#define RDATA_COUNT 8
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews#define RDATALIST_COUNT 8
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews#define RDATASET_COUNT RDATALIST_COUNT
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews/*
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews * Text representation of the different items, for message_totext
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews * functions.
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews */
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrewsstatic char *sectiontext[] = {
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews "QUESTION",
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews "ANSWER",
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews "AUTHORITY",
93d6dfaf66258337985427c86181f01fc51f0bb4Mark Andrews "ADDITIONAL"
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews};
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrewsstatic char *opcodetext[] = {
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews "QUERY",
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews "IQUERY",
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews "STATUS",
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews "RESERVED3",
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews "NOTIFY",
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews "UPDATE",
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews "RESERVED6",
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews "RESERVED7",
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews "RESERVED8",
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews "RESERVED9",
a5d43b72413db3edd6b36a58f9bdf2cf6ff692f2Bob Halley "RESERVED10",
fc80027fb54b501cdd88461bf879d078259e0226David Lawrence "RESERVED11",
ca42dcc0686fefd3db202edab80e5adb09d6add5Evan Hunt "RESERVED12",
e7fb847ed570dd8c1bcdacabb3d69bd81feb79aeMark Andrews "RESERVED13",
e7fb847ed570dd8c1bcdacabb3d69bd81feb79aeMark Andrews "RESERVED14",
96d3f5b8ca61f7b59365d68e26aa923d13a0846bBob Halley "RESERVED15"
a27fe4c990f96bd792f2a07ca4d38c78d5b9df2cTatuya JINMEI 神明達哉};
96d3f5b8ca61f7b59365d68e26aa923d13a0846bBob Halley
64828244e04e86dfa40f0a4f0c05f27923da499dMichael Graffstatic char *rcodetext[] = {
fc80027fb54b501cdd88461bf879d078259e0226David Lawrence "NOERROR",
5c14b1acbe0e7d244fbebbe5a16cea29233d79a3Michael Graff "FORMERR",
96e79f7ede9fd09c79ac6452ab09e4e48b288e4dMichael Graff "SERVFAIL",
60e0b5df02d27961f735a04ae62b5f09a9c17480Mark Andrews "NXDOMAIN",
f40036cbd6ba047c954dcea8705a0b9392d403d9Mark Andrews "NOTIMPL",
3e1178f56e54ec8c955056c8883718d07b833357Michael Graff "REFUSED",
6a88ed7c3f786bdf2e2ecfae16d2b5cb530281c6David Lawrence "YXDOMAIN",
a71f9502bfb791b572c22d3bd39875842de6532aMark Andrews "YXRRSET",
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff "NXRRSET",
56b40c0185bcbd4a04422acc1211b5e2d2ba8a3eBob Halley "NOTAUTH",
f4ce616539dd81322fa4db9676f42ef2e0a19031Michael Graff "NOTZONE",
fc80027fb54b501cdd88461bf879d078259e0226David Lawrence "RESERVED11",
a3ab70dae26d009bf78b0594b2ab5eb9208f4b91Michael Graff "RESERVED12",
54d47fb18b5ee4d36d6ef77e379501f6cbe2f23cDavid Lawrence "RESERVED13",
fc80027fb54b501cdd88461bf879d078259e0226David Lawrence "RESERVED14",
fc80027fb54b501cdd88461bf879d078259e0226David Lawrence "RESERVED15",
56b40c0185bcbd4a04422acc1211b5e2d2ba8a3eBob Halley "BADVERS"
93d6dfaf66258337985427c86181f01fc51f0bb4Mark Andrews};
fc80027fb54b501cdd88461bf879d078259e0226David Lawrence
53c24d27c83084d93f591d2d5eeb40a127b514c6Andreas Gustafsson
fc80027fb54b501cdd88461bf879d078259e0226David Lawrence/*
fc80027fb54b501cdd88461bf879d078259e0226David Lawrence * "helper" type, which consists of a block of some type, and is linkable.
fc80027fb54b501cdd88461bf879d078259e0226David Lawrence * For it to work, sizeof(dns_msgblock_t) must be a multiple of the pointer
fc80027fb54b501cdd88461bf879d078259e0226David Lawrence * size, or the allocated elements will not be alligned correctly.
fc80027fb54b501cdd88461bf879d078259e0226David Lawrence */
ca22760047a55f78cf6071d558b6f49f20c5202dBrian Wellingtonstruct dns_msgblock {
56b40c0185bcbd4a04422acc1211b5e2d2ba8a3eBob Halley unsigned int count;
56b40c0185bcbd4a04422acc1211b5e2d2ba8a3eBob Halley unsigned int remaining;
56b40c0185bcbd4a04422acc1211b5e2d2ba8a3eBob Halley ISC_LINK(dns_msgblock_t) link;
56b40c0185bcbd4a04422acc1211b5e2d2ba8a3eBob Halley}; /* dynamically sized */
56b40c0185bcbd4a04422acc1211b5e2d2ba8a3eBob Halley
a9eeaeb1c22d8a1de51510fa4fe260cf784f50dcAndreas Gustafssonstatic inline dns_msgblock_t *
56b40c0185bcbd4a04422acc1211b5e2d2ba8a3eBob Halleymsgblock_allocate(isc_mem_t *, unsigned int, unsigned int);
a5d43b72413db3edd6b36a58f9bdf2cf6ff692f2Bob Halley
a5d43b72413db3edd6b36a58f9bdf2cf6ff692f2Bob Halley#define msgblock_get(block, type) \
a27fe4c990f96bd792f2a07ca4d38c78d5b9df2cTatuya JINMEI 神明達哉 ((type *)msgblock_internalget(block, sizeof(type)))
a27fe4c990f96bd792f2a07ca4d38c78d5b9df2cTatuya JINMEI 神明達哉
a27fe4c990f96bd792f2a07ca4d38c78d5b9df2cTatuya JINMEI 神明達哉static inline void *
a27fe4c990f96bd792f2a07ca4d38c78d5b9df2cTatuya JINMEI 神明達哉msgblock_internalget(dns_msgblock_t *, unsigned int);
8dd17056ff254ed8ce429843bada5621f786d257Michael Graff
8dd17056ff254ed8ce429843bada5621f786d257Michael Graffstatic inline void
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrewsmsgblock_reset(dns_msgblock_t *);
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrews
64828244e04e86dfa40f0a4f0c05f27923da499dMichael Graffstatic inline void
64828244e04e86dfa40f0a4f0c05f27923da499dMichael Graffmsgblock_free(isc_mem_t *, dns_msgblock_t *, unsigned int);
a27fe4c990f96bd792f2a07ca4d38c78d5b9df2cTatuya JINMEI 神明達哉
a27fe4c990f96bd792f2a07ca4d38c78d5b9df2cTatuya JINMEI 神明達哉/*
a27fe4c990f96bd792f2a07ca4d38c78d5b9df2cTatuya JINMEI 神明達哉 * Allocate a new dns_msgblock_t, and return a pointer to it. If no memory
a27fe4c990f96bd792f2a07ca4d38c78d5b9df2cTatuya JINMEI 神明達哉 * is free, return NULL.
a27fe4c990f96bd792f2a07ca4d38c78d5b9df2cTatuya JINMEI 神明達哉 */
a27fe4c990f96bd792f2a07ca4d38c78d5b9df2cTatuya JINMEI 神明達哉static inline dns_msgblock_t *
a27fe4c990f96bd792f2a07ca4d38c78d5b9df2cTatuya JINMEI 神明達哉msgblock_allocate(isc_mem_t *mctx, unsigned int sizeof_type,
a27fe4c990f96bd792f2a07ca4d38c78d5b9df2cTatuya JINMEI 神明達哉 unsigned int count)
a27fe4c990f96bd792f2a07ca4d38c78d5b9df2cTatuya JINMEI 神明達哉{
a27fe4c990f96bd792f2a07ca4d38c78d5b9df2cTatuya JINMEI 神明達哉 dns_msgblock_t *block;
a27fe4c990f96bd792f2a07ca4d38c78d5b9df2cTatuya JINMEI 神明達哉 unsigned int length;
a27fe4c990f96bd792f2a07ca4d38c78d5b9df2cTatuya JINMEI 神明達哉
a27fe4c990f96bd792f2a07ca4d38c78d5b9df2cTatuya JINMEI 神明達哉 length = sizeof(dns_msgblock_t) + (sizeof_type * count);
a27fe4c990f96bd792f2a07ca4d38c78d5b9df2cTatuya JINMEI 神明達哉
a27fe4c990f96bd792f2a07ca4d38c78d5b9df2cTatuya JINMEI 神明達哉 block = isc_mem_get(mctx, length);
a27fe4c990f96bd792f2a07ca4d38c78d5b9df2cTatuya JINMEI 神明達哉 if (block == NULL)
a27fe4c990f96bd792f2a07ca4d38c78d5b9df2cTatuya JINMEI 神明達哉 return (NULL);
a27fe4c990f96bd792f2a07ca4d38c78d5b9df2cTatuya JINMEI 神明達哉
a27fe4c990f96bd792f2a07ca4d38c78d5b9df2cTatuya JINMEI 神明達哉 block->count = count;
a27fe4c990f96bd792f2a07ca4d38c78d5b9df2cTatuya JINMEI 神明達哉 block->remaining = count;
a27fe4c990f96bd792f2a07ca4d38c78d5b9df2cTatuya JINMEI 神明達哉
a27fe4c990f96bd792f2a07ca4d38c78d5b9df2cTatuya JINMEI 神明達哉 ISC_LINK_INIT(block, link);
a27fe4c990f96bd792f2a07ca4d38c78d5b9df2cTatuya JINMEI 神明達哉
a27fe4c990f96bd792f2a07ca4d38c78d5b9df2cTatuya JINMEI 神明達哉 return (block);
a27fe4c990f96bd792f2a07ca4d38c78d5b9df2cTatuya JINMEI 神明達哉}
93d6dfaf66258337985427c86181f01fc51f0bb4Mark Andrews
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrews/*
20eb22375f52b35105fa36263f83f116b580eba8Bob Halley * Return an element from the msgblock. If no more are available, return
20eb22375f52b35105fa36263f83f116b580eba8Bob Halley * NULL.
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews */
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrewsstatic inline void *
2d80f690e0b5f9d7494448cf844e2f178419d6e9Bob Halleymsgblock_internalget(dns_msgblock_t *block, unsigned int sizeof_type) {
2d80f690e0b5f9d7494448cf844e2f178419d6e9Bob Halley void *ptr;
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrews if (block == NULL || block->remaining == 0)
99a9539ccde4a3769fd890bdae5bcce3a3492fbaBob Halley return (NULL);
99a9539ccde4a3769fd890bdae5bcce3a3492fbaBob Halley
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews block->remaining--;
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrews
adde4612541b23d82b00741563ef84cb5192df8cMichael Graff ptr = (((unsigned char *)block)
60804eec9b2e36ead801e6ff7ad46586774ad828Michael Graff + sizeof(dns_msgblock_t)
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews + (sizeof_type * block->remaining));
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrews
f4ce616539dd81322fa4db9676f42ef2e0a19031Michael Graff return (ptr);
f4ce616539dd81322fa4db9676f42ef2e0a19031Michael Graff}
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrewsstatic inline void
fc80027fb54b501cdd88461bf879d078259e0226David Lawrencemsgblock_reset(dns_msgblock_t *block) {
fc80027fb54b501cdd88461bf879d078259e0226David Lawrence block->remaining = block->count;
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews}
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrews
adde4612541b23d82b00741563ef84cb5192df8cMichael Graff/*
9e4292a2b46bc30568bd1eb204761f7134609405David Lawrence * Release memory associated with a message block.
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews */
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrewsstatic inline void
74da616f07f038138ddd45c10fc8de0920244d12Michael Graffmsgblock_free(isc_mem_t *mctx, dns_msgblock_t *block, unsigned int sizeof_type)
74da616f07f038138ddd45c10fc8de0920244d12Michael Graff{
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews unsigned int length;
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrews
5c14b1acbe0e7d244fbebbe5a16cea29233d79a3Michael Graff length = sizeof(dns_msgblock_t) + (sizeof_type * block->count);
5c14b1acbe0e7d244fbebbe5a16cea29233d79a3Michael Graff
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews isc_mem_put(mctx, block, length);
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrews}
96e79f7ede9fd09c79ac6452ab09e4e48b288e4dMichael Graff
96e79f7ede9fd09c79ac6452ab09e4e48b288e4dMichael Graff/*
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews * Allocate a new dynamic buffer, and attach it to this message as the
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrews * "current" buffer. (which is always the last on the list, for our
adde4612541b23d82b00741563ef84cb5192df8cMichael Graff * uses)
a5d43b72413db3edd6b36a58f9bdf2cf6ff692f2Bob Halley */
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrewsstatic inline isc_result_t
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrewsnewbuffer(dns_message_t *msg, unsigned int size) {
adde4612541b23d82b00741563ef84cb5192df8cMichael Graff isc_result_t result;
a5d43b72413db3edd6b36a58f9bdf2cf6ff692f2Bob Halley isc_buffer_t *dynbuf;
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews
36ff1620e474585845d05ea9b013071d5fc345aaMark Andrews dynbuf = NULL;
adde4612541b23d82b00741563ef84cb5192df8cMichael Graff result = isc_buffer_allocate(msg->mctx, &dynbuf, size);
c9f9dd2dd2344e45cf7b95d064338d97f08f1fb8Bob Halley if (result != ISC_R_SUCCESS)
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews return (ISC_R_NOMEMORY);
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrews
adde4612541b23d82b00741563ef84cb5192df8cMichael Graff ISC_LIST_APPEND(msg->scratchpad, dynbuf, link);
a5d43b72413db3edd6b36a58f9bdf2cf6ff692f2Bob Halley return (ISC_R_SUCCESS);
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews}
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrews
adde4612541b23d82b00741563ef84cb5192df8cMichael Graffstatic inline isc_buffer_t *
9a2eda6f8e435ecf0d84502c84b1e75e2f5e9220Bob Halleycurrentbuffer(dns_message_t *msg) {
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews isc_buffer_t *dynbuf;
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrews
53c24d27c83084d93f591d2d5eeb40a127b514c6Andreas Gustafsson dynbuf = ISC_LIST_TAIL(msg->scratchpad);
53c24d27c83084d93f591d2d5eeb40a127b514c6Andreas Gustafsson INSIST(dynbuf != NULL);
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrews return (dynbuf);
adde4612541b23d82b00741563ef84cb5192df8cMichael Graff}
a5d43b72413db3edd6b36a58f9bdf2cf6ff692f2Bob Halley
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrewsstatic inline void
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrewsreleaserdata(dns_message_t *msg, dns_rdata_t *rdata) {
adde4612541b23d82b00741563ef84cb5192df8cMichael Graff ISC_LIST_PREPEND(msg->freerdata, rdata, link);
9e4292a2b46bc30568bd1eb204761f7134609405David Lawrence}
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrewsstatic inline dns_rdata_t *
adde4612541b23d82b00741563ef84cb5192df8cMichael Graffnewrdata(dns_message_t *msg) {
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews dns_msgblock_t *msgblock;
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews dns_rdata_t *rdata;
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrews
adde4612541b23d82b00741563ef84cb5192df8cMichael Graff rdata = ISC_LIST_HEAD(msg->freerdata);
9e4292a2b46bc30568bd1eb204761f7134609405David Lawrence if (rdata != NULL) {
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews ISC_LIST_UNLINK(msg->freerdata, rdata, link);
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrews return (rdata);
adde4612541b23d82b00741563ef84cb5192df8cMichael Graff }
9e4292a2b46bc30568bd1eb204761f7134609405David Lawrence
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews msgblock = ISC_LIST_TAIL(msg->rdatas);
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrews rdata = msgblock_get(msgblock, dns_rdata_t);
adde4612541b23d82b00741563ef84cb5192df8cMichael Graff if (rdata == NULL) {
3ff55a3111fe09f517218905248974b8319b2c59Mark Andrews msgblock = msgblock_allocate(msg->mctx, sizeof(dns_rdata_t),
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews RDATA_COUNT);
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrews if (msgblock == NULL)
adde4612541b23d82b00741563ef84cb5192df8cMichael Graff return (NULL);
e535faecc70425e7c9fc3faf4064792804c125a0Bob Halley
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews ISC_LIST_APPEND(msg->rdatas, msgblock, link);
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrews
adde4612541b23d82b00741563ef84cb5192df8cMichael Graff rdata = msgblock_get(msgblock, dns_rdata_t);
1ef8965366d91e02a4672c35a187d30aa4a4c72cMark Andrews }
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrews return (rdata);
60650dd537ca5e2eda953914bf5715d5e8f8b872Mark Andrews}
60650dd537ca5e2eda953914bf5715d5e8f8b872Mark Andrews
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrewsstatic inline void
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrewsreleaserdatalist(dns_message_t *msg, dns_rdatalist_t *rdatalist) {
658c72a46ee3b1193cc38e4f94121e344e0751e9Mark Andrews ISC_LIST_PREPEND(msg->freerdatalist, rdatalist, link);
658c72a46ee3b1193cc38e4f94121e344e0751e9Mark Andrews}
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrewsstatic inline dns_rdatalist_t *
6a88ed7c3f786bdf2e2ecfae16d2b5cb530281c6David Lawrencenewrdatalist(dns_message_t *msg) {
6a88ed7c3f786bdf2e2ecfae16d2b5cb530281c6David Lawrence dns_msgblock_t *msgblock;
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews dns_rdatalist_t *rdatalist;
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrews
a71f9502bfb791b572c22d3bd39875842de6532aMark Andrews rdatalist = ISC_LIST_HEAD(msg->freerdatalist);
a71f9502bfb791b572c22d3bd39875842de6532aMark Andrews if (rdatalist != NULL) {
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews ISC_LIST_UNLINK(msg->freerdatalist, rdatalist, link);
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrews return (rdatalist);
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff }
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews msgblock = ISC_LIST_TAIL(msg->rdatalists);
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrews rdatalist = msgblock_get(msgblock, dns_rdatalist_t);
e25765f84a5646fc7d54bdc73e8b353b5a78ae86James Brister if (rdatalist == NULL) {
e25765f84a5646fc7d54bdc73e8b353b5a78ae86James Brister msgblock = msgblock_allocate(msg->mctx,
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews sizeof(dns_rdatalist_t),
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrews RDATALIST_COUNT);
ae7d0a4375abaecfd5c5b0816616d9882831e69bMichael Graff if (msgblock == NULL)
dbb75545040bd612809c475d9f84affed56d417aBrian Wellington return (NULL);
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrews ISC_LIST_APPEND(msg->rdatalists, msgblock, link);
f40036cbd6ba047c954dcea8705a0b9392d403d9Mark Andrews
f40036cbd6ba047c954dcea8705a0b9392d403d9Mark Andrews rdatalist = msgblock_get(msgblock, dns_rdatalist_t);
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews }
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrews
60e0b5df02d27961f735a04ae62b5f09a9c17480Mark Andrews return (rdatalist);
60e0b5df02d27961f735a04ae62b5f09a9c17480Mark Andrews}
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrewsstatic inline void
ca22760047a55f78cf6071d558b6f49f20c5202dBrian Wellingtonmsginitheader(dns_message_t *m) {
ca22760047a55f78cf6071d558b6f49f20c5202dBrian Wellington m->id = 0;
f4cbe536b11da614fe05aeaeff41e324854cda7bMark Andrews m->flags = 0;
9f7d51ee3290e2a064d71016a6bd555b47134a7cMark Andrews m->rcode = 0;
b6d52ee5bea1b9d9074698e693b49ce96edff47bMark Andrews m->opcode = 0;
5d9cd26d79773c0014554a70972a24bc76aa4796Andreas Gustafsson m->rdclass = 0;
d3397876060db0baa38432d90283eda6097f608dBob Halley}
d3397876060db0baa38432d90283eda6097f608dBob Halley
d3397876060db0baa38432d90283eda6097f608dBob Halleystatic inline void
3d7e707dd6059735e48d54af7dfc96927e22ca9aBob Halleymsginitprivate(dns_message_t *m) {
fd088dc8c8e63f495c4d0257f21849542a582c1eDavid Lawrence unsigned int i;
44aae046c38e796e581110b7ecdf4478167d684dBob Halley
a27fe4c990f96bd792f2a07ca4d38c78d5b9df2cTatuya JINMEI 神明達哉 for (i = 0; i < DNS_SECTION_MAX; i++) {
e2ab74e3bf3c7604112d064c849e621e3f519eccJames Brister m->cursors[i] = NULL;
66870de5231491d8132b98159734a62b1fa12f1fAndreas Gustafsson m->counts[i] = 0;
66870de5231491d8132b98159734a62b1fa12f1fAndreas Gustafsson }
37aab7fc211828df0358628f119ef91140c6f2c9William King m->opt = NULL;
e582897ab49fe449d606fa14e0e4bbb8124099ffWilliam King m->sig0 = NULL;
37aab7fc211828df0358628f119ef91140c6f2c9William King m->sig0name = NULL;
7bdfc3914aaeeeea66918c735619f7592b8a2c39William King m->tsigset = NULL;
37aab7fc211828df0358628f119ef91140c6f2c9William King m->tsigname = NULL;
m->state = DNS_SECTION_ANY; /* indicate nothing parsed or rendered */
m->opt_reserved = 0;
m->reserved = 0;
m->buffer = NULL;
m->need_cctx_cleanup = 0;
}
static inline void
msginittsig(dns_message_t *m) {
m->tsigstatus = m->querytsigstatus = dns_rcode_noerror;
m->tsig = m->querytsig = NULL;
m->tsigkey = NULL;
m->tsigctx = NULL;
m->sigstart = -1;
m->sig0key = NULL;
m->sig0status = dns_rcode_noerror;
m->query = NULL;
m->saved = NULL;
}
/*
* 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;
}
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;
}
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;
}
}
static void
msgresetsigs(dns_message_t *msg) {
if (msg->tsigset != NULL) {
INSIST(dns_rdataset_isassociated(msg->tsigset));
INSIST(msg->namepool != NULL);
dns_rdataset_disassociate(msg->tsigset);
isc_mempool_put(msg->rdspool, msg->tsigset);
isc_mempool_put(msg->namepool, msg->tsigname);
msg->tsigset = NULL;
msg->tsigname = 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)
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_parse().
*/
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);
/*
* 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);
INSIST(msgblock != NULL);
if (!everything) {
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;
}
if (msg->need_cctx_cleanup == 1)
dns_compress_invalidate(&msg->cctx);
if (msg->tsig != NULL) {
dns_rdata_freestruct(msg->tsig);
isc_mem_put(msg->mctx, msg->tsig,
sizeof(dns_rdata_any_tsig_t));
msg->tsig = NULL;
}
if (msg->querytsig != NULL) {
dns_rdata_freestruct(msg->querytsig);
isc_mem_put(msg->mctx, msg->querytsig,
sizeof(dns_rdata_any_tsig_t));
msg->querytsig = NULL;
}
if (msg->tsigkey != NULL) {
dns_tsigkey_free(&msg->tsigkey);
msg->tsigkey = NULL;
}
if (msg->query != NULL) {
isc_mem_put(msg->mctx, msg->query->base, msg->query->length);
isc_mem_put(msg->mctx, msg->query, sizeof(isc_region_t));
msg->query = NULL;
}
if (msg->saved != NULL) {
isc_mem_put(msg->mctx, msg->saved->base, msg->saved->length);
isc_mem_put(msg->mctx, msg->saved, sizeof(isc_region_t));
msg->saved = NULL;
}
/*
* 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);
}
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;
dns_msgblock_t *msgblock;
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 = 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->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_setfillcount(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_setfillcount(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);
msgblock = msgblock_allocate(mctx, sizeof(dns_rdata_t),
RDATA_COUNT);
if (msgblock == NULL)
goto cleanup;
ISC_LIST_APPEND(m->rdatas, msgblock, link);
if (intent == DNS_MESSAGE_INTENTPARSE) {
msgblock = msgblock_allocate(mctx, sizeof(dns_rdatalist_t),
RDATALIST_COUNT);
if (msgblock == NULL)
goto cleanup;
ISC_LIST_APPEND(m->rdatalists, msgblock, link);
}
*msgp = m;
return (ISC_R_SUCCESS);
/*
* Cleanup for error returns.
*/
cleanup:
msgblock = ISC_LIST_HEAD(m->rdatas);
if (msgblock != NULL)
msgblock_free(mctx, msgblock, sizeof(dns_rdata_t));
dynbuf = ISC_LIST_HEAD(m->scratchpad);
if (dynbuf != NULL)
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_put(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_put(msg->mctx, msg, sizeof(dns_message_t));
}
static isc_result_t
findname(dns_name_t **foundname, dns_name_t *target, unsigned int attributes,
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) &&
(curr->attributes & attributes) == attributes) {
if (foundname != NULL)
*foundname = 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;
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) {
dns_name_init(name, NULL);
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);
} 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;
/*
* In dynamic update messages, the rdata can be empty.
*/
if (msg->opcode == dns_opcode_update && rdatalen == 0) {
/*
* When the rdata is empty, the data pointer is never
* dereferenced, but it must still be non-NULL.
*/
rdata->data = (unsigned char *)"";
rdata->length = 0;
rdata->rdclass = rdclass;
rdata->type = rdtype;
return (ISC_R_SUCCESS);
}
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, ISC_FALSE,
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);
}
}
}
static isc_result_t
getquestions(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx)
{
isc_region_t r;
unsigned int count;
dns_name_t *name;
dns_name_t *name2;
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;
section = &msg->sections[DNS_SECTION_QUESTION];
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;
/*
* 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, 0, 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)) {
ISC_LIST_APPEND(*section, name, link);
free_name = ISC_FALSE;
} else {
result = DNS_R_FORMERR;
goto cleanup;
}
} 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) {
result = DNS_R_FORMERR;
goto cleanup;
}
/*
* If this is a type that cannot occur in a question section,
* return failure.
*/
if (dns_rdatatype_notquestion(rdtype)) {
result = DNS_R_FORMERR;
goto cleanup;
}
/*
* Can't ask the same question twice.
*/
result = dns_message_findtype(name, rdtype, 0, NULL);
if (result == ISC_R_SUCCESS) {
result = DNS_R_FORMERR;
goto cleanup;
}
/*
* 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->covers = 0;
rdatalist->rdclass = rdclass;
rdatalist->ttl = 0;
ISC_LIST_INIT(rdatalist->rdata);
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;
}
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_result_t
getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
dns_section_t sectionid, isc_boolean_t preserve_order)
{
isc_region_t r;
unsigned int count, rdatalen, attributes;
dns_name_t *name;
dns_name_t *name2;
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;
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_name = ISC_FALSE;
free_rdataset = ISC_FALSE;
name = isc_mempool_get(msg->namepool);
if (name == NULL)
return (ISC_R_NOMEMORY);
free_name = ISC_TRUE;
/*
* 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) {
if (rdclass == 0 || rdclass == dns_rdataclass_any) {
result = DNS_R_FORMERR;
goto cleanup;
}
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_key /* XXX in a TKEY query */
&& rdtype != dns_rdatatype_sig /* XXX SIG(0) */
&& msg->rdclass != rdclass) {
result = DNS_R_FORMERR;
goto cleanup;
}
/*
* 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, and switch sections for
* the rest of this rdata.
*/
if ((sectionid != DNS_SECTION_ADDITIONAL)
|| (rdclass != dns_rdataclass_any)) {
result = DNS_R_FORMERR;
goto cleanup;
}
if (msg->tsigset != NULL) {
result = DNS_R_FORMERR;
goto cleanup;
}
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) {
result = DNS_R_FORMERR;
goto cleanup;
}
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.
* 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) {
result = DNS_R_FORMERR;
goto cleanup;
}
}
/*
* ... 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;
}
attributes = 0;
if (rdtype != dns_rdatatype_tsig) {
if (rdtype == dns_rdatatype_cname) {
name->attributes |= DNS_NAMEATTR_CNAME;
attributes = DNS_NAMEATTR_CNAME;
skip_name_search = ISC_TRUE;
} else if (rdtype == dns_rdatatype_dname) {
name->attributes |= DNS_NAMEATTR_DNAME;
attributes = DNS_NAMEATTR_DNAME;
skip_name_search = ISC_TRUE;
}
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;
if (rdtype == dns_rdatatype_sig && rdata->length > 0) {
covers = dns_rdata_covers(rdata);
if (covers == dns_rdatatype_cname)
attributes = DNS_NAMEATTR_CNAME;
else if (covers == dns_rdatatype_dname)
attributes = DNS_NAMEATTR_DNAME;
else if (covers == 0 &&
sectionid == DNS_SECTION_ADDITIONAL)
{
if (msg->sig0 != NULL) {
result = DNS_R_FORMERR;
goto cleanup;
}
msg->sigstart = recstart;
skip_name_search = ISC_TRUE;
skip_type_search = ISC_TRUE;
}
} else
covers = 0;
/*
* If we are doing a dynamic update 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 &&
!(rdtype == dns_rdatatype_sig && covers == 0 &&
sectionid == DNS_SECTION_ADDITIONAL))
{
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, attributes, 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 TSIG.
*/
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)) {
result = DNS_R_FORMERR;
goto cleanup;
}
rdataset = NULL;
result = dns_message_findtype(name, 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)) {
result = DNS_R_FORMERR;
goto cleanup;
}
}
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;
ISC_LIST_INIT(rdatalist->rdata);
dns_rdataset_init(rdataset);
dns_rdatalist_tordataset(rdatalist, rdataset);
if (rdtype != dns_rdatatype_opt) {
ISC_LIST_APPEND(name->list, rdataset, link);
free_rdataset = ISC_FALSE;
}
}
/*
* Minimize TTLs.
*
* Section 5.2 of RFC 2181 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;
}
/*
* XXXMLG Perform a totally ugly hack here to pull
* the rdatalist out of the private field in the rdataset,
* and append this rdata to the rdatalist's linked list
* of rdata.
*/
rdatalist = (dns_rdatalist_t *)(rdataset->private1);
ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
/*
* If this is an OPT record, remember it. Also, set
* the extended rcode.
*/
if (rdtype == dns_rdatatype_opt) {
unsigned int ercode;
msg->opt = rdataset;
rdataset = NULL;
free_rdataset = ISC_FALSE;
ercode = (msg->opt->ttl & DNS_MESSAGE_EDNSRCODE_MASK)
>> 20;
msg->rcode |= ercode;
isc_mempool_put(msg->namepool, name);
free_name = ISC_FALSE;
}
/*
* If this is an SIG(0) or TSIG record, remember it.
*/
if (rdtype == dns_rdatatype_sig && covers == 0 &&
sectionid == DNS_SECTION_ADDITIONAL)
{
msg->sig0 = rdataset;
msg->sig0name = name;
rdataset = NULL;
free_rdataset = ISC_FALSE;
free_name = ISC_FALSE;
}
else if (rdtype == dns_rdatatype_tsig) {
msg->tsigset = rdataset;
msg->tsigname = name;
rdataset = NULL;
free_rdataset = ISC_FALSE;
free_name = ISC_FALSE;
}
INSIST(free_name == ISC_FALSE);
INSIST(free_rdataset == ISC_FALSE);
}
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,
isc_boolean_t preserve_order)
{
isc_region_t r;
dns_decompress_t dctx;
isc_result_t ret;
isc_uint16_t tmpflags;
isc_buffer_t origsource;
REQUIRE(DNS_MESSAGE_VALID(msg));
REQUIRE(source != NULL);
REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTPARSE);
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 = (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, ISC_FALSE);
dns_decompress_setmethods(&dctx, DNS_COMPRESS_GLOBAL14);
ret = getquestions(source, msg, &dctx);
if (ret != ISC_R_SUCCESS)
return (ret);
msg->question_ok = 1;
ret = getsection(source, msg, &dctx, DNS_SECTION_ANSWER,
preserve_order);
if (ret != ISC_R_SUCCESS)
return (ret);
ret = getsection(source, msg, &dctx, DNS_SECTION_AUTHORITY,
preserve_order);
if (ret != ISC_R_SUCCESS)
return (ret);
ret = getsection(source, msg, &dctx, DNS_SECTION_ADDITIONAL,
preserve_order);
if (ret != ISC_R_SUCCESS)
return (ret);
isc_buffer_remainingregion(source, &r);
if (r.length != 0)
return (DNS_R_FORMERR);
if (msg->tsigset != NULL || msg->tsigkey != NULL ||
msg->sig0 != NULL) {
msg->saved = isc_mem_get(msg->mctx, sizeof(isc_region_t));
if (msg->saved == NULL)
return (ISC_R_NOMEMORY);
isc_buffer_usedregion(&origsource, &r);
msg->saved->length = r.length;
msg->saved->base = isc_mem_get(msg->mctx, msg->saved->length);
if (msg->saved->base == NULL) {
isc_mem_put(msg->mctx, msg->saved,
sizeof(isc_region_t));
msg->saved = NULL;
return (ISC_R_NOMEMORY);
}
memcpy(msg->saved->base, r.base, msg->saved->length);
}
return (ISC_R_SUCCESS);
}
isc_result_t
dns_message_renderbegin(dns_message_t *msg, isc_buffer_t *buffer) {
isc_region_t r;
isc_result_t result;
REQUIRE(DNS_MESSAGE_VALID(msg));
REQUIRE(buffer != NULL);
REQUIRE(msg->buffer == NULL);
REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER);
/*
* 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);
REQUIRE(r.length >= DNS_MESSAGE_HEADERLEN);
result = dns_compress_init(&msg->cctx, -1, msg->mctx);
if (result != ISC_R_SUCCESS)
return (result);
msg->need_cctx_cleanup = 1;
/*
* 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);
memcpy(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(msg->buffer != NULL);
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));
REQUIRE(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) {
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:
case dns_rdatatype_a6:
pass_needed = 3;
break;
case dns_rdatatype_sig:
case dns_rdatatype_key:
pass_needed = 2;
break;
default:
pass_needed = 1;
}
if (pass_needed >= pass)
return (ISC_FALSE);
return (ISC_TRUE);
}
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;
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)
pass = 3;
else
pass = 1;
/*
* Shrink the space in the buffer by the reserved amount.
*/
msg->buffer->length -= msg->reserved;
total = 0;
do {
name = ISC_LIST_HEAD(*section);
if (name == NULL)
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))
goto next;
st = *(msg->buffer);
count = 0;
result = dns_rdataset_towire(rdataset, name,
&msg->cctx,
msg->buffer,
&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 arbitary 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 (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 pending data, ensure
* that the AD bit is not set.
*/
if (rdataset->trust == dns_trust_pending &&
(sectionid == DNS_SECTION_ANSWER ||
sectionid == DNS_SECTION_AUTHORITY))
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'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, &count);
msg->counts[DNS_SECTION_ADDITIONAL] += count;
if (result != ISC_R_SUCCESS)
return (result);
}
if (msg->tsigkey != NULL) {
REQUIRE(msg->tsig == NULL);
result = dns_tsig_sign(msg);
if (result != ISC_R_SUCCESS)
return (result);
count = 0;
result = dns_rdataset_towire(msg->tsigset, msg->tsigname,
&msg->cctx, msg->buffer, &count);
msg->counts[DNS_SECTION_ADDITIONAL] += count;
if (result != ISC_R_SUCCESS)
return (result);
}
else if (msg->sig0key != NULL) {
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, &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 */
dns_compress_invalidate(&msg->cctx);
msg->need_cctx_cleanup = 0;
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;
}
}
}
}
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;
unsigned int attributes;
dns_rdatatype_t atype;
/*
* 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);
}
/*
* Figure out what attributes we should look for.
*/
if (type == dns_rdatatype_sig)
atype = covers;
else
atype = type;
attributes = 0;
if (atype == dns_rdatatype_cname)
attributes = DNS_NAMEATTR_CNAME;
else if (atype == dns_rdatatype_cname)
attributes = DNS_NAMEATTR_DNAME;
/*
* Search through, looking for the name.
*/
result = findname(&foundname, target, attributes,
&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);
}
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);
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);
isc_mempool_put(msg->namepool, *item);
*item = NULL;
}
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_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 first_section;
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 (want_question_section) {
if (!msg->question_ok)
return (DNS_R_FORMERR);
first_section = DNS_SECTION_ANSWER;
} else
first_section = DNS_SECTION_QUESTION;
msg->from_to_wire = DNS_MESSAGE_INTENTRENDER;
msgresetnames(msg, first_section);
msgresetopt(msg);
msgresetsigs(msg);
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 information for later use, if there is
* any. This only happens once - that is, if dns_message_reply
* has already moved the variables, this has no effect.
*/
if (msg->tsig != NULL) {
msg->querytsig = msg->tsig;
msg->tsig = NULL;
msg->querytsigstatus = msg->tsigstatus;
msg->tsigstatus = dns_rcode_noerror;
}
if (msg->saved != NULL) {
msg->query = msg->saved;
msg->saved = NULL;
}
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;
/*
* 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->buffer != NULL);
REQUIRE(msg->state == DNS_SECTION_ANY);
msgresetopt(msg);
msgresetsigs(msg);
result = dns_rdataset_first(opt);
if (result != ISC_R_SUCCESS)
return (result);
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;
return (result);
}
msg->opt = opt;
return (ISC_R_SUCCESS);
}
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);
*owner = msg->tsigname;
return (msg->tsigset);
}
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));
if (msg->sig0 != 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);
}
else {
*owner = NULL;
return (NULL);
}
}
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_region_t r;
isc_result_t result = ISC_R_SUCCESS;
REQUIRE(DNS_MESSAGE_VALID(msg));
REQUIRE(signer != NULL);
REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTPARSE);
if ((msg->tsig == NULL || msg->tsigkey == 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_t rdata;
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, msg->mctx);
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_toregion(&sig.signer, &r);
dns_name_fromregion(signer, &r);
dns_rdata_freestruct(&sig);
}
else {
dns_name_t *identity;
if (msg->tsigstatus != dns_rcode_noerror)
result = DNS_R_TSIGVERIFYFAILURE;
else if (msg->tsig->error != dns_rcode_noerror)
result = DNS_R_TSIGERRORSET;
else
result = ISC_R_SUCCESS;
identity = dns_tsigkey_identity(msg->tsigkey);
if (identity == NULL) {
if (result == ISC_R_SUCCESS)
result = DNS_R_NOIDENTITY;
identity = &msg->tsigkey->name;
}
dns_name_toregion(identity, &r);
dns_name_fromregion(signer, &r);
}
return (result);
}
isc_result_t
dns_message_checksig(dns_message_t *msg, dns_view_t *view) {
isc_buffer_t b, msgb;
REQUIRE(DNS_MESSAGE_VALID(msg));
REQUIRE(view != NULL);
if (msg->tsigkey == NULL && msg->tsigset == NULL && msg->sig0 == NULL)
return (ISC_R_SUCCESS);
INSIST(msg->saved != NULL);
isc_buffer_init(&msgb, msg->saved->base, msg->saved->length);
isc_buffer_add(&msgb, msg->saved->length);
if (msg->tsigkey != NULL || msg->tsigset != NULL)
return (dns_view_checksig(view, &msgb, msg));
else {
dns_rdata_t rdata;
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);
result = dns_view_simplefind(view, &sig.signer,
dns_rdatatype_key, 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_rdataset_current(&keyset, &rdata);
isc_buffer_init(&b, rdata.data, rdata.length);
isc_buffer_add(&b, rdata.length);
/*
* XXXBEW should actually pass in the key name,
* but it's not used anyway.
*/
result = dst_key_fromdns("", &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,
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 no_rdata;
isc_boolean_t omit_final_dot;
REQUIRE(DNS_MESSAGE_VALID(msg));
REQUIRE(target != NULL);
REQUIRE(VALID_SECTION(section));
omit_final_dot = ISC_TF((flags & DNS_MESSAGETEXTFLAG_OMITDOT) != 0);
if (ISC_LIST_EMPTY(msg->sections[section]))
return ISC_R_SUCCESS;
if (section == DNS_SECTION_QUESTION)
no_rdata = ISC_TRUE;
else
no_rdata = ISC_FALSE;
if ((flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0) {
ADD_STRING(target, ";; ");
ADD_STRING(target, sectiontext[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 (no_rdata)
ADD_STRING(target, ";");
result = dns_rdataset_totext(rdataset, name,
omit_final_dot,
no_rdata,
target);
if (result != ISC_R_SUCCESS)
return (result);
}
result = dns_message_nextname(msg, section);
} while (result == ISC_R_SUCCESS);
ADD_STRING(target, "\n");
if (result == ISC_R_NOMORE)
result = ISC_R_SUCCESS;
return (result);
}
isc_result_t
dns_message_pseudosectiontotext(dns_message_t *msg,
dns_pseudosection_t section,
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_boolean_t omit_final_dot;
REQUIRE(DNS_MESSAGE_VALID(msg));
REQUIRE(target != NULL);
REQUIRE(VALID_PSEUDOSECTION(section));
omit_final_dot = ISC_TF((flags & DNS_MESSAGETEXTFLAG_OMITDOT) != 0);
switch (section) {
case DNS_PSEUDOSECTION_OPT:
ps = dns_message_getopt(msg);
if (ps == NULL)
return (ISC_R_SUCCESS);
if ((flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0)
ADD_STRING(target, ";; OPT PSEUDOSECTION:\n");
ADD_STRING(target, "; EDNS: version: ");
sprintf(buf, "%4u",
(unsigned int)((ps->ttl &
0x00ff0000 >> 16)));
ADD_STRING(target, buf);
ADD_STRING(target, ", udp=");
sprintf(buf, "%7u\n",
(unsigned int)ps->rdclass);
ADD_STRING(target, buf);
return (ISC_R_SUCCESS);
case DNS_PSEUDOSECTION_TSIG:
ps = dns_message_gettsig(msg, &name);
if (ps == NULL)
return (ISC_R_SUCCESS);
if ((flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0)
ADD_STRING(target, ";; TSIG PSEUDOSECTION:\n");
result = dns_rdataset_totext(ps, name, omit_final_dot,
ISC_FALSE, target);
ADD_STRING(target, "\n");
return (result);
case DNS_PSEUDOSECTION_SIG0:
ps = dns_message_getsig0(msg, &name);
if (ps == NULL)
return (ISC_R_SUCCESS);
if ((flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0)
ADD_STRING(target, ";; SIG0 PSEUDOSECTION:\n");
result = dns_rdataset_totext(ps, name, omit_final_dot,
ISC_FALSE, target);
ADD_STRING(target, "\n");
return (result);
}
return (ISC_R_UNEXPECTED);
}
isc_result_t
dns_message_totext(dns_message_t *msg, dns_messagetextflag_t flags,
isc_buffer_t *target) {
char buf[sizeof "1234567890"];
isc_result_t result;
REQUIRE(DNS_MESSAGE_VALID(msg));
REQUIRE(target != NULL);
if ((flags & DNS_MESSAGETEXTFLAG_NOHEADERS) == 0) {
ADD_STRING(target, ";; ->>HEADER<<- opcode: ");
ADD_STRING(target, opcodetext[msg->opcode]);
ADD_STRING(target, ", status: ");
ADD_STRING(target, rcodetext[msg->rcode]);
ADD_STRING(target, ", id: ");
sprintf(buf, "%6u", msg->id);
ADD_STRING(target, buf);
ADD_STRING(target, "\n;; 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 ");
ADD_STRING(target, "\n; QUESTION: ");
sprintf(buf, "%3u", msg->counts[DNS_SECTION_QUESTION]);
ADD_STRING(target, buf);
ADD_STRING(target, ", ANSWER: ");
sprintf(buf, "%3u", msg->counts[DNS_SECTION_ANSWER]);
ADD_STRING(target, buf);
ADD_STRING(target, ", AUTHORITY: ");
sprintf(buf, "%3u", msg->counts[DNS_SECTION_AUTHORITY]);
ADD_STRING(target, buf);
ADD_STRING(target, ", ADDITIONAL: ");
sprintf(buf, "%3u", msg->counts[DNS_SECTION_ADDITIONAL]);
ADD_STRING(target, buf);
ADD_STRING(target, "\n");
}
result = dns_message_pseudosectiontotext(msg,
DNS_PSEUDOSECTION_OPT,
flags, target);
if (result != ISC_R_SUCCESS)
return (result);
result = dns_message_sectiontotext(msg, DNS_SECTION_QUESTION,
flags, target);
if (result != ISC_R_SUCCESS)
return (result);
result = dns_message_sectiontotext(msg, DNS_SECTION_ANSWER,
flags, target);
if (result != ISC_R_SUCCESS)
return (result);
result = dns_message_sectiontotext(msg, DNS_SECTION_AUTHORITY,
flags, target);
if (result != ISC_R_SUCCESS)
return (result);
result = dns_message_sectiontotext(msg, DNS_SECTION_ADDITIONAL,
flags, target);
if (result != ISC_R_SUCCESS)
return (result);
result = dns_message_pseudosectiontotext(msg,
DNS_PSEUDOSECTION_TSIG,
flags, target);
if (result != ISC_R_SUCCESS)
return (result);
result = dns_message_pseudosectiontotext(msg,
DNS_PSEUDOSECTION_SIG0,
flags, target);
if (result != ISC_R_SUCCESS)
return (result);
return (ISC_R_SUCCESS);
}