diff --git a/bin/tests/name_test.c b/bin/tests/name_test.c new file mode 100644 index 0000000000..5e048d77fe --- /dev/null +++ b/bin/tests/name_test.c @@ -0,0 +1,138 @@ + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +static void +print_wirename(isc_region_t name) { + unsigned char *ccurr, *cend; + + ccurr = name->base; + cend = ccurr + name->length; + while (ccurr != cend) + printf("%02x ", *ccurr++); + printf("\n"); +} + +int +main(int argc, char *argv[]) { + char s[1000]; + unsigned char b[255]; + unsigned char o[255]; + unsigned char c[255]; + unsigned int tbytes; + dns_result_t result; + struct dns_name name, oname, compname; + struct isc_region source, target, r; + dns_name_t origin, comp; + isc_boolean_t downcase = ISC_FALSE; + + argc--; + argv++; + + if (argc > 0) { + if (strcasecmp("none", argv[0]) == 0) + origin = NULL; + else { + source.base = (unsigned char *)argv[0]; + source.length = strlen(argv[0]); + target.base = o; + target.length = 255; + result = dns_name_fromtext(&oname, &source, + dns_rootname, 0, + &target); + if (result != 0) { + fprintf(stderr, + "dns_name_fromtext() failed: %d\n", + result); + exit(1); + } + origin = &oname; + } + } else + origin = dns_rootname; + + if (argc > 1) { + if (strcasecmp("none", argv[0]) == 0) + comp = NULL; + else { + source.base = (unsigned char *)argv[1]; + source.length = strlen(argv[1]); + target.base = c; + target.length = 255; + result = dns_name_fromtext(&compname, &source, + dns_rootname, 0, + &target); + if (result != 0) { + fprintf(stderr, + "dns_name_fromtext() failed: %d\n", + result); + exit(1); + } + comp = &compname; + } + } else + comp = NULL; + + while (gets(s) != NULL) { + source.base = (unsigned char *)s; + source.length = strlen(s); + target.base = b; + target.length = 255; + result = dns_name_fromtext(&name, &source, origin, downcase, + &target); + if (result == DNS_R_SUCCESS) { + dns_name_toregion(&name, &r); +#ifndef QUIET + print_wirename(&r); + printf("%u labels, %u bytes.\n", + dns_name_countlabels(&name), + r.length); +#endif + } else + printf("%s\n", dns_result_totext(result)); + + if (result == 0) { + target.base = (unsigned char *)s; + target.length = 1000; + result = dns_name_totext(&name, 0, &target, &tbytes); + if (result == DNS_R_SUCCESS) { + printf("%.*s\n", (int)tbytes, s); +#ifndef QUIET + printf("%u bytes.\n", tbytes); +#endif + } else + printf("%s\n", dns_result_totext(result)); + } + +#ifndef QUIET + if (comp != NULL) { + int i; + isc_boolean_t b; + + i = dns_name_compare(&name, comp); + b = dns_name_issubdomain(&name, comp); + if (i < 0) + printf("<, "); + else if (i > 0) + printf(">, "); + else + printf("=, "); + if (!b) + printf("not "); + printf("subdomain\n"); + } +#endif + } + + return (0); +} diff --git a/lib/dns/include/dns/name.h b/lib/dns/include/dns/name.h new file mode 100644 index 0000000000..66206b2195 --- /dev/null +++ b/lib/dns/include/dns/name.h @@ -0,0 +1,499 @@ + +#ifndef DNS_NAME_H +#define DNS_NAME_H 1 + +/***** + ***** Module Info + *****/ + +/* + * DNS Names and Labels + * + * Provides facilities for manipulating DNS names and labels, including + * conversions to and from wire format and text format. + * + * Given the large number of names possible in a nameserver, and because + * names occur in rdata, it was important to come up with a very efficient + * way of storing name data, but at the same time allow names to be + * manipulated. The decision was to store names in uncompressed wire format, + * and not to make them fully abstracted objects; i.e. certain parts of the + * server know names are stored that way. This saves a lot of memory, and + * makes adding names to messages easy. Having much of the server know + * the representation would be perilous, and we certainly don't want each + * user of names to be manipulating such a low-level structure. This is + * where the Names and Labels module comes in. The module allows name or + * label handles to be created and attached to uncompressed wire format + * regions. All name operations and conversions are done through these + * handles. + * + * MP: + * Clients of this module must impose any required synchronization. + * + * Reliability: + * This module deals with low-level byte streams. Errors in any of + * the functions are likely to crash the server or corrupt memory. + * + * Resources: + * None. + * + * Security: + * + * *** WARNING *** + * + * dns_name_fromwire() deals with raw network data. An error in + * this routine could result in the failure or hijacking of the server. + * + * Standards: + * RFC 1035 + * Draft EDNS0 (0) + * Draft EDNS1 (0) + * Draft Binary Labels (2) + * Draft Local Compression (1) + * + */ + +/*** + *** Imports + ***/ + +#include + +#include + + + +/***** + ***** Labels + ***** + ***** A 'label' is basically a region. It contains one DNS wire format + ***** label of either type 00 (ordinary) or type 01000001 (bitstring). + *****/ + +#define DNS_LABELTYPE_BITSTRING 0x41 + +/*** + *** Properties + ***/ + +dns_labeltype_t dns_label_type(dns_label_t label); +/* + * Get the type of 'label'. + * + * Requires: + * 'label' is a valid label (i.e. not NULL, points to a + * struct dns_label) + * 'label' is a type 00 or type 01000001 label (i.e. not compressed). + * + * Returns: + * dns_labeltype_ordinary type 00 label + * dns_labeltype_bitstring type 01000001 label + */ + +/*** + *** Bitstring Labels + ***/ + +unsigned int dns_label_countbits(dns_label_t label); +/* + * The number of bits in a bitstring label. + * + * Requires: + * 'label' is a valid label + * + * dns_label_type(label) == dns_labeltype_bitstring + * + * Ensures: + * Result is <= 256. + * + * Returns: + * The number of bits in the bitstring label. + */ + +dns_bitlabel_t dns_label_getbit(dns_label_t label, unsigned int n); +/* + * The 'n'th most significant bit of 'label'. + * + * Notes: + * Numbering starts at 0. + * + * Require: + * n < dns_label_countbits(label) + * + * Returns: + * dns_bitlabel_0 The bit was 0. + * dns_bitlabel_1 The bit was 1. + */ + +/*** + *** Note + *** + *** Some provision still needs to be made for splitting bitstring labels, + *** and for merging them, but doing either one requires memory in the + *** obvious implementation. + *** + *** Perhaps we can simply leave merging to FromWire, and deal with splitting + *** by some other, noncopying method. I suspect copying is the way to go, + *** however. + ***/ + + + +/***** + ***** Names + ***** + ***** A 'name' is a handle to a binary region. It contains a sequence of one + ***** or more DNS wire format labels of either type 00 (ordinary) or type + ***** 01000001 (bitstring). Note that all names are not required to end + ***** with the root label, as they are in the actual DNS wire protocol. + *****/ + +/*** + *** Types + ***/ + +/* + * Clients are discouraged from using this type directly. + */ +struct dns_name { + unsigned char *ndata; + unsigned int length; + unsigned int labels; + unsigned char offsets[128]; +}; + +extern dns_name_t dns_rootname; + +/*** + *** Initialization + ***/ + +void dns_name_init(dns_name_t name); +/* + * Make 'name' empty. + * + * Requires: + * 'name' is a valid name (i.e. not NULL, points to a struct dns_name) + * + * Ensures: + * dns_name_countlabels(name) == 0 + */ + + +/*** + *** Properties + ***/ + +isc_boolean_t dns_name_isabsolute(dns_name_t name); +/* + * Does 'name' end in the root label? + * + * Requires: + * 'name' is a valid name + * + * dns_name_countlabels(name) > 0 + * + * Returns: + * TRUE The last label in 'name' is the root label. + * FALSE The last label in 'name' is not the root label. + */ + + +/*** + *** Comparisons + ***/ + +int dns_name_compare(dns_name_t name1, dns_name_t name2); +/* + * Determine the relative ordering under the DNSSEC order relation of + * 'name1' and 'name2'. + * + * Requires: + * 'name1' is a valid name + * + * dns_name_countlabels(name1) > 0 + * + * 'name2' is a valid name + * + * dns_name_countlabels(name2) > 0 + * + * Returns: + * -1 'name1' is less than 'name2' + * 0 'name1' is equal to 'name2' + * 1 'name1' is greater than 'name2' + */ + +isc_boolean_t +dns_name_issubdomain(dns_name_t name1, dns_name_t name2); +/* + * Is 'name1' a subdomain of 'name2'? + * + * Notes: + * name1 is a subdomain of name2 if name1 is contained in name2, or + * name1 equals name2. + * + * 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. + * + * Requires: + * 'name1' is a valid name + * + * dns_name_countlabels(name1) > 0 + * + * 'name2' is a valid name + * + * dns_name_countlabels(name2) > 0 + * + * Returns: + * TRUE 'name1' is a subdomain of 'name2' + * FALSE 'name1' is not a subdomain of 'name2' + */ + + +/*** + *** Labels + ***/ + +unsigned int dns_name_countlabels(dns_name_t name); +/* + * How many labels does 'name' have? + * + * Notes: + * In this case, as in other places, a 'label' is an ordinary label + * or a bitstring label. The term is not meant to refer to individual + * bit labels. + * + * Requires: + * 'name' is a valid name + * + * Ensures: + * The result is <= 128. + * + * Returns: + * The number of labels in 'name'. + */ + +void dns_name_getlabel(dns_name_t name, unsigned int n, dns_label_t label); +/* + * Make 'label' refer to the 'n'th least significant label of 'name'. + * + * Notes: + * Numbering starts at 0. + * + * Given "rc.vix.com.", the label 0 is "rc", and label 3 is the + * root label. + * + * 'label' refers to the same memory as 'name', so 'name' must not + * be changed while 'label' is still in use. + * + * Requires: + * n < dns_label_countlabels(name) + */ + +void dns_name_getlabelsequence(dns_name_t source, + unsigned int first, + unsigned int n, + dns_name_t target); +/* + * Make 'target' refer to the 'n' labels including and following 'first' + * in 'source'. + * + * Notes: + * Numbering starts at 0. + * + * 'target' refers to the same memory as 'source', so 'source' + * must not be changed while 'target' is still in use. + * + * Requires: + * first < dns_label_countlabels(name) + * first + n <= dns_label_countlabels(name) + */ + +/*** + *** Conversions + ***/ + +void dns_name_fromregion(dns_name_t name, isc_region_t r); +/* + * Make 'name' refer to region 'r'. + * + * Requires: + * The data in 'r' is a sequence of one or more type 00 or type 01000001 + * labels. + * The length of 'r' is <= 255. + */ + +void dns_name_toregion(dns_name_t name, isc_region_t r); +/* + * Make 'r' refer to 'name'. + * + * Requires: + * + * 'name' is a valid name. + * + * 'r' is a valid region. + */ + +dns_result_t dns_name_fromwire(dns_name_t name, + isc_region_t source, + dns_decompression_t dctx, + isc_boolean_t downcase, + isc_region_t target); +/* + * Copy the possibly-compressed name at source into target, decompressing it. + * + * Notes: + * Decompression policy is controlled by 'dctx'. + * + * If 'downcase' is true, any uppercase letters in 'source' will be + * downcased when they are copied into 'target'. + * + * Security: + * + * *** WARNING *** + * + * This routine will often be used when 'source' contains raw network + * data. An error in this routine could result in a denial of service, + * or in the hijacking of the server. + * + * Requires: + * + * 'source' and 'target' are valid regions. + * + * 'dctx' is a valid decompression context. + * + * Ensures: + * + * If result is success: + * 'name' is attached to the target. + * + * Uppercase letters are downcased in the copy iff. 'downcase' is + * true. + * + * Any bitstring labels in source are canonicalized. + * (i.e. maximally packed and any padding bits zeroed.) + * + * Result: + * Success + * Bad Form: Label Length + * Bad Form: Unknown Label Type + * Bad Form: Name Length + * Bad Form: Local compression not allowed + * Bad Form: Compression pointer loop + * Bad Form: Input too short + * Resource Limit: Too many compression pointers + * Resource Limit: Not enough space in buffer + */ + +dns_result_t dns_name_towire(dns_name_t name, + dns_compression_t cctx, + isc_region_t target, unsigned int *bytesp); +/* + * Convert 'name' into wire format, compressing it as specified by the + * compression context 'cctx', and storing the result in 'target'. + * + * Notes: + * If the compression context allows global compression, then the + * global compression table may be updated. + * + * Requires: + * 'name' is a valid name + * + * dns_name_countlabels(name) > 0 + * + * dns_name_isabsolute(name) == TRUE + * + * target is a valid region + * + * Any offsets specified in a global compression table are valid + * for buffer. + * + * Ensures: + * If the result is success: + * Any bitstring labels are in canonical form. + * + * *bytesp is the number of bytes of the target region that + * were used. + * + * Returns: + * Success + * Resource Limit: Not enough space in buffer + */ + +dns_result_t dns_name_fromtext(dns_name_t name, + isc_region_t source, + dns_name_t origin, + isc_boolean_t downcase, + isc_region_t target); +/* + * Convert the textual representation of a DNS name at source + * into uncompressed wire form stored in target. + * + * Notes: + * Relative domain names will have 'origin' appended to them + * unless 'origin' is NULL, in which case relative domain names + * will remain relative. + * + * If 'downcase' is true, any uppercase letters in 'source' will be + * downcased when they are copied into 'target'. + * + * Requires: + * + * 'source' and 'target' are valid regions. + * 'name' is a valid name. + * + * Ensures: + * If result is success: + * 'name' is attached to the target. + * + * Any bitstring labels in source are canonicalized. + * + * Uppercase letters are downcased in the copy iff. 'downcase' is + * true. + * + * The current location in source is advanced, and the used space + * in target is updated. + * + * Result: + * Success + * Bad Form: Label Length + * Bad Form: Unknown Label Type + * Bad Form: Name Length + * Bad Form: Empty Label + * Bad Form: Input too short + * Resource Limit: Not enough space in buffer + */ + +dns_result_t dns_name_totext(dns_name_t name, + isc_boolean_t omit_final_dot, + isc_region_t target, unsigned int *bytesp); +/* + * Convert 'name' into text format, storing the result in 'target'. + * + * Notes: + * If 'omit_final_dot' is true, then the final '.' in an absolute + * name will not be emitted. + * + * Requires: + * 'name' is a valid name + * + * 'target' is a valid buffer + * + * dns_name_countlabels(name) > 0 + * + * if dns_name_isabsolute == FALSE, then omit_final_dot == FALSE + * + * Ensures: + * If the result is success: + * Any bitstring labels are in canonical form. + * + * *bytesp is the number of bytes of the target region that + * were used. + * + * Returns: + * Success + * Resource Limit: Not enough space in buffer + */ + +#endif /* DNS_NAME_H */ diff --git a/lib/dns/include/dns/result.h b/lib/dns/include/dns/result.h new file mode 100644 index 0000000000..7e4af6d630 --- /dev/null +++ b/lib/dns/include/dns/result.h @@ -0,0 +1,26 @@ + +#ifndef DNS_RESULT_H +#define DNS_RESULT_H 1 + +typedef unsigned int dns_result_t; + +#define DNS_R_SUCCESS 0 +#define DNS_R_NOMEMORY 1 +/* Names */ +#define DNS_R_NOSPACE 2 +#define DNS_R_LABELTOOLONG 3 +#define DNS_R_BADESCAPE 4 +#define DNS_R_BADBITSTRING 5 +#define DNS_R_BITSTRINGTOOLONG 6 +#define DNS_R_EMPTYLABEL 7 +#define DNS_R_BADDOTTEDQUAD 8 +#define DNS_R_UNEXPECTEDEND 9 +#define DNS_R_NOTIMPLEMENTED 10 + +#define DNS_R_LASTENTRY 10 /* Last entry on list. */ + +#define DNS_R_UNEXPECTED 0xFFFFFFFFL + +char * dns_result_totext(dns_result_t); + +#endif /* DNS_RESULT_H */ diff --git a/lib/dns/include/dns/types.h b/lib/dns/include/dns/types.h new file mode 100644 index 0000000000..7717c78f06 --- /dev/null +++ b/lib/dns/include/dns/types.h @@ -0,0 +1,24 @@ + +#ifndef DNS_TYPES_H +#define DNS_TYPES_H 1 + +#include +#include + +typedef isc_region_t dns_label_t; +typedef struct dns_name * dns_name_t; +typedef struct dns_lex * dns_lex_t; +typedef struct dns_compression * dns_compression_t; +typedef struct dns_decompression * dns_decompression_t; + +typedef enum { + dns_labeltype_ordinary = 0, + dns_labeltype_bitstring = 1 +} dns_labeltype_t; + +typedef enum { + dns_bitlabel_0 = 0, + dns_bitlabel_1 = 1 +} dns_bitlabel_t; + +#endif /* DNS_TYPES_H */ diff --git a/lib/dns/name.c b/lib/dns/name.c new file mode 100644 index 0000000000..d0e7af7863 --- /dev/null +++ b/lib/dns/name.c @@ -0,0 +1,1266 @@ +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#define VALID_NAME(n) ((n) != NULL && (n)->length > 0) + +typedef enum { + tw_init = 0, + tw_start, + tw_ordinary, + tw_initialescape, + tw_escape, + tw_escdecimal, + tw_bitstring, + tw_binary, + tw_octal, + tw_hex, + tw_dottedquad, + tw_dqdecimal, + tw_maybeslash, + tw_finishbitstring, + tw_bitlength, + tw_eatdot +} tw_state; + +static char digitvalue[256] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*16*/ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*32*/ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*48*/ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, /*64*/ + -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*80*/ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*96*/ + -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*112*/ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*128*/ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*256*/ +}; + +static char hexdigits[16] = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' +}; + +static unsigned char maptolower[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff +}; + +static struct dns_name root = { "", 1, 1 }; + +dns_name_t dns_rootname = &root; + +static unsigned int get_bit(unsigned char *, unsigned int); +static void set_bit(unsigned char *, unsigned int, unsigned int); +static void set_offsets(dns_name_t, isc_boolean_t); +static void compact(dns_name_t); + + +dns_labeltype_t +dns_label_type(dns_label_t label) { + /* + * Get the type of 'label'. + */ + + REQUIRE(label != NULL); + REQUIRE(label->length > 0); + REQUIRE(label->base[0] <= 63 || + label->base[0] == DNS_LABELTYPE_BITSTRING); + + if (label->base[0] <= 63) + return (dns_labeltype_ordinary); + else + return (dns_labeltype_bitstring); +} + +unsigned int +dns_label_countbits(dns_label_t label) { + unsigned int count; + + /* + * The number of bits in a bitstring label. + */ + + REQUIRE(label != NULL); + REQUIRE(label->length > 2); + REQUIRE(label->base[0] == DNS_LABELTYPE_BITSTRING); + + count = label->base[1]; + if (count == 0) + count = 256; + + return (count); +} + +dns_bitlabel_t +dns_label_getbit(dns_label_t label, unsigned int n) { + unsigned int count, bit; + + /* + * The 'n'th most significant bit of 'label'. + * + * Notes: + * Numbering starts at 0. + */ + + REQUIRE(label != NULL); + REQUIRE(label->length > 2); + REQUIRE(label->base[0] == DNS_LABELTYPE_BITSTRING); + + count = label->base[1]; + if (count == 0) + count = 256; + + REQUIRE(n < count); + + bit = get_bit(&label->base[2], n); + if (bit == 0) + return (dns_bitlabel_0); + return (dns_bitlabel_1); +} + +void +dns_name_init(dns_name_t name) { + /* + * Make 'name' empty. + */ + + name->ndata = NULL; + name->length = 0; + name->labels = 0; +} + +isc_boolean_t +dns_name_isabsolute(dns_name_t name) { + /* + * Does 'name' end in the root label? + */ + + REQUIRE(VALID_NAME(name)); + + if (name->ndata[name->offsets[name->labels - 1]] == 0) + return (ISC_TRUE); + return (ISC_FALSE); +} + +int +dns_name_compare(dns_name_t name1, dns_name_t name2) { + unsigned int l1, l2, l, count1, count2, count; + unsigned int b1, b2, n; + unsigned char c1, c2; + int cdiff, ldiff; + unsigned char *label1, *label2; + + /* + * Determine the relative ordering under the DNSSEC order relation of + * 'name1' and 'name2'. + */ + + REQUIRE(VALID_NAME(name1)); + REQUIRE(VALID_NAME(name2)); + + l1 = name1->labels; + l2 = name2->labels; + if (l1 < l2) { + l = l1; + ldiff = -1; + } else { + l = l2; + if (l1 > l2) + ldiff = 1; + else + ldiff = 0; + } + + while (l > 0) { + l--; + l1--; + l2--; + label1 = &name1->ndata[name1->offsets[l1]]; + label2 = &name2->ndata[name2->offsets[l2]]; + count1 = *label1++; + count2 = *label2++; + if (count1 <= 63 && count2 <= 63) { + if (count1 < count2) { + cdiff = -1; + count = count1; + } else { + count = count2; + if (count1 > count2) + cdiff = 1; + else + cdiff = 0; + } + + while (count > 0) { + count--; + c1 = maptolower[*label1++]; + c2 = maptolower[*label2++]; + if (c1 < c2) + return (-1); + else if (c1 > c2) + return (1); + } + if (cdiff != 0) + return (cdiff); + } else if (count1 == DNS_LABELTYPE_BITSTRING && count2 <= 63) { + if (count2 == 0) + return (1); + return (-1); + } else if (count2 == DNS_LABELTYPE_BITSTRING && count1 <= 63) { + if (count1 == 0) + return (-1); + return (1); + } else { + INSIST(count1 == DNS_LABELTYPE_BITSTRING && + count2 == DNS_LABELTYPE_BITSTRING); + count1 = *label1++; + if (count1 == 0) + count1 = 256; + count2 = *label2++; + if (count2 == 0) + count2 = 256; + if (count1 < count2) { + cdiff = -1; + count = count1; + } else { + count = count2; + if (count1 > count2) + cdiff = 1; + else + cdiff = 0; + } + /* Yes, this loop is really slow! */ + for (n = 0; n < count; n++) { + b1 = get_bit(label1, n); + b2 = get_bit(label2, n); + if (b1 < b2) + return (-1); + else if (b1 > b2) + return (1); + } + if (cdiff != 0) + return (cdiff); + } + } + + return (ldiff); +} + +isc_boolean_t +dns_name_issubdomain(dns_name_t name1, dns_name_t name2) { + isc_boolean_t a1, a2; + unsigned int l1, l2, count1, count2; + unsigned int b1, b2, n; + unsigned char c1, c2; + unsigned char *label1, *label2; + + /* + * 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. + */ + + REQUIRE(VALID_NAME(name1)); + REQUIRE(VALID_NAME(name2)); + + /* We're not going for maximal speed yet... */ + a1 = dns_name_isabsolute(name1); + a2 = dns_name_isabsolute(name2); + + REQUIRE((a1 && a2) || (!a1 && !a2)); + + l1 = name1->labels; + l2 = name2->labels; + if (l1 < l2) + return (ISC_FALSE); + + while (l2 > 0) { + l1--; + l2--; + label1 = &name1->ndata[name1->offsets[l1]]; + label2 = &name2->ndata[name2->offsets[l2]]; + count1 = *label1++; + count2 = *label2++; + if (count1 <= 63 && count2 <= 63) { + if (count1 != count2) + return (ISC_FALSE); + while (count2 > 0) { + count2--; + c1 = maptolower[*label1++]; + c2 = maptolower[*label2++]; + if (c1 != c2) + return (ISC_FALSE); + } + } else { + if (count1 != count2) + return (ISC_FALSE); + INSIST(count1 == DNS_LABELTYPE_BITSTRING && + count2 == DNS_LABELTYPE_BITSTRING); + count1 = *label1++; + if (count1 == 0) + count1 = 256; + count2 = *label2++; + if (count2 == 0) + count2 = 256; + if (count1 < count2) + return (ISC_FALSE); + /* Yes, this loop is really slow! */ + for (n = 0; n < count2; n++) { + b1 = get_bit(label1, n); + b2 = get_bit(label2, n); + if (b1 != b2) + return (ISC_FALSE); + } + if (count1 != count2 && l2 != 0) + return (ISC_FALSE); + } + } + + return (ISC_TRUE); +} + +unsigned int +dns_name_countlabels(dns_name_t name) { + /* + * How many labels does 'name' have? + */ + + REQUIRE(VALID_NAME(name)); + + ENSURE(name->labels <= 128); + + return (name->labels); +} + +void +dns_name_getlabel(dns_name_t name, unsigned int n, dns_label_t label) { + /* + * Make 'label' refer to the 'n'th least significant label of 'name'. + */ + + REQUIRE(VALID_NAME(name)); + REQUIRE(n < name->labels); + REQUIRE(label != NULL); + + label->base = &name->ndata[name->offsets[n]]; + if (n == name->labels - 1) + label->length = name->length - name->offsets[n]; + else + label->length = name->offsets[n + 1] - name->offsets[n]; +} + +void +dns_name_getlabelsequence(dns_name_t source, + unsigned int first, unsigned int n, + dns_name_t target) +{ + /* + * Make 'target' refer to the 'n' labels including and following + * 'first' in 'source'. + */ + + REQUIRE(VALID_NAME(source)); + REQUIRE(n > 0); + REQUIRE(first < source->labels); + REQUIRE(first + n <= source->labels); + + target->ndata = &source->ndata[source->offsets[first]]; + if (first + n == source->labels) + target->length = source->length - source->offsets[first]; + else + target->length = source->offsets[first + n] - + source->offsets[first]; + target->labels = n; + + set_offsets(target, ISC_FALSE); +} + +void +dns_name_fromregion(dns_name_t name, isc_region_t r) { + /* + * Make 'name' refer to region 'r'. + */ + + REQUIRE(name != NULL); + REQUIRE(r != NULL); + REQUIRE(r->length <= 255); + + name->ndata = r->base; + name->length = r->length; + + if (r->length > 0) + set_offsets(name, ISC_TRUE); + else + name->labels = 0; +} + +void +dns_name_toregion(dns_name_t name, isc_region_t r) { + /* + * Make 'r' refer to 'name'. + */ + + REQUIRE(VALID_NAME(name)); + REQUIRE(r != NULL); + + r->base = name->ndata; + r->length = name->length; +} + + +dns_result_t +dns_name_fromtext(dns_name_t name, isc_region_t source, + dns_name_t origin, isc_boolean_t downcase, + isc_region_t target) +{ + unsigned char *ndata, *label; + char *tdata; + char c; + tw_state state, kind; + unsigned int value, count, tbcount, bitlength, maxlength; + unsigned int n1, n2, vlen, tlen, nrem, digits, labels; + isc_boolean_t done, saw_bitstring; + unsigned char dqchars[4]; + + /* + * Convert the textual representation of a DNS name at source + * into uncompressed wire form stored in target. + * + * Notes: + * Relative domain names will have 'origin' appended to them + * unless 'origin' is NULL, in which case relative domain names + * will remain relative. + */ + + REQUIRE(source != NULL); + REQUIRE(target != NULL); + + /* + * Initialize things to make the compiler happy; they're not required. + */ + n1 = 0; + n2 = 0; + vlen = 0; + label = NULL; + digits = 0; + value = 0; + count = 0; + tbcount = 0; + bitlength = 0; + maxlength = 0; + kind = tw_init; + + /* + * Invalidate 'name'. + */ + name->ndata = NULL; + name->length = 0; + name->labels = 0; + + /* + * Set up the state machine. + */ + tdata = (char *)source->base; + tlen = source->length; + ndata = target->base; + nrem = target->length; + if (nrem > 255) + nrem = 255; + labels = 0; + done = ISC_FALSE; + saw_bitstring = ISC_FALSE; + state = tw_init; + + while (nrem > 0 && tlen > 0 && !done) { + c = *tdata++; + tlen--; + + no_read: + switch (state) { + case tw_init: + /* + * Is this the root name? + */ + if (c == '.') { + if (tlen != 0) + return (DNS_R_EMPTYLABEL); + labels++; + *ndata++ = 0; + nrem--; + done = ISC_TRUE; + break; + } + /* FALLTHROUGH */ + case tw_start: + label = ndata; + ndata++; + nrem--; + count = 0; + if (c == '\\') { + state = tw_initialescape; + break; + } + kind = tw_ordinary; + state = tw_ordinary; + /* FALLTHROUGH */ + case tw_ordinary: + if (c == '.') { + if (count == 0) + return (DNS_R_EMPTYLABEL); + *label = count; + labels++; + if (tlen == 0) { + labels++; + *ndata++ = 0; + nrem--; + done = ISC_TRUE; + } + state = tw_start; + } else if (c == '\\') { + state = tw_escape; + } else { + if (count >= 63) + return (DNS_R_LABELTOOLONG); + count++; + if (downcase) + c = maptolower[(int)c]; + *ndata++ = c; + nrem--; + } + break; + case tw_initialescape: + if (c == '[') { + saw_bitstring = ISC_TRUE; + kind = tw_bitstring; + state = tw_bitstring; + *label = DNS_LABELTYPE_BITSTRING; + label = ndata; + ndata++; + nrem--; + break; + } + kind = tw_ordinary; + state = tw_escape; + /* FALLTHROUGH */ + case tw_escape: + if (!isdigit(c)) { + if (count >= 63) + return (DNS_R_LABELTOOLONG); + count++; + if (downcase) + c = maptolower[(int)c]; + *ndata++ = c; + nrem--; + state = tw_ordinary; + break; + } + digits = 0; + value = 0; + state = tw_escdecimal; + /* FALLTHROUGH */ + case tw_escdecimal: + if (!isdigit(c)) + return (DNS_R_BADESCAPE); + value *= 10; + value += digitvalue[(int)c]; + digits++; + if (digits == 3) { + if (value > 255) + return (DNS_R_BADESCAPE); + if (count >= 63) + return (DNS_R_LABELTOOLONG); + count++; + if (downcase) + value = maptolower[value]; + *ndata++ = value; + nrem--; + state = tw_ordinary; + } + break; + case tw_bitstring: + /* count is zero */ + tbcount = 0; + value = 0; + if (c == 'b') { + vlen = 8; + maxlength = 256; + kind = tw_binary; + state = tw_binary; + } else if (c == 'o') { + vlen = 8; + maxlength = 256; + kind = tw_octal; + state = tw_octal; + } else if (c == 'x') { + vlen = 8; + maxlength = 256; + kind = tw_hex; + state = tw_hex; + } else if (isdigit(c)) { + vlen = 32; + maxlength = 32; + n1 = 0; + n2 = 0; + digits = 0; + kind = tw_dottedquad; + state = tw_dqdecimal; + goto no_read; + } else + return (DNS_R_BADBITSTRING); + break; + case tw_binary: + if (c != '0' && c != '1') { + state = tw_maybeslash; + goto no_read; + } + value <<= 1; + if (c == '1') + value |= 1; + count++; + tbcount++; + if (tbcount > 256) + return (DNS_R_BITSTRINGTOOLONG); + if (count == 8) { + *ndata++ = value; + nrem--; + count = 0; + } + break; + case tw_octal: + if (!isdigit(c) || c == '9') { + state = tw_maybeslash; + goto no_read; + } + value <<= 3; + value += digitvalue[(int)c]; + count += 3; + tbcount += 3; + if (tbcount > 256) + return (DNS_R_BITSTRINGTOOLONG); + if (count == 8) { + *ndata++ = value; + nrem--; + count = 0; + } else if (count == 9) { + *ndata++ = (value >> 1); + nrem--; + value &= 1; + count = 1; + } else if (count == 10) { + *ndata++ = (value >> 2); + nrem--; + value &= 3; + count = 2; + } + break; + case tw_hex: + if (!isxdigit(c)) { + state = tw_maybeslash; + goto no_read; + } + value <<= 4; + value += digitvalue[(int)c]; + count += 4; + tbcount += 4; + if (tbcount > 256) + return (DNS_R_BITSTRINGTOOLONG); + if (count == 8) { + *ndata++ = value; + nrem--; + count = 0; + } + break; + case tw_dottedquad: + if (c != '.' && n1 < 3) + return (DNS_R_BADDOTTEDQUAD); + dqchars[n1] = value; + n2 *= 256; + n2 += value; + n1++; + if (n1 == 4) { + tbcount = 32; + value = n2; + state = tw_maybeslash; + goto no_read; + } + value = 0; + digits = 0; + state = tw_dqdecimal; + break; + case tw_dqdecimal: + if (!isdigit(c)) { + if (digits == 0 || value > 255) + return (DNS_R_BADDOTTEDQUAD); + state = tw_dottedquad; + goto no_read; + } + digits++; + if (digits > 3) + return (DNS_R_BADDOTTEDQUAD); + value *= 10; + value += digitvalue[(int)c]; + break; + case tw_maybeslash: + bitlength = 0; + if (c == '/') { + state = tw_bitlength; + break; + } + /* FALLTHROUGH */ + case tw_finishbitstring: + if (c == ']') { + if (tbcount == 0) + return (DNS_R_BADBITSTRING); + if (count > 0) { + n1 = count % 8; + if (n1 != 0) + value <<= (8 - n1); + *ndata++ = value; + nrem--; + } + if (bitlength != 0) { + if (bitlength > tbcount) + return (DNS_R_BADBITSTRING); + if (kind == tw_binary && + bitlength != tbcount) { + return (DNS_R_BADBITSTRING); + } else if (kind == tw_octal) { + /* + * Figure out correct number + * of octal digits for the + * bitlength, and compare to + * what was given. + */ + n1 = bitlength / 3; + if (bitlength % 3 != 0) + n1++; + n2 = tbcount / 3; + /* tbcount % 3 == 0 */ + if (n1 != n2) + return (DNS_R_BADBITSTRING); + } else if (kind == tw_hex) { + /* + * Figure out correct number + * of hex digits for the + * bitlength, and compare to + * what was given. + */ + n1 = bitlength / 4; + if (bitlength % 4 != 0) + n1++; + n2 = tbcount / 4; + /* tbcount % 4 == 0 */ + if (n1 != n2) + return (DNS_R_BADBITSTRING); + } + n1 = bitlength % vlen; + if (n1 != 0) { + /* + * Are the pad bits in the + * last 'vlen' bits zero? + */ + if ((value & + ~((~0) << (vlen-n1))) != 0) + return (DNS_R_BADBITSTRING); + } + } else if (kind == tw_dottedquad) + bitlength = 32; + else + bitlength = tbcount; + if (kind == tw_dottedquad) { + n1 = bitlength / 8; + if (bitlength % 8 != 0) + n1++; + if (nrem < n1) + return (DNS_R_NOSPACE); + for (n2 = 0; n2 < n1; n2++) { + *ndata++ = dqchars[n2]; + nrem--; + } + } + if (bitlength == 256) + *label = 0; + else + *label = bitlength; + labels++; + } else + return (DNS_R_BADBITSTRING); + state = tw_eatdot; + break; + case tw_bitlength: + if (!isdigit(c)) { + if (bitlength == 0) + return (DNS_R_BADBITSTRING); + state = tw_finishbitstring; + goto no_read; + } + bitlength *= 10; + bitlength += digitvalue[(int)c]; + if (bitlength > maxlength) + return (DNS_R_BADBITSTRING); + break; + case tw_eatdot: + if (c != '.') + return (DNS_R_BADBITSTRING); + if (tlen == 0) { + labels++; + *ndata++ = 0; + nrem--; + done = ISC_TRUE; + } + state = tw_start; + break; + default: + INSIST(0); + } + } + if (!done) { + if (nrem == 0) + return (DNS_R_NOSPACE); + if (state != tw_ordinary && state != tw_eatdot) + return (DNS_R_UNEXPECTEDEND); + if (state == tw_ordinary) { + INSIST(tlen == 0 && count != 0); + *label = count; + labels++; + } + if (tlen == 0 && origin != NULL) { + if (nrem < origin->length) + return (DNS_R_NOSPACE); + label = origin->ndata; + n1 = origin->length; + nrem -= n1; + labels += origin->labels; + while (n1 > 0) { + c = *label++; + if (downcase) + c = maptolower[(int)c]; + *ndata++ = c; + n1--; + } + } + } + + name->ndata = target->base; + name->labels = labels; + name->length = target->length - nrem; + + /* + * We should build the offsets table directly. + */ + set_offsets(name, ISC_FALSE); + + if (saw_bitstring) + compact(name); + + return (DNS_R_SUCCESS); +} + +dns_result_t +dns_name_totext(dns_name_t name, isc_boolean_t omit_final_dot, + isc_region_t target, unsigned int *bytesp) +{ + unsigned char *ndata; + char *tdata; + unsigned int nlen, tlen; + unsigned char c; + unsigned int trem, count; + unsigned int bytes, nibbles; + size_t i, len; + unsigned int labels; + isc_boolean_t saw_root = ISC_FALSE; + char num[4]; + + /* + * This function assumes the name is in proper uncompressed + * wire format. + */ + REQUIRE(name->labels > 0); + + ndata = name->ndata; + nlen = name->length; + labels = name->labels; + tdata = target->base; + tlen = target->length; + + trem = tlen; + + /* Special handling for root label. */ + if (nlen == 1 && labels == 1 && *ndata == 0) { + saw_root = ISC_TRUE; + labels = 0; + nlen = 0; + if (trem == 0) + return (DNS_R_NOSPACE); + *tdata++ = '.'; + trem--; + } + + while (labels > 0 && nlen > 0 && trem > 0) { + labels--; + count = *ndata++; + nlen--; + if (count == 0) { + saw_root = ISC_TRUE; + break; + } + if (count < 64) { + INSIST(nlen >= count); + while (count > 0) { + c = *ndata; + switch (c) { + case 0x22: /* '"' */ + case 0x2E: /* '.' */ + case 0x3B: /* ';' */ + case 0x5C: /* '\\' */ + /* Special modifiers in zone files. */ + case 0x40: /* '@' */ + case 0x24: /* '$' */ + if (trem < 2) + return (DNS_R_NOSPACE); + *tdata++ = '\\'; + *tdata++ = c; + ndata++; + trem -= 2; + nlen--; + break; + default: + if (c > 0x20 && c < 0x7f) { + if (trem == 0) + return (DNS_R_NOSPACE); + *tdata++ = c; + ndata++; + trem--; + nlen--; + } else { + if (trem < 4) + return (DNS_R_NOSPACE); + sprintf(tdata, "\\%03u", + c); + tdata += 4; + trem -= 4; + ndata++; + nlen--; + } + } + count--; + } + } else if (count == DNS_LABELTYPE_BITSTRING) { + if (trem < 3) + return (DNS_R_NOSPACE); + *tdata++ = '\\'; + *tdata++ = '['; + *tdata++ = 'x'; + trem -= 3; + INSIST(nlen > 0); + count = *ndata++; + if (count == 0) + count = 256; + nlen--; + len = sprintf(num, "%u", count); /* XXX */ + INSIST(len <= 4); + bytes = count / 8; + if (count % 8 != 0) + bytes++; + INSIST(nlen >= bytes); + nibbles = count / 4; + if (count % 4 != 0) + nibbles++; + if (trem < nibbles) + return (DNS_R_NOSPACE); + trem -= nibbles; + nlen -= bytes; + while (nibbles > 0) { + c = *ndata++; + *tdata++ = hexdigits[(c >> 4)]; + nibbles--; + if (nibbles != 0) { + *tdata++ = hexdigits[c & 0xf]; + i++; + nibbles--; + } + } + if (trem < 2 + len) + return (DNS_R_NOSPACE); + *tdata++ = '/'; + for (i = 0; i < len; i++) + *tdata++ = num[i]; + *tdata++ = ']'; + trem -= 2 + len; + } else + INSIST(0); + /* + * 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 (DNS_R_NOSPACE); + *tdata++ = '.'; + trem--; + } + + if (nlen != 0 && trem == 0) + return (DNS_R_NOSPACE); + INSIST(nlen == 0); + if (!saw_root || omit_final_dot) + trem++; + + *bytesp = tlen - trem; + + return (DNS_R_SUCCESS); +} + +/* Yes, get_bit and set_bit are lame. */ + +static unsigned int +get_bit(unsigned char *array, unsigned int index) { + unsigned int byte, shift; + + byte = array[index / 8]; + shift = 7 - (index % 8); + + return ((byte >> shift) & 0x01); +} + +static void +set_bit(unsigned char *array, unsigned int index, unsigned int bit) { + unsigned int byte, shift, mask; + + byte = array[index / 8]; + shift = 7 - (index % 8); + mask = 1 << shift; + + if (bit) + array[index / 8] |= mask; + else + array[index / 8] &= (~mask & 0xFF); +} + +static void +set_offsets(dns_name_t name, isc_boolean_t set_labels) { + unsigned int offset, count, nlabels, nrem, n; + unsigned char *ndata; + + ndata = name->ndata; + nrem = name->length; + offset = 0; + nlabels = 0; + while (nrem > 0) { + INSIST(nlabels < 128); + name->offsets[nlabels++] = offset; + count = *ndata++; + nrem--; + offset++; + if (count > 63) { + INSIST(count == DNS_LABELTYPE_BITSTRING); + INSIST(nrem != 0); + count = *ndata++; + nrem--; + offset++; + if (count == 0) + count = 256; + n = count / 8; + if (count % 8 != 0) + n++; + count = n; + } + INSIST(nrem >= count); + nrem -= count; + offset += count; + ndata += count; + } + if (set_labels) + name->labels = nlabels; + INSIST(nlabels == name->labels); +} + +static void +compact(dns_name_t name) { + unsigned char *head, *curr, *last; + unsigned int count, n, bit; + unsigned int headbits, currbits, tailbits, newbits; + unsigned int headrem, newrem; + unsigned int headindex, currindex, tailindex, newindex; + unsigned char tail[32]; + + /* + * The caller MUST ensure that all bitstrings are correctly formatted + * and that the offsets table is valid. + */ + + again: + memset(tail, 0, sizeof tail); + INSIST(name->labels != 0); + n = name->labels - 1; + + while (n > 0) { + head = &name->ndata[name->offsets[n]]; + if (head[0] == DNS_LABELTYPE_BITSTRING && head[1] != 0) { + if (n != 0) { + n--; + curr = &name->ndata[name->offsets[n]]; + if (curr[0] != DNS_LABELTYPE_BITSTRING) + break; + /* + * We have consecutive bitstrings labels, and + * the more significant label ('head') has + * space. + */ + currbits = curr[1]; + if (currbits == 0) + currbits = 256; + currindex = 0; + headbits = head[1]; + if (headbits == 0) + headbits = 256; + headindex = headbits; + count = 256 - headbits; + if (count > currbits) + count = currbits; + headrem = headbits % 8; + if (headrem != 0) + headrem = 8 - headrem; + if (headrem != 0) { + if (headrem > count) + headrem = count; + do { + bit = get_bit(&curr[2], + currindex); + set_bit(&head[2], headindex, + bit); + currindex++; + headindex++; + headbits++; + count--; + headrem--; + } while (headrem != 0); + } + tailindex = 0; + tailbits = 0; + while (count > 0) { + bit = get_bit(&curr[2], currindex); + set_bit(tail, tailindex, bit); + currindex++; + tailindex++; + tailbits++; + count--; + } + newbits = 0; + newindex = 0; + if (currindex < currbits) { + while (currindex < currbits) { + bit = get_bit(&curr[2], + currindex); + set_bit(&curr[2], newindex, + bit); + currindex++; + newindex++; + newbits++; + } + INSIST(newbits < 256); + curr[1] = newbits; + count = newbits / 8; + newrem = newbits % 8; + /* Zero remaining pad bits, if any. */ + if (newrem != 0) { + count++; + newrem = 8 - newrem; + while (newrem > 0) { + set_bit(&curr[2], + newindex, + 0); + newrem--; + newindex++; + } + } + curr += count + 2; + } else { + /* We got rid of curr. */ + name->labels--; + } + /* copy head, then tail, then rest to curr. */ + count = headbits + tailbits; + INSIST(count <= 256); + curr[0] = DNS_LABELTYPE_BITSTRING; + if (count == 256) + curr[1] = 0; + else + curr[1] = count; + curr += 2; + head += 2; + count = headbits / 8; + if (headbits % 8 != 0) + count++; + while (count > 0) { + *curr++ = *head++; + count--; + } + count = tailbits / 8; + if (tailbits % 8 != 0) + count++; + last = tail; + while (count > 0) { + *curr++ = *last++; + count--; + } + last = name->ndata + name->length; + while (head != last) + *curr++ = *head++; + name->length = (curr - name->ndata); + goto again; + } + } + n--; + }; +} diff --git a/lib/dns/result.c b/lib/dns/result.c new file mode 100644 index 0000000000..191b57d253 --- /dev/null +++ b/lib/dns/result.c @@ -0,0 +1,25 @@ + +#include + +static char *text_table[DNS_R_LASTENTRY + 1] = { + "success", /* 0 */ + "out of memory", /* 1 */ + "ran out of space", /* 2 */ + "label too long", /* 3 */ + "bad escape", /* 4 */ + "bad bitstring", /* 5 */ + "bitstring too long", /* 6 */ + "empty label", /* 7 */ + "bad dotted quad", /* 8 */ + "unexpected end of input", /* 9 */ + "not implemented", /* 10 */ +}; + +char * +dns_result_totext(dns_result_t result) { + if (result == DNS_R_UNEXPECTED) + return ("unexpected error"); + if (result > DNS_R_LASTENTRY) + return ("unknown result code"); + return (text_table[result]); +}