diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 07:24:22 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 07:24:22 +0000 |
commit | 45d6379135504814ab723b57f0eb8be23393a51d (patch) | |
tree | d4f2ec4acca824a8446387a758b0ce4238a4dffa /lib/dns/rdata.c | |
parent | Initial commit. (diff) | |
download | bind9-45d6379135504814ab723b57f0eb8be23393a51d.tar.xz bind9-45d6379135504814ab723b57f0eb8be23393a51d.zip |
Adding upstream version 1:9.16.44.upstream/1%9.16.44
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'lib/dns/rdata.c')
-rw-r--r-- | lib/dns/rdata.c | 2365 |
1 files changed, 2365 insertions, 0 deletions
diff --git a/lib/dns/rdata.c b/lib/dns/rdata.c new file mode 100644 index 0000000..a283831 --- /dev/null +++ b/lib/dns/rdata.c @@ -0,0 +1,2365 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#include <ctype.h> +#include <inttypes.h> +#include <stdbool.h> + +#include <isc/base64.h> +#include <isc/hex.h> +#include <isc/lex.h> +#include <isc/mem.h> +#include <isc/parseint.h> +#include <isc/print.h> +#include <isc/string.h> +#include <isc/util.h> + +#include <dns/callbacks.h> +#include <dns/cert.h> +#include <dns/compress.h> +#include <dns/dsdigest.h> +#include <dns/enumtype.h> +#include <dns/keyflags.h> +#include <dns/keyvalues.h> +#include <dns/message.h> +#include <dns/rcode.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rdatastruct.h> +#include <dns/rdatatype.h> +#include <dns/result.h> +#include <dns/secalg.h> +#include <dns/secproto.h> +#include <dns/time.h> +#include <dns/ttl.h> + +#define RETERR(x) \ + do { \ + isc_result_t _r = (x); \ + if (_r != ISC_R_SUCCESS) \ + return ((_r)); \ + } while (0) + +#define RETTOK(x) \ + do { \ + isc_result_t _r = (x); \ + if (_r != ISC_R_SUCCESS) { \ + isc_lex_ungettoken(lexer, &token); \ + return (_r); \ + } \ + } while (0) + +#define CHECK(op) \ + do { \ + result = (op); \ + if (result != ISC_R_SUCCESS) \ + goto cleanup; \ + } while (0) + +#define CHECKTOK(op) \ + do { \ + result = (op); \ + if (result != ISC_R_SUCCESS) { \ + isc_lex_ungettoken(lexer, &token); \ + goto cleanup; \ + } \ + } while (0) + +#define DNS_AS_STR(t) ((t).value.as_textregion.base) + +#define ARGS_FROMTEXT \ + int rdclass, dns_rdatatype_t type, isc_lex_t *lexer, \ + const dns_name_t *origin, unsigned int options, \ + isc_buffer_t *target, dns_rdatacallbacks_t *callbacks + +#define CALL_FROMTEXT rdclass, type, lexer, origin, options, target, callbacks + +#define ARGS_TOTEXT \ + dns_rdata_t *rdata, dns_rdata_textctx_t *tctx, isc_buffer_t *target + +#define CALL_TOTEXT rdata, tctx, target + +#define ARGS_FROMWIRE \ + int rdclass, dns_rdatatype_t type, isc_buffer_t *source, \ + dns_decompress_t *dctx, unsigned int options, \ + isc_buffer_t *target + +#define CALL_FROMWIRE rdclass, type, source, dctx, options, target + +#define ARGS_TOWIRE \ + dns_rdata_t *rdata, dns_compress_t *cctx, isc_buffer_t *target + +#define CALL_TOWIRE rdata, cctx, target + +#define ARGS_COMPARE const dns_rdata_t *rdata1, const dns_rdata_t *rdata2 + +#define CALL_COMPARE rdata1, rdata2 + +#define ARGS_FROMSTRUCT \ + int rdclass, dns_rdatatype_t type, void *source, isc_buffer_t *target + +#define CALL_FROMSTRUCT rdclass, type, source, target + +#define ARGS_TOSTRUCT const dns_rdata_t *rdata, void *target, isc_mem_t *mctx + +#define CALL_TOSTRUCT rdata, target, mctx + +#define ARGS_FREESTRUCT void *source + +#define CALL_FREESTRUCT source + +#define ARGS_ADDLDATA \ + dns_rdata_t *rdata, dns_additionaldatafunc_t add, void *arg + +#define CALL_ADDLDATA rdata, add, arg + +#define ARGS_DIGEST dns_rdata_t *rdata, dns_digestfunc_t digest, void *arg + +#define CALL_DIGEST rdata, digest, arg + +#define ARGS_CHECKOWNER \ + const dns_name_t *name, dns_rdataclass_t rdclass, \ + dns_rdatatype_t type, bool wildcard + +#define CALL_CHECKOWNER name, rdclass, type, wildcard + +#define ARGS_CHECKNAMES \ + dns_rdata_t *rdata, const dns_name_t *owner, dns_name_t *bad + +#define CALL_CHECKNAMES rdata, owner, bad + +/*% + * Context structure for the totext_ functions. + * Contains formatting options for rdata-to-text + * conversion. + */ +typedef struct dns_rdata_textctx { + const dns_name_t *origin; /*%< Current origin, or NULL. */ + dns_masterstyle_flags_t flags; /*%< DNS_STYLEFLAG_* */ + unsigned int width; /*%< Width of rdata column. */ + const char *linebreak; /*%< Line break string. */ +} dns_rdata_textctx_t; + +static isc_result_t +txt_totext(isc_region_t *source, bool quote, 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_result_t +commatxt_fromtext(isc_textregion_t *source, bool comma, isc_buffer_t *target); + +static isc_result_t +commatxt_totext(isc_region_t *source, bool quote, bool comma, + isc_buffer_t *target); + +static isc_result_t +multitxt_totext(isc_region_t *source, isc_buffer_t *target); + +static isc_result_t +multitxt_fromtext(isc_textregion_t *source, isc_buffer_t *target); + +static bool +name_prefix(dns_name_t *name, const dns_name_t *origin, dns_name_t *target); + +static unsigned int +name_length(const dns_name_t *name); + +static isc_result_t +str_totext(const char *source, isc_buffer_t *target); + +static isc_result_t +inet_totext(int af, uint32_t flags, isc_region_t *src, isc_buffer_t *target); + +static bool +buffer_empty(isc_buffer_t *source); + +static void +buffer_fromregion(isc_buffer_t *buffer, isc_region_t *region); + +static isc_result_t +uint32_tobuffer(uint32_t, isc_buffer_t *target); + +static isc_result_t +uint16_tobuffer(uint32_t, isc_buffer_t *target); + +static isc_result_t +uint8_tobuffer(uint32_t, isc_buffer_t *target); + +static isc_result_t +name_tobuffer(const dns_name_t *name, isc_buffer_t *target); + +static uint32_t +uint32_fromregion(isc_region_t *region); + +static uint16_t +uint16_fromregion(isc_region_t *region); + +static uint8_t +uint8_fromregion(isc_region_t *region); + +static uint8_t +uint8_consume_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 void +default_fromtext_callback(dns_rdatacallbacks_t *callbacks, const char *, ...) + ISC_FORMAT_PRINTF(2, 3); + +static void +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); + +static void +fromtext_warneof(isc_lex_t *lexer, dns_rdatacallbacks_t *callbacks); + +static isc_result_t +rdata_totext(dns_rdata_t *rdata, dns_rdata_textctx_t *tctx, + isc_buffer_t *target); + +static void +warn_badname(const dns_name_t *name, isc_lex_t *lexer, + dns_rdatacallbacks_t *callbacks); + +static void +warn_badmx(isc_token_t *token, isc_lex_t *lexer, + dns_rdatacallbacks_t *callbacks); + +static uint16_t +uint16_consume_fromregion(isc_region_t *region); + +static isc_result_t +unknown_totext(dns_rdata_t *rdata, dns_rdata_textctx_t *tctx, + isc_buffer_t *target); + +static isc_result_t generic_fromtext_key(ARGS_FROMTEXT); + +static isc_result_t generic_totext_key(ARGS_TOTEXT); + +static isc_result_t generic_fromwire_key(ARGS_FROMWIRE); + +static isc_result_t generic_fromstruct_key(ARGS_FROMSTRUCT); + +static isc_result_t generic_tostruct_key(ARGS_TOSTRUCT); + +static void generic_freestruct_key(ARGS_FREESTRUCT); + +static isc_result_t generic_fromtext_txt(ARGS_FROMTEXT); + +static isc_result_t generic_totext_txt(ARGS_TOTEXT); + +static isc_result_t generic_fromwire_txt(ARGS_FROMWIRE); + +static isc_result_t generic_fromstruct_txt(ARGS_FROMSTRUCT); + +static isc_result_t generic_tostruct_txt(ARGS_TOSTRUCT); + +static void generic_freestruct_txt(ARGS_FREESTRUCT); + +static isc_result_t +generic_txt_first(dns_rdata_txt_t *txt); + +static isc_result_t +generic_txt_next(dns_rdata_txt_t *txt); + +static isc_result_t +generic_txt_current(dns_rdata_txt_t *txt, dns_rdata_txt_string_t *string); + +static isc_result_t generic_totext_ds(ARGS_TOTEXT); + +static isc_result_t generic_tostruct_ds(ARGS_TOSTRUCT); + +static isc_result_t generic_fromtext_ds(ARGS_FROMTEXT); + +static isc_result_t generic_fromwire_ds(ARGS_FROMWIRE); + +static isc_result_t generic_fromstruct_ds(ARGS_FROMSTRUCT); + +static isc_result_t generic_fromtext_tlsa(ARGS_FROMTEXT); + +static isc_result_t generic_totext_tlsa(ARGS_TOTEXT); + +static isc_result_t generic_fromwire_tlsa(ARGS_FROMWIRE); + +static isc_result_t generic_fromstruct_tlsa(ARGS_FROMSTRUCT); + +static isc_result_t generic_tostruct_tlsa(ARGS_TOSTRUCT); + +static void generic_freestruct_tlsa(ARGS_FREESTRUCT); + +static isc_result_t generic_fromtext_in_svcb(ARGS_FROMTEXT); +static isc_result_t generic_totext_in_svcb(ARGS_TOTEXT); +static isc_result_t generic_fromwire_in_svcb(ARGS_FROMWIRE); +static isc_result_t generic_towire_in_svcb(ARGS_TOWIRE); +static isc_result_t generic_fromstruct_in_svcb(ARGS_FROMSTRUCT); +static isc_result_t generic_tostruct_in_svcb(ARGS_TOSTRUCT); +static void generic_freestruct_in_svcb(ARGS_FREESTRUCT); +static isc_result_t generic_additionaldata_in_svcb(ARGS_ADDLDATA); +static bool generic_checknames_in_svcb(ARGS_CHECKNAMES); +static isc_result_t +generic_rdata_in_svcb_first(dns_rdata_in_svcb_t *); +static isc_result_t +generic_rdata_in_svcb_next(dns_rdata_in_svcb_t *); +static void +generic_rdata_in_svcb_current(dns_rdata_in_svcb_t *, isc_region_t *); + +/*% INT16 Size */ +#define NS_INT16SZ 2 +/*% IPv6 Address Size */ +#define NS_LOCATORSZ 8 + +/* + * Active Directory gc._msdcs.<forest> prefix. + */ +static unsigned char gc_msdcs_data[] = "\002gc\006_msdcs"; +static unsigned char gc_msdcs_offset[] = { 0, 3 }; + +static dns_name_t const gc_msdcs = DNS_NAME_INITNONABSOLUTE(gc_msdcs_data, + gc_msdcs_offset); + +/*% + * convert presentation level address to network order binary form. + * \return + * 1 if `src' is a valid [RFC1884 2.2] address, else 0. + * \note + * (1) does not touch `dst' unless it's returning 1. + */ +static int +locator_pton(const char *src, unsigned char *dst) { + static const char xdigits_l[] = "0123456789abcdef", + xdigits_u[] = "0123456789ABCDEF"; + unsigned char tmp[NS_LOCATORSZ]; + unsigned char *tp = tmp, *endp; + const char *xdigits; + int ch, seen_xdigits; + unsigned int val; + + memset(tp, '\0', NS_LOCATORSZ); + endp = tp + NS_LOCATORSZ; + seen_xdigits = 0; + val = 0; + while ((ch = *src++) != '\0') { + const char *pch; + + pch = strchr((xdigits = xdigits_l), ch); + if (pch == NULL) { + pch = strchr((xdigits = xdigits_u), ch); + } + if (pch != NULL) { + val <<= 4; + val |= (pch - xdigits); + if (++seen_xdigits > 4) { + return (0); + } + continue; + } + if (ch == ':') { + if (!seen_xdigits) { + return (0); + } + if (tp + NS_INT16SZ > endp) { + return (0); + } + *tp++ = (unsigned char)(val >> 8) & 0xff; + *tp++ = (unsigned char)val & 0xff; + seen_xdigits = 0; + val = 0; + continue; + } + return (0); + } + if (seen_xdigits) { + if (tp + NS_INT16SZ > endp) { + return (0); + } + *tp++ = (unsigned char)(val >> 8) & 0xff; + *tp++ = (unsigned char)val & 0xff; + } + if (tp != endp) { + return (0); + } + memmove(dst, tmp, NS_LOCATORSZ); + return (1); +} + +static isc_result_t +name_duporclone(const dns_name_t *source, isc_mem_t *mctx, dns_name_t *target) { + if (mctx != NULL) { + dns_name_dup(source, mctx, target); + } else { + dns_name_clone(source, target); + } + return (ISC_R_SUCCESS); +} + +static void * +mem_maybedup(isc_mem_t *mctx, void *source, size_t length) { + void *copy; + + if (mctx == NULL) { + return (source); + } + copy = isc_mem_allocate(mctx, length); + memmove(copy, source, length); + + return (copy); +} + +static isc_result_t +typemap_fromtext(isc_lex_t *lexer, isc_buffer_t *target, bool allow_empty) { + isc_token_t token; + unsigned char bm[8 * 1024]; /* 64k bits */ + dns_rdatatype_t covered, max_used; + int octet; + unsigned int max_octet, newend, end; + int window; + bool first = true; + + max_used = 0; + bm[0] = 0; + end = 0; + + do { + RETERR(isc_lex_getmastertoken(lexer, &token, + isc_tokentype_string, true)); + if (token.type != isc_tokentype_string) { + break; + } + RETTOK(dns_rdatatype_fromtext(&covered, + &token.value.as_textregion)); + if (covered > max_used) { + newend = covered / 8; + if (newend > end) { + memset(&bm[end + 1], 0, newend - end); + end = newend; + } + max_used = covered; + } + bm[covered / 8] |= (0x80 >> (covered % 8)); + first = false; + } while (1); + isc_lex_ungettoken(lexer, &token); + if (!allow_empty && first) { + return (DNS_R_FORMERR); + } + + for (window = 0; window < 256; window++) { + if (max_used < window * 256) { + break; + } + + max_octet = max_used - (window * 256); + if (max_octet >= 256) { + max_octet = 31; + } else { + max_octet /= 8; + } + + /* + * Find if we have a type in this window. + */ + for (octet = max_octet; octet >= 0; octet--) { + if (bm[window * 32 + octet] != 0) { + break; + } + } + if (octet < 0) { + continue; + } + RETERR(uint8_tobuffer(window, target)); + RETERR(uint8_tobuffer(octet + 1, target)); + RETERR(mem_tobuffer(target, &bm[window * 32], octet + 1)); + } + return (ISC_R_SUCCESS); +} + +static isc_result_t +typemap_totext(isc_region_t *sr, dns_rdata_textctx_t *tctx, + isc_buffer_t *target) { + unsigned int i, j, k; + unsigned int window, len; + bool first = true; + + for (i = 0; i < sr->length; i += len) { + if (tctx != NULL && + (tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0) + { + RETERR(str_totext(tctx->linebreak, target)); + first = true; + } + INSIST(i + 2 <= sr->length); + window = sr->base[i]; + len = sr->base[i + 1]; + INSIST(len > 0 && len <= 32); + i += 2; + INSIST(i + len <= sr->length); + for (j = 0; j < len; j++) { + dns_rdatatype_t t; + if (sr->base[i + j] == 0) { + continue; + } + for (k = 0; k < 8; k++) { + if ((sr->base[i + j] & (0x80 >> k)) == 0) { + continue; + } + t = window * 256 + j * 8 + k; + if (!first) { + RETERR(str_totext(" ", target)); + } + first = false; + if (dns_rdatatype_isknown(t)) { + RETERR(dns_rdatatype_totext(t, target)); + } else { + char buf[sizeof("TYPE65535")]; + snprintf(buf, sizeof(buf), "TYPE%u", t); + RETERR(str_totext(buf, target)); + } + } + } + } + return (ISC_R_SUCCESS); +} + +static isc_result_t +typemap_test(isc_region_t *sr, bool allow_empty) { + unsigned int window, lastwindow = 0; + unsigned int len; + bool first = true; + unsigned int i; + + for (i = 0; i < sr->length; i += len) { + /* + * Check for overflow. + */ + if (i + 2 > sr->length) { + RETERR(DNS_R_FORMERR); + } + window = sr->base[i]; + len = sr->base[i + 1]; + i += 2; + /* + * Check that bitmap windows are in the correct order. + */ + if (!first && window <= lastwindow) { + RETERR(DNS_R_FORMERR); + } + /* + * Check for legal lengths. + */ + if (len < 1 || len > 32) { + RETERR(DNS_R_FORMERR); + } + /* + * Check for overflow. + */ + if (i + len > sr->length) { + RETERR(DNS_R_FORMERR); + } + /* + * The last octet of the bitmap must be non zero. + */ + if (sr->base[i + len - 1] == 0) { + RETERR(DNS_R_FORMERR); + } + lastwindow = window; + first = false; + } + if (i != sr->length) { + return (DNS_R_EXTRADATA); + } + if (!allow_empty && first) { + RETERR(DNS_R_FORMERR); + } + return (ISC_R_SUCCESS); +} + +static const char hexdigits[] = "0123456789abcdef"; +static const char decdigits[] = "0123456789"; + +#include "code.h" + +#define META 0x0001 +#define RESERVED 0x0002 + +/*** + *** Initialization + ***/ + +void +dns_rdata_init(dns_rdata_t *rdata) { + REQUIRE(rdata != NULL); + + rdata->data = NULL; + rdata->length = 0; + rdata->rdclass = 0; + rdata->type = 0; + rdata->flags = 0; + ISC_LINK_INIT(rdata, link); + /* ISC_LIST_INIT(rdata->list); */ +} + +void +dns_rdata_reset(dns_rdata_t *rdata) { + REQUIRE(rdata != NULL); + + REQUIRE(!ISC_LINK_LINKED(rdata, link)); + REQUIRE(DNS_RDATA_VALIDFLAGS(rdata)); + + rdata->data = NULL; + rdata->length = 0; + rdata->rdclass = 0; + rdata->type = 0; + rdata->flags = 0; +} + +/*** + *** + ***/ + +void +dns_rdata_clone(const dns_rdata_t *src, dns_rdata_t *target) { + REQUIRE(src != NULL); + REQUIRE(target != NULL); + + REQUIRE(DNS_RDATA_INITIALIZED(target)); + + REQUIRE(DNS_RDATA_VALIDFLAGS(src)); + REQUIRE(DNS_RDATA_VALIDFLAGS(target)); + + target->data = src->data; + target->length = src->length; + target->rdclass = src->rdclass; + target->type = src->type; + target->flags = src->flags; +} + +/*** + *** Comparisons + ***/ + +int +dns_rdata_compare(const dns_rdata_t *rdata1, const dns_rdata_t *rdata2) { + int result = 0; + bool use_default = false; + + REQUIRE(rdata1 != NULL); + REQUIRE(rdata2 != NULL); + REQUIRE(rdata1->length == 0 || rdata1->data != NULL); + REQUIRE(rdata2->length == 0 || rdata2->data != NULL); + REQUIRE(DNS_RDATA_VALIDFLAGS(rdata1)); + REQUIRE(DNS_RDATA_VALIDFLAGS(rdata2)); + + if (rdata1->rdclass != rdata2->rdclass) { + return (rdata1->rdclass < rdata2->rdclass ? -1 : 1); + } + + if (rdata1->type != rdata2->type) { + return (rdata1->type < rdata2->type ? -1 : 1); + } + + COMPARESWITCH + + if (use_default) { + isc_region_t r1; + isc_region_t r2; + + dns_rdata_toregion(rdata1, &r1); + dns_rdata_toregion(rdata2, &r2); + result = isc_region_compare(&r1, &r2); + } + return (result); +} + +int +dns_rdata_casecompare(const dns_rdata_t *rdata1, const dns_rdata_t *rdata2) { + int result = 0; + bool use_default = false; + + REQUIRE(rdata1 != NULL); + REQUIRE(rdata2 != NULL); + REQUIRE(rdata1->length == 0 || rdata1->data != NULL); + REQUIRE(rdata2->length == 0 || rdata2->data != NULL); + REQUIRE(DNS_RDATA_VALIDFLAGS(rdata1)); + REQUIRE(DNS_RDATA_VALIDFLAGS(rdata2)); + + if (rdata1->rdclass != rdata2->rdclass) { + return (rdata1->rdclass < rdata2->rdclass ? -1 : 1); + } + + if (rdata1->type != rdata2->type) { + return (rdata1->type < rdata2->type ? -1 : 1); + } + + CASECOMPARESWITCH + + if (use_default) { + isc_region_t r1; + isc_region_t r2; + + dns_rdata_toregion(rdata1, &r1); + dns_rdata_toregion(rdata2, &r2); + result = isc_region_compare(&r1, &r2); + } + return (result); +} + +/*** + *** Conversions + ***/ + +void +dns_rdata_fromregion(dns_rdata_t *rdata, dns_rdataclass_t rdclass, + dns_rdatatype_t type, isc_region_t *r) { + REQUIRE(rdata != NULL); + REQUIRE(DNS_RDATA_INITIALIZED(rdata)); + REQUIRE(r != NULL); + + REQUIRE(DNS_RDATA_VALIDFLAGS(rdata)); + + rdata->data = r->base; + rdata->length = r->length; + rdata->rdclass = rdclass; + rdata->type = type; + rdata->flags = 0; +} + +void +dns_rdata_toregion(const dns_rdata_t *rdata, isc_region_t *r) { + REQUIRE(rdata != NULL); + REQUIRE(r != NULL); + REQUIRE(DNS_RDATA_VALIDFLAGS(rdata)); + + r->base = rdata->data; + r->length = rdata->length; +} + +isc_result_t +dns_rdata_fromwire(dns_rdata_t *rdata, dns_rdataclass_t rdclass, + dns_rdatatype_t type, isc_buffer_t *source, + dns_decompress_t *dctx, unsigned int options, + isc_buffer_t *target) { + isc_result_t result = ISC_R_NOTIMPLEMENTED; + isc_region_t region; + isc_buffer_t ss; + isc_buffer_t st; + bool use_default = false; + uint32_t activelength; + unsigned int length; + + REQUIRE(dctx != NULL); + if (rdata != NULL) { + REQUIRE(DNS_RDATA_INITIALIZED(rdata)); + REQUIRE(DNS_RDATA_VALIDFLAGS(rdata)); + } + REQUIRE(source != NULL); + REQUIRE(target != NULL); + + if (type == 0) { + return (DNS_R_FORMERR); + } + + ss = *source; + st = *target; + + activelength = isc_buffer_activelength(source); + INSIST(activelength < 65536); + + FROMWIRESWITCH + + 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; + } + } + + /* + * Reject any rdata that expands out to more than DNS_RDATA_MAXLENGTH + * as we cannot transmit it. + */ + length = isc_buffer_usedlength(target) - isc_buffer_usedlength(&st); + if (result == ISC_R_SUCCESS && length > DNS_RDATA_MAXLENGTH) { + result = DNS_R_FORMERR; + } + + /* + * We should have consumed all of our buffer. + */ + if (result == ISC_R_SUCCESS && !buffer_empty(source)) { + result = DNS_R_EXTRADATA; + } + + if (rdata != NULL && result == ISC_R_SUCCESS) { + region.base = isc_buffer_used(&st); + region.length = length; + dns_rdata_fromregion(rdata, rdclass, type, ®ion); + } + + if (result != ISC_R_SUCCESS) { + *source = ss; + *target = st; + } + return (result); +} + +isc_result_t +dns_rdata_towire(dns_rdata_t *rdata, dns_compress_t *cctx, + isc_buffer_t *target) { + isc_result_t result = ISC_R_NOTIMPLEMENTED; + bool use_default = false; + isc_region_t tr; + isc_buffer_t st; + + REQUIRE(rdata != NULL); + REQUIRE(DNS_RDATA_VALIDFLAGS(rdata)); + + /* + * Some DynDNS meta-RRs have empty rdata. + */ + if ((rdata->flags & DNS_RDATA_UPDATE) != 0) { + INSIST(rdata->length == 0); + return (ISC_R_SUCCESS); + } + + st = *target; + + TOWIRESWITCH + + if (use_default) { + isc_buffer_availableregion(target, &tr); + if (tr.length < rdata->length) { + return (ISC_R_NOSPACE); + } + memmove(tr.base, rdata->data, rdata->length); + isc_buffer_add(target, rdata->length); + return (ISC_R_SUCCESS); + } + if (result != ISC_R_SUCCESS) { + *target = st; + INSIST(target->used < 65536); + dns_compress_rollback(cctx, (uint16_t)target->used); + } + return (result); +} + +/* + * 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. + */ +static isc_result_t +rdata_validate(isc_buffer_t *src, isc_buffer_t *dest, dns_rdataclass_t rdclass, + dns_rdatatype_t type) { + dns_decompress_t dctx; + isc_result_t result; + + dns_decompress_init(&dctx, -1, DNS_DECOMPRESS_NONE); + isc_buffer_setactive(src, isc_buffer_usedlength(src)); + result = dns_rdata_fromwire(NULL, rdclass, type, src, &dctx, 0, dest); + dns_decompress_invalidate(&dctx); + + return (result); +} + +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; + + if (type == 0 || dns_rdatatype_ismeta(type)) { + return (DNS_R_METATYPE); + } + + RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number, + false)); + if (token.value.as_ulong > 65535U) { + return (ISC_R_RANGE); + } + isc_buffer_allocate(mctx, &buf, token.value.as_ulong); + + if (token.value.as_ulong != 0U) { + result = isc_hex_tobuffer(lexer, buf, + (unsigned int)token.value.as_ulong); + 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)) { + result = rdata_validate(buf, target, rdclass, type); + } 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); +} + +isc_result_t +dns_rdata_fromtext(dns_rdata_t *rdata, dns_rdataclass_t rdclass, + dns_rdatatype_t type, isc_lex_t *lexer, + const dns_name_t *origin, unsigned int options, + isc_mem_t *mctx, isc_buffer_t *target, + dns_rdatacallbacks_t *callbacks) { + isc_result_t result = ISC_R_NOTIMPLEMENTED; + isc_region_t region; + isc_buffer_t st; + isc_token_t token; + unsigned int lexoptions = ISC_LEXOPT_EOL | ISC_LEXOPT_EOF | + ISC_LEXOPT_DNSMULTILINE | ISC_LEXOPT_ESCAPE; + char *name; + unsigned long line; + void (*callback)(dns_rdatacallbacks_t *, const char *, ...); + isc_result_t tresult; + unsigned int length; + bool unknown; + + REQUIRE(origin == NULL || dns_name_isabsolute(origin)); + if (rdata != NULL) { + REQUIRE(DNS_RDATA_INITIALIZED(rdata)); + REQUIRE(DNS_RDATA_VALIDFLAGS(rdata)); + } + if (callbacks != NULL) { + REQUIRE(callbacks->warn != NULL); + REQUIRE(callbacks->error != NULL); + } + + st = *target; + + if (callbacks != NULL) { + callback = callbacks->error; + } else { + callback = default_fromtext_callback; + } + + result = isc_lex_getmastertoken(lexer, &token, isc_tokentype_qstring, + true); + if (result != ISC_R_SUCCESS) { + name = isc_lex_getsourcename(lexer); + line = isc_lex_getsourceline(lexer); + fromtext_error(callback, callbacks, name, line, NULL, result); + return (result); + } + + unknown = false; + if (token.type == isc_tokentype_string && + strcmp(DNS_AS_STR(token), "\\#") == 0) + { + /* + * If this is a TXT record '\#' could be a escaped '#'. + * Look to see if the next token is a number and if so + * treat it as a unknown record format. + */ + if (type == dns_rdatatype_txt) { + result = isc_lex_getmastertoken( + lexer, &token, isc_tokentype_number, false); + if (result == ISC_R_SUCCESS) { + isc_lex_ungettoken(lexer, &token); + } + } + + if (result == ISC_R_SUCCESS) { + unknown = true; + result = unknown_fromtext(rdclass, type, lexer, mctx, + target); + } else { + options |= DNS_RDATA_UNKNOWNESCAPE; + } + } else { + isc_lex_ungettoken(lexer, &token); + } + + if (!unknown) { + FROMTEXTSWITCH + + /* + * Consume to end of line / file. + * If not at end of line initially set error code. + * Call callback via fromtext_error once if there was an error. + */ + } + do { + name = isc_lex_getsourcename(lexer); + line = isc_lex_getsourceline(lexer); + tresult = isc_lex_gettoken(lexer, lexoptions, &token); + if (tresult != ISC_R_SUCCESS) { + if (result == ISC_R_SUCCESS) { + result = tresult; + } + if (callback != NULL) { + fromtext_error(callback, callbacks, name, line, + NULL, result); + } + break; + } else if (token.type != isc_tokentype_eol && + token.type != isc_tokentype_eof) + { + if (result == ISC_R_SUCCESS) { + result = DNS_R_EXTRATOKEN; + } + if (callback != NULL) { + fromtext_error(callback, callbacks, name, line, + &token, result); + callback = NULL; + } + } else if (result != ISC_R_SUCCESS && callback != NULL) { + fromtext_error(callback, callbacks, name, line, &token, + result); + break; + } else { + if (token.type == isc_tokentype_eof) { + fromtext_warneof(lexer, callbacks); + } + break; + } + } while (1); + + length = isc_buffer_usedlength(target) - isc_buffer_usedlength(&st); + if (result == ISC_R_SUCCESS && length > DNS_RDATA_MAXLENGTH) { + result = ISC_R_NOSPACE; + } + + if (rdata != NULL && result == ISC_R_SUCCESS) { + region.base = isc_buffer_used(&st); + region.length = length; + dns_rdata_fromregion(rdata, rdclass, type, ®ion); + } + if (result != ISC_R_SUCCESS) { + *target = st; + } + return (result); +} + +static isc_result_t +unknown_totext(dns_rdata_t *rdata, dns_rdata_textctx_t *tctx, + isc_buffer_t *target) { + isc_result_t result; + char buf[sizeof("65535")]; + isc_region_t sr; + + strlcpy(buf, "\\# ", sizeof(buf)); + result = str_totext(buf, target); + if (result != ISC_R_SUCCESS) { + return (result); + } + + dns_rdata_toregion(rdata, &sr); + INSIST(sr.length < 65536); + snprintf(buf, sizeof(buf), "%u", sr.length); + result = str_totext(buf, target); + if (result != ISC_R_SUCCESS) { + return (result); + } + + if (sr.length != 0U) { + if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0) { + result = str_totext(" ( ", target); + } else { + result = str_totext(" ", target); + } + + if (result != ISC_R_SUCCESS) { + return (result); + } + + if (tctx->width == 0) { /* No splitting */ + result = isc_hex_totext(&sr, 0, "", target); + } else { + result = isc_hex_totext(&sr, tctx->width - 2, + tctx->linebreak, target); + } + if (result == ISC_R_SUCCESS && + (tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0) + { + result = str_totext(" )", target); + } + } + return (result); +} + +static isc_result_t +rdata_totext(dns_rdata_t *rdata, dns_rdata_textctx_t *tctx, + isc_buffer_t *target) { + isc_result_t result = ISC_R_NOTIMPLEMENTED; + bool use_default = false; + unsigned int cur; + + REQUIRE(rdata != NULL); + REQUIRE(tctx->origin == NULL || dns_name_isabsolute(tctx->origin)); + + /* + * Some DynDNS meta-RRs have empty rdata. + */ + if ((rdata->flags & DNS_RDATA_UPDATE) != 0) { + INSIST(rdata->length == 0); + return (ISC_R_SUCCESS); + } + + if ((tctx->flags & DNS_STYLEFLAG_UNKNOWNFORMAT) != 0) { + return (unknown_totext(rdata, tctx, target)); + } + + cur = isc_buffer_usedlength(target); + + TOTEXTSWITCH + + if (use_default || (result == ISC_R_NOTIMPLEMENTED)) { + unsigned int u = isc_buffer_usedlength(target); + + INSIST(u >= cur); + isc_buffer_subtract(target, u - cur); + result = unknown_totext(rdata, tctx, target); + } + + return (result); +} + +isc_result_t +dns_rdata_totext(dns_rdata_t *rdata, const dns_name_t *origin, + isc_buffer_t *target) { + dns_rdata_textctx_t tctx; + + REQUIRE(DNS_RDATA_VALIDFLAGS(rdata)); + + /* + * Set up formatting options for single-line output. + */ + tctx.origin = origin; + tctx.flags = 0; + tctx.width = 60; + tctx.linebreak = " "; + return (rdata_totext(rdata, &tctx, target)); +} + +isc_result_t +dns_rdata_tofmttext(dns_rdata_t *rdata, const dns_name_t *origin, + dns_masterstyle_flags_t flags, unsigned int width, + unsigned int split_width, const char *linebreak, + isc_buffer_t *target) { + dns_rdata_textctx_t tctx; + + REQUIRE(DNS_RDATA_VALIDFLAGS(rdata)); + + /* + * Set up formatting options for formatted output. + */ + tctx.origin = origin; + tctx.flags = flags; + if (split_width == 0xffffffff) { + tctx.width = width; + } else { + tctx.width = split_width; + } + + if ((flags & DNS_STYLEFLAG_MULTILINE) != 0) { + tctx.linebreak = linebreak; + } else { + if (split_width == 0xffffffff) { + tctx.width = 60; /* Used for hex word length only. */ + } + tctx.linebreak = " "; + } + return (rdata_totext(rdata, &tctx, target)); +} + +isc_result_t +dns_rdata_fromstruct(dns_rdata_t *rdata, dns_rdataclass_t rdclass, + dns_rdatatype_t type, void *source, isc_buffer_t *target) { + isc_result_t result = ISC_R_NOTIMPLEMENTED; + isc_buffer_t st; + isc_region_t region; + bool use_default = false; + unsigned int length; + + REQUIRE(source != NULL); + if (rdata != NULL) { + REQUIRE(DNS_RDATA_INITIALIZED(rdata)); + REQUIRE(DNS_RDATA_VALIDFLAGS(rdata)); + } + + st = *target; + + FROMSTRUCTSWITCH + + if (use_default) { + (void)NULL; + } + + length = isc_buffer_usedlength(target) - isc_buffer_usedlength(&st); + if (result == ISC_R_SUCCESS && length > DNS_RDATA_MAXLENGTH) { + result = ISC_R_NOSPACE; + } + + if (rdata != NULL && result == ISC_R_SUCCESS) { + region.base = isc_buffer_used(&st); + region.length = length; + dns_rdata_fromregion(rdata, rdclass, type, ®ion); + } + if (result != ISC_R_SUCCESS) { + *target = st; + } + return (result); +} + +isc_result_t +dns_rdata_tostruct(const dns_rdata_t *rdata, void *target, isc_mem_t *mctx) { + isc_result_t result = ISC_R_NOTIMPLEMENTED; + bool use_default = false; + + REQUIRE(rdata != NULL); + REQUIRE(DNS_RDATA_VALIDFLAGS(rdata)); + REQUIRE((rdata->flags & DNS_RDATA_UPDATE) == 0); + + TOSTRUCTSWITCH + + if (use_default) { + (void)NULL; + } + + return (result); +} + +void +dns_rdata_freestruct(void *source) { + dns_rdatacommon_t *common = source; + REQUIRE(common != NULL); + + FREESTRUCTSWITCH +} + +isc_result_t +dns_rdata_additionaldata(dns_rdata_t *rdata, dns_additionaldatafunc_t add, + void *arg) { + isc_result_t result = ISC_R_NOTIMPLEMENTED; + bool use_default = false; + + /* + * Call 'add' for each name and type from 'rdata' which is subject to + * additional section processing. + */ + + REQUIRE(rdata != NULL); + REQUIRE(add != NULL); + REQUIRE(DNS_RDATA_VALIDFLAGS(rdata)); + + ADDITIONALDATASWITCH + + /* No additional processing for unknown types */ + if (use_default) { + result = ISC_R_SUCCESS; + } + + return (result); +} + +isc_result_t +dns_rdata_digest(dns_rdata_t *rdata, dns_digestfunc_t digest, void *arg) { + isc_result_t result = ISC_R_NOTIMPLEMENTED; + bool use_default = false; + isc_region_t r; + + /* + * Send 'rdata' in DNSSEC canonical form to 'digest'. + */ + + REQUIRE(rdata != NULL); + REQUIRE(digest != NULL); + REQUIRE(DNS_RDATA_VALIDFLAGS(rdata)); + + DIGESTSWITCH + + if (use_default) { + dns_rdata_toregion(rdata, &r); + result = (digest)(arg, &r); + } + + return (result); +} + +bool +dns_rdata_checkowner(const dns_name_t *name, dns_rdataclass_t rdclass, + dns_rdatatype_t type, bool wildcard) { + bool result; + + CHECKOWNERSWITCH + return (result); +} + +bool +dns_rdata_checknames(dns_rdata_t *rdata, const dns_name_t *owner, + dns_name_t *bad) { + bool result; + + CHECKNAMESSWITCH + return (result); +} + +unsigned int +dns_rdatatype_attributes(dns_rdatatype_t type) { + RDATATYPE_ATTRIBUTE_SW + if (type >= (dns_rdatatype_t)128 && type <= (dns_rdatatype_t)255) { + return (DNS_RDATATYPEATTR_UNKNOWN | DNS_RDATATYPEATTR_META); + } + return (DNS_RDATATYPEATTR_UNKNOWN); +} + +isc_result_t +dns_rdatatype_fromtext(dns_rdatatype_t *typep, isc_textregion_t *source) { + unsigned int hash; + unsigned int n; + unsigned char a, b; + + n = source->length; + + if (n == 0) { + return (DNS_R_UNKNOWN); + } + + a = tolower((unsigned char)source->base[0]); + b = tolower((unsigned char)source->base[n - 1]); + + hash = ((a + n) * b) % 256; + + /* + * This switch block is inlined via \#define, and will use "return" + * to return a result to the caller if it is a valid (known) + * rdatatype name. + */ + RDATATYPE_FROMTEXT_SW(hash, source->base, n, typep); + + if (source->length > 4 && source->length < (4 + sizeof("65000")) && + strncasecmp("type", source->base, 4) == 0) + { + char buf[sizeof("65000")]; + char *endp; + unsigned int val; + + /* + * source->base is not required to be NUL terminated. + * Copy up to remaining bytes and NUL terminate. + */ + snprintf(buf, sizeof(buf), "%.*s", (int)(source->length - 4), + source->base + 4); + val = strtoul(buf, &endp, 10); + if (*endp == '\0' && val <= 0xffff) { + *typep = (dns_rdatatype_t)val; + return (ISC_R_SUCCESS); + } + } + + return (DNS_R_UNKNOWN); +} + +isc_result_t +dns_rdatatype_totext(dns_rdatatype_t type, isc_buffer_t *target) { + RDATATYPE_TOTEXT_SW + + return (dns_rdatatype_tounknowntext(type, target)); +} + +isc_result_t +dns_rdatatype_tounknowntext(dns_rdatatype_t type, isc_buffer_t *target) { + char buf[sizeof("TYPE65535")]; + + snprintf(buf, sizeof(buf), "TYPE%u", type); + return (str_totext(buf, target)); +} + +void +dns_rdatatype_format(dns_rdatatype_t rdtype, char *array, unsigned int size) { + isc_result_t result; + isc_buffer_t buf; + + if (size == 0U) { + return; + } + + isc_buffer_init(&buf, array, size); + result = dns_rdatatype_totext(rdtype, &buf); + /* + * Null terminate. + */ + if (result == ISC_R_SUCCESS) { + if (isc_buffer_availablelength(&buf) >= 1) { + isc_buffer_putuint8(&buf, 0); + } else { + result = ISC_R_NOSPACE; + } + } + if (result != ISC_R_SUCCESS) { + strlcpy(array, "<unknown>", size); + } +} + +/* + * Private function. + */ + +static unsigned int +name_length(const dns_name_t *name) { + return (name->length); +} + +static isc_result_t +commatxt_totext(isc_region_t *source, bool quote, bool comma, + isc_buffer_t *target) { + unsigned int tl; + unsigned int n; + unsigned char *sp; + char *tp; + isc_region_t region; + + isc_buffer_availableregion(target, ®ion); + sp = source->base; + tp = (char *)region.base; + tl = region.length; + + n = *sp++; + + REQUIRE(n + 1 <= source->length); + if (n == 0U) { + REQUIRE(quote); + } + + if (quote) { + if (tl < 1) { + return (ISC_R_NOSPACE); + } + *tp++ = '"'; + tl--; + } + while (n--) { + /* + * \DDD space (0x20) if not quoting. + */ + if (*sp < (quote ? ' ' : '!') || *sp >= 0x7f) { + if (tl < 4) { + return (ISC_R_NOSPACE); + } + *tp++ = '\\'; + *tp++ = '0' + ((*sp / 100) % 10); + *tp++ = '0' + ((*sp / 10) % 10); + *tp++ = '0' + (*sp % 10); + sp++; + tl -= 4; + continue; + } + /* + * Escape double quote and backslash. If we are not + * enclosing the string in double quotes, also escape + * at sign (@) and semicolon (;) unless comma is set. + * If comma is set, then only escape commas (,). + */ + if (*sp == '"' || *sp == '\\' || (comma && *sp == ',') || + (!comma && !quote && (*sp == '@' || *sp == ';'))) + { + if (tl < 2) { + return (ISC_R_NOSPACE); + } + *tp++ = '\\'; + tl--; + /* + * Perform comma escape processing. + * ',' => '\\,' + * '\' => '\\\\' + */ + if (comma && (*sp == ',' || *sp == '\\')) { + if (tl < ((*sp == '\\') ? 3 : 2)) { + return (ISC_R_NOSPACE); + } + *tp++ = '\\'; + tl--; + if (*sp == '\\') { + *tp++ = '\\'; + tl--; + } + } + } + if (tl < 1) { + return (ISC_R_NOSPACE); + } + *tp++ = *sp++; + tl--; + } + if (quote) { + if (tl < 1) { + return (ISC_R_NOSPACE); + } + *tp++ = '"'; + tl--; + POST(tl); + } + isc_buffer_add(target, (unsigned int)(tp - (char *)region.base)); + isc_region_consume(source, *source->base + 1); + return (ISC_R_SUCCESS); +} + +static isc_result_t +txt_totext(isc_region_t *source, bool quote, isc_buffer_t *target) { + return (commatxt_totext(source, quote, false, target)); +} + +static isc_result_t +commatxt_fromtext(isc_textregion_t *source, bool comma, isc_buffer_t *target) { + isc_region_t tregion; + bool escape = false, comma_escape = false, seen_comma = false; + unsigned int n, nrem; + char *s; + unsigned char *t; + int d; + int c; + + isc_buffer_availableregion(target, &tregion); + s = source->base; + n = source->length; + t = tregion.base; + nrem = tregion.length; + if (nrem < 1) { + return (ISC_R_NOSPACE); + } + /* + * Length byte. + */ + nrem--; + t++; + /* + * Maximum text string length. + */ + if (nrem > 255) { + nrem = 255; + } + while (n-- != 0) { + c = (*s++) & 0xff; + if (escape && (d = decvalue((char)c)) != -1) { + c = d; + if (n == 0) { + return (DNS_R_SYNTAX); + } + n--; + if ((d = decvalue(*s++)) != -1) { + c = c * 10 + d; + } else { + return (DNS_R_SYNTAX); + } + if (n == 0) { + return (DNS_R_SYNTAX); + } + n--; + if ((d = decvalue(*s++)) != -1) { + c = c * 10 + d; + } else { + return (DNS_R_SYNTAX); + } + if (c > 255) { + return (DNS_R_SYNTAX); + } + } else if (!escape && c == '\\') { + escape = true; + continue; + } + escape = false; + /* + * Level 1 escape processing complete. + * If comma is set perform comma escape processing. + * + * Level 1 Level 2 ALPN's + * h1\,h2 => h1,h2 => h1 and h2 + * h1\\,h2 => h1\,h2 => h1,h2 + * h1\\h2 => h1\h2 => h1h2 + * h1\\\\h2 => h1\\h2 => h1\h2 + */ + if (comma && !comma_escape && c == ',') { + seen_comma = true; + break; + } + if (comma && !comma_escape && c == '\\') { + comma_escape = true; + continue; + } + comma_escape = false; + if (nrem == 0) { + return ((tregion.length <= 256U) ? ISC_R_NOSPACE + : DNS_R_SYNTAX); + } + *t++ = c; + nrem--; + } + + /* + * Incomplete escape processing? + */ + if (escape || (comma && comma_escape)) { + return (DNS_R_SYNTAX); + } + + if (comma) { + /* + * Disallow empty ALPN at start (",h1") or in the + * middle ("h1,,h2"). + */ + if (s == source->base || (seen_comma && s == source->base + 1)) + { + return (DNS_R_SYNTAX); + } + isc_textregion_consume(source, s - source->base); + /* + * Disallow empty ALPN at end ("h1,"). + */ + if (seen_comma && source->length == 0) { + return (DNS_R_SYNTAX); + } + } + *tregion.base = (unsigned char)(t - tregion.base - 1); + isc_buffer_add(target, *tregion.base + 1); + return (ISC_R_SUCCESS); +} + +static isc_result_t +txt_fromtext(isc_textregion_t *source, isc_buffer_t *target) { + return (commatxt_fromtext(source, false, target)); +} + +static isc_result_t +txt_fromwire(isc_buffer_t *source, isc_buffer_t *target) { + unsigned int n; + isc_region_t sregion; + isc_region_t tregion; + + isc_buffer_activeregion(source, &sregion); + if (sregion.length == 0) { + return (ISC_R_UNEXPECTEDEND); + } + n = *sregion.base + 1; + if (n > sregion.length) { + return (ISC_R_UNEXPECTEDEND); + } + + isc_buffer_availableregion(target, &tregion); + if (n > tregion.length) { + return (ISC_R_NOSPACE); + } + + if (tregion.base != sregion.base) { + memmove(tregion.base, sregion.base, n); + } + isc_buffer_forward(source, n); + isc_buffer_add(target, n); + return (ISC_R_SUCCESS); +} + +/* + * Conversion of TXT-like rdata fields without length limits. + */ +static isc_result_t +multitxt_totext(isc_region_t *source, isc_buffer_t *target) { + unsigned int tl; + unsigned int n0, n; + unsigned char *sp; + char *tp; + isc_region_t region; + + isc_buffer_availableregion(target, ®ion); + sp = source->base; + tp = (char *)region.base; + tl = region.length; + + if (tl < 1) { + return (ISC_R_NOSPACE); + } + *tp++ = '"'; + tl--; + do { + n = source->length; + n0 = source->length - 1; + + while (n--) { + if (*sp < ' ' || *sp >= 0x7f) { + if (tl < 4) { + return (ISC_R_NOSPACE); + } + *tp++ = '\\'; + *tp++ = '0' + ((*sp / 100) % 10); + *tp++ = '0' + ((*sp / 10) % 10); + *tp++ = '0' + (*sp % 10); + sp++; + tl -= 4; + continue; + } + /* double quote, backslash */ + if (*sp == '"' || *sp == '\\') { + if (tl < 2) { + return (ISC_R_NOSPACE); + } + *tp++ = '\\'; + tl--; + } + if (tl < 1) { + return (ISC_R_NOSPACE); + } + *tp++ = *sp++; + tl--; + } + isc_region_consume(source, n0 + 1); + } while (source->length != 0); + if (tl < 1) { + return (ISC_R_NOSPACE); + } + *tp++ = '"'; + tl--; + POST(tl); + isc_buffer_add(target, (unsigned int)(tp - (char *)region.base)); + return (ISC_R_SUCCESS); +} + +static isc_result_t +multitxt_fromtext(isc_textregion_t *source, isc_buffer_t *target) { + isc_region_t tregion; + bool escape; + unsigned int n, nrem; + char *s; + unsigned char *t0, *t; + int d; + int c; + + s = source->base; + n = source->length; + escape = false; + + do { + isc_buffer_availableregion(target, &tregion); + t0 = t = tregion.base; + nrem = tregion.length; + if (nrem < 1) { + return (ISC_R_NOSPACE); + } + + while (n != 0) { + --n; + c = (*s++) & 0xff; + if (escape && (d = decvalue((char)c)) != -1) { + c = d; + if (n == 0) { + return (DNS_R_SYNTAX); + } + n--; + if ((d = decvalue(*s++)) != -1) { + c = c * 10 + d; + } else { + return (DNS_R_SYNTAX); + } + if (n == 0) { + return (DNS_R_SYNTAX); + } + n--; + if ((d = decvalue(*s++)) != -1) { + c = c * 10 + d; + } else { + return (DNS_R_SYNTAX); + } + if (c > 255) { + return (DNS_R_SYNTAX); + } + } else if (!escape && c == '\\') { + escape = true; + continue; + } + escape = false; + *t++ = c; + nrem--; + if (nrem == 0) { + break; + } + } + if (escape) { + return (DNS_R_SYNTAX); + } + + isc_buffer_add(target, (unsigned int)(t - t0)); + } while (n != 0); + return (ISC_R_SUCCESS); +} + +static bool +name_prefix(dns_name_t *name, const dns_name_t *origin, dns_name_t *target) { + int l1, l2; + + if (origin == NULL) { + goto return_false; + } + + if (dns_name_compare(origin, dns_rootname) == 0) { + goto return_false; + } + + if (!dns_name_issubdomain(name, origin)) { + goto return_false; + } + + l1 = dns_name_countlabels(name); + l2 = dns_name_countlabels(origin); + + if (l1 == l2) { + goto return_false; + } + + /* Master files should be case preserving. */ + dns_name_getlabelsequence(name, l1 - l2, l2, target); + if (!dns_name_caseequal(origin, target)) { + goto return_false; + } + + dns_name_getlabelsequence(name, 0, l1 - l2, target); + return (true); + +return_false: + *target = *name; + return (false); +} + +static isc_result_t +str_totext(const char *source, isc_buffer_t *target) { + unsigned int l; + isc_region_t region; + + isc_buffer_availableregion(target, ®ion); + l = strlen(source); + + if (l > region.length) { + return (ISC_R_NOSPACE); + } + + memmove(region.base, source, l); + isc_buffer_add(target, l); + return (ISC_R_SUCCESS); +} + +static isc_result_t +inet_totext(int af, uint32_t flags, isc_region_t *src, isc_buffer_t *target) { + char tmpbuf[64]; + + /* Note - inet_ntop doesn't do size checking on its input. */ + if (inet_ntop(af, src->base, tmpbuf, sizeof(tmpbuf)) == NULL) { + return (ISC_R_NOSPACE); + } + if (strlen(tmpbuf) > isc_buffer_availablelength(target)) { + return (ISC_R_NOSPACE); + } + isc_buffer_putstr(target, tmpbuf); + + /* + * An IPv6 address ending in "::" breaks YAML + * parsing, so append 0 in that case. + */ + if (af == AF_INET6 && (flags & DNS_STYLEFLAG_YAML) != 0) { + isc_region_t r; + isc_buffer_usedregion(target, &r); + if (r.length > 0 && r.base[r.length - 1] == ':') { + if (isc_buffer_availablelength(target) == 0) { + return (ISC_R_NOSPACE); + } + isc_buffer_putmem(target, (const unsigned char *)"0", + 1); + } + } + + return (ISC_R_SUCCESS); +} + +static bool +buffer_empty(isc_buffer_t *source) { + return ((source->current == source->active) ? true : false); +} + +static void +buffer_fromregion(isc_buffer_t *buffer, isc_region_t *region) { + isc_buffer_init(buffer, region->base, region->length); + isc_buffer_add(buffer, region->length); + isc_buffer_setactive(buffer, region->length); +} + +static isc_result_t +uint32_tobuffer(uint32_t value, isc_buffer_t *target) { + isc_region_t region; + + isc_buffer_availableregion(target, ®ion); + if (region.length < 4) { + return (ISC_R_NOSPACE); + } + isc_buffer_putuint32(target, value); + return (ISC_R_SUCCESS); +} + +static isc_result_t +uint16_tobuffer(uint32_t value, isc_buffer_t *target) { + isc_region_t region; + + if (value > 0xffff) { + return (ISC_R_RANGE); + } + isc_buffer_availableregion(target, ®ion); + if (region.length < 2) { + return (ISC_R_NOSPACE); + } + isc_buffer_putuint16(target, (uint16_t)value); + return (ISC_R_SUCCESS); +} + +static isc_result_t +uint8_tobuffer(uint32_t value, isc_buffer_t *target) { + isc_region_t region; + + if (value > 0xff) { + return (ISC_R_RANGE); + } + isc_buffer_availableregion(target, ®ion); + if (region.length < 1) { + return (ISC_R_NOSPACE); + } + isc_buffer_putuint8(target, (uint8_t)value); + return (ISC_R_SUCCESS); +} + +static isc_result_t +name_tobuffer(const dns_name_t *name, isc_buffer_t *target) { + isc_region_t r; + dns_name_toregion(name, &r); + return (isc_buffer_copyregion(target, &r)); +} + +static uint32_t +uint32_fromregion(isc_region_t *region) { + uint32_t value; + + REQUIRE(region->length >= 4); + value = (uint32_t)region->base[0] << 24; + value |= (uint32_t)region->base[1] << 16; + value |= (uint32_t)region->base[2] << 8; + value |= (uint32_t)region->base[3]; + return (value); +} + +static uint16_t +uint16_consume_fromregion(isc_region_t *region) { + uint16_t r = uint16_fromregion(region); + + isc_region_consume(region, 2); + return (r); +} + +static uint16_t +uint16_fromregion(isc_region_t *region) { + REQUIRE(region->length >= 2); + + return ((region->base[0] << 8) | region->base[1]); +} + +static uint8_t +uint8_fromregion(isc_region_t *region) { + REQUIRE(region->length >= 1); + + return (region->base[0]); +} + +static uint8_t +uint8_consume_fromregion(isc_region_t *region) { + uint8_t r = uint8_fromregion(region); + + isc_region_consume(region, 1); + return (r); +} + +static isc_result_t +mem_tobuffer(isc_buffer_t *target, void *base, unsigned int length) { + isc_region_t tr; + + if (length == 0U) { + return (ISC_R_SUCCESS); + } + + isc_buffer_availableregion(target, &tr); + if (length > tr.length) { + return (ISC_R_NOSPACE); + } + if (tr.base != base) { + memmove(tr.base, base, length); + } + isc_buffer_add(target, length); + return (ISC_R_SUCCESS); +} + +static int +hexvalue(char value) { + const char *s; + unsigned char c; + + c = (unsigned char)value; + + if (!isascii(c)) { + return (-1); + } + if (isupper(c)) { + c = tolower(c); + } + if ((s = strchr(hexdigits, c)) == NULL) { + return (-1); + } + return ((int)(s - hexdigits)); +} + +static int +decvalue(char value) { + const char *s; + + /* + * isascii() is valid for full range of int values, no need to + * mask or cast. + */ + if (!isascii(value)) { + return (-1); + } + if ((s = strchr(decdigits, value)) == NULL) { + return (-1); + } + return ((int)(s - decdigits)); +} + +static void +default_fromtext_callback(dns_rdatacallbacks_t *callbacks, const char *fmt, + ...) { + va_list ap; + + UNUSED(callbacks); + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, "\n"); +} + +static void +fromtext_warneof(isc_lex_t *lexer, dns_rdatacallbacks_t *callbacks) { + if (isc_lex_isfile(lexer) && callbacks != NULL) { + const char *name = isc_lex_getsourcename(lexer); + if (name == NULL) { + name = "UNKNOWN"; + } + (*callbacks->warn)(callbacks, + "%s:%lu: file does not end with newline", + name, isc_lex_getsourceline(lexer)); + } +} + +static void +warn_badmx(isc_token_t *token, isc_lex_t *lexer, + dns_rdatacallbacks_t *callbacks) { + const char *file; + unsigned long line; + + if (lexer != NULL) { + file = isc_lex_getsourcename(lexer); + line = isc_lex_getsourceline(lexer); + (*callbacks->warn)(callbacks, "%s:%u: warning: '%s': %s", file, + line, DNS_AS_STR(*token), + dns_result_totext(DNS_R_MXISADDRESS)); + } +} + +static void +warn_badname(const dns_name_t *name, isc_lex_t *lexer, + dns_rdatacallbacks_t *callbacks) { + const char *file; + unsigned long line; + char namebuf[DNS_NAME_FORMATSIZE]; + + if (lexer != NULL) { + file = isc_lex_getsourcename(lexer); + line = isc_lex_getsourceline(lexer); + dns_name_format(name, namebuf, sizeof(namebuf)); + (*callbacks->warn)(callbacks, "%s:%u: warning: %s: %s", file, + line, namebuf, + dns_result_totext(DNS_R_BADNAME)); + } +} + +static void +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) { + if (name == NULL) { + name = "UNKNOWN"; + } + + if (token != NULL) { + switch (token->type) { + case isc_tokentype_eol: + (*callback)(callbacks, "%s: %s:%lu: near eol: %s", + "dns_rdata_fromtext", name, line, + dns_result_totext(result)); + break; + case isc_tokentype_eof: + (*callback)(callbacks, "%s: %s:%lu: near eof: %s", + "dns_rdata_fromtext", name, line, + dns_result_totext(result)); + break; + case isc_tokentype_number: + (*callback)(callbacks, "%s: %s:%lu: near %lu: %s", + "dns_rdata_fromtext", name, line, + token->value.as_ulong, + dns_result_totext(result)); + break; + case isc_tokentype_string: + case isc_tokentype_qstring: + (*callback)(callbacks, "%s: %s:%lu: near '%s': %s", + "dns_rdata_fromtext", name, line, + DNS_AS_STR(*token), + dns_result_totext(result)); + break; + default: + (*callback)(callbacks, "%s: %s:%lu: %s", + "dns_rdata_fromtext", name, line, + dns_result_totext(result)); + break; + } + } else { + (*callback)(callbacks, "dns_rdata_fromtext: %s:%lu: %s", name, + line, dns_result_totext(result)); + } +} + +dns_rdatatype_t +dns_rdata_covers(dns_rdata_t *rdata) { + if (rdata->type == dns_rdatatype_rrsig) { + return (covers_rrsig(rdata)); + } + return (covers_sig(rdata)); +} + +bool +dns_rdatatype_ismeta(dns_rdatatype_t type) { + if ((dns_rdatatype_attributes(type) & DNS_RDATATYPEATTR_META) != 0) { + return (true); + } + return (false); +} + +bool +dns_rdatatype_issingleton(dns_rdatatype_t type) { + if ((dns_rdatatype_attributes(type) & DNS_RDATATYPEATTR_SINGLETON) != 0) + { + return (true); + } + return (false); +} + +bool +dns_rdatatype_notquestion(dns_rdatatype_t type) { + if ((dns_rdatatype_attributes(type) & DNS_RDATATYPEATTR_NOTQUESTION) != + 0) + { + return (true); + } + return (false); +} + +bool +dns_rdatatype_questiononly(dns_rdatatype_t type) { + if ((dns_rdatatype_attributes(type) & DNS_RDATATYPEATTR_QUESTIONONLY) != + 0) + { + return (true); + } + return (false); +} + +bool +dns_rdatatype_atcname(dns_rdatatype_t type) { + if ((dns_rdatatype_attributes(type) & DNS_RDATATYPEATTR_ATCNAME) != 0) { + return (true); + } + return (false); +} + +bool +dns_rdatatype_atparent(dns_rdatatype_t type) { + if ((dns_rdatatype_attributes(type) & DNS_RDATATYPEATTR_ATPARENT) != 0) + { + return (true); + } + return (false); +} + +bool +dns_rdatatype_followadditional(dns_rdatatype_t type) { + if ((dns_rdatatype_attributes(type) & + DNS_RDATATYPEATTR_FOLLOWADDITIONAL) != 0) + { + return (true); + } + return (false); +} + +bool +dns_rdataclass_ismeta(dns_rdataclass_t rdclass) { + if (rdclass == dns_rdataclass_reserved0 || + rdclass == dns_rdataclass_none || rdclass == dns_rdataclass_any) + { + return (true); + } + + return (false); /* Assume it is not a meta class. */ +} + +bool +dns_rdatatype_isdnssec(dns_rdatatype_t type) { + if ((dns_rdatatype_attributes(type) & DNS_RDATATYPEATTR_DNSSEC) != 0) { + return (true); + } + return (false); +} + +bool +dns_rdatatype_iszonecutauth(dns_rdatatype_t type) { + if ((dns_rdatatype_attributes(type) & DNS_RDATATYPEATTR_ZONECUTAUTH) != + 0) + { + return (true); + } + return (false); +} + +bool +dns_rdatatype_isknown(dns_rdatatype_t type) { + if ((dns_rdatatype_attributes(type) & DNS_RDATATYPEATTR_UNKNOWN) == 0) { + return (true); + } + return (false); +} + +void +dns_rdata_exists(dns_rdata_t *rdata, dns_rdatatype_t type) { + REQUIRE(rdata != NULL); + REQUIRE(DNS_RDATA_INITIALIZED(rdata)); + + rdata->data = NULL; + rdata->length = 0; + rdata->flags = DNS_RDATA_UPDATE; + rdata->type = type; + rdata->rdclass = dns_rdataclass_any; +} + +void +dns_rdata_notexist(dns_rdata_t *rdata, dns_rdatatype_t type) { + REQUIRE(rdata != NULL); + REQUIRE(DNS_RDATA_INITIALIZED(rdata)); + + rdata->data = NULL; + rdata->length = 0; + rdata->flags = DNS_RDATA_UPDATE; + rdata->type = type; + rdata->rdclass = dns_rdataclass_none; +} + +void +dns_rdata_deleterrset(dns_rdata_t *rdata, dns_rdatatype_t type) { + REQUIRE(rdata != NULL); + REQUIRE(DNS_RDATA_INITIALIZED(rdata)); + + rdata->data = NULL; + rdata->length = 0; + rdata->flags = DNS_RDATA_UPDATE; + rdata->type = type; + rdata->rdclass = dns_rdataclass_any; +} + +void +dns_rdata_makedelete(dns_rdata_t *rdata) { + REQUIRE(rdata != NULL); + + rdata->rdclass = dns_rdataclass_none; +} + +const char * +dns_rdata_updateop(dns_rdata_t *rdata, dns_section_t section) { + REQUIRE(rdata != NULL); + REQUIRE(DNS_RDATA_INITIALIZED(rdata)); + + switch (section) { + case DNS_SECTION_PREREQUISITE: + switch (rdata->rdclass) { + case dns_rdataclass_none: + switch (rdata->type) { + case dns_rdatatype_any: + return ("domain doesn't exist"); + default: + return ("rrset doesn't exist"); + } + case dns_rdataclass_any: + switch (rdata->type) { + case dns_rdatatype_any: + return ("domain exists"); + default: + return ("rrset exists (value independent)"); + } + default: + return ("rrset exists (value dependent)"); + } + case DNS_SECTION_UPDATE: + switch (rdata->rdclass) { + case dns_rdataclass_none: + return ("delete"); + case dns_rdataclass_any: + switch (rdata->type) { + case dns_rdatatype_any: + return ("delete all rrsets"); + default: + return ("delete rrset"); + } + default: + return ("add"); + } + } + return ("invalid"); +} |