message.c revision e223094b2248afa2697c531f75e6f84855638bec
/*
* Copyright (C) 1999 Internet Software Consortium.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
* ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
* CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
* ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
* SOFTWARE.
*/
/***
*** Imports
***/
#include <config.h>
#include <stddef.h>
#include <string.h>
#include <isc/assertions.h>
#include <isc/boolean.h>
#include <isc/region.h>
#include <dns/message.h>
#include <dns/rdataset.h>
#include <dns/rdata.h>
#include <dns/rdataclass.h>
#include <dns/rdatatype.h>
#include <dns/rdatalist.h>
#include <dns/compress.h>
#define MESSAGE_MAGIC 0x4d534740U /* MSG@ */
#define VALID_MESSAGE(msg) (((msg)->magic) == MESSAGE_MAGIC)
#define VALID_NAMED_SECTION(s) (((s) > DNS_SECTION_ANY) \
&& ((s) < DNS_SECTION_MAX))
#define VALID_SECTION(s) (((s) >= DNS_SECTION_ANY) \
&& ((s) < DNS_SECTION_MAX))
/*
* This is the size of each individual scratchpad buffer, and the numbers
* of various block allocations used within the server.
*/
#define SCRATCHPAD_SIZE 512
#define NAME_COUNT 16
#define RDATA_COUNT 32
#define RDATALIST_COUNT 32 /* should match RDATASET_COUNT */
#define RDATASET_COUNT 32
/*
* internal state stuff.
*/
#define TO_FROM_UNKNOWN 0
#define TO_FROM_FROMWIRE 1
#define TO_FROM_TOWIRE 2
/*
* "helper" type, which consists of a block of some type, and is linkable.
* For it to work, sizeof(dns_msgblock_t) must be a multiple of the pointer
* size, or the allocated elements will not be alligned correctly.
*/
struct dns_msgblock {
unsigned int length;
unsigned int remaining;
ISC_LINK(dns_msgblock_t) link;
}; /* dynamically sized */
static inline void
msgblock_free(isc_mem_t *, dns_msgblock_t *);
#define msgblock_get(block, type) \
((type *)msgblock_internalget(block, sizeof(type)))
static inline void *
msgblock_internalget(dns_msgblock_t *, unsigned int);
static inline void
msgblock_reset(dns_msgblock_t *, unsigned int);
static inline dns_msgblock_t *
msgblock_allocate(isc_mem_t *, unsigned int, unsigned int);
/*
* Allocate a new dns_msgblock_t, and return a pointer to it. If no memory
* is free, return NULL.
*/
static inline dns_msgblock_t *
msgblock_allocate(isc_mem_t *mctx, unsigned int sizeof_type,
unsigned int count)
{
dns_msgblock_t *block;
unsigned int length;
length = sizeof(dns_msgblock_t) + (sizeof_type * count);
block = isc_mem_get(mctx, length);
if (block == NULL)
return NULL;
block->length = length;
block->remaining = count;
ISC_LINK_INIT(block, link);
return (block);
}
/*
* Return an element from the msgblock. If no more are available, return
* NULL.
*/
static inline void *
msgblock_internalget(dns_msgblock_t *block, unsigned int sizeof_type)
{
void *ptr;
if (block->remaining == 0)
return (NULL);
block->remaining--;
ptr = (((unsigned char *)block)
+ sizeof(dns_msgblock_t)
+ (sizeof_type * block->remaining));
return (ptr);
}
static inline void
msgblock_reset(dns_msgblock_t *block, unsigned int count)
{
block->remaining = count;
}
/*
* Release memory associated with a message block.
*/
static inline void
msgblock_free(isc_mem_t *mctx, dns_msgblock_t *block)
{
isc_mem_put(mctx, block, block->length);
}
/*
* Allocate a new dynamic buffer, and attach it to this message as the
* "current" buffer. (which is always the last on the list, for our
* uses)
*/
static inline dns_result_t
newbuffer(dns_message_t *msg)
{
isc_result_t result;
isc_dynbuffer_t *dynbuf;
dynbuf = NULL;
result = isc_dynbuffer_allocate(msg->mctx, &dynbuf, SCRATCHPAD_SIZE,
ISC_BUFFERTYPE_BINARY);
if (result != ISC_R_SUCCESS)
return (DNS_R_NOMEMORY);
ISC_LIST_APPEND(msg->scratchpad, dynbuf, link);
return (DNS_R_SUCCESS);
}
static inline isc_buffer_t *
currentbuffer(dns_message_t *msg)
{
isc_dynbuffer_t *dynbuf;
dynbuf = ISC_LIST_TAIL(msg->scratchpad);
return (&dynbuf->buffer);
}
static inline void
releasename(dns_message_t *msg, dns_name_t *name)
{
msg->nextname = name;
}
static inline dns_name_t *
newname(dns_message_t *msg)
{
dns_msgblock_t *msgblock;
dns_name_t *name;
if (msg->nextname != NULL) {
name = msg->nextname;
msg->nextname = NULL;
dns_name_init(name, NULL);
return (name);
}
msgblock = ISC_LIST_HEAD(msg->names);
name = msgblock_get(msgblock, dns_name_t);
if (name == NULL) {
msgblock = msgblock_allocate(msg->mctx, sizeof(dns_name_t),
NAME_COUNT);
if (msgblock == NULL)
return (NULL);
ISC_LIST_APPEND(msg->names, msgblock, link);
name = msgblock_get(msgblock, dns_name_t);
}
dns_name_init(name, NULL);
return (name);
}
static inline void
releaserdata(dns_message_t *msg, dns_rdata_t *rdata)
{
msg->nextrdata = rdata;
}
static inline dns_rdata_t *
newrdata(dns_message_t *msg)
{
dns_msgblock_t *msgblock;
dns_rdata_t *rdata;
if (msg->nextrdata != NULL) {
rdata = msg->nextrdata;
msg->nextrdata = NULL;
return (rdata);
}
msgblock = ISC_LIST_HEAD(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);
}
return (rdata);
}
static inline void
releaserdatalist(dns_message_t *msg, dns_rdatalist_t *rdatalist)
{
msg->nextrdatalist = rdatalist;
}
static inline dns_rdatalist_t *
newrdatalist(dns_message_t *msg)
{
dns_msgblock_t *msgblock;
dns_rdatalist_t *rdatalist;
if (msg->nextrdatalist != NULL) {
rdatalist = msg->nextrdatalist;
msg->nextrdatalist = NULL;
return (rdatalist);
}
msgblock = ISC_LIST_HEAD(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);
}
return (rdatalist);
}
static inline void
releaserdataset(dns_message_t *msg, dns_rdataset_t *rdataset)
{
msg->nextrdataset = rdataset;
}
static inline dns_rdataset_t *
newrdataset(dns_message_t *msg)
{
dns_msgblock_t *msgblock;
dns_rdataset_t *rdataset;
if (msg->nextrdataset != NULL) {
rdataset = msg->nextrdataset;
msg->nextrdataset = NULL;
return (rdataset);
}
msgblock = ISC_LIST_HEAD(msg->rdatasets);
rdataset = msgblock_get(msgblock, dns_rdataset_t);
if (rdataset == NULL) {
msgblock = msgblock_allocate(msg->mctx, sizeof(dns_rdataset_t),
RDATASET_COUNT);
if (msgblock == NULL)
return (NULL);
ISC_LIST_APPEND(msg->rdatasets, msgblock, link);
rdataset = msgblock_get(msgblock, dns_rdataset_t);
}
return (rdataset);
}
/*
* Init elements to default state. Used both when allocating a new element
* and when resetting one.
*/
static inline void
msginit(dns_message_t *m)
{
unsigned int i;
m->id = 0;
m->flags = 0;
m->rcode = 0;
m->opcode = 0;
m->rdclass = 0;
for (i = 0 ; i < DNS_SECTION_MAX ; i++) {
m->cursors[i] = NULL;
m->counts[i] = NULL;
}
m->state = DNS_SECTION_ANY; /* indicate nothing parsed or rendered */
m->nextname = NULL;
m->nextrdata = NULL;
m->nextrdataset = NULL;
m->nextrdatalist = 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_dynbuffer_t *dynbuf, *next_dynbuf;
dns_rdataset_t *rds, *next_rds;
dns_name_t *name, *next_name;
unsigned int i;
/*
* Clean up name lists by calling the rdataset disassociate function.
*/
for (i = 0 ; 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);
dns_rdataset_disassociate(rds);
rds = next_rds;
}
}
}
/*
* Clean up linked lists.
*/
dynbuf = ISC_LIST_HEAD(msg->scratchpad);
INSIST(dynbuf != NULL);
if (everything == ISC_FALSE) {
isc_dynbuffer_reset(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_dynbuffer_free(msg->mctx, &dynbuf);
dynbuf = next_dynbuf;
}
msgblock = ISC_LIST_HEAD(msg->names);
INSIST(msgblock != NULL);
if (everything == ISC_FALSE) {
msgblock_reset(msgblock, NAME_COUNT);
msgblock = ISC_LIST_NEXT(msgblock, link);
}
while (msgblock != NULL) {
next_msgblock = ISC_LIST_NEXT(msgblock, link);
ISC_LIST_UNLINK(msg->names, msgblock, link);
msgblock_free(msg->mctx, msgblock);
msgblock = next_msgblock;
}
msgblock = ISC_LIST_HEAD(msg->rdatas);
INSIST(msgblock != NULL);
if (everything == ISC_FALSE) {
msgblock_reset(msgblock, RDATA_COUNT);
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);
msgblock = next_msgblock;
}
msgblock = ISC_LIST_HEAD(msg->rdatasets);
INSIST(msgblock != NULL);
if (everything == ISC_FALSE) {
msgblock_reset(msgblock, RDATASET_COUNT);
msgblock = ISC_LIST_NEXT(msgblock, link);
}
while (msgblock != NULL) {
next_msgblock = ISC_LIST_NEXT(msgblock, link);
ISC_LIST_UNLINK(msg->rdatasets, msgblock, link);
msgblock_free(msg->mctx, msgblock);
msgblock = next_msgblock;
}
if (msg->from_to_wire == DNS_MESSAGE_INTENT_PARSE) {
msgblock = ISC_LIST_HEAD(msg->rdatalists);
INSIST(msgblock != NULL);
if (everything == ISC_FALSE) {
msgblock_reset(msgblock, RDATALIST_COUNT);
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);
msgblock = next_msgblock;
}
}
/*
* Set other bits to normal default values.
*/
msginit(msg);
}
dns_result_t
dns_message_create(isc_mem_t *mctx, dns_message_t **msg, unsigned int intent)
{
dns_message_t *m;
isc_result_t iresult;
dns_msgblock_t *msgblock;
isc_dynbuffer_t *dynbuf;
unsigned int i;
REQUIRE(mctx != NULL);
REQUIRE(msg != NULL);
REQUIRE(*msg == NULL);
REQUIRE(intent == DNS_MESSAGE_INTENT_PARSE
|| intent == DNS_MESSAGE_INTENT_RENDER);
m = isc_mem_get(mctx, sizeof(dns_message_t));
if (m == NULL)
return(DNS_R_NOMEMORY);
m->magic = 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->names);
ISC_LIST_INIT(m->rdatas);
ISC_LIST_INIT(m->rdatalists);
dynbuf = NULL;
iresult = isc_dynbuffer_allocate(mctx, &dynbuf, SCRATCHPAD_SIZE,
ISC_BUFFERTYPE_BINARY);
if (iresult != ISC_R_SUCCESS)
goto cleanup1;
ISC_LIST_APPEND(m->scratchpad, dynbuf, link);
msgblock = msgblock_allocate(mctx, sizeof(dns_name_t),
NAME_COUNT);
if (msgblock == NULL)
goto cleanup2;
ISC_LIST_APPEND(m->names, msgblock, link);
msgblock = msgblock_allocate(mctx, sizeof(dns_rdata_t),
RDATA_COUNT);
if (msgblock == NULL)
goto cleanup3;
ISC_LIST_APPEND(m->rdatas, msgblock, link);
msgblock = msgblock_allocate(mctx, sizeof(dns_rdataset_t),
RDATASET_COUNT);
if (msgblock == NULL)
goto cleanup4;
ISC_LIST_APPEND(m->rdatas, msgblock, link);
if (intent == DNS_MESSAGE_INTENT_PARSE) {
msgblock = msgblock_allocate(mctx, sizeof(dns_rdatalist_t),
RDATALIST_COUNT);
if (msgblock == NULL)
goto cleanup5;
ISC_LIST_APPEND(m->rdatalists, msgblock, link);
}
return (DNS_R_SUCCESS);
/*
* Cleanup for error returns.
*/
cleanup5:
msgblock = ISC_LIST_HEAD(m->rdatasets);
msgblock_free(mctx, msgblock);
cleanup4:
msgblock = ISC_LIST_HEAD(m->rdatas);
msgblock_free(mctx, msgblock);
cleanup3:
msgblock = ISC_LIST_HEAD(m->names);
msgblock_free(mctx, msgblock);
cleanup2:
dynbuf = ISC_LIST_HEAD(m->scratchpad);
isc_dynbuffer_free(mctx, &dynbuf);
cleanup1:
m->magic = 0;
isc_mem_put(mctx, m, sizeof(dns_message_t));
return (DNS_R_NOMEMORY);
}
void
dns_message_reset(dns_message_t *msg)
{
msgreset(msg, ISC_FALSE);
}
void
dns_message_destroy(dns_message_t **xmsg)
{
dns_message_t *msg;
REQUIRE(xmsg != NULL);
REQUIRE(VALID_MESSAGE(*xmsg));
msg = *xmsg;
*xmsg = NULL;
msgreset(msg, ISC_TRUE);
msg->magic = 0;
isc_mem_put(msg->mctx, msg, sizeof(dns_message_t));
}
static dns_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_compare(curr, target) == 0) {
if (foundname != NULL)
*foundname = curr;
return (DNS_R_SUCCESS);
}
}
return (DNS_R_NOTFOUND);
}
static dns_result_t
findtype(dns_rdataset_t **rdataset, dns_name_t *name, dns_rdatatype_t type)
{
dns_rdataset_t *curr;
for (curr = ISC_LIST_TAIL(name->list) ;
curr != NULL ;
curr = ISC_LIST_PREV(curr, link)) {
if (curr->type == type) {
if (rdataset != NULL)
*rdataset = curr;
return (DNS_R_SUCCESS);
}
}
return (DNS_R_NOTFOUND);
}
/*
* Read a name from buffer "source".
*
* Assumes dns_name_init() was already called on this name.
*/
static dns_result_t
getname(dns_name_t *name, isc_buffer_t *source, dns_message_t *msg,
dns_decompress_t *dctx)
{
isc_buffer_t *scratch;
dns_result_t result;
unsigned int tries;
scratch = currentbuffer(msg);
if (dns_decompress_edns(dctx) > 1 || !dns_decompress_strict(dctx))
dns_decompress_setmethods(dctx, DNS_COMPRESS_GLOBAL);
else
dns_decompress_setmethods(dctx, DNS_COMPRESS_GLOBAL14);
tries = 0;
while (tries < 2) {
result = dns_name_fromwire(name, source, dctx, ISC_FALSE,
scratch);
if (result == DNS_R_NOSPACE) {
tries++;
result = newbuffer(msg);
if (result != DNS_R_SUCCESS)
return (result);
scratch = currentbuffer(msg);
} else {
return (result);
}
}
return (DNS_R_UNEXPECTED); /* should never get here... XXXMLG */
}
static dns_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;
dns_result_t result;
dns_rdatatype_t rdtype;
dns_rdataclass_t rdclass;
dns_namelist_t *section;
section = &msg->sections[DNS_SECTION_QUESTION];
for (count = 0 ; count < msg->counts[DNS_SECTION_QUESTION] ; count++) {
name = newname(msg);
if (name == NULL)
return (DNS_R_NOMEMORY);
/*
* Parse the name out of this packet.
*/
result = getname(name, source, msg, dctx);
if (result != DNS_R_SUCCESS)
return (result);
/*
* 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. 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.
*/
releasename(msg, name);
if (result != DNS_R_SUCCESS)
return (DNS_R_FORMERR);
name = name2;
ISC_LIST_APPEND(*section, name, link);
/*
* Get type and class.
*/
isc_buffer_remaining(source, &r);
if (r.length < 4)
return (DNS_R_UNEXPECTEDEND);
rdtype = isc_buffer_getuint16(source);
rdclass = isc_buffer_getuint16(source);
/*
* If this class is different than the one we alrady read,
* this is an error.
*/
if (msg->state == DNS_SECTION_ANY) {
msg->state = DNS_SECTION_QUESTION;
msg->rdclass = rdclass;
} else if (msg->rdclass != rdclass)
return (DNS_R_FORMERR);
/*
* Search name for the particular type and class.
* If it was found, this is an error, return FORMERR.
*/
result = findtype(NULL, name, rdtype);
if (result == DNS_R_SUCCESS)
return (DNS_R_FORMERR);
/*
* Allocate a new rdatalist.
*/
rdatalist = newrdatalist(msg);
rdataset = newrdataset(msg);
/*
* Convert rdatalist to rdataset, and attach the latter to
* the name.
*/
rdatalist->type = rdtype;
rdatalist->rdclass = rdclass;
rdatalist->ttl = 0;
ISC_LIST_INIT(rdatalist->rdata);
result = dns_rdatalist_tordataset(rdatalist, rdataset);
if (result != DNS_R_SUCCESS)
return (result);
ISC_LIST_APPEND(name->list, rdataset, link);
}
return (DNS_R_SUCCESS);
}
static dns_result_t
getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
dns_section_t sectionid)
{
isc_region_t r;
unsigned int count;
dns_name_t *name;
dns_name_t *name2;
dns_rdataset_t *rdataset;
dns_rdatalist_t *rdatalist;
dns_result_t result;
dns_rdatatype_t rdtype;
dns_rdataclass_t rdclass;
dns_ttl_t ttl;
dns_namelist_t *section;
section = &msg->sections[sectionid];
for (count = 0 ; count < msg->counts[sectionid] ; count++) {
name = newname(msg);
if (name == NULL)
return (DNS_R_NOMEMORY);
/*
* Parse the name out of this packet.
*/
result = getname(name, source, msg, dctx);
if (result != DNS_R_SUCCESS)
return (result);
/*
* 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 == DNS_R_SUCCESS) {
releasename(msg, name);
name = name2;
}
name = name2;
ISC_LIST_APPEND(msg->sections[DNS_SECTION_QUESTION],
name, link);
/*
* Get type, class, ttl, and rdatalen. Verify that at least
* rdatalen bytes remain. (Some of this is deferred to
* later.
*/
isc_buffer_remaining(source, &r);
if (r.length < 10)
return (DNS_R_UNEXPECTEDEND);
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 = sectionid;
msg->rdclass = rdclass;
} else if (msg->rdclass != rdclass)
return (DNS_R_FORMERR);
/*
* ... now get ttl and rdatalen, and check buffer.
*/
ttl = isc_buffer_getuint32(source);
rdatalen = isc_buffer_getuint16(source);
r.length -= 10;
if (r.length < rdatalen)
return (DNS_R_UNEXPECTEDEND);
/*
* Search name for the particular type and class.
* If it was found, this is an error, return FORMERR.
*/
result = findtype(&rdataset, name, rdtype);
/*
* Oh hurt me... I need to add this name to the rdatalist,
* but I have to cheat to get at that given the rdataset...
*
* This sucks. XXXMLG stop point, code below probably wrong.
*/
if (result != DNS_R_SUCCESS) {
rdataset = newrdataset(msg);
if (rdataset == NULL)
return (DNS_R_NOMEMORY);
ISC_LIST_APPEND(section, rdataset,
return (DNS_R_FORMERR);
/*
* Allocate a new rdatalist, rdata.
*/
rdatalist = newrdatalist(msg);
rdataset = newrdataset(msg);
/*
* Convert rdatalist to rdataset, and attach the latter to
* the name.
*/
rdatalist->type = rdtype;
rdatalist->rdclass = rdclass;
rdatalist->ttl = 0;
ISC_LIST_INIT(rdatalist->rdata);
result = dns_rdatalist_tordataset(rdatalist, rdataset);
if (result != DNS_R_SUCCESS)
return (result);
ISC_LIST_APPEND(name->list, rdataset, link);
}
return (DNS_R_SUCCESS);
}
dns_result_t
dns_message_parse(dns_message_t *msg, isc_buffer_t *source)
{
isc_region_t r;
dns_decompress_t dctx;
dns_result_t ret;
REQUIRE(VALID_MESSAGE(msg));
REQUIRE(source != NULL);
isc_buffer_remaining(source, &r);
if (r.length >= DNS_MESSAGE_HEADER_LEN)
return (DNS_R_UNEXPECTEDEND);
msg->id = isc_buffer_getuint16(source);
msg->flags = isc_buffer_getuint16(source);
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);
dns_decompress_init(&dctx, -1, ISC_FALSE);
ret = getquestions(source, msg, &dctx);
if (ret != DNS_R_SUCCESS)
return (ret);
ret = getsection(source, msg, &dctx, DNS_SECTION_ANSWER);
if (ret != DNS_R_SUCCESS)
return (ret);
ret = getsection(source, msg, &dctx, DNS_SECTION_AUTHORITY);
if (ret != DNS_R_SUCCESS)
return (ret);
ret = getsection(source, msg, &dctx, DNS_SECTION_ADDITIONAL);
if (ret != DNS_R_SUCCESS)
return (ret);
/*
* XXXMLG Need to check the tsig(s) here...
*/
return (DNS_R_SUCCESS);
}
dns_result_t
dns_message_renderbegin(dns_message_t *msg, isc_buffer_t *buffer)
{
REQUIRE(VALID_MESSAGE(msg));
REQUIRE(buffer != NULL);
/* XXX implement */
return (ISC_R_NOTIMPLEMENTED);
}
dns_result_t
dns_message_renderrelease(dns_message_t *msg, unsigned int space)
{
REQUIRE(VALID_MESSAGE(msg));
if (msg->reserved < space)
return (DNS_R_NOSPACE);
msg->reserved -= space;
return (DNS_R_SUCCESS);
}
dns_result_t
dns_message_renderreserve(dns_message_t *msg, isc_buffer_t *buffer,
unsigned int space)
{
isc_region_t r;
REQUIRE(VALID_MESSAGE(msg));
REQUIRE(buffer != NULL);
/*
* "space" can be positive or negative. If it is negative we are
* removing our reservation of space. If it is positive, we are
* requesting more space to be reserved.
*/
isc_buffer_available(buffer, &r);
if (r.length < (space + msg->reserved))
return (DNS_R_NOSPACE);
msg->reserved += space;
return (DNS_R_SUCCESS);
}
dns_result_t
dns_message_rendersection(dns_message_t *msg, isc_buffer_t *buffer,
dns_section_t section, unsigned int priority,
unsigned int flags)
{
REQUIRE(VALID_MESSAGE(msg));
REQUIRE(buffer != NULL);
REQUIRE(VALID_NAMED_SECTION(section));
/* XXX implement */
return (ISC_R_NOTIMPLEMENTED);
}
dns_result_t
dns_message_renderend(dns_message_t *msg, isc_buffer_t *buffer)
{
REQUIRE(VALID_MESSAGE(msg));
REQUIRE(buffer != NULL);
/* XXX implement */
return (ISC_R_NOTIMPLEMENTED);
}
dns_result_t
dns_message_firstname(dns_message_t *msg, dns_section_t section)
{
REQUIRE(VALID_MESSAGE(msg));
REQUIRE(VALID_NAMED_SECTION(section));
msg->cursors[section] = ISC_LIST_HEAD(msg->sections[section]);
if (msg->cursors[section] == NULL)
return (DNS_R_NOMORE);
return (ISC_R_SUCCESS);
}
dns_result_t
dns_message_nextname(dns_message_t *msg, dns_section_t section)
{
REQUIRE(VALID_MESSAGE(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 (DNS_R_NOMORE);
return (ISC_R_SUCCESS);
}
void
dns_message_currentname(dns_message_t *msg, dns_section_t section,
dns_name_t **name)
{
REQUIRE(VALID_MESSAGE(msg));
REQUIRE(VALID_NAMED_SECTION(section));
REQUIRE(name != NULL && name == NULL);
REQUIRE(msg->cursors[section] != NULL);
*name = msg->cursors[section];
}
dns_result_t
dns_message_findname(dns_message_t *msg, dns_section_t section,
dns_name_t *target, dns_rdatatype_t type,
dns_name_t **name, dns_rdataset_t **rdataset)
{
/*
* 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);
}
return (ISC_R_NOTIMPLEMENTED);
/* XXX implement */
}
void
dns_message_movename(dns_message_t *msg, dns_name_t *name,
dns_section_t fromsection,
dns_section_t tosection)
{
REQUIRE(msg != NULL);
REQUIRE(name != NULL);
REQUIRE(VALID_NAMED_SECTION(fromsection));
REQUIRE(VALID_NAMED_SECTION(tosection));
REQUIRE(fromsection != 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(name != NULL);
REQUIRE(VALID_NAMED_SECTION(section));
ISC_LIST_APPEND(msg->sections[section], name, link);
}