/* * rdata.c -- RDATA conversion functions. * * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. * * See LICENSE for the license. * */ #include "config.h" #include #include #include #include #include #include #include #include #include #ifdef HAVE_STRINGS_H #include #endif #include "rdata.h" #include "zonec.h" #include "query.h" #include "dname.h" /* Taken from RFC 4398, section 2.1. */ lookup_table_type dns_certificate_types[] = { /* 0 Reserved */ { 1, "PKIX" }, /* X.509 as per PKIX */ { 2, "SPKI" }, /* SPKI cert */ { 3, "PGP" }, /* OpenPGP packet */ { 4, "IPKIX" }, /* The URL of an X.509 data object */ { 5, "ISPKI" }, /* The URL of an SPKI certificate */ { 6, "IPGP" }, /* The fingerprint and URL of an OpenPGP packet */ { 7, "ACPKIX" }, /* Attribute Certificate */ { 8, "IACPKIX" }, /* The URL of an Attribute Certificate */ { 253, "URI" }, /* URI private */ { 254, "OID" }, /* OID private */ /* 255 Reserved */ /* 256-65279 Available for IANA assignment */ /* 65280-65534 Experimental */ /* 65535 Reserved */ { 0, NULL } }; /* Taken from RFC 2535, section 7. */ lookup_table_type dns_algorithms[] = { { 1, "RSAMD5" }, /* RFC 2537 */ { 2, "DH" }, /* RFC 2539 */ { 3, "DSA" }, /* RFC 2536 */ { 4, "ECC" }, { 5, "RSASHA1" }, /* RFC 3110 */ { 6, "DSA-NSEC3-SHA1" }, /* RFC 5155 */ { 7, "RSASHA1-NSEC3-SHA1" }, /* RFC 5155 */ { 8, "RSASHA256" }, /* RFC 5702 */ { 10, "RSASHA512" }, /* RFC 5702 */ { 12, "ECC-GOST" }, /* RFC 5933 */ { 13, "ECDSAP256SHA256" }, /* RFC 6605 */ { 14, "ECDSAP384SHA384" }, /* RFC 6605 */ { 15, "ED25519" }, /* RFC 8080 */ { 16, "ED448" }, /* RFC 8080 */ { 252, "INDIRECT" }, { 253, "PRIVATEDNS" }, { 254, "PRIVATEOID" }, { 0, NULL } }; /* Print svcparam key without a value */ static int print_svcparam_no_value(struct buffer *output, uint16_t svcparamkey, const uint8_t* data, uint16_t datalen); /* Print svcparam mandatory */ static int print_svcparam_mandatory(struct buffer *output, uint16_t svcparamkey, const uint8_t* data, uint16_t datalen); /* Print svcparam alpn */ static int print_svcparam_alpn(struct buffer *output, uint16_t svcparamkey, const uint8_t* data, uint16_t datalen); /* Print svcparam port */ static int print_svcparam_port(struct buffer *output, uint16_t svcparamkey, const uint8_t* data, uint16_t datalen); /* Print svcparam ipv4hint */ static int print_svcparam_ipv4hint(struct buffer *output, uint16_t svcparamkey, const uint8_t* data, uint16_t datalen); /* Print svcparam ech */ static int print_svcparam_ech(struct buffer *output, uint16_t svcparamkey, const uint8_t* data, uint16_t datalen); /* Print svcparam ipv6hint */ static int print_svcparam_ipv6hint(struct buffer *output, uint16_t svcparamkey, const uint8_t* data, uint16_t datalen); /* Print svcparam dohpath */ static int print_svcparam_dohpath(struct buffer *output, uint16_t svcparamkey, const uint8_t* data, uint16_t datalen); /* Print svcparam tls-supported-groups */ static int print_svcparam_tls_supported_groups(struct buffer *output, uint16_t svcparamkey, const uint8_t* data, uint16_t datalen); static const nsd_svcparam_descriptor_type svcparams[] = { { SVCB_KEY_MANDATORY, "mandatory", print_svcparam_mandatory }, { SVCB_KEY_ALPN, "alpn", print_svcparam_alpn }, { SVCB_KEY_NO_DEFAULT_ALPN, "no-default-alpn", print_svcparam_no_value }, { SVCB_KEY_PORT, "port", print_svcparam_port }, { SVCB_KEY_IPV4HINT, "ipv4hint", print_svcparam_ipv4hint }, { SVCB_KEY_ECH, "ech", print_svcparam_ech }, { SVCB_KEY_IPV6HINT, "ipv6hint", print_svcparam_ipv6hint }, { SVCB_KEY_DOHPATH, "dohpath", print_svcparam_dohpath }, { SVCB_KEY_OHTTP, "ohttp", print_svcparam_no_value }, { SVCB_KEY_TLS_SUPPORTED_GROUPS, "tls-supported-groups", print_svcparam_tls_supported_groups }, }; /* * Print domain name, as example.com. with escapes. * The domain name is wireformat in the rdata. That is a literal dname * in the rdata. * @param output: the string is output here. * @param rdlength: length of rdata. * @param rdata: the rdata. The rdata+*offset is where the field is. * @param offset: the current position on input. The position is updated to * be incremented with the length of rdata that was used. * @return false on failure. */ static int print_name_literal(struct buffer *output, uint16_t rdlength, const uint8_t *rdata, uint16_t *offset) { const uint8_t *name, *label, *limit; assert(rdlength >= *offset); if (rdlength - *offset == 0) return 0; name = rdata + *offset; label = name; limit = rdata + rdlength; if(*label) { do { /* space for labellen, label and a next root label. */ if (label - name > MAXDOMAINLEN-1 || *label > MAXLABELLEN || limit - label < 2 + *label) return 0; label += 1 + *label; } while (*label); } else { /* root domain. */ /* The label is within the rdlength by the checks at start of * the function. */ } buffer_printf(output, "%s", wiredname2str(name)); *offset += (label - name) + 1 /* root label */; return 1; } /* * Print domain name, as example.com. with escapes. * The domain must be a reference in the rdata. That is a stored pointer * to struct domain. * @param output: the string is output here. * @param rdlength: length of rdata. * @param rdata: the rdata. The rdata+*offset is where the field is. * @param offset: the current position on input. The position is updated to * be incremented with the length of rdata that was used. * @return false on failure. */ static int print_domain(struct buffer *output, uint16_t rdlength, const uint8_t *rdata, uint16_t *offset) { const struct dname *dname; struct domain *domain; if(rdlength - *offset < (uint16_t)sizeof(void*)) return 0; memcpy(&domain, rdata+*offset, sizeof(void*)); dname = domain_dname(domain); buffer_printf(output, "%s", dname_to_string(dname, NULL)); *offset += sizeof(void*); return 1; } /* Return length of string or -1 on wireformat error. offset is moved +len. */ static inline int32_t skip_string(struct buffer* output, uint16_t rdlength, uint16_t* offset) { int32_t length; if (rdlength - *offset < 1) return -1; length = buffer_read_u8(output); if (length + 1 > rdlength - *offset) return -1; buffer_skip(output, length); *offset += length + 1; return length + 1; } /* Return length of strings or -1 on wireformat error. offset is moved +len. */ static inline int32_t skip_strings(struct buffer* output, uint16_t rdlength, uint16_t* offset) { int32_t olen = 0; while(*offset < rdlength) { int32_t slen = skip_string(output, rdlength, offset); if(slen < 0) return slen; olen += slen; } return olen; } /* * Print string, as "string" with escapes. * @param output: the string is output here. * @param rdlength: length of rdata. * @param rdata: the rdata. The rdata+*offset is where the field is. * @param offset: the current position on input. The position is updated to * be incremented with the length of rdata that was used. * @return false on failure. */ static int print_string(struct buffer *output, uint16_t rdlength, const uint8_t *rdata, uint16_t *offset) { size_t n; if(rdlength - *offset < 1) return 0; n = rdata[*offset]; if((size_t)rdlength - *offset < 1 + n) return 0; buffer_printf(output, "\""); for (size_t i = 1; i <= n; i++) { char ch = (char) rdata[*offset+i]; if (isprint((unsigned char)ch)) { if (ch == '"' || ch == '\\') { buffer_printf(output, "\\"); } buffer_printf(output, "%c", ch); } else { buffer_printf(output, "\\%03u", (unsigned) rdata[*offset+i]); } } buffer_printf(output, "\""); *offset += 1; *offset += n; return 1; } static int32_t print_text(struct buffer *output, uint16_t rdlength, const uint8_t *rdata, uint16_t *offset) { buffer_printf(output, "\""); for (size_t i = *offset; i < rdlength; ++i) { char ch = (char) rdata[i]; if (isprint((unsigned char)ch)) { if (ch == '"' || ch == '\\') { buffer_printf(output, "\\"); } buffer_printf(output, "%c", ch); } else { buffer_printf(output, "\\%03u", (unsigned) rdata[i]); } } buffer_printf(output, "\""); *offset = rdlength; return 1; } static int print_unquoted(buffer_type *output, uint16_t rdlength, const uint8_t* rdata, uint16_t* offset) { uint8_t len; size_t i; if(rdlength - *offset < 1) return 0; len = rdata[*offset]; if(((size_t)len) + 1 > (size_t)rdlength - *offset) return 0; for (i = 1; i <= (size_t)len; ++i) { char ch = (char) rdata[*offset + i]; if (isprint((unsigned char)ch)) { if (ch == '"' || ch == '\\' || ch == '(' || ch == ')' || ch == '\'' || isspace((unsigned char)ch)) { buffer_printf(output, "\\"); } buffer_printf(output, "%c", ch); } else { buffer_printf(output, "\\%03u", (unsigned) rdata[*offset + i]); } } *offset += 1; *offset += len; return 1; } static int print_unquoteds(buffer_type *output, uint16_t rdlength, const uint8_t* rdata, uint16_t* offset) { while (*offset < rdlength) { if(!print_unquoted(output, rdlength, rdata, offset)) return 0; if(*offset < rdlength) buffer_printf(output, " "); } return 1; } /* * Print IP4 address. * @param output: the string is output here. * @param rdlength: length of rdata. * @param rdata: the rdata. The rdata+*offset is where the field is. * @param offset: the current position on input. The position is updated to * be incremented with the length of rdata that was used. * @return false on failure. */ static int print_ip4(struct buffer *output, size_t rdlength, const uint8_t *rdata, uint16_t *offset) { char str[INET_ADDRSTRLEN + 1]; assert(rdlength >= *offset); if(((size_t)*offset) + 4 > rdlength) return 0; if(!inet_ntop(AF_INET, rdata + *offset, str, sizeof(str))) return 0; buffer_printf(output, "%s", str); *offset += 4; return 1; } /* * Print IP6 address. * @param output: the string is output here. * @param rdlength: length of rdata. * @param rdata: the rdata. The rdata+*offset is where the field is. * @param offset: the current position on input. The position is updated to * be incremented with the length of rdata that was used. * @return false on failure. */ static int print_ip6(struct buffer *output, size_t rdlength, const uint8_t *rdata, uint16_t *offset) { char str[INET6_ADDRSTRLEN + 1]; assert(rdlength >= *offset); if (rdlength - *offset < 16) return 0; if (!inet_ntop(AF_INET6, rdata + *offset, str, sizeof(str))) return 0; buffer_printf(output, "%s", str); *offset += 16; return 1; } /* * Print ilnp64 field. * @param output: the string is output here. * @param rdlength: length of rdata. * @param rdata: the rdata. The rdata+*offset is where the field is. * @param offset: the current position on input. The position is updated to * be incremented with the length of rdata that was used. * @return false on failure. */ static int print_ilnp64(struct buffer *output, uint16_t rdlength, const uint8_t *rdata, uint16_t *offset) { uint16_t a1, a2, a3, a4; assert(rdlength >= *offset); if (rdlength - *offset < 8) return 0; a1 = read_uint16(rdata + *offset); a2 = read_uint16(rdata + *offset + 2); a3 = read_uint16(rdata + *offset + 4); a4 = read_uint16(rdata + *offset + 6); buffer_printf(output, "%.4x:%.4x:%.4x:%.4x", a1, a2, a3, a4); *offset += 8; return 1; } /* * Print certificate type. * @param output: the string is output here. * @param rdlength: length of rdata. * @param rdata: the rdata. The rdata+*offset is where the field is. * @param offset: the current position on input. The position is updated to * be incremented with the length of rdata that was used. * @return false on failure. */ static int print_certificate_type(struct buffer *output, size_t rdlength, const uint8_t *rdata, uint16_t *offset) { uint16_t id; lookup_table_type* type; if (rdlength < *offset || rdlength - *offset < 2) return 0; id = read_uint16(rdata + *offset); type = lookup_by_id(dns_certificate_types, id); if (type) buffer_printf(output, "%s", type->name); else buffer_printf(output, "%u", (unsigned) id); *offset += 2; return 1; } /* * Print time field. * @param output: the string is output here. * @param rdlength: length of rdata. * @param rdata: the rdata. The rdata+*offset is where the field is. * @param offset: the current position on input. The position is updated to * be incremented with the length of rdata that was used. * @return false on failure. */ static int print_time(struct buffer *output, uint16_t rdlength, const uint8_t *rdata, uint16_t *offset) { time_t time; struct tm tmbuf; struct tm* tm; char buf[15]; assert(rdlength >= *offset); if (rdlength - *offset < 4) return 0; time = (time_t)read_uint32(rdata + *offset); tm = gmtime_r(&time, &tmbuf); if (!strftime(buf, sizeof(buf), "%Y%m%d%H%M%S", tm)) return 0; buffer_printf(output, "%s", buf); *offset += 4; return 1; } /* * Print base32 output for a b32 field length uint8_t, like for NSEC3. * @param output: the string is output here. * @param rdlength: length of rdata. * @param rdata: the rdata. The rdata+*offset is where the field is. * @param offset: the current position on input. The position is updated to * be incremented with the length of rdata that was used. * @return false on failure. */ static int print_base32(struct buffer *output, uint16_t rdlength, const uint8_t *rdata, uint16_t *offset) { size_t size; int length; if(rdlength - *offset == 0) return 0; size = rdata[*offset]; if (rdlength - ((size_t)*offset) < 1 + size) return 0; if (size == 0) { buffer_write(output, "-", 1); *offset += 1; return 1; } buffer_reserve(output, size * 2 + 1); length = b32_ntop(rdata + *offset + 1, size, (char *)buffer_current(output), size * 2); if (length == -1) return 0; buffer_skip(output, length); *offset += 1 + size; return 1; } /* * Print base64 output for the remainder of rdata. * @param output: the string is output here. * @param rdlength: length of rdata. * @param rdata: the rdata. The rdata+*offset is where the field is. * @param offset: the current position on input. The position is updated to * be incremented with the length of rdata that was used. * @return false on failure. */ static int print_base64(struct buffer *output, uint16_t rdlength, const uint8_t *rdata, uint16_t *offset) { int length; size_t size = rdlength - *offset; if(size == 0) { /* single zero represents empty buffer */ buffer_write(output, "0", 1); return 1; } buffer_reserve(output, size * 2 + 1); length = b64_ntop(rdata + *offset, size, (char *) buffer_current(output), size * 2); if (length == -1) return 0; buffer_skip(output, length); *offset += size; return 1; } static void buffer_print_hex(buffer_type *output, const uint8_t *data, size_t size) { static const char hexdigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; size_t i; buffer_reserve(output, size * 2); for (i = 0; i < size; ++i) { uint8_t octet = *data++; buffer_write_u8(output, hexdigits[octet >> 4]); buffer_write_u8(output, hexdigits[octet & 0x0f]); } } /* * Print base16 output for the remainder of rdata, in hex lowercase. * @param output: the string is output here. * @param rdlength: length of rdata. * @param rdata: the rdata. The rdata+*offset is where the field is. * @param offset: the current position on input. The position is updated to * be incremented with the length of rdata that was used. * @return false on failure. */ static int print_base16(struct buffer *output, uint16_t rdlength, const uint8_t *rdata, uint16_t *offset) { size_t size = rdlength - *offset; if(size == 0) { /* single zero represents empty buffer, such as CDS deletes */ buffer_write(output, "0", 1); return 1; } else { buffer_print_hex(output, rdata+*offset, size); } *offset += size; return 1; } /* * Print salt, for NSEC3, in hex lowercase. * @param output: the string is output here. * @param rdlength: length of rdata. * @param rdata: the rdata. The rdata+*offset is where the field is. * @param offset: the current position on input. The position is updated to * be incremented with the length of rdata that was used. * @return false on failure. */ static int print_salt(struct buffer *output, uint16_t rdlength, const uint8_t *rdata, uint16_t *offset) { uint8_t length; assert(rdlength >= *offset); if (rdlength - *offset == 0) return 0; length = rdata[*offset]; if (rdlength - *offset < 1 + length) return 0; if (!length) /* NSEC3 salt hex can be empty */ buffer_printf(output, "-"); else buffer_print_hex(output, rdata + *offset + 1, length); *offset += 1 + (uint16_t)length; return 1; } /* Return length of nsec type bitmap or -1 on wireformat error. offset is * moved +len. rdlength is the length of the rdata, offset is offset in it. */ static inline int32_t skip_nsec(struct buffer* packet, uint16_t rdlength, uint16_t *offset) { uint16_t length = 0; uint8_t last_window = 0; while (rdlength - *offset - length > 2) { uint8_t window = buffer_read_u8(packet); uint8_t blocks = buffer_read_u8(packet); if (length > 0 && window <= last_window) return -1; if (!blocks || blocks > 32) return -1; if (rdlength - *offset - length < 2 + blocks) return -1; buffer_skip(packet, blocks); length += 2 + blocks; last_window = window; } *offset += length; if (rdlength != *offset) return -1; return length; } /* * Print nsec type bitmap, for the remainder of rdata. * @param output: the string is output here. * @param rdlength: length of rdata. * @param rdata: the rdata. The rdata+*offset is where the field is. * @param offset: the current position on input. The position is updated to * be incremented with the length of rdata that was used. * @return false on failure. */ static int print_nsec_bitmap(struct buffer *output, uint16_t rdlength, const uint8_t *rdata, uint16_t *offset) { int insert_space = 0; rdata += *offset; while(rdlength - *offset > 0) { uint8_t window, bitmap_size; const uint8_t* bitmap; int i; if(rdlength - *offset < 2) return 0; window = rdata[0]; bitmap_size = rdata[1]; *offset += 2; if(rdlength - *offset < bitmap_size) return 0; bitmap = rdata + 2; rdata += 2; for(i=0; i<((int)bitmap_size)*8; i++) { if (get_bit(bitmap, i)) { buffer_printf(output, "%s%s", insert_space ? " " : "", rrtype_to_string( window * 256 + i)); insert_space = 1; } } rdata += bitmap_size; *offset += bitmap_size; } return 1; } /* If an svcparam must have a value */ static int svcparam_must_have_value(uint16_t svcparamkey) { switch (svcparamkey) { case SVCB_KEY_ALPN: case SVCB_KEY_PORT: case SVCB_KEY_IPV4HINT: case SVCB_KEY_IPV6HINT: case SVCB_KEY_MANDATORY: case SVCB_KEY_DOHPATH: case SVCB_KEY_TLS_SUPPORTED_GROUPS: return 1; default: break; } return 0; } /* If an svcparam must not have a value */ static int svcparam_must_not_have_value(uint16_t svcparamkey) { switch (svcparamkey) { case SVCB_KEY_NO_DEFAULT_ALPN: case SVCB_KEY_OHTTP: return 1; default: break; } return 0; }; /* * Skip over the svcparams in the packet. Moves position. * @param packet: wire packet, position at rdata fields of svcparams. * @param rdlength: remaining rdata length in the packet. * @return 0 on wireformat error. */ static inline int skip_svcparams(struct buffer *packet, uint16_t rdlength) { unsigned pos = 0; uint16_t key, count; while(pos < rdlength) { if(pos+4 > (unsigned)rdlength) return 0; if(!buffer_available(packet, 4)) return 0; key = buffer_read_u16(packet); count = buffer_read_u16(packet); if(count == 0 && svcparam_must_have_value(key)) return 0; if(count != 0 && svcparam_must_not_have_value(key)) return 0; pos += 4; if(pos+count > (unsigned)rdlength) return 0; if(!buffer_available(packet, count)) return 0; buffer_skip(packet, count); pos += count; } return 1; } /* * Print svcparamkey name to the buffer, or unknown as key. * @param output: printed to string. * @param svcparamkey: the key to print. */ static void buffer_print_svcparamkey(buffer_type *output, uint16_t svcparamkey) { if (svcparamkey < sizeof(svcparams)/sizeof(svcparams[0])) buffer_printf(output, "%s", svcparams[svcparamkey].name); else buffer_printf(output, "key%" PRIu16, svcparamkey); } static int print_svcparam_no_value(struct buffer *output, uint16_t svcparamkey, const uint8_t* ATTR_UNUSED(data), uint16_t ATTR_UNUSED(datalen)) { buffer_print_svcparamkey(output, svcparamkey); return 1; } static int print_svcparam_mandatory(struct buffer *output, uint16_t svcparamkey, const uint8_t* data, uint16_t datalen) { assert(datalen > 0); /* Guaranteed by svcparam_print */ if (datalen % sizeof(uint16_t)) return 0; /* wireformat error, val_len must be multiple of shorts */ buffer_print_svcparamkey(output, svcparamkey); buffer_write_u8(output, '='); buffer_print_svcparamkey(output, read_uint16(data)); data += 2; while ((datalen -= sizeof(uint16_t))) { buffer_write_u8(output, ','); buffer_print_svcparamkey(output, read_uint16(data)); data += 2; } return 1; } static int print_svcparam_alpn(struct buffer *output, uint16_t svcparamkey, const uint8_t* data, uint16_t datalen) { uint8_t *dp = (void *)data; assert(datalen > 0); /* Guaranteed by svcparam_print */ buffer_print_svcparamkey(output, svcparamkey); buffer_write_u8(output, '='); buffer_write_u8(output, '"'); while (datalen) { uint8_t i, str_len = *dp++; if (str_len > --datalen) return 0; for (i = 0; i < str_len; i++) { if (dp[i] == '"' || dp[i] == '\\') buffer_printf(output, "\\\\\\%c", dp[i]); else if (dp[i] == ',') buffer_printf(output, "\\\\%c", dp[i]); else if (!isprint(dp[i])) buffer_printf(output, "\\%03u", (unsigned) dp[i]); else buffer_write_u8(output, dp[i]); } dp += str_len; if ((datalen -= str_len)) buffer_write_u8(output, ','); } buffer_write_u8(output, '"'); return 1; } static int print_svcparam_port(struct buffer *output, uint16_t svcparamkey, const uint8_t* data, uint16_t datalen) { if (datalen != 2) return 0; /* wireformat error, a short is 2 bytes */ buffer_print_svcparamkey(output, svcparamkey); buffer_printf(output, "=%d", (int)read_uint16(data)); return 1; } static int print_svcparam_ipv4hint(struct buffer *output, uint16_t svcparamkey, const uint8_t* data, uint16_t datalen) { char ip_str[INET_ADDRSTRLEN + 1]; assert(datalen > 0); /* Guaranteed by svcparam_print */ buffer_print_svcparamkey(output, svcparamkey); if ((datalen % IP4ADDRLEN) == 0) { if (inet_ntop(AF_INET, data, ip_str, sizeof(ip_str)) == NULL) return 0; /* wireformat error, incorrect size or inet family */ buffer_printf(output, "=%s", ip_str); data += IP4ADDRLEN; while ((datalen -= IP4ADDRLEN) > 0) { if (inet_ntop(AF_INET, data, ip_str, sizeof(ip_str)) == NULL) return 0; /* wireformat error, incorrect size or inet family */ buffer_printf(output, ",%s", ip_str); data += IP4ADDRLEN; } return 1; } return 0; } static int print_svcparam_ech(struct buffer *output, uint16_t svcparamkey, const uint8_t* data, uint16_t datalen) { int length; buffer_print_svcparamkey(output, svcparamkey); if(datalen == 0) return 1; buffer_write_u8(output, '='); buffer_reserve(output, datalen * 2 + 1); length = b64_ntop(data, datalen, (char*)buffer_current(output), datalen * 2); if (length > 0) { buffer_skip(output, length); } return length != -1; } static int print_svcparam_ipv6hint(struct buffer *output, uint16_t svcparamkey, const uint8_t* data, uint16_t datalen) { char ip_str[INET6_ADDRSTRLEN + 1]; assert(datalen > 0); /* Guaranteed by svcparam_print */ buffer_print_svcparamkey(output, svcparamkey); if ((datalen % IP6ADDRLEN) == 0) { if (inet_ntop(AF_INET6, data, ip_str, sizeof(ip_str)) == NULL) return 0; /* wireformat error, incorrect size or inet family */ buffer_printf(output, "=%s", ip_str); data += IP6ADDRLEN; while ((datalen -= IP6ADDRLEN) > 0) { if (inet_ntop(AF_INET6, data, ip_str, sizeof(ip_str)) == NULL) return 0; /* wireformat error, incorrect size or inet family */ buffer_printf(output, ",%s", ip_str); data += IP6ADDRLEN; } return 1; } return 0; } static int print_svcparam_dohpath(struct buffer *output, uint16_t svcparamkey, const uint8_t* data, uint16_t datalen) { const uint8_t* dp = data; unsigned i; buffer_print_svcparamkey(output, svcparamkey); buffer_write(output, "=\"", 2); for (i = 0; i < datalen; i++) { if (dp[i] == '"' || dp[i] == '\\') buffer_printf(output, "\\%c", dp[i]); else if (!isprint(dp[i])) buffer_printf(output, "\\%03u", (unsigned) dp[i]); else buffer_write_u8(output, dp[i]); } buffer_write_u8(output, '"'); return 1; } static int print_svcparam_tls_supported_groups(struct buffer *output, uint16_t svcparamkey, const uint8_t* data, uint16_t datalen) { assert(datalen > 0); /* Guaranteed by svcparam_print */ if ((datalen % sizeof(uint16_t)) == 1) return 0; /* A series of uint16_t is an even number of bytes */ buffer_print_svcparamkey(output, svcparamkey); buffer_printf(output, "=%d", (int)read_uint16(data)); data += 2; while ((datalen -= sizeof(uint16_t)) > 0) { buffer_printf(output, ",%d", (int)read_uint16(data)); data += 2; } return 1; } /* * Print svcparam. * @param output: the string is output here. * @param rdlength: length of rdata. * @param rdata: the rdata. The rdata+*offset is where the field is. * @param offset: the current position on input. The position is updated to * be incremented with the length of rdata that was used. * @return false on failure. */ static int print_svcparam(struct buffer *output, uint16_t rdlength, const uint8_t *rdata, uint16_t *offset) { uint16_t key, length; const uint8_t* dp; unsigned i; assert(rdlength >= *offset); if (rdlength - *offset < 4) return 0; key = read_uint16(rdata + *offset); length = read_uint16(rdata + *offset + 2); if (rdlength - *offset < length + 4) return 0; /* wireformat error */ if(length == 0 && svcparam_must_have_value(key)) return 0; if(length != 0 && svcparam_must_not_have_value(key)) return 0; if (key < sizeof(svcparams)/sizeof(svcparams[0])) { if(!svcparams[key].print_rdata(output, key, rdata+*offset+4, length)) return 0; *offset += length+4; return 1; } buffer_printf(output, "key%" PRIu16, key); if (!length) { *offset += 4; return 1; } buffer_write(output, "=\"", 2); dp = rdata + *offset + 4; for (i = 0; i < length; i++) { if (dp[i] == '"' || dp[i] == '\\') buffer_printf(output, "\\%c", dp[i]); else if (!isprint(dp[i])) buffer_printf(output, "\\%03u", (unsigned) dp[i]); else buffer_write_u8(output, dp[i]); } buffer_write_u8(output, '"'); *offset += length + 4; return 1; } static inline int32_t read_rdata(struct domain_table *domains, uint16_t rdlength, struct buffer *packet, struct rr **rr) { if (buffer_remaining(packet) < rdlength) return MALFORMED; if (!(*rr = region_alloc(domains->region, sizeof(**rr) + rdlength))) return TRUNCATED; if(rdlength != 0) buffer_read(packet, (*rr)->rdata, rdlength); (*rr)->rdlength = rdlength; return rdlength; } int32_t read_generic_rdata(struct domain_table *domains, uint16_t rdlength, struct buffer *packet, struct rr **rr) { return read_rdata(domains, rdlength, packet, rr); } void write_generic_rdata(struct query *query, const struct rr *rr) { if(rr->rdlength != 0) buffer_write(query->packet, rr->rdata, rr->rdlength); } int print_generic_rdata(struct buffer *output, const struct rr *rr) { const nsd_type_descriptor_type* descriptor = nsd_type_descriptor(rr->type); return print_unknown_rdata_field(output, descriptor, rr); } int lookup_rdata_field_entry(const nsd_type_descriptor_type* descriptor, size_t index, const rr_type* rr, uint16_t offset, uint16_t* field_len, struct domain** domain) { const nsd_rdata_descriptor_type* field = &descriptor->rdata.fields[index]; if(field->calculate_length) { /* Call field length function. */ int32_t l = field->calculate_length(rr->rdlength, rr->rdata, offset, domain); if(l < 0) return 0; *field_len = l; } else if(field->length >= 0) { *field_len = field->length; *domain = NULL; } else { size_t dlen; int32_t flen; if(offset > rr->rdlength) return 0; /* Handle the specialized value cases. */ switch(field->length) { case RDATA_COMPRESSED_DNAME: case RDATA_UNCOMPRESSED_DNAME: if(rr->rdlength - offset < (uint16_t)sizeof(void*)) return 0; *field_len = sizeof(void*); memcpy(domain, rr->rdata+offset, sizeof(void*)); break; case RDATA_LITERAL_DNAME: dlen = buf_dname_length(rr->rdata+offset, rr->rdlength-offset); if(dlen == 0) return 0; *field_len = dlen; *domain = NULL; break; case RDATA_STRING: case RDATA_BINARY: if(rr->rdlength - offset < 1) return 0; flen = (rr->rdata+offset)[0]; *field_len = flen+1; *domain = NULL; break; case RDATA_IPSECGATEWAY: case RDATA_AMTRELAY_RELAY: return 0; /* Has a callback function. */ case RDATA_REMAINDER: *field_len = rr->rdlength - offset; *domain = NULL; break; default: /* Unknown specialized value. */ return 0; } } if(offset + *field_len > rr->rdlength) return 0; /* The field does not fit in the rdata. */ return 1; } int lookup_rdata_field_entry_uncompressed_wire( const nsd_type_descriptor_type* descriptor, size_t index, const uint8_t* rdata, uint16_t rdlength, uint16_t offset, uint16_t* field_len, struct domain** domain) { const nsd_rdata_descriptor_type* field = &descriptor->rdata.fields[index]; if(field->calculate_length) { /* Call field length function. */ int32_t l = field->calculate_length_uncompressed_wire(rdlength, rdata, offset, domain); if(l < 0) return 0; *field_len = l; } else if(field->length >= 0) { *field_len = field->length; *domain = NULL; } else { size_t dlen; int32_t flen; if(offset > rdlength) return 0; /* Handle the specialized value cases. */ switch(field->length) { case RDATA_COMPRESSED_DNAME: case RDATA_UNCOMPRESSED_DNAME: /* In the case that the field type is of type dname, * since this function deals with uncompressed * wireformat in the rdata buffer, the dname is * going to be there as an uncompressed wireformat * dname, for RDATA_COMPRESSED_DNAME and * RDATA_UNCOMPRESSED_DNAME. */ case RDATA_LITERAL_DNAME: dlen = buf_dname_length(rdata+offset, rdlength-offset); if(dlen == 0) return 0; *field_len = dlen; *domain = NULL; break; case RDATA_STRING: case RDATA_BINARY: if(rdlength - offset < 1) return 0; flen = (rdata+offset)[0]; *field_len = flen+1; *domain = NULL; break; case RDATA_IPSECGATEWAY: case RDATA_AMTRELAY_RELAY: return 0; /* Has a callback function. */ case RDATA_REMAINDER: *field_len = rdlength - offset; *domain = NULL; break; default: /* Unknown specialized value. */ return 0; } } if(offset + *field_len > rdlength) return 0; /* The field does not fit in the rdata. */ return 1; } int32_t rr_calculate_uncompressed_rdata_length(const rr_type* rr) { size_t i; const nsd_type_descriptor_type* descriptor = nsd_type_descriptor(rr->type); uint16_t offset = 0; int32_t result = 0; if(!descriptor->has_references) return rr->rdlength; /* It does not really validate the fields versus the rdlength. */ for(i=0; i < descriptor->rdata.length; i++) { uint16_t field_len; struct domain* domain; if(rr->rdlength == offset && descriptor->rdata.fields[i].is_optional) break; /* There are no more rdata fields. */ if(!lookup_rdata_field_entry(descriptor, i, rr, offset, &field_len, &domain)) return -1; /* malformed rdata buffer */ if(domain != NULL) { /* Handle RDATA_COMPRESSED_DNAME and * RDATA_UNCOMPRESSED_DNAME fields. */ const struct dname* dname = domain_dname(domain); result += dname->name_size; } else { result += field_len; } offset += field_len; } return result; } void rr_write_uncompressed_rdata(const rr_type* rr, uint8_t* buf, size_t len) { size_t i; const nsd_type_descriptor_type* descriptor = nsd_type_descriptor(rr->type); uint16_t offset = 0; /* The offset in rr->rdatas. */ size_t pos = 0; /* The position in buf. */ if(!descriptor->has_references) { /* It does not really validate the fields versus the * rdlength. */ if(rr->rdlength > len) return; /* buffer too small */ memcpy(buf, rr->rdata, rr->rdlength); return; } for(i=0; i < descriptor->rdata.length; i++) { uint16_t field_len; struct domain* domain; if(rr->rdlength == offset && descriptor->rdata.fields[i].is_optional) break; /* There are no more rdata fields. */ if(!lookup_rdata_field_entry(descriptor, i, rr, offset, &field_len, &domain)) return; /* malformed rdata buffer */ if(domain != NULL) { /* Handle RDATA_COMPRESSED_DNAME and * RDATA_UNCOMPRESSED_DNAME fields. */ const struct dname* dname = domain_dname(domain); if(pos + dname->name_size > len) return; /* buffer too small */ memcpy(buf+pos, dname_name(dname), dname->name_size); pos += dname->name_size; } else { if(pos + field_len > len) return; /* buffer too small */ memcpy(buf+pos, rr->rdata+offset, field_len); pos += field_len; } offset += field_len; } } int print_unknown_rdata_field(buffer_type *output, const nsd_type_descriptor_type *descriptor, const rr_type *rr) { size_t i; int32_t size; uint16_t length = 0; if(!descriptor->has_references) { /* There are no references to the domain table, the * rdata can be printed literally. */ buffer_printf(output, "\\# %lu ", (unsigned long)rr->rdlength); buffer_print_hex(output, rr->rdata, rr->rdlength); return 1; } size = rr_calculate_uncompressed_rdata_length(rr); if(size < 0) return 0; buffer_printf(output, "\\# %lu ", (unsigned long) size); for(i=0; i < descriptor->rdata.length; i++) { uint16_t field_len; struct domain* domain; const uint8_t* to_print; size_t to_print_len; if(rr->rdlength == length && descriptor->rdata.fields[i].is_optional) break; /* There are no more rdata fields. */ if(!lookup_rdata_field_entry(descriptor, i, rr, length, &field_len, &domain)) return 0; /* malformed rdata buffer */ if(domain != NULL) { /* Handle RDATA_COMPRESSED_DNAME and * RDATA_UNCOMPRESSED_DNAME fields. */ const struct dname* dname = domain_dname(domain); to_print = dname_name(dname); to_print_len = dname->name_size; } else { to_print = rr->rdata+length; to_print_len = field_len; } buffer_print_hex(output, to_print, to_print_len); length += field_len; } return 1; } int print_unknown_rdata(buffer_type *output, const nsd_type_descriptor_type *descriptor, const rr_type *rr) { buffer_printf(output, "\t"); return print_unknown_rdata_field(output, descriptor, rr); } int32_t read_compressed_name_rdata(struct domain_table *domains, uint16_t rdlength, struct buffer *packet, struct rr **rr) { struct domain *domain; struct dname_buffer dname; size_t size; const size_t mark = buffer_position(packet); if (!dname_make_from_packet_buffered(&dname, packet, 1, 1) || rdlength != buffer_position(packet) - mark) return MALFORMED; size = sizeof(**rr) + sizeof(void*); if (!(*rr = region_alloc(domains->region, size))) return TRUNCATED; domain = domain_table_insert(domains, (void*)&dname); domain->usage++; memcpy((*rr)->rdata, &domain, sizeof(void*)); (*rr)->rdlength = sizeof(void*); return rdlength; } int32_t read_uncompressed_name_rdata(struct domain_table *domains, uint16_t rdlength, struct buffer *packet, struct rr **rr) { struct domain *domain; struct dname_buffer dname; size_t size; const size_t mark = buffer_position(packet); if (!dname_make_from_packet_buffered(&dname, packet, 1 /* Lenient, allows pointers. */, 1) || rdlength != buffer_position(packet)-mark) return MALFORMED; size = sizeof(**rr) + sizeof(void*); if (!(*rr = region_alloc(domains->region, size))) return TRUNCATED; domain = domain_table_insert(domains, (void*)&dname); domain->usage++; memcpy((*rr)->rdata, &domain, sizeof(void*)); (*rr)->rdlength = sizeof(void*); return rdlength; } static void encode_dname(query_type *q, domain_type *domain) { while (domain->parent && query_get_dname_offset(q, domain) == 0) { query_put_dname_offset(q, domain, buffer_position(q->packet)); DEBUG(DEBUG_NAME_COMPRESSION, 2, (LOG_INFO, "dname: %s, number: %lu, offset: %u\n", domain_to_string(domain), (unsigned long) domain->number, query_get_dname_offset(q, domain))); buffer_write(q->packet, dname_name(domain_dname(domain)), label_length(dname_name(domain_dname(domain))) + 1U); domain = domain->parent; } if (domain->parent) { DEBUG(DEBUG_NAME_COMPRESSION, 2, (LOG_INFO, "dname: %s, number: %lu, pointer: %u\n", domain_to_string(domain), (unsigned long) domain->number, query_get_dname_offset(q, domain))); assert(query_get_dname_offset(q, domain) <= MAX_COMPRESSION_OFFSET); buffer_write_u16(q->packet, 0xc000 | query_get_dname_offset(q, domain)); } else { buffer_write_u8(q->packet, 0); } } void write_compressed_name_rdata(struct query *query, const struct rr *rr) { struct domain *domain; assert(rr->rdlength == sizeof(void*)); memcpy(&domain, rr->rdata, sizeof(void*)); encode_dname(query, domain); } void write_uncompressed_name_rdata(struct query *query, const struct rr *rr) { const struct dname *dname; struct domain *domain; assert(rr->rdlength >= sizeof(void*)); memcpy(&domain, rr->rdata, sizeof(void*)); dname = domain_dname(domain); buffer_write(query->packet, dname_name(dname), dname->name_size); } int print_name_rdata(struct buffer *output, const struct rr *rr) { uint16_t length = 0; assert(rr->rdlength == sizeof(void*)); /* This prints a reference to a name stored as a pointer. */ return print_domain(output, rr->rdlength, rr->rdata, &length); } int32_t read_a_rdata(struct domain_table *domains, uint16_t rdlength, struct buffer *packet, struct rr **rr) { if (rdlength != 4) return MALFORMED; return read_rdata(domains, rdlength, packet, rr); } int print_a_rdata(struct buffer *output, const struct rr *rr) { uint16_t length = 0; assert(rr->rdlength == 4); return print_ip4(output, rr->rdlength, rr->rdata, &length); } int32_t read_soa_rdata(struct domain_table *domains, uint16_t rdlength, struct buffer *packet, struct rr **rr) { struct domain *primary_domain, *mailbox_domain; struct dname_buffer primary, mailbox; size_t size; /* domain + domain + uint32 + uint32 + uint32 + uint32 + uint32 */ const size_t mark = buffer_position(packet); if (!dname_make_from_packet_buffered(&primary, packet, 1, 1) || !dname_make_from_packet_buffered(&mailbox, packet, 1, 1) || rdlength != (buffer_position(packet) - mark) + 20) return MALFORMED; size = sizeof(**rr) + 2 * sizeof(void*) + 20; if (!(*rr = region_alloc(domains->region, size))) return TRUNCATED; primary_domain = domain_table_insert(domains, (void*)&primary); primary_domain->usage++; mailbox_domain = domain_table_insert(domains, (void*)&mailbox); mailbox_domain->usage++; memcpy((*rr)->rdata, &primary_domain, sizeof(void*)); memcpy((*rr)->rdata + sizeof(void*), &mailbox_domain, sizeof(void*)); buffer_read(packet, (*rr)->rdata + 2 * sizeof(void*), 20); (*rr)->rdlength = 2 * sizeof(void*) + 20; return rdlength; } void write_soa_rdata(struct query *query, const struct rr *rr) { struct domain *primary, *mailbox; /* domain + domain + uint32 + uint32 + uint32 + uint32 + uint32 */ assert(rr->rdlength == 2 * sizeof(void*) + 20); memcpy(&primary, rr->rdata, sizeof(void*)); memcpy(&mailbox, rr->rdata + sizeof(void*), sizeof(void*)); encode_dname(query, primary); encode_dname(query, mailbox); buffer_write(query->packet, rr->rdata + (2 * sizeof(void*)), 20); } int print_soa_rdata(struct buffer *output, const struct rr *rr) { uint16_t length = 0; uint32_t serial, refresh, retry, expire, minimum; assert(rr->rdlength == 2 * sizeof(void*) + 20); if (!print_domain(output, rr->rdlength, rr->rdata, &length)) return 0; buffer_printf(output, " "); if (!print_domain(output, rr->rdlength, rr->rdata, &length)) return 0; assert(length == 2 * sizeof(void*)); serial = read_uint32(rr->rdata + length); refresh = read_uint32(rr->rdata + length + 4); retry = read_uint32(rr->rdata + length + 8); expire = read_uint32(rr->rdata + length + 12); minimum = read_uint32(rr->rdata + length + 16); buffer_printf( output, " %" PRIu32 " %" PRIu32 " %" PRIu32 " %" PRIu32 " %" PRIu32, serial, refresh, retry, expire, minimum); return 1; } int print_soa_rdata_twoline(struct buffer *output, const struct rr *rr) { uint16_t length = 0; uint32_t serial, refresh, retry, expire, minimum; assert(rr->rdlength == 2 * sizeof(void*) + 20); if (!print_domain(output, rr->rdlength, rr->rdata, &length)) return 0; buffer_printf(output, " "); if (!print_domain(output, rr->rdlength, rr->rdata, &length)) return 0; assert(length == 2 * sizeof(void*)); serial = read_uint32(rr->rdata + length); refresh = read_uint32(rr->rdata + length + 4); retry = read_uint32(rr->rdata + length + 8); expire = read_uint32(rr->rdata + length + 12); minimum = read_uint32(rr->rdata + length + 16); buffer_printf( output, " (\n\t\t%" PRIu32 " %" PRIu32 " %" PRIu32 " %" PRIu32 " %" PRIu32 " )", serial, refresh, retry, expire, minimum); return 1; } int32_t read_wks_rdata(struct domain_table *domains, uint16_t rdlength, struct buffer *packet, struct rr **rr) { if (rdlength < 5) return MALFORMED; return read_rdata(domains, rdlength, packet, rr); } /* * Print protocol and service numbers rather than names for Well-Know Services * (WKS) RRs. WKS RRs are deprecated, though not technically, and should not * be used. The parser supports tcp/udp for protocols and a small subset of * services because getprotobyname and/or getservbyname are marked MT-Unsafe * and locale. getprotobyname_r and getservbyname_r exist on some platforms, * but are still marked locale (meaning the locale object is used without * synchonization, which is a problem for a library). Failure to load a zone * on a primary server because of an unknown protocol or service name is * acceptable as the operator can opt to use the numeric value. Failure to * load a zone on a secondary server is problematic because "unsupported" * protocols and services might be written. Print the numeric value for * maximum compatibility. * * (see simdzone/generic/wks.h for details). */ int print_wks_rdata(struct buffer *output, const struct rr *rr) { uint16_t length = 0; uint8_t protocol; int bits; const uint8_t* bitmap; if(rr->rdlength < 5) return 0; if (!print_ip4(output, rr->rdlength, rr->rdata, &length)) return 0; buffer_printf(output, " "); protocol = rr->rdata[4]; if(protocol == 6) buffer_printf(output, "tcp"); else if(protocol == 17) buffer_printf(output, "udp"); else buffer_printf(output, "%" PRIu8, protocol); bits = (rr->rdlength - 5) * 8; bitmap = rr->rdata + 5; for (int service = 0; service < bits; service++) { if (get_bit(bitmap, service)) buffer_printf(output, " %d", service); } return 1; } int32_t read_hinfo_rdata(struct domain_table *domains, uint16_t rdlength, struct buffer *packet, struct rr **rr) { uint16_t length = 0; const size_t mark = buffer_position(packet); if(!buffer_available(packet, rdlength)) return MALFORMED; if (skip_string(packet, rdlength, &length) < 0 || skip_string(packet, rdlength, &length) < 0 || rdlength != length) return MALFORMED; buffer_set_position(packet, mark); return read_rdata(domains, rdlength, packet, rr); } int print_hinfo_rdata(struct buffer *output, const struct rr *rr) { uint16_t length = 0; if (!print_string(output, rr->rdlength, rr->rdata, &length)) return 0; buffer_printf(output, " "); if (!print_string(output, rr->rdlength, rr->rdata, &length)) return 0; if(rr->rdlength != length) return 0; return 1; } int32_t read_minfo_rdata(struct domain_table *domains, uint16_t rdlength, struct buffer *packet, struct rr **rr) { struct domain *rmailbx_domain, *emailbx_domain; struct dname_buffer rmailbx, emailbx; size_t size; const size_t mark = buffer_position(packet); if (buffer_remaining(packet) < rdlength || !dname_make_from_packet_buffered(&rmailbx, packet, 1, 1) || !dname_make_from_packet_buffered(&emailbx, packet, 1, 1) || rdlength != buffer_position(packet)-mark) return MALFORMED; size = sizeof(**rr) + 2 * sizeof(void*); if (!(*rr = region_alloc(domains->region, size))) return TRUNCATED; rmailbx_domain = domain_table_insert(domains, (void*)&rmailbx); rmailbx_domain->usage++; emailbx_domain = domain_table_insert(domains, (void*)&emailbx); emailbx_domain->usage++; memcpy((*rr)->rdata, &rmailbx_domain, sizeof(void*)); memcpy((*rr)->rdata + sizeof(void*), &emailbx_domain, sizeof(void*)); (*rr)->rdlength = 2 * sizeof(void*); return rdlength; } void write_minfo_rdata(struct query *query, const struct rr *rr) { struct domain *rmailbx, *emailbx; assert(rr->rdlength == 2 * sizeof(void*)); memcpy(&rmailbx, rr->rdata, sizeof(void*)); memcpy(&emailbx, rr->rdata + sizeof(void*), sizeof(void*)); encode_dname(query, rmailbx); encode_dname(query, emailbx); } int print_minfo_rdata(struct buffer *output, const struct rr *rr) { uint16_t length = 0; assert(rr->rdlength == 2 * sizeof(void*)); if (!print_domain(output, rr->rdlength, rr->rdata, &length)) return 0; buffer_printf(output, " "); if (!print_domain(output, rr->rdlength, rr->rdata, &length)) return 0; assert(rr->rdlength == length); return 1; } int32_t read_mx_rdata(struct domain_table *domains, uint16_t rdlength, struct buffer *packet, struct rr **rr) { struct domain *domain; struct dname_buffer exchange; size_t size; /* short + name */ const size_t mark = buffer_position(packet); if (buffer_remaining(packet) < rdlength || rdlength < 2) return MALFORMED; buffer_skip(packet, 2); if (!dname_make_from_packet_buffered(&exchange, packet, 1, 1)) return MALFORMED; if (rdlength != buffer_position(packet)-mark) return MALFORMED; size = sizeof(**rr) + 2 + sizeof(void*); if (!(*rr = region_alloc(domains->region, size))) return -1; domain = domain_table_insert(domains, (void*)&exchange); domain->usage++; buffer_read_at(packet, mark, (*rr)->rdata, 2); memcpy((*rr)->rdata + 2, &domain, sizeof(void*)); (*rr)->rdlength = 2 + sizeof(void*); return rdlength; } void write_mx_rdata(struct query *query, const struct rr *rr) { struct domain *domain; assert(rr->rdlength == 2 + sizeof(void*)); memcpy(&domain, rr->rdata + 2, sizeof(void*)); buffer_write(query->packet, rr->rdata, 2); encode_dname(query, domain); } int print_mx_rdata(struct buffer *output, const struct rr *rr) { uint16_t length = 2; assert(rr->rdlength > length); buffer_printf(output, "%" PRIu16 " ", read_uint16(rr->rdata)); if (!print_domain(output, rr->rdlength, rr->rdata, &length)) return 0; assert(rr->rdlength == length); return 1; } int32_t read_txt_rdata(struct domain_table *owners, uint16_t rdlength, struct buffer *packet, struct rr **rr) { uint16_t length = 0; const size_t mark = buffer_position(packet); if(!buffer_available(packet, rdlength)) return MALFORMED; if (skip_strings(packet, rdlength, &length) < 0 || rdlength != length) return MALFORMED; buffer_set_position(packet, mark); return read_rdata(owners, rdlength, packet, rr); } int print_txt_rdata(struct buffer *output, const struct rr *rr) { uint16_t length = 0; if (length < rr->rdlength) { if (!print_string(output, rr->rdlength, rr->rdata, &length)) return 0; while (length < rr->rdlength) { buffer_printf(output, " "); if (!print_string(output, rr->rdlength, rr->rdata, &length)) return 0; } } return 1; } int32_t read_rp_rdata(struct domain_table *domains, uint16_t rdlength, struct buffer *packet, struct rr **rr) { struct domain *mbox_domain, *txt_domain; struct dname_buffer mbox, txt; size_t size; const size_t mark = buffer_position(packet); if (buffer_remaining(packet) < rdlength || !dname_make_from_packet_buffered(&mbox, packet, 1 /*lenient*/, 1) || !dname_make_from_packet_buffered(&txt, packet, 1 /*lenient*/, 1) || rdlength != buffer_position(packet)-mark) return MALFORMED; size = sizeof(**rr) + 2 * sizeof(void*); if (!(*rr = region_alloc(domains->region, size))) return TRUNCATED; mbox_domain = domain_table_insert(domains, (void*)&mbox); mbox_domain->usage++; txt_domain = domain_table_insert(domains, (void*)&txt); txt_domain->usage++; memcpy((*rr)->rdata, &mbox_domain, sizeof(void*)); memcpy((*rr)->rdata + sizeof(void*), &txt_domain, sizeof(void*)); (*rr)->rdlength = 2 * sizeof(void*); return rdlength; } void write_rp_rdata(struct query *query, const struct rr *rr) { struct domain *mbox_domain, *txt_domain; const struct dname *mbox, *txt; assert(rr->rdlength == 2 * sizeof(void*)); memcpy(&mbox_domain, rr->rdata, sizeof(void*)); memcpy(&txt_domain, rr->rdata + sizeof(void*), sizeof(void*)); mbox = domain_dname(mbox_domain); txt = domain_dname(txt_domain); buffer_write(query->packet, dname_name(mbox), mbox->name_size); buffer_write(query->packet, dname_name(txt), txt->name_size); } int print_rp_rdata(struct buffer *output, const struct rr *rr) { uint16_t length = 0; if(!print_domain(output, rr->rdlength, rr->rdata, &length)) return 0; buffer_printf(output, " "); if(!print_domain(output, rr->rdlength, rr->rdata, &length)) return 0; assert(rr->rdlength == length); return 1; } int32_t read_afsdb_rdata(struct domain_table *domains, uint16_t rdlength, struct buffer *packet, struct rr **rr) { struct domain *domain; struct dname_buffer hostname; size_t size; /* short + uncompressed name */ const size_t mark = buffer_position(packet); if (buffer_remaining(packet) < rdlength || rdlength < 2) return MALFORMED; buffer_skip(packet, 2); if (!dname_make_from_packet_buffered(&hostname, packet, 1 /*lenient*/, 1) || rdlength != buffer_position(packet)-mark) return MALFORMED; size = sizeof(**rr) + 2 + sizeof(void*); if (!(*rr = region_alloc(domains->region, size))) return TRUNCATED; domain = domain_table_insert(domains, (void*)&hostname); domain->usage++; buffer_read_at(packet, mark, (*rr)->rdata, 2); memcpy((*rr)->rdata + 2, &domain, sizeof(void*)); (*rr)->rdlength = 2 + sizeof(void*); return rdlength; } void write_afsdb_rdata(struct query *query, const struct rr *rr) { struct domain *domain; const struct dname *dname; assert(rr->rdlength == 2 + sizeof(void*)); memcpy(&domain, rr->rdata + 2, sizeof(void*)); dname = domain_dname(domain); buffer_write(query->packet, rr->rdata, 2); buffer_write(query->packet, dname_name(dname), dname->name_size); } int print_afsdb_rdata(struct buffer *output, const struct rr *rr) { uint16_t subtype, length=2; assert(rr->rdlength == 2 + sizeof(void*)); subtype = read_uint16(rr->rdata); buffer_printf(output, "%" PRIu16 " ", subtype); if(!print_domain(output, rr->rdlength, rr->rdata, &length)) return 0; return 1; } int32_t read_x25_rdata(struct domain_table *domains, uint16_t rdlength, struct buffer *packet, struct rr **rr) { uint16_t length = 0; const size_t mark = buffer_position(packet); if(!buffer_available(packet, rdlength)) return MALFORMED; if (skip_string(packet, rdlength, &length) < 0 || rdlength != length) return MALFORMED; buffer_set_position(packet, mark); return read_rdata(domains, rdlength, packet, rr); } int print_x25_rdata(struct buffer *output, const struct rr *rr) { uint16_t length = 0; if (!print_string(output, rr->rdlength, rr->rdata, &length)) return 0; if(rr->rdlength != length) return 0; return 1; } int32_t read_isdn_rdata(struct domain_table *domains, uint16_t rdlength, struct buffer *packet, struct rr **rr) { uint16_t length = 0; const size_t mark = buffer_position(packet); if(!buffer_available(packet, rdlength)) return MALFORMED; if (skip_string(packet, rdlength, &length) < 0) return MALFORMED; if(rdlength > length) { /* Optional subaddress field is present. */ if (skip_string(packet, rdlength, &length) < 0 || rdlength != length) return MALFORMED; } buffer_set_position(packet, mark); return read_rdata(domains, rdlength, packet, rr); } int print_isdn_rdata(struct buffer *output, const struct rr *rr) { uint16_t length = 0; if (!print_string(output, rr->rdlength, rr->rdata, &length)) return 0; if(rr->rdlength > length) { /* Optional subaddress field is present. */ buffer_printf(output, " "); if (!print_string(output, rr->rdlength, rr->rdata, &length)) return 0; } if(rr->rdlength != length) return 0; return 1; } int32_t read_rt_rdata(struct domain_table *domains, uint16_t rdlength, struct buffer *packet, struct rr **rr) { struct domain *domain; struct dname_buffer dname; size_t size; const size_t mark = buffer_position(packet); if(!buffer_available(packet, rdlength)) return MALFORMED; if (rdlength < 2) return MALFORMED; buffer_skip(packet, 2); if (!dname_make_from_packet_buffered(&dname, packet, 1 /*lenient*/, 1)) return MALFORMED; if (rdlength != buffer_position(packet)-mark) return MALFORMED; size = sizeof(**rr) + 2 + sizeof(void*); if (!(*rr = region_alloc(domains->region, size))) return -1; domain = domain_table_insert(domains, (void*)&dname); domain->usage++; buffer_read_at(packet, mark, (*rr)->rdata, 2); memcpy((*rr)->rdata + 2, &domain, sizeof(void*)); (*rr)->rdlength = 2 + sizeof(void*); return rdlength; } void write_rt_rdata(struct query *query, const struct rr *rr) { struct domain *domain; const struct dname *dname; assert(rr->rdlength == 2 + sizeof(void*)); memcpy(&domain, rr->rdata + 2, sizeof(void*)); dname = domain_dname(domain); buffer_write(query->packet, rr->rdata, 2); buffer_write(query->packet, dname_name(dname), dname->name_size); } int print_nsap_rdata(struct buffer *output, const struct rr *rr) { buffer_printf(output, "0x"); buffer_print_hex(output, rr->rdata, rr->rdlength); return 1; } int print_nsap_ptr_rdata(struct buffer *output, const struct rr *rr) { uint16_t length = 0; if(!print_unquoted(output, rr->rdlength, rr->rdata, &length)) return 0; if(rr->rdlength != length) return 0; return 1; } int print_key_rdata(struct buffer *output, const struct rr *rr) { uint16_t length = 4; if(rr->rdlength < length) return 0; buffer_printf( output, "%" PRIu16 " %" PRIu8 " %" PRIu8 " ", read_uint16(rr->rdata), rr->rdata[2], rr->rdata[3]); if (!print_base64(output, rr->rdlength, rr->rdata, &length)) return 0; if(rr->rdlength != length) return 0; return 1; } int32_t read_px_rdata(struct domain_table *domains, uint16_t rdlength, struct buffer *packet, struct rr **rr) { struct domain *map822_domain, *mapx400_domain; struct dname_buffer map822, mapx400; size_t size; const size_t mark = buffer_position(packet); /* short + uncompressed name + uncompressed name */ if (buffer_remaining(packet) < rdlength || rdlength < 2) return MALFORMED; buffer_skip(packet, 2); if(!dname_make_from_packet_buffered(&map822, packet, 1 /*lenient*/, 1) || !dname_make_from_packet_buffered(&mapx400, packet, 1 /*lenient*/, 1) || rdlength != buffer_position(packet) - mark) return MALFORMED; size = sizeof(**rr) + 2 + 2*sizeof(void*); if (!(*rr = region_alloc(domains->region, size))) return TRUNCATED; map822_domain = domain_table_insert(domains, (void*)&map822); map822_domain->usage++; mapx400_domain = domain_table_insert(domains, (void*)&mapx400); mapx400_domain->usage++; buffer_read_at(packet, mark, (*rr)->rdata, 2); memcpy((*rr)->rdata+2, &map822_domain, sizeof(void*)); memcpy((*rr)->rdata+2+sizeof(void*), &mapx400_domain, sizeof(void*)); (*rr)->rdlength = 2 + 2*sizeof(void*); return rdlength; } void write_px_rdata(struct query *query, const struct rr *rr) { struct domain *map822_domain, *mapx400_domain; const struct dname *map822, *mapx400; memcpy(&map822_domain, rr->rdata + 2, sizeof(void*)); memcpy(&mapx400_domain, rr->rdata + 2 + sizeof(void*), sizeof(void*)); map822 = domain_dname(map822_domain); mapx400 = domain_dname(mapx400_domain); buffer_write(query->packet, rr->rdata, 2); buffer_write(query->packet, dname_name(map822), map822->name_size); buffer_write(query->packet, dname_name(mapx400), mapx400->name_size); } int print_px_rdata(struct buffer *output, const struct rr *rr) { uint16_t length = 2; assert(rr->rdlength > 3); buffer_printf(output, "%" PRIu16 " ", read_uint16(rr->rdata)); if (!print_domain(output, rr->rdlength, rr->rdata, &length)) return 0; buffer_printf(output, " "); if (!print_domain(output, rr->rdlength, rr->rdata, &length)) return 0; assert(rr->rdlength == length); return 1; } int print_gpos_rdata(struct buffer *output, const struct rr *rr) { uint16_t length = 0; if(!print_unquoted(output, rr->rdlength, rr->rdata, &length)) return 0; buffer_printf(output, " "); if(!print_unquoted(output, rr->rdlength, rr->rdata, &length)) return 0; buffer_printf(output, " "); if(!print_unquoted(output, rr->rdlength, rr->rdata, &length)) return 0; if(rr->rdlength != length) return 0; return 1; } int32_t read_aaaa_rdata(struct domain_table *domains, uint16_t rdlength, struct buffer *packet, struct rr **rr) { if (rdlength != 16) return MALFORMED; return read_rdata(domains, rdlength, packet, rr); } int print_aaaa_rdata(struct buffer *output, const struct rr *rr) { uint16_t length = 0; assert(rr->rdlength == 16); return print_ip6(output, rr->rdlength, rr->rdata, &length); } int32_t read_loc_rdata(struct domain_table *domains, uint16_t rdlength, struct buffer *packet, struct rr **rr) { size_t mark; uint8_t version; uint16_t size_version_0; if(!buffer_available(packet, rdlength)) return MALFORMED; /* version (byte) */ if (rdlength < 1) return MALFORMED; /* version (byte) + size (byte) * + horizontal precision (byte) + vertical precision (byte) * + latitude (uint32) + longitude (uint32) + altitude (uint32) */ mark = buffer_position(packet); version = buffer_read_u8_at(packet, mark + 0); size_version_0 = 16u; if (version == 0 && rdlength != size_version_0) return MALFORMED; return read_rdata(domains, rdlength, packet, rr); } /* Print the cm for LOC. */ static void loc_cm_print(struct buffer* output, uint8_t mantissa, uint8_t exponent) { uint8_t i; /* is it 0. ? */ if(exponent < 2) { if(exponent == 1) mantissa *= 10; buffer_printf(output, "0.%02ld", (long)mantissa); return; } /* always */ buffer_printf(output, "%d", (int)mantissa); for(i=0; irdlength < 16) return 0; version = rr->rdata[0]; if(version != 0) return 0; size = rr->rdata[1]; horizontal_precision = rr->rdata[2]; vertical_precision = rr->rdata[3]; latitude = read_uint32(rr->rdata+4); longitude = read_uint32(rr->rdata+8); altitude = read_uint32(rr->rdata+12); if (latitude > equator) { northerness = 'N'; latitude = latitude - equator; } else { northerness = 'S'; latitude = equator - latitude; } h = latitude / (1000 * 60 * 60); latitude = latitude % (1000 * 60 * 60); m = latitude / (1000 * 60); latitude = latitude % (1000 * 60); s = (double) latitude / 1000.0; buffer_printf(output, "%02u %02u %06.3f %c ", h, m, s, northerness); if (longitude > equator) { easterness = 'E'; longitude = longitude - equator; } else { easterness = 'W'; longitude = equator - longitude; } h = longitude / (1000 * 60 * 60); longitude = longitude % (1000 * 60 * 60); m = longitude / (1000 * 60); longitude = longitude % (1000 * 60); s = (double) longitude / (1000.0); buffer_printf(output, "%02u %02u %06.3f %c ", h, m, s, easterness); s = ((double) altitude) / 100; s -= 100000; if(altitude%100 != 0) buffer_printf(output, "%.2f", s); else buffer_printf(output, "%.0f", s); buffer_printf(output, "m "); loc_cm_print(output, (size & 0xf0) >> 4, size & 0x0f); buffer_printf(output, "m "); loc_cm_print(output, (horizontal_precision & 0xf0) >> 4, horizontal_precision & 0x0f); buffer_printf(output, "m "); loc_cm_print(output, (vertical_precision & 0xf0) >> 4, vertical_precision & 0x0f); buffer_printf(output, "m"); return 1; } int32_t read_nxt_rdata(struct domain_table *domains, uint16_t rdlength, struct buffer *packet, struct rr **rr) { struct domain *domain; struct dname_buffer dname; size_t size; uint16_t bitmap_size; const size_t mark = buffer_position(packet); /* name + nxt */ if(!buffer_available(packet, rdlength)) return MALFORMED; if (!dname_make_from_packet_buffered(&dname, packet, 1, 1)) return MALFORMED; if(rdlength < buffer_position(packet) - mark) return MALFORMED; bitmap_size = rdlength - (buffer_position(packet) - mark); size = sizeof(**rr) + sizeof(void*) + bitmap_size; if (!(*rr = region_alloc(domains->region, size))) return TRUNCATED; domain = domain_table_insert(domains, (void*)&dname); domain->usage++; memcpy((*rr)->rdata, &domain, sizeof(void*)); if(bitmap_size != 0) buffer_read(packet, (*rr)->rdata + sizeof(void*), bitmap_size); (*rr)->rdlength = sizeof(void*) + bitmap_size; return rdlength; } void write_nxt_rdata(struct query *query, const struct rr *rr) { struct domain *domain; const struct dname *dname; assert(rr->rdlength >= sizeof(void*)); memcpy(&domain, rr->rdata, sizeof(void*)); dname = domain_dname(domain); buffer_write(query->packet, dname_name(dname), dname->name_size); if(rr->rdlength - sizeof(void*) != 0) buffer_write(query->packet, rr->rdata + sizeof(void*), rr->rdlength - sizeof(void*)); } int print_nxt_rdata(struct buffer *output, const struct rr *rr) { uint16_t length = 0; int bitmap_size; const uint8_t* bitmap; assert(rr->rdlength > sizeof(void*)); if (!print_domain(output, rr->rdlength, rr->rdata, &length)) return 0; bitmap_size = rr->rdlength - length; bitmap = rr->rdata + length; for (int type = 0; type < bitmap_size * 8; type++) { if (get_bit(bitmap, type)) { buffer_printf(output, " %s", rrtype_to_string(type)); } } return 1; } int print_eid_rdata(struct buffer *output, const struct rr *rr) { uint16_t length = 0; if(!print_base16(output, rr->rdlength, rr->rdata, &length)) return 0; if(rr->rdlength != length) return 0; return 1; } int print_nimloc_rdata(struct buffer *output, const struct rr *rr) { uint16_t length = 0; if(!print_base16(output, rr->rdlength, rr->rdata, &length)) return 0; if(rr->rdlength != length) return 0; return 1; } int32_t read_srv_rdata(struct domain_table *domains, uint16_t rdlength, struct buffer *packet, struct rr **rr) { struct domain *domain; struct dname_buffer dname; size_t size; const size_t mark = buffer_position(packet); /* short + short + short + name */ if (buffer_remaining(packet) < rdlength || rdlength < 6) return MALFORMED; buffer_skip(packet, 6); if (!dname_make_from_packet_buffered(&dname, packet, 1 /* lenient */, 1) || rdlength != buffer_position(packet)-mark) return MALFORMED; size = sizeof(**rr) + 6 + sizeof(void*); if (!(*rr = region_alloc(domains->region, size))) return TRUNCATED; domain = domain_table_insert(domains, (void*)&dname); domain->usage++; buffer_read_at(packet, mark, (*rr)->rdata, 6); memcpy((*rr)->rdata + 6, &domain, sizeof(void*)); (*rr)->rdlength = 6 + sizeof(void*); return rdlength; } void write_srv_rdata(struct query *query, const struct rr *rr) { struct domain *domain; const struct dname *dname; assert(rr->rdlength == 6 + sizeof(void*)); memcpy(&domain, rr->rdata + 6, sizeof(void*)); dname = domain_dname(domain); buffer_write(query->packet, rr->rdata, 6); buffer_write(query->packet, dname_name(dname), dname->name_size); } int print_srv_rdata(struct buffer *output, const struct rr *rr) { uint16_t length = 6; assert(rr->rdlength > length); buffer_printf( output, "%" PRIu16 " %" PRIu16 " %" PRIu16 " ", read_uint16(rr->rdata), read_uint16(rr->rdata+2), read_uint16(rr->rdata+4)); if (!print_domain(output, rr->rdlength, rr->rdata, &length)) return 0; assert(rr->rdlength == length); return 1; } int print_atma_rdata(struct buffer *output, const struct rr *rr) { uint8_t format; if(rr->rdlength < 1) return 0; format = rr->rdata[0]; if(format == 0) { /* AESA format (ATM End System Address). */ uint16_t length = 1; if (!print_base16(output, rr->rdlength, rr->rdata, &length)) return 0; if(rr->rdlength != length) return 0; return 1; } else if(format == 1) { /* E.164 format. */ /* '+' and then digits '0'-'9' from the rdata string. */ buffer_printf(output, "+"); for (size_t i = 1; i < rr->rdlength; i++) { char ch = (char)rr->rdata[i]; if(!isdigit((unsigned char)ch)) return 0; buffer_printf(output, "%c", ch); } return 1; } /* Unknown format. */ return 0; } int32_t read_naptr_rdata(struct domain_table *domains, uint16_t rdlength, struct buffer *packet, struct rr **rr) { struct domain *domain; struct dname_buffer dname; uint16_t length = 4; size_t size; const size_t mark = buffer_position(packet); /* short + short + text + text + text + name */ if (buffer_remaining(packet) < rdlength || rdlength < length) return MALFORMED; buffer_skip(packet, 4); if(skip_string(packet, rdlength, &length) < 0 || skip_string(packet, rdlength, &length) < 0 || skip_string(packet, rdlength, &length) < 0 || !dname_make_from_packet_buffered(&dname, packet, 1, 1) || rdlength != buffer_position(packet) - mark) return MALFORMED; size = sizeof(**rr) + length + sizeof(void*); if (!(*rr = region_alloc(domains->region, size))) return TRUNCATED; domain = domain_table_insert(domains, (void*)&dname); domain->usage++; buffer_read_at(packet, mark, (*rr)->rdata, length); memcpy((*rr)->rdata + length, &domain, sizeof(void*)); (*rr)->rdlength = length + sizeof(void*); return rdlength; } void write_naptr_rdata(struct query *query, const struct rr *rr) { struct domain *domain; const struct dname *dname; uint16_t length; /* short + short + string + string + string + uncompressed name */ assert(rr->rdlength >= 7 + sizeof(void*)); length = rr->rdlength - sizeof(void*); memcpy(&domain, rr->rdata + length, sizeof(void*)); dname = domain_dname(domain); buffer_write(query->packet, rr->rdata, length); buffer_write(query->packet, dname_name(dname), dname->name_size); } int print_naptr_rdata(struct buffer *output, const struct rr *rr) { uint16_t length = 4; assert(rr->rdlength >= 7 + sizeof(void*)); buffer_printf( output, "%" PRIu16 " %" PRIu16 " ", read_uint16(rr->rdata), read_uint16(rr->rdata+2)); if (!print_string(output, rr->rdlength, rr->rdata, &length)) return 0; buffer_printf(output, " "); if (!print_string(output, rr->rdlength, rr->rdata, &length)) return 0; buffer_printf(output, " "); if (!print_string(output, rr->rdlength, rr->rdata, &length)) return 0; buffer_printf(output, " "); if (!print_domain(output, rr->rdlength, rr->rdata, &length)) return 0; assert(rr->rdlength == length); return 1; } int32_t read_kx_rdata(struct domain_table *domains, uint16_t rdlength, struct buffer *packet, struct rr **rr) { struct domain *domain; struct dname_buffer dname; const size_t mark = buffer_position(packet); size_t size; /* short + uncompressed name */ if(buffer_remaining(packet) < rdlength || rdlength < 2) return MALFORMED; buffer_skip(packet, 2); if(!dname_make_from_packet_buffered(&dname, packet, 1 /* lenient */, 1) || rdlength != buffer_position(packet) - mark) return MALFORMED; size = sizeof(**rr) + 2 + sizeof(void*); if (!(*rr = region_alloc(domains->region, size))) return TRUNCATED; domain = domain_table_insert(domains, (void*)&dname); domain->usage++; buffer_read_at(packet, mark, (*rr)->rdata, 2); memcpy((*rr)->rdata + 2, &domain, sizeof(void*)); (*rr)->rdlength = 2 + sizeof(void*); return rdlength; } void write_kx_rdata(struct query *query, const struct rr *rr) { struct domain *domain; const struct dname *dname; /* short + uncompressed name */ assert(rr->rdlength == 2 + sizeof(void*)); memcpy(&domain, rr->rdata + 2, sizeof(void*)); dname = domain_dname(domain); buffer_write(query->packet, rr->rdata, 2); buffer_write(query->packet, dname_name(dname), dname->name_size); } int32_t read_cert_rdata(struct domain_table *domains, uint16_t rdlength, struct buffer *packet, struct rr **rr) { /* short + short + byte + binary */ if (rdlength < 5) return MALFORMED; return read_rdata(domains, rdlength, packet, rr); } int print_cert_rdata(struct buffer *output, const struct rr *rr) { uint16_t length = 0; if(rr->rdlength < 5) return 0; if(!print_certificate_type(output, rr->rdlength, rr->rdata, &length)) return 0; buffer_printf(output, " %" PRIu16 " %" PRIu8 " ", read_uint16(rr->rdata+2), rr->rdata[4]); length += 3; if (!print_base64(output, rr->rdlength, rr->rdata, &length)) return 0; if(rr->rdlength != length) return 0; return 1; } int print_sink_rdata(struct buffer *output, const struct rr *rr) { uint16_t length = 2; if(rr->rdlength < 2) return 0; buffer_printf(output, "%" PRIu8 " %" PRIu8 " ", rr->rdata[0], rr->rdata[1]); if (!print_base64(output, rr->rdlength, rr->rdata, &length)) return 0; if(rr->rdlength != length) return 0; return 1; } int32_t read_apl_rdata(struct domain_table *domains, uint16_t rdlength, struct buffer *packet, struct rr **rr) { uint16_t length = 0; const uint8_t *rdata = buffer_current(packet); if (buffer_remaining(packet) < rdlength) return MALFORMED; while (rdlength - length >= 4) { uint8_t afdlength = rdata[length + 3] & APL_LENGTH_MASK; if (rdlength - (length + 4) < afdlength) return MALFORMED; length += 4 + afdlength; } if (length != rdlength) return MALFORMED; return read_rdata(domains, rdlength, packet, rr); } /* * Print one APL field. * @param output: string is printed to the buffer. * @param rdlength: length of rdata. * @param rdata: the rdata * @param offset: on input the current position, adjusted on output to * increment for the rdata used. * @return false on failure. */ static int print_apl(struct buffer *output, size_t rdlength, const uint8_t *rdata, uint16_t *offset) { size_t size = rdlength - *offset; uint16_t address_family; uint8_t prefix, length, negated; int af; char text_address[INET6_ADDRSTRLEN + 1]; uint8_t address[16]; if (size < 4) return 0; address_family = read_uint16(rdata + *offset); prefix = rdata[*offset + 2]; length = rdata[*offset + 3] & APL_LENGTH_MASK; negated = rdata[*offset + 3] & APL_NEGATION_MASK; af = -1; switch (address_family) { case 1: af = AF_INET; break; case 2: af = AF_INET6; break; } if (af == -1 || size - 4 < length) return 0; memset(address, 0, sizeof(address)); memmove(address, rdata + *offset + 4, length); if (!inet_ntop(af, address, text_address, sizeof(text_address))) return 0; buffer_printf( output, "%s%" PRIu16 ":%s/%" PRIu8, (negated ? "!" : ""), address_family, text_address, prefix); *offset += 4 + length; return 1; } int print_apl_rdata(struct buffer *output, const struct rr *rr) { uint16_t length = 0; while (length < rr->rdlength) { if(length != 0) buffer_printf(output, " "); if (!print_apl(output, rr->rdlength, rr->rdata, &length)) return 0; } if(rr->rdlength != length) return 0; return 1; } int32_t read_ds_rdata(struct domain_table *domains, uint16_t rdlength, struct buffer *packet, struct rr **rr) { /* short + byte + byte + binary */ if (rdlength < 4) return MALFORMED; return read_rdata(domains, rdlength, packet, rr); } int print_ds_rdata(struct buffer *output, const struct rr *rr) { uint16_t length = 4; if(rr->rdlength < length) return 0; buffer_printf( output, "%" PRIu16 " %" PRIu8 " %" PRIu8 " ", read_uint16(rr->rdata), rr->rdata[2], rr->rdata[3]); if (!print_base16(output, rr->rdlength, rr->rdata, &length)) return 0; if(rr->rdlength != length) return 0; return 1; } int32_t read_sshfp_rdata(struct domain_table *domains, uint16_t rdlength, struct buffer *packet, struct rr **rr) { /* byte + byte + binary */ if (rdlength < 2) return MALFORMED; return read_rdata(domains, rdlength, packet, rr); } int print_sshfp_rdata(struct buffer *output, const struct rr *rr) { uint16_t length = 2; uint8_t algorithm, ftype; if(rr->rdlength < length) return 0; algorithm = rr->rdata[0]; ftype = rr->rdata[1]; buffer_printf(output, "%" PRIu8 " %" PRIu8 " ", algorithm, ftype); if (!print_base16(output, rr->rdlength, rr->rdata, &length)) return 0; if(rr->rdlength != length) return 0; return 1; } int32_t read_ipseckey_rdata(struct domain_table *domains, uint16_t rdlength, struct buffer *packet, struct rr **rr) { struct dname_buffer gateway; const uint8_t *gateway_rdata; uint8_t gateway_length = 0; const size_t mark = buffer_position(packet); uint16_t keylen; /* byte + byte + byte + gateway + binary */ if (buffer_remaining(packet) < rdlength || rdlength < 3) return MALFORMED; buffer_skip(packet, 3); switch (buffer_read_u8_at(packet, mark + 1)) { case IPSECKEY_NOGATEWAY: gateway_length = 0; gateway_rdata = NULL; break; case IPSECKEY_IP4: gateway_length = 4; gateway_rdata = buffer_current(packet); if (rdlength < 3 + gateway_length) return MALFORMED; buffer_skip(packet, gateway_length); break; case IPSECKEY_IP6: gateway_length = 16; gateway_rdata = buffer_current(packet); if (rdlength < 3 + gateway_length) return MALFORMED; buffer_skip(packet, gateway_length); break; case IPSECKEY_DNAME: /* The dname is stored as literal dname. On the wire * skip possibly compressed format, in the rdata there * is an uncompressed wire format. */ if (!dname_make_from_packet_buffered(&gateway, packet, 1 /* lenient */, 1)) return MALFORMED; if(rdlength < buffer_position(packet) - mark) return MALFORMED; gateway_length = gateway.dname.name_size; gateway_rdata = dname_name((void*)&gateway); break; default: return MALFORMED; } keylen = rdlength - (buffer_position(packet)-mark); if (!(*rr = region_alloc(domains->region, sizeof(**rr) + 3 + gateway_length + keylen))) return TRUNCATED; buffer_read_at(packet, mark, (*rr)->rdata, 3); if(gateway_rdata) memcpy((*rr)->rdata + 3, gateway_rdata, gateway_length); if(keylen != 0) buffer_read(packet, (*rr)->rdata + 3 + gateway_length, keylen); (*rr)->rdlength = 3 + gateway_length + keylen; return rdlength; } int print_ipseckey_rdata(struct buffer *output, const struct rr *rr) { uint16_t length = 3; uint8_t gateway_type; if(rr->rdlength < length) return 0; buffer_printf( output, "%" PRIu8 " %" PRIu8 " %" PRIu8 " ", rr->rdata[0], rr->rdata[1], rr->rdata[2]); gateway_type = rr->rdata[1]; switch (gateway_type) { case IPSECKEY_NOGATEWAY: buffer_printf(output, "."); break; case IPSECKEY_IP4: if (!print_ip4(output, rr->rdlength, rr->rdata, &length)) return 0; break; case IPSECKEY_IP6: if (!print_ip6(output, rr->rdlength, rr->rdata, &length)) return 0; break; case IPSECKEY_DNAME: if (!print_name_literal(output, rr->rdlength, rr->rdata, &length)) return 0; break; default: return 0; } if(rr->rdlength > length) { /* Print key field in base64. */ buffer_printf(output, " "); if (!print_base64(output, rr->rdlength, rr->rdata, &length)) return 0; } if(rr->rdlength != length) return 0; return 1; } int32_t ipseckey_gateway_length(uint16_t rdlength, const uint8_t *rdata, uint16_t offset, struct domain** domain) { /* The ipseckey gateway length depends only on earlier bytes, so both * the in-memory and uncompressed wireformat refer to the same * earlier bytes. Also the domain name is stored literally, so it * does not need to return a reference. */ uint8_t gateway_type; *domain = NULL; /* Calculate the gateway length, based on the gateway type. * That is stored in an earlier field. */ if(rdlength < 3 || offset < 3) return -1; /* too short */ gateway_type = rdata[1]; switch(gateway_type) { case IPSECKEY_NOGATEWAY: return 0; case IPSECKEY_IP4: return 4; case IPSECKEY_IP6: return 16; case IPSECKEY_DNAME: return buf_dname_length(rdata+offset, rdlength-offset); default: /* Unknown gateway type. */ break; } return -1; } int32_t read_rrsig_rdata(struct domain_table *domains, uint16_t rdlength, struct buffer *packet, struct rr **rr) { struct dname_buffer signer; const size_t mark = buffer_position(packet); uint16_t memrdlen, b64len; /* short + byte + byte + uint32 + uint32 + uint32 + short */ if (buffer_remaining(packet) < rdlength || rdlength < 18) return MALFORMED; buffer_skip(packet, 18); if (!dname_make_from_packet_buffered(&signer, packet, 1 /* lenient */, 1)) return MALFORMED; if (rdlength < buffer_position(packet) - mark) return MALFORMED; memrdlen = 18 + signer.dname.name_size; b64len = rdlength - (buffer_position(packet) - mark); if (!(*rr = region_alloc(domains->region, sizeof(**rr) + memrdlen + b64len))) return TRUNCATED; buffer_read_at(packet, mark, (*rr)->rdata, 18); memcpy((*rr)->rdata + 18, dname_name((void*)&signer), signer.dname.name_size); if(b64len != 0) buffer_read(packet, (*rr)->rdata+memrdlen, b64len); (*rr)->rdlength = memrdlen + b64len; return rdlength; } int print_rrsig_rdata(struct buffer *output, const struct rr *rr) { uint16_t length = 8; if(rr->rdlength < 18) return 0; buffer_printf( output, "%s %" PRIu8 " %" PRIu8 " %" PRIu32 " ", rrtype_to_string(read_uint16(rr->rdata)), rr->rdata[2], rr->rdata[3], read_uint32(rr->rdata+4)); if (!print_time(output, rr->rdlength, rr->rdata, &length)) return 0; buffer_printf(output, " "); if (!print_time(output, rr->rdlength, rr->rdata, &length)) return 0; buffer_printf(output, " %" PRIu16 " ", read_uint16(rr->rdata+length)); length += 2; if (!print_name_literal(output, rr->rdlength, rr->rdata, &length)) return 0; buffer_printf(output, " "); if (!print_base64(output, rr->rdlength, rr->rdata, &length)) return 0; if(rr->rdlength != length) return 0; return 1; } int32_t read_nsec_rdata(struct domain_table *domains, uint16_t rdlength, struct buffer *packet, struct rr **rr) { struct dname_buffer next; uint16_t length, memrdlen, bitmaplen; size_t bitmapmark; const size_t mark = buffer_position(packet); /* uncompressed name + nsec */ if(buffer_remaining(packet) < rdlength || !dname_make_from_packet_buffered(&next, packet, 1 /* lenient */, 1) || rdlength < buffer_position(packet) - mark) return MALFORMED; bitmapmark = buffer_position(packet); length = bitmapmark - mark; if (skip_nsec(packet, rdlength, &length) < 0 || rdlength != length) return MALFORMED; bitmaplen = buffer_position(packet) - bitmapmark; memrdlen = next.dname.name_size + bitmaplen; if (!(*rr = region_alloc(domains->region, sizeof(**rr) + memrdlen))) return TRUNCATED; memcpy((*rr)->rdata, dname_name((void*)&next), next.dname.name_size); if(bitmaplen != 0) buffer_read_at(packet, bitmapmark, (*rr)->rdata + next.dname.name_size, bitmaplen); (*rr)->rdlength = memrdlen; return rdlength; } int print_nsec_rdata(struct buffer *output, const struct rr *rr) { uint16_t length = 0; if (!print_name_literal(output, rr->rdlength, rr->rdata, &length)) return 0; buffer_printf(output, " "); if (!print_nsec_bitmap(output, rr->rdlength, rr->rdata, &length)) return 0; if(rr->rdlength != length) return 0; return 1; } int32_t read_dnskey_rdata(struct domain_table *domains, uint16_t rdlength, struct buffer *packet, struct rr **rr) { /* short + byte + byte + binary of remainder */ if (rdlength < 4) return MALFORMED; return read_rdata(domains, rdlength, packet, rr); } int print_dnskey_rdata(struct buffer *output, const struct rr *rr) { uint16_t length = 4; if(rr->rdlength < length) return 0; buffer_printf( output, "%" PRIu16 " %" PRIu8 " %" PRIu8 " ", read_uint16(rr->rdata), rr->rdata[2], rr->rdata[3]); if (!print_base64(output, rr->rdlength, rr->rdata, &length)) return 0; if(rr->rdlength != length) return 0; return 1; } int32_t read_dhcid_rdata(struct domain_table *domains, uint16_t rdlength, struct buffer *packet, struct rr **rr) { /* short + byte + digest */ if (rdlength < 3) return MALFORMED; return read_rdata(domains, rdlength, packet, rr); } int print_dhcid_rdata(struct buffer *output, const struct rr *rr) { uint16_t length = 0; if (!print_base64(output, rr->rdlength, rr->rdata, &length)) return 0; if(rr->rdlength != length) return 0; return 1; } int32_t read_nsec3_rdata(struct domain_table *domains, uint16_t rdlength, struct buffer *packet, struct rr **rr) { uint16_t length = 4; /* byte + byte + short + string + string + binary */ const size_t mark = buffer_position(packet); if (buffer_remaining(packet) < rdlength || rdlength < length) return MALFORMED; buffer_skip(packet, length); if (skip_string(packet, rdlength, &length) < 0 || skip_string(packet, rdlength, &length) < 0 || skip_nsec(packet, rdlength, &length) < 0 || rdlength != length) return MALFORMED; buffer_set_position(packet, mark); return read_rdata(domains, rdlength, packet, rr); } int print_nsec3_rdata(struct buffer *output, const struct rr *rr) { uint16_t length = 4; if(rr->rdlength < length) return 0; buffer_printf( output, "%" PRIu8 " %" PRIu8 " %" PRIu16 " ", rr->rdata[0], rr->rdata[1], read_uint16(rr->rdata + 2)); if (!print_salt(output, rr->rdlength, rr->rdata, &length)) return 0; buffer_printf(output, " "); if (!print_base32(output, rr->rdlength, rr->rdata, &length)) return 0; buffer_printf(output, " "); if (!print_nsec_bitmap(output, rr->rdlength, rr->rdata, &length)) return 0; if(rr->rdlength != length) return 0; return 1; } int32_t read_nsec3param_rdata(struct domain_table *domains, uint16_t rdlength, struct buffer *packet, struct rr **rr) { uint16_t length = 4; /* byte + byte + short + string */ const size_t mark = buffer_position(packet); if (buffer_remaining(packet) < rdlength || rdlength < length) return MALFORMED; buffer_skip(packet, length); if (skip_string(packet, rdlength, &length) < 0 || rdlength != length) return MALFORMED; buffer_set_position(packet, mark); return read_rdata(domains, rdlength, packet, rr); } int print_nsec3param_rdata(struct buffer *output, const struct rr *rr) { uint16_t length = 4; if(rr->rdlength < length) return 0; buffer_printf( output, "%" PRIu8 " %" PRIu8 " %" PRIu16 " ", rr->rdata[0], rr->rdata[1], read_uint16(rr->rdata + 2)); if (!print_salt(output, rr->rdlength, rr->rdata, &length)) return 0; if(rr->rdlength != length) return 0; return 1; } int32_t read_tlsa_rdata(struct domain_table *domains, uint16_t rdlength, struct buffer *packet, struct rr **rr) { /* byte + byte + byte + binary */ if (rdlength < 3) return MALFORMED; return read_rdata(domains, rdlength, packet, rr); } int print_tlsa_rdata(struct buffer *output, const struct rr *rr) { uint16_t length = 3; if(rr->rdlength < length) return 0; buffer_printf( output, "%" PRIu8 " %" PRIu8 " %" PRIu8 " ", rr->rdata[0], rr->rdata[1], rr->rdata[2]); if (!print_base16(output, rr->rdlength, rr->rdata, &length)) return 0; if(rr->rdlength != length) return 0; return 1; } int32_t read_hip_rdata(struct domain_table *domains, uint16_t rdlength, struct buffer *packet, struct rr **rr) { /* byte (hit length) + byte (PK algorithm) + short (PK length) + * HIT(hex) + pubkey(base64) + rendezvous servers(literal dnames) */ if (rdlength < 4) return MALFORMED; return read_rdata(domains, rdlength, packet, rr); } int print_hip_rdata(struct buffer *output, const struct rr *rr) { /* byte (hit length) + byte (PK algorithm) + short (PK length) + * HIT(hex) + pubkey(base64) + rendezvous servers(literal dnames) */ uint8_t hit_length, pk_algorithm; uint16_t pk_length; uint16_t length = 4; if(rr->rdlength < length) return 0; hit_length = rr->rdata[0]; pk_algorithm = rr->rdata[1]; pk_length = read_uint16(rr->rdata+2); buffer_printf(output, "%" PRIu8 " ", pk_algorithm); if(!print_base16(output, length+hit_length, rr->rdata, &length)) return 0; buffer_printf(output, " "); if(!print_base64(output, length+pk_length, rr->rdata, &length)) return 0; while(length < rr->rdlength) { buffer_printf(output, " "); if(!print_name_literal(output, rr->rdlength, rr->rdata, &length)) return 0; } if(rr->rdlength != length) return 0; return 1; } int32_t read_rkey_rdata(struct domain_table *domains, uint16_t rdlength, struct buffer *packet, struct rr **rr) { /* short + byte + byte + binary of remainder */ if (rdlength < 4) return MALFORMED; return read_rdata(domains, rdlength, packet, rr); } int print_rkey_rdata(struct buffer *output, const struct rr *rr) { uint16_t length = 4; if(rr->rdlength < length) return 0; buffer_printf( output, "%" PRIu16 " %" PRIu8 " %" PRIu8 " ", read_uint16(rr->rdata), rr->rdata[2], rr->rdata[3]); if (!print_base64(output, rr->rdlength, rr->rdata, &length)) return 0; if(rr->rdlength != length) return 0; return 1; } int32_t read_talink_rdata(struct domain_table *domains, uint16_t rdlength, struct buffer *packet, struct rr **rr) { struct dname_buffer prev, next; const size_t mark = buffer_position(packet); if(!buffer_available(packet, rdlength)) return MALFORMED; if(!dname_make_from_packet_buffered(&prev, packet, 1 /* lenient */, 1)) return MALFORMED; if(buffer_position(packet)-mark > rdlength) return MALFORMED; if(!dname_make_from_packet_buffered(&next, packet, 1 /* lenient */, 1)) return MALFORMED; if(buffer_position(packet)-mark != rdlength) return MALFORMED; if (!(*rr = region_alloc(domains->region, sizeof(**rr) + prev.dname.name_size + next.dname.name_size))) return TRUNCATED; memcpy((*rr)->rdata, dname_name((void*)&prev), prev.dname.name_size); memcpy((*rr)->rdata + prev.dname.name_size, dname_name((void*)&next), next.dname.name_size); (*rr)->rdlength = prev.dname.name_size + next.dname.name_size; return rdlength; } int print_talink_rdata(struct buffer *output, const struct rr *rr) { uint16_t length = 0; if(!print_name_literal(output, rr->rdlength, rr->rdata, &length)) return 0; buffer_printf(output, " "); if(!print_name_literal(output, rr->rdlength, rr->rdata, &length)) return 0; if(rr->rdlength != length) return 0; return 1; } int print_openpgpkey_rdata(struct buffer *output, const struct rr *rr) { uint16_t length = 0; if (!print_base64(output, rr->rdlength, rr->rdata, &length)) return 0; if(rr->rdlength != length) return 0; return 1; } int32_t read_csync_rdata(struct domain_table *domains, uint16_t rdlength, struct buffer *packet, struct rr **rr) { /* long + short + binary bitmap in remainder */ if (rdlength < 6) return MALFORMED; return read_rdata(domains, rdlength, packet, rr); } int print_csync_rdata(struct buffer *output, const struct rr *rr) { uint16_t length = 6; if(rr->rdlength < length) return 0; buffer_printf( output, "%" PRIu32 " %" PRIu16 " ", read_uint32(rr->rdata), read_uint16(rr->rdata + 4)); if (!print_nsec_bitmap(output, rr->rdlength, rr->rdata, &length)) return 0; if(rr->rdlength != length) return 0; return 1; } int32_t read_zonemd_rdata(struct domain_table *domains, uint16_t rdlength, struct buffer *packet, struct rr **rr) { /* long + byte + byte + binary */ if (rdlength < 6) return MALFORMED; return read_rdata(domains, rdlength, packet, rr); } int print_zonemd_rdata(struct buffer *output, const struct rr *rr) { uint16_t length = 6; if(rr->rdlength < length) return 0; buffer_printf( output, "%" PRIu32 " %" PRIu8 " %" PRIu8 " ", read_uint32(rr->rdata), rr->rdata[4], rr->rdata[5]); if (!print_base16(output, rr->rdlength, rr->rdata, &length)) return 0; if(rr->rdlength != length) return 0; return 1; } int32_t read_svcb_rdata(struct domain_table *domains, uint16_t rdlength, struct buffer *packet, struct rr **rr) { struct domain *domain; struct dname_buffer target; uint16_t length = 2, svcparams_length = 0; uint16_t size; const size_t mark = buffer_position(packet); /* short + name + svc_params */ if (buffer_remaining(packet) < rdlength || rdlength < length) return MALFORMED; buffer_skip(packet, length); if (!dname_make_from_packet_buffered(&target, packet, 1 /* lenient */, 1)) return MALFORMED; if(rdlength < buffer_position(packet) - mark) return MALFORMED; length = buffer_position(packet)-mark; /* For secondary, the server should accept the wireformat more * leniently. So this check is skipped. */ /* if(!skip_svcparams(packet, rdlength-length)) return MALFORMED; if(rdlength < buffer_position(packet) - mark) return MALFORMED; */ svcparams_length = rdlength - length; buffer_skip(packet, svcparams_length); size = sizeof(**rr) + 2 + sizeof(void*) + svcparams_length; if (!(*rr = region_alloc(domains->region, size))) return TRUNCATED; domain = domain_table_insert(domains, (void*)&target); domain->usage++; buffer_read_at(packet, mark, (*rr)->rdata, 2); memcpy((*rr)->rdata + 2, &domain, sizeof(void*)); if(svcparams_length != 0) buffer_read_at(packet, mark + length, (*rr)->rdata + 2 + sizeof(void*), svcparams_length); (*rr)->rdlength = 2 + sizeof(void*) + svcparams_length; return rdlength; } void write_svcb_rdata(struct query *query, const struct rr *rr) { struct domain *domain; const struct dname *target; uint8_t length; assert(rr->rdlength >= 2 + sizeof(void*)); memcpy(&domain, rr->rdata + 2, sizeof(void*)); target = domain_dname(domain); buffer_write(query->packet, rr->rdata, 2); buffer_write(query->packet, dname_name(target), target->name_size); length = 2 + sizeof(void*); if(rr->rdlength > length) buffer_write(query->packet, rr->rdata + length, rr->rdlength - length); } int print_svcb_rdata(struct buffer *output, const struct rr *rr) { uint16_t length = 2; assert(rr->rdlength > length); /* It has 2+sizeof(void*) at least. */ buffer_printf(output, "%" PRIu16 " ", read_uint16(rr->rdata)); if (!print_domain(output, rr->rdlength, rr->rdata, &length)) return 0; while (length < rr->rdlength) { buffer_printf(output, " "); if (!print_svcparam(output, rr->rdlength, rr->rdata, &length)) return 0; } if(rr->rdlength != length) return 0; return 1; } int32_t read_dsync_rdata(struct domain_table *domains, uint16_t rdlength, struct buffer *packet, struct rr **rr) { struct dname_buffer target; uint16_t length = 5; const size_t mark = buffer_position(packet); /* type + byte + short + literaldname */ if (buffer_remaining(packet) < rdlength || rdlength < length) return MALFORMED; buffer_skip(packet, length); if(!dname_make_from_packet_buffered(&target, packet, 1 /* lenient */, 1) || rdlength != buffer_position(packet) - mark) return MALFORMED; length += target.dname.name_size; if (!(*rr = region_alloc(domains->region, sizeof(**rr)+length))) return TRUNCATED; buffer_read_at(packet, mark, (*rr)->rdata, 5); memcpy((*rr)->rdata + 5, dname_name((void*)&target), target.dname.name_size); (*rr)->rdlength = length; return rdlength; } int print_dsync_rdata(struct buffer *output, const struct rr *rr) { uint16_t length = 5; if(rr->rdlength < length) return 0; buffer_printf(output, "%s %" PRIu8 " %" PRIu16 " ", rrtype_to_string(read_uint16(rr->rdata)), rr->rdata[2], read_uint16(rr->rdata+3)); if(!print_name_literal(output, rr->rdlength, rr->rdata, &length)) return 0; if(rr->rdlength != length) return 0; return 1; } int32_t read_nid_rdata(struct domain_table *domains, uint16_t rdlength, struct buffer *packet, struct rr **rr) { if (rdlength != 10) return MALFORMED; return read_rdata(domains, rdlength, packet, rr); } int print_nid_rdata(struct buffer *output, const struct rr *rr) { uint16_t length = 2; if(rr->rdlength != 10) return 0; buffer_printf(output, "%" PRIu16 " ", read_uint16(rr->rdata)); if (!print_ilnp64(output, rr->rdlength, rr->rdata, &length)) return 0; if(rr->rdlength != length) return 0; return 1; } int32_t read_l32_rdata(struct domain_table *domains, uint16_t rdlength, struct buffer *packet, struct rr **rr) { if (rdlength != 6) return MALFORMED; return read_rdata(domains, rdlength, packet, rr); } int print_l32_rdata(struct buffer *output, const struct rr *rr) { uint16_t length = 2; if(rr->rdlength != 6) return 0; buffer_printf(output, "%" PRIu16 " ", read_uint16(rr->rdata)); if (!print_ip4(output, rr->rdlength, rr->rdata, &length)) return 0; if(rr->rdlength != length) return 0; return 1; } int32_t read_l64_rdata(struct domain_table *domains, uint16_t rdlength, struct buffer *packet, struct rr **rr) { if (rdlength != 10) return MALFORMED; return read_rdata(domains, rdlength, packet, rr); } int print_l64_rdata(struct buffer *output, const struct rr *rr) { uint16_t length = 2; if(rr->rdlength != 10) return 0; buffer_printf(output, "%" PRIu16 " ", read_uint16(rr->rdata)); if (!print_ilnp64(output, rr->rdlength, rr->rdata, &length)) return 0; if(rr->rdlength != length) return 0; return 1; } int32_t read_lp_rdata(struct domain_table *domains, uint16_t rdlength, struct buffer *packet, struct rr **rr) { struct domain *domain; struct dname_buffer target; size_t size; /* short + name */ const size_t mark = buffer_position(packet); if (buffer_remaining(packet) < rdlength || rdlength < 2) return MALFORMED; buffer_skip(packet, 2); if (!dname_make_from_packet_buffered(&target, packet, 1 /* lenient */, 1) || rdlength != buffer_position(packet) - mark) return MALFORMED; size = sizeof(**rr) + 2 + sizeof(void*); if (!(*rr = region_alloc(domains->region, size))) return TRUNCATED; domain = domain_table_insert(domains, (void*)&target); domain->usage++; buffer_read_at(packet, mark, (*rr)->rdata, 2); memcpy((*rr)->rdata + 2, &domain, sizeof(void*)); (*rr)->rdlength = 2 + sizeof(void*); return rdlength; } void write_lp_rdata(struct query *query, const struct rr *rr) { struct domain *domain; const struct dname *dname; /* short + uncompressed name */ assert(rr->rdlength == 2 + sizeof(void*)); memcpy(&domain, rr->rdata + 2, sizeof(void*)); dname = domain_dname(domain); buffer_write(query->packet, rr->rdata, 2); buffer_write(query->packet, dname_name(dname), dname->name_size); } int print_lp_rdata(struct buffer *output, const struct rr *rr) { uint16_t length = 2; assert(rr->rdlength == 2 + sizeof(void*)); buffer_printf(output, "%" PRIu16 " ", read_uint16(rr->rdata)); if (!print_domain(output, rr->rdlength, rr->rdata, &length)) return 0; assert(rr->rdlength == length); return 1; } int32_t read_eui48_rdata(struct domain_table *domains, uint16_t rdlength, struct buffer *packet, struct rr **rr) { if (rdlength != 6) return MALFORMED; return read_rdata(domains, rdlength, packet, rr); } int print_eui48_rdata(struct buffer *output, const struct rr *rr) { const uint8_t *x = rr->rdata; if(rr->rdlength != 6) return 0; buffer_printf(output, "%.2x-%.2x-%.2x-%.2x-%.2x-%.2x", x[0], x[1], x[2], x[3], x[4], x[5]); return 1; } int32_t read_eui64_rdata(struct domain_table *domains, uint16_t rdlength, struct buffer *packet, struct rr **rr) { if (rdlength != 8) return MALFORMED; return read_rdata(domains, rdlength, packet, rr); } int print_eui64_rdata(struct buffer *output, const struct rr *rr) { const uint8_t *x = rr->rdata; if(rr->rdlength != 8) return 0; buffer_printf(output, "%.2x-%.2x-%.2x-%.2x-%.2x-%.2x-%.2x-%.2x", x[0], x[1], x[2], x[3], x[4], x[5], x[6], x[7]); return 1; } int32_t read_uri_rdata(struct domain_table *domains, uint16_t rdlength, struct buffer *packet, struct rr **rr) { /* short + short + long string */ if (rdlength < 4) return MALFORMED; return read_rdata(domains, rdlength, packet, rr); } int print_uri_rdata(struct buffer *output, const struct rr *rr) { uint16_t length = 4; if(rr->rdlength < length) return 0; buffer_printf( output, "%" PRIu16 " %" PRIu16 " ", read_uint16(rr->rdata), read_uint16(rr->rdata + 2)); if(!print_text(output, rr->rdlength, rr->rdata, &length)) return 0; if(rr->rdlength != length) return 0; return 1; } int print_resinfo_rdata(struct buffer *output, const struct rr *rr) { uint16_t length = 0; if(!print_unquoteds(output, rr->rdlength, rr->rdata, &length)) return 0; if(rr->rdlength != length) return 0; return 1; } int32_t read_caa_rdata(struct domain_table *domains, uint16_t rdlength, struct buffer *packet, struct rr **rr) { const size_t mark = buffer_position(packet); uint16_t length = 1; /* byte + string + long string */ if (buffer_remaining(packet) < rdlength || rdlength < 2) return MALFORMED; buffer_skip(packet, 1); if (skip_string(packet, rdlength, &length) < 0 || rdlength < length) return MALFORMED; buffer_set_position(packet, mark); return read_rdata(domains, rdlength, packet, rr); } int print_caa_rdata(struct buffer *output, const struct rr *rr) { uint16_t length = 1; if(rr->rdlength < 2) return 0; buffer_printf(output, "%" PRIu8 " ", rr->rdata[0]); length = 2 + rr->rdata[1]; if (rr->rdlength < length) return 0; for (uint16_t i = 2; i < length; ++i) { char ch = (char) rr->rdata[i]; if (isdigit((unsigned char)ch) || islower((unsigned char)ch)) buffer_printf(output, "%c", ch); else return 0; } buffer_printf(output, " "); if (!print_text(output, rr->rdlength, rr->rdata, &length)) return 0; if(rr->rdlength != length) return 0; return 1; } int print_doa_rdata(struct buffer *output, const struct rr *rr) { uint16_t length = 9; if(rr->rdlength < length) return 0; buffer_printf(output, "%" PRIu32 " %" PRIu32 " %" PRIu8 " ", read_uint32(rr->rdata), read_uint32(rr->rdata+4), rr->rdata[8]); if(!print_string(output, rr->rdlength, rr->rdata, &length)) return 0; buffer_printf(output, " "); if(rr->rdlength == length) { /* The base64 string is empty, and DOA uses '-' for that. */ buffer_printf(output, "-"); } else { if(!print_base64(output, rr->rdlength, rr->rdata, &length)) return 0; } if(rr->rdlength != length) return 0; return 1; } int32_t read_amtrelay_rdata(struct domain_table *domains, uint16_t rdlength, struct buffer *packet, struct rr **rr) { struct dname_buffer relay; const uint8_t *relay_rdata; uint8_t relay_length = 0; const size_t mark = buffer_position(packet); /* byte + byte + relay */ if (buffer_remaining(packet) < rdlength || rdlength < 2) return MALFORMED; buffer_skip(packet, 2); switch (buffer_read_u8_at(packet, mark + 1)&AMTRELAY_TYPE_MASK) { case AMTRELAY_NOGATEWAY: relay_length = 0; relay_rdata = NULL; break; case AMTRELAY_IP4: relay_length = 4; relay_rdata = buffer_current(packet); if (rdlength < 2 + relay_length) return MALFORMED; buffer_skip(packet, relay_length); break; case AMTRELAY_IP6: relay_length = 16; relay_rdata = buffer_current(packet); if (rdlength < 2 + relay_length) return MALFORMED; buffer_skip(packet, relay_length); break; case AMTRELAY_DNAME: /* The dname is stored as literal dname. On the wire * skip possibly compressed format, in the rdata there * is an uncompressed wire format. */ if(!dname_make_from_packet_buffered(&relay, packet, 1 /* lenient */, 1)) return MALFORMED; if(rdlength < buffer_position(packet) - mark) return MALFORMED; relay_length = relay.dname.name_size; relay_rdata = dname_name((void*)&relay); break; default: return MALFORMED; } if(rdlength != buffer_position(packet) - mark) return MALFORMED; /* trailing bytes */ if (!(*rr = region_alloc(domains->region, sizeof(**rr) + 2 + relay_length))) return TRUNCATED; buffer_read_at(packet, mark, (*rr)->rdata, 2); if(relay_rdata) memcpy((*rr)->rdata + 2, relay_rdata, relay_length); (*rr)->rdlength = 2 + relay_length; return rdlength; } int print_amtrelay_rdata(struct buffer *output, const struct rr *rr) { uint16_t length = 2; uint8_t relay_type; if(rr->rdlength < length) return 0; relay_type = rr->rdata[1]&AMTRELAY_TYPE_MASK; buffer_printf(output, "%" PRIu8 " %c %" PRIu8, rr->rdata[0], (rr->rdata[1] & AMTRELAY_DISCOVERY_OPTIONAL_MASK ? '1' : '0'), relay_type); switch(relay_type) { case AMTRELAY_NOGATEWAY: buffer_printf(output, " ."); break; case AMTRELAY_IP4: buffer_printf(output, " "); if(!print_ip4(output, rr->rdlength, rr->rdata, &length)) return 0; break; case AMTRELAY_IP6: buffer_printf(output, " "); if(!print_ip6(output, rr->rdlength, rr->rdata, &length)) return 0; break; case AMTRELAY_DNAME: buffer_printf(output, " "); if(!print_name_literal(output, rr->rdlength, rr->rdata, &length)) return 0; break; default: return 0; } if(rr->rdlength != length) return 0; return 1; } int32_t amtrelay_relay_length(uint16_t rdlength, const uint8_t *rdata, uint16_t offset, struct domain** domain) { /* The amtrelay relay length depends only on earlier bytes, so both * the in-memory and uncompressed wireformat refer to the same * earlier bytes. Also the domain name is stored literally, so it * does not need to return a reference. */ uint8_t relay_type; *domain = NULL; /* Calculate the relay length, based on the relay type. * That is stored in an earlier field. */ if(rdlength < 2 || offset < 2) return -1; /* too short */ relay_type = rdata[1]&AMTRELAY_TYPE_MASK; switch(relay_type) { case AMTRELAY_NOGATEWAY: return 0; case AMTRELAY_IP4: return 4; case AMTRELAY_IP6: return 16; case AMTRELAY_DNAME: return buf_dname_length(rdata+offset, rdlength-offset); default: /* Unknown relay type. */ break; } return -1; } int print_ipn_rdata(struct buffer *output, const struct rr *rr) { uint64_t data; if(rr->rdlength < sizeof(data)) return 0; data = read_uint64(rr->rdata); buffer_printf(output, "%llu", (unsigned long long) data); return 1; } int32_t read_dlv_rdata(struct domain_table *domains, uint16_t rdlength, struct buffer *packet, struct rr **rr) { /* short + byte + byte + binary */ if (rdlength < 4) return MALFORMED; return read_rdata(domains, rdlength, packet, rr); } int print_dlv_rdata(struct buffer *output, const struct rr *rr) { uint16_t length = 4; if(rr->rdlength < length) return 0; buffer_printf( output, "%" PRIu16 " %" PRIu8 " %" PRIu8 " ", read_uint16(rr->rdata), rr->rdata[2], rr->rdata[3]); if (!print_base16(output, rr->rdlength, rr->rdata, &length)) return 0; if(rr->rdlength != length) return 0; return 1; } int print_rdata(buffer_type *output, const nsd_type_descriptor_type *descriptor, const rr_type *rr) { size_t saved_position = buffer_position(output); /* If print_rdata is going to print "", omit the tab printout. */ if(!(rr->type == TYPE_APL && rr->rdlength == 0)) buffer_printf(output, "\t"); if(!descriptor->print_rdata(output, rr)) { buffer_set_position(output, saved_position); return 0; } return 1; } /* * Compare two wireformat byte strings. In canonical order for this comparison. * Sorts equal as equal, and if there is a prefix match, the shorter one is * before the longer one; so it sorts like normalized domain names, * ab ac acd ace ad ae af. And ab < abc , abc < ac . * @param b1: byte string1. * @param len1: length of b1. * @param b2: byte string2. * @param len2: length of b2 * @return comparison, -1, 0, 1. */ static int compare_bytestring(const uint8_t* b1, uint16_t len1, const uint8_t* b2, uint16_t len2) { uint16_t blen; int res; if(len1 == 0 && len2 == 0) return 0; if(len1 == 0 && len2 != 0) return -1; if(len1 != 0 && len2 == 0) return 1; blen = len1; if(len2 < blen) blen = len2; res = memcmp(b1, b2, blen); if(res != 0) return res; if(len1 < len2) return -1; if(len1 > len2) return 1; /* len1 == len2 and equal. */ return 0; } int equal_rr_rdata(const nsd_type_descriptor_type *descriptor, const struct rr *rr1, const struct rr *rr2) { size_t i; uint16_t offset = 0; int res; if(!descriptor->has_references) { /* Compare the wireformat of the rdata. */ res = compare_bytestring(rr1->rdata, rr1->rdlength, rr2->rdata, rr2->rdlength); if(res != 0) return 0; return 1; } for(i=0; i < descriptor->rdata.length; i++) { uint16_t field_len1, field_len2; struct domain* domain1, *domain2; int malf1 = 0, malf2 = 0; /* The fields are equal up to this point. */ if((rr1->rdlength == offset || rr2->rdlength == offset) && descriptor->rdata.fields[i].is_optional) { /* There are no more rdata fields. */ /* Check lengths. */ if(rr1->rdlength == rr2->rdlength) return 1; else return 0; } if(!lookup_rdata_field_entry(descriptor, i, rr1, offset, &field_len1, &domain1)) malf1 = 1; /* malformed rdata buffer */ if(!lookup_rdata_field_entry(descriptor, i, rr2, offset, &field_len2, &domain2)) malf2 = 1; /* malformed rdata buffer */ if(malf1 || malf2) { /* Malformed entries sort last, and are sorted * equal with other malformed entries. */ if(malf1 && malf2) return 1; else return 0; } /* Compare the two fields. */ /* If they have a different type field, they are not the * same. */ if(domain1 && !domain2) return 0; if(!domain1 && domain2) return 0; if(domain1 && domain2) { /* Handle RDATA_COMPRESSED_DNAME and * RDATA_UNCOMPRESSED_DNAME fields. */ res = dname_compare(domain_dname(domain1), domain_dname(domain2)); if(res != 0) return 0; } else { res = compare_bytestring(rr1->rdata + offset, field_len1, rr2->rdata + offset, field_len2); if(res != 0) return 0; } /* The fields are equal, field_len1 == field_len2. */ offset += field_len1; } return 1; } int equal_rr_rdata_uncompressed_wire(const nsd_type_descriptor_type *descriptor, const struct rr *rr1, const uint8_t* rr2_rdata, uint16_t rr2_rdlen) { size_t i; uint16_t offset1 = 0, offset2 = 0; int res; if(!descriptor->has_references) { /* Compare the wireformat of the rdata. */ res = compare_bytestring(rr1->rdata, rr1->rdlength, rr2_rdata, rr2_rdlen); if(res != 0) return 0; return 1; } for(i=0; i < descriptor->rdata.length; i++) { uint16_t field_len1, field_len2; struct domain* domain1, *domain2; int malf1 = 0, malf2 = 0; /* The fields are equal up to this point. */ if((rr1->rdlength == offset1 || rr2_rdlen == offset2) && descriptor->rdata.fields[i].is_optional) { /* There are no more rdata fields. */ /* Check lengths. */ int remain1 = rr1->rdlength - offset1; int remain2 = rr2_rdlen - offset2; if(remain1 < remain2) return 0; if(remain1 > remain2) return 0; /* It is equal. */ return 1; } if(!lookup_rdata_field_entry(descriptor, i, rr1, offset1, &field_len1, &domain1)) malf1 = 1; /* malformed rdata buffer */ if(!lookup_rdata_field_entry_uncompressed_wire(descriptor, i, rr2_rdata, rr2_rdlen, offset2, &field_len2, &domain2)) malf2 = 1; /* malformed rdata buffer */ if(malf1 || malf2) { /* Malformed entries sort last, and are sorted * equal with other malformed entries. */ if(!malf1 && malf2) return 0; if(malf1 && !malf2) return 0; return 1; } /* Compare the two fields. */ /* If they have a different type field, they are not the * same. */ if(domain1) { if(domain2) { /* Handle RDATA_COMPRESSED_DNAME and * RDATA_UNCOMPRESSED_DNAME fields. */ res = dname_compare(domain_dname(domain1), domain_dname(domain2)); if(res != 0) return 0; } else { uint16_t dname_len2 = buf_dname_length( rr2_rdata+offset2, rr2_rdlen-offset2); if(domain_dname(domain1)->name_size != dname_len2) { /* not the same length dnames. */ return 0; } if(!dname_equal_nocase( (uint8_t*)dname_name(domain_dname( domain1)), (uint8_t*)rr2_rdata+offset2, dname_len2)) { /* name comparison not equal. */ return 0; } } } else { if(domain2) { uint16_t dname_len1 = buf_dname_length( rr1->rdata + offset1, rr1->rdlength-offset1); if(dname_len1 != domain_dname(domain2)->name_size) { /* not the same length dnames. */ return 0; } if(!dname_equal_nocase( (uint8_t*)rr1->rdata+offset1, (uint8_t*)dname_name(domain_dname( domain2)), dname_len1)) { /* name comparison not equal. */ return 0; } } else { res = compare_bytestring(rr1->rdata + offset1, field_len1, rr2_rdata + offset2, field_len2); if(res != 0) return 0; } } offset1 += field_len1; offset2 += field_len2; } return 1; } struct domain* retrieve_rdata_ref_domain_offset(const struct rr* rr, uint16_t offset) { struct domain *domain; if(rr->rdlength < offset+sizeof(void*)) return NULL; memcpy(&domain, rr->rdata+offset, sizeof(void*)); return domain; } struct domain* retrieve_rdata_ref_domain(const struct rr* rr) { struct domain *domain; if(rr->rdlength < sizeof(void*)) return NULL; memcpy(&domain, rr->rdata, sizeof(void*)); return domain; } struct domain* retrieve_ns_ref_domain(const struct rr* rr) { assert(rr->type == TYPE_NS); return retrieve_rdata_ref_domain(rr); } struct domain* retrieve_cname_ref_domain(const struct rr* rr) { assert(rr->type == TYPE_CNAME); return retrieve_rdata_ref_domain(rr); } struct domain* retrieve_dname_ref_domain(const struct rr* rr) { assert(rr->type == TYPE_DNAME); return retrieve_rdata_ref_domain(rr); } struct domain* retrieve_mb_ref_domain(const struct rr* rr) { assert(rr->type == TYPE_MB); return retrieve_rdata_ref_domain(rr); } struct domain* retrieve_mx_ref_domain(const struct rr* rr) { assert(rr->type == TYPE_MX); return retrieve_rdata_ref_domain_offset(rr, 2); } struct domain* retrieve_kx_ref_domain(const struct rr* rr) { assert(rr->type == TYPE_KX); return retrieve_rdata_ref_domain_offset(rr, 2); } struct domain* retrieve_rt_ref_domain(const struct rr* rr) { assert(rr->type == TYPE_RT); return retrieve_rdata_ref_domain_offset(rr, 2); } struct domain* retrieve_srv_ref_domain(const struct rr* rr) { assert(rr->type == TYPE_SRV); return retrieve_rdata_ref_domain_offset(rr, 6); } struct domain* retrieve_ptr_ref_domain(const struct rr* rr) { assert(rr->type == TYPE_PTR); return retrieve_rdata_ref_domain(rr); } int retrieve_soa_rdata_serial(const struct rr* rr, uint32_t* serial) { assert(rr->type == TYPE_SOA); if(rr->rdlength < 20 + 2*sizeof(void*)) return 0; /* primary mail serial[4] refresh[4] retry[4] expire[4] minimum[4] */ *serial = read_uint32(rr->rdata+2*sizeof(void*)); return 1; } int retrieve_soa_rdata_minttl(const struct rr* rr, uint32_t* minttl) { assert(rr->type == TYPE_SOA); if(rr->rdlength < 20 + 2*sizeof(void*)) return 0; /* primary mail serial[4] refresh[4] retry[4] expire[4] minimum[4] */ *minttl = read_uint32(rr->rdata+2*sizeof(void*)+16); return 1; } struct dname* retrieve_cname_ref_dname(const struct rr* rr) { struct domain* domain; assert(rr->type == TYPE_CNAME); domain = retrieve_rdata_ref_domain(rr); if(!domain) return NULL; return domain_dname(domain); } void rr_lower_usage(namedb_type* db, rr_type* rr) { const nsd_type_descriptor_type *descriptor = nsd_type_descriptor(rr->type); uint16_t offset = 0; size_t i; for(i=0; irdata.length; i++) { uint16_t field_len; struct domain* domain; if(rr->rdlength == offset && descriptor->rdata.fields[i].is_optional) break; if(!lookup_rdata_field_entry(descriptor, i, rr, offset, &field_len, &domain)) break; if(domain) { assert(domain->usage > 0); domain->usage --; if(domain->usage == 0) domain_table_deldomain(db, domain); } offset += field_len; } } const char* read_rdata_fail_str(int32_t code) { switch(code) { case TRUNCATED: return "out of memory"; case MALFORMED: return "malformed rdata fields"; default: break; } return "failed to read rdata fields"; }