2
0
mirror of https://gitlab.isc.org/isc-projects/bind9 synced 2025-08-22 10:10:06 +00:00
bind/lib/dns/name.c

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

2139 lines
48 KiB
C
Raw Normal View History

1998-12-12 20:48:14 +00:00
/*
2011-01-13 04:59:26 +00:00
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
1998-12-12 20:48:14 +00:00
*
* SPDX-License-Identifier: MPL-2.0
*
1998-12-12 20:48:14 +00:00
* 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/.
*
1998-12-12 20:48:14 +00:00
* See the COPYRIGHT file distributed with this work for additional
1998-12-04 02:27:01 +00:00
* information regarding copyright ownership.
1998-12-12 20:48:14 +00:00
*/
1998-12-12 19:25:20 +00:00
/*! \file */
2000-06-22 22:00:42 +00:00
1998-12-04 02:27:01 +00:00
#include <ctype.h>
#include <inttypes.h>
#include <stdbool.h>
2005-09-10 01:02:08 +00:00
#include <stdlib.h>
#include <unistd.h>
1998-12-04 02:27:01 +00:00
#include <isc/ascii.h>
#include <isc/attributes.h>
2000-04-04 20:10:29 +00:00
#include <isc/buffer.h>
#include <isc/hash.h>
#include <isc/hex.h>
#include <isc/mem.h>
#include <isc/once.h>
2015-12-09 19:07:20 +05:30
#include <isc/random.h>
#include <isc/result.h>
#include <isc/string.h>
#include <isc/thread.h>
2000-04-28 01:12:23 +00:00
#include <isc/util.h>
1998-12-04 02:27:01 +00:00
1999-01-06 20:04:41 +00:00
#include <dns/compress.h>
#include <dns/fixedname.h>
#include <dns/name.h>
1998-12-04 02:27:01 +00:00
typedef enum {
ft_init = 0,
ft_start,
ft_ordinary,
ft_initialescape,
ft_escape,
ft_escdecimal,
1999-01-27 23:39:40 +00:00
ft_at
} ft_state;
/*%
* Note that the name data must be a char array, not a string
* literal, to avoid compiler warnings about discarding
* the const attribute of a string.
*/
static unsigned char root_ndata[] = { "" };
static dns_name_t root = DNS_NAME_INITABSOLUTE(root_ndata);
const dns_name_t *dns_rootname = &root;
static unsigned char wild_ndata[] = { "\001*" };
1999-08-12 07:48:16 +00:00
static dns_name_t const wild = DNS_NAME_INITNONABSOLUTE(wild_ndata);
const dns_name_t *dns_wildcardname = &wild;
1999-08-12 07:48:16 +00:00
/*
* dns_name_t to text post-conversion procedure.
*/
Complete rewrite the BIND 9 build system The rewrite of BIND 9 build system is a large work and cannot be reasonable split into separate merge requests. Addition of the automake has a positive effect on the readability and maintainability of the build system as it is more declarative, it allows conditional and we are able to drop all of the custom make code that BIND 9 developed over the years to overcome the deficiencies of autoconf + custom Makefile.in files. This squashed commit contains following changes: - conversion (or rather fresh rewrite) of all Makefile.in files to Makefile.am by using automake - the libtool is now properly integrated with automake (the way we used it was rather hackish as the only official way how to use libtool is via automake - the dynamic module loading was rewritten from a custom patchwork to libtool's libltdl (which includes the patchwork to support module loading on different systems internally) - conversion of the unit test executor from kyua to automake parallel driver - conversion of the system test executor from custom make/shell to automake parallel driver - The GSSAPI has been refactored, the custom SPNEGO on the basis that all major KRB5/GSSAPI (mit-krb5, heimdal and Windows) implementations support SPNEGO mechanism. - The various defunct tests from bin/tests have been removed: bin/tests/optional and bin/tests/pkcs11 - The text files generated from the MD files have been removed, the MarkDown has been designed to be readable by both humans and computers - The xsl header is now generated by a simple sed command instead of perl helper - The <irs/platform.h> header has been removed - cleanups of configure.ac script to make it more simpler, addition of multiple macros (there's still work to be done though) - the tarball can now be prepared with `make dist` - the system tests are partially able to run in oot build Here's a list of unfinished work that needs to be completed in subsequent merge requests: - `make distcheck` doesn't yet work (because of system tests oot run is not yet finished) - documentation is not yet built, there's a different merge request with docbook to sphinx-build rst conversion that needs to be rebased and adapted on top of the automake - msvc build is non functional yet and we need to decide whether we will just cross-compile bind9 using mingw-w64 or fix the msvc build - contributed dlz modules are not included neither in the autoconf nor automake
2018-08-07 16:46:53 +02:00
static thread_local dns_name_totextfilter_t *totext_filter_proc = NULL;
bool
dns_name_isvalid(const dns_name_t *name) {
unsigned char *ndata;
unsigned int offset, count, length, nlabels;
if (!DNS_NAME_VALID(name)) {
return false;
}
ndata = name->ndata;
length = name->length;
offset = 0;
nlabels = 0;
while (offset != length) {
count = *ndata;
if (count > DNS_NAME_LABELLEN) {
return false;
}
nlabels++;
offset += count + 1;
ndata += count + 1;
if (offset > length) {
return false;
}
if (count == 0) {
break;
}
}
if (nlabels > DNS_NAME_MAXLABELS || offset != name->length) {
return false;
}
return true;
}
bool
dns_name_hasbuffer(const dns_name_t *name) {
/*
* Does 'name' have a dedicated buffer?
*/
REQUIRE(DNS_NAME_VALID(name));
if (name->buffer != NULL) {
return true;
}
return false;
}
bool
dns_name_isabsolute(const dns_name_t *name) {
1998-12-04 02:27:01 +00:00
/*
* Does 'name' end in the root label?
*/
REQUIRE(DNS_NAME_VALID(name));
1998-12-04 02:27:01 +00:00
return name->attributes.absolute;
1998-12-04 02:27:01 +00:00
}
#define hyphenchar(c) ((c) == 0x2d)
#define asterchar(c) ((c) == 0x2a)
#define alphachar(c) \
(((c) >= 0x41 && (c) <= 0x5a) || ((c) >= 0x61 && (c) <= 0x7a))
#define digitchar(c) ((c) >= 0x30 && (c) <= 0x39)
#define borderchar(c) (alphachar(c) || digitchar(c))
#define middlechar(c) (borderchar(c) || hyphenchar(c))
#define domainchar(c) ((c) > 0x20 && (c) < 0x7f)
bool
dns_name_ismailbox(const dns_name_t *name) {
unsigned char *ndata, ch;
unsigned int n;
bool first;
REQUIRE(DNS_NAME_VALID(name));
REQUIRE(name->length > 0);
REQUIRE(name->attributes.absolute);
2008-04-01 23:47:10 +00:00
/*
* Root label.
*/
if (name->length == 1) {
return true;
}
ndata = name->ndata;
n = *ndata++;
INSIST(n <= DNS_NAME_LABELLEN);
while (n--) {
ch = *ndata++;
if (!domainchar(ch)) {
return false;
}
}
2008-04-01 23:47:10 +00:00
if (ndata == name->ndata + name->length) {
return false;
}
/*
2021-05-04 17:21:52 +02:00
* RFC952/RFC1123 hostname.
*/
while (ndata < (name->ndata + name->length)) {
n = *ndata++;
INSIST(n <= DNS_NAME_LABELLEN);
first = true;
while (n--) {
ch = *ndata++;
if (first || n == 0) {
if (!borderchar(ch)) {
return false;
}
} else {
if (!middlechar(ch)) {
return false;
}
}
first = false;
}
}
return true;
}
bool
dns_name_ishostname(const dns_name_t *name, bool wildcard) {
unsigned char *ndata, ch;
unsigned int n;
bool first;
REQUIRE(DNS_NAME_VALID(name));
REQUIRE(name->length > 0);
REQUIRE(name->attributes.absolute);
2008-04-01 23:47:10 +00:00
/*
* Root label.
*/
if (name->length == 1) {
return true;
}
/*
* Skip wildcard if this is a ownername.
*/
ndata = name->ndata;
if (wildcard && ndata[0] == 1 && ndata[1] == '*') {
ndata += 2;
}
/*
2021-05-04 17:21:52 +02:00
* RFC952/RFC1123 hostname.
*/
while (ndata < (name->ndata + name->length)) {
n = *ndata++;
INSIST(n <= DNS_NAME_LABELLEN);
first = true;
while (n--) {
ch = *ndata++;
if (first || n == 0) {
if (!borderchar(ch)) {
return false;
}
} else {
if (!middlechar(ch)) {
return false;
}
}
first = false;
}
}
return true;
}
bool
dns_name_iswildcard(const dns_name_t *name) {
1999-08-12 01:29:03 +00:00
unsigned char *ndata;
/*
* Is 'name' a wildcard name?
*/
REQUIRE(DNS_NAME_VALID(name));
REQUIRE(name->length > 0);
1999-08-12 01:29:03 +00:00
if (name->length >= 2) {
ndata = name->ndata;
if (ndata[0] == 1 && ndata[1] == '*') {
return true;
}
1999-08-12 01:29:03 +00:00
}
return false;
1999-08-12 01:29:03 +00:00
}
bool
dns_name_internalwildcard(const dns_name_t *name) {
unsigned char *ndata;
unsigned int count;
unsigned int label;
/*
* Does 'name' contain a internal wildcard?
*/
REQUIRE(DNS_NAME_VALID(name));
/*
* Skip first label.
*/
ndata = name->ndata;
count = *ndata++;
INSIST(count <= DNS_NAME_LABELLEN);
ndata += count;
label = 1;
uint8_t labels = dns_name_countlabels(name);
while (label + 1 < labels) {
count = *ndata++;
INSIST(count <= DNS_NAME_LABELLEN);
if (count == 1 && *ndata == '*') {
return true;
}
ndata += count;
label++;
}
return false;
}
uint32_t
dns_name_hash(const dns_name_t *name) {
REQUIRE(DNS_NAME_VALID(name));
return isc_hash32(name->ndata, name->length, false);
}
dns_namereln_t
dns_name_fullcompare(const dns_name_t *name1, const dns_name_t *name2,
int *orderp, unsigned int *nlabelsp) {
unsigned int l1, l2, l, count1, count2, count, nlabels;
int cdiff, ldiff, diff;
1998-12-04 02:27:01 +00:00
unsigned char *label1, *label2;
dns_offsets_t offsets1, offsets2;
dns_namereln_t namereln = dns_namereln_none;
1998-12-04 02:27:01 +00:00
/*
* Determine the relative ordering under the DNSSEC order relation of
* 'name1' and 'name2', and also determine the hierarchical
* relationship of the names.
*
* Note: It makes no sense for one of the names to be relative and the
* other absolute. If both names are relative, then to be meaningfully
* compared the caller must ensure that they are both relative to the
* same domain.
1998-12-04 02:27:01 +00:00
*/
REQUIRE(DNS_NAME_VALID(name1));
REQUIRE(DNS_NAME_VALID(name2));
REQUIRE(orderp != NULL);
REQUIRE(nlabelsp != NULL);
/*
* Either name1 is absolute and name2 is absolute, or neither is.
*/
REQUIRE((name1->attributes.absolute) == (name2->attributes.absolute));
1998-12-04 02:27:01 +00:00
if (name1 == name2) {
*orderp = 0;
*nlabelsp = dns_name_countlabels(name1);
return dns_namereln_equal;
}
l1 = dns_name_offsets(name1, offsets1);
l2 = dns_name_offsets(name2, offsets2);
nlabels = 0;
if (l2 > l1) {
1998-12-04 02:27:01 +00:00
l = l1;
ldiff = 0 - (l2 - l1);
} else {
1998-12-04 02:27:01 +00:00
l = l2;
ldiff = l1 - l2;
}
1998-12-04 02:27:01 +00:00
while (l-- > 0) {
l1--;
l2--;
label1 = &name1->ndata[offsets1[l1]];
label2 = &name2->ndata[offsets2[l2]];
1998-12-04 02:27:01 +00:00
count1 = *label1++;
count2 = *label2++;
cdiff = (int)count1 - (int)count2;
if (cdiff < 0) {
count = count1;
} else {
count = count2;
}
diff = isc_ascii_lowercmp(label1, label2, count);
if (diff != 0) {
*orderp = diff;
goto done;
}
if (cdiff != 0) {
*orderp = cdiff;
goto done;
1998-12-04 02:27:01 +00:00
}
nlabels++;
1998-12-04 02:27:01 +00:00
}
*orderp = ldiff;
if (ldiff < 0) {
namereln = dns_namereln_contains;
} else if (ldiff > 0) {
namereln = dns_namereln_subdomain;
} else {
namereln = dns_namereln_equal;
}
2015-12-09 19:07:20 +05:30
*nlabelsp = nlabels;
return namereln;
done:
*nlabelsp = nlabels;
2015-12-09 19:07:20 +05:30
if (nlabels > 0) {
namereln = dns_namereln_commonancestor;
}
return namereln;
}
int
dns_name_compare(const dns_name_t *name1, const dns_name_t *name2) {
int order;
unsigned int nlabels;
/*
* Determine the relative ordering under the DNSSEC order relation of
* 'name1' and 'name2'.
*
* Note: It makes no sense for one of the names to be relative and the
* other absolute. If both names are relative, then to be meaningfully
* compared the caller must ensure that they are both relative to the
* same domain.
*/
(void)dns_name_fullcompare(name1, name2, &order, &nlabels);
return order;
1998-12-04 02:27:01 +00:00
}
bool
dns_name_equal(const dns_name_t *name1, const dns_name_t *name2) {
unsigned int length;
1999-05-18 22:05:40 +00:00
/*
* Are 'name1' and 'name2' equal?
*
* Note: It makes no sense for one of the names to be relative and the
* other absolute. If both names are relative, then to be meaningfully
* compared the caller must ensure that they are both relative to the
* same domain.
*/
REQUIRE(DNS_NAME_VALID(name1));
REQUIRE(DNS_NAME_VALID(name2));
1999-05-18 22:05:40 +00:00
/*
* Either name1 is absolute and name2 is absolute, or neither is.
*/
REQUIRE((name1->attributes.absolute) == (name2->attributes.absolute));
1999-05-18 22:05:40 +00:00
if (name1 == name2) {
return true;
}
length = name1->length;
if (length != name2->length) {
return false;
}
1999-05-18 22:05:40 +00:00
/* label lengths are < 64 so tolower() does not affect them */
return isc_ascii_lowerequal(name1->ndata, name2->ndata, length);
1999-05-18 22:05:40 +00:00
}
bool
dns_name_caseequal(const dns_name_t *name1, const dns_name_t *name2) {
/*
* Are 'name1' and 'name2' equal?
*
* Note: It makes no sense for one of the names to be relative and the
* other absolute. If both names are relative, then to be meaningfully
* compared the caller must ensure that they are both relative to the
* same domain.
*/
REQUIRE(DNS_NAME_VALID(name1));
REQUIRE(DNS_NAME_VALID(name2));
/*
* Either name1 is absolute and name2 is absolute, or neither is.
*/
REQUIRE((name1->attributes.absolute) == (name2->attributes.absolute));
if (name1->length != name2->length) {
return false;
}
if (memcmp(name1->ndata, name2->ndata, name1->length) != 0) {
return false;
}
return true;
}
int
dns_name_rdatacompare(const dns_name_t *name1, const dns_name_t *name2) {
/*
* Compare two absolute names as rdata.
*/
REQUIRE(DNS_NAME_VALID(name1));
REQUIRE(name1->length > 0);
REQUIRE(name1->attributes.absolute);
REQUIRE(DNS_NAME_VALID(name2));
REQUIRE(name2->length > 0);
REQUIRE(name2->attributes.absolute);
/* label lengths are < 64 so tolower() does not affect them */
return isc_ascii_lowercmp(name1->ndata, name2->ndata,
ISC_MIN(name1->length, name2->length));
}
bool
dns_name_issubdomain(const dns_name_t *name1, const dns_name_t *name2) {
int order;
unsigned int nlabels;
dns_namereln_t namereln;
1998-12-04 02:27:01 +00:00
/*
* Is 'name1' a subdomain of 'name2'?
*
* Note: It makes no sense for one of the names to be relative and the
* other absolute. If both names are relative, then to be meaningfully
* compared the caller must ensure that they are both relative to the
* same domain.
*/
namereln = dns_name_fullcompare(name1, name2, &order, &nlabels);
if (namereln == dns_namereln_subdomain ||
2022-11-02 19:33:14 +01:00
namereln == dns_namereln_equal)
{
return true;
}
1998-12-04 02:27:01 +00:00
return false;
1998-12-04 02:27:01 +00:00
}
bool
dns_name_matcheswildcard(const dns_name_t *name, const dns_name_t *wname) {
int order;
unsigned int nlabels, labels;
2000-02-02 20:11:55 +00:00
dns_name_t tname;
REQUIRE(DNS_NAME_VALID(name));
REQUIRE(name->length > 0);
REQUIRE(DNS_NAME_VALID(wname));
labels = dns_name_countlabels(wname);
REQUIRE(labels > 0);
2000-02-02 20:11:55 +00:00
REQUIRE(dns_name_iswildcard(wname));
dns_name_init(&tname);
2000-02-02 20:11:55 +00:00
dns_name_getlabelsequence(wname, 1, labels - 1, &tname);
if (dns_name_fullcompare(name, &tname, &order, &nlabels) ==
dns_namereln_subdomain)
{
return true;
}
return false;
2000-02-02 20:11:55 +00:00
}
1998-12-04 02:27:01 +00:00
void
dns_name_getlabel(const dns_name_t *name, unsigned int n, dns_label_t *label) {
dns_offsets_t offsets;
1998-12-04 02:27:01 +00:00
/*
* Make 'label' refer to the 'n'th least significant label of 'name'.
*/
REQUIRE(DNS_NAME_VALID(name));
1998-12-04 02:27:01 +00:00
REQUIRE(label != NULL);
uint8_t labels = dns_name_offsets(name, offsets);
REQUIRE(labels > 0);
REQUIRE(n < labels);
label->base = &name->ndata[offsets[n]];
if (n == (unsigned int)labels - 1) {
label->length = name->length - offsets[n];
1998-12-04 02:27:01 +00:00
} else {
label->length = offsets[n + 1] - offsets[n];
}
1998-12-04 02:27:01 +00:00
}
void
dns_name_getlabelsequence(const dns_name_t *source, unsigned int first,
1998-12-13 23:45:21 +00:00
unsigned int n, dns_name_t *target) {
unsigned char *p, l;
unsigned int firstoffset, endoffset;
unsigned int i;
1998-12-04 02:27:01 +00:00
/*
* Make 'target' refer to the 'n' labels including and following
* 'first' in 'source'.
*/
REQUIRE(DNS_NAME_VALID(source));
REQUIRE(DNS_NAME_VALID(target));
REQUIRE(DNS_NAME_BINDABLE(target));
1998-12-04 02:27:01 +00:00
uint8_t labels = dns_name_countlabels(source);
REQUIRE(first <= labels && n <= labels - first);
p = source->ndata;
if (first == labels) {
firstoffset = source->length;
} else {
for (i = 0; i < first; i++) {
l = *p;
p += l + 1;
}
firstoffset = (unsigned int)(p - source->ndata);
}
if (first + n == labels) {
endoffset = source->length;
} else {
for (i = 0; i < n; i++) {
l = *p;
p += l + 1;
}
endoffset = (unsigned int)(p - source->ndata);
}
target->ndata = &source->ndata[firstoffset];
target->length = endoffset - firstoffset;
2008-04-01 23:47:10 +00:00
if (first + n == labels && n > 0 && source->attributes.absolute) {
target->attributes.absolute = true;
} else {
target->attributes.absolute = false;
}
1998-12-04 02:27:01 +00:00
}
1999-04-24 02:03:07 +00:00
void
dns_name_clone(const dns_name_t *source, dns_name_t *target) {
1999-04-24 02:03:07 +00:00
/*
* Make 'target' refer to the same name as 'source'.
*/
REQUIRE(DNS_NAME_VALID(source));
REQUIRE(DNS_NAME_VALID(target));
REQUIRE(DNS_NAME_BINDABLE(target));
1999-04-24 02:03:07 +00:00
target->ndata = source->ndata;
target->length = source->length;
target->attributes = source->attributes;
target->attributes.readonly = false;
target->attributes.dynamic = false;
1999-04-24 02:03:07 +00:00
}
1998-12-04 02:27:01 +00:00
void
dns_name_fromregion(dns_name_t *name, const isc_region_t *r) {
size_t length;
isc_region_t r2 = { .base = NULL, .length = 0 };
1998-12-04 02:27:01 +00:00
/*
* Make 'name' refer to region 'r'.
*/
REQUIRE(DNS_NAME_VALID(name));
1998-12-04 02:27:01 +00:00
REQUIRE(r != NULL);
REQUIRE(DNS_NAME_BINDABLE(name));
1998-12-04 02:27:01 +00:00
name->ndata = r->base;
if (name->buffer != NULL) {
isc_buffer_clear(name->buffer);
isc_buffer_availableregion(name->buffer, &r2);
length = (r->length < r2.length) ? r->length : r2.length;
if (length > DNS_NAME_MAXWIRE) {
length = DNS_NAME_MAXWIRE;
}
} else {
length = (r->length <= DNS_NAME_MAXWIRE) ? r->length
: DNS_NAME_MAXWIRE;
}
1998-12-04 02:27:01 +00:00
name->attributes.absolute = false;
if (length > 0) {
size_t offset = 0;
uint8_t nlabels = 0;
while (offset != length) {
uint8_t count;
INSIST(nlabels < DNS_NAME_MAXLABELS);
nlabels++;
count = name->ndata[offset];
INSIST(count <= DNS_NAME_LABELLEN);
offset += count + 1;
INSIST(offset <= length);
if (count == 0) {
name->attributes.absolute = true;
break;
}
}
name->length = offset;
}
if (name->buffer != NULL) {
/*
* name->length has been updated by set_offsets to the actual
* length of the name data so we can now copy the actual name
* data and not anything after it.
*/
if (name->length > 0) {
memmove(r2.base, r->base, name->length);
}
name->ndata = r2.base;
isc_buffer_add(name->buffer, name->length);
}
1998-12-04 02:27:01 +00:00
}
static isc_result_t
convert_text(isc_buffer_t *source, const dns_name_t *origin,
unsigned int options, dns_name_t *name, isc_buffer_t *target) {
unsigned char *ndata = NULL, *label = NULL;
char *tdata = NULL;
1998-12-04 02:27:01 +00:00
char c;
ft_state state;
unsigned int value = 0, count = 0;
unsigned int n1 = 0, n2 = 0;
unsigned int tlen, nrem, nused, digits = 0, labels, tused;
bool done;
bool downcase;
1998-12-04 02:27:01 +00:00
REQUIRE(DNS_NAME_VALID(name));
REQUIRE(ISC_BUFFER_VALID(source));
REQUIRE(ISC_BUFFER_VALID(target));
2008-04-01 23:47:10 +00:00
downcase = ((options & DNS_NAME_DOWNCASE) != 0);
if (target == NULL && name->buffer != NULL) {
target = name->buffer;
isc_buffer_clear(target);
}
REQUIRE(DNS_NAME_BINDABLE(name));
1998-12-04 02:27:01 +00:00
/*
* Set up the state machine.
*/
tdata = (char *)source->base + source->current;
tlen = isc_buffer_remaininglength(source);
tused = 0;
ndata = isc_buffer_used(target);
nrem = isc_buffer_availablelength(target);
if (nrem > DNS_NAME_MAXWIRE) {
nrem = DNS_NAME_MAXWIRE;
}
nused = 0;
1998-12-04 02:27:01 +00:00
labels = 0;
done = false;
state = ft_init;
1998-12-04 02:27:01 +00:00
while (nrem > 0 && tlen > 0 && !done) {
c = *tdata++;
tlen--;
tused++;
1998-12-04 02:27:01 +00:00
switch (state) {
case ft_init:
1998-12-04 02:27:01 +00:00
/*
* Is this the root name?
*/
if (c == '.') {
if (tlen != 0) {
return DNS_R_EMPTYLABEL;
}
1998-12-04 02:27:01 +00:00
labels++;
*ndata++ = 0;
nrem--;
nused++;
done = true;
1998-12-04 02:27:01 +00:00
break;
}
if (c == '@' && tlen == 0) {
state = ft_at;
break;
1999-01-27 23:39:40 +00:00
}
FALLTHROUGH;
case ft_start:
1998-12-04 02:27:01 +00:00
label = ndata;
ndata++;
nrem--;
nused++;
1998-12-04 02:27:01 +00:00
count = 0;
if (c == '\\') {
state = ft_initialescape;
1998-12-04 02:27:01 +00:00
break;
}
state = ft_ordinary;
if (nrem == 0) {
return ISC_R_NOSPACE;
}
FALLTHROUGH;
case ft_ordinary:
1998-12-04 02:27:01 +00:00
if (c == '.') {
if (count == 0) {
return DNS_R_EMPTYLABEL;
}
1998-12-04 02:27:01 +00:00
*label = count;
labels++;
INSIST(labels < DNS_NAME_MAXLABELS);
1998-12-04 02:27:01 +00:00
if (tlen == 0) {
labels++;
*ndata++ = 0;
nrem--;
nused++;
done = true;
1998-12-04 02:27:01 +00:00
}
state = ft_start;
1998-12-04 02:27:01 +00:00
} else if (c == '\\') {
state = ft_escape;
1998-12-04 02:27:01 +00:00
} else {
if (count >= DNS_NAME_LABELLEN) {
1998-12-04 02:27:01 +00:00
return DNS_R_LABELTOOLONG;
}
1998-12-04 02:27:01 +00:00
count++;
if (downcase) {
c = isc_ascii_tolower(c);
}
1998-12-04 02:27:01 +00:00
*ndata++ = c;
nrem--;
nused++;
1998-12-04 02:27:01 +00:00
}
break;
case ft_initialescape:
1998-12-04 02:27:01 +00:00
if (c == '[') {
/*
* This looks like a bitstring label, which
* was deprecated. Intentionally drop it.
*/
return DNS_R_BADLABELTYPE;
1998-12-04 02:27:01 +00:00
}
state = ft_escape;
POST(state);
FALLTHROUGH;
case ft_escape:
if (!isdigit((unsigned char)c)) {
if (count >= DNS_NAME_LABELLEN) {
1998-12-04 02:27:01 +00:00
return DNS_R_LABELTOOLONG;
}
1998-12-04 02:27:01 +00:00
count++;
if (downcase) {
c = isc_ascii_tolower(c);
}
1998-12-04 02:27:01 +00:00
*ndata++ = c;
nrem--;
nused++;
state = ft_ordinary;
1998-12-04 02:27:01 +00:00
break;
}
digits = 0;
value = 0;
state = ft_escdecimal;
FALLTHROUGH;
case ft_escdecimal:
if (!isdigit((unsigned char)c)) {
1998-12-04 02:27:01 +00:00
return DNS_R_BADESCAPE;
}
value = 10 * value + c - '0';
1998-12-04 02:27:01 +00:00
digits++;
if (digits == 3) {
if (value > 255) {
return DNS_R_BADESCAPE;
}
if (count >= DNS_NAME_LABELLEN) {
1998-12-04 02:27:01 +00:00
return DNS_R_LABELTOOLONG;
}
1998-12-04 02:27:01 +00:00
count++;
if (downcase) {
value = isc_ascii_tolower(value);
}
1998-12-04 02:27:01 +00:00
*ndata++ = value;
nrem--;
nused++;
state = ft_ordinary;
1998-12-04 02:27:01 +00:00
}
break;
default:
FATAL_ERROR("Unexpected state %d", state);
/* Does not return. */
1998-12-04 02:27:01 +00:00
}
}
1998-12-04 02:27:01 +00:00
if (!done) {
if (nrem == 0) {
return ISC_R_NOSPACE;
}
INSIST(tlen == 0);
if (state != ft_ordinary && state != ft_at) {
return ISC_R_UNEXPECTEDEND;
}
if (state == ft_ordinary) {
INSIST(count != 0);
INSIST(label != NULL);
1998-12-04 02:27:01 +00:00
*label = count;
labels++;
INSIST(labels < DNS_NAME_MAXLABELS);
1998-12-04 02:27:01 +00:00
}
if (origin != NULL) {
1998-12-04 02:27:01 +00:00
if (nrem < origin->length) {
return ISC_R_NOSPACE;
}
1998-12-04 02:27:01 +00:00
label = origin->ndata;
n1 = origin->length;
nrem -= n1;
POST(nrem);
1998-12-04 02:27:01 +00:00
while (n1 > 0) {
n2 = *label++;
INSIST(n2 <= DNS_NAME_LABELLEN);
*ndata++ = n2;
n1 -= n2 + 1;
nused += n2 + 1;
while (n2 > 0) {
c = *label++;
if (downcase) {
c = isc_ascii_tolower(c);
}
*ndata++ = c;
n2--;
}
labels++;
if (n1 > 0) {
INSIST(labels < DNS_NAME_MAXLABELS);
}
1998-12-04 02:27:01 +00:00
}
if (origin->attributes.absolute) {
name->attributes.absolute = true;
}
1998-12-04 02:27:01 +00:00
}
} else {
name->attributes.absolute = true;
}
1998-12-04 02:27:01 +00:00
name->ndata = (unsigned char *)target->base + target->used;
name->length = nused;
1998-12-04 02:27:01 +00:00
isc_buffer_forward(source, tused);
isc_buffer_add(target, name->length);
return ISC_R_SUCCESS;
1998-12-04 02:27:01 +00:00
}
isc_result_t
dns_name_wirefromtext(isc_buffer_t *source, const dns_name_t *origin,
unsigned int options, isc_buffer_t *target) {
dns_name_t name;
REQUIRE(ISC_BUFFER_VALID(target));
dns_name_init(&name);
return convert_text(source, origin, options, &name, target);
}
isc_result_t
dns_name_fromtext(dns_name_t *name, isc_buffer_t *source,
const dns_name_t *origin, unsigned int options) {
REQUIRE(DNS_NAME_VALID(name));
REQUIRE(ISC_BUFFER_VALID(name->buffer));
isc_buffer_clear(name->buffer);
return convert_text(source, origin, options, name, name->buffer);
}
isc_result_t
dns_name_totext(const dns_name_t *name, unsigned int options,
isc_buffer_t *target) {
1998-12-04 02:27:01 +00:00
unsigned char *ndata;
char *tdata;
unsigned int nlen, tlen;
unsigned char c;
unsigned int trem, count;
unsigned int labels;
bool saw_root = false;
unsigned int oused;
bool omit_final_dot = ((options & DNS_NAME_OMITFINALDOT) != 0);
1998-12-04 02:27:01 +00:00
/*
* This function assumes the name is in proper uncompressed
* wire format.
*/
REQUIRE(DNS_NAME_VALID(name));
REQUIRE(ISC_BUFFER_VALID(target));
1998-12-04 02:27:01 +00:00
oused = target->used;
1998-12-04 02:27:01 +00:00
ndata = name->ndata;
nlen = name->length;
labels = dns_name_countlabels(name);
tdata = isc_buffer_used(target);
tlen = isc_buffer_availablelength(target);
1998-12-04 02:27:01 +00:00
trem = tlen;
if (labels == 0 && nlen == 0) {
/*
* Special handling for an empty name.
*/
if (trem == 0) {
return ISC_R_NOSPACE;
}
/*
* The names of these booleans are misleading in this case.
* This empty name is not necessarily from the root node of
* the DNS root zone, nor is a final dot going to be included.
* They need to be set this way, though, to keep the "@"
* from being trounced.
*/
saw_root = true;
omit_final_dot = false;
*tdata++ = '@';
trem--;
/*
* Skip the while() loop.
*/
1998-12-04 02:27:01 +00:00
nlen = 0;
} else if (nlen == 1 && labels == 1 && *ndata == '\0') {
/*
* Special handling for the root label.
*/
1998-12-04 02:27:01 +00:00
if (trem == 0) {
return ISC_R_NOSPACE;
}
saw_root = true;
omit_final_dot = false;
1998-12-04 02:27:01 +00:00
*tdata++ = '.';
trem--;
/*
* Skip the while() loop.
*/
nlen = 0;
1998-12-04 02:27:01 +00:00
}
1998-12-04 02:27:01 +00:00
while (labels > 0 && nlen > 0 && trem > 0) {
labels--;
count = *ndata++;
nlen--;
if (count == 0) {
saw_root = true;
1998-12-04 02:27:01 +00:00
break;
}
if (count <= DNS_NAME_LABELLEN) {
1998-12-04 02:27:01 +00:00
INSIST(nlen >= count);
while (count > 0) {
c = *ndata;
switch (c) {
/* Special modifiers in zone files. */
case 0x40: /* '@' */
case 0x24: /* '$' */
if ((options & DNS_NAME_PRINCIPAL) != 0)
2022-11-02 19:33:14 +01:00
{
goto no_escape;
}
FALLTHROUGH;
1998-12-04 02:27:01 +00:00
case 0x22: /* '"' */
case 0x28: /* '(' */
case 0x29: /* ')' */
1998-12-04 02:27:01 +00:00
case 0x2E: /* '.' */
case 0x3B: /* ';' */
case 0x5C: /* '\\' */
if (trem < 2) {
return ISC_R_NOSPACE;
}
1998-12-04 02:27:01 +00:00
*tdata++ = '\\';
*tdata++ = c;
ndata++;
trem -= 2;
nlen--;
break;
no_escape:
1998-12-04 02:27:01 +00:00
default:
if (c > 0x20 && c < 0x7f) {
if (trem == 0) {
return ISC_R_NOSPACE;
}
1998-12-04 02:27:01 +00:00
*tdata++ = c;
ndata++;
trem--;
nlen--;
} else {
if (trem < 4) {
return ISC_R_NOSPACE;
}
*tdata++ = 0x5c;
*tdata++ = 0x30 +
((c / 100) % 10);
*tdata++ = 0x30 +
((c / 10) % 10);
*tdata++ = 0x30 + (c % 10);
1998-12-04 02:27:01 +00:00
trem -= 4;
ndata++;
nlen--;
}
}
count--;
}
} else {
FATAL_ERROR("Unexpected label type %02x", count);
UNREACHABLE();
}
1998-12-04 02:27:01 +00:00
/*
* The following assumes names are absolute. If not, we
* fix things up later. Note that this means that in some
* cases one more byte of text buffer is required than is
* needed in the final output.
*/
if (trem == 0) {
return ISC_R_NOSPACE;
}
1998-12-04 02:27:01 +00:00
*tdata++ = '.';
trem--;
}
if (nlen != 0 && trem == 0) {
return ISC_R_NOSPACE;
}
if (!saw_root || omit_final_dot) {
1998-12-04 02:27:01 +00:00
trem++;
tdata--;
}
if (trem > 0) {
*tdata = 0;
}
isc_buffer_add(target, tlen - trem);
1998-12-04 02:27:01 +00:00
if (totext_filter_proc != NULL) {
return (totext_filter_proc)(target, oused);
}
return ISC_R_SUCCESS;
1998-12-04 02:27:01 +00:00
}
isc_result_t
dns_name_tofilenametext(const dns_name_t *name, bool omit_final_dot,
isc_buffer_t *target) {
unsigned char *ndata;
char *tdata;
unsigned int nlen, tlen;
unsigned char c;
unsigned int trem, count;
unsigned int labels;
/*
* This function assumes the name is in proper uncompressed
* wire format.
*/
REQUIRE(DNS_NAME_VALID(name));
REQUIRE(name->attributes.absolute);
REQUIRE(ISC_BUFFER_VALID(target));
ndata = name->ndata;
nlen = name->length;
labels = dns_name_countlabels(name);
tdata = isc_buffer_used(target);
tlen = isc_buffer_availablelength(target);
trem = tlen;
if (nlen == 1 && labels == 1 && *ndata == '\0') {
/*
* Special handling for the root label.
*/
if (trem == 0) {
return ISC_R_NOSPACE;
}
omit_final_dot = false;
*tdata++ = '.';
trem--;
/*
* Skip the while() loop.
*/
nlen = 0;
}
while (labels > 0 && nlen > 0 && trem > 0) {
labels--;
count = *ndata++;
nlen--;
if (count == 0) {
break;
}
if (count <= DNS_NAME_LABELLEN) {
INSIST(nlen >= count);
while (count > 0) {
c = *ndata;
if ((c >= 0x30 && c <= 0x39) || /* digit */
(c >= 0x41 && c <= 0x5A) || /* uppercase */
(c >= 0x61 && c <= 0x7A) || /* lowercase */
c == 0x2D || /* hyphen */
c == 0x5F) /* underscore */
{
if (trem == 0) {
return ISC_R_NOSPACE;
}
/* downcase */
if (c >= 0x41 && c <= 0x5A) {
c += 0x20;
}
*tdata++ = c;
ndata++;
trem--;
nlen--;
} else {
2017-09-13 21:18:26 -07:00
if (trem < 4) {
return ISC_R_NOSPACE;
}
snprintf(tdata, trem, "%%%02X", c);
tdata += 3;
trem -= 3;
ndata++;
nlen--;
}
count--;
}
} else {
FATAL_ERROR("Unexpected label type %02x", count);
UNREACHABLE();
}
/*
* The following assumes names are absolute. If not, we
* fix things up later. Note that this means that in some
* cases one more byte of text buffer is required than is
* needed in the final output.
*/
if (trem == 0) {
return ISC_R_NOSPACE;
}
*tdata++ = '.';
trem--;
}
if (nlen != 0 && trem == 0) {
return ISC_R_NOSPACE;
}
if (omit_final_dot) {
trem++;
}
isc_buffer_add(target, tlen - trem);
return ISC_R_SUCCESS;
}
isc_result_t
dns_name_downcase(const dns_name_t *source, dns_name_t *name) {
/*
* Downcase 'source'.
*/
REQUIRE(DNS_NAME_VALID(source));
REQUIRE(DNS_NAME_VALID(name));
if (source == name) {
REQUIRE(!name->attributes.readonly);
isc_ascii_lowercopy(name->ndata, source->ndata, source->length);
return ISC_R_SUCCESS;
}
1999-08-20 17:01:06 +00:00
REQUIRE(DNS_NAME_BINDABLE(name));
REQUIRE(ISC_BUFFER_VALID(name->buffer));
isc_buffer_clear(name->buffer);
name->ndata = (uint8_t *)name->buffer->base + name->buffer->used;
/* label lengths are < 64 so tolower() does not affect them */
isc_ascii_lowercopy(name->ndata, source->ndata, source->length);
name->length = source->length;
name->attributes = (struct dns_name_attrs){
.absolute = source->attributes.absolute
};
isc_buffer_add(name->buffer, name->length);
return ISC_R_SUCCESS;
1999-08-20 17:01:06 +00:00
}
isc_result_t
2022-11-07 14:00:45 +00:00
dns_name_fromwire(dns_name_t *const name, isc_buffer_t *const source,
const dns_decompress_t dctx, isc_buffer_t *target) {
/*
2022-11-07 14:00:45 +00:00
* Copy the name at source into target, decompressing it.
*
* *** WARNING ***
*
* dns_name_fromwire() deals with raw network data. An error in this
* routine could result in the failure or hijacking of the server.
*
* The description of name compression in RFC 1035 section 4.1.4 is
* subtle wrt certain edge cases. The first important sentence is:
*
* > In this scheme, an entire domain name or a list of labels at the
* > end of a domain name is replaced with a pointer to a prior
* > occurance of the same name.
*
* The key word is "prior". This says that compression pointers must
* point strictly earlier in the message (before our "marker" variable),
* which is enough to prevent DoS attacks due to compression loops.
*
* The next important sentence is:
*
* > If a domain name is contained in a part of the message subject to a
* > length field (such as the RDATA section of an RR), and compression
* > is used, the length of the compressed name is used in the length
* > calculation, rather than the length of the expanded name.
*
* When decompressing, this means that the amount of the source buffer
* that we consumed (which is checked wrt the container's length field)
* is the length of the compressed name. A compressed name is defined as
* a sequence of labels ending with the root label or a compression
* pointer, that is, the segment of the name that dns_name_fromwire()
* examines first.
*
* This matters when handling names that play dirty tricks, like:
*
* +---+---+---+---+---+---+
* | 4 | 1 |'a'|192| 0 | 0 |
* +---+---+---+---+---+---+
*
* We start at octet 1. There is an ordinary single character label "a",
* followed by a compression pointer that refers back to octet zero.
* Here there is a label of length 4, which weirdly re-uses the octets
* we already examined as the data for the label. It is followed by the
* root label,
*
* The specification says that the compressed name ends after the first
* zero octet (after the compression pointer) not the second zero octet,
* even though the second octet is later in the message. This shows the
* correct way to set our "consumed" variable.
*/
REQUIRE(DNS_NAME_VALID(name));
REQUIRE(DNS_NAME_BINDABLE(name));
REQUIRE((target != NULL && ISC_BUFFER_VALID(target)) ||
(target == NULL && ISC_BUFFER_VALID(name->buffer)));
if (target == NULL && name->buffer != NULL) {
target = name->buffer;
isc_buffer_clear(target);
}
2022-11-07 14:00:45 +00:00
uint8_t *const name_buf = isc_buffer_used(target);
const uint32_t name_max = ISC_MIN(DNS_NAME_MAXWIRE,
isc_buffer_availablelength(target));
uint32_t name_len = 0;
/*
2022-11-07 14:00:45 +00:00
* After chasing a compression pointer, these variables refer to the
* source buffer as follows:
*
* sb --- mr --- cr --- st --- cd --- sm
*
* sb = source_buf (const)
* mr = marker
* cr = cursor
* st = start (const)
* cd = consumed
* sm = source_max (const)
*
* The marker hops backwards for each pointer.
* The cursor steps forwards for each label.
* The amount of the source we consumed is set once.
*/
2022-11-07 14:00:45 +00:00
const uint8_t *const source_buf = isc_buffer_base(source);
const uint8_t *const source_max = isc_buffer_used(source);
const uint8_t *const start = isc_buffer_current(source);
const uint8_t *marker = start;
const uint8_t *cursor = start;
const uint8_t *consumed = NULL;
/*
2022-11-07 14:00:45 +00:00
* One iteration per label.
*/
2022-11-07 14:00:45 +00:00
while (cursor < source_max) {
const uint8_t label_len = *cursor++;
if (label_len <= DNS_NAME_LABELLEN) {
2022-11-07 14:00:45 +00:00
/*
* Normal label: record its offset, and check bounds on
* the name length, which also ensures we don't overrun
* the offsets array. Don't touch any source bytes yet!
* The source bounds check will happen when we loop.
*/
/* and then a step to the ri-i-i-i-i-ight */
cursor += label_len;
name_len += label_len + 1;
if (name_len > name_max) {
return name_max == DNS_NAME_MAXWIRE
? DNS_R_NAMETOOLONG
: ISC_R_NOSPACE;
} else if (label_len == 0) {
goto root_label;
}
2022-11-07 14:00:45 +00:00
} else if (label_len < 192) {
return DNS_R_BADLABELTYPE;
} else if (!dns_decompress_getpermitted(dctx)) {
return DNS_R_DISALLOWED;
} else if (cursor < source_max) {
/*
* Compression pointer. Ensure it does not loop.
*
* Copy multiple labels in one go, to make the most of
* memmove() performance. Start at the marker and finish
* just before the pointer's hi+lo bytes, before the
* cursor. Bounds were already checked.
*/
const uint32_t hi = label_len & 0x3F;
const uint32_t lo = *cursor++;
const uint8_t *pointer = source_buf + (256 * hi + lo);
if (pointer >= marker) {
1999-02-24 06:31:35 +00:00
return DNS_R_BADPOINTER;
}
2022-11-07 14:00:45 +00:00
const uint32_t copy_len = (cursor - 2) - marker;
uint8_t *const dest = name_buf + name_len - copy_len;
memmove(dest, marker, copy_len);
consumed = consumed != NULL ? consumed : cursor;
/* it's just a jump to the left */
cursor = marker = pointer;
}
}
2022-11-07 14:00:45 +00:00
return ISC_R_UNEXPECTEDEND;
root_label:;
/*
* Copy labels almost like we do for compression pointers,
* from the marker up to and including the root label.
*/
const uint32_t copy_len = cursor - marker;
memmove(name_buf + name_len - copy_len, marker, copy_len);
consumed = consumed != NULL ? consumed : cursor;
isc_buffer_forward(source, consumed - start);
name->attributes.absolute = true;
2022-11-07 14:00:45 +00:00
name->ndata = name_buf;
name->length = name_len;
isc_buffer_add(target, name_len);
return ISC_R_SUCCESS;
}
isc_result_t
dns_name_towire(const dns_name_t *name, dns_compress_t *cctx,
isc_buffer_t *target) {
bool compress, multi;
unsigned int here;
unsigned int prefix_length;
unsigned int suffix_coff;
1999-02-22 07:24:05 +00:00
/*
* Convert 'name' into wire format, compressing it as specified by the
* compression context 'cctx', and storing the result in 'target'.
*/
REQUIRE(DNS_NAME_VALID(name));
REQUIRE(cctx != NULL);
REQUIRE(ISC_BUFFER_VALID(target));
compress = !name->attributes.nocompress &&
dns_compress_getpermitted(cctx);
multi = compress && dns_compress_getmultiuse(cctx);
/*
* Write a compression pointer directly if the caller passed us
* a pointer to this name's offset that we saved previously.
*/
if (multi && cctx->coff < 0x4000) {
if (isc_buffer_availablelength(target) < 2) {
return ISC_R_NOSPACE;
}
isc_buffer_putuint16(target, cctx->coff | 0xc000);
return ISC_R_SUCCESS;
}
/*
* Always add the name to the compression context; if compression
* is off, reset the return values before writing the name.
*/
prefix_length = name->length;
suffix_coff = 0;
dns_compress_name(cctx, target, name, &prefix_length, &suffix_coff);
if (!compress) {
prefix_length = name->length;
suffix_coff = 0;
}
/*
* Return this name's compression offset for use next time, provided
* it isn't too short for compression to help (i.e. it's the root)
*/
here = isc_buffer_usedlength(target);
if (multi && here < 0x4000 && prefix_length > 1) {
cctx->coff = (uint16_t)here;
}
if (prefix_length > 0) {
if (isc_buffer_availablelength(target) < prefix_length) {
return ISC_R_NOSPACE;
}
memmove(isc_buffer_used(target), name->ndata, prefix_length);
isc_buffer_add(target, prefix_length);
}
if (suffix_coff > 0) {
if (multi && prefix_length == 0) {
cctx->coff = suffix_coff;
}
if (isc_buffer_availablelength(target) < 2) {
return ISC_R_NOSPACE;
}
isc_buffer_putuint16(target, suffix_coff | 0xc000);
1999-02-22 07:24:05 +00:00
}
return ISC_R_SUCCESS;
1999-02-22 07:24:05 +00:00
}
isc_result_t
dns_name_concatenate(const dns_name_t *prefix, const dns_name_t *suffix,
dns_name_t *name) {
unsigned char *ndata = NULL;
unsigned int nrem, prefix_length, length;
bool copy_prefix = true;
bool copy_suffix = true;
bool absolute = false;
1999-03-11 00:44:17 +00:00
dns_name_t tmp_name;
isc_buffer_t *target = NULL;
/*
1999-03-11 00:44:17 +00:00
* Concatenate 'prefix' and 'suffix'.
*/
1999-02-22 07:24:05 +00:00
REQUIRE(prefix == NULL || DNS_NAME_VALID(prefix));
REQUIRE(suffix == NULL || DNS_NAME_VALID(suffix));
REQUIRE(DNS_NAME_VALID(name) && ISC_BUFFER_VALID(name->buffer));
REQUIRE(DNS_NAME_BINDABLE(name));
if (prefix == NULL || prefix->length == 0) {
copy_prefix = false;
}
if (suffix == NULL || suffix->length == 0) {
copy_suffix = false;
}
if (copy_prefix && prefix->attributes.absolute) {
absolute = true;
1999-03-11 00:44:17 +00:00
REQUIRE(!copy_suffix);
}
if (name == NULL) {
dns_name_init(&tmp_name);
1999-03-11 00:44:17 +00:00
name = &tmp_name;
}
target = name->buffer;
isc_buffer_clear(target);
1999-02-22 07:24:05 +00:00
/*
* Set up.
*/
1999-02-22 07:24:05 +00:00
nrem = target->length - target->used;
ndata = (unsigned char *)target->base + target->used;
if (nrem > DNS_NAME_MAXWIRE) {
nrem = DNS_NAME_MAXWIRE;
}
1999-03-11 00:44:17 +00:00
length = 0;
prefix_length = 0;
if (copy_prefix) {
prefix_length = prefix->length;
length += prefix_length;
}
if (copy_suffix) {
length += suffix->length;
}
if (length > DNS_NAME_MAXWIRE) {
return DNS_R_NAMETOOLONG;
}
1999-03-11 00:44:17 +00:00
if (length > nrem) {
return ISC_R_NOSPACE;
1999-03-11 00:44:17 +00:00
}
if (copy_suffix) {
if (suffix->attributes.absolute) {
absolute = true;
}
memmove(ndata + prefix_length, suffix->ndata, suffix->length);
1999-03-11 00:44:17 +00:00
}
1999-02-22 07:24:05 +00:00
/*
* If 'prefix' and 'name' are the same object, we don't have to
1999-03-11 00:44:17 +00:00
* copy anything.
*/
1999-03-11 00:44:17 +00:00
if (copy_prefix && (prefix != name || prefix->buffer != target)) {
memmove(ndata, prefix->ndata, prefix_length);
}
1999-03-11 00:44:17 +00:00
name->ndata = ndata;
name->length = length;
name->attributes.absolute = absolute;
1999-02-22 07:24:05 +00:00
isc_buffer_add(target, name->length);
return ISC_R_SUCCESS;
}
1999-04-23 04:58:43 +00:00
void
dns_name_dup(const dns_name_t *source, isc_mem_t *mctx, dns_name_t *target) {
1999-08-31 22:09:52 +00:00
/*
* Make 'target' a dynamically allocated copy of 'source'.
*/
REQUIRE(DNS_NAME_VALID(source));
1999-06-12 01:08:16 +00:00
REQUIRE(source->length > 0);
REQUIRE(DNS_NAME_VALID(target));
REQUIRE(DNS_NAME_BINDABLE(target));
1999-06-12 01:08:16 +00:00
target->ndata = isc_mem_get(mctx, source->length);
memmove(target->ndata, source->ndata, source->length);
1999-06-12 01:08:16 +00:00
target->length = source->length;
target->attributes = (struct dns_name_attrs){ .dynamic = true };
target->attributes.absolute = source->attributes.absolute;
1999-06-12 01:08:16 +00:00
}
void
dns_name_free(dns_name_t *name, isc_mem_t *mctx) {
size_t size;
1999-06-12 01:08:16 +00:00
/*
* Free 'name'.
*/
REQUIRE(DNS_NAME_VALID(name));
REQUIRE(name->attributes.dynamic);
1999-08-31 22:09:52 +00:00
size = name->length;
isc_mem_put(mctx, name->ndata, size);
1999-06-12 01:08:16 +00:00
dns_name_invalidate(name);
}
1999-08-31 22:09:52 +00:00
size_t
dns_name_size(const dns_name_t *name) {
size_t size;
REQUIRE(DNS_NAME_VALID(name));
if (!name->attributes.dynamic) {
return 0;
}
size = name->length;
return size;
}
isc_result_t
dns_name_digest(const dns_name_t *name, dns_digestfunc_t digest, void *arg) {
1999-08-31 22:09:52 +00:00
/*
* Send 'name' in DNSSEC canonical form to 'digest'.
*/
REQUIRE(DNS_NAME_VALID(name));
1999-08-31 22:09:52 +00:00
REQUIRE(digest != NULL);
unsigned char ndata[DNS_NAME_MAXWIRE];
isc_ascii_lowercopy(ndata, name->ndata, name->length);
isc_region_t r = {
.base = ndata,
.length = name->length,
};
1999-08-31 22:09:52 +00:00
return (digest)(arg, &r);
}
1999-10-13 22:45:17 +00:00
bool
dns_name_dynamic(const dns_name_t *name) {
REQUIRE(DNS_NAME_VALID(name));
1999-10-13 22:45:17 +00:00
1999-10-17 19:22:50 +00:00
/*
* Returns whether there is dynamic memory associated with this name.
*/
return name->attributes.dynamic;
1999-10-13 22:45:17 +00:00
}
1999-10-17 19:22:50 +00:00
isc_result_t
dns_name_print(const dns_name_t *name, FILE *stream) {
1999-10-17 19:22:50 +00:00
isc_result_t result;
isc_buffer_t b;
isc_region_t r;
char t[1024];
/*
* Print 'name' on 'stream'.
*/
REQUIRE(DNS_NAME_VALID(name));
1999-10-17 19:22:50 +00:00
isc_buffer_init(&b, t, sizeof(t));
result = dns_name_totext(name, 0, &b);
1999-10-17 19:22:50 +00:00
if (result != ISC_R_SUCCESS) {
return result;
}
isc_buffer_usedregion(&b, &r);
1999-10-17 19:22:50 +00:00
fprintf(stream, "%.*s", (int)r.length, (char *)r.base);
return ISC_R_SUCCESS;
}
isc_result_t
dns_name_settotextfilter(dns_name_totextfilter_t *proc) {
2005-09-10 01:02:08 +00:00
/*
* If we already have been here set / clear as appropriate.
*/
if (totext_filter_proc != NULL && proc != NULL) {
if (totext_filter_proc == proc) {
return ISC_R_SUCCESS;
}
2005-09-10 01:02:08 +00:00
}
if (proc == NULL && totext_filter_proc != NULL) {
totext_filter_proc = NULL;
return ISC_R_SUCCESS;
2005-09-10 01:02:08 +00:00
}
2008-04-01 23:47:10 +00:00
totext_filter_proc = proc;
return ISC_R_SUCCESS;
}
void
dns_name_format(const dns_name_t *name, char *cp, unsigned int size) {
isc_result_t result;
isc_buffer_t buf;
REQUIRE(size > 0);
/*
* Leave room for null termination after buffer.
*/
isc_buffer_init(&buf, cp, size - 1);
result = dns_name_totext(name, DNS_NAME_OMITFINALDOT, &buf);
if (result == ISC_R_SUCCESS) {
2019-08-08 13:52:44 +10:00
isc_buffer_putuint8(&buf, (uint8_t)'\0');
} else {
snprintf(cp, size, "<unknown>");
2019-08-08 13:52:44 +10:00
}
}
/*
* dns_name_tostring() -- similar to dns_name_format() but allocates its own
* memory.
*/
isc_result_t
dns_name_tostring(const dns_name_t *name, char **target, isc_mem_t *mctx) {
isc_result_t result;
isc_buffer_t buf;
isc_region_t reg;
char *p, txt[DNS_NAME_FORMATSIZE];
REQUIRE(DNS_NAME_VALID(name));
2009-03-11 23:47:35 +00:00
REQUIRE(target != NULL && *target == NULL);
isc_buffer_init(&buf, txt, sizeof(txt));
result = dns_name_totext(name, 0, &buf);
if (result != ISC_R_SUCCESS) {
return result;
}
isc_buffer_usedregion(&buf, &reg);
p = isc_mem_allocate(mctx, reg.length + 1);
memmove(p, (char *)reg.base, (int)reg.length);
p[reg.length] = '\0';
*target = p;
return ISC_R_SUCCESS;
}
isc_result_t
dns_name_fromstring(dns_name_t *target, const char *src,
const dns_name_t *origin, unsigned int options,
isc_mem_t *mctx) {
isc_result_t result;
isc_buffer_t buf;
dns_fixedname_t fn;
dns_name_t *name;
REQUIRE(src != NULL);
isc_buffer_constinit(&buf, src, strlen(src));
isc_buffer_add(&buf, strlen(src));
if (DNS_NAME_BINDABLE(target) && target->buffer != NULL) {
name = target;
} else {
name = dns_fixedname_initname(&fn);
}
result = dns_name_fromtext(name, &buf, origin, options);
if (result != ISC_R_SUCCESS) {
return result;
}
if (name != target) {
dns_name_dup(name, mctx, target);
}
return result;
}
void
dns_name_copy(const dns_name_t *source, dns_name_t *dest) {
isc_buffer_t *target = NULL;
unsigned char *ndata = NULL;
REQUIRE(DNS_NAME_VALID(source));
REQUIRE(DNS_NAME_VALID(dest));
REQUIRE(DNS_NAME_BINDABLE(dest));
target = dest->buffer;
REQUIRE(target != NULL);
REQUIRE(target->length >= source->length);
isc_buffer_clear(target);
ndata = (unsigned char *)target->base;
dest->ndata = target->base;
if (source->length != 0) {
memmove(ndata, source->ndata, source->length);
}
dest->ndata = ndata;
dest->length = source->length;
dest->attributes.absolute = source->attributes.absolute;
isc_buffer_add(target, dest->length);
}
/*
* Service Discovery Prefixes RFC 6763.
*/
static unsigned char b_dns_sd_udp_data[] = "\001b\007_dns-sd\004_udp";
static unsigned char db_dns_sd_udp_data[] = "\002db\007_dns-sd\004_udp";
static unsigned char r_dns_sd_udp_data[] = "\001r\007_dns-sd\004_udp";
static unsigned char dr_dns_sd_udp_data[] = "\002dr\007_dns-sd\004_udp";
static unsigned char lb_dns_sd_udp_data[] = "\002lb\007_dns-sd\004_udp";
static dns_name_t const dns_sd[] = {
DNS_NAME_INITNONABSOLUTE(b_dns_sd_udp_data),
DNS_NAME_INITNONABSOLUTE(db_dns_sd_udp_data),
DNS_NAME_INITNONABSOLUTE(r_dns_sd_udp_data),
DNS_NAME_INITNONABSOLUTE(dr_dns_sd_udp_data),
DNS_NAME_INITNONABSOLUTE(lb_dns_sd_udp_data)
};
bool
dns_name_isdnssd(const dns_name_t *name) {
size_t i;
dns_name_t prefix;
if (dns_name_countlabels(name) > 3U) {
dns_name_init(&prefix);
dns_name_getlabelsequence(name, 0, 3, &prefix);
for (i = 0; i < (sizeof(dns_sd) / sizeof(dns_sd[0])); i++) {
if (dns_name_equal(&prefix, &dns_sd[i])) {
return true;
}
}
}
return false;
}
static unsigned char inaddr10[] = "\00210\007IN-ADDR\004ARPA";
static unsigned char inaddr16172[] = "\00216\003172\007IN-ADDR\004ARPA";
static unsigned char inaddr17172[] = "\00217\003172\007IN-ADDR\004ARPA";
static unsigned char inaddr18172[] = "\00218\003172\007IN-ADDR\004ARPA";
static unsigned char inaddr19172[] = "\00219\003172\007IN-ADDR\004ARPA";
static unsigned char inaddr20172[] = "\00220\003172\007IN-ADDR\004ARPA";
static unsigned char inaddr21172[] = "\00221\003172\007IN-ADDR\004ARPA";
static unsigned char inaddr22172[] = "\00222\003172\007IN-ADDR\004ARPA";
static unsigned char inaddr23172[] = "\00223\003172\007IN-ADDR\004ARPA";
static unsigned char inaddr24172[] = "\00224\003172\007IN-ADDR\004ARPA";
static unsigned char inaddr25172[] = "\00225\003172\007IN-ADDR\004ARPA";
static unsigned char inaddr26172[] = "\00226\003172\007IN-ADDR\004ARPA";
static unsigned char inaddr27172[] = "\00227\003172\007IN-ADDR\004ARPA";
static unsigned char inaddr28172[] = "\00228\003172\007IN-ADDR\004ARPA";
static unsigned char inaddr29172[] = "\00229\003172\007IN-ADDR\004ARPA";
static unsigned char inaddr30172[] = "\00230\003172\007IN-ADDR\004ARPA";
static unsigned char inaddr31172[] = "\00231\003172\007IN-ADDR\004ARPA";
static unsigned char inaddr168192[] = "\003168\003192\007IN-ADDR\004ARPA";
static dns_name_t const rfc1918names[] = {
DNS_NAME_INITABSOLUTE(inaddr10), DNS_NAME_INITABSOLUTE(inaddr16172),
DNS_NAME_INITABSOLUTE(inaddr17172), DNS_NAME_INITABSOLUTE(inaddr18172),
DNS_NAME_INITABSOLUTE(inaddr19172), DNS_NAME_INITABSOLUTE(inaddr20172),
DNS_NAME_INITABSOLUTE(inaddr21172), DNS_NAME_INITABSOLUTE(inaddr22172),
DNS_NAME_INITABSOLUTE(inaddr23172), DNS_NAME_INITABSOLUTE(inaddr24172),
DNS_NAME_INITABSOLUTE(inaddr25172), DNS_NAME_INITABSOLUTE(inaddr26172),
DNS_NAME_INITABSOLUTE(inaddr27172), DNS_NAME_INITABSOLUTE(inaddr28172),
DNS_NAME_INITABSOLUTE(inaddr29172), DNS_NAME_INITABSOLUTE(inaddr30172),
DNS_NAME_INITABSOLUTE(inaddr31172), DNS_NAME_INITABSOLUTE(inaddr168192)
};
bool
dns_name_isrfc1918(const dns_name_t *name) {
size_t i;
for (i = 0; i < (sizeof(rfc1918names) / sizeof(*rfc1918names)); i++) {
if (dns_name_issubdomain(name, &rfc1918names[i])) {
return true;
}
}
return false;
}
static unsigned char ip6fc[] = "\001c\001f\003ip6\004ARPA";
static unsigned char ip6fd[] = "\001d\001f\003ip6\004ARPA";
static dns_name_t const ulanames[] = { DNS_NAME_INITABSOLUTE(ip6fc),
DNS_NAME_INITABSOLUTE(ip6fd) };
bool
dns_name_isula(const dns_name_t *name) {
size_t i;
for (i = 0; i < (sizeof(ulanames) / sizeof(*ulanames)); i++) {
if (dns_name_issubdomain(name, &ulanames[i])) {
return true;
}
}
return false;
}
bool
dns_name_istat(const dns_name_t *name) {
unsigned char len;
const unsigned char *ndata;
REQUIRE(DNS_NAME_VALID(name));
if (name->length == 0) {
return false;
}
ndata = name->ndata;
len = ndata[0];
INSIST(len <= name->length);
ndata++;
/*
* Is there at least one trust anchor reported and is the
2018-07-04 10:19:53 +10:00
* label length consistent with a trust-anchor-telemetry label.
*/
if ((len < 8) || (len - 3) % 5 != 0) {
return false;
}
if (ndata[0] != '_' || isc_ascii_tolower(ndata[1]) != 't' ||
isc_ascii_tolower(ndata[2]) != 'a')
{
return false;
}
ndata += 3;
len -= 3;
while (len > 0) {
INSIST(len >= 5);
if (ndata[0] != '-' || !isc_hex_char(ndata[1]) ||
!isc_hex_char(ndata[2]) || !isc_hex_char(ndata[3]) ||
!isc_hex_char(ndata[4]))
{
return false;
}
ndata += 5;
len -= 5;
}
return true;
}
bool
dns_name_isdnssvcb(const dns_name_t *name) {
unsigned char len, len1;
const unsigned char *ndata;
REQUIRE(DNS_NAME_VALID(name));
if (name->length < 5) {
return false;
}
ndata = name->ndata;
len = len1 = ndata[0];
INSIST(len <= name->length);
ndata++;
if (len < 2 || ndata[0] != '_') {
return false;
}
if (isdigit(ndata[1]) && name->length > len + 1) {
char buf[sizeof("65000")];
long port;
char *endp;
/*
* Do we have a valid _port label?
*/
if (len > 6U || (ndata[1] == '0' && len != 2)) {
return false;
}
memcpy(buf, ndata + 1, len - 1);
buf[len - 1] = 0;
port = strtol(buf, &endp, 10);
if (*endp != 0 || port < 0 || port > 0xffff) {
return false;
}
/*
* Move to next label.
*/
ndata += len;
INSIST(len1 + 1U < name->length);
len = *ndata;
INSIST(len + len1 + 1U <= name->length);
ndata++;
}
if (len == 4U && strncasecmp((const char *)ndata, "_dns", 4) == 0) {
return true;
}
return false;
}
bool
dns_name_israd(const dns_name_t *name, const dns_name_t *rad) {
dns_name_t suffix;
char labelbuf[64];
unsigned long v, last = ULONG_MAX;
char *end, *l;
REQUIRE(DNS_NAME_VALID(name));
REQUIRE(DNS_NAME_VALID(rad));
uint8_t name_labels = dns_name_countlabels(name);
uint8_t rad_labels = dns_name_countlabels(rad);
if (name_labels < rad_labels + 4U || name->length < 4U) {
return false;
}
if (name->ndata[0] != 3 || name->ndata[1] != '_' ||
tolower(name->ndata[2]) != 'e' || tolower(name->ndata[3]) != 'r')
{
return false;
}
dns_name_init(&suffix);
dns_name_split(name, rad_labels + 1, NULL, &suffix);
if (suffix.ndata[0] != 3 || suffix.ndata[1] != '_' ||
tolower(suffix.ndata[2]) != 'e' || tolower(suffix.ndata[3]) != 'r')
{
return false;
}
/* type list */
dns_name_split(name, name_labels - 1, NULL, &suffix);
INSIST(*suffix.ndata < sizeof(labelbuf));
memmove(labelbuf, suffix.ndata + 1, *suffix.ndata);
labelbuf[*suffix.ndata] = 0;
if (strlen(labelbuf) != *suffix.ndata) {
return false;
}
l = labelbuf;
do {
v = strtoul(l, &end, 10);
if (v > 0xffff || (*end != 0 && *end != '-') || end == l) {
return false;
}
if (last != ULONG_MAX && v <= last) {
return false;
}
last = v;
if (*end == '-') {
l = end + 1;
}
} while (*end != 0);
/* extended error code */
dns_name_split(name, rad_labels + 2, NULL, &suffix);
INSIST(*suffix.ndata < sizeof(labelbuf));
memmove(labelbuf, suffix.ndata + 1, *suffix.ndata);
labelbuf[*suffix.ndata] = 0;
if (strlen(labelbuf) != *suffix.ndata) {
return false;
}
v = strtoul(labelbuf, &end, 10);
if (v > 0xfff || *end != 0) {
return false;
}
return dns_name_issubdomain(name, rad);
}
uint8_t
dns_name_offsets(const dns_name_t *name, dns_offsets_t offsets) {
REQUIRE(DNS_NAME_VALID(name));
unsigned int offset, count, length, nlabels;
unsigned char *ndata;
ndata = name->ndata;
length = name->length;
offset = 0;
nlabels = 0;
while (offset != length) {
INSIST(nlabels < DNS_NAME_MAXLABELS);
if (offsets != NULL) {
offsets[nlabels] = offset;
}
nlabels++;
count = *ndata;
INSIST(count <= DNS_NAME_LABELLEN);
offset += count + 1;
ndata += count + 1;
INSIST(offset <= length);
if (count == 0) {
/* Final root label */
break;
}
}
INSIST(offset == name->length);
return nlabels;
}