mirror of
https://gitlab.isc.org/isc-projects/bind9
synced 2025-08-28 13:08:06 +00:00
update are now fully supported and no longer require defines to enable. We now no longer overload the NSEC3PARAM flag field, nor the NSEC OPT bit at the apex. Secure to insecure changes are controlled by by the named.conf option 'secure-to-insecure'. Warning: If you had previously enabled support by adding defines at compile time to BIND 9.6 you should ensure that all changes that are in progress have completed prior to upgrading to BIND 9.7. BIND 9.7 is not backwards compatible.
1844 lines
43 KiB
C
1844 lines
43 KiB
C
/*
|
|
* Copyright (C) 2004-2009 Internet Systems Consortium, Inc. ("ISC")
|
|
* Copyright (C) 1998-2003 Internet Software Consortium.
|
|
*
|
|
* Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH
|
|
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
* AND FITNESS. IN NO EVENT SHALL ISC 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.
|
|
*/
|
|
|
|
/* $Id: rdata.c,v 1.203 2009/10/08 23:13:07 marka Exp $ */
|
|
|
|
/*! \file */
|
|
|
|
#include <config.h>
|
|
#include <ctype.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/stdlib.h>
|
|
#include <isc/util.h>
|
|
|
|
#include <dns/callbacks.h>
|
|
#include <dns/cert.h>
|
|
#include <dns/compress.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 DNS_AS_STR(t) ((t).value.as_textregion.base)
|
|
|
|
#define ARGS_FROMTEXT int rdclass, dns_rdatatype_t type, \
|
|
isc_lex_t *lexer, dns_name_t *origin, \
|
|
unsigned int options, isc_buffer_t *target, \
|
|
dns_rdatacallbacks_t *callbacks
|
|
|
|
#define ARGS_TOTEXT dns_rdata_t *rdata, dns_rdata_textctx_t *tctx, \
|
|
isc_buffer_t *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 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
|
|
|
|
#define ARGS_FROMSTRUCT int rdclass, dns_rdatatype_t type, \
|
|
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
|
|
|
|
#define ARGS_CHECKOWNER dns_name_t *name, dns_rdataclass_t rdclass, \
|
|
dns_rdatatype_t type, isc_boolean_t wildcard
|
|
|
|
#define ARGS_CHECKNAMES dns_rdata_t *rdata, dns_name_t *owner, dns_name_t *bad
|
|
|
|
|
|
/*%
|
|
* Context structure for the totext_ functions.
|
|
* 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. */
|
|
const char *linebreak; /*%< Line break string. */
|
|
} dns_rdata_textctx_t;
|
|
|
|
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
|
|
str_totext(const char *source, isc_buffer_t *target);
|
|
|
|
static isc_result_t
|
|
inet_totext(int af, isc_region_t *src, isc_buffer_t *target);
|
|
|
|
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_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 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
|
|
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(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 isc_uint16_t
|
|
uint16_consume_fromregion(isc_region_t *region);
|
|
|
|
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) {
|
|
const char *name = isc_lex_getsourcename(lexer);
|
|
if (name == NULL)
|
|
name = "UNKNOWN";
|
|
(*callbacks->warn)(callbacks, "%s:%lu: \"%s\" "
|
|
"is not a decimal dotted quad", name,
|
|
isc_lex_getsourceline(lexer), src);
|
|
}
|
|
return (result);
|
|
}
|
|
|
|
static inline isc_result_t
|
|
name_duporclone(dns_name_t *source, isc_mem_t *mctx, dns_name_t *target) {
|
|
|
|
if (mctx != NULL)
|
|
return (dns_name_dup(source, mctx, target));
|
|
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);
|
|
|
|
return (new);
|
|
}
|
|
|
|
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;
|
|
isc_boolean_t use_default = ISC_FALSE;
|
|
|
|
REQUIRE(rdata1 != NULL);
|
|
REQUIRE(rdata2 != NULL);
|
|
REQUIRE(rdata1->data != NULL);
|
|
REQUIRE(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);
|
|
}
|
|
|
|
/***
|
|
*** 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;
|
|
isc_boolean_t use_default = ISC_FALSE;
|
|
isc_uint32_t activelength;
|
|
|
|
REQUIRE(dctx != NULL);
|
|
if (rdata != NULL) {
|
|
REQUIRE(DNS_RDATA_INITIALIZED(rdata));
|
|
REQUIRE(DNS_RDATA_VALIDFLAGS(rdata));
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 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 = isc_buffer_usedlength(target) -
|
|
isc_buffer_usedlength(&st);
|
|
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;
|
|
isc_boolean_t use_default = ISC_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);
|
|
memcpy(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, (isc_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;
|
|
dns_rdata_t rdata = DNS_RDATA_INIT;
|
|
isc_result_t result;
|
|
|
|
dns_decompress_init(&dctx, -1, DNS_DECOMPRESS_NONE);
|
|
isc_buffer_setactive(src, isc_buffer_usedlength(src));
|
|
result = dns_rdata_fromwire(&rdata, 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);
|
|
|
|
result = isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
|
|
ISC_FALSE);
|
|
if (result == ISC_R_SUCCESS && token.value.as_ulong > 65535U)
|
|
return (ISC_R_RANGE);
|
|
result = isc_buffer_allocate(mctx, &buf, token.value.as_ulong);
|
|
if (result != ISC_R_SUCCESS)
|
|
return (result);
|
|
|
|
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,
|
|
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;
|
|
|
|
REQUIRE(origin == NULL || dns_name_isabsolute(origin) == ISC_TRUE);
|
|
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,
|
|
ISC_FALSE);
|
|
if (result != ISC_R_SUCCESS) {
|
|
name = isc_lex_getsourcename(lexer);
|
|
line = isc_lex_getsourceline(lexer);
|
|
fromtext_error(callback, callbacks, name, line,
|
|
&token, result);
|
|
return (result);
|
|
}
|
|
|
|
if (strcmp(DNS_AS_STR(token), "\\#") == 0)
|
|
result = unknown_fromtext(rdclass, type, lexer, mctx, target);
|
|
else {
|
|
isc_lex_ungettoken(lexer, &token);
|
|
|
|
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);
|
|
|
|
if (rdata != NULL && result == ISC_R_SUCCESS) {
|
|
region.base = isc_buffer_used(&st);
|
|
region.length = isc_buffer_usedlength(target) -
|
|
isc_buffer_usedlength(&st);
|
|
dns_rdata_fromregion(rdata, rdclass, type, ®ion);
|
|
}
|
|
if (result != ISC_R_SUCCESS) {
|
|
*target = st;
|
|
}
|
|
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;
|
|
isc_boolean_t use_default = ISC_FALSE;
|
|
char buf[sizeof("65535")];
|
|
isc_region_t sr;
|
|
|
|
REQUIRE(rdata != NULL);
|
|
REQUIRE(tctx->origin == NULL ||
|
|
dns_name_isabsolute(tctx->origin) == ISC_TRUE);
|
|
|
|
/*
|
|
* Some DynDNS meta-RRs have empty rdata.
|
|
*/
|
|
if ((rdata->flags & DNS_RDATA_UPDATE) != 0) {
|
|
INSIST(rdata->length == 0);
|
|
return (ISC_R_SUCCESS);
|
|
}
|
|
|
|
TOTEXTSWITCH
|
|
|
|
if (use_default) {
|
|
strlcpy(buf, "\\# ", sizeof(buf));
|
|
result = str_totext(buf, target);
|
|
dns_rdata_toregion(rdata, &sr);
|
|
INSIST(sr.length < 65536);
|
|
snprintf(buf, sizeof(buf), "%u", sr.length);
|
|
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)
|
|
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);
|
|
}
|
|
|
|
isc_result_t
|
|
dns_rdata_totext(dns_rdata_t *rdata, 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, dns_name_t *origin,
|
|
unsigned int flags, unsigned int 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 ((flags & DNS_STYLEFLAG_MULTILINE) != 0) {
|
|
tctx.width = width;
|
|
tctx.linebreak = linebreak;
|
|
} else {
|
|
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;
|
|
isc_boolean_t use_default = ISC_FALSE;
|
|
|
|
REQUIRE(source != NULL);
|
|
if (rdata != NULL) {
|
|
REQUIRE(DNS_RDATA_INITIALIZED(rdata));
|
|
REQUIRE(DNS_RDATA_VALIDFLAGS(rdata));
|
|
}
|
|
|
|
st = *target;
|
|
|
|
FROMSTRUCTSWITCH
|
|
|
|
if (use_default)
|
|
(void)NULL;
|
|
|
|
if (rdata != NULL && result == ISC_R_SUCCESS) {
|
|
region.base = isc_buffer_used(&st);
|
|
region.length = isc_buffer_usedlength(target) -
|
|
isc_buffer_usedlength(&st);
|
|
dns_rdata_fromregion(rdata, rdclass, type, ®ion);
|
|
}
|
|
if (result != ISC_R_SUCCESS)
|
|
*target = st;
|
|
return (result);
|
|
}
|
|
|
|
isc_result_t
|
|
dns_rdata_tostruct(dns_rdata_t *rdata, void *target, isc_mem_t *mctx) {
|
|
isc_result_t result = ISC_R_NOTIMPLEMENTED;
|
|
isc_boolean_t use_default = ISC_FALSE;
|
|
|
|
REQUIRE(rdata != NULL);
|
|
REQUIRE(DNS_RDATA_VALIDFLAGS(rdata));
|
|
|
|
TOSTRUCTSWITCH
|
|
|
|
if (use_default)
|
|
(void)NULL;
|
|
|
|
return (result);
|
|
}
|
|
|
|
void
|
|
dns_rdata_freestruct(void *source) {
|
|
dns_rdatacommon_t *common = source;
|
|
REQUIRE(source != 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;
|
|
isc_boolean_t use_default = ISC_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;
|
|
isc_boolean_t use_default = ISC_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);
|
|
}
|
|
|
|
isc_boolean_t
|
|
dns_rdata_checkowner(dns_name_t *name, dns_rdataclass_t rdclass,
|
|
dns_rdatatype_t type, isc_boolean_t wildcard)
|
|
{
|
|
isc_boolean_t result;
|
|
|
|
CHECKOWNERSWITCH
|
|
return (result);
|
|
}
|
|
|
|
isc_boolean_t
|
|
dns_rdata_checknames(dns_rdata_t *rdata, dns_name_t *owner, dns_name_t *bad)
|
|
{
|
|
isc_boolean_t 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;
|
|
|
|
strncpy(buf, source->base + 4, source->length - 4);
|
|
buf[source->length - 4] = '\0';
|
|
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) {
|
|
char buf[sizeof("TYPE65535")];
|
|
|
|
RDATATYPE_TOTEXT_SW
|
|
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;
|
|
|
|
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) {
|
|
snprintf(array, size, "<unknown>");
|
|
array[size - 1] = '\0';
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Private function.
|
|
*/
|
|
|
|
static unsigned int
|
|
name_length(dns_name_t *name) {
|
|
return (name->length);
|
|
}
|
|
|
|
static isc_result_t
|
|
txt_totext(isc_region_t *source, 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 (tl < 1)
|
|
return (ISC_R_NOSPACE);
|
|
*tp++ = '"';
|
|
tl--;
|
|
while (n--) {
|
|
if (*sp < 0x20 || *sp >= 0x7f) {
|
|
if (tl < 4)
|
|
return (ISC_R_NOSPACE);
|
|
*tp++ = 0x5c;
|
|
*tp++ = 0x30 + ((*sp / 100) % 10);
|
|
*tp++ = 0x30 + ((*sp / 10) % 10);
|
|
*tp++ = 0x30 + (*sp % 10);
|
|
sp++;
|
|
tl -= 4;
|
|
continue;
|
|
}
|
|
/* double quote, semi-colon, backslash */
|
|
if (*sp == 0x22 || *sp == 0x3b || *sp == 0x5c) {
|
|
if (tl < 2)
|
|
return (ISC_R_NOSPACE);
|
|
*tp++ = '\\';
|
|
tl--;
|
|
}
|
|
if (tl < 1)
|
|
return (ISC_R_NOSPACE);
|
|
*tp++ = *sp++;
|
|
tl--;
|
|
}
|
|
if (tl < 1)
|
|
return (ISC_R_NOSPACE);
|
|
*tp++ = '"';
|
|
tl--;
|
|
isc_buffer_add(target, tp - (char *)region.base);
|
|
isc_region_consume(source, *source->base + 1);
|
|
return (ISC_R_SUCCESS);
|
|
}
|
|
|
|
static isc_result_t
|
|
txt_fromtext(isc_textregion_t *source, isc_buffer_t *target) {
|
|
isc_region_t tregion;
|
|
isc_boolean_t escape;
|
|
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;
|
|
escape = ISC_FALSE;
|
|
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 = ISC_TRUE;
|
|
continue;
|
|
}
|
|
escape = ISC_FALSE;
|
|
if (nrem == 0)
|
|
return (ISC_R_NOSPACE);
|
|
*t++ = c;
|
|
nrem--;
|
|
}
|
|
if (escape)
|
|
return (DNS_R_SYNTAX);
|
|
*tregion.base = t - tregion.base - 1;
|
|
isc_buffer_add(target, *tregion.base + 1);
|
|
return (ISC_R_SUCCESS);
|
|
}
|
|
|
|
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);
|
|
|
|
memcpy(tregion.base, sregion.base, n);
|
|
isc_buffer_forward(source, n);
|
|
isc_buffer_add(target, n);
|
|
return (ISC_R_SUCCESS);
|
|
}
|
|
|
|
static isc_boolean_t
|
|
name_prefix(dns_name_t *name, 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;
|
|
|
|
dns_name_getlabelsequence(name, 0, l1 - l2, target);
|
|
return (ISC_TRUE);
|
|
|
|
return_false:
|
|
*target = *name;
|
|
return (ISC_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);
|
|
|
|
memcpy(region.base, source, l);
|
|
isc_buffer_add(target, l);
|
|
return (ISC_R_SUCCESS);
|
|
}
|
|
|
|
static isc_result_t
|
|
inet_totext(int af, 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);
|
|
return (ISC_R_SUCCESS);
|
|
}
|
|
|
|
static isc_boolean_t
|
|
buffer_empty(isc_buffer_t *source) {
|
|
return((source->current == source->active) ? ISC_TRUE : ISC_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(isc_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(isc_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, (isc_uint16_t)value);
|
|
return (ISC_R_SUCCESS);
|
|
}
|
|
|
|
static isc_result_t
|
|
uint8_tobuffer(isc_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, (isc_uint8_t)value);
|
|
return (ISC_R_SUCCESS);
|
|
}
|
|
|
|
static isc_result_t
|
|
name_tobuffer(dns_name_t *name, isc_buffer_t *target) {
|
|
isc_region_t r;
|
|
dns_name_toregion(name, &r);
|
|
return (isc_buffer_copyregion(target, &r));
|
|
}
|
|
|
|
static isc_uint32_t
|
|
uint32_fromregion(isc_region_t *region) {
|
|
isc_uint32_t value;
|
|
|
|
REQUIRE(region->length >= 4);
|
|
value = region->base[0] << 24;
|
|
value |= region->base[1] << 16;
|
|
value |= region->base[2] << 8;
|
|
value |= region->base[3];
|
|
return(value);
|
|
}
|
|
|
|
static isc_uint16_t
|
|
uint16_consume_fromregion(isc_region_t *region) {
|
|
isc_uint16_t r = uint16_fromregion(region);
|
|
|
|
isc_region_consume(region, 2);
|
|
return r;
|
|
}
|
|
|
|
static isc_uint16_t
|
|
uint16_fromregion(isc_region_t *region) {
|
|
|
|
REQUIRE(region->length >= 2);
|
|
|
|
return ((region->base[0] << 8) | region->base[1]);
|
|
}
|
|
|
|
static isc_uint8_t
|
|
uint8_fromregion(isc_region_t *region) {
|
|
|
|
REQUIRE(region->length >= 1);
|
|
|
|
return (region->base[0]);
|
|
}
|
|
|
|
static isc_uint8_t
|
|
uint8_consume_fromregion(isc_region_t *region) {
|
|
isc_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;
|
|
|
|
isc_buffer_availableregion(target, &tr);
|
|
if (length > tr.length)
|
|
return (ISC_R_NOSPACE);
|
|
memcpy(tr.base, base, length);
|
|
isc_buffer_add(target, length);
|
|
return (ISC_R_SUCCESS);
|
|
}
|
|
|
|
static int
|
|
hexvalue(char value) {
|
|
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 (s - hexdigits);
|
|
}
|
|
|
|
static int
|
|
decvalue(char value) {
|
|
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 (s - decdigits);
|
|
}
|
|
|
|
static const char atob_digits[86] =
|
|
"!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`" \
|
|
"abcdefghijklmnopqrstu";
|
|
/*
|
|
* Subroutines to convert between 8 bit binary bytes and printable ASCII.
|
|
* Computes the number of bytes, and three kinds of simple checksums.
|
|
* Incoming bytes are collected into 32-bit words, then printed in base 85:
|
|
* exp(85,5) > exp(2,32)
|
|
* The ASCII characters used are between '!' and 'u';
|
|
* 'z' encodes 32-bit zero; 'x' is used to mark the end of encoded data.
|
|
*
|
|
* Originally by Paul Rutter (philabs!per) and Joe Orost (petsd!joe) for
|
|
* the atob/btoa programs, released with the compress program, in mod.sources.
|
|
* Modified by Mike Schwartz 8/19/86 for use in BIND.
|
|
* Modified to be re-entrant 3/2/99.
|
|
*/
|
|
|
|
|
|
struct state {
|
|
isc_int32_t Ceor;
|
|
isc_int32_t Csum;
|
|
isc_int32_t Crot;
|
|
isc_int32_t word;
|
|
isc_int32_t bcount;
|
|
};
|
|
|
|
#define Ceor state->Ceor
|
|
#define Csum state->Csum
|
|
#define Crot state->Crot
|
|
#define word state->word
|
|
#define bcount state->bcount
|
|
|
|
#define times85(x) ((((((x<<2)+x)<<2)+x)<<2)+x)
|
|
|
|
static isc_result_t byte_atob(int c, isc_buffer_t *target,
|
|
struct state *state);
|
|
static isc_result_t putbyte(int c, isc_buffer_t *, struct state *state);
|
|
static isc_result_t byte_btoa(int c, isc_buffer_t *, struct state *state);
|
|
|
|
/*
|
|
* Decode ASCII-encoded byte c into binary representation and
|
|
* place into *bufp, advancing bufp.
|
|
*/
|
|
static isc_result_t
|
|
byte_atob(int c, isc_buffer_t *target, struct state *state) {
|
|
char *s;
|
|
if (c == 'z') {
|
|
if (bcount != 0)
|
|
return(DNS_R_SYNTAX);
|
|
else {
|
|
RETERR(putbyte(0, target, state));
|
|
RETERR(putbyte(0, target, state));
|
|
RETERR(putbyte(0, target, state));
|
|
RETERR(putbyte(0, target, state));
|
|
}
|
|
} else if ((s = strchr(atob_digits, c)) != NULL) {
|
|
if (bcount == 0) {
|
|
word = s - atob_digits;
|
|
++bcount;
|
|
} else if (bcount < 4) {
|
|
word = times85(word);
|
|
word += s - atob_digits;
|
|
++bcount;
|
|
} else {
|
|
word = times85(word);
|
|
word += s - atob_digits;
|
|
RETERR(putbyte((word >> 24) & 0xff, target, state));
|
|
RETERR(putbyte((word >> 16) & 0xff, target, state));
|
|
RETERR(putbyte((word >> 8) & 0xff, target, state));
|
|
RETERR(putbyte(word & 0xff, target, state));
|
|
word = 0;
|
|
bcount = 0;
|
|
}
|
|
} else
|
|
return(DNS_R_SYNTAX);
|
|
return(ISC_R_SUCCESS);
|
|
}
|
|
|
|
/*
|
|
* Compute checksum info and place c into target.
|
|
*/
|
|
static isc_result_t
|
|
putbyte(int c, isc_buffer_t *target, struct state *state) {
|
|
isc_region_t tr;
|
|
|
|
Ceor ^= c;
|
|
Csum += c;
|
|
Csum += 1;
|
|
if ((Crot & 0x80000000)) {
|
|
Crot <<= 1;
|
|
Crot += 1;
|
|
} else {
|
|
Crot <<= 1;
|
|
}
|
|
Crot += c;
|
|
isc_buffer_availableregion(target, &tr);
|
|
if (tr.length < 1)
|
|
return (ISC_R_NOSPACE);
|
|
tr.base[0] = c;
|
|
isc_buffer_add(target, 1);
|
|
return (ISC_R_SUCCESS);
|
|
}
|
|
|
|
/*
|
|
* Read the ASCII-encoded data from inbuf, of length inbuflen, and convert
|
|
* it into T_UNSPEC (binary data) in outbuf, not to exceed outbuflen bytes;
|
|
* outbuflen must be divisible by 4. (Note: this is because outbuf is filled
|
|
* in 4 bytes at a time. If the actual data doesn't end on an even 4-byte
|
|
* boundary, there will be no problem...it will be padded with 0 bytes, and
|
|
* numbytes will indicate the correct number of bytes. The main point is
|
|
* that since the buffer is filled in 4 bytes at a time, even if there is
|
|
* not a full 4 bytes of data at the end, there has to be room to 0-pad the
|
|
* data, so the buffer must be of size divisible by 4). Place the number of
|
|
* output bytes in numbytes, and return a failure/success status.
|
|
*/
|
|
|
|
static isc_result_t
|
|
atob_tobuffer(isc_lex_t *lexer, isc_buffer_t *target) {
|
|
long oeor, osum, orot;
|
|
struct state statebuf, *state= &statebuf;
|
|
isc_token_t token;
|
|
char c;
|
|
char *e;
|
|
|
|
Ceor = Csum = Crot = word = bcount = 0;
|
|
|
|
RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
|
|
ISC_FALSE));
|
|
while (token.value.as_textregion.length != 0) {
|
|
if ((c = token.value.as_textregion.base[0]) == 'x') {
|
|
break;
|
|
} else
|
|
RETERR(byte_atob(c, target, state));
|
|
isc_textregion_consume(&token.value.as_textregion, 1);
|
|
}
|
|
|
|
/*
|
|
* Number of bytes.
|
|
*/
|
|
RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
|
|
ISC_FALSE));
|
|
if ((token.value.as_ulong % 4) != 0U)
|
|
isc_buffer_subtract(target, 4 - (token.value.as_ulong % 4));
|
|
|
|
/*
|
|
* Checksum.
|
|
*/
|
|
RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
|
|
ISC_FALSE));
|
|
oeor = strtol(DNS_AS_STR(token), &e, 16);
|
|
if (*e != 0)
|
|
return (DNS_R_SYNTAX);
|
|
|
|
/*
|
|
* Checksum.
|
|
*/
|
|
RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
|
|
ISC_FALSE));
|
|
osum = strtol(DNS_AS_STR(token), &e, 16);
|
|
if (*e != 0)
|
|
return (DNS_R_SYNTAX);
|
|
|
|
/*
|
|
* Checksum.
|
|
*/
|
|
RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
|
|
ISC_FALSE));
|
|
orot = strtol(DNS_AS_STR(token), &e, 16);
|
|
if (*e != 0)
|
|
return (DNS_R_SYNTAX);
|
|
|
|
if ((oeor != Ceor) || (osum != Csum) || (orot != Crot))
|
|
return(DNS_R_BADCKSUM);
|
|
return (ISC_R_SUCCESS);
|
|
}
|
|
|
|
/*
|
|
* Encode binary byte c into ASCII representation and place into *bufp,
|
|
* advancing bufp.
|
|
*/
|
|
static isc_result_t
|
|
byte_btoa(int c, isc_buffer_t *target, struct state *state) {
|
|
isc_region_t tr;
|
|
|
|
isc_buffer_availableregion(target, &tr);
|
|
Ceor ^= c;
|
|
Csum += c;
|
|
Csum += 1;
|
|
if ((Crot & 0x80000000)) {
|
|
Crot <<= 1;
|
|
Crot += 1;
|
|
} else {
|
|
Crot <<= 1;
|
|
}
|
|
Crot += c;
|
|
|
|
word <<= 8;
|
|
word |= c;
|
|
if (bcount == 3) {
|
|
if (word == 0) {
|
|
if (tr.length < 1)
|
|
return (ISC_R_NOSPACE);
|
|
tr.base[0] = 'z';
|
|
isc_buffer_add(target, 1);
|
|
} else {
|
|
register int tmp = 0;
|
|
register isc_int32_t tmpword = word;
|
|
|
|
if (tmpword < 0) {
|
|
/*
|
|
* Because some don't support u_long.
|
|
*/
|
|
tmp = 32;
|
|
tmpword -= (isc_int32_t)(85 * 85 * 85 * 85 * 32);
|
|
}
|
|
if (tmpword < 0) {
|
|
tmp = 64;
|
|
tmpword -= (isc_int32_t)(85 * 85 * 85 * 85 * 32);
|
|
}
|
|
if (tr.length < 5)
|
|
return (ISC_R_NOSPACE);
|
|
tr.base[0] = atob_digits[(tmpword /
|
|
(isc_int32_t)(85 * 85 * 85 * 85))
|
|
+ tmp];
|
|
tmpword %= (isc_int32_t)(85 * 85 * 85 * 85);
|
|
tr.base[1] = atob_digits[tmpword / (85 * 85 * 85)];
|
|
tmpword %= (85 * 85 * 85);
|
|
tr.base[2] = atob_digits[tmpword / (85 * 85)];
|
|
tmpword %= (85 * 85);
|
|
tr.base[3] = atob_digits[tmpword / 85];
|
|
tmpword %= 85;
|
|
tr.base[4] = atob_digits[tmpword];
|
|
isc_buffer_add(target, 5);
|
|
}
|
|
bcount = 0;
|
|
} else {
|
|
bcount += 1;
|
|
}
|
|
return (ISC_R_SUCCESS);
|
|
}
|
|
|
|
|
|
/*
|
|
* Encode the binary data from inbuf, of length inbuflen, into a
|
|
* target. Return success/failure status
|
|
*/
|
|
static isc_result_t
|
|
btoa_totext(unsigned char *inbuf, int inbuflen, isc_buffer_t *target) {
|
|
int inc;
|
|
struct state statebuf, *state = &statebuf;
|
|
char buf[sizeof("x 2000000000 ffffffff ffffffff ffffffff")];
|
|
|
|
Ceor = Csum = Crot = word = bcount = 0;
|
|
for (inc = 0; inc < inbuflen; inbuf++, inc++)
|
|
RETERR(byte_btoa(*inbuf, target, state));
|
|
|
|
while (bcount != 0)
|
|
RETERR(byte_btoa(0, target, state));
|
|
|
|
/*
|
|
* Put byte count and checksum information at end of buffer,
|
|
* delimited by 'x'
|
|
*/
|
|
snprintf(buf, sizeof(buf), "x %d %x %x %x", inbuflen, Ceor, Csum, Crot);
|
|
return (str_totext(buf, target));
|
|
}
|
|
|
|
|
|
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(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 == 46)
|
|
return (covers_rrsig(rdata));
|
|
return (covers_sig(rdata));
|
|
}
|
|
|
|
isc_boolean_t
|
|
dns_rdatatype_ismeta(dns_rdatatype_t type) {
|
|
if ((dns_rdatatype_attributes(type) & DNS_RDATATYPEATTR_META) != 0)
|
|
return (ISC_TRUE);
|
|
return (ISC_FALSE);
|
|
}
|
|
|
|
isc_boolean_t
|
|
dns_rdatatype_issingleton(dns_rdatatype_t type) {
|
|
if ((dns_rdatatype_attributes(type) & DNS_RDATATYPEATTR_SINGLETON)
|
|
!= 0)
|
|
return (ISC_TRUE);
|
|
return (ISC_FALSE);
|
|
}
|
|
|
|
isc_boolean_t
|
|
dns_rdatatype_notquestion(dns_rdatatype_t type) {
|
|
if ((dns_rdatatype_attributes(type) & DNS_RDATATYPEATTR_NOTQUESTION)
|
|
!= 0)
|
|
return (ISC_TRUE);
|
|
return (ISC_FALSE);
|
|
}
|
|
|
|
isc_boolean_t
|
|
dns_rdatatype_questiononly(dns_rdatatype_t type) {
|
|
if ((dns_rdatatype_attributes(type) & DNS_RDATATYPEATTR_QUESTIONONLY)
|
|
!= 0)
|
|
return (ISC_TRUE);
|
|
return (ISC_FALSE);
|
|
}
|
|
|
|
isc_boolean_t
|
|
dns_rdatatype_atparent(dns_rdatatype_t type) {
|
|
if ((dns_rdatatype_attributes(type) & DNS_RDATATYPEATTR_ATPARENT) != 0)
|
|
return (ISC_TRUE);
|
|
return (ISC_FALSE);
|
|
}
|
|
|
|
isc_boolean_t
|
|
dns_rdataclass_ismeta(dns_rdataclass_t rdclass) {
|
|
|
|
if (rdclass == dns_rdataclass_reserved0
|
|
|| rdclass == dns_rdataclass_none
|
|
|| rdclass == dns_rdataclass_any)
|
|
return (ISC_TRUE);
|
|
|
|
return (ISC_FALSE); /* Assume it is not a meta class. */
|
|
}
|
|
|
|
isc_boolean_t
|
|
dns_rdatatype_isdnssec(dns_rdatatype_t type) {
|
|
if ((dns_rdatatype_attributes(type) & DNS_RDATATYPEATTR_DNSSEC) != 0)
|
|
return (ISC_TRUE);
|
|
return (ISC_FALSE);
|
|
}
|
|
|
|
isc_boolean_t
|
|
dns_rdatatype_iszonecutauth(dns_rdatatype_t type) {
|
|
if ((dns_rdatatype_attributes(type)
|
|
& (DNS_RDATATYPEATTR_DNSSEC | DNS_RDATATYPEATTR_ZONECUTAUTH))
|
|
!= 0)
|
|
return (ISC_TRUE);
|
|
return (ISC_FALSE);
|
|
}
|
|
|
|
isc_boolean_t
|
|
dns_rdatatype_isknown(dns_rdatatype_t type) {
|
|
if ((dns_rdatatype_attributes(type) & DNS_RDATATYPEATTR_UNKNOWN)
|
|
== 0)
|
|
return (ISC_TRUE);
|
|
return (ISC_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");
|
|
}
|