message.c revision 7153a32ae99388656620b200e6f4ba6e170a208c
/*
* 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 <dns/rdataset.h>
#include <dns/rdataclass.h>
#include <dns/rdatatype.h>
#include <dns/rdatalist.h>
#include <dns/compress.h>
#define DNS_MESSAGE_OPCODE_MASK 0x7800U
#define DNS_MESSAGE_OPCODE_SHIFT 11
#define DNS_MESSAGE_RCODE_MASK 0x000fU
#define DNS_MESSAGE_FLAG_MASK 0x8ff0U
#define DNS_MESSAGE_EDNSRCODE_MASK 0xff000000U
#define DNS_MESSAGE_EDNSRCODE_SHIFT 24
#define DNS_MESSAGE_EDNSVERSION_MASK 0x00ff0000U
#define DNS_MESSAGE_EDNSVERSION_SHIFT 16
#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.
* XXXMLG These should come from a config setting.
*/
#define SCRATCHPAD_SIZE 512
#define NAME_COUNT 8
#define RDATA_COUNT 8
#define RDATALIST_COUNT 8
#define RDATASET_COUNT RDATALIST_COUNT
/*
* "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 count;
unsigned int remaining;
}; /* dynamically sized */
static inline dns_msgblock_t *
msgblock_allocate(isc_mem_t *, unsigned int, unsigned int);
static inline void *
msgblock_internalget(dns_msgblock_t *, unsigned int);
static inline void
static inline void
/*
* Allocate a new dns_msgblock_t, and return a pointer to it. If no memory
* is free, return NULL.
*/
static inline dns_msgblock_t *
unsigned int count)
{
unsigned int length;
return (NULL);
return (block);
}
/*
* Return an element from the msgblock. If no more are available, return
* NULL.
*/
static inline void *
{
void *ptr;
return (NULL);
+ sizeof(dns_msgblock_t)
return (ptr);
}
static inline void
{
}
/*
* Release memory associated with a message block.
*/
static inline void
unsigned int sizeof_type)
{
unsigned int 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
{
if (result != ISC_R_SUCCESS)
return (DNS_R_NOMEMORY);
return (DNS_R_SUCCESS);
}
static inline isc_buffer_t *
{
return (dynbuf);
}
static inline void
{
}
static inline dns_rdata_t *
{
return (rdata);
}
return (NULL);
}
return (rdata);
}
static inline void
{
}
static inline dns_rdatalist_t *
{
return (rdatalist);
}
sizeof(dns_rdatalist_t),
return (NULL);
}
return (rdatalist);
}
static inline void
{
m->id = 0;
m->flags = 0;
m->rcode = 0;
m->opcode = 0;
m->rdclass = 0;
}
static inline void
{
unsigned int i;
for (i = 0; i < DNS_SECTION_MAX; i++) {
m->counts[i] = 0;
}
m->reserved = 0;
m->need_cctx_cleanup = 0;
}
static inline void
{
m->tsigstart = -1;
}
/*
* 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;
}
static inline void
unsigned int i;
/*
* Clean up name lists by calling the rdataset disassociate function.
*/
for (i = first_section; i < DNS_SECTION_MAX; i++) {
}
}
}
}
/*
* Free all but one (or everything) for this message. This is used by
* both dns_message_reset() and dns_message_parse().
*/
static void
{
msgresetnames(msg, 0);
}
/*
* 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.
*/
}
}
if (!everything) {
}
}
if (!everything) {
}
}
/*
* rdatalists could be empty.
*/
}
}
sizeof(dns_rdata_any_tsig_t));
}
sizeof(dns_rdata_any_tsig_t));
}
/*
* cleanup the buffer cleanup list
*/
}
/*
* Set other bits to normal default values.
*/
if (!everything)
}
{
dns_message_t *m;
unsigned int i;
|| intent == DNS_MESSAGE_INTENTRENDER);
if (m == NULL)
return(DNS_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]);
ISC_LIST_INIT(m->scratchpad);
ISC_LIST_INIT(m->cleanup);
ISC_LIST_INIT(m->rdatas);
ISC_LIST_INIT(m->rdatalists);
ISC_LIST_INIT(m->freerdata);
/*
* Ok, it is safe to allocate (and then "goto cleanup" if failure)
*/
if (result != ISC_R_SUCCESS)
goto cleanup;
&m->rdspool);
if (result != ISC_R_SUCCESS)
goto cleanup;
if (result != ISC_R_SUCCESS)
goto cleanup;
goto cleanup;
if (intent == DNS_MESSAGE_INTENTPARSE) {
goto cleanup;
}
*msgp = m;
return (DNS_R_SUCCESS);
/*
* Cleanup for error returns.
*/
isc_mempool_destroy(&m->namepool);
isc_mempool_destroy(&m->rdspool);
m->magic = 0;
return (DNS_R_NOMEMORY);
}
void
{
|| intent == DNS_MESSAGE_INTENTRENDER);
}
void
{
}
static dns_result_t
{
return (DNS_R_SUCCESS);
}
}
return (DNS_R_NOTFOUND);
}
{
}
return (DNS_R_SUCCESS);
}
}
return (DNS_R_NOTFOUND);
}
/*
* Read a name from buffer "source".
*/
static dns_result_t
{
unsigned int tries;
/*
* First try: use current buffer.
* Second try: allocate a new buffer and use that.
*/
tries = 0;
while (tries < 2) {
scratch);
if (result == DNS_R_NOSPACE) {
tries++;
if (result != DNS_R_SUCCESS)
return (result);
} else {
return (result);
}
}
INSIST(0); /* Cannot get here... */
return (DNS_R_UNEXPECTED);
}
static dns_result_t
{
unsigned int tries;
unsigned int trysize;
/*
* In dynamic update messages, the rdata can be empty.
*/
/*
* When the rdata is empty, the data pointer is never
* dereferenced, but it must still be non-NULL.
*/
return DNS_R_SUCCESS;
}
/*
* 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;
for (;;) {
scratch);
if (result == DNS_R_NOSPACE) {
if (tries == 0) {
if (trysize < SCRATCHPAD_SIZE)
} else {
if (trysize >= 65535)
return (ISC_R_NOSPACE);
/* XXX DNS_R_RRTOOLONG? */
trysize *= 2;
}
tries++;
if (result != DNS_R_SUCCESS)
return (result);
} else {
return (result);
}
}
}
static dns_result_t
{
isc_region_t r;
unsigned int count;
return (DNS_R_NOMEMORY);
/*
* Parse the name out of this packet.
*/
isc_buffer_remaining(source, &r);
if (result != DNS_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.
*/
/*
* 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 != DNS_R_SUCCESS) {
if (ISC_LIST_EMPTY(*section)) {
} else {
goto cleanup;
}
} else {
}
/*
* Get type and class.
*/
isc_buffer_remaining(source, &r);
if (r.length < 4) {
goto cleanup;
}
/*
* If this class is different than the one we already read,
* this is an error.
*/
goto cleanup;
}
/*
* Can't ask the same question twice.
*/
if (result == DNS_R_SUCCESS) {
goto cleanup;
}
/*
* Allocate a new rdatalist.
*/
goto cleanup;
}
goto cleanup;
}
/*
* Convert rdatalist to rdataset, and attach the latter to
* the name.
*/
if (result != DNS_R_SUCCESS)
goto cleanup;
}
return (DNS_R_SUCCESS);
#if 0
#endif
if (free_name)
return (result);
}
static dns_result_t
{
isc_region_t r;
unsigned int count;
unsigned int rdatalen;
return (DNS_R_NOMEMORY);
/*
* Parse the name out of this packet.
*/
isc_buffer_remaining(source, &r);
if (result != DNS_R_SUCCESS)
return (result);
/*
* 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);
return (DNS_R_UNEXPECTEDEND);
/*
* If there was no question section, we may not yet have
* established a class. Do so now.
*/
return (DNS_R_FORMERR);
}
/*
* If this class is different than the one in the question
* section, bail.
*/
&& rdtype != dns_rdatatype_tsig
&& rdtype != dns_rdatatype_opt
return (DNS_R_FORMERR);
/*
* Special type handling for TSIG and OPT.
*/
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)
return (DNS_R_FORMERR);
if (rdclass != dns_rdataclass_any)
return (DNS_R_FORMERR);
} 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.
*/
return (DNS_R_FORMERR);
}
/*
* ... now get ttl and rdatalen, and check buffer.
*/
return (DNS_R_UNEXPECTEDEND);
/*
* If we are doing a dynamic update don't bother searching
* for a name, just append this one to the end of the message.
*/
skip_search) {
if (rdtype != dns_rdatatype_opt)
} 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.
*/
/*
* If it is a new name, append to the section.
*/
if (result == DNS_R_SUCCESS) {
} else {
}
}
/*
* 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.
*/
return (DNS_R_NOMEMORY);
if (rdtype != dns_rdatatype_tsig)
else
if (result != DNS_R_SUCCESS)
return (result);
else
covers = 0;
/*
* Search name for the particular type and class.
* Skip this stage if in update mode, or this is a TSIG.
*/
else {
&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.
*/
if (result == DNS_R_NOTFOUND) {
return (DNS_R_NOMEMORY);
return (DNS_R_NOMEMORY);
if (rdtype != dns_rdatatype_opt)
}
/*
* XXXRTH Normalize TTLs.
*/
/*
* 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.
*/
/*
* If this is an OPT record, remember it. Also, set
* the extended rcode.
*/
if (rdtype == dns_rdatatype_opt) {
unsigned int ercode;
>> 20;
}
}
return (DNS_R_SUCCESS);
}
{
isc_region_t r;
msg->question_ok = 0;
isc_buffer_remaining(source, &r);
if (r.length < DNS_MESSAGE_HEADERLEN)
return (DNS_R_UNEXPECTEDEND);
/*
* -1 means no EDNS.
*/
else
if (ret != DNS_R_SUCCESS)
return (ret);
if (ret != DNS_R_SUCCESS)
return (ret);
if (ret != DNS_R_SUCCESS)
return (ret);
if (ret != DNS_R_SUCCESS)
return (ret);
isc_buffer_remaining(source, &r);
if (r.length != 0)
return (DNS_R_FORMERR);
{
if (!msg->tcp_continuation)
else
if (ret != DNS_R_SUCCESS)
return ret;
}
return (DNS_R_SUCCESS);
}
{
isc_region_t r;
/*
* Erase the contents of this buffer.
*/
/*
* Make certain there is enough for at least the header in this
* buffer.
*/
isc_buffer_available(buffer, &r);
if (result != DNS_R_SUCCESS)
return (result);
/*
* Reserve enough space for the header in this buffer.
*/
return (DNS_R_SUCCESS);
}
{
isc_region_t r, rn;
/*
* ensure that the new buffer is empty, and has enough space to
* hold the current contents.
*/
/*
* Copy the contents from the old to the new buffer.
*/
return (DNS_R_SUCCESS);
}
{
return (DNS_R_NOSPACE);
return (DNS_R_SUCCESS);
}
{
isc_region_t r;
return (DNS_R_NOSPACE);
return (DNS_R_SUCCESS);
}
{
(void)priority; /* XXXMLG implement */
(void)options; /* XXXMLG implement */
return (DNS_R_SUCCESS);
/*
* Shrink the space in the buffer by the reserved amount.
*/
total = 0;
continue;
}
count = 0;
/*
* 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 != DNS_R_SUCCESS) {
return (result);
}
}
}
return (DNS_R_SUCCESS);
}
void
{
isc_region_t r;
isc_buffer_available(target, &r);
}
{
isc_region_t r;
int result;
unsigned int count;
/*
* We have an extended rcode but are not using EDNS.
*/
return (DNS_R_FORMERR);
}
/*
* If we've got an OPT record, render it.
*/
if (result != ISC_R_SUCCESS)
return (result);
if (result != ISC_R_SUCCESS)
return (result);
/*
* Set the extended rcode.
*/
/*
* Render.
*/
count = 0;
if (result != ISC_R_SUCCESS)
return (result);
}
{
if (result != DNS_R_SUCCESS)
return (result);
0);
if (result != DNS_R_SUCCESS)
return (result);
}
msg->need_cctx_cleanup = 0;
return (DNS_R_SUCCESS);
}
{
return (DNS_R_NOMORE);
return (DNS_R_SUCCESS);
}
{
return (DNS_R_NOMORE);
return (DNS_R_SUCCESS);
}
void
dns_name_t **name)
{
}
{
/*
* 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.
*/
if (type == dns_rdatatype_any) {
} else {
}
/*
* Search through, looking for the name.
*/
if (result == DNS_R_NOTFOUND)
return (DNS_R_NXDOMAIN);
else if (result != DNS_R_SUCCESS)
return (result);
/*
* And now look for the type.
*/
if (type == dns_rdatatype_any)
return (DNS_R_SUCCESS);
if (result == DNS_R_NOTFOUND)
return (DNS_R_NXRDATASET);
return (result);
}
void
{
/*
* Unlink the name from the old section
*/
}
void
{
}
{
return (DNS_R_NOMEMORY);
return (DNS_R_SUCCESS);
}
void
{
}
{
return (DNS_R_NOMEMORY);
return (DNS_R_SUCCESS);
}
{
return (DNS_R_NOMEMORY);
return (DNS_R_SUCCESS);
}
{
return (DNS_R_NOMEMORY);
return (DNS_R_SUCCESS);
}
void
{
}
void
{
}
void
{
}
unsigned int *flagsp)
{
isc_region_t r;
unsigned int flags;
isc_buffer_remaining(&buffer, &r);
if (r.length < DNS_MESSAGE_HEADERLEN)
return (DNS_R_UNEXPECTEDEND);
return (DNS_R_SUCCESS);
}
unsigned int first_section;
return (DNS_R_FORMERR);
if (want_question_section) {
if (!msg->question_ok)
return (DNS_R_FORMERR);
} else
/*
* We now clear most flags and then set QR, ensuring that the
* reply's flags will be in a reasonable state.
*/
/*
* 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.
*/
}
return (DNS_R_SUCCESS);
}
/*
* Get the OPT record for 'msg'.
*/
}
/*
* 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.
*/
if (result != ISC_R_SUCCESS)
return (result);
}
if (result != ISC_R_SUCCESS)
return (result);
if (result != ISC_R_SUCCESS)
return (result);
return (DNS_R_SUCCESS);
}
void
{
}