message.c 50.6 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 */
281
	m->opt_reserved = 0;
Michael Graff's avatar
Michael Graff committed
282
	m->reserved = 0;
283
	m->buffer = NULL;
284
	m->need_cctx_cleanup = 0;
Michael Graff's avatar
Michael Graff committed
285 286
}

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

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

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

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

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

348 349 350 351 352 353 354 355 356 357 358 359 360 361 362
static void
msgresetopt(dns_message_t *msg)
{
	if (msg->opt != NULL) {
		if (msg->opt_reserved > 0) {
			dns_message_renderrelease(msg, msg->opt_reserved);
			msg->opt_reserved = 0;
		}
		INSIST(dns_rdataset_isassociated(msg->opt));
		dns_rdataset_disassociate(msg->opt);
		isc_mempool_put(msg->rdspool, msg->opt);
		msg->opt = NULL;
	}
}

363 364 365 366 367 368 369 370
/*
 * 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;
371
	isc_buffer_t *dynbuf, *next_dynbuf;
372 373 374 375
	dns_rdata_t *rdata;
	dns_rdatalist_t *rdatalist;

	msgresetnames(msg, 0);
376
	msgresetopt(msg);
Bob Halley's avatar
EDNS0  
Bob Halley committed
377

Michael Graff's avatar
Michael Graff committed
378 379 380 381
	/*
	 * Clean up linked lists.
	 */

382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397
	/*
	 * 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
398 399
	dynbuf = ISC_LIST_HEAD(msg->scratchpad);
	INSIST(dynbuf != NULL);
Michael Graff's avatar
Michael Graff committed
400
	if (!everything) {
401
		isc_buffer_clear(dynbuf);
Michael Graff's avatar
Michael Graff committed
402 403 404 405 406
		dynbuf = ISC_LIST_NEXT(dynbuf, link);
	}
	while (dynbuf != NULL) {
		next_dynbuf = ISC_LIST_NEXT(dynbuf, link);
		ISC_LIST_UNLINK(msg->scratchpad, dynbuf, link);
407
		isc_buffer_free(&dynbuf);
Michael Graff's avatar
Michael Graff committed
408 409 410 411 412
		dynbuf = next_dynbuf;
	}

	msgblock = ISC_LIST_HEAD(msg->rdatas);
	INSIST(msgblock != NULL);
Michael Graff's avatar
Michael Graff committed
413 414
	if (!everything) {
		msgblock_reset(msgblock);
Michael Graff's avatar
Michael Graff committed
415 416 417 418 419
		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
420
		msgblock_free(msg->mctx, msgblock, sizeof(dns_rdata_t));
Michael Graff's avatar
Michael Graff committed
421 422 423
		msgblock = next_msgblock;
	}

Michael Graff's avatar
Michael Graff committed
424
	/*
Michael Graff's avatar
Michael Graff committed
425
	 * rdatalists could be empty.
Michael Graff's avatar
Michael Graff committed
426 427 428
	 */

	msgblock = ISC_LIST_HEAD(msg->rdatalists);
Michael Graff's avatar
Michael Graff committed
429 430
	if (!everything && msgblock != NULL) {
		msgblock_reset(msgblock);
Michael Graff's avatar
Michael Graff committed
431 432 433 434 435
		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
436
		msgblock_free(msg->mctx, msgblock, sizeof(dns_rdatalist_t));
Michael Graff's avatar
Michael Graff committed
437
		msgblock = next_msgblock;
Michael Graff's avatar
Michael Graff committed
438 439
	}

440
	if (msg->need_cctx_cleanup == 1)
Michael Graff's avatar
Michael Graff committed
441 442
		dns_compress_invalidate(&msg->cctx);

443 444
	if (msg->tsig != NULL) {
		dns_rdata_freestruct(msg->tsig);
Bob Halley's avatar
EDNS0  
Bob Halley committed
445 446
		isc_mem_put(msg->mctx, msg->tsig,
			    sizeof(dns_rdata_any_tsig_t));
447
		msg->tsig = NULL;
448 449 450 451 452 453
	}

	if (msg->querytsig != NULL) {
		dns_rdata_freestruct(msg->querytsig);
		isc_mem_put(msg->mctx, msg->querytsig,
			    sizeof(dns_rdata_any_tsig_t));
454
		msg->querytsig = NULL;
455 456
        }

457
	if (msg->tsigkey != NULL) {
458
		dns_tsigkey_free(&msg->tsigkey);
459 460 461 462 463 464 465 466
		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;
	}
467

Brian Wellington's avatar
Brian Wellington committed
468 469 470 471 472 473
	if (msg->saved != NULL) {
		isc_mem_put(msg->mctx, msg->saved->base, msg->saved->length);
		isc_mem_put(msg->mctx, msg->saved, sizeof(isc_region_t));
		msg->saved = NULL;
	}

474 475 476 477 478 479
	/*
	 * cleanup the buffer cleanup list
	 */
	dynbuf = ISC_LIST_HEAD(msg->cleanup);
	while (dynbuf != NULL) {
		next_dynbuf = ISC_LIST_NEXT(dynbuf, link);
480
		ISC_LIST_UNLINK(msg->cleanup, dynbuf, link);
481 482 483 484
		isc_buffer_free(&dynbuf);
		dynbuf = next_dynbuf;
	}

Michael Graff's avatar
Michael Graff committed
485 486 487
	/*
	 * Set other bits to normal default values.
	 */
Michael Graff's avatar
Michael Graff committed
488 489
	if (!everything)
		msginit(msg);
Michael Graff's avatar
Michael Graff committed
490 491 492
}

dns_result_t
493
dns_message_create(isc_mem_t *mctx, unsigned int intent, dns_message_t **msgp)
Michael Graff's avatar
Michael Graff committed
494 495
{
	dns_message_t *m;
496
	isc_result_t result;
Michael Graff's avatar
Michael Graff committed
497
	dns_msgblock_t *msgblock;
498
	isc_buffer_t *dynbuf;
Michael Graff's avatar
Michael Graff committed
499 500 501
	unsigned int i;

	REQUIRE(mctx != NULL);
502 503
	REQUIRE(msgp != NULL);
	REQUIRE(*msgp == NULL);
Michael Graff's avatar
Michael Graff committed
504 505
	REQUIRE(intent == DNS_MESSAGE_INTENTPARSE
		|| intent == DNS_MESSAGE_INTENTRENDER);
Michael Graff's avatar
Michael Graff committed
506 507 508 509 510

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

511 512 513 514 515
	/*
	 * No allocations until further notice.  Just initialize all lists
	 * and other members that are freed in the cleanup phase here.
	 */

516
	m->magic = DNS_MESSAGE_MAGIC;
Michael Graff's avatar
Michael Graff committed
517 518
	m->from_to_wire = intent;
	msginit(m);
519

Michael Graff's avatar
Michael Graff committed
520 521 522
	for (i = 0 ; i < DNS_SECTION_MAX ; i++)
		ISC_LIST_INIT(m->sections[i]);
	m->mctx = mctx;
523

Michael Graff's avatar
Michael Graff committed
524
	ISC_LIST_INIT(m->scratchpad);
525 526
	ISC_LIST_INIT(m->cleanup);
	m->namepool = NULL;
527
	m->rdspool = NULL;
Michael Graff's avatar
Michael Graff committed
528 529
	ISC_LIST_INIT(m->rdatas);
	ISC_LIST_INIT(m->rdatalists);
530 531
	ISC_LIST_INIT(m->freerdata);
	ISC_LIST_INIT(m->freerdatalist);
Michael Graff's avatar
Michael Graff committed
532

533 534 535 536 537 538 539 540 541
	/*
	 * 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
542
	isc_mempool_setname(m->namepool, "msg:names");
543

544 545 546 547 548 549
	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
550
	isc_mempool_setname(m->rdspool, "msg:rdataset");
551

Michael Graff's avatar
Michael Graff committed
552
	dynbuf = NULL;
553 554 555 556
	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
557 558 559 560 561
	ISC_LIST_APPEND(m->scratchpad, dynbuf, link);

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

Michael Graff's avatar
Michael Graff committed
565
	if (intent == DNS_MESSAGE_INTENTPARSE) {
Michael Graff's avatar
Michael Graff committed
566 567 568
		msgblock = msgblock_allocate(mctx, sizeof(dns_rdatalist_t),
					     RDATALIST_COUNT);
		if (msgblock == NULL)
569
			goto cleanup;
Michael Graff's avatar
Michael Graff committed
570 571 572
		ISC_LIST_APPEND(m->rdatalists, msgblock, link);
	}

573
	*msgp = m;
Michael Graff's avatar
Michael Graff committed
574 575 576 577 578
	return (DNS_R_SUCCESS);

	/*
	 * Cleanup for error returns.
	 */
579
 cleanup:
Michael Graff's avatar
Michael Graff committed
580
	msgblock = ISC_LIST_HEAD(m->rdatas);
581 582
	if (msgblock != NULL)
		msgblock_free(mctx, msgblock, sizeof(dns_rdata_t));
Michael Graff's avatar
Michael Graff committed
583
	dynbuf = ISC_LIST_HEAD(m->scratchpad);
584 585 586 587
	if (dynbuf != NULL)
		isc_buffer_free(&dynbuf);
	if (m->namepool != NULL)
		isc_mempool_destroy(&m->namepool);
588 589
	if (m->rdspool != NULL)
		isc_mempool_destroy(&m->rdspool);
Michael Graff's avatar
Michael Graff committed
590 591 592 593 594 595 596
	m->magic = 0;
	isc_mem_put(mctx, m, sizeof(dns_message_t));

	return (DNS_R_NOMEMORY);
}

void
597
dns_message_reset(dns_message_t *msg, unsigned int intent)
Michael Graff's avatar
Michael Graff committed
598
{
599 600 601 602 603
	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
604 605 606 607
	msgreset(msg, ISC_FALSE);
}

void
608
dns_message_destroy(dns_message_t **msgp)
Michael Graff's avatar
Michael Graff committed
609 610 611
{
	dns_message_t *msg;

612
	REQUIRE(msgp != NULL);
613
	REQUIRE(DNS_MESSAGE_VALID(*msgp));
Michael Graff's avatar
Michael Graff committed
614

615 616
	msg = *msgp;
	*msgp = NULL;
Michael Graff's avatar
Michael Graff committed
617 618

	msgreset(msg, ISC_TRUE);
619
	isc_mempool_destroy(&msg->namepool);
620
	isc_mempool_destroy(&msg->rdspool);
Michael Graff's avatar
Michael Graff committed
621 622 623 624
	msg->magic = 0;
	isc_mem_put(msg->mctx, msg, sizeof(dns_message_t));
}

Michael Graff's avatar
Michael Graff committed
625
static dns_result_t
626 627
findname(dns_name_t **foundname, dns_name_t *target, unsigned int attributes,
	 dns_namelist_t *section)
Michael Graff's avatar
Michael Graff committed
628 629 630 631 632 633
{
	dns_name_t *curr;

	for (curr = ISC_LIST_TAIL(*section) ;
	     curr != NULL ;
	     curr = ISC_LIST_PREV(curr, link)) {
634 635
		if (dns_name_equal(curr, target) &&
		    (curr->attributes & attributes) == attributes) {
Michael Graff's avatar
Michael Graff committed
636 637 638 639 640 641 642 643 644
			if (foundname != NULL)
				*foundname = curr;
			return (DNS_R_SUCCESS);
		}
	}

	return (DNS_R_NOTFOUND);
}

645 646 647
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
648 649 650
{
	dns_rdataset_t *curr;

651 652 653 654
	if (rdataset != NULL) {
		REQUIRE(*rdataset == NULL);
	}

Michael Graff's avatar
Michael Graff committed
655 656 657
	for (curr = ISC_LIST_TAIL(name->list) ;
	     curr != NULL ;
	     curr = ISC_LIST_PREV(curr, link)) {
Bob Halley's avatar
Bob Halley committed
658
		if (curr->type == type && curr->covers == covers) {
Michael Graff's avatar
Michael Graff committed
659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680
			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
681 682 683 684
	/*
	 * First try:  use current buffer.
	 * Second try:  allocate a new buffer and use that.
	 */
Michael Graff's avatar
Michael Graff committed
685 686
	tries = 0;
	while (tries < 2) {
687
		dns_name_init(name, NULL);
Michael Graff's avatar
Michael Graff committed
688 689 690 691 692 693
		result = dns_name_fromwire(name, source, dctx, ISC_FALSE,
					   scratch);

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

694
			result = newbuffer(msg, SCRATCHPAD_SIZE);
Michael Graff's avatar
Michael Graff committed
695 696 697 698 699 700 701 702 703
			if (result != DNS_R_SUCCESS)
				return (result);

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

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

708 709 710 711 712 713 714 715
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;
716
	unsigned int trysize;
717

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
		rdata->length = 0;
Bob Halley's avatar
Bob Halley committed
728
		rdata->rdclass = rdclass;
729 730 731 732
		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
	/*
	 * First try:  use current buffer.
740 741 742 743
	 * 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
744
	 */
745
	tries = 0;
746 747
	trysize = 0;
	for (;;) {
748 749 750 751 752
		result = dns_rdata_fromwire(rdata, rdclass, rdtype,
					    source, dctx, ISC_FALSE,
					    scratch);

		if (result == DNS_R_NOSPACE) {
753 754 755 756 757 758 759 760 761 762 763
			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;
			}
764
			tries++;
765
			result = newbuffer(msg, trysize);
766 767 768 769 770 771 772 773 774
			if (result != DNS_R_SUCCESS)
				return (result);

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

Michael Graff's avatar
Michael Graff committed
776 777 778 779 780 781 782 783 784 785
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
786 787 788
	dns_rdatatype_t rdtype;
	dns_rdataclass_t rdclass;
	dns_namelist_t *section;
789
	isc_boolean_t free_name;
Michael Graff's avatar
Michael Graff committed
790

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

793 794 795 796
	name = NULL;
	rdataset = NULL;
	rdatalist = NULL;

Michael Graff's avatar
Michael Graff committed
797
	for (count = 0 ; count < msg->counts[DNS_SECTION_QUESTION] ; count++) {
798
		name = isc_mempool_get(msg->namepool);
Michael Graff's avatar
Michael Graff committed
799 800
		if (name == NULL)
			return (DNS_R_NOMEMORY);
801
		free_name = ISC_TRUE;
Michael Graff's avatar
Michael Graff committed
802 803 804 805

		/*
		 * Parse the name out of this packet.
		 */
806 807
		isc_buffer_remaining(source, &r);
		isc_buffer_setactive(source, r.length);
Michael Graff's avatar
Michael Graff committed
808 809
		result = getname(name, source, msg, dctx);
		if (result != DNS_R_SUCCESS)
810
			goto cleanup;
Michael Graff's avatar
Michael Graff committed
811 812 813 814 815 816 817

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

		/*
821 822 823 824
		 * 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
825 826 827 828
		 * 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
829
		 */
830 831 832
		if (result != DNS_R_SUCCESS) {
			if (ISC_LIST_EMPTY(*section)) {
				ISC_LIST_APPEND(*section, name, link);
833
				free_name = ISC_FALSE;
834
			} else {
835
				result = DNS_R_FORMERR;
836
				goto cleanup;
837 838
			}
		} else {
839
			isc_mempool_put(msg->namepool, name);
840
			name = name2;
841
			name2 = NULL;
842
			free_name = ISC_FALSE;
843
		}
Michael Graff's avatar
Michael Graff committed
844 845 846 847

		/*
		 * Get type and class.
		 */
Michael Graff's avatar
Michael Graff committed
848
		isc_buffer_remaining(source, &r);
849 850
		if (r.length < 4) {
			result = DNS_R_UNEXPECTEDEND;
851
			goto cleanup;
852
		}
Michael Graff's avatar
Michael Graff committed
853 854
		rdtype = isc_buffer_getuint16(source);
		rdclass = isc_buffer_getuint16(source);
Michael Graff's avatar
Michael Graff committed
855 856

		/*
857
		 * If this class is different than the one we already read,
Michael Graff's avatar
Michael Graff committed
858
		 * this is an error.
Michael Graff's avatar
Michael Graff committed
859
		 */
Michael Graff's avatar
Michael Graff committed
860 861 862
		if (msg->state == DNS_SECTION_ANY) {
			msg->state = DNS_SECTION_QUESTION;
			msg->rdclass = rdclass;
863
			msg->state = DNS_SECTION_QUESTION;
864 865
		} else if (msg->rdclass != rdclass) {
			result = DNS_R_FORMERR;
866
			goto cleanup;
867
		}
Michael Graff's avatar
Michael Graff committed
868
		
Michael Graff's avatar
Michael Graff committed
869
		/*
Michael Graff's avatar
Michael Graff committed
870
		 * Can't ask the same question twice.
Michael Graff's avatar
Michael Graff committed
871
		 */
872
		result = dns_message_findtype(name, rdtype, 0, NULL);
873 874
		if (result == DNS_R_SUCCESS) {
			result = DNS_R_FORMERR;
875
			goto cleanup;
876
		}
Michael Graff's avatar
Michael Graff committed
877 878 879 880

		/*
		 * Allocate a new rdatalist.
		 */
Michael Graff's avatar
Michael Graff committed
881
		rdatalist = newrdatalist(msg);
882 883
		if (rdatalist == NULL) {
			result = DNS_R_NOMEMORY;
884
			goto cleanup;
885
		}
886
		rdataset =  isc_mempool_get(msg->rdspool);
887 888
		if (rdataset == NULL) {
			result = DNS_R_NOMEMORY;
889
			goto cleanup;
890
		}
Michael Graff's avatar
Michael Graff committed
891 892 893 894 895

		/*
		 * Convert rdatalist to rdataset, and attach the latter to
		 * the name.
		 */
Michael Graff's avatar
Michael Graff committed
896
		rdatalist->type = rdtype;
897
		rdatalist->covers = 0;
Michael Graff's avatar
Michael Graff committed
898 899 900 901
		rdatalist->rdclass = rdclass;
		rdatalist->ttl = 0;
		ISC_LIST_INIT(rdatalist->rdata);

902
		dns_rdataset_init(rdataset);
Michael Graff's avatar
Michael Graff committed
903 904
		result = dns_rdatalist_tordataset(rdatalist, rdataset);
		if (result != DNS_R_SUCCESS)
905
			goto cleanup;
906

907
		rdataset->attributes |= DNS_RDATASETATTR_QUESTION;
Michael Graff's avatar
Michael Graff committed
908 909

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

Michael Graff's avatar
Michael Graff committed
912
	return (DNS_R_SUCCESS);
913

914
 cleanup:
915 916
	if (rdataset != NULL) {
		INSIST(!dns_rdataset_isassociated(rdataset));
917
		isc_mempool_put(msg->rdspool, rdataset);
918
	}
919 920 921 922 923 924
#if 0
	if (rdatalist != NULL)
		isc_mempool_put(msg->rdlpool, rdatalist);
#endif
	if (free_name)
		isc_mempool_put(msg->namepool, name);
925 926

	return (result);
Michael Graff's avatar
Michael Graff committed
927 928 929 930
}

static dns_result_t
getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
931
	   dns_section_t sectionid, isc_boolean_t preserve_order)
Michael Graff's avatar
Michael Graff committed
932
{
Michael Graff's avatar
Michael Graff committed
933
	isc_region_t r;
934
	unsigned int count, rdatalen, attributes;
Michael Graff's avatar
Michael Graff committed
935 936 937 938 939
	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
940
	dns_rdatatype_t rdtype, covers;
Michael Graff's avatar
Michael Graff committed
941
	dns_rdataclass_t rdclass;
942
	dns_rdata_t *rdata;
Michael Graff's avatar
Michael Graff committed
943 944
	dns_ttl_t ttl;
	dns_namelist_t *section;
945
	isc_boolean_t free_name, free_rdataset;
Michael Graff's avatar
Michael Graff committed
946 947

	for (count = 0 ; count < msg->counts[sectionid] ; count++) {
948
		int recstart = source->current;
949
		isc_boolean_t skip_name_search, skip_type_search;
950

951 952
		section = &msg->sections[sectionid];

953 954
		skip_name_search = ISC_FALSE;
		skip_type_search = ISC_FALSE;
955 956 957
		free_name = ISC_FALSE;
		free_rdataset = ISC_FALSE;

958
		name = isc_mempool_get(msg->namepool);
Michael Graff's avatar
Michael Graff committed
959 960
		if (name == NULL)
			return (DNS_R_NOMEMORY);
961
		free_name = ISC_TRUE;
Michael Graff's avatar
Michael Graff committed
962 963 964 965

		/*
		 * Parse the name out of this packet.
		 */
966 967
		isc_buffer_remaining(source, &r);
		isc_buffer_setactive(source, r.length);
Michael Graff's avatar
Michael Graff committed
968 969
		result = getname(