message.c 49.4 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>
39
#include <dns/tsig.h>
40
#include <dns/dnssec.h>
Michael Graff's avatar
Michael Graff committed
41

42
#define DNS_MESSAGE_OPCODE_MASK		0x7800U
Bob Halley's avatar
EDNS0  
Bob Halley committed
43
#define DNS_MESSAGE_OPCODE_SHIFT	11
Michael Graff's avatar
Michael Graff committed
44 45
#define DNS_MESSAGE_RCODE_MASK		0x000fU
#define DNS_MESSAGE_FLAG_MASK		0x8ff0U
Bob Halley's avatar
EDNS0  
Bob Halley committed
46 47 48 49
#define DNS_MESSAGE_EDNSRCODE_MASK	0xff000000U
#define DNS_MESSAGE_EDNSRCODE_SHIFT	24
#define DNS_MESSAGE_EDNSVERSION_MASK	0x00ff0000U
#define DNS_MESSAGE_EDNSVERSION_SHIFT	16
Michael Graff's avatar
Michael Graff committed
50 51 52 53 54 55 56 57 58

#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
59
 * XXXMLG These should come from a config setting.
Michael Graff's avatar
Michael Graff committed
60
 */
Michael Graff's avatar
Michael Graff committed
61
#define SCRATCHPAD_SIZE		512
62 63 64 65
#define NAME_COUNT		  8
#define RDATA_COUNT		  8
#define RDATALIST_COUNT		  8
#define RDATASET_COUNT		 RDATALIST_COUNT
Michael Graff's avatar
Michael Graff committed
66 67 68 69 70 71 72

/*
 * "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
73
	unsigned int			count;
Michael Graff's avatar
Michael Graff committed
74 75 76
	unsigned int			remaining;
	ISC_LINK(dns_msgblock_t)	link;
}; /* dynamically sized */
Michael Graff's avatar
Michael Graff committed
77

Michael Graff's avatar
Michael Graff committed
78 79
static inline dns_msgblock_t *
msgblock_allocate(isc_mem_t *, unsigned int, unsigned int);
80

Michael Graff's avatar
Michael Graff committed
81
#define msgblock_get(block, type) \
Michael Graff's avatar
Michael Graff committed
82
	((type *)msgblock_internalget(block, sizeof(type)))
Michael Graff's avatar
Michael Graff committed
83 84

static inline void *
Michael Graff's avatar
Michael Graff committed
85 86 87
msgblock_internalget(dns_msgblock_t *, unsigned int);

static inline void
Michael Graff's avatar
Michael Graff committed
88
msgblock_reset(dns_msgblock_t *);
Michael Graff's avatar
Michael Graff committed
89

Michael Graff's avatar
Michael Graff committed
90 91
static inline void
msgblock_free(isc_mem_t *, dns_msgblock_t *, unsigned int);
Michael Graff's avatar
Michael Graff committed
92 93 94 95 96 97

/*
 * 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
98 99
msgblock_allocate(isc_mem_t *mctx, unsigned int sizeof_type,
		  unsigned int count)
Michael Graff's avatar
Michael Graff committed
100 101 102 103 104 105 106 107
{
	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
108
		return (NULL);
Michael Graff's avatar
Michael Graff committed
109

Michael Graff's avatar
Michael Graff committed
110
	block->count = count;
Michael Graff's avatar
Michael Graff committed
111 112 113 114 115 116 117 118 119 120 121 122
	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
123
msgblock_internalget(dns_msgblock_t *block, unsigned int sizeof_type)
Michael Graff's avatar
Michael Graff committed
124 125 126
{
	void *ptr;

Andreas Gustafsson's avatar
Andreas Gustafsson committed
127
	if (block == NULL || block->remaining == 0)
Michael Graff's avatar
Michael Graff committed
128 129 130 131 132 133 134 135 136 137 138
		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
139
static inline void
Michael Graff's avatar
Michael Graff committed
140
msgblock_reset(dns_msgblock_t *block)
Michael Graff's avatar
Michael Graff committed
141
{
Michael Graff's avatar
Michael Graff committed
142
	block->remaining = block->count;
Michael Graff's avatar
Michael Graff committed
143 144
}

Michael Graff's avatar
Michael Graff committed
145 146 147 148
/*
 * Release memory associated with a message block.
 */
static inline void
Michael Graff's avatar
Michael Graff committed
149 150
msgblock_free(isc_mem_t *mctx, dns_msgblock_t *block,
	      unsigned int sizeof_type)
Michael Graff's avatar
Michael Graff committed
151
{
Michael Graff's avatar
Michael Graff committed
152 153 154 155 156
	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
157
}
Michael Graff's avatar
Michael Graff committed
158

Michael Graff's avatar
Michael Graff committed
159 160 161 162 163 164
/*
 * 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
165
newbuffer(dns_message_t *msg, unsigned int size)
Michael Graff's avatar
Michael Graff committed
166 167
{
	isc_result_t result;
168
	isc_buffer_t *dynbuf;
Michael Graff's avatar
Michael Graff committed
169 170

	dynbuf = NULL;
171
	result = isc_buffer_allocate(msg->mctx, &dynbuf, size,
Michael Graff's avatar
Michael Graff committed
172 173 174 175 176 177 178 179 180 181 182
					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)
{
183
	isc_buffer_t *dynbuf;
Michael Graff's avatar
Michael Graff committed
184 185

	dynbuf = ISC_LIST_TAIL(msg->scratchpad);
Michael Graff's avatar
Michael Graff committed
186
	INSIST(dynbuf != NULL);
Michael Graff's avatar
Michael Graff committed
187

188
	return (dynbuf);
Michael Graff's avatar
Michael Graff committed
189 190 191 192 193
}

static inline void
releaserdata(dns_message_t *msg, dns_rdata_t *rdata)
{
194
	ISC_LIST_PREPEND(msg->freerdata, rdata, link);
Michael Graff's avatar
Michael Graff committed
195 196 197 198 199 200 201 202
}

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

203 204 205
	rdata = ISC_LIST_HEAD(msg->freerdata);
	if (rdata != NULL) {
		ISC_LIST_UNLINK(msg->freerdata, rdata, link);
Michael Graff's avatar
Michael Graff committed
206 207 208
		return (rdata);
	}

Michael Graff's avatar
Michael Graff committed
209
	msgblock = ISC_LIST_TAIL(msg->rdatas);
Michael Graff's avatar
Michael Graff committed
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
	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)
{
228
	ISC_LIST_PREPEND(msg->freerdatalist, rdatalist, link);
Michael Graff's avatar
Michael Graff committed
229 230 231 232 233 234 235 236
}

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

237 238 239
	rdatalist = ISC_LIST_HEAD(msg->freerdatalist);
	if (rdatalist != NULL) {
		ISC_LIST_UNLINK(msg->freerdatalist, rdatalist, link);
Michael Graff's avatar
Michael Graff committed
240 241 242
		return (rdatalist);
	}

Michael Graff's avatar
Michael Graff committed
243
	msgblock = ISC_LIST_TAIL(msg->rdatalists);
Michael Graff's avatar
Michael Graff committed
244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259
	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);
}

Michael Graff's avatar
Michael Graff committed
260
static inline void
261
msginitheader(dns_message_t *m)
Michael Graff's avatar
Michael Graff committed
262 263 264 265 266
{
	m->id = 0;
	m->flags = 0;
	m->rcode = 0;
	m->opcode = 0;
Michael Graff's avatar
Michael Graff committed
267
	m->rdclass = 0;
268
}
Michael Graff's avatar
Michael Graff committed
269

270 271 272 273 274 275
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
276
		m->cursors[i] = NULL;
Michael Graff's avatar
Michael Graff committed
277
		m->counts[i] = 0;
Michael Graff's avatar
Michael Graff committed
278
	}
279
	m->opt = NULL;
Michael Graff's avatar
Michael Graff committed
280
	m->state = DNS_SECTION_ANY;  /* indicate nothing parsed or rendered */
Michael Graff's avatar
Michael Graff committed
281
	m->reserved = 0;
282
	m->buffer = NULL;
283
	m->need_cctx_cleanup = 0;
Michael Graff's avatar
Michael Graff committed
284 285
}

286 287 288 289 290 291
static inline void
msginittsig(dns_message_t *m)
{
	m->tsigstatus = m->querytsigstatus = dns_rcode_noerror;
	m->tsig = m->querytsig = NULL;
	m->tsigkey = NULL;
292
	m->tsigctx = NULL;
293 294 295 296
	m->sigstart = -1;
	m->sig0key = NULL;
	m->sig0status = dns_rcode_noerror;
	m->query = NULL;
297 298
}

Michael Graff's avatar
Michael Graff committed
299
/*
300 301
 * Init elements to default state.  Used both when allocating a new element
 * and when resetting one.
Michael Graff's avatar
Michael Graff committed
302
 */
303 304
static inline void
msginit(dns_message_t *m)
Michael Graff's avatar
Michael Graff committed
305
{
306 307
	msginitheader(m);
	msginitprivate(m);
308
	msginittsig(m);
Bob Halley's avatar
Bob Halley committed
309 310
	m->header_ok = 0;
	m->question_ok = 0;
311
	m->tcp_continuation = 0;
312 313
	m->response_needs_sig0 = 0;
	m->verified_sig0 = 0;
314 315 316 317
}

static inline void
msgresetnames(dns_message_t *msg, unsigned int first_section) {
Michael Graff's avatar
Michael Graff committed
318
	unsigned int i;
319 320
	dns_name_t *name, *next_name;
	dns_rdataset_t *rds, *next_rds;
Michael Graff's avatar
Michael Graff committed
321 322 323 324

	/*
	 * Clean up name lists by calling the rdataset disassociate function.
	 */
325
	for (i = first_section; i < DNS_SECTION_MAX; i++) {
Michael Graff's avatar
Michael Graff committed
326 327 328 329 330 331 332 333 334 335
		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);

336
				INSIST(dns_rdataset_isassociated(rds));
Michael Graff's avatar
Michael Graff committed
337
				dns_rdataset_disassociate(rds);
338
				isc_mempool_put(msg->rdspool, rds);
Michael Graff's avatar
Michael Graff committed
339 340
				rds = next_rds;
			}
341
			isc_mempool_put(msg->namepool, name);
342
			name = next_name;
Michael Graff's avatar
Michael Graff committed
343 344
		}
	}
345 346 347 348 349 350 351 352 353 354
}

/*
 * 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;
355
	isc_buffer_t *dynbuf, *next_dynbuf;
356 357 358 359
	dns_rdata_t *rdata;
	dns_rdatalist_t *rdatalist;

	msgresetnames(msg, 0);
Michael Graff's avatar
Michael Graff committed
360

361
	if (msg->opt != NULL) {
362
		INSIST(dns_rdataset_isassociated(msg->opt));
Bob Halley's avatar
EDNS0  
Bob Halley committed
363
		dns_rdataset_disassociate(msg->opt);
364 365 366
		isc_mempool_put(msg->rdspool, msg->opt);
		msg->opt = NULL;
	}
Bob Halley's avatar
EDNS0  
Bob Halley committed
367

Michael Graff's avatar
Michael Graff committed
368 369 370 371
	/*
	 * Clean up linked lists.
	 */

372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387
	/*
	 * 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);
	}

Michael Graff's avatar
Michael Graff committed
388 389
	dynbuf = ISC_LIST_HEAD(msg->scratchpad);
	INSIST(dynbuf != NULL);
Michael Graff's avatar
Michael Graff committed
390
	if (!everything) {
391
		isc_buffer_clear(dynbuf);
Michael Graff's avatar
Michael Graff committed
392 393 394 395 396
		dynbuf = ISC_LIST_NEXT(dynbuf, link);
	}
	while (dynbuf != NULL) {
		next_dynbuf = ISC_LIST_NEXT(dynbuf, link);
		ISC_LIST_UNLINK(msg->scratchpad, dynbuf, link);
397
		isc_buffer_free(&dynbuf);
Michael Graff's avatar
Michael Graff committed
398 399 400 401 402
		dynbuf = next_dynbuf;
	}

	msgblock = ISC_LIST_HEAD(msg->rdatas);
	INSIST(msgblock != NULL);
Michael Graff's avatar
Michael Graff committed
403 404
	if (!everything) {
		msgblock_reset(msgblock);
Michael Graff's avatar
Michael Graff committed
405 406 407 408 409
		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
410
		msgblock_free(msg->mctx, msgblock, sizeof(dns_rdata_t));
Michael Graff's avatar
Michael Graff committed
411 412 413
		msgblock = next_msgblock;
	}

Michael Graff's avatar
Michael Graff committed
414
	/*
Michael Graff's avatar
Michael Graff committed
415
	 * rdatalists could be empty.
Michael Graff's avatar
Michael Graff committed
416 417 418
	 */

	msgblock = ISC_LIST_HEAD(msg->rdatalists);
Michael Graff's avatar
Michael Graff committed
419 420
	if (!everything && msgblock != NULL) {
		msgblock_reset(msgblock);
Michael Graff's avatar
Michael Graff committed
421 422 423 424 425
		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
426
		msgblock_free(msg->mctx, msgblock, sizeof(dns_rdatalist_t));
Michael Graff's avatar
Michael Graff committed
427
		msgblock = next_msgblock;
Michael Graff's avatar
Michael Graff committed
428 429
	}

430
	if (msg->need_cctx_cleanup == 1)
Michael Graff's avatar
Michael Graff committed
431 432
		dns_compress_invalidate(&msg->cctx);

433 434
	if (msg->tsig != NULL) {
		dns_rdata_freestruct(msg->tsig);
Bob Halley's avatar
EDNS0  
Bob Halley committed
435 436
		isc_mem_put(msg->mctx, msg->tsig,
			    sizeof(dns_rdata_any_tsig_t));
437
		msg->tsig = NULL;
438 439 440 441 442 443
	}

	if (msg->querytsig != NULL) {
		dns_rdata_freestruct(msg->querytsig);
		isc_mem_put(msg->mctx, msg->querytsig,
			    sizeof(dns_rdata_any_tsig_t));
444
		msg->querytsig = NULL;
445 446
        }

447
	if (msg->tsigkey != NULL) {
448
		dns_tsigkey_free(&msg->tsigkey);
449 450 451 452 453 454 455 456
		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;
	}
457

458 459 460 461 462 463
	/*
	 * cleanup the buffer cleanup list
	 */
	dynbuf = ISC_LIST_HEAD(msg->cleanup);
	while (dynbuf != NULL) {
		next_dynbuf = ISC_LIST_NEXT(dynbuf, link);
464
		ISC_LIST_UNLINK(msg->cleanup, dynbuf, link);
465 466 467 468
		isc_buffer_free(&dynbuf);
		dynbuf = next_dynbuf;
	}

Michael Graff's avatar
Michael Graff committed
469 470 471
	/*
	 * Set other bits to normal default values.
	 */
Michael Graff's avatar
Michael Graff committed
472 473
	if (!everything)
		msginit(msg);
Michael Graff's avatar
Michael Graff committed
474 475 476
}

dns_result_t
477
dns_message_create(isc_mem_t *mctx, unsigned int intent, dns_message_t **msgp)
Michael Graff's avatar
Michael Graff committed
478 479
{
	dns_message_t *m;
480
	isc_result_t result;
Michael Graff's avatar
Michael Graff committed
481
	dns_msgblock_t *msgblock;
482
	isc_buffer_t *dynbuf;
Michael Graff's avatar
Michael Graff committed
483 484 485
	unsigned int i;

	REQUIRE(mctx != NULL);
486 487
	REQUIRE(msgp != NULL);
	REQUIRE(*msgp == NULL);
Michael Graff's avatar
Michael Graff committed
488 489
	REQUIRE(intent == DNS_MESSAGE_INTENTPARSE
		|| intent == DNS_MESSAGE_INTENTRENDER);
Michael Graff's avatar
Michael Graff committed
490 491 492 493 494

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

495 496 497 498 499
	/*
	 * No allocations until further notice.  Just initialize all lists
	 * and other members that are freed in the cleanup phase here.
	 */

500
	m->magic = DNS_MESSAGE_MAGIC;
Michael Graff's avatar
Michael Graff committed
501 502
	m->from_to_wire = intent;
	msginit(m);
503

Michael Graff's avatar
Michael Graff committed
504 505 506
	for (i = 0 ; i < DNS_SECTION_MAX ; i++)
		ISC_LIST_INIT(m->sections[i]);
	m->mctx = mctx;
507

Michael Graff's avatar
Michael Graff committed
508
	ISC_LIST_INIT(m->scratchpad);
509 510
	ISC_LIST_INIT(m->cleanup);
	m->namepool = NULL;
511
	m->rdspool = NULL;
Michael Graff's avatar
Michael Graff committed
512 513
	ISC_LIST_INIT(m->rdatas);
	ISC_LIST_INIT(m->rdatalists);
514 515
	ISC_LIST_INIT(m->freerdata);
	ISC_LIST_INIT(m->freerdatalist);
Michael Graff's avatar
Michael Graff committed
516

517 518 519 520 521 522 523 524 525
	/*
	 * 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);
Michael Graff's avatar
Michael Graff committed
526
	isc_mempool_setname(m->namepool, "msg:names");
527

528 529 530 531 532 533
	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);
Michael Graff's avatar
Michael Graff committed
534
	isc_mempool_setname(m->rdspool, "msg:rdataset");
535

Michael Graff's avatar
Michael Graff committed
536
	dynbuf = NULL;
537 538 539 540
	result = isc_buffer_allocate(mctx, &dynbuf, SCRATCHPAD_SIZE,
				     ISC_BUFFERTYPE_BINARY);
	if (result != ISC_R_SUCCESS)
		goto cleanup;
Michael Graff's avatar
Michael Graff committed
541 542 543 544 545
	ISC_LIST_APPEND(m->scratchpad, dynbuf, link);

	msgblock = msgblock_allocate(mctx, sizeof(dns_rdata_t),
				     RDATA_COUNT);
	if (msgblock == NULL)
546
		goto cleanup;
Michael Graff's avatar
Michael Graff committed
547 548
	ISC_LIST_APPEND(m->rdatas, msgblock, link);

Michael Graff's avatar
Michael Graff committed
549
	if (intent == DNS_MESSAGE_INTENTPARSE) {
Michael Graff's avatar
Michael Graff committed
550 551 552
		msgblock = msgblock_allocate(mctx, sizeof(dns_rdatalist_t),
					     RDATALIST_COUNT);
		if (msgblock == NULL)
553
			goto cleanup;
Michael Graff's avatar
Michael Graff committed
554 555 556
		ISC_LIST_APPEND(m->rdatalists, msgblock, link);
	}

557
	*msgp = m;
Michael Graff's avatar
Michael Graff committed
558 559 560 561 562
	return (DNS_R_SUCCESS);

	/*
	 * Cleanup for error returns.
	 */
563
 cleanup:
Michael Graff's avatar
Michael Graff committed
564
	msgblock = ISC_LIST_HEAD(m->rdatas);
565 566
	if (msgblock != NULL)
		msgblock_free(mctx, msgblock, sizeof(dns_rdata_t));
Michael Graff's avatar
Michael Graff committed
567
	dynbuf = ISC_LIST_HEAD(m->scratchpad);
568 569 570 571
	if (dynbuf != NULL)
		isc_buffer_free(&dynbuf);
	if (m->namepool != NULL)
		isc_mempool_destroy(&m->namepool);
572 573
	if (m->rdspool != NULL)
		isc_mempool_destroy(&m->rdspool);
Michael Graff's avatar
Michael Graff committed
574 575 576 577 578 579 580
	m->magic = 0;
	isc_mem_put(mctx, m, sizeof(dns_message_t));

	return (DNS_R_NOMEMORY);
}

void
581
dns_message_reset(dns_message_t *msg, unsigned int intent)
Michael Graff's avatar
Michael Graff committed
582
{
583 584 585 586 587
	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
588 589 590 591
	msgreset(msg, ISC_FALSE);
}

void
592
dns_message_destroy(dns_message_t **msgp)
Michael Graff's avatar
Michael Graff committed
593 594 595
{
	dns_message_t *msg;

596
	REQUIRE(msgp != NULL);
597
	REQUIRE(DNS_MESSAGE_VALID(*msgp));
Michael Graff's avatar
Michael Graff committed
598

599 600
	msg = *msgp;
	*msgp = NULL;
Michael Graff's avatar
Michael Graff committed
601 602

	msgreset(msg, ISC_TRUE);
603
	isc_mempool_destroy(&msg->namepool);
604
	isc_mempool_destroy(&msg->rdspool);
Michael Graff's avatar
Michael Graff committed
605 606 607 608
	msg->magic = 0;
	isc_mem_put(msg->mctx, msg, sizeof(dns_message_t));
}

Michael Graff's avatar
Michael Graff committed
609
static dns_result_t
610 611
findname(dns_name_t **foundname, dns_name_t *target, unsigned int attributes,
	 dns_namelist_t *section)
Michael Graff's avatar
Michael Graff committed
612 613 614 615 616 617
{
	dns_name_t *curr;

	for (curr = ISC_LIST_TAIL(*section) ;
	     curr != NULL ;
	     curr = ISC_LIST_PREV(curr, link)) {
618 619
		if (dns_name_equal(curr, target) &&
		    (curr->attributes & attributes) == attributes) {
Michael Graff's avatar
Michael Graff committed
620 621 622 623 624 625 626 627 628
			if (foundname != NULL)
				*foundname = curr;
			return (DNS_R_SUCCESS);
		}
	}

	return (DNS_R_NOTFOUND);
}

629 630 631
dns_result_t
dns_message_findtype(dns_name_t *name, dns_rdatatype_t type,
		     dns_rdatatype_t covers, dns_rdataset_t **rdataset)
Michael Graff's avatar
Michael Graff committed
632 633 634
{
	dns_rdataset_t *curr;

635 636 637 638
	if (rdataset != NULL) {
		REQUIRE(*rdataset == NULL);
	}

Michael Graff's avatar
Michael Graff committed
639 640 641
	for (curr = ISC_LIST_TAIL(name->list) ;
	     curr != NULL ;
	     curr = ISC_LIST_PREV(curr, link)) {
Bob Halley's avatar
Bob Halley committed
642
		if (curr->type == type && curr->covers == covers) {
Michael Graff's avatar
Michael Graff committed
643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664
			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
665 666 667 668
	/*
	 * First try:  use current buffer.
	 * Second try:  allocate a new buffer and use that.
	 */
Michael Graff's avatar
Michael Graff committed
669 670
	tries = 0;
	while (tries < 2) {
671
		dns_name_init(name, NULL);
Michael Graff's avatar
Michael Graff committed
672 673 674 675 676 677
		result = dns_name_fromwire(name, source, dctx, ISC_FALSE,
					   scratch);

		if (result == DNS_R_NOSPACE) {
			tries++;

678
			result = newbuffer(msg, SCRATCHPAD_SIZE);
Michael Graff's avatar
Michael Graff committed
679 680 681 682 683 684 685 686 687
			if (result != DNS_R_SUCCESS)
				return (result);

			scratch = currentbuffer(msg);
		} else {
			return (result);
		}
	}

Michael Graff's avatar
Michael Graff committed
688 689
	INSIST(0);  /* Cannot get here... */
	return (DNS_R_UNEXPECTED);
Michael Graff's avatar
Michael Graff committed
690 691
}

692 693 694 695 696 697 698 699
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;
700
	unsigned int trysize;
701

702 703 704 705 706 707 708 709
	/*
	 * 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
710
		rdata->data = (unsigned char *)""; 
711
		rdata->length = 0;
Bob Halley's avatar
Bob Halley committed
712
		rdata->rdclass = rdclass;
713 714 715 716
		rdata->type = rdtype;
		return DNS_R_SUCCESS;
	}
	    
717 718 719 720 721
	scratch = currentbuffer(msg);

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

Michael Graff's avatar
Michael Graff committed
722 723
	/*
	 * First try:  use current buffer.
724 725 726 727
	 * 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.
Michael Graff's avatar
Michael Graff committed
728
	 */
729
	tries = 0;
730 731
	trysize = 0;
	for (;;) {
732 733 734 735 736
		result = dns_rdata_fromwire(rdata, rdclass, rdtype,
					    source, dctx, ISC_FALSE,
					    scratch);

		if (result == DNS_R_NOSPACE) {
737 738 739 740 741 742 743 744 745 746 747
			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;
			}
748
			tries++;
749
			result = newbuffer(msg, trysize);
750 751 752 753 754 755 756 757 758
			if (result != DNS_R_SUCCESS)
				return (result);

			scratch = currentbuffer(msg);
		} else {
			return (result);
		}
	}
}
Michael Graff's avatar
Michael Graff committed
759

Michael Graff's avatar
Michael Graff committed
760 761 762 763 764 765 766 767 768 769
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
770 771 772
	dns_rdatatype_t rdtype;
	dns_rdataclass_t rdclass;
	dns_namelist_t *section;
773
	isc_boolean_t free_name;
Michael Graff's avatar
Michael Graff committed
774

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

777 778 779 780
	name = NULL;
	rdataset = NULL;
	rdatalist = NULL;

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

		/*
		 * Parse the name out of this packet.
		 */
790 791
		isc_buffer_remaining(source, &r);
		isc_buffer_setactive(source, r.length);
Michael Graff's avatar
Michael Graff committed
792 793
		result = getname(name, source, msg, dctx);
		if (result != DNS_R_SUCCESS)
794
			goto cleanup;
Michael Graff's avatar
Michael Graff committed
795 796 797 798 799 800 801

		/*
		 * 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.
		 */
802
		result = findname(&name2, name, 0, section);
Michael Graff's avatar
Michael Graff committed
803 804

		/*
805 806 807 808
		 * 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
809 810 811 812
		 * 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
813
		 */
814 815 816
		if (result != DNS_R_SUCCESS) {
			if (ISC_LIST_EMPTY(*section)) {
				ISC_LIST_APPEND(*section, name, link);
817
				free_name = ISC_FALSE;
818
			} else {
819
				result = DNS_R_FORMERR;
820
				goto cleanup;
821 822
			}
		} else {
823
			isc_mempool_put(msg->namepool, name);
824
			name = name2;
825
			name2 = NULL;
826
			free_name = ISC_FALSE;
827
		}
Michael Graff's avatar
Michael Graff committed
828 829 830 831

		/*
		 * Get type and class.
		 */
Michael Graff's avatar
Michael Graff committed
832
		isc_buffer_remaining(source, &r);
833 834
		if (r.length < 4) {
			result = DNS_R_UNEXPECTEDEND;
835
			goto cleanup;
836
		}
Michael Graff's avatar
Michael Graff committed
837 838
		rdtype = isc_buffer_getuint16(source);
		rdclass = isc_buffer_getuint16(source);
Michael Graff's avatar
Michael Graff committed
839 840

		/*
841
		 * If this class is different than the one we already read,
Michael Graff's avatar
Michael Graff committed
842
		 * this is an error.
Michael Graff's avatar
Michael Graff committed
843
		 */
Michael Graff's avatar
Michael Graff committed
844 845 846
		if (msg->state == DNS_SECTION_ANY) {
			msg->state = DNS_SECTION_QUESTION;
			msg->rdclass = rdclass;
847
			msg->state = DNS_SECTION_QUESTION;
848 849
		} else if (msg->rdclass != rdclass) {
			result = DNS_R_FORMERR;
850
			goto cleanup;
851
		}
Michael Graff's avatar
Michael Graff committed
852
		
Michael Graff's avatar
Michael Graff committed
853
		/*
Michael Graff's avatar
Michael Graff committed
854
		 * Can't ask the same question twice.
Michael Graff's avatar
Michael Graff committed
855
		 */
856
		result = dns_message_findtype(name, rdtype, 0, NULL);
857 858
		if (result == DNS_R_SUCCESS) {
			result = DNS_R_FORMERR;
859
			goto cleanup;
860
		}
Michael Graff's avatar
Michael Graff committed
861 862 863 864

		/*
		 * Allocate a new rdatalist.
		 */
Michael Graff's avatar
Michael Graff committed
865
		rdatalist = newrdatalist(msg);
866 867
		if (rdatalist == NULL) {
			result = DNS_R_NOMEMORY;
868
			goto cleanup;
869
		}
870
		rdataset =  isc_mempool_get(msg->rdspool);
871 872
		if (rdataset == NULL) {
			result = DNS_R_NOMEMORY;
873
			goto cleanup;
874
		}
Michael Graff's avatar
Michael Graff committed
875 876 877 878 879

		/*
		 * Convert rdatalist to rdataset, and attach the latter to
		 * the name.
		 */
Michael Graff's avatar
Michael Graff committed
880 881 882 883 884
		rdatalist->type = rdtype;
		rdatalist->rdclass = rdclass;
		rdatalist->ttl = 0;
		ISC_LIST_INIT(rdatalist->rdata);

885
		dns_rdataset_init(rdataset);
Michael Graff's avatar
Michael Graff committed
886 887
		result = dns_rdatalist_tordataset(rdatalist, rdataset);
		if (result != DNS_R_SUCCESS)
888
			goto cleanup;
889

890
		rdataset->attributes |= DNS_RDATASETATTR_QUESTION;
Michael Graff's avatar
Michael Graff committed
891 892

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

Michael Graff's avatar
Michael Graff committed
895
	return (DNS_R_SUCCESS);
896

897
 cleanup:
898 899
	if (rdataset != NULL) {
		INSIST(!dns_rdataset_isassociated(rdataset));
900
		isc_mempool_put(msg->rdspool, rdataset);
901
	}
902 903 904 905 906 907
#if 0
	if (rdatalist != NULL)
		isc_mempool_put(msg->rdlpool, rdatalist);
#endif
	if (free_name)
		isc_mempool_put(msg->namepool, name);
908 909

	return (result);
Michael Graff's avatar
Michael Graff committed
910 911 912 913
}

static dns_result_t
getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
914
	   dns_section_t sectionid, isc_boolean_t preserve_order)
Michael Graff's avatar
Michael Graff committed
915
{
Michael Graff's avatar
Michael Graff committed
916
	isc_region_t r;
917
	unsigned int count, rdatalen, attributes;
Michael Graff's avatar
Michael Graff committed
918 919 920 921 922
	dns_name_t *name;
	dns_name_t *name2;
	dns_rdataset_t *rdataset;
	dns_rdatalist_t *rdatalist;
	dns_result_t result;
Bob Halley's avatar
Bob Halley committed
923
	dns_rdatatype_t rdtype, covers;
Michael Graff's avatar
Michael Graff committed
924
	dns_rdataclass_t rdclass;
925
	dns_rdata_t *rdata;
Michael Graff's avatar
Michael Graff committed
926 927 928 929
	dns_ttl_t ttl;
	dns_namelist_t *section;

	for (count = 0 ; count < msg->counts[sectionid] ; count++) {
930
		int recstart = source->current;
931
		isc_boolean_t skip_name_search, skip_type_search;
932 933
		section = &msg->sections[sectionid];

934 935
		skip_name_search = ISC_FALSE;
		skip_type_search = ISC_FALSE;
936
		name = isc_mempool_get(msg->namepool);
Michael Graff's avatar
Michael Graff committed
937 938 939 940 941 942
		if (name == NULL)
			return (DNS_R_NOMEMORY);

		/*
		 * Parse the name out of this packet.
		 */
943 944
		isc_buffer_remaining(source, &r);
		isc_buffer_setactive(source, r.length);
Michael Graff's avatar
Michael Graff committed
945 946 947 948 949 950 951
		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
952
		 * later.)
Michael Graff's avatar
Michael Graff committed
953 954
		 */
		isc_buffer_remaining(source, &r);
Michael Graff's avatar
Michael Graff committed
955
		if (r.length < 2 + 2 + 4 + 2)
Michael Graff's avatar
Michael Graff committed
956 957 958 959
			return (DNS_R_UNEXPECTEDEND);
		rdtype = isc_buffer_getuint16(source);
		rdclass = isc_buffer_getuint16(source);

960 961 962 963 964 965 966 967 968 969 970
		/*
		 * 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)
				return (DNS_R_FORMERR);
			msg->rdclass = rdclass;
			msg->state = DNS_SECTION_QUESTION;
		}
		   
Michael Graff's avatar
Michael Graff committed
971
		/*
972 973
		 * If this class is different than the one in the question
		 * section, bail.
Michael Graff's avatar
Michael Graff committed
974
		 */