676 lines
14 KiB
C
676 lines
14 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;
|
|
|
|
switch(ldns_rdf_get_type(rd)) {
|
|
case LDNS_RDF_TYPE_TIME:
|
|
memcpy(&data, ldns_rdf_data(rd), sizeof(data));
|
|
return (time_t)ntohl(data);
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
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_TSIG:
|
|
status = ldns_str2rdf_tsig(&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_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(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 octett (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_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;
|
|
}
|