rdata.c 49.8 KB
Newer Older
1
/*
Mark Andrews's avatar
Mark Andrews committed
2
 * Copyright (C) 1998-2003  Internet Software Consortium.
3
 *
4 5 6
 * 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.
7
 *
8 9 10 11 12 13 14 15
 * 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.
16 17
 */

Mark Andrews's avatar
Mark Andrews committed
18
/* $Id: rdata.c,v 1.181 2003/10/17 03:46:43 marka Exp $ */
19 20

#include <config.h>
21
#include <ctype.h>
22

23
#include <isc/base64.h>
24
#include <isc/hex.h>
25
#include <isc/lex.h>
26
#include <isc/mem.h>
27
#include <isc/parseint.h>
Mark Andrews's avatar
Mark Andrews committed
28
#include <isc/print.h>
29
#include <isc/string.h>
30
#include <isc/stdlib.h>
Mark Andrews's avatar
Mark Andrews committed
31
#include <isc/util.h>
32

33 34 35 36
#include <dns/callbacks.h>
#include <dns/cert.h>
#include <dns/compress.h>
#include <dns/keyflags.h>
37
#include <dns/keyvalues.h>
38
#include <dns/rcode.h>
39
#include <dns/rdata.h>
40
#include <dns/rdataclass.h>
41
#include <dns/rdatastruct.h>
42
#include <dns/rdatatype.h>
43
#include <dns/result.h>
Mark Andrews's avatar
Mark Andrews committed
44
#include <dns/secalg.h>
45
#include <dns/secproto.h>
46 47
#include <dns/time.h>
#include <dns/ttl.h>
48

49 50 51 52 53 54
#define RETERR(x) \
	do { \
		isc_result_t _r = (x); \
		if (_r != ISC_R_SUCCESS) \
			return (_r); \
	} while (0)
55

56 57 58 59 60 61 62
#define RETTOK(x) \
	do { \
		isc_result_t _r = (x); \
		if (_r != ISC_R_SUCCESS) { \
			isc_lex_ungettoken(lexer, &token); \
			return (_r); \
		} \
63
	} while (0)
64

65 66
#define DNS_AS_STR(t) ((t).value.as_textregion.base)

67
#define ARGS_FROMTEXT	int rdclass, dns_rdatatype_t type, \
David Lawrence's avatar
David Lawrence committed
68
			isc_lex_t *lexer, dns_name_t *origin, \
69 70
			isc_boolean_t downcase, isc_buffer_t *target, \
			dns_rdatacallbacks_t *callbacks
David Lawrence's avatar
David Lawrence committed
71 72 73 74

#define ARGS_TOTEXT	dns_rdata_t *rdata, dns_rdata_textctx_t *tctx, \
			isc_buffer_t *target

75
#define ARGS_FROMWIRE	int rdclass, dns_rdatatype_t type, \
David Lawrence's avatar
David Lawrence committed
76 77 78 79 80 81 82 83
			isc_buffer_t *source, dns_decompress_t *dctx, \
			isc_boolean_t downcase, isc_buffer_t *target

#define ARGS_TOWIRE	dns_rdata_t *rdata, dns_compress_t *cctx, \
			isc_buffer_t *target

#define ARGS_COMPARE	const dns_rdata_t *rdata1, const dns_rdata_t *rdata2

84
#define ARGS_FROMSTRUCT	int rdclass, dns_rdatatype_t type, \
David Lawrence's avatar
David Lawrence committed
85 86 87 88 89 90 91 92 93 94 95
			void *source, isc_buffer_t *target

#define ARGS_TOSTRUCT	dns_rdata_t *rdata, void *target, isc_mem_t *mctx

#define ARGS_FREESTRUCT void *source

#define ARGS_ADDLDATA	dns_rdata_t *rdata, dns_additionaldatafunc_t add, \
			void *arg

#define ARGS_DIGEST	dns_rdata_t *rdata, dns_digestfunc_t digest, void *arg

96
/*
97
 * Context structure for the totext_ functions.
98 99 100 101 102 103 104
 * Contains formatting options for rdata-to-text
 * conversion.
 */
typedef struct dns_rdata_textctx {
	dns_name_t *origin;	/* Current origin, or NULL. */
	unsigned int flags;	/* DNS_STYLEFLAG_* */
	unsigned int width;	/* Width of rdata column. */
David Lawrence's avatar
David Lawrence committed
105
  	const char *linebreak;	/* Line break string. */
106 107
} dns_rdata_textctx_t;

108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
static isc_result_t
txt_totext(isc_region_t *source, isc_buffer_t *target);

static isc_result_t
txt_fromtext(isc_textregion_t *source, isc_buffer_t *target);

static isc_result_t
txt_fromwire(isc_buffer_t *source, isc_buffer_t *target);

static isc_boolean_t
name_prefix(dns_name_t *name, dns_name_t *origin, dns_name_t *target);

static unsigned int
name_length(dns_name_t *name);

static isc_result_t
David Lawrence's avatar
David Lawrence committed
124
str_totext(const char *source, isc_buffer_t *target);
125

126 127 128
static isc_result_t
inet_totext(int af, isc_region_t *src, isc_buffer_t *target);

129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
static isc_boolean_t
buffer_empty(isc_buffer_t *source);

static void
buffer_fromregion(isc_buffer_t *buffer, isc_region_t *region);

static isc_result_t
uint32_tobuffer(isc_uint32_t, isc_buffer_t *target);

static isc_result_t
uint16_tobuffer(isc_uint32_t, isc_buffer_t *target);

static isc_result_t
uint8_tobuffer(isc_uint32_t, isc_buffer_t *target);

static isc_result_t
name_tobuffer(dns_name_t *name, isc_buffer_t *target);

static isc_uint32_t
uint32_fromregion(isc_region_t *region);

static isc_uint16_t
uint16_fromregion(isc_region_t *region);

static isc_uint8_t
uint8_fromregion(isc_region_t *region);

static isc_result_t
mem_tobuffer(isc_buffer_t *target, void *base, unsigned int length);

static int
hexvalue(char value);

static int
decvalue(char value);

static isc_result_t
btoa_totext(unsigned char *inbuf, int inbuflen, isc_buffer_t *target);

static isc_result_t
atob_tobuffer(isc_lex_t *lexer, isc_buffer_t *target);

static void
172 173
default_fromtext_callback(dns_rdatacallbacks_t *callbacks, const char *, ...)
     ISC_FORMAT_PRINTF(2, 3);
174 175

static void
David Lawrence's avatar
David Lawrence committed
176 177 178
fromtext_error(void (*callback)(dns_rdatacallbacks_t *, const char *, ...),
	       dns_rdatacallbacks_t *callbacks, const char *name,
	       unsigned long line, isc_token_t *token, isc_result_t result);
179

180 181 182
static void
fromtext_warneof(isc_lex_t *lexer, dns_rdatacallbacks_t *callbacks);

183 184 185
static isc_result_t
rdata_totext(dns_rdata_t *rdata, dns_rdata_textctx_t *tctx,
	     isc_buffer_t *target);
186

187 188 189 190 191 192 193 194 195 196
static inline int
getquad(const void *src, struct in_addr *dst,
	isc_lex_t *lexer, dns_rdatacallbacks_t *callbacks)
{
	int result;
	struct in_addr *tmp;

	result = inet_aton(src, dst);
	if (result == 1 && callbacks != NULL &&
	    inet_pton(AF_INET, src, &tmp) != 1) {
197
		const char *name = isc_lex_getsourcename(lexer);
198 199
		if (name == NULL)
			name = "UNKNOWN";
200
		(*callbacks->warn)(callbacks, "%s:%lu: \"%s\" "
201 202
				   "is not a decimal dotted quad", name,
				   isc_lex_getsourceline(lexer), src);
203 204 205 206
	}
	return (result);
}

207 208 209 210
static inline isc_result_t
name_duporclone(dns_name_t *source, isc_mem_t *mctx, dns_name_t *target) {

	if (mctx != NULL)
Brian Wellington's avatar
Brian Wellington committed
211
		return (dns_name_dup(source, mctx, target));
212 213 214 215 216 217 218 219 220 221 222 223 224
	dns_name_clone(source, target);
	return (ISC_R_SUCCESS);
}

static inline void *
mem_maybedup(isc_mem_t *mctx, void *source, size_t length) {
	void *new;

	if (mctx == NULL)
		return (source);
	new = isc_mem_allocate(mctx, length);
	if (new != NULL)
		memcpy(new, source, length);
225

226 227 228
	return (new);
}

229 230
static const char hexdigits[] = "0123456789abcdef";
static const char decdigits[] = "0123456789";
231 232 233

#include "code.h"

234 235 236
#define META 0x0001
#define RESERVED 0x0002

237 238 239 240 241 242 243 244 245 246 247 248
#define RCODENAMES \
	/* standard rcodes */ \
	{ dns_rcode_noerror, "NOERROR", 0}, \
	{ dns_rcode_formerr, "FORMERR", 0}, \
	{ dns_rcode_servfail, "SERVFAIL", 0}, \
	{ dns_rcode_nxdomain, "NXDOMAIN", 0}, \
	{ dns_rcode_notimp, "NOTIMP", 0}, \
	{ dns_rcode_refused, "REFUSED", 0}, \
	{ dns_rcode_yxdomain, "YXDOMAIN", 0}, \
	{ dns_rcode_yxrrset, "YXRRSET", 0}, \
	{ dns_rcode_nxrrset, "NXRRSET", 0}, \
	{ dns_rcode_notauth, "NOTAUTH", 0}, \
249 250 251
	{ dns_rcode_notzone, "NOTZONE", 0},

#define ERCODENAMES \
252
	/* extended rcodes */ \
253
	{ dns_rcode_badvers, "BADVERS", 0}, \
254 255
	{ 0, NULL, 0 }

256 257 258 259 260 261 262 263 264 265
#define TSIGRCODENAMES \
	/* extended rcodes */ \
	{ dns_tsigerror_badsig, "BADSIG", 0}, \
	{ dns_tsigerror_badkey, "BADKEY", 0}, \
	{ dns_tsigerror_badtime, "BADTIME", 0}, \
	{ dns_tsigerror_badmode, "BADMODE", 0}, \
	{ dns_tsigerror_badname, "BADNAME", 0}, \
	{ dns_tsigerror_badalg, "BADALG", 0}, \
	{ 0, NULL, 0 }

266 267
/* RFC2538 section 2.1 */

Mark Andrews's avatar
Mark Andrews committed
268
#define CERTNAMES \
269
	{ 1, "PKIX", 0}, \
Mark Andrews's avatar
Mark Andrews committed
270 271 272 273 274 275
	{ 2, "SPKI", 0}, \
	{ 3, "PGP", 0}, \
	{ 253, "URI", 0}, \
	{ 254, "OID", 0}, \
	{ 0, NULL, 0}

276
/* RFC2535 section 7, RFC3110 */
Mark Andrews's avatar
Mark Andrews committed
277

Mark Andrews's avatar
Mark Andrews committed
278
#define SECALGNAMES \
279
	{ DNS_KEYALG_RSAMD5, "RSAMD5", 0 }, \
280
	{ DNS_KEYALG_RSAMD5, "RSA", 0 }, \
281 282 283 284 285 286 287
	{ DNS_KEYALG_DH, "DH", 0 }, \
	{ DNS_KEYALG_DSA, "DSA", 0 }, \
	{ DNS_KEYALG_ECC, "ECC", 0 }, \
	{ DNS_KEYALG_RSASHA1, "RSASHA1", 0 }, \
	{ DNS_KEYALG_INDIRECT, "INDIRECT", 0 }, \
	{ DNS_KEYALG_PRIVATEDNS, "PRIVATEDNS", 0 }, \
	{ DNS_KEYALG_PRIVATEOID, "PRIVATEOID", 0 }, \
Mark Andrews's avatar
Mark Andrews committed
288 289
	{ 0, NULL, 0}

290 291 292 293 294 295 296 297 298 299
/* RFC2535 section 7.1 */

#define SECPROTONAMES \
	{   0,    "NONE", 0 }, \
	{   1,    "TLS", 0 }, \
	{   2,    "EMAIL", 0 }, \
	{   3,    "DNSSEC", 0 }, \
	{   4,    "IPSEC", 0 }, \
	{ 255,    "ALL", 0 }, \
	{ 0, NULL, 0}
Mark Andrews's avatar
Mark Andrews committed
300

301
struct tbl {
302
	unsigned int	value;
David Lawrence's avatar
David Lawrence committed
303 304
	const char	*name;
	int		flags;
305 306
};

307 308
static struct tbl rcodes[] = { RCODENAMES ERCODENAMES };
static struct tbl tsigrcodes[] = { RCODENAMES TSIGRCODENAMES };
309 310 311
static struct tbl certs[] = { CERTNAMES };
static struct tbl secalgs[] = { SECALGNAMES };
static struct tbl secprotos[] = { SECPROTONAMES };
312 313

static struct keyflag {
David Lawrence's avatar
David Lawrence committed
314
	const char *name;
315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348
	unsigned int value;
	unsigned int mask;
} keyflags[] = {
	{ "NOCONF", 0x4000, 0xC000 },
	{ "NOAUTH", 0x8000, 0xC000 },
	{ "NOKEY",  0xC000, 0xC000 },
	{ "FLAG2",  0x2000, 0x2000 },
	{ "EXTEND", 0x1000, 0x1000 },
	{ "FLAG4",  0x0800, 0x0800 },
	{ "FLAG5",  0x0400, 0x0400 },
	{ "USER",   0x0000, 0x0300 },
	{ "ZONE",   0x0100, 0x0300 },
	{ "HOST",   0x0200, 0x0300 },
	{ "NTYP3",  0x0300, 0x0300 },
	{ "FLAG8",  0x0080, 0x0080 },
	{ "FLAG9",  0x0040, 0x0040 },
	{ "FLAG10", 0x0020, 0x0020 },
	{ "FLAG11", 0x0010, 0x0010 },
	{ "SIG0",   0x0000, 0x000F },
	{ "SIG1",   0x0001, 0x000F },
	{ "SIG2",   0x0002, 0x000F },
	{ "SIG3",   0x0003, 0x000F },
	{ "SIG4",   0x0004, 0x000F },
	{ "SIG5",   0x0005, 0x000F },
	{ "SIG6",   0x0006, 0x000F },
	{ "SIG7",   0x0007, 0x000F },
	{ "SIG8",   0x0008, 0x000F },
	{ "SIG9",   0x0009, 0x000F },
	{ "SIG10",  0x000A, 0x000F },
	{ "SIG11",  0x000B, 0x000F },
	{ "SIG12",  0x000C, 0x000F },
	{ "SIG13",  0x000D, 0x000F },
	{ "SIG14",  0x000E, 0x000F },
	{ "SIG15",  0x000F, 0x000F },
349
	{ "KSK",  DNS_KEYFLAG_KSK, DNS_KEYFLAG_KSK },
350
	{ NULL,     0, 0 }
351
};
352

353 354 355 356 357 358 359 360 361 362 363
/***
 *** Initialization
 ***/

void
dns_rdata_init(dns_rdata_t *rdata) {

	REQUIRE(rdata != NULL);

	rdata->data = NULL;
	rdata->length = 0;
364
	rdata->rdclass = 0;
365
	rdata->type = 0;
Mark Andrews's avatar
Mark Andrews committed
366
	rdata->flags = 0;
367 368 369 370
	ISC_LINK_INIT(rdata, link);
	/* ISC_LIST_INIT(rdata->list); */
}

Mark Andrews's avatar
Mark Andrews committed
371
#if 0
372
#define DNS_RDATA_INITIALIZED(rdata) \
373 374 375
	((rdata)->data == NULL && (rdata)->length == 0 && \
	 (rdata)->rdclass == 0 && (rdata)->type == 0 && (rdata)->flags == 0 && \
	 !ISC_LINK_LINKED((rdata), link))
Mark Andrews's avatar
Mark Andrews committed
376
#else
377
#ifdef ISC_LIST_CHECKINIT
Mark Andrews's avatar
Mark Andrews committed
378 379
#define DNS_RDATA_INITIALIZED(rdata) \
	(!ISC_LINK_LINKED((rdata), link))
380 381
#else
#define DNS_RDATA_INITIALIZED(rdata) ISC_TRUE
Mark Andrews's avatar
Mark Andrews committed
382
#endif
Mark Andrews's avatar
Mark Andrews committed
383
#endif
384 385 386
#define DNS_RDATA_VALIDFLAGS(rdata) \
	(((rdata)->flags & ~DNS_RDATA_UPDATE) == 0)

387
void
388
dns_rdata_reset(dns_rdata_t *rdata) {
389

Brian Wellington's avatar
Brian Wellington committed
390 391
	REQUIRE(rdata != NULL);

392
	REQUIRE(!ISC_LINK_LINKED(rdata, link));
393
	REQUIRE(DNS_RDATA_VALIDFLAGS(rdata));
394 395 396 397 398 399 400 401

	rdata->data = NULL;
	rdata->length = 0;
	rdata->rdclass = 0;
	rdata->type = 0;
	rdata->flags = 0;
}

402 403 404 405
/***
 ***
 ***/

406
void
407 408
dns_rdata_clone(const dns_rdata_t *src, dns_rdata_t *target) {

Brian Wellington's avatar
Brian Wellington committed
409 410 411
	REQUIRE(src != NULL);
	REQUIRE(target != NULL);

412
	REQUIRE(DNS_RDATA_INITIALIZED(target));
413 414 415 416

	REQUIRE(DNS_RDATA_VALIDFLAGS(src));
	REQUIRE(DNS_RDATA_VALIDFLAGS(target));

417 418 419 420 421 422 423 424
	target->data = src->data;
	target->length = src->length;
	target->rdclass = src->rdclass;
	target->type = src->type;
	target->flags = src->flags;
}


425 426 427 428 429
/***
 *** Comparisons
 ***/

int
David Lawrence's avatar
David Lawrence committed
430
dns_rdata_compare(const dns_rdata_t *rdata1, const dns_rdata_t *rdata2) {
Mark Andrews's avatar
Mark Andrews committed
431 432
	int result = 0;
	isc_boolean_t use_default = ISC_FALSE;
433 434 435 436 437

	REQUIRE(rdata1 != NULL);
	REQUIRE(rdata2 != NULL);
	REQUIRE(rdata1->data != NULL);
	REQUIRE(rdata2->data != NULL);
438 439
	REQUIRE(DNS_RDATA_VALIDFLAGS(rdata1));
	REQUIRE(DNS_RDATA_VALIDFLAGS(rdata2));
440

441 442
	if (rdata1->rdclass != rdata2->rdclass)
		return (rdata1->rdclass < rdata2->rdclass ? -1 : 1);
443 444 445 446 447 448

	if (rdata1->type != rdata2->type)
		return (rdata1->type < rdata2->type ? -1 : 1);

	COMPARESWITCH

Mark Andrews's avatar
Mark Andrews committed
449
	if (use_default) {
Mark Andrews's avatar
Mark Andrews committed
450 451 452 453 454
		isc_region_t r1;
		isc_region_t r2;

		dns_rdata_toregion(rdata1, &r1);
		dns_rdata_toregion(rdata2, &r2);
455
		result = isc_region_compare(&r1, &r2);
456 457 458 459 460 461 462 463 464
	}
	return (result);
}

/***
 *** Conversions
 ***/

void
465
dns_rdata_fromregion(dns_rdata_t *rdata, dns_rdataclass_t rdclass,
Mark Andrews's avatar
Mark Andrews committed
466 467
		     dns_rdatatype_t type, isc_region_t *r)
{
468

469
	REQUIRE(rdata != NULL);
470
	REQUIRE(DNS_RDATA_INITIALIZED(rdata));
471 472
	REQUIRE(r != NULL);

473 474
	REQUIRE(DNS_RDATA_VALIDFLAGS(rdata));

475 476
	rdata->data = r->base;
	rdata->length = r->length;
477
	rdata->rdclass = rdclass;
478
	rdata->type = type;
479
	rdata->flags = 0;
480 481 482
}

void
David Lawrence's avatar
David Lawrence committed
483
dns_rdata_toregion(const dns_rdata_t *rdata, isc_region_t *r) {
484

485 486
	REQUIRE(rdata != NULL);
	REQUIRE(r != NULL);
487
	REQUIRE(DNS_RDATA_VALIDFLAGS(rdata));
488

489 490 491 492
	r->base = rdata->data;
	r->length = rdata->length;
}

493
isc_result_t
494
dns_rdata_fromwire(dns_rdata_t *rdata, dns_rdataclass_t rdclass,
Mark Andrews's avatar
Mark Andrews committed
495 496 497 498
		   dns_rdatatype_t type, isc_buffer_t *source,
		   dns_decompress_t *dctx, isc_boolean_t downcase,
		   isc_buffer_t *target)
{
499
	isc_result_t result = ISC_R_NOTIMPLEMENTED;
500 501 502
	isc_region_t region;
	isc_buffer_t ss;
	isc_buffer_t st;
Mark Andrews's avatar
Mark Andrews committed
503
	isc_boolean_t use_default = ISC_FALSE;
Mark Andrews's avatar
Mark Andrews committed
504
	isc_uint32_t activelength;
505

506
	REQUIRE(dctx != NULL);
507
	if (rdata != NULL) {
508
		REQUIRE(DNS_RDATA_INITIALIZED(rdata));
509 510
		REQUIRE(DNS_RDATA_VALIDFLAGS(rdata));
	}
511

512 513 514
	if (type == 0)
		return (DNS_R_FORMERR);

515 516 517
	ss = *source;
	st = *target;

Mark Andrews's avatar
Mark Andrews committed
518 519 520
	activelength = isc_buffer_activelength(source);
	INSIST(activelength < 65536);

521 522
	FROMWIRESWITCH

Mark Andrews's avatar
Mark Andrews committed
523 524 525 526 527 528 529 530 531 532
	if (use_default) {
		if (activelength > isc_buffer_availablelength(target))
			result = ISC_R_NOSPACE;
		else {
			isc_buffer_putmem(target, isc_buffer_current(source),
					  activelength);
			isc_buffer_forward(source, activelength);
			result = ISC_R_SUCCESS;
		}
	}
533

534 535 536
	/*
	 * We should have consumed all of our buffer.
	 */
537
	if (result == ISC_R_SUCCESS && !buffer_empty(source))
Mark Andrews's avatar
Mark Andrews committed
538
		result = DNS_R_EXTRADATA;
539

540
	if (rdata != NULL && result == ISC_R_SUCCESS) {
541 542 543
		region.base = isc_buffer_used(&st);
		region.length = isc_buffer_usedlength(target) -
				isc_buffer_usedlength(&st);
544
		dns_rdata_fromregion(rdata, rdclass, type, &region);
545 546
	}

547
	if (result != ISC_R_SUCCESS) {
548 549 550 551 552 553
		*source = ss;
		*target = st;
	}
	return (result);
}

554
isc_result_t
555
dns_rdata_towire(dns_rdata_t *rdata, dns_compress_t *cctx,
Brian Wellington's avatar
Brian Wellington committed
556
		 isc_buffer_t *target)
Mark Andrews's avatar
Mark Andrews committed
557
{
558
	isc_result_t result = ISC_R_NOTIMPLEMENTED;
Mark Andrews's avatar
Mark Andrews committed
559
	isc_boolean_t use_default = ISC_FALSE;
560
	isc_region_t tr;
Mark Andrews's avatar
Mark Andrews committed
561
	isc_buffer_t st;
562

563
	REQUIRE(rdata != NULL);
564
	REQUIRE(DNS_RDATA_VALIDFLAGS(rdata));
565

566 567 568
	/*
	 * Some DynDNS meta-RRs have empty rdata.
	 */
569 570
	if ((rdata->flags & DNS_RDATA_UPDATE) != 0) {
		INSIST(rdata->length == 0);
571
		return (ISC_R_SUCCESS);
572
	}
573

Mark Andrews's avatar
Mark Andrews committed
574
	st = *target;
575

576
	TOWIRESWITCH
577

Mark Andrews's avatar
Mark Andrews committed
578
	if (use_default) {
579
		isc_buffer_availableregion(target, &tr);
580
		if (tr.length < rdata->length)
581
			return (ISC_R_NOSPACE);
582 583
		memcpy(tr.base, rdata->data, rdata->length);
		isc_buffer_add(target, rdata->length);
584
		return (ISC_R_SUCCESS);
585
	}
586
	if (result != ISC_R_SUCCESS) {
Mark Andrews's avatar
Mark Andrews committed
587
		*target = st;
588 589
		INSIST(target->used < 65536);
		dns_compress_rollback(cctx, (isc_uint16_t)target->used);
Mark Andrews's avatar
Mark Andrews committed
590
	}
591 592 593
	return (result);
}

594 595 596 597 598
/*
 * If the binary data in 'src' is valid uncompressed wire format
 * rdata of class 'rdclass' and type 'type', return ISC_R_SUCCESS
 * and copy the validated rdata to 'dest'.  Otherwise return an error.
 */
599
static isc_result_t
600
rdata_validate(isc_buffer_t *src, isc_buffer_t *dest, dns_rdataclass_t rdclass,
601
	    dns_rdatatype_t type)
Mark Andrews's avatar
Mark Andrews committed
602
{
603
	dns_decompress_t dctx;
604
	dns_rdata_t rdata = DNS_RDATA_INIT;
605 606
	isc_result_t result;

607
	dns_decompress_init(&dctx, -1, DNS_DECOMPRESS_NONE);
608 609 610
	isc_buffer_setactive(src, isc_buffer_usedlength(src));
	result = dns_rdata_fromwire(&rdata, rdclass, type, src,
				    &dctx, ISC_FALSE, dest);
611
	dns_decompress_invalidate(&dctx);
612

Mark Andrews's avatar
Mark Andrews committed
613 614 615
	return (result);
}

616 617 618 619 620 621 622 623
static isc_result_t
unknown_fromtext(dns_rdataclass_t rdclass, dns_rdatatype_t type,
		 isc_lex_t *lexer, isc_mem_t *mctx, isc_buffer_t *target)
{
	isc_result_t result;
	isc_buffer_t *buf = NULL;
	isc_token_t token;

624
	if (type == 0 || dns_rdatatype_ismeta(type))
625 626 627 628
		return (DNS_R_METATYPE);

	result = isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
					ISC_FALSE);
629
	if (result == ISC_R_SUCCESS && token.value.as_ulong > 65535U)
630
		return (ISC_R_RANGE);
631
	result = isc_buffer_allocate(mctx, &buf, token.value.as_ulong);
632 633 634
	if (result != ISC_R_SUCCESS)
		return (result);
	
635 636
	result = isc_hex_tobuffer(lexer, buf,
				  (unsigned int)token.value.as_ulong);
637 638 639 640 641 642 643 644
	if (result != ISC_R_SUCCESS)
	       goto failure;
	if (isc_buffer_usedlength(buf) != token.value.as_ulong) {
		result = ISC_R_UNEXPECTEDEND;
		goto failure;
	}

	if (dns_rdatatype_isknown(type)) {
645
		result = rdata_validate(buf, target, rdclass, type);
646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661
	} else {
		isc_region_t r;
		isc_buffer_usedregion(buf, &r);
		result = isc_buffer_copyregion(target, &r);
	}
	if (result != ISC_R_SUCCESS)
		goto failure;

	isc_buffer_free(&buf);
	return (ISC_R_SUCCESS);

 failure:
	isc_buffer_free(&buf);
	return (result);
}

662
isc_result_t
663
dns_rdata_fromtext(dns_rdata_t *rdata, dns_rdataclass_t rdclass,
Mark Andrews's avatar
Mark Andrews committed
664
		   dns_rdatatype_t type, isc_lex_t *lexer,
665
		   dns_name_t *origin, isc_boolean_t downcase, isc_mem_t *mctx,
Mark Andrews's avatar
Mark Andrews committed
666 667
		   isc_buffer_t *target, dns_rdatacallbacks_t *callbacks)
{
668
	isc_result_t result = ISC_R_NOTIMPLEMENTED;
669 670
	isc_region_t region;
	isc_buffer_t st;
Mark Andrews's avatar
Mark Andrews committed
671
	isc_token_t token;
Bob Halley's avatar
Bob Halley committed
672
	unsigned int options = ISC_LEXOPT_EOL | ISC_LEXOPT_EOF |
673
			       ISC_LEXOPT_DNSMULTILINE | ISC_LEXOPT_ESCAPE;
674
	char *name;
675
	unsigned long line;
David Lawrence's avatar
David Lawrence committed
676
	void (*callback)(dns_rdatacallbacks_t *, const char *, ...);
677
	isc_result_t tresult;
678

679
	REQUIRE(origin == NULL || dns_name_isabsolute(origin) == ISC_TRUE);
680
	if (rdata != NULL) {
681
		REQUIRE(DNS_RDATA_INITIALIZED(rdata));
682 683
		REQUIRE(DNS_RDATA_VALIDFLAGS(rdata));
	}
684 685 686 687
	if (callbacks != NULL) {
		REQUIRE(callbacks->warn != NULL);
		REQUIRE(callbacks->error != NULL);
	}
688

689 690
	st = *target;

691
	if (callbacks != NULL)
692
		callback = callbacks->error;
693
	else
694 695
		callback = default_fromtext_callback;

696 697
	result = isc_lex_getmastertoken(lexer, &token, isc_tokentype_qstring,
					ISC_FALSE);
698 699 700 701 702
	if (result != ISC_R_SUCCESS) {
		name = isc_lex_getsourcename(lexer);
		line = isc_lex_getsourceline(lexer);
		fromtext_error(callback, callbacks, name, line,
			       &token, result);
703
		return (result);
704 705
	}

706
	if (strcmp(DNS_AS_STR(token), "\\#") == 0)
707 708
		result = unknown_fromtext(rdclass, type, lexer, mctx, target);
	else {
709 710 711
		isc_lex_ungettoken(lexer, &token);

		FROMTEXTSWITCH
Mark Andrews's avatar
Mark Andrews committed
712
	}
Mark Andrews's avatar
Mark Andrews committed
713 714 715 716

	/*
	 * Consume to end of line / file.
	 * If not at end of line initially set error code.
717
	 * Call callback via fromtext_error once if there was an error.
Mark Andrews's avatar
Mark Andrews committed
718 719
	 */
	do {
720 721
		name = isc_lex_getsourcename(lexer);
		line = isc_lex_getsourceline(lexer);
722 723 724 725
		tresult = isc_lex_gettoken(lexer, options, &token);
		if (tresult != ISC_R_SUCCESS) {
			if (result == ISC_R_SUCCESS)
				result = tresult;
726 727 728
			if (callback != NULL)
				fromtext_error(callback, callbacks, name,
					       line, NULL, result);
Mark Andrews's avatar
Mark Andrews committed
729 730 731
			break;
		} else if (token.type != isc_tokentype_eol &&
			   token.type != isc_tokentype_eof) {
732
			if (result == ISC_R_SUCCESS)
Mark Andrews's avatar
Mark Andrews committed
733
				result = DNS_R_EXTRATOKEN;
734 735 736 737 738
			if (callback != NULL) {
				fromtext_error(callback, callbacks, name,
					       line, &token, result);
				callback = NULL;
			}
739
		} else if (result != ISC_R_SUCCESS && callback != NULL) {
740 741 742
			fromtext_error(callback, callbacks, name, line,
				       &token, result);
			break;
743 744 745
		} else {
			if (token.type == isc_tokentype_eof)
				fromtext_warneof(lexer, callbacks);
Mark Andrews's avatar
Mark Andrews committed
746
			break;
747
		}
Mark Andrews's avatar
Mark Andrews committed
748
	} while (1);
749

750
	if (rdata != NULL && result == ISC_R_SUCCESS) {
751 752 753
		region.base = isc_buffer_used(&st);
		region.length = isc_buffer_usedlength(target) -
				isc_buffer_usedlength(&st);
754
		dns_rdata_fromregion(rdata, rdclass, type, &region);
755
	}
756
	if (result != ISC_R_SUCCESS) {
757 758 759 760 761
		*target = st;
	}
	return (result);
}

762
static isc_result_t
Bob Halley's avatar
Bob Halley committed
763 764
rdata_totext(dns_rdata_t *rdata, dns_rdata_textctx_t *tctx,
	     isc_buffer_t *target)
765
{
766
	isc_result_t result = ISC_R_NOTIMPLEMENTED;
Mark Andrews's avatar
Mark Andrews committed
767
	isc_boolean_t use_default = ISC_FALSE;
768
	char buf[sizeof("65535")];
Mark Andrews's avatar
Mark Andrews committed
769
	isc_region_t sr;
770

771
	REQUIRE(rdata != NULL);
772 773
	REQUIRE(tctx->origin == NULL ||
		dns_name_isabsolute(tctx->origin) == ISC_TRUE);
774

775 776 777
	/*
	 * Some DynDNS meta-RRs have empty rdata.
	 */
778 779
	if ((rdata->flags & DNS_RDATA_UPDATE) != 0) {
		INSIST(rdata->length == 0);
780
		return (ISC_R_SUCCESS);
781
	}
782

783 784
	TOTEXTSWITCH

Mark Andrews's avatar
Mark Andrews committed
785
	if (use_default) {
786
		strlcpy(buf, "\\# ", sizeof(buf));
787
		result = str_totext(buf, target);
Mark Andrews's avatar
Mark Andrews committed
788 789
		dns_rdata_toregion(rdata, &sr);
		INSIST(sr.length < 65536);
790
		snprintf(buf, sizeof(buf), "%u", sr.length);
Mark Andrews's avatar
Mark Andrews committed
791 792 793 794 795 796 797
		result = str_totext(buf, target);
		if (sr.length != 0 && result == ISC_R_SUCCESS) {
			if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0)
				result = str_totext(" ( ", target);
			else
				result = str_totext(" ", target);
			if (result == ISC_R_SUCCESS)
798 799 800
				result = isc_hex_totext(&sr, tctx->width - 2,
							tctx->linebreak,
							target);
Mark Andrews's avatar
Mark Andrews committed
801 802 803 804 805
			if (result == ISC_R_SUCCESS &&
			    (tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0)
				result = str_totext(" )", target);
		}
	}
Mark Andrews's avatar
Mark Andrews committed
806

807 808 809
	return (result);
}

810
isc_result_t
811
dns_rdata_totext(dns_rdata_t *rdata, dns_name_t *origin, isc_buffer_t *target)
812
{
813 814 815 816
	dns_rdata_textctx_t tctx;

	REQUIRE(DNS_RDATA_VALIDFLAGS(rdata));

817 818 819
	/*
	 * Set up formatting options for single-line output.
	 */
820 821 822 823
	tctx.origin = origin;
	tctx.flags = 0;
	tctx.width = 60;
	tctx.linebreak = " ";
Bob Halley's avatar
Bob Halley committed
824
	return (rdata_totext(rdata, &tctx, target));
825 826
}

827
isc_result_t
828 829 830 831
dns_rdata_tofmttext(dns_rdata_t *rdata, dns_name_t *origin,
		    unsigned int flags, unsigned int width,
		    char *linebreak, isc_buffer_t *target)
{
832 833 834 835
	dns_rdata_textctx_t tctx;

	REQUIRE(DNS_RDATA_VALIDFLAGS(rdata));

836 837 838
	/*
	 * Set up formatting options for formatted output.
	 */
839 840 841 842 843 844
	tctx.origin = origin;
	tctx.flags = flags;
	if ((flags & DNS_STYLEFLAG_MULTILINE) != 0) {
		tctx.width = width;
		tctx.linebreak = linebreak;
	} else {
845
		tctx.width = 60; /* Used for hex word length only. */
846 847
		tctx.linebreak = " ";
	}
Bob Halley's avatar
Bob Halley committed
848
	return (rdata_totext(rdata, &tctx, target));
849 850
}

851
isc_result_t
852
dns_rdata_fromstruct(dns_rdata_t *rdata, dns_rdataclass_t rdclass,
Mark Andrews's avatar
Mark Andrews committed
853 854 855
		     dns_rdatatype_t type, void *source,
		     isc_buffer_t *target)
{
856
	isc_result_t result = ISC_R_NOTIMPLEMENTED;
857 858
	isc_buffer_t st;
	isc_region_t region;
Mark Andrews's avatar
Mark Andrews committed
859
	isc_boolean_t use_default = ISC_FALSE;
860

861
	REQUIRE(source != NULL);
862
	if (rdata != NULL) {
863
		REQUIRE(DNS_RDATA_INITIALIZED(rdata));
864 865
		REQUIRE(DNS_RDATA_VALIDFLAGS(rdata));
	}
866

867 868 869 870
	st = *target;

	FROMSTRUCTSWITCH

Mark Andrews's avatar
Mark Andrews committed
871 872
	if (use_default)
		(void)NULL;
873

874
	if (rdata != NULL && result == ISC_R_SUCCESS) {
875 876 877
		region.base = isc_buffer_used(&st);
		region.length = isc_buffer_usedlength(target) -
				isc_buffer_usedlength(&st);
878
		dns_rdata_fromregion(rdata, rdclass, type, &region);
879
	}
880
	if (result != ISC_R_SUCCESS)
881 882 883 884
		*target = st;
	return (result);
}

885
isc_result_t
886
dns_rdata_tostruct(dns_rdata_t *rdata, void *target, isc_mem_t *mctx) {
887
	isc_result_t result = ISC_R_NOTIMPLEMENTED;
Mark Andrews's avatar
Mark Andrews committed
888
	isc_boolean_t use_default = ISC_FALSE;
889

890
	REQUIRE(rdata != NULL);
891
	REQUIRE(DNS_RDATA_VALIDFLAGS(rdata));
892

893 894
	TOSTRUCTSWITCH

Mark Andrews's avatar
Mark Andrews committed
895 896 897
	if (use_default)
		(void)NULL;

898 899 900
	return (result);
}

901 902 903 904 905 906 907 908
void
dns_rdata_freestruct(void *source) {
	dns_rdatacommon_t *common = source;
	REQUIRE(source != NULL);

	FREESTRUCTSWITCH
}

909
isc_result_t