freebsd-dev/contrib/ldns/rdata.c
Dag-Erling Smørgrav 986ba33c7a Upgrade LDNS to 1.7.0.
I've been holding back on this because 1.7.0 requires OpenSSL 1.1.0 or
newer for full DANE support.  But we can't wait forever, and nothing in
base uses DANE anyway, so here we go.
2018-05-12 12:00:18 +00:00

758 lines
16 KiB
C

/*
* rdata.c
*
* rdata implementation
*
* a Net::DNS like library for C
*
* (c) NLnet Labs, 2004-2006
*
* See the file LICENSE for the license
*/
#include <ldns/config.h>
#include <ldns/ldns.h>
/*
* Access functions
* do this as functions to get type checking
*/
/* read */
size_t
ldns_rdf_size(const ldns_rdf *rd)
{
assert(rd != NULL);
return rd->_size;
}
ldns_rdf_type
ldns_rdf_get_type(const ldns_rdf *rd)
{
assert(rd != NULL);
return rd->_type;
}
uint8_t *
ldns_rdf_data(const ldns_rdf *rd)
{
assert(rd != NULL);
return rd->_data;
}
/* write */
void
ldns_rdf_set_size(ldns_rdf *rd, size_t size)
{
assert(rd != NULL);
rd->_size = size;
}
void
ldns_rdf_set_type(ldns_rdf *rd, ldns_rdf_type type)
{
assert(rd != NULL);
rd->_type = type;
}
void
ldns_rdf_set_data(ldns_rdf *rd, void *data)
{
/* only copy the pointer */
assert(rd != NULL);
rd->_data = data;
}
/* for types that allow it, return
* the native/host order type */
uint8_t
ldns_rdf2native_int8(const ldns_rdf *rd)
{
uint8_t data;
/* only allow 8 bit rdfs */
if (ldns_rdf_size(rd) != LDNS_RDF_SIZE_BYTE) {
return 0;
}
memcpy(&data, ldns_rdf_data(rd), sizeof(data));
return data;
}
uint16_t
ldns_rdf2native_int16(const ldns_rdf *rd)
{
uint16_t data;
/* only allow 16 bit rdfs */
if (ldns_rdf_size(rd) != LDNS_RDF_SIZE_WORD) {
return 0;
}
memcpy(&data, ldns_rdf_data(rd), sizeof(data));
return ntohs(data);
}
uint32_t
ldns_rdf2native_int32(const ldns_rdf *rd)
{
uint32_t data;
/* only allow 32 bit rdfs */
if (ldns_rdf_size(rd) != LDNS_RDF_SIZE_DOUBLEWORD) {
return 0;
}
memcpy(&data, ldns_rdf_data(rd), sizeof(data));
return ntohl(data);
}
time_t
ldns_rdf2native_time_t(const ldns_rdf *rd)
{
uint32_t data;
/* only allow 32 bit rdfs */
if (ldns_rdf_size(rd) != LDNS_RDF_SIZE_DOUBLEWORD ||
ldns_rdf_get_type(rd) != LDNS_RDF_TYPE_TIME) {
return 0;
}
memcpy(&data, ldns_rdf_data(rd), sizeof(data));
return (time_t)ntohl(data);
}
ldns_rdf *
ldns_native2rdf_int8(ldns_rdf_type type, uint8_t value)
{
return ldns_rdf_new_frm_data(type, LDNS_RDF_SIZE_BYTE, &value);
}
ldns_rdf *
ldns_native2rdf_int16(ldns_rdf_type type, uint16_t value)
{
uint16_t *rdf_data = LDNS_XMALLOC(uint16_t, 1);
ldns_rdf* rdf;
if (!rdf_data) {
return NULL;
}
ldns_write_uint16(rdf_data, value);
rdf = ldns_rdf_new(type, LDNS_RDF_SIZE_WORD, rdf_data);
if(!rdf)
LDNS_FREE(rdf_data);
return rdf;
}
ldns_rdf *
ldns_native2rdf_int32(ldns_rdf_type type, uint32_t value)
{
uint32_t *rdf_data = LDNS_XMALLOC(uint32_t, 1);
ldns_rdf* rdf;
if (!rdf_data) {
return NULL;
}
ldns_write_uint32(rdf_data, value);
rdf = ldns_rdf_new(type, LDNS_RDF_SIZE_DOUBLEWORD, rdf_data);
if(!rdf)
LDNS_FREE(rdf_data);
return rdf;
}
ldns_rdf *
ldns_native2rdf_int16_data(size_t size, uint8_t *data)
{
uint8_t *rdf_data = LDNS_XMALLOC(uint8_t, size + 2);
ldns_rdf* rdf;
if (!rdf_data) {
return NULL;
}
ldns_write_uint16(rdf_data, size);
memcpy(rdf_data + 2, data, size);
rdf = ldns_rdf_new(LDNS_RDF_TYPE_INT16_DATA, size + 2, rdf_data);
if(!rdf)
LDNS_FREE(rdf_data);
return rdf;
}
/* note: data must be allocated memory */
ldns_rdf *
ldns_rdf_new(ldns_rdf_type type, size_t size, void *data)
{
ldns_rdf *rd;
rd = LDNS_MALLOC(ldns_rdf);
if (!rd) {
return NULL;
}
ldns_rdf_set_size(rd, size);
ldns_rdf_set_type(rd, type);
ldns_rdf_set_data(rd, data);
return rd;
}
ldns_rdf *
ldns_rdf_new_frm_data(ldns_rdf_type type, size_t size, const void *data)
{
ldns_rdf *rdf;
/* if the size is too big, fail */
if (size > LDNS_MAX_RDFLEN) {
return NULL;
}
/* allocate space */
rdf = LDNS_MALLOC(ldns_rdf);
if (!rdf) {
return NULL;
}
rdf->_data = LDNS_XMALLOC(uint8_t, size);
if (!rdf->_data) {
LDNS_FREE(rdf);
return NULL;
}
/* set the values */
ldns_rdf_set_type(rdf, type);
ldns_rdf_set_size(rdf, size);
memcpy(rdf->_data, data, size);
return rdf;
}
ldns_rdf *
ldns_rdf_clone(const ldns_rdf *rd)
{
assert(rd != NULL);
return (ldns_rdf_new_frm_data( ldns_rdf_get_type(rd),
ldns_rdf_size(rd), ldns_rdf_data(rd)));
}
void
ldns_rdf_deep_free(ldns_rdf *rd)
{
if (rd) {
if (rd->_data) {
LDNS_FREE(rd->_data);
}
LDNS_FREE(rd);
}
}
void
ldns_rdf_free(ldns_rdf *rd)
{
if (rd) {
LDNS_FREE(rd);
}
}
ldns_rdf *
ldns_rdf_new_frm_str(ldns_rdf_type type, const char *str)
{
ldns_rdf *rdf = NULL;
ldns_status status;
switch (type) {
case LDNS_RDF_TYPE_DNAME:
status = ldns_str2rdf_dname(&rdf, str);
break;
case LDNS_RDF_TYPE_INT8:
status = ldns_str2rdf_int8(&rdf, str);
break;
case LDNS_RDF_TYPE_INT16:
status = ldns_str2rdf_int16(&rdf, str);
break;
case LDNS_RDF_TYPE_INT32:
status = ldns_str2rdf_int32(&rdf, str);
break;
case LDNS_RDF_TYPE_A:
status = ldns_str2rdf_a(&rdf, str);
break;
case LDNS_RDF_TYPE_AAAA:
status = ldns_str2rdf_aaaa(&rdf, str);
break;
case LDNS_RDF_TYPE_STR:
status = ldns_str2rdf_str(&rdf, str);
break;
case LDNS_RDF_TYPE_APL:
status = ldns_str2rdf_apl(&rdf, str);
break;
case LDNS_RDF_TYPE_B64:
status = ldns_str2rdf_b64(&rdf, str);
break;
case LDNS_RDF_TYPE_B32_EXT:
status = ldns_str2rdf_b32_ext(&rdf, str);
break;
case LDNS_RDF_TYPE_HEX:
status = ldns_str2rdf_hex(&rdf, str);
break;
case LDNS_RDF_TYPE_NSEC:
status = ldns_str2rdf_nsec(&rdf, str);
break;
case LDNS_RDF_TYPE_TYPE:
status = ldns_str2rdf_type(&rdf, str);
break;
case LDNS_RDF_TYPE_CLASS:
status = ldns_str2rdf_class(&rdf, str);
break;
case LDNS_RDF_TYPE_CERT_ALG:
status = ldns_str2rdf_cert_alg(&rdf, str);
break;
case LDNS_RDF_TYPE_ALG:
status = ldns_str2rdf_alg(&rdf, str);
break;
case LDNS_RDF_TYPE_UNKNOWN:
status = ldns_str2rdf_unknown(&rdf, str);
break;
case LDNS_RDF_TYPE_TIME:
status = ldns_str2rdf_time(&rdf, str);
break;
case LDNS_RDF_TYPE_PERIOD:
status = ldns_str2rdf_period(&rdf, str);
break;
case LDNS_RDF_TYPE_HIP:
status = ldns_str2rdf_hip(&rdf, str);
break;
case LDNS_RDF_TYPE_SERVICE:
status = ldns_str2rdf_service(&rdf, str);
break;
case LDNS_RDF_TYPE_LOC:
status = ldns_str2rdf_loc(&rdf, str);
break;
case LDNS_RDF_TYPE_WKS:
status = ldns_str2rdf_wks(&rdf, str);
break;
case LDNS_RDF_TYPE_NSAP:
status = ldns_str2rdf_nsap(&rdf, str);
break;
case LDNS_RDF_TYPE_ATMA:
status = ldns_str2rdf_atma(&rdf, str);
break;
case LDNS_RDF_TYPE_IPSECKEY:
status = ldns_str2rdf_ipseckey(&rdf, str);
break;
case LDNS_RDF_TYPE_NSEC3_SALT:
status = ldns_str2rdf_nsec3_salt(&rdf, str);
break;
case LDNS_RDF_TYPE_NSEC3_NEXT_OWNER:
status = ldns_str2rdf_b32_ext(&rdf, str);
break;
case LDNS_RDF_TYPE_ILNP64:
status = ldns_str2rdf_ilnp64(&rdf, str);
break;
case LDNS_RDF_TYPE_EUI48:
status = ldns_str2rdf_eui48(&rdf, str);
break;
case LDNS_RDF_TYPE_EUI64:
status = ldns_str2rdf_eui64(&rdf, str);
break;
case LDNS_RDF_TYPE_TAG:
status = ldns_str2rdf_tag(&rdf, str);
break;
case LDNS_RDF_TYPE_LONG_STR:
status = ldns_str2rdf_long_str(&rdf, str);
break;
case LDNS_RDF_TYPE_CERTIFICATE_USAGE:
status = ldns_str2rdf_certificate_usage(&rdf, str);
break;
case LDNS_RDF_TYPE_SELECTOR:
status = ldns_str2rdf_selector(&rdf, str);
break;
case LDNS_RDF_TYPE_MATCHING_TYPE:
status = ldns_str2rdf_matching_type(&rdf, str);
break;
case LDNS_RDF_TYPE_NONE:
default:
/* default default ??? */
status = LDNS_STATUS_ERR;
break;
}
if (LDNS_STATUS_OK == status) {
ldns_rdf_set_type(rdf, type);
return rdf;
}
if (rdf) {
LDNS_FREE(rdf);
}
return NULL;
}
ldns_status
ldns_rdf_new_frm_fp(ldns_rdf **rdf, ldns_rdf_type type, FILE *fp)
{
return ldns_rdf_new_frm_fp_l(rdf, type, fp, NULL);
}
ldns_status
ldns_rdf_new_frm_fp_l(ldns_rdf **rdf, ldns_rdf_type type, FILE *fp, int *line_nr)
{
char *line;
ldns_rdf *r;
ssize_t t;
line = LDNS_XMALLOC(char, LDNS_MAX_LINELEN + 1);
if (!line) {
return LDNS_STATUS_MEM_ERR;
}
/* read an entire line in from the file */
if ((t = ldns_fget_token_l(fp, line, LDNS_PARSE_SKIP_SPACE, 0, line_nr)) == -1 || t == 0) {
LDNS_FREE(line);
return LDNS_STATUS_SYNTAX_RDATA_ERR;
}
r = ldns_rdf_new_frm_str(type, (const char*) line);
LDNS_FREE(line);
if (rdf) {
*rdf = r;
return LDNS_STATUS_OK;
} else {
return LDNS_STATUS_NULL;
}
}
ldns_rdf *
ldns_rdf_address_reverse(const ldns_rdf *rd)
{
uint8_t buf_4[LDNS_IP4ADDRLEN];
uint8_t buf_6[LDNS_IP6ADDRLEN * 2];
ldns_rdf *rev;
ldns_rdf *in_addr;
ldns_rdf *ret_dname;
uint8_t octet;
uint8_t nnibble;
uint8_t nibble;
uint8_t i, j;
char *char_dname;
int nbit;
if (ldns_rdf_get_type(rd) != LDNS_RDF_TYPE_A &&
ldns_rdf_get_type(rd) != LDNS_RDF_TYPE_AAAA) {
return NULL;
}
in_addr = NULL;
ret_dname = NULL;
switch(ldns_rdf_get_type(rd)) {
case LDNS_RDF_TYPE_A:
/* the length of the buffer is 4 */
buf_4[3] = ldns_rdf_data(rd)[0];
buf_4[2] = ldns_rdf_data(rd)[1];
buf_4[1] = ldns_rdf_data(rd)[2];
buf_4[0] = ldns_rdf_data(rd)[3];
in_addr = ldns_dname_new_frm_str("in-addr.arpa.");
if (!in_addr) {
return NULL;
}
/* make a new rdf and convert that back */
rev = ldns_rdf_new_frm_data( LDNS_RDF_TYPE_A,
LDNS_IP4ADDRLEN, (void*)&buf_4);
if (!rev) {
LDNS_FREE(in_addr);
return NULL;
}
/* convert rev to a string */
char_dname = ldns_rdf2str(rev);
if (!char_dname) {
LDNS_FREE(in_addr);
ldns_rdf_deep_free(rev);
return NULL;
}
/* transform back to rdf with type dname */
ret_dname = ldns_dname_new_frm_str(char_dname);
if (!ret_dname) {
LDNS_FREE(in_addr);
ldns_rdf_deep_free(rev);
LDNS_FREE(char_dname);
return NULL;
}
/* not needed anymore */
ldns_rdf_deep_free(rev);
LDNS_FREE(char_dname);
break;
case LDNS_RDF_TYPE_AAAA:
/* some foo magic to reverse the nibbles ... */
for (nbit = 127; nbit >= 0; nbit = nbit - 4) {
/* calculate octet (8 bit) */
octet = ( ((unsigned int) nbit) & 0x78) >> 3;
/* calculate nibble */
nnibble = ( ((unsigned int) nbit) & 0x04) >> 2;
/* extract nibble */
nibble = (ldns_rdf_data(rd)[octet] & ( 0xf << (4 * (1 -
nnibble)) ) ) >> ( 4 * (1 -
nnibble));
buf_6[(LDNS_IP6ADDRLEN * 2 - 1) -
(octet * 2 + nnibble)] =
(uint8_t)ldns_int_to_hexdigit((int)nibble);
}
char_dname = LDNS_XMALLOC(char, (LDNS_IP6ADDRLEN * 4));
if (!char_dname) {
return NULL;
}
char_dname[LDNS_IP6ADDRLEN * 4 - 1] = '\0'; /* closure */
/* walk the string and add . 's */
for (i = 0, j = 0; i < LDNS_IP6ADDRLEN * 2; i++, j = j + 2) {
char_dname[j] = (char)buf_6[i];
if (i != LDNS_IP6ADDRLEN * 2 - 1) {
char_dname[j + 1] = '.';
}
}
in_addr = ldns_dname_new_frm_str("ip6.arpa.");
if (!in_addr) {
LDNS_FREE(char_dname);
return NULL;
}
/* convert rev to a string */
ret_dname = ldns_dname_new_frm_str(char_dname);
LDNS_FREE(char_dname);
if (!ret_dname) {
ldns_rdf_deep_free(in_addr);
return NULL;
}
break;
default:
break;
}
/* add the suffix */
rev = ldns_dname_cat_clone(ret_dname, in_addr);
ldns_rdf_deep_free(ret_dname);
ldns_rdf_deep_free(in_addr);
return rev;
}
ldns_status
ldns_rdf_hip_get_alg_hit_pk(ldns_rdf *rdf, uint8_t* alg,
uint8_t *hit_size, uint8_t** hit,
uint16_t *pk_size, uint8_t** pk)
{
uint8_t *data;
size_t rdf_size;
if (! rdf || ! alg || ! hit || ! hit_size || ! pk || ! pk_size) {
return LDNS_STATUS_INVALID_POINTER;
} else if (ldns_rdf_get_type(rdf) != LDNS_RDF_TYPE_HIP) {
return LDNS_STATUS_INVALID_RDF_TYPE;
} else if ((rdf_size = ldns_rdf_size(rdf)) < 6) {
return LDNS_STATUS_WIRE_RDATA_ERR;
}
data = ldns_rdf_data(rdf);
*hit_size = data[0];
*alg = data[1];
*pk_size = ldns_read_uint16(data + 2);
*hit = data + 4;
*pk = data + 4 + *hit_size;
if (*hit_size == 0 || *pk_size == 0 ||
rdf_size < (size_t) *hit_size + *pk_size + 4) {
return LDNS_STATUS_WIRE_RDATA_ERR;
}
return LDNS_STATUS_OK;
}
ldns_status
ldns_rdf_hip_new_frm_alg_hit_pk(ldns_rdf** rdf, uint8_t alg,
uint8_t hit_size, uint8_t *hit,
uint16_t pk_size, uint8_t *pk)
{
uint8_t *data;
if (! rdf) {
return LDNS_STATUS_INVALID_POINTER;
}
if (4 + hit_size + pk_size > LDNS_MAX_RDFLEN) {
return LDNS_STATUS_RDATA_OVERFLOW;
}
data = LDNS_XMALLOC(uint8_t, 4 + hit_size + pk_size);
if (data == NULL) {
return LDNS_STATUS_MEM_ERR;
}
data[0] = hit_size;
data[1] = alg;
ldns_write_uint16(data + 2, pk_size);
memcpy(data + 4, hit, hit_size);
memcpy(data + 4 + hit_size, pk, pk_size);
*rdf = ldns_rdf_new(LDNS_RDF_TYPE_HIP, 4 + hit_size + pk_size, data);
if (! *rdf) {
LDNS_FREE(data);
return LDNS_STATUS_MEM_ERR;
}
return LDNS_STATUS_OK;
}
ldns_status
ldns_octet(char *word, size_t *length)
{
char *s;
char *p;
*length = 0;
for (s = p = word; *s != '\0'; s++,p++) {
switch (*s) {
case '.':
if (s[1] == '.') {
return LDNS_STATUS_EMPTY_LABEL;
}
*p = *s;
(*length)++;
break;
case '\\':
if ('0' <= s[1] && s[1] <= '9' &&
'0' <= s[2] && s[2] <= '9' &&
'0' <= s[3] && s[3] <= '9') {
/* \DDD seen */
int val = ((s[1] - '0') * 100 +
(s[2] - '0') * 10 + (s[3] - '0'));
if (0 <= val && val <= 255) {
/* this also handles \0 */
s += 3;
*p = val;
(*length)++;
} else {
return LDNS_STATUS_DDD_OVERFLOW;
}
} else {
/* an espaced character, like \<space> ?
* remove the '\' keep the rest */
*p = *++s;
(*length)++;
}
break;
case '\"':
/* non quoted " Is either first or the last character in
* the string */
*p = *++s; /* skip it */
(*length)++;
/* I'm not sure if this is needed in libdns... MG */
if ( *s == '\0' ) {
/* ok, it was the last one */
*p = '\0';
return LDNS_STATUS_OK;
}
break;
default:
*p = *s;
(*length)++;
break;
}
}
*p = '\0';
return LDNS_STATUS_OK;
}
int
ldns_rdf_compare(const ldns_rdf *rd1, const ldns_rdf *rd2)
{
uint16_t i1, i2, i;
uint8_t *d1, *d2;
/* only when both are not NULL we can say anything about them */
if (!rd1 && !rd2) {
return 0;
}
if (!rd1 || !rd2) {
return -1;
}
i1 = ldns_rdf_size(rd1);
i2 = ldns_rdf_size(rd2);
if (i1 < i2) {
return -1;
} else if (i1 > i2) {
return +1;
} else {
d1 = (uint8_t*)ldns_rdf_data(rd1);
d2 = (uint8_t*)ldns_rdf_data(rd2);
for(i = 0; i < i1; i++) {
if (d1[i] < d2[i]) {
return -1;
} else if (d1[i] > d2[i]) {
return +1;
}
}
}
return 0;
}
uint32_t
ldns_str2period(const char *nptr, const char **endptr)
{
int sign = 0;
uint32_t i = 0;
uint32_t seconds = 0;
for(*endptr = nptr; **endptr; (*endptr)++) {
switch (**endptr) {
case ' ':
case '\t':
break;
case '-':
if(sign == 0) {
sign = -1;
} else {
return seconds;
}
break;
case '+':
if(sign == 0) {
sign = 1;
} else {
return seconds;
}
break;
case 's':
case 'S':
seconds += i;
i = 0;
break;
case 'm':
case 'M':
seconds += i * 60;
i = 0;
break;
case 'h':
case 'H':
seconds += i * 60 * 60;
i = 0;
break;
case 'd':
case 'D':
seconds += i * 60 * 60 * 24;
i = 0;
break;
case 'w':
case 'W':
seconds += i * 60 * 60 * 24 * 7;
i = 0;
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
i *= 10;
i += (**endptr - '0');
break;
default:
seconds += i;
/* disregard signedness */
return seconds;
}
}
seconds += i;
/* disregard signedness */
return seconds;
}