message.c 38.3 KB
Newer Older
Michael Graff's avatar
Michael Graff committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
/*
 * 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>
Michael Graff's avatar
Michael Graff committed
28 29
#include <isc/boolean.h>
#include <isc/region.h>
30
#include <isc/types.h>
Michael Graff's avatar
Michael Graff committed
31 32 33

#include <dns/message.h>
#include <dns/rdataset.h>
Michael Graff's avatar
Michael Graff committed
34 35 36 37 38
#include <dns/rdata.h>
#include <dns/rdataclass.h>
#include <dns/rdatatype.h>
#include <dns/rdatalist.h>
#include <dns/compress.h>
Michael Graff's avatar
Michael Graff committed
39

40
#define DNS_MESSAGE_OPCODE_MASK		0x7800U
Michael Graff's avatar
Michael Graff committed
41 42 43
#define DNS_MESSAGE_OPCODE_SHIFT	    11
#define DNS_MESSAGE_RCODE_MASK		0x000fU
#define DNS_MESSAGE_FLAG_MASK		0x8ff0U
Michael Graff's avatar
Michael Graff committed
44 45 46 47 48 49 50 51 52

#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.
Michael Graff's avatar
Michael Graff committed
53
 * XXXMLG These should come from a config setting.
Michael Graff's avatar
Michael Graff committed
54
 */
Michael Graff's avatar
Michael Graff committed
55
#define SCRATCHPAD_SIZE		512
56 57 58 59
#define NAME_COUNT		  8
#define RDATA_COUNT		  8
#define RDATALIST_COUNT		  8
#define RDATASET_COUNT		 RDATALIST_COUNT
Michael Graff's avatar
Michael Graff committed
60 61 62 63 64 65 66

/*
 * "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 {
Michael Graff's avatar
Michael Graff committed
67
	unsigned int			count;
Michael Graff's avatar
Michael Graff committed
68 69 70
	unsigned int			remaining;
	ISC_LINK(dns_msgblock_t)	link;
}; /* dynamically sized */
Michael Graff's avatar
Michael Graff committed
71

Michael Graff's avatar
Michael Graff committed
72 73
static inline dns_msgblock_t *
msgblock_allocate(isc_mem_t *, unsigned int, unsigned int);
74

Michael Graff's avatar
Michael Graff committed
75
#define msgblock_get(block, type) \
Michael Graff's avatar
Michael Graff committed
76
	((type *)msgblock_internalget(block, sizeof(type)))
Michael Graff's avatar
Michael Graff committed
77 78

static inline void *
Michael Graff's avatar
Michael Graff committed
79 80 81
msgblock_internalget(dns_msgblock_t *, unsigned int);

static inline void
Michael Graff's avatar
Michael Graff committed
82
msgblock_reset(dns_msgblock_t *);
Michael Graff's avatar
Michael Graff committed
83

Michael Graff's avatar
Michael Graff committed
84 85
static inline void
msgblock_free(isc_mem_t *, dns_msgblock_t *, unsigned int);
Michael Graff's avatar
Michael Graff committed
86 87 88 89 90 91

/*
 * Allocate a new dns_msgblock_t, and return a pointer to it.  If no memory
 * is free, return NULL.
 */
static inline dns_msgblock_t *
Michael Graff's avatar
Michael Graff committed
92 93
msgblock_allocate(isc_mem_t *mctx, unsigned int sizeof_type,
		  unsigned int count)
Michael Graff's avatar
Michael Graff committed
94 95 96 97 98 99 100 101
{
	dns_msgblock_t *block;
	unsigned int length;

	length = sizeof(dns_msgblock_t) + (sizeof_type * count);

	block = isc_mem_get(mctx, length);
	if (block == NULL)
Michael Graff's avatar
Michael Graff committed
102
		return (NULL);
Michael Graff's avatar
Michael Graff committed
103

Michael Graff's avatar
Michael Graff committed
104
	block->count = count;
Michael Graff's avatar
Michael Graff committed
105 106 107 108 109 110 111 112 113 114 115 116
	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 *
Michael Graff's avatar
Michael Graff committed
117
msgblock_internalget(dns_msgblock_t *block, unsigned int sizeof_type)
Michael Graff's avatar
Michael Graff committed
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
{
	void *ptr;

	if (block->remaining == 0)
		return (NULL);

	block->remaining--;

	ptr = (((unsigned char *)block)
	       + sizeof(dns_msgblock_t)
	       + (sizeof_type * block->remaining));

	return (ptr);
}

Michael Graff's avatar
Michael Graff committed
133
static inline void
Michael Graff's avatar
Michael Graff committed
134
msgblock_reset(dns_msgblock_t *block)
Michael Graff's avatar
Michael Graff committed
135
{
Michael Graff's avatar
Michael Graff committed
136
	block->remaining = block->count;
Michael Graff's avatar
Michael Graff committed
137 138
}

Michael Graff's avatar
Michael Graff committed
139 140 141 142
/*
 * Release memory associated with a message block.
 */
static inline void
Michael Graff's avatar
Michael Graff committed
143 144
msgblock_free(isc_mem_t *mctx, dns_msgblock_t *block,
	      unsigned int sizeof_type)
Michael Graff's avatar
Michael Graff committed
145
{
Michael Graff's avatar
Michael Graff committed
146 147 148 149 150
	unsigned int length;

	length = sizeof(dns_msgblock_t) + (sizeof_type * block->count);

	isc_mem_put(mctx, block, length);
Michael Graff's avatar
Michael Graff committed
151
}
Michael Graff's avatar
Michael Graff committed
152

Michael Graff's avatar
Michael Graff committed
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
/*
 * 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);
Michael Graff's avatar
Michael Graff committed
180
	INSIST(dynbuf != NULL);
Michael Graff's avatar
Michael Graff committed
181 182 183 184 185 186 187

	return (&dynbuf->buffer);
}

static inline void
releasename(dns_message_t *msg, dns_name_t *name)
{
188
	ISC_LIST_PREPEND(msg->freename, name, link);
Michael Graff's avatar
Michael Graff committed
189 190 191 192 193 194 195 196
}

static inline dns_name_t *
newname(dns_message_t *msg)
{
	dns_msgblock_t *msgblock;
	dns_name_t *name;

197 198 199
	name = ISC_LIST_HEAD(msg->freename);
	if (name != NULL) {
		ISC_LIST_UNLINK(msg->freename, name, link);
Michael Graff's avatar
Michael Graff committed
200 201 202
		return (name);
	}

Michael Graff's avatar
Michael Graff committed
203
	msgblock = ISC_LIST_TAIL(msg->names);
Michael Graff's avatar
Michael Graff committed
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
	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);
	}

	return (name);
}

static inline void
releaserdata(dns_message_t *msg, dns_rdata_t *rdata)
{
222
	ISC_LIST_PREPEND(msg->freerdata, rdata, link);
Michael Graff's avatar
Michael Graff committed
223 224 225 226 227 228 229 230
}

static inline dns_rdata_t *
newrdata(dns_message_t *msg)
{
	dns_msgblock_t *msgblock;
	dns_rdata_t *rdata;

231 232 233
	rdata = ISC_LIST_HEAD(msg->freerdata);
	if (rdata != NULL) {
		ISC_LIST_UNLINK(msg->freerdata, rdata, link);
Michael Graff's avatar
Michael Graff committed
234 235 236
		return (rdata);
	}

Michael Graff's avatar
Michael Graff committed
237
	msgblock = ISC_LIST_TAIL(msg->rdatas);
Michael Graff's avatar
Michael Graff committed
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
	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)
{
256
	ISC_LIST_PREPEND(msg->freerdatalist, rdatalist, link);
Michael Graff's avatar
Michael Graff committed
257 258 259 260 261 262 263 264
}

static inline dns_rdatalist_t *
newrdatalist(dns_message_t *msg)
{
	dns_msgblock_t *msgblock;
	dns_rdatalist_t *rdatalist;

265 266 267
	rdatalist = ISC_LIST_HEAD(msg->freerdatalist);
	if (rdatalist != NULL) {
		ISC_LIST_UNLINK(msg->freerdatalist, rdatalist, link);
Michael Graff's avatar
Michael Graff committed
268 269 270
		return (rdatalist);
	}

Michael Graff's avatar
Michael Graff committed
271
	msgblock = ISC_LIST_TAIL(msg->rdatalists);
Michael Graff's avatar
Michael Graff committed
272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290
	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)
{
291
	ISC_LIST_PREPEND(msg->freerdataset, rdataset, link);
Michael Graff's avatar
Michael Graff committed
292 293 294 295 296 297 298 299
}

static inline dns_rdataset_t *
newrdataset(dns_message_t *msg)
{
	dns_msgblock_t *msgblock;
	dns_rdataset_t *rdataset;

300 301 302
	rdataset = ISC_LIST_HEAD(msg->freerdataset);
	if (rdataset != NULL) {
		ISC_LIST_UNLINK(msg->freerdataset, rdataset, link);
Michael Graff's avatar
Michael Graff committed
303 304 305
		return (rdataset);
	}

Michael Graff's avatar
Michael Graff committed
306
	msgblock = ISC_LIST_TAIL(msg->rdatasets);
Michael Graff's avatar
Michael Graff committed
307 308 309 310 311 312 313 314 315 316 317 318 319 320 321
	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);
}

Michael Graff's avatar
Michael Graff committed
322
static inline void
323
msginitheader(dns_message_t *m)
Michael Graff's avatar
Michael Graff committed
324 325 326 327 328
{
	m->id = 0;
	m->flags = 0;
	m->rcode = 0;
	m->opcode = 0;
Michael Graff's avatar
Michael Graff committed
329
	m->rdclass = 0;
330
}
Michael Graff's avatar
Michael Graff committed
331

332 333 334 335 336 337
static inline void
msginitprivate(dns_message_t *m)
{
	unsigned int i;

	for (i = 0; i < DNS_SECTION_MAX; i++) {
Michael Graff's avatar
Michael Graff committed
338
		m->cursors[i] = NULL;
Michael Graff's avatar
Michael Graff committed
339
		m->counts[i] = 0;
Michael Graff's avatar
Michael Graff committed
340
	}
341
	m->opt = NULL;
Michael Graff's avatar
Michael Graff committed
342
	m->state = DNS_SECTION_ANY;  /* indicate nothing parsed or rendered */
Michael Graff's avatar
Michael Graff committed
343
	m->reserved = 0;
344
	m->buffer = NULL;
345
	m->need_cctx_cleanup = 0;
Michael Graff's avatar
Michael Graff committed
346 347 348
}

/*
349 350
 * Init elements to default state.  Used both when allocating a new element
 * and when resetting one.
Michael Graff's avatar
Michael Graff committed
351
 */
352 353
static inline void
msginit(dns_message_t *m)
Michael Graff's avatar
Michael Graff committed
354
{
355 356
	msginitheader(m);
	msginitprivate(m);
Bob Halley's avatar
Bob Halley committed
357 358
	m->header_ok = 0;
	m->question_ok = 0;
359 360 361 362
}

static inline void
msgresetnames(dns_message_t *msg, unsigned int first_section) {
Michael Graff's avatar
Michael Graff committed
363
	unsigned int i;
364 365
	dns_name_t *name, *next_name;
	dns_rdataset_t *rds, *next_rds;
Michael Graff's avatar
Michael Graff committed
366 367 368 369

	/*
	 * Clean up name lists by calling the rdataset disassociate function.
	 */
370
	for (i = first_section; i < DNS_SECTION_MAX; i++) {
Michael Graff's avatar
Michael Graff committed
371 372 373 374 375 376 377 378 379 380 381 382 383
		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;
			}
384
			name = next_name;
Michael Graff's avatar
Michael Graff committed
385 386
		}
	}
387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403
}

/*
 * 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;
	dns_name_t *name;
	dns_rdata_t *rdata;
	dns_rdatalist_t *rdatalist;

	msgresetnames(msg, 0);
Michael Graff's avatar
Michael Graff committed
404 405 406 407 408

	/*
	 * Clean up linked lists.
	 */

409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434
	/*
	 * 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.
	 */
	name = ISC_LIST_HEAD(msg->freename);
	while (name != NULL) {
		ISC_LIST_UNLINK(msg->freename, name, link);
		name = ISC_LIST_HEAD(msg->freename);
	}
	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);
	}
	rds = ISC_LIST_HEAD(msg->freerdataset);
	while (rds != NULL) {
		ISC_LIST_UNLINK(msg->freerdataset, rds, link);
		rds = ISC_LIST_HEAD(msg->freerdataset);
	}

Michael Graff's avatar
Michael Graff committed
435 436
	dynbuf = ISC_LIST_HEAD(msg->scratchpad);
	INSIST(dynbuf != NULL);
Michael Graff's avatar
Michael Graff committed
437
	if (!everything) {
Michael Graff's avatar
Michael Graff committed
438 439 440 441 442 443 444 445 446 447 448 449
		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);
Michael Graff's avatar
Michael Graff committed
450 451
	if (!everything) {
		msgblock_reset(msgblock);
Michael Graff's avatar
Michael Graff committed
452 453 454 455 456
		msgblock = ISC_LIST_NEXT(msgblock, link);
	}
	while (msgblock != NULL) {
		next_msgblock = ISC_LIST_NEXT(msgblock, link);
		ISC_LIST_UNLINK(msg->names, msgblock, link);
Michael Graff's avatar
Michael Graff committed
457
		msgblock_free(msg->mctx, msgblock, sizeof(dns_name_t));
Michael Graff's avatar
Michael Graff committed
458 459 460 461 462
		msgblock = next_msgblock;
	}

	msgblock = ISC_LIST_HEAD(msg->rdatas);
	INSIST(msgblock != NULL);
Michael Graff's avatar
Michael Graff committed
463 464
	if (!everything) {
		msgblock_reset(msgblock);
Michael Graff's avatar
Michael Graff committed
465 466 467 468 469
		msgblock = ISC_LIST_NEXT(msgblock, link);
	}
	while (msgblock != NULL) {
		next_msgblock = ISC_LIST_NEXT(msgblock, link);
		ISC_LIST_UNLINK(msg->rdatas, msgblock, link);
Michael Graff's avatar
Michael Graff committed
470
		msgblock_free(msg->mctx, msgblock, sizeof(dns_rdata_t));
Michael Graff's avatar
Michael Graff committed
471 472 473
		msgblock = next_msgblock;
	}

Michael Graff's avatar
Michael Graff committed
474 475
	msgblock = ISC_LIST_HEAD(msg->rdatasets);
	INSIST(msgblock != NULL);
Michael Graff's avatar
Michael Graff committed
476 477
	if (!everything) {
		msgblock_reset(msgblock);
Michael Graff's avatar
Michael Graff committed
478 479 480 481 482
		msgblock = ISC_LIST_NEXT(msgblock, link);
	}
	while (msgblock != NULL) {
		next_msgblock = ISC_LIST_NEXT(msgblock, link);
		ISC_LIST_UNLINK(msg->rdatasets, msgblock, link);
Michael Graff's avatar
Michael Graff committed
483
		msgblock_free(msg->mctx, msgblock, sizeof(dns_rdataset_t));
Michael Graff's avatar
Michael Graff committed
484 485 486
		msgblock = next_msgblock;
	}

Michael Graff's avatar
Michael Graff committed
487
	/*
Michael Graff's avatar
Michael Graff committed
488
	 * rdatalists could be empty.
Michael Graff's avatar
Michael Graff committed
489 490 491
	 */

	msgblock = ISC_LIST_HEAD(msg->rdatalists);
Michael Graff's avatar
Michael Graff committed
492 493
	if (!everything && msgblock != NULL) {
		msgblock_reset(msgblock);
Michael Graff's avatar
Michael Graff committed
494 495 496 497 498
		msgblock = ISC_LIST_NEXT(msgblock, link);
	}
	while (msgblock != NULL) {
		next_msgblock = ISC_LIST_NEXT(msgblock, link);
		ISC_LIST_UNLINK(msg->rdatalists, msgblock, link);
Michael Graff's avatar
Michael Graff committed
499
		msgblock_free(msg->mctx, msgblock, sizeof(dns_rdatalist_t));
Michael Graff's avatar
Michael Graff committed
500
		msgblock = next_msgblock;
Michael Graff's avatar
Michael Graff committed
501 502
	}

503
	if (msg->need_cctx_cleanup == 1)
Michael Graff's avatar
Michael Graff committed
504 505
		dns_compress_invalidate(&msg->cctx);

Michael Graff's avatar
Michael Graff committed
506 507 508
	/*
	 * Set other bits to normal default values.
	 */
Michael Graff's avatar
Michael Graff committed
509 510
	if (!everything)
		msginit(msg);
Michael Graff's avatar
Michael Graff committed
511 512 513
}

dns_result_t
514
dns_message_create(isc_mem_t *mctx, unsigned int intent, dns_message_t **msgp)
Michael Graff's avatar
Michael Graff committed
515 516 517 518 519 520 521 522
{
	dns_message_t *m;
	isc_result_t iresult;
	dns_msgblock_t *msgblock;
	isc_dynbuffer_t *dynbuf;
	unsigned int i;

	REQUIRE(mctx != NULL);
523 524
	REQUIRE(msgp != NULL);
	REQUIRE(*msgp == NULL);
Michael Graff's avatar
Michael Graff committed
525 526
	REQUIRE(intent == DNS_MESSAGE_INTENTPARSE
		|| intent == DNS_MESSAGE_INTENTRENDER);
Michael Graff's avatar
Michael Graff committed
527 528 529 530 531

	m = isc_mem_get(mctx, sizeof(dns_message_t));
	if (m == NULL)
		return(DNS_R_NOMEMORY);

532
	m->magic = DNS_MESSAGE_MAGIC;
Michael Graff's avatar
Michael Graff committed
533 534 535 536 537 538 539 540
	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);
541
	ISC_LIST_INIT(m->rdatasets);
Michael Graff's avatar
Michael Graff committed
542
	ISC_LIST_INIT(m->rdatalists);
543 544 545 546
	ISC_LIST_INIT(m->freename);
	ISC_LIST_INIT(m->freerdata);
	ISC_LIST_INIT(m->freerdataset);
	ISC_LIST_INIT(m->freerdatalist);
Michael Graff's avatar
Michael Graff committed
547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566

	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);

Michael Graff's avatar
Michael Graff committed
567 568 569 570
	msgblock = msgblock_allocate(mctx, sizeof(dns_rdataset_t),
				     RDATASET_COUNT);
	if (msgblock == NULL)
		goto cleanup4;
571
	ISC_LIST_APPEND(m->rdatasets, msgblock, link);
Michael Graff's avatar
Michael Graff committed
572

Michael Graff's avatar
Michael Graff committed
573
	if (intent == DNS_MESSAGE_INTENTPARSE) {
Michael Graff's avatar
Michael Graff committed
574 575 576
		msgblock = msgblock_allocate(mctx, sizeof(dns_rdatalist_t),
					     RDATALIST_COUNT);
		if (msgblock == NULL)
Michael Graff's avatar
Michael Graff committed
577
			goto cleanup5;
Michael Graff's avatar
Michael Graff committed
578 579 580
		ISC_LIST_APPEND(m->rdatalists, msgblock, link);
	}

581
	*msgp = m;
Michael Graff's avatar
Michael Graff committed
582 583 584 585 586
	return (DNS_R_SUCCESS);

	/*
	 * Cleanup for error returns.
	 */
Michael Graff's avatar
Michael Graff committed
587 588
 cleanup5:
	msgblock = ISC_LIST_HEAD(m->rdatasets);
Michael Graff's avatar
Michael Graff committed
589
	msgblock_free(mctx, msgblock, sizeof(dns_rdataset_t));
Michael Graff's avatar
Michael Graff committed
590 591
 cleanup4:
	msgblock = ISC_LIST_HEAD(m->rdatas);
Michael Graff's avatar
Michael Graff committed
592
	msgblock_free(mctx, msgblock, sizeof(dns_rdata_t));
Michael Graff's avatar
Michael Graff committed
593 594
 cleanup3:
	msgblock = ISC_LIST_HEAD(m->names);
Michael Graff's avatar
Michael Graff committed
595
	msgblock_free(mctx, msgblock, sizeof(dns_name_t));
Michael Graff's avatar
Michael Graff committed
596 597 598 599 600 601 602 603 604 605 606
 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
607
dns_message_reset(dns_message_t *msg, unsigned int intent)
Michael Graff's avatar
Michael Graff committed
608
{
609 610 611 612 613
	REQUIRE(DNS_MESSAGE_VALID(msg));
	REQUIRE(intent == DNS_MESSAGE_INTENTPARSE
		|| intent == DNS_MESSAGE_INTENTRENDER);

	msg->from_to_wire = intent;
Michael Graff's avatar
Michael Graff committed
614 615 616 617
	msgreset(msg, ISC_FALSE);
}

void
618
dns_message_destroy(dns_message_t **msgp)
Michael Graff's avatar
Michael Graff committed
619 620 621
{
	dns_message_t *msg;

622
	REQUIRE(msgp != NULL);
623
	REQUIRE(DNS_MESSAGE_VALID(*msgp));
Michael Graff's avatar
Michael Graff committed
624

625 626
	msg = *msgp;
	*msgp = NULL;
Michael Graff's avatar
Michael Graff committed
627 628 629 630 631 632

	msgreset(msg, ISC_TRUE);
	msg->magic = 0;
	isc_mem_put(msg->mctx, msg, sizeof(dns_message_t));
}

Michael Graff's avatar
Michael Graff committed
633 634 635 636 637 638 639 640
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)) {
Michael Graff's avatar
Michael Graff committed
641
		if (dns_name_equal(curr, target)) {
Michael Graff's avatar
Michael Graff committed
642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681
			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".
 */
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);

Michael Graff's avatar
Michael Graff committed
682 683 684 685
	/*
	 * First try:  use current buffer.
	 * Second try:  allocate a new buffer and use that.
	 */
Michael Graff's avatar
Michael Graff committed
686 687
	tries = 0;
	while (tries < 2) {
688
		dns_name_init(name, NULL);
Michael Graff's avatar
Michael Graff committed
689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704
		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);
		}
	}

Michael Graff's avatar
Michael Graff committed
705 706
	INSIST(0);  /* Cannot get here... */
	return (DNS_R_UNEXPECTED);
Michael Graff's avatar
Michael Graff committed
707 708
}

709 710 711 712 713 714 715 716 717
static dns_result_t
getrdata(dns_name_t *name, 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;
	dns_result_t result;
	unsigned int tries;

718 719 720 721 722 723 724 725
	/*
	 * 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.
		 */
Bob Halley's avatar
Bob Halley committed
726
		rdata->data = (unsigned char *)""; 
727 728 729 730 731 732
		rdata->length = 0;
		rdata->class = rdclass;
		rdata->type = rdtype;
		return DNS_R_SUCCESS;
	}
	    
733 734 735 736 737
	scratch = currentbuffer(msg);

	isc_buffer_setactive(source, rdatalen);
	dns_decompress_localinit(dctx, name, source);

Michael Graff's avatar
Michael Graff committed
738 739 740 741
	/*
	 * First try:  use current buffer.
	 * Second try:  allocate a new buffer and use that.
	 */
742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760
	tries = 0;
	while (tries < 2) {
		result = dns_rdata_fromwire(rdata, rdclass, rdtype,
					    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);
		}
	}

Michael Graff's avatar
Michael Graff committed
761 762
	INSIST(0);  /* Cannot get here... */
	return (DNS_R_UNEXPECTED);
763
}
Michael Graff's avatar
Michael Graff committed
764

Michael Graff's avatar
Michael Graff committed
765 766 767 768 769 770 771 772 773 774
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;
Michael Graff's avatar
Michael Graff committed
775 776 777
	dns_rdatatype_t rdtype;
	dns_rdataclass_t rdclass;
	dns_namelist_t *section;
Michael Graff's avatar
Michael Graff committed
778

Michael Graff's avatar
Michael Graff committed
779
	section = &msg->sections[DNS_SECTION_QUESTION];
Michael Graff's avatar
Michael Graff committed
780

Michael Graff's avatar
Michael Graff committed
781
	for (count = 0 ; count < msg->counts[DNS_SECTION_QUESTION] ; count++) {
Michael Graff's avatar
Michael Graff committed
782 783 784 785 786 787 788
		name = newname(msg);
		if (name == NULL)
			return (DNS_R_NOMEMORY);

		/*
		 * Parse the name out of this packet.
		 */
789 790
		isc_buffer_remaining(source, &r);
		isc_buffer_setactive(source, r.length);
Michael Graff's avatar
Michael Graff committed
791 792 793 794 795 796 797 798 799 800
		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.
		 */
Michael Graff's avatar
Michael Graff committed
801
		result = findname(&name2, name, section);
Michael Graff's avatar
Michael Graff committed
802 803

		/*
804 805 806 807
		 * 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
Michael Graff's avatar
Michael Graff committed
808 809 810 811
		 * 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.
Michael Graff's avatar
Michael Graff committed
812
		 */
813 814 815 816 817 818 819 820 821
		if (result != DNS_R_SUCCESS) {
			if (ISC_LIST_EMPTY(*section)) {
				ISC_LIST_APPEND(*section, name, link);
			} else {
				return (DNS_R_FORMERR);
			}
		} else {
			name = name2;
		}
Michael Graff's avatar
Michael Graff committed
822 823 824 825

		/*
		 * Get type and class.
		 */
Michael Graff's avatar
Michael Graff committed
826 827 828 829 830
		isc_buffer_remaining(source, &r);
		if (r.length < 4)
			return (DNS_R_UNEXPECTEDEND);
		rdtype = isc_buffer_getuint16(source);
		rdclass = isc_buffer_getuint16(source);
Michael Graff's avatar
Michael Graff committed
831 832

		/*
833
		 * If this class is different than the one we already read,
Michael Graff's avatar
Michael Graff committed
834
		 * this is an error.
Michael Graff's avatar
Michael Graff committed
835
		 */
Michael Graff's avatar
Michael Graff committed
836 837 838
		if (msg->state == DNS_SECTION_ANY) {
			msg->state = DNS_SECTION_QUESTION;
			msg->rdclass = rdclass;
839
			msg->state = DNS_SECTION_QUESTION;
Michael Graff's avatar
Michael Graff committed
840 841 842
		} else if (msg->rdclass != rdclass)
			return (DNS_R_FORMERR);
		
Michael Graff's avatar
Michael Graff committed
843
		/*
Michael Graff's avatar
Michael Graff committed
844
		 * Can't ask the same question twice.
Michael Graff's avatar
Michael Graff committed
845
		 */
Michael Graff's avatar
Michael Graff committed
846 847 848
		result = findtype(NULL, name, rdtype);
		if (result == DNS_R_SUCCESS)
			return (DNS_R_FORMERR);
Michael Graff's avatar
Michael Graff committed
849 850 851 852

		/*
		 * Allocate a new rdatalist.
		 */
Michael Graff's avatar
Michael Graff committed
853
		rdatalist = newrdatalist(msg);
Michael Graff's avatar
Michael Graff committed
854 855
		if (rdatalist == NULL)
			return (DNS_R_NOMEMORY);
Michael Graff's avatar
Michael Graff committed
856
		rdataset = newrdataset(msg);
Michael Graff's avatar
Michael Graff committed
857 858
		if (rdataset == NULL)
			return (DNS_R_NOMEMORY);
Michael Graff's avatar
Michael Graff committed
859 860 861 862 863

		/*
		 * Convert rdatalist to rdataset, and attach the latter to
		 * the name.
		 */
Michael Graff's avatar
Michael Graff committed
864 865 866 867 868
		rdatalist->type = rdtype;
		rdatalist->rdclass = rdclass;
		rdatalist->ttl = 0;
		ISC_LIST_INIT(rdatalist->rdata);

869
		dns_rdataset_init(rdataset);
Michael Graff's avatar
Michael Graff committed
870 871 872
		result = dns_rdatalist_tordataset(rdatalist, rdataset);
		if (result != DNS_R_SUCCESS)
			return (result);
873
		rdataset->attributes |= DNS_RDATASETATTR_QUESTION;
Michael Graff's avatar
Michael Graff committed
874 875

		ISC_LIST_APPEND(name->list, rdataset, link);
Michael Graff's avatar
Michael Graff committed
876 877 878 879 880 881 882
	}
	
	return (DNS_R_SUCCESS);
}

static dns_result_t
getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
Michael Graff's avatar
Michael Graff committed
883
	   dns_section_t sectionid)
Michael Graff's avatar
Michael Graff committed
884
{
Michael Graff's avatar
Michael Graff committed
885 886
	isc_region_t r;
	unsigned int count;
Michael Graff's avatar
Michael Graff committed
887
	unsigned int rdatalen;
Michael Graff's avatar
Michael Graff committed
888 889 890 891 892 893 894
	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;
895
	dns_rdata_t *rdata;
Michael Graff's avatar
Michael Graff committed
896 897 898 899
	dns_ttl_t ttl;
	dns_namelist_t *section;

	for (count = 0 ; count < msg->counts[sectionid] ; count++) {
900 901
		section = &msg->sections[sectionid];

Michael Graff's avatar
Michael Graff committed
902 903 904 905 906 907 908
		name = newname(msg);
		if (name == NULL)
			return (DNS_R_NOMEMORY);

		/*
		 * Parse the name out of this packet.
		 */
909 910
		isc_buffer_remaining(source, &r);
		isc_buffer_setactive(source, r.length);
Michael Graff's avatar
Michael Graff committed
911 912 913 914 915 916 917
		result = getname(name, source, msg, dctx);
		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
Michael Graff's avatar
Michael Graff committed
918
		 * later.)
Michael Graff's avatar
Michael Graff committed
919 920
		 */
		isc_buffer_remaining(source, &r);
Michael Graff's avatar
Michael Graff committed
921
		if (r.length < 2 + 2 + 4 + 2)
Michael Graff's avatar
Michael Graff committed
922 923 924 925 926
			return (DNS_R_UNEXPECTEDEND);
		rdtype = isc_buffer_getuint16(source);
		rdclass = isc_buffer_getuint16(source);

		/*
927 928
		 * If this class is different than the one in the question
		 * section, bail.
Michael Graff's avatar
Michael Graff committed
929
		 */
Michael Graff's avatar
Michael Graff committed
930
		if (msg->opcode != dns_opcode_update
931
		    && rdtype != dns_rdatatype_tsig
Michael Graff's avatar
Michael Graff committed
932
		    && msg->rdclass != rdclass)
Michael Graff's avatar
Michael Graff committed
933
			return (DNS_R_FORMERR);
934 935 936 937 938 939 940 941

		/*
		 * If it is a tsig, verify that it is in the additional data
		 * section, and switch sections for the rest of this rdata.
		 */
		if (rdtype == dns_rdatatype_tsig) {
			if (sectionid != DNS_SECTION_ADDITIONAL)
				return (DNS_R_FORMERR);
942 943
			if (rdclass != dns_rdataclass_any)
				return (DNS_R_FORMERR);
944 945
			section = &msg->sections[DNS_SECTION_TSIG];
		}
Michael Graff's avatar
Michael Graff committed
946 947 948 949 950 951
		
		/*
		 * ... now get ttl and rdatalen, and check buffer.
		 */
		ttl = isc_buffer_getuint32(source);
		rdatalen = isc_buffer_getuint16(source);
Michael Graff's avatar
Michael Graff committed
952
		r.length -= (2 + 2 + 4 + 2);
Michael Graff's avatar
Michael Graff committed
953 954 955
		if (r.length < rdatalen)
			return (DNS_R_UNEXPECTEDEND);

956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990
		/*
		 * 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 (msg->opcode == dns_opcode_update
		    || rdtype == dns_rdatatype_tsig) {
			ISC_LIST_APPEND(*section, name, link);
		} else {
			/*
			 * Run through the section, looking to see if this name
			 * is already there.  If it is found, put back the
			 * allocated name since we no longer need it, and set
			 * our name pointer to point to the name we found.
			 */
			result = findname(&name2, name, section);

			/*
			 * If it is a new name, append to the section.
			 */
			if (result == DNS_R_SUCCESS) {
				releasename(msg, name);
				name = name2;
			} else {
				ISC_LIST_APPEND(*section, name, link);
			}
		}

		/*
		 * If this is an OPT record, There Can Be Only One.
		 */
#if 0 /* until there is a dns_rdatatype_opt  XXXMLG */
		if (rdtype == dns_rdatatype_opt && msg->opt != NULL)
			return (DNS_R_FORMERR);
#endif

Michael Graff's avatar
Michael Graff committed
991 992
		/*
		 * Search name for the particular type and class.
993
		 * Skip this stage if in update mode, or this is a TSIG.
Michael Graff's avatar
Michael Graff committed
994
		 */
995 996 997 998 999
		if (msg->opcode == dns_opcode_update
		    || rdtype == dns_rdatatype_tsig)
			result = DNS_R_NOTFOUND;
		else
			result = findtype(&rdataset, name, rdtype);
Michael Graff's avatar
Michael Graff committed
1000 1001

		/*
1002 1003 1004 1005 1006
		 * 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.
Michael Graff's avatar
Michael Graff committed
1007
		 */
Michael Graff's avatar
Michael Graff committed
1008
		if (result == DNS_R_NOTFOUND) {
Michael Graff's avatar
Michael Graff committed
1009 1010 1011
			rdataset = newrdataset(msg);
			if (rdataset == NULL)
				return (DNS_R_NOMEMORY);
1012 1013 1014
			rdatalist = newrdatalist(msg);
			if (rdatalist == NULL)
				return (DNS_R_NOMEMORY);
Michael Graff's avatar
Michael Graff committed
1015

1016 1017 1018 1019
			rdatalist->type = rdtype;
			rdatalist->rdclass = rdclass;
			rdatalist->ttl = ttl;
			ISC_LIST_INIT(rdatalist->rdata);
Michael Graff's avatar
Michael Graff committed
1020

1021 1022 1023 1024 1025
			dns_rdataset_init(rdataset);
			dns_rdatalist_tordataset(rdatalist, rdataset);

			ISC_LIST_APPEND(name->list, rdataset, link);
		}
Michael Graff's avatar
Michael Graff committed
1026 1027

		/*
1028 1029 1030 1031
		 * 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.  Then put the meta-class
		 * back into the finished rdata.
Michael Graff's avatar
Michael Graff committed
1032
		 */
1033 1034 1035 1036
		rdata = newrdata(msg);
		if (rdata == NULL)
			return (DNS_R_NOMEMORY);
		result = getrdata(name, source, msg, dctx,
1037
				  msg->rdclass, rdtype, rdatalen, rdata);
Michael Graff's avatar
Michael Graff committed
1038 1039
		if (result != DNS_R_SUCCESS)
			return (result);
1040
		rdata->class = rdclass;
Michael Graff's avatar
Michael Graff committed
1041

1042
		/*
Michael Graff's avatar
Michael Graff committed
1043
		 * XXXMLG Perform a totally ugly hack here to pull
1044 1045 1046 1047 1048 1049 1050
		 * 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);
1051 1052 1053 1054 1055 1056 1057 1058

		/*
		 * If this is an OPT record, remember it.
		 */
#if 0 /* until there is a dns_rdatatype_opt  XXXMLG */
		if (rdtype == dns_rdatatype_opt)
			msg->opt = rdata;
#endif
Michael Graff's avatar
Michael Graff committed
1059 1060 1061
	}
	
	return (DNS_R_SUCCESS);
Michael Graff's avatar
Michael Graff committed
1062 1063
}

Michael Graff's avatar
Michael Graff committed
1064
dns_result_t
Michael Graff's avatar
Michael Graff committed
1065
dns_message_parse(dns_message_t *msg, isc_buffer_t *source)
Michael Graff's avatar
Michael Graff committed
1066
{
Michael Graff's avatar
Michael Graff committed
1067 1068 1069
	isc_region_t r;
	dns_decompress_t dctx;
	dns_result_t ret;
1070
	isc_uint16_t tmpflags;
Michael Graff's avatar
Michael Graff committed
1071

1072
	REQUIRE(DNS_MESSAGE_VALID(msg));
Michael Graff's avatar
Michael Graff committed
1073
	REQUIRE(source != NULL);
Michael Graff's avatar
Michael Graff committed
1074
	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTPARSE);
Michael Graff's avatar
Michael Graff committed
1075

1076 1077 1078
	msg->header_ok = 0;
	msg->question_ok = 0;

Michael Graff's avatar
Michael Graff committed
1079
	isc_buffer_remaining(source, &r);
Michael Graff's avatar
Michael Graff committed
1080
	if (r.length < DNS_MESSAGE_HEADERLEN)
Michael Graff's avatar
Michael Graff committed
1081 1082 1083
		return (DNS_R_UNEXPECTEDEND);

	msg->id = isc_buffer_getuint16(source);
1084 1085 1086 1087
	tmpflags = isc_buffer_getuint16(source);
	msg->opcode = ((tmpflags & DNS_MESSAGE_OPCODE_MASK)
		       >> DNS_MESSAGE_OPCODE_SHIFT);
	msg->rcode = (tmpflags & DNS_MESSAGE_RCODE_MASK);
1088
	msg->flags = (tmpflags & DNS_MESSAGE_FLAG_MASK);
Michael Graff's avatar
Michael Graff committed
1089 1090 1091 1092
	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);
Michael Graff's avatar
Michael Graff committed
1093

1094 1095
	msg->header_ok = 1;

Michael Graff's avatar
Michael Graff committed
1096 1097 1098
	/*
	 * -1 means no EDNS.
	 */
Michael Graff's avatar
Michael Graff committed
1099 1100
	dns_decompress_init(&dctx, -1, ISC_FALSE);

Michael Graff's avatar
Michael Graff committed
1101 1102 1103 1104 1105 1106
	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);


Michael Graff's avatar
Michael Graff committed
1107 1108 1109
	ret = getquestions(source, msg, &dctx);
	if (ret != DNS_R_SUCCESS)
		return (ret);
1110
	msg->question_ok = 1;
Michael Graff's avatar
Michael Graff committed
1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123

	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);

1124 1125 1126 1127
	isc_buffer_remaining(source, &r);
	if (r.length != 0)
		return (DNS_R_FORMERR);

Michael Graff's avatar
Michael Graff committed
1128 1129 1130 1131 1132
	/*
	 * XXXMLG Need to check the tsig(s) here...
	 */

	return (DNS_R_SUCCESS);
Michael Graff's avatar
Michael Graff committed
1133 1134 1135 1136 1137
}

dns_result_t
dns_message_renderbegin(dns_message_t *msg, isc_buffer_t *buffer)
{
1138
	isc_region_t r;
Michael Graff's avatar
Michael Graff committed
1139
	dns_result_t result;
1140

1141
	REQUIRE(DNS_MESSAGE_VALID(msg));
Michael Graff's avatar
Michael Graff committed
1142
	REQUIRE(buffer != NULL);
1143
	REQUIRE(msg->buffer == NULL);
Michael Graff's avatar
Michael Graff committed
1144
	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER);
Michael Graff's avatar
Michael Graff committed
1145

1146