message.c 35.8 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

Michael Graff's avatar
Michael Graff committed
40
41
42
43
#define DNS_MESSAGE_OPCODE_MASK		0x7000U
#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
323
324
325
326
327
328
/*
 * Init elements to default state.  Used both when allocating a new element
 * and when resetting one.
 */
static inline void
msginit(dns_message_t *m)
{
Michael Graff's avatar
Michael Graff committed
329
330
	unsigned int i;

Michael Graff's avatar
Michael Graff committed
331
332
333
334
	m->id = 0;
	m->flags = 0;
	m->rcode = 0;
	m->opcode = 0;
Michael Graff's avatar
Michael Graff committed
335
	m->rdclass = 0;
Michael Graff's avatar
Michael Graff committed
336

Michael Graff's avatar
Michael Graff committed
337
	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

Michael Graff's avatar
Michael Graff committed
343
	m->state = DNS_SECTION_ANY;  /* indicate nothing parsed or rendered */
Michael Graff's avatar
Michael Graff committed
344

Michael Graff's avatar
Michael Graff committed
345
	m->reserved = 0;
Michael Graff's avatar
Michael Graff committed
346

347
	m->buffer = NULL;
348
	m->need_cctx_cleanup = 0;
Michael Graff's avatar
Michael Graff committed
349
350
351
352
353
354
355
356
357
358
359
360
361
}

/*
 * Free all but one (or everything) for this message.  This is used by
 * both dns_message_reset() and dns_message_parse().
 */
static void
msgreset(dns_message_t *msg, isc_boolean_t everything)
{
	dns_msgblock_t *msgblock, *next_msgblock;
	isc_dynbuffer_t *dynbuf, *next_dynbuf;
	dns_rdataset_t *rds, *next_rds;
	dns_name_t *name, *next_name;
362
363
	dns_rdata_t *rdata;
	dns_rdatalist_t *rdatalist;
Michael Graff's avatar
Michael Graff committed
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
	unsigned int i;

	/*
	 * Clean up name lists by calling the rdataset disassociate function.
	 */
	for (i = 0 ; i < DNS_SECTION_MAX ; i++) {
		name = ISC_LIST_HEAD(msg->sections[i]);
		while (name != NULL) {
			next_name = ISC_LIST_NEXT(name, link);
			ISC_LIST_UNLINK(msg->sections[i], name, link);

			rds = ISC_LIST_HEAD(name->list);
			while (rds != NULL) {
				next_rds = ISC_LIST_NEXT(rds, link);
				ISC_LIST_UNLINK(name->list, rds, link);

				dns_rdataset_disassociate(rds);
				rds = next_rds;
			}
383
			name = next_name;
Michael Graff's avatar
Michael Graff committed
384
385
386
387
388
389
390
		}
	}

	/*
	 * Clean up linked lists.
	 */

391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
	/*
	 * 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
417
418
	dynbuf = ISC_LIST_HEAD(msg->scratchpad);
	INSIST(dynbuf != NULL);
Michael Graff's avatar
Michael Graff committed
419
	if (!everything) {
Michael Graff's avatar
Michael Graff committed
420
421
422
423
424
425
426
427
428
429
430
431
		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
432
433
	if (!everything) {
		msgblock_reset(msgblock);
Michael Graff's avatar
Michael Graff committed
434
435
436
437
438
		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
439
		msgblock_free(msg->mctx, msgblock, sizeof(dns_name_t));
Michael Graff's avatar
Michael Graff committed
440
441
442
443
444
		msgblock = next_msgblock;
	}

	msgblock = ISC_LIST_HEAD(msg->rdatas);
	INSIST(msgblock != NULL);
Michael Graff's avatar
Michael Graff committed
445
446
	if (!everything) {
		msgblock_reset(msgblock);
Michael Graff's avatar
Michael Graff committed
447
448
449
450
451
		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
452
		msgblock_free(msg->mctx, msgblock, sizeof(dns_rdata_t));
Michael Graff's avatar
Michael Graff committed
453
454
455
		msgblock = next_msgblock;
	}

Michael Graff's avatar
Michael Graff committed
456
457
	msgblock = ISC_LIST_HEAD(msg->rdatasets);
	INSIST(msgblock != NULL);
Michael Graff's avatar
Michael Graff committed
458
459
	if (!everything) {
		msgblock_reset(msgblock);
Michael Graff's avatar
Michael Graff committed
460
461
462
463
464
		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
465
		msgblock_free(msg->mctx, msgblock, sizeof(dns_rdataset_t));
Michael Graff's avatar
Michael Graff committed
466
467
468
		msgblock = next_msgblock;
	}

Michael Graff's avatar
Michael Graff committed
469
	/*
Michael Graff's avatar
Michael Graff committed
470
	 * rdatalists could be empty.
Michael Graff's avatar
Michael Graff committed
471
472
473
	 */

	msgblock = ISC_LIST_HEAD(msg->rdatalists);
Michael Graff's avatar
Michael Graff committed
474
475
	if (!everything && msgblock != NULL) {
		msgblock_reset(msgblock);
Michael Graff's avatar
Michael Graff committed
476
477
478
479
480
		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
481
		msgblock_free(msg->mctx, msgblock, sizeof(dns_rdatalist_t));
Michael Graff's avatar
Michael Graff committed
482
		msgblock = next_msgblock;
Michael Graff's avatar
Michael Graff committed
483
484
	}

485
	if (msg->need_cctx_cleanup == 1)
Michael Graff's avatar
Michael Graff committed
486
487
		dns_compress_invalidate(&msg->cctx);

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

dns_result_t
496
dns_message_create(isc_mem_t *mctx, dns_message_t **msgp, unsigned int intent)
Michael Graff's avatar
Michael Graff committed
497
498
499
500
501
502
503
504
{
	dns_message_t *m;
	isc_result_t iresult;
	dns_msgblock_t *msgblock;
	isc_dynbuffer_t *dynbuf;
	unsigned int i;

	REQUIRE(mctx != NULL);
505
506
	REQUIRE(msgp != NULL);
	REQUIRE(*msgp == NULL);
Michael Graff's avatar
Michael Graff committed
507
508
	REQUIRE(intent == DNS_MESSAGE_INTENTPARSE
		|| intent == DNS_MESSAGE_INTENTRENDER);
Michael Graff's avatar
Michael Graff committed
509
510
511
512
513
514
515
516
517
518
519
520
521
522

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

	m->magic = MESSAGE_MAGIC;
	m->from_to_wire = intent;
	msginit(m);
	for (i = 0 ; i < DNS_SECTION_MAX ; i++)
		ISC_LIST_INIT(m->sections[i]);
	m->mctx = mctx;
	ISC_LIST_INIT(m->scratchpad);
	ISC_LIST_INIT(m->names);
	ISC_LIST_INIT(m->rdatas);
523
	ISC_LIST_INIT(m->rdatasets);
Michael Graff's avatar
Michael Graff committed
524
	ISC_LIST_INIT(m->rdatalists);
525
526
527
528
	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
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548

	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
549
550
551
552
	msgblock = msgblock_allocate(mctx, sizeof(dns_rdataset_t),
				     RDATASET_COUNT);
	if (msgblock == NULL)
		goto cleanup4;
553
	ISC_LIST_APPEND(m->rdatasets, msgblock, link);
Michael Graff's avatar
Michael Graff committed
554

Michael Graff's avatar
Michael Graff committed
555
	if (intent == DNS_MESSAGE_INTENTPARSE) {
Michael Graff's avatar
Michael Graff committed
556
557
558
		msgblock = msgblock_allocate(mctx, sizeof(dns_rdatalist_t),
					     RDATALIST_COUNT);
		if (msgblock == NULL)
Michael Graff's avatar
Michael Graff committed
559
			goto cleanup5;
Michael Graff's avatar
Michael Graff committed
560
561
562
		ISC_LIST_APPEND(m->rdatalists, msgblock, link);
	}

563
	*msgp = m;
Michael Graff's avatar
Michael Graff committed
564
565
566
567
568
	return (DNS_R_SUCCESS);

	/*
	 * Cleanup for error returns.
	 */
Michael Graff's avatar
Michael Graff committed
569
570
 cleanup5:
	msgblock = ISC_LIST_HEAD(m->rdatasets);
Michael Graff's avatar
Michael Graff committed
571
	msgblock_free(mctx, msgblock, sizeof(dns_rdataset_t));
Michael Graff's avatar
Michael Graff committed
572
573
 cleanup4:
	msgblock = ISC_LIST_HEAD(m->rdatas);
Michael Graff's avatar
Michael Graff committed
574
	msgblock_free(mctx, msgblock, sizeof(dns_rdata_t));
Michael Graff's avatar
Michael Graff committed
575
576
 cleanup3:
	msgblock = ISC_LIST_HEAD(m->names);
Michael Graff's avatar
Michael Graff committed
577
	msgblock_free(mctx, msgblock, sizeof(dns_name_t));
Michael Graff's avatar
Michael Graff committed
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
 cleanup2:
	dynbuf = ISC_LIST_HEAD(m->scratchpad);
	isc_dynbuffer_free(mctx, &dynbuf);
 cleanup1:
	m->magic = 0;
	isc_mem_put(mctx, m, sizeof(dns_message_t));

	return (DNS_R_NOMEMORY);
}

void
dns_message_reset(dns_message_t *msg)
{
	msgreset(msg, ISC_FALSE);
}

void
595
dns_message_destroy(dns_message_t **msgp)
Michael Graff's avatar
Michael Graff committed
596
597
598
{
	dns_message_t *msg;

599
600
	REQUIRE(msgp != NULL);
	REQUIRE(VALID_MESSAGE(*msgp));
Michael Graff's avatar
Michael Graff committed
601

602
603
	msg = *msgp;
	*msgp = NULL;
Michael Graff's avatar
Michael Graff committed
604
605
606
607
608
609

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

Michael Graff's avatar
Michael Graff committed
610
611
612
613
614
615
616
617
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
618
		if (dns_name_equal(curr, target)) {
Michael Graff's avatar
Michael Graff committed
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
			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
659
660
661
662
	/*
	 * First try:  use current buffer.
	 * Second try:  allocate a new buffer and use that.
	 */
Michael Graff's avatar
Michael Graff committed
663
664
	tries = 0;
	while (tries < 2) {
665
		dns_name_init(name, NULL);
Michael Graff's avatar
Michael Graff committed
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
		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
682
683
	INSIST(0);  /* Cannot get here... */
	return (DNS_R_UNEXPECTED);
Michael Graff's avatar
Michael Graff committed
684
685
}

686
687
688
689
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;

	scratch = currentbuffer(msg);

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

Michael Graff's avatar
Michael Graff committed
700
701
702
703
	/*
	 * First try:  use current buffer.
	 * Second try:  allocate a new buffer and use that.
	 */
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
	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
723
724
	INSIST(0);  /* Cannot get here... */
	return (DNS_R_UNEXPECTED);
725
}
Michael Graff's avatar
Michael Graff committed
726

Michael Graff's avatar
Michael Graff committed
727
728
729
730
731
732
733
734
735
736
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
737
738
739
	dns_rdatatype_t rdtype;
	dns_rdataclass_t rdclass;
	dns_namelist_t *section;
Michael Graff's avatar
Michael Graff committed
740

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

Michael Graff's avatar
Michael Graff committed
743
	for (count = 0 ; count < msg->counts[DNS_SECTION_QUESTION] ; count++) {
Michael Graff's avatar
Michael Graff committed
744
745
746
747
748
749
750
		name = newname(msg);
		if (name == NULL)
			return (DNS_R_NOMEMORY);

		/*
		 * Parse the name out of this packet.
		 */
751
752
		isc_buffer_remaining(source, &r);
		isc_buffer_setactive(source, r.length);
Michael Graff's avatar
Michael Graff committed
753
754
755
756
757
758
759
760
761
762
		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
763
		result = findname(&name2, name, section);
Michael Graff's avatar
Michael Graff committed
764
765

		/*
766
767
768
769
		 * 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
770
771
772
773
		 * 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
774
		 */
775
776
777
778
779
780
781
782
783
		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
784
785
786
787

		/*
		 * Get type and class.
		 */
Michael Graff's avatar
Michael Graff committed
788
789
790
791
792
		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
793
794

		/*
795
		 * If this class is different than the one we already read,
Michael Graff's avatar
Michael Graff committed
796
		 * this is an error.
Michael Graff's avatar
Michael Graff committed
797
		 */
Michael Graff's avatar
Michael Graff committed
798
799
800
		if (msg->state == DNS_SECTION_ANY) {
			msg->state = DNS_SECTION_QUESTION;
			msg->rdclass = rdclass;
801
			msg->state = DNS_SECTION_QUESTION;
Michael Graff's avatar
Michael Graff committed
802
803
804
		} else if (msg->rdclass != rdclass)
			return (DNS_R_FORMERR);
		
Michael Graff's avatar
Michael Graff committed
805
		/*
Michael Graff's avatar
Michael Graff committed
806
		 * Can't ask the same question twice.
Michael Graff's avatar
Michael Graff committed
807
		 */
Michael Graff's avatar
Michael Graff committed
808
809
810
		result = findtype(NULL, name, rdtype);
		if (result == DNS_R_SUCCESS)
			return (DNS_R_FORMERR);
Michael Graff's avatar
Michael Graff committed
811
812
813
814

		/*
		 * Allocate a new rdatalist.
		 */
Michael Graff's avatar
Michael Graff committed
815
		rdatalist = newrdatalist(msg);
Michael Graff's avatar
Michael Graff committed
816
817
		if (rdatalist == NULL)
			return (DNS_R_NOMEMORY);
Michael Graff's avatar
Michael Graff committed
818
		rdataset = newrdataset(msg);
Michael Graff's avatar
Michael Graff committed
819
820
		if (rdataset == NULL)
			return (DNS_R_NOMEMORY);
Michael Graff's avatar
Michael Graff committed
821
822
823
824
825

		/*
		 * Convert rdatalist to rdataset, and attach the latter to
		 * the name.
		 */
Michael Graff's avatar
Michael Graff committed
826
827
828
829
830
		rdatalist->type = rdtype;
		rdatalist->rdclass = rdclass;
		rdatalist->ttl = 0;
		ISC_LIST_INIT(rdatalist->rdata);

831
		dns_rdataset_init(rdataset);
Michael Graff's avatar
Michael Graff committed
832
833
834
835
836
		result = dns_rdatalist_tordataset(rdatalist, rdataset);
		if (result != DNS_R_SUCCESS)
			return (result);

		ISC_LIST_APPEND(name->list, rdataset, link);
Michael Graff's avatar
Michael Graff committed
837
838
839
840
841
842
843
	}
	
	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
844
	   dns_section_t sectionid)
Michael Graff's avatar
Michael Graff committed
845
{
Michael Graff's avatar
Michael Graff committed
846
847
	isc_region_t r;
	unsigned int count;
Michael Graff's avatar
Michael Graff committed
848
	unsigned int rdatalen;
Michael Graff's avatar
Michael Graff committed
849
850
851
852
853
854
855
	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;
856
	dns_rdata_t *rdata;
Michael Graff's avatar
Michael Graff committed
857
858
859
860
	dns_ttl_t ttl;
	dns_namelist_t *section;

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

Michael Graff's avatar
Michael Graff committed
863
864
865
866
867
868
869
		name = newname(msg);
		if (name == NULL)
			return (DNS_R_NOMEMORY);

		/*
		 * Parse the name out of this packet.
		 */
870
871
		isc_buffer_remaining(source, &r);
		isc_buffer_setactive(source, r.length);
Michael Graff's avatar
Michael Graff committed
872
873
874
875
876
877
878
		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
879
		 * later.)
Michael Graff's avatar
Michael Graff committed
880
881
		 */
		isc_buffer_remaining(source, &r);
Michael Graff's avatar
Michael Graff committed
882
		if (r.length < 2 + 2 + 4 + 2)
Michael Graff's avatar
Michael Graff committed
883
884
885
886
887
			return (DNS_R_UNEXPECTEDEND);
		rdtype = isc_buffer_getuint16(source);
		rdclass = isc_buffer_getuint16(source);

		/*
888
889
		 * If this class is different than the one in the question
		 * section, bail.
Michael Graff's avatar
Michael Graff committed
890
		 */
Michael Graff's avatar
Michael Graff committed
891
892
		if (msg->opcode != dns_opcode_update
		    && msg->rdclass != rdclass)
Michael Graff's avatar
Michael Graff committed
893
			return (DNS_R_FORMERR);
894
895
896
897
898
899
900
901
902
903

		/*
		 * 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);
			section = &msg->sections[DNS_SECTION_TSIG];
		}
Michael Graff's avatar
Michael Graff committed
904
905
906
907
908
909
		
		/*
		 * ... 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
910
		r.length -= (2 + 2 + 4 + 2);
Michael Graff's avatar
Michael Graff committed
911
912
913
		if (r.length < rdatalen)
			return (DNS_R_UNEXPECTEDEND);

914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
		/*
		 * 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
949
950
		/*
		 * Search name for the particular type and class.
951
		 * Skip this stage if in update mode, or this is a TSIG.
Michael Graff's avatar
Michael Graff committed
952
		 */
953
954
955
956
957
		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
958
959

		/*
960
961
962
963
964
		 * 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
965
		 */
Michael Graff's avatar
Michael Graff committed
966
		if (result == DNS_R_NOTFOUND) {
Michael Graff's avatar
Michael Graff committed
967
968
969
			rdataset = newrdataset(msg);
			if (rdataset == NULL)
				return (DNS_R_NOMEMORY);
970
971
972
			rdatalist = newrdatalist(msg);
			if (rdatalist == NULL)
				return (DNS_R_NOMEMORY);
Michael Graff's avatar
Michael Graff committed
973

974
975
976
977
			rdatalist->type = rdtype;
			rdatalist->rdclass = rdclass;
			rdatalist->ttl = ttl;
			ISC_LIST_INIT(rdatalist->rdata);
Michael Graff's avatar
Michael Graff committed
978

979
980
981
982
983
			dns_rdataset_init(rdataset);
			dns_rdatalist_tordataset(rdatalist, rdataset);

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

		/*
986
		 * Read the rdata from the wire format.
Michael Graff's avatar
Michael Graff committed
987
		 */
988
989
990
991
992
		rdata = newrdata(msg);
		if (rdata == NULL)
			return (DNS_R_NOMEMORY);
		result = getrdata(name, source, msg, dctx,
				  rdclass, rdtype, rdatalen, rdata);
Michael Graff's avatar
Michael Graff committed
993
994
995
		if (result != DNS_R_SUCCESS)
			return (result);

996
		/*
Michael Graff's avatar
Michael Graff committed
997
		 * XXXMLG Perform a totally ugly hack here to pull
998
999
1000
1001
1002
1003
1004
		 * 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);
1005
1006
1007
1008
1009
1010
1011
1012

		/*
		 * 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
1013
1014
1015
	}
	
	return (DNS_R_SUCCESS);
Michael Graff's avatar
Michael Graff committed
1016
1017
}

Michael Graff's avatar
Michael Graff committed
1018
dns_result_t
Michael Graff's avatar
Michael Graff committed
1019
dns_message_parse(dns_message_t *msg, isc_buffer_t *source)
Michael Graff's avatar
Michael Graff committed
1020
{
Michael Graff's avatar
Michael Graff committed
1021
1022
1023
	isc_region_t r;
	dns_decompress_t dctx;
	dns_result_t ret;
1024
	isc_uint16_t tmpflags;
Michael Graff's avatar
Michael Graff committed
1025
1026

	REQUIRE(VALID_MESSAGE(msg));
Michael Graff's avatar
Michael Graff committed
1027
	REQUIRE(source != NULL);
Michael Graff's avatar
Michael Graff committed
1028
	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTPARSE);
Michael Graff's avatar
Michael Graff committed
1029

Michael Graff's avatar
Michael Graff committed
1030
	isc_buffer_remaining(source, &r);
Michael Graff's avatar
Michael Graff committed
1031
	if (r.length < DNS_MESSAGE_HEADERLEN)
Michael Graff's avatar
Michael Graff committed
1032
1033
1034
		return (DNS_R_UNEXPECTEDEND);

	msg->id = isc_buffer_getuint16(source);
1035
1036
1037
1038
	tmpflags = isc_buffer_getuint16(source);
	msg->opcode = ((tmpflags & DNS_MESSAGE_OPCODE_MASK)
		       >> DNS_MESSAGE_OPCODE_SHIFT);
	msg->rcode = (tmpflags & DNS_MESSAGE_RCODE_MASK);
1039
	msg->flags = (tmpflags & DNS_MESSAGE_FLAG_MASK);
Michael Graff's avatar
Michael Graff committed
1040
1041
1042
1043
	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
1044

Michael Graff's avatar
Michael Graff committed
1045
1046
1047
	/*
	 * -1 means no EDNS.
	 */
Michael Graff's avatar
Michael Graff committed
1048
1049
	dns_decompress_init(&dctx, -1, ISC_FALSE);

Michael Graff's avatar
Michael Graff committed
1050
1051
1052
1053
1054
1055
	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
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
	ret = getquestions(source, msg, &dctx);
	if (ret != DNS_R_SUCCESS)
		return (ret);

	ret = getsection(source, msg, &dctx, DNS_SECTION_ANSWER);
	if (ret != DNS_R_SUCCESS)
		return (ret);

	ret = getsection(source, msg, &dctx, DNS_SECTION_AUTHORITY);
	if (ret != DNS_R_SUCCESS)
		return (ret);

	ret = getsection(source, msg, &dctx, DNS_SECTION_ADDITIONAL);
	if (ret != DNS_R_SUCCESS)
		return (ret);

1072
1073
1074
1075
	isc_buffer_remaining(source, &r);
	if (r.length != 0)
		return (DNS_R_FORMERR);

Michael Graff's avatar
Michael Graff committed
1076
1077
1078
1079
1080
	/*
	 * XXXMLG Need to check the tsig(s) here...
	 */

	return (DNS_R_SUCCESS);
Michael Graff's avatar
Michael Graff committed
1081
1082
1083
1084
1085
}

dns_result_t
dns_message_renderbegin(dns_message_t *msg, isc_buffer_t *buffer)
{
1086
	isc_region_t r;
Michael Graff's avatar
Michael Graff committed
1087
	dns_result_t result;
1088

Michael Graff's avatar
Michael Graff committed
1089
1090
	REQUIRE(VALID_MESSAGE(msg));
	REQUIRE(buffer != NULL);
1091
	REQUIRE(msg->buffer == NULL);
Michael Graff's avatar
Michael Graff committed
1092
	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER);
Michael Graff's avatar
Michael Graff committed
1093

1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
	/*
	 * Erase the contents of this buffer.
	 */
	isc_buffer_clear(buffer);

	/*
	 * Make certain there is enough for at least the header in this
	 * buffer.
	 */
	isc_buffer_available(buffer, &r);
Michael Graff's avatar
Michael Graff committed
1104
	REQUIRE(r.length >= DNS_MESSAGE_HEADERLEN);
1105

Michael Graff's avatar
Michael Graff committed
1106
1107
1108
	result = dns_compress_init(&msg->cctx, -1, msg->mctx);
	if (result != DNS_R_SUCCESS)
		return (result);
1109
	msg->need_cctx_cleanup = 1;
Michael Graff's avatar
Michael Graff committed
1110

1111
1112
1113
	/*
	 * Reserve enough space for the header in this buffer.
	 */
Michael Graff's avatar
Michael Graff committed
1114
	isc_buffer_add(buffer, DNS_MESSAGE_HEADERLEN);
1115

Michael Graff's avatar
Michael Graff committed
1116
1117
	msg->buffer = buffer;

1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
	return (DNS_R_SUCCESS);
}

dns_result_t
dns_message_renderchangebuffer(dns_message_t *msg, isc_buffer_t *buffer)
{
	isc_region_t r, rn;

	REQUIRE(VALID_MESSAGE(msg));
	REQUIRE(buffer != NULL);
	REQUIRE(msg->buffer != NULL);

	/*
	 * ensure that the new buffer is empty, and has enough space to
	 * hold the current contents.
	 */
	isc_buffer_clear(buffer);

	isc_buffer_available(buffer, &rn);
	isc_buffer_used(msg->buffer, &r);
Michael Graff's avatar
Michael Graff committed
1138
	REQUIRE(rn.length > r.length);
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148

	/*
	 * Copy the contents from the old to the new buffer.
	 */
	isc_buffer_add(buffer, r.length);
	memcpy(rn.base, r.base, r.length);

	msg->buffer = buffer;

	return (DNS_R_SUCCESS);
Michael Graff's avatar
Michael Graff committed
1149
1150
1151
1152
1153
1154
}

dns_result_t
dns_message_renderrelease(dns_message_t *msg, unsigned int space)
{
	REQUIRE(VALID_MESSAGE(msg));
1155
	REQUIRE(msg->buffer != NULL);
Michael Graff's avatar
Michael Graff committed
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165

	if (msg->reserved < space)
		return (DNS_R_NOSPACE);

	msg->reserved -= space;

	return (DNS_R_SUCCESS);
}

dns_result_t
1166
dns_message_renderreserve(dns_message_t *msg, unsigned int space)
Michael Graff's avatar
Michael Graff committed
1167
1168
1169
1170
{
	isc_region_t r;

	REQUIRE(VALID_MESSAGE(msg));
1171
	REQUIRE(msg->buffer != NULL);
Michael Graff's avatar
Michael Graff committed
1172

1173
	isc_buffer_available(msg->buffer, &r);
Michael Graff's avatar
Michael Graff committed
1174
1175
1176
1177
1178
1179
1180
1181
1182
	if (r.length < (space + msg->reserved))
		return (DNS_R_NOSPACE);

	msg->reserved += space;

	return (DNS_R_SUCCESS);
}

dns_result_t
1183
dns_message_rendersection(dns_message_t *msg, dns_section_t sectionid,
1184
			  unsigned int priority, unsigned int options)
Michael Graff's avatar
Michael Graff committed
1185
{
1186
	dns_namelist_t *section;
Michael Graff's avatar
Michael Graff committed
1187
1188
1189
1190
1191
	dns_name_t *name, *next_name;
	dns_rdataset_t *rdataset, *next_rdataset;
	unsigned int count, total;
	isc_boolean_t no_render_rdata;
	dns_result_t result;
Michael Graff's avatar
Michael Graff committed
1192
1193
1194
1195
	isc_buffer_t st; /* for rollbacks */

	(void)priority; /* XXXMLG implement */
	(void)options;  /* XXXMLG implement */
1196

Michael Graff's avatar
Michael Graff committed
1197
	REQUIRE(VALID_MESSAGE(msg));
1198
	REQUIRE(msg->buffer != NULL);
1199
1200
1201
	REQUIRE(VALID_NAMED_SECTION(sectionid));

	section = &msg->sections[sectionid];
Michael Graff's avatar
Michael Graff committed
1202
1203
1204
1205
1206
1207
1208
1209
	if (sectionid == DNS_SECTION_QUESTION)
		no_render_rdata = ISC_TRUE;
	else
		no_render_rdata = ISC_FALSE;

	name = ISC_LIST_HEAD(*section);
	if (name == NULL)
		return (ISC_R_SUCCESS);
Michael Graff's avatar
Michael Graff committed
1210

Michael Graff's avatar
Michael Graff committed
1211
	/*
1212
	 * Shrink the space in the buffer by the reserved amount.
Michael Graff's avatar
Michael Graff committed
1213
	 */
1214
1215
	msg->buffer->length -= msg->reserved;

Michael Graff's avatar
Michael Graff committed
1216
1217
	total = 0;
	while (name != NULL) {
Michael Graff's avatar
Michael Graff committed
1218
1219
1220
1221
1222
		next_name = ISC_LIST_NEXT(name, link);

		rdataset = ISC_LIST_HEAD(name->list);
		while (rdataset != NULL) {
			next_rdataset = ISC_LIST_NEXT(rdataset, link);
Michael Graff's avatar
Michael Graff committed
1223
1224
1225
1226
1227
1228
1229

			if (rdataset->attributes & DNS_RDATASETATTR_RENDERED) {
				rdataset = next_rdataset;
				continue;
			}

			st = *(msg->buffer);
Michael Graff's avatar
Michael Graff committed
1230
1231

			count = 0;
Michael Graff's avatar
Michael Graff committed
1232
			result = dns_rdataset_towire(rdataset, name,
1233
1234
						     &msg->cctx,
						     no_render_rdata,
1235
						     msg->buffer, &count);
Michael Graff's avatar
Michael Graff committed
1236

Michael Graff's avatar
Michael Graff committed
1237
1238
			total += count;

Michael Graff's avatar
Michael Graff committed
1239
1240
1241
			/*
			 * If out of space, record stats on what we rendered
			 * so far, and return that status.
Michael Graff's avatar
Michael Graff committed
1242
1243
1244
1245
1246
1247
1248
1249
1250
			 *
			 * XXXMLG Need to change this when
			 * dns_rdataset_towire() can render partial
			 * sets starting at some arbitary point in the set.
			 * This will include setting a bit in the
			 * rdataset to indicate that a partial rendering
			 * was done, and some state saved somewhere
			 * (probably in the message struct)
			 * to indicate where to continue from.
Michael Graff's avatar
Michael Graff committed
1251
			 */
1252
			if (result != DNS_R_SUCCESS) {
Michael Graff's avatar
Michael Graff committed
1253
1254
				dns_compress_rollback(&msg->cctx, st.used);
				*(msg->buffer) = st;  /* rollback */
1255
				msg->buffer->length += msg->reserved;
Michael Graff's avatar
Michael Graff committed
1256
1257
				msg->counts[sectionid] += total;
				return (result);
1258
			}
Michael Graff's avatar
Michael Graff committed
1259

Michael Graff's avatar
Michael Graff committed
1260
			rdataset->attributes |= DNS_RDATASETATTR_RENDERED;
Michael Graff's avatar
Michael Graff committed
1261

Michael Graff's avatar
Michael Graff committed
1262
1263
1264
1265
1266
			rdataset = next_rdataset;
		}

		name = next_name;
	}
Michael Graff's avatar
Michael Graff committed
1267

1268
	msg->buffer->length += msg->reserved;
Michael Graff's avatar
Michael Graff committed
1269
	msg->counts[sectionid] += total;
Michael Graff's avatar
Michael Graff committed
1270

1271
	return (ISC_R_SUCCESS);
Michael Graff's avatar
Michael Graff committed
1272
1273
1274
}

dns_result_t
1275
dns_message_renderend(dns_message_t *msg)
Michael Graff's avatar
Michael Graff committed
1276
{
Michael Graff's avatar
Michael Graff committed
1277
1278
	isc_buffer_t tmpbuf;
	isc_region_t r;
1279
	isc_uint16_t tmp;
Michael Graff's avatar
Michael Graff committed
1280

Michael Graff's avatar
Michael Graff committed
1281
	REQUIRE(VALID_MESSAGE(msg));
1282
1283
	REQUIRE(msg->buffer != NULL);

Michael Graff's avatar
Michael Graff committed
1284
1285
1286
1287
1288
	isc_buffer_used(msg->buffer, &r);
	isc_buffer_init(&tmpbuf, r.base, r.length, ISC_BUFFERTYPE_BINARY);

	isc_buffer_putuint16(&tmpbuf, msg->id);

1289
1290
1291
1292
	tmp = ((msg->opcode << DNS_MESSAGE_OPCODE_SHIFT)
	       & DNS_MESSAGE_OPCODE_MASK);
	tmp |= (msg->rcode & DNS_MESSAGE_RCODE_MASK);  /* XXX edns? */
	tmp |= (msg->flags & DNS_MESSAGE_FLAG_MASK);
Michael Graff's avatar
Michael Graff committed
1293

1294
	isc_buffer_putuint16(&tmpbuf, tmp);
Michael Graff's avatar
Michael Graff committed
1295
1296
1297
	isc_buffer_putuint16(&tmpbuf, msg->counts[DNS_SECTION_QUESTION]);
	isc_buffer_putuint16(&tmpbuf, msg->counts[DNS_SECTION_ANSWER]);
	isc_buffer_putuint16(&tmpbuf, msg->counts[DNS_SECTION_AUTHORITY]);
1298
1299
1300
	tmp  = msg->counts[DNS_SECTION_ADDITIONAL]
		+ msg->counts[DNS_SECTION_TSIG];
	isc_buffer_putuint16(&tmpbuf, tmp);
Michael Graff's avatar
Michael Graff committed
1301

1302
	msg->buffer = NULL;  /* forget about this buffer only on success XXX */
Michael Graff's avatar
Michael Graff committed
1303

Michael Graff's avatar
Michael Graff committed
1304
	dns_compress_invalidate(&msg->cctx);
1305
	msg->need_cctx_cleanup = 0;
Michael Graff's avatar
Michael Graff committed
1306

Michael Graff's avatar
Michael Graff committed
1307
	return (DNS_R_SUCCESS);
Michael Graff's avatar
Michael Graff committed
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
}

dns_result_t
dns_message_firstname(dns_message_t *msg, dns_section_t section)
{
	REQUIRE(VALID_MESSAGE(msg));
	REQUIRE(VALID_NAMED_SECTION(section));

	msg->cursors[section] = ISC_LIST_HEAD(msg->sections[section]);

	if (msg->cursors[section] == NULL)
		return (DNS_R_NOMORE);

Michael Graff's avatar
Michael Graff committed
1321
	return (DNS_R_SUCCESS);
Michael Graff's avatar
Michael Graff committed
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
}

dns_result_t
dns_message_nextname(dns_message_t *msg, dns_section_t section)
{
	REQUIRE(VALID_MESSAGE(msg));
	REQUIRE(VALID_NAMED_SECTION(section));
	REQUIRE(msg->cursors[section] != NULL);
	
	msg->cursors[section] = ISC_LIST_NEXT(msg->cursors[section], link);

	if (msg->cursors[section] == NULL)
		return (DNS_R_NOMORE);

Michael Graff's avatar
Michael Graff committed
1336
	return (DNS_R_SUCCESS);
Michael Graff's avatar
Michael Graff committed
1337
1338
1339
1340
1341
1342
1343
1344
}

void
dns_message_currentname(dns_message_t *msg, dns_section_t section,
			dns_name_t **name)
{
	REQUIRE(VALID_MESSAGE(msg));
	REQUIRE(VALID_NAMED_SECTION(section));
1345
	REQUIRE(name != NULL && *name == NULL);
Michael Graff's avatar
Michael Graff committed
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
	REQUIRE(msg->cursors[section] != NULL);

	*name = msg->cursors[section];
}

dns_result_t
dns_message_findname(dns_message_t *msg, dns_section_t section,
		     dns_name_t *target, dns_rdatatype_t type,
		     dns_name_t **name, dns_rdataset_t **rdataset)
{
1356
1357
	dns_name_t *foundname;
	dns_result_t result;
Michael Graff's avatar
Michael Graff committed
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376

	/*
	 * XXX These requirements are probably too intensive, especially
	 * where things can be NULL, but as they are they ensure that if
	 * something is NON-NULL, indicating that the caller expects it
	 * to be filled in, that we can in fact fill it in.
	 */
	REQUIRE(msg != NULL);
	REQUIRE(VALID_SECTION(section));
	REQUIRE(target != NULL);
	if (name != NULL)
		REQUIRE(*name == NULL);
	if (type == dns_rdatatype_any) {
		REQUIRE(rdataset == NULL);
	} else {
		if (rdataset != NULL)
			REQUIRE(*rdataset == NULL);
	}

1377
1378
1379
1380
	/*
	 * Search through, looking for the name.
	 */
	result = findname(&foundname, target, &msg->sections[section]);
1381
1382
1383
	if (result == DNS_R_NOTFOUND)
		return (DNS_R_NXDOMAIN);
	else if (result != DNS_R_SUCCESS)
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
		return (result);

	if (name != NULL)
		*name = foundname;

	/*
	 * And now look for the type.
	 */
	if (rdataset == NULL)
		return (DNS_R_SUCCESS);

	result = findtype(rdataset, foundname, type);
1396
1397
1398
	if (result == DNS_R_NOTFOUND)
		return (DNS_R_NXRDATASET);

1399
	return (result);