/** * * /brief getdns support functions for DNS Resource Records * * This file contains the tables with the information needed by getdns about * individual RRs, such as their name and rdata fields and types. * This information is provided via the response dict. * */ /* * Copyright (c) 2013, NLnet Labs, Verisign, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the names of the copyright holders nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL Verisign, Inc. BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "rr-dict.h" #include "gldns/gbuffer.h" #include "util-internal.h" #include "types-internal.h" #include "context.h" #include "dict.h" #define ALEN(a) (sizeof(a)/sizeof(a[0])) #define UNKNOWN_RDATA NULL static const uint8_t * apl_n_rdf_end(const uint8_t *pkt, const uint8_t *pkt_end, const uint8_t *rdf) { return rdf < pkt_end ? rdf + 1 : NULL; } static getdns_return_t apl_n_wire2dict(getdns_dict *dict, const uint8_t *rdf) { return getdns_dict_set_int(dict, "n", (*rdf >> 7)); } static getdns_return_t apl_n_wire2list(getdns_list *list, const uint8_t *rdf) { return _getdns_list_append_int(list, (*rdf >> 7)); } static getdns_return_t apl_n_2wire(uint32_t value, uint8_t *rdata, uint8_t *rdf, size_t *rdf_len) { (void)rdata; /* unused parameter */ if (*rdf_len < 1) { *rdf_len = 1; return GETDNS_RETURN_NEED_MORE_SPACE; } *rdf_len = 1; *rdf = value ? 0x80 : 0x00; return GETDNS_RETURN_GOOD; } static getdns_return_t apl_n_dict2wire(const getdns_dict *dict, uint8_t *rdata, uint8_t *rdf, size_t *rdf_len) { getdns_return_t r; uint32_t value; if ((r = getdns_dict_get_int(dict, "n", &value))) return r; else return apl_n_2wire(value, rdata, rdf, rdf_len); } static getdns_return_t apl_n_list2wire(const getdns_list *list, size_t i, uint8_t *rdata, uint8_t *rdf, size_t *rdf_len) { getdns_return_t r; uint32_t value; if ((r = getdns_list_get_int(list, i, &value))) return r; else return apl_n_2wire(value, rdata, rdf, rdf_len); } static _getdns_rdf_special apl_n = { apl_n_rdf_end, apl_n_wire2dict, apl_n_wire2list, apl_n_dict2wire, apl_n_list2wire }; static const uint8_t * apl_afdpart_rdf_end( const uint8_t *pkt, const uint8_t *pkt_end, const uint8_t *rdf) { const uint8_t *end = rdf + (rdf[-1] & 0x7F); return end <= pkt_end ? end : NULL; } static getdns_return_t apl_afdpart_wire2dict(getdns_dict *dict, const uint8_t *rdf) { return _getdns_dict_set_const_bindata( dict, "afdpart", (rdf[-1] & 0x7F), rdf); } static getdns_return_t apl_afdpart_wire2list(getdns_list *list, const uint8_t *rdf) { return _getdns_list_append_const_bindata(list, (rdf[-1] & 0x7F), rdf); } static getdns_return_t apl_afdpart_2wire( const getdns_bindata *value, uint8_t *rdata, uint8_t *rdf, size_t *rdf_len) { if (value->size > 0x7F) return GETDNS_RETURN_INVALID_PARAMETER; if (rdf - 1 < rdata) return GETDNS_RETURN_GENERIC_ERROR; if (*rdf_len < value->size) { *rdf_len = value->size; return GETDNS_RETURN_NEED_MORE_SPACE; } *rdf_len = value->size; /* Keeping first bit is safe because value->size <= 0x7F */ rdf[-1] |= value->size; (void) memcpy(rdf, value->data, value->size); return GETDNS_RETURN_GOOD; } static getdns_return_t apl_afdpart_dict2wire( const getdns_dict *dict, uint8_t *rdata, uint8_t *rdf, size_t *rdf_len) { getdns_return_t r; getdns_bindata *value; if ((r = getdns_dict_get_bindata(dict, "afdpart", &value))) return r; else return apl_afdpart_2wire(value, rdata, rdf, rdf_len); } static getdns_return_t apl_afdpart_list2wire(const getdns_list *list, size_t i, uint8_t *rdata, uint8_t *rdf, size_t *rdf_len) { getdns_return_t r; getdns_bindata *value; if ((r = getdns_list_get_bindata(list, i, &value))) return r; else return apl_afdpart_2wire(value, rdata, rdf, rdf_len); } static _getdns_rdf_special apl_afdpart = { apl_afdpart_rdf_end, apl_afdpart_wire2dict, apl_afdpart_wire2list, apl_afdpart_dict2wire, apl_afdpart_list2wire }; static const uint8_t * ipseckey_gateway_rdf_end( const uint8_t *pkt, const uint8_t *pkt_end, const uint8_t *rdf) { const uint8_t *end; if (rdf - 5 < pkt) return NULL; switch (rdf[-2]) { case 0: end = rdf; break; case 1: end = rdf + 4; break; case 2: end = rdf + 16; break; case 3: for (end = rdf; end < pkt_end; end += *end + 1) if ((*end & 0xC0) == 0xC0) end += 2; else if (*end & 0xC0) return NULL; else if (!*end) { end += 1; break; } break; default: return NULL; } return end <= pkt_end ? end : NULL; } static getdns_return_t ipseckey_gateway_equip_const_bindata( const uint8_t *rdf, size_t *size, const uint8_t **data) { *data = rdf; switch (rdf[-2]) { case 0: *size = 0; break; case 1: *size = 4; break; case 2: *size = 16; break; case 3: while (*rdf) if ((*rdf & 0xC0) == 0xC0) rdf += 2; else if (*rdf & 0xC0) return GETDNS_RETURN_GENERIC_ERROR; else rdf += *rdf + 1; *size = rdf + 1 - *data; break; default: return GETDNS_RETURN_GENERIC_ERROR; } return GETDNS_RETURN_GOOD; } static getdns_return_t ipseckey_gateway_wire2dict(getdns_dict *dict, const uint8_t *rdf) { size_t size; const uint8_t *data; if (ipseckey_gateway_equip_const_bindata(rdf, &size, &data)) return GETDNS_RETURN_GENERIC_ERROR; else if (! size) return GETDNS_RETURN_GOOD; else return _getdns_dict_set_const_bindata(dict, "gateway", size, data); } static getdns_return_t ipseckey_gateway_wire2list(getdns_list *list, const uint8_t *rdf) { size_t size; const uint8_t *data; if (ipseckey_gateway_equip_const_bindata(rdf, &size, &data)) return GETDNS_RETURN_GENERIC_ERROR; else if (!size) return GETDNS_RETURN_GOOD; else return _getdns_list_append_const_bindata(list, size, data); } static getdns_return_t ipseckey_gateway_2wire( const getdns_bindata *value, uint8_t *rdata, uint8_t *rdf, size_t *rdf_len) { if (rdf - 2 < rdata) return GETDNS_RETURN_GENERIC_ERROR; switch (rdf[-2]) { case 0: if (value && value->size > 0) return GETDNS_RETURN_INVALID_PARAMETER; break; case 1: if (!value || value->size != 4) return GETDNS_RETURN_INVALID_PARAMETER; if (*rdf_len < 4) { *rdf_len = 4; return GETDNS_RETURN_NEED_MORE_SPACE; } *rdf_len = 4; (void)memcpy(rdf, value->data, 4); return GETDNS_RETURN_GOOD; case 2: if (!value || value->size != 16) return GETDNS_RETURN_INVALID_PARAMETER; if (*rdf_len < 16) { *rdf_len = 16; return GETDNS_RETURN_NEED_MORE_SPACE; } *rdf_len = 16; (void)memcpy(rdf, value->data, 16); return GETDNS_RETURN_GOOD; case 3: if (!value || value->size == 0) return GETDNS_RETURN_INVALID_PARAMETER; /* Assume bindata is a valid dname; garbage in, garbage out */ if (*rdf_len < value->size) { *rdf_len = value->size; return GETDNS_RETURN_NEED_MORE_SPACE; } *rdf_len = value->size; (void)memcpy(rdf, value->data, value->size); return GETDNS_RETURN_GOOD; default: return GETDNS_RETURN_GENERIC_ERROR; } return GETDNS_RETURN_GOOD; } static getdns_return_t ipseckey_gateway_dict2wire( const getdns_dict *dict, uint8_t *rdata, uint8_t *rdf, size_t *rdf_len) { getdns_return_t r; getdns_bindata *value; if ((r = getdns_dict_get_bindata(dict, "gateway", &value))) return r; else return ipseckey_gateway_2wire(value, rdata, rdf, rdf_len); } static getdns_return_t ipseckey_gateway_list2wire(const getdns_list *list, size_t i, uint8_t *rdata, uint8_t *rdf, size_t *rdf_len) { getdns_return_t r; getdns_bindata *value; if ((r = getdns_list_get_bindata(list, i, &value))) return r; else return ipseckey_gateway_2wire(value, rdata, rdf, rdf_len); } static _getdns_rdf_special ipseckey_gateway = { ipseckey_gateway_rdf_end, ipseckey_gateway_wire2dict, ipseckey_gateway_wire2list, ipseckey_gateway_dict2wire, ipseckey_gateway_list2wire }; static const uint8_t * hip_pk_algorithm_rdf_end( const uint8_t *pkt, const uint8_t *pkt_end, const uint8_t *rdf) { return rdf + 4 > pkt_end ? NULL : rdf + 4 + *rdf + gldns_read_uint16(rdf + 2) > pkt_end ? NULL : rdf + 1; } static getdns_return_t hip_pk_algorithm_wire2dict(getdns_dict *dict, const uint8_t *rdf) { return getdns_dict_set_int(dict, "pk_algorithm", rdf[1]); } static getdns_return_t hip_pk_algorithm_wire2list(getdns_list *list, const uint8_t *rdf) { return _getdns_list_append_int(list, rdf[1]); } static getdns_return_t hip_pk_algorithm_2wire(uint32_t value, uint8_t *rdata, uint8_t *rdf, size_t *rdf_len) { if (rdata != rdf) return GETDNS_RETURN_GENERIC_ERROR; if (value > 0xFF) return GETDNS_RETURN_INVALID_PARAMETER; if (*rdf_len < 4) { *rdf_len = 4; return GETDNS_RETURN_NEED_MORE_SPACE; } *rdf_len = 4; rdata[1] = value; return GETDNS_RETURN_GOOD; } static getdns_return_t hip_pk_algorithm_dict2wire( const getdns_dict *dict,uint8_t *rdata, uint8_t *rdf, size_t *rdf_len) { getdns_return_t r; uint32_t value; if ((r = getdns_dict_get_int(dict, "pk_algorithm", &value))) return r; else return hip_pk_algorithm_2wire(value, rdata, rdf, rdf_len); } static getdns_return_t hip_pk_algorithm_list2wire(const getdns_list *list, size_t i, uint8_t *rdata, uint8_t *rdf, size_t *rdf_len) { getdns_return_t r; uint32_t value; if ((r = getdns_list_get_int(list, i, &value))) return r; else return hip_pk_algorithm_2wire(value, rdata, rdf, rdf_len); } static _getdns_rdf_special hip_pk_algorithm = { hip_pk_algorithm_rdf_end, hip_pk_algorithm_wire2dict, hip_pk_algorithm_wire2list, hip_pk_algorithm_dict2wire, hip_pk_algorithm_list2wire }; static const uint8_t * hip_hit_rdf_end(const uint8_t *pkt, const uint8_t *pkt_end, const uint8_t *rdf) { return rdf + 3 > pkt_end ? NULL : rdf + 3 + rdf[-1] + gldns_read_uint16(rdf + 1) > pkt_end ? NULL : rdf + 1; } static getdns_return_t hip_hit_wire2dict(getdns_dict *dict, const uint8_t *rdf) { return _getdns_dict_set_const_bindata(dict, "hit", rdf[-1], rdf + 3); } static getdns_return_t hip_hit_wire2list(getdns_list *list, const uint8_t *rdf) { return _getdns_list_append_const_bindata(list, rdf[-1], rdf + 3); } static getdns_return_t hip_hit_2wire( const getdns_bindata *value, uint8_t *rdata, uint8_t *rdf, size_t *rdf_len) { if (rdata != rdf - 4) return GETDNS_RETURN_GENERIC_ERROR; if (value && value->size > 0xFF) return GETDNS_RETURN_INVALID_PARAMETER; if (!value || value->size == 0) { rdata[0] = 0; *rdf_len = 0; return GETDNS_RETURN_GOOD; } if (value->size > *rdf_len) { *rdf_len = value->size; return GETDNS_RETURN_NEED_MORE_SPACE; } *rdf_len = value->size; rdata[0] = value->size; (void)memcpy(rdf, value->data, value->size); return GETDNS_RETURN_GOOD; } static getdns_return_t hip_hit_dict2wire( const getdns_dict *dict, uint8_t *rdata, uint8_t *rdf, size_t *rdf_len) { getdns_return_t r; getdns_bindata *value; if ((r = getdns_dict_get_bindata(dict, "hit", &value))) return r; else return hip_hit_2wire(value, rdata, rdf, rdf_len); } static getdns_return_t hip_hit_list2wire(const getdns_list *list, size_t i, uint8_t *rdata, uint8_t *rdf, size_t *rdf_len) { getdns_return_t r; getdns_bindata *value; if ((r = getdns_list_get_bindata(list, i, &value))) return r; else return hip_hit_2wire(value, rdata, rdf, rdf_len); } static _getdns_rdf_special hip_hit = { hip_hit_rdf_end, hip_hit_wire2dict, hip_hit_wire2list, hip_hit_dict2wire, hip_hit_list2wire }; static const uint8_t * hip_public_key_rdf_end( const uint8_t *pkt, const uint8_t *pkt_end, const uint8_t *rdf) { return rdf + 2 > pkt_end ? NULL : rdf + 2 + rdf[-2] + gldns_read_uint16(rdf) > pkt_end ? NULL : rdf + 2 + rdf[-2] + gldns_read_uint16(rdf); } static getdns_return_t hip_public_key_wire2dict(getdns_dict *dict, const uint8_t *rdf) { return _getdns_dict_set_const_bindata( dict, "public_key", gldns_read_uint16(rdf), rdf + 2 + rdf[-2]); } static getdns_return_t hip_public_key_wire2list(getdns_list *list, const uint8_t *rdf) { return _getdns_list_append_const_bindata( list, gldns_read_uint16(rdf), rdf + 2 + rdf[-2]); } static getdns_return_t hip_public_key_2wire( const getdns_bindata *value, uint8_t *rdata, uint8_t *rdf, size_t *rdf_len) { if (rdata > rdf - 4 || rdata + 4 + rdata[0] != rdf) return GETDNS_RETURN_GENERIC_ERROR; if (value && value->size > 0xFFFF) return GETDNS_RETURN_INVALID_PARAMETER; if (!value || value->size == 0) { rdata[2] = rdata[3] = 0; *rdf_len = 0; return GETDNS_RETURN_GOOD; } if (value->size > *rdf_len) { *rdf_len = value->size; return GETDNS_RETURN_NEED_MORE_SPACE; } *rdf_len = value->size; gldns_write_uint16(rdata + 2, value->size); (void)memcpy(rdf, value->data, value->size); return GETDNS_RETURN_GOOD; } static getdns_return_t hip_public_key_dict2wire( const getdns_dict *dict, uint8_t *rdata, uint8_t *rdf, size_t *rdf_len) { getdns_return_t r; getdns_bindata *value; if ((r = getdns_dict_get_bindata(dict, "public_key", &value))) return r; else return hip_public_key_2wire(value, rdata, rdf, rdf_len); } static getdns_return_t hip_public_key_list2wire( const getdns_list *list, size_t i, uint8_t *rdata, uint8_t *rdf, size_t *rdf_len) { getdns_return_t r; getdns_bindata *value; if ((r = getdns_list_get_bindata(list, i, &value))) return r; else return hip_public_key_2wire(value, rdata, rdf, rdf_len); } static _getdns_rdf_special hip_public_key = { hip_public_key_rdf_end, hip_public_key_wire2dict, hip_public_key_wire2list, hip_public_key_dict2wire, hip_public_key_list2wire }; static _getdns_rdata_def a_rdata[] = { { "ipv4_address" , GETDNS_RDF_A }}; static _getdns_rdata_def ns_rdata[] = { { "nsdname" , GETDNS_RDF_N_C }}; static _getdns_rdata_def md_rdata[] = { { "madname" , GETDNS_RDF_N_C }}; static _getdns_rdata_def cname_rdata[] = { { "cname" , GETDNS_RDF_N_C }}; static _getdns_rdata_def soa_rdata[] = { { "mname" , GETDNS_RDF_N_C }, { "rname" , GETDNS_RDF_N_C }, { "serial" , GETDNS_RDF_I4 }, { "refresh" , GETDNS_RDF_I4 }, { "retry" , GETDNS_RDF_I4 }, { "expire" , GETDNS_RDF_I4 }, { "minimum" , GETDNS_RDF_I4 }}; static _getdns_rdata_def mg_rdata[] = { { "mgmname" , GETDNS_RDF_N_C }}; static _getdns_rdata_def mr_rdata[] = { { "newname" , GETDNS_RDF_N_C }}; static _getdns_rdata_def null_rdata[] = { { "anything" , GETDNS_RDF_X }}; static _getdns_rdata_def wks_rdata[] = { { "address" , GETDNS_RDF_A }, { "protocol" , GETDNS_RDF_I1 }, { "bitmap" , GETDNS_RDF_X }}; static _getdns_rdata_def ptr_rdata[] = { { "ptrdname" , GETDNS_RDF_N_C }}; static _getdns_rdata_def hinfo_rdata[] = { { "cpu" , GETDNS_RDF_S }, { "os" , GETDNS_RDF_S }}; static _getdns_rdata_def minfo_rdata[] = { { "rmailbx" , GETDNS_RDF_N_C }, { "emailbx" , GETDNS_RDF_N_C }}; static _getdns_rdata_def mx_rdata[] = { { "preference" , GETDNS_RDF_I2 }, { "exchange" , GETDNS_RDF_N_C }}; static _getdns_rdata_def txt_rdata[] = { { "txt_strings" , GETDNS_RDF_S_M }}; static _getdns_rdata_def rp_rdata[] = { { "mbox_dname" , GETDNS_RDF_N }, { "txt_dname" , GETDNS_RDF_N }}; static _getdns_rdata_def afsdb_rdata[] = { { "subtype" , GETDNS_RDF_I2 }, { "hostname" , GETDNS_RDF_N }}; static _getdns_rdata_def x25_rdata[] = { { "psdn_address" , GETDNS_RDF_S }}; static _getdns_rdata_def isdn_rdata[] = { { "isdn_address" , GETDNS_RDF_S }, { "sa" , GETDNS_RDF_S }}; static _getdns_rdata_def rt_rdata[] = { { "preference" , GETDNS_RDF_I2 }, { "intermediate_host" , GETDNS_RDF_N }}; static _getdns_rdata_def nsap_rdata[] = { { "nsap" , GETDNS_RDF_X }}; static _getdns_rdata_def sig_rdata[] = { { "sig_obsolete" , GETDNS_RDF_X }}; static _getdns_rdata_def key_rdata[] = { { "key_obsolete" , GETDNS_RDF_X }}; static _getdns_rdata_def px_rdata[] = { { "preference" , GETDNS_RDF_I2 }, { "map822" , GETDNS_RDF_N }, { "mapx400" , GETDNS_RDF_N }}; static _getdns_rdata_def gpos_rdata[] = { { "longitude" , GETDNS_RDF_S }, { "latitude" , GETDNS_RDF_S }, { "altitude" , GETDNS_RDF_S }}; static _getdns_rdata_def aaaa_rdata[] = { { "ipv6_address" , GETDNS_RDF_AAAA }}; static _getdns_rdata_def loc_rdata[] = { { "loc_obsolete" , GETDNS_RDF_X }}; static _getdns_rdata_def nxt_rdata[] = { { "nxt_obsolete" , GETDNS_RDF_X }}; static _getdns_rdata_def srv_rdata[] = { { "priority" , GETDNS_RDF_I2 }, { "weight" , GETDNS_RDF_I2 }, { "port" , GETDNS_RDF_I2 }, { "target" , GETDNS_RDF_N }}; static _getdns_rdata_def atma_rdata[] = { { "format" , GETDNS_RDF_X }}; static _getdns_rdata_def naptr_rdata[] = { { "order" , GETDNS_RDF_I2 }, { "preference" , GETDNS_RDF_I2 }, { "flags" , GETDNS_RDF_S }, { "service" , GETDNS_RDF_S }, { "regexp" , GETDNS_RDF_S }, { "replacement" , GETDNS_RDF_N }}; static _getdns_rdata_def kx_rdata[] = { { "preference" , GETDNS_RDF_I2 }, { "exchanger" , GETDNS_RDF_N }}; static _getdns_rdata_def cert_rdata[] = { { "type" , GETDNS_RDF_I2 }, { "key_tag" , GETDNS_RDF_I2 }, { "algorithm" , GETDNS_RDF_I1 }, { "certificate_or_crl" , GETDNS_RDF_B }}; static _getdns_rdata_def a6_rdata[] = { { "a6_obsolete" , GETDNS_RDF_X }}; static _getdns_rdata_def dname_rdata[] = { { "target" , GETDNS_RDF_N }}; static _getdns_rdata_def opt_rdata[] = { { "options" , GETDNS_RDF_R }, { "option_code" , GETDNS_RDF_I2 }, { "option_data" , GETDNS_RDF_X_S }}; static _getdns_rdata_def apl_rdata[] = { { "apitems" , GETDNS_RDF_R }, { "address_family" , GETDNS_RDF_I2 }, { "prefix" , GETDNS_RDF_I1 }, { "n" , GETDNS_RDF_SPECIAL, &apl_n }, { "afdpart" , GETDNS_RDF_SPECIAL, &apl_afdpart }}; static _getdns_rdata_def ds_rdata[] = { { "key_tag" , GETDNS_RDF_I2 }, { "algorithm" , GETDNS_RDF_I1 }, { "digest_type" , GETDNS_RDF_I1 }, { "digest" , GETDNS_RDF_X }}; static _getdns_rdata_def sshfp_rdata[] = { { "algorithm" , GETDNS_RDF_I1 }, { "fp_type" , GETDNS_RDF_I1 }, { "fingerprint" , GETDNS_RDF_X }}; static _getdns_rdata_def ipseckey_rdata[] = { { "algorithm" , GETDNS_RDF_I1 }, { "gateway_type" , GETDNS_RDF_I1 }, { "precedence" , GETDNS_RDF_I1 }, { "gateway" , GETDNS_RDF_SPECIAL, &ipseckey_gateway }, { "public_key" , GETDNS_RDF_B }}; static _getdns_rdata_def rrsig_rdata[] = { { "type_covered" , GETDNS_RDF_I2 }, { "algorithm" , GETDNS_RDF_I1 }, { "labels" , GETDNS_RDF_I1 }, { "original_ttl" , GETDNS_RDF_I4 }, { "signature_expiration" , GETDNS_RDF_T }, { "signature_inception" , GETDNS_RDF_T }, { "key_tag" , GETDNS_RDF_I2 }, { "signers_name" , GETDNS_RDF_N }, { "signature" , GETDNS_RDF_B }}; static _getdns_rdata_def nsec_rdata[] = { { "next_domain_name" , GETDNS_RDF_N }, { "type_bit_maps" , GETDNS_RDF_X }}; static _getdns_rdata_def dnskey_rdata[] = { { "flags" , GETDNS_RDF_I2 }, { "protocol" , GETDNS_RDF_I1 }, { "algorithm" , GETDNS_RDF_I1 }, { "public_key" , GETDNS_RDF_B }}; static _getdns_rdata_def dhcid_rdata[] = { { "dhcid_opaque" , GETDNS_RDF_B }}; static _getdns_rdata_def nsec3_rdata[] = { { "hash_algorithm" , GETDNS_RDF_I1 }, { "flags" , GETDNS_RDF_I1 }, { "iterations" , GETDNS_RDF_I2 }, { "salt" , GETDNS_RDF_X_C }, { "next_hashed_owner_name" , GETDNS_RDF_B32_C}, { "type_bit_maps" , GETDNS_RDF_X }}; static _getdns_rdata_def nsec3param_rdata[] = { { "hash_algorithm" , GETDNS_RDF_I1 }, { "flags" , GETDNS_RDF_I1 }, { "iterations" , GETDNS_RDF_I2 }, { "salt" , GETDNS_RDF_X_C }}; static _getdns_rdata_def tlsa_rdata[] = { { "certificate_usage" , GETDNS_RDF_I1 }, { "selector" , GETDNS_RDF_I1 }, { "matching_type" , GETDNS_RDF_I1 }, { "certificate_association_data", GETDNS_RDF_X }}; static _getdns_rdata_def hip_rdata[] = { { "pk_algorithm" , GETDNS_RDF_SPECIAL, &hip_pk_algorithm }, { "hit" , GETDNS_RDF_SPECIAL, &hip_hit }, { "public_key" , GETDNS_RDF_SPECIAL, &hip_public_key }, { "rendezvous_servers" , GETDNS_RDF_N_M }}; static _getdns_rdata_def csync_rdata[] = { { "serial" , GETDNS_RDF_I4 }, { "flags" , GETDNS_RDF_I2 }, { "type_bit_maps" , GETDNS_RDF_X }}; static _getdns_rdata_def spf_rdata[] = { { "text" , GETDNS_RDF_S_M }}; static _getdns_rdata_def nid_rdata[] = { { "preference" , GETDNS_RDF_I2 }, { "node_id" , GETDNS_RDF_AA }}; static _getdns_rdata_def l32_rdata[] = { { "preference" , GETDNS_RDF_I2 }, { "locator32" , GETDNS_RDF_A }}; static _getdns_rdata_def l64_rdata[] = { { "preference" , GETDNS_RDF_I2 }, { "locator64" , GETDNS_RDF_AA }}; static _getdns_rdata_def lp_rdata[] = { { "preference" , GETDNS_RDF_I2 }, { "fqdn" , GETDNS_RDF_N }}; static _getdns_rdata_def eui48_rdata[] = { { "eui48_address" , GETDNS_RDF_X6 }}; static _getdns_rdata_def eui64_rdata[] = { { "eui64_address" , GETDNS_RDF_X8 }}; static _getdns_rdata_def tkey_rdata[] = { { "algorithm" , GETDNS_RDF_N }, { "inception" , GETDNS_RDF_T }, { "expiration" , GETDNS_RDF_T }, { "mode" , GETDNS_RDF_I2 }, { "error" , GETDNS_RDF_I2 }, { "key_data" , GETDNS_RDF_X_S }, { "other_data" , GETDNS_RDF_X_S }}; static _getdns_rdata_def tsig_rdata[] = { { "algorithm" , GETDNS_RDF_N }, { "time_signed" , GETDNS_RDF_T6 }, { "fudge" , GETDNS_RDF_I2 }, { "mac" , GETDNS_RDF_X_S }, { "original_id" , GETDNS_RDF_I2 }, { "error" , GETDNS_RDF_I2 }, { "other_data" , GETDNS_RDF_X_S }}; static _getdns_rdata_def uri_rdata[] = { { "priority" , GETDNS_RDF_I2 }, { "weight" , GETDNS_RDF_I2 }, { "target" , GETDNS_RDF_S_L }}; static _getdns_rdata_def caa_rdata[] = { { "flags" , GETDNS_RDF_I1 }, { "tag" , GETDNS_RDF_S }, { "value" , GETDNS_RDF_S_L }}; static _getdns_rdata_def dlv_rdata[] = { { "key_tag" , GETDNS_RDF_I2 }, { "algorithm" , GETDNS_RDF_I1 }, { "digest_type" , GETDNS_RDF_I1 }, { "digest" , GETDNS_RDF_X }}; static _getdns_rr_def _getdns_rr_defs[] = { { NULL, NULL, 0 }, { "A", a_rdata, ALEN( a_rdata) }, /* 1 - */ { "NS", ns_rdata, ALEN( ns_rdata) }, { "MD", md_rdata, ALEN( md_rdata) }, { "MF", md_rdata, ALEN( md_rdata) }, { "CNAME", cname_rdata, ALEN( cname_rdata) }, { "SOA", soa_rdata, ALEN( soa_rdata) }, { "MB", md_rdata, ALEN( md_rdata) }, { "MG", mg_rdata, ALEN( mg_rdata) }, { "MR", mr_rdata, ALEN( mr_rdata) }, { "NULL", null_rdata, ALEN( null_rdata) }, { "WKS", wks_rdata, ALEN( wks_rdata) }, { "PTR", ptr_rdata, ALEN( ptr_rdata) }, { "HINFO", hinfo_rdata, ALEN( hinfo_rdata) }, { "MINFO", minfo_rdata, ALEN( minfo_rdata) }, { "MX", mx_rdata, ALEN( mx_rdata) }, { "TXT", txt_rdata, ALEN( txt_rdata) }, { "RP", rp_rdata, ALEN( rp_rdata) }, { "AFSDB", afsdb_rdata, ALEN( afsdb_rdata) }, { "X25", x25_rdata, ALEN( x25_rdata) }, { "ISDN", isdn_rdata, ALEN( isdn_rdata) }, { "RT", rt_rdata, ALEN( rt_rdata) }, { "NSAP", nsap_rdata, ALEN( nsap_rdata) }, /* - 22 */ { NULL, NULL, 0 }, { "SIG", sig_rdata, ALEN( sig_rdata) }, /* 24 - */ { "KEY", key_rdata, ALEN( key_rdata) }, { "PX", px_rdata, ALEN( px_rdata) }, { "GPOS", gpos_rdata, ALEN( gpos_rdata) }, { "AAAA", aaaa_rdata, ALEN( aaaa_rdata) }, { "LOC", loc_rdata, ALEN( loc_rdata) }, { "NXT", nxt_rdata, ALEN( nxt_rdata) }, { "EID", UNKNOWN_RDATA, 0 }, { "NIMLOC", UNKNOWN_RDATA, 0 }, { "SRV", srv_rdata, ALEN( srv_rdata) }, { "ATMA", atma_rdata, ALEN( atma_rdata) }, { "NAPTR", naptr_rdata, ALEN( naptr_rdata) }, { "KX", kx_rdata, ALEN( kx_rdata) }, { "CERT", cert_rdata, ALEN( cert_rdata) }, { "A6", a6_rdata, ALEN( a6_rdata) }, { "DNAME", dname_rdata, ALEN( dname_rdata) }, { "SINK", UNKNOWN_RDATA, 0 }, { "OPT", opt_rdata, ALEN( opt_rdata) }, { "APL", apl_rdata, ALEN( apl_rdata) }, { "DS", ds_rdata, ALEN( ds_rdata) }, { "SSHFP", sshfp_rdata, ALEN( sshfp_rdata) }, { "IPSECKEY", ipseckey_rdata, ALEN( ipseckey_rdata) }, { "RRSIG", rrsig_rdata, ALEN( rrsig_rdata) }, { "NSEC", nsec_rdata, ALEN( nsec_rdata) }, { "DNSKEY", dnskey_rdata, ALEN( dnskey_rdata) }, { "DHCID", dhcid_rdata, ALEN( dhcid_rdata) }, { "NSEC3", nsec3_rdata, ALEN( nsec3_rdata) }, { "NSEC3PARAM", nsec3param_rdata, ALEN(nsec3param_rdata) }, { "TLSA", tlsa_rdata, ALEN( tlsa_rdata) }, /* - 52 */ { NULL, NULL, 0 }, { NULL, NULL, 0 }, { "HIP", hip_rdata, ALEN( hip_rdata) }, /* 55 - */ { "NINFO", UNKNOWN_RDATA, 0 }, { "RKEY", UNKNOWN_RDATA, 0 }, { "TALINK", UNKNOWN_RDATA, 0 }, { "CDS", ds_rdata, ALEN( ds_rdata) }, { "CDNSKEY", dnskey_rdata, ALEN( dnskey_rdata) }, { "OPENPGPKEY", UNKNOWN_RDATA, 0 }, /* 61 - */ { "CSYNC", csync_rdata, ALEN( csync_rdata) }, /* - 62 */ { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { "SPF", spf_rdata, ALEN( spf_rdata) }, /* 99 - */ { "UINFO", UNKNOWN_RDATA, 0 }, { "UID", UNKNOWN_RDATA, 0 }, { "GID", UNKNOWN_RDATA, 0 }, { "UNSPEC", UNKNOWN_RDATA, 0 }, { "NID", nid_rdata, ALEN( nid_rdata) }, { "L32", l32_rdata, ALEN( l32_rdata) }, { "L64", l64_rdata, ALEN( l64_rdata) }, { "LP", lp_rdata, ALEN( lp_rdata) }, { "EUI48", eui48_rdata, ALEN( eui48_rdata) }, { "EUI64", eui64_rdata, ALEN( eui64_rdata) }, /* - 109 */ { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 }, { "TKEY", tkey_rdata, ALEN( tkey_rdata) }, /* 249 - */ { "TSIG", tsig_rdata, ALEN( tsig_rdata) }, /* - 250 */ { NULL, NULL, 0 }, { NULL, NULL, 0 }, { "MAILB", UNKNOWN_RDATA, 0 }, /* 253 - */ { "MAILA", UNKNOWN_RDATA, 0 }, /* - 254 */ { NULL, NULL, 0 }, { "URI", uri_rdata, ALEN( uri_rdata) }, /* 256 - */ { "CAA", caa_rdata, ALEN( caa_rdata) }, /* - 257 */ { "TA", UNKNOWN_RDATA, 0 }, /* 32768 */ { "DLV", dlv_rdata, ALEN( dlv_rdata) } /* 32769 */ }; const _getdns_rr_def * _getdns_rr_def_lookup(uint16_t rr_type) { if (rr_type <= 257) return &_getdns_rr_defs[rr_type]; else if (rr_type == 32768) return &_getdns_rr_defs[258]; else if (rr_type == 32769) return &_getdns_rr_defs[259]; return _getdns_rr_defs; } const char * _getdns_rr_type_name(int rr_type) { return _getdns_rr_def_lookup(rr_type)->name; } static void write_int_rdata(gldns_buffer *buf, _getdns_rdf_type type, uint32_t value) { size_t j; for (j = type & GETDNS_RDF_FIXEDSZ; j; j--) gldns_buffer_write_u8(buf, (uint8_t)(value >> (8 * (j - 1))) & 0xff); } static void write_bindata_rdata(gldns_buffer *buf, _getdns_rdf_type type, getdns_bindata *bindata) { if (type & GETDNS_RDF_LEN_VAL) write_int_rdata(buf, type >> 8, bindata->size); gldns_buffer_write(buf, bindata->data, bindata->size); } static getdns_return_t write_rdata_field(gldns_buffer *buf, uint8_t *rdata_start, const _getdns_rdata_def *rd_def, getdns_dict *rdata) { getdns_return_t r; getdns_list *list; uint32_t value; getdns_bindata *bindata; size_t i, rdf_len; if (rd_def->type & GETDNS_RDF_INTEGER) { if (!(rd_def->type & GETDNS_RDF_REPEAT)) { if ((r = getdns_dict_get_int( rdata, rd_def->name, &value))) return r; else write_int_rdata(buf, rd_def->type, value); } else if ((r = getdns_dict_get_list( rdata, rd_def->name, &list))) return r == GETDNS_RETURN_NO_SUCH_DICT_NAME ? GETDNS_RETURN_GOOD : r; else for ( i = 0 ; GETDNS_RETURN_GOOD == (r = getdns_list_get_int(list, i, &value)) ; i++) write_int_rdata(buf, rd_def->type, value); } else if (rd_def->type & GETDNS_RDF_BINDATA) { if (!(rd_def->type & GETDNS_RDF_REPEAT)) { if ((r = getdns_dict_get_bindata( rdata, rd_def->name, &bindata))) return r; else write_bindata_rdata(buf, rd_def->type, bindata); } else if ((r = getdns_dict_get_list( rdata, rd_def->name, &list))) return r == GETDNS_RETURN_NO_SUCH_DICT_NAME ? GETDNS_RETURN_GOOD : r; else for ( i = 0 ; GETDNS_RETURN_GOOD == (r = getdns_list_get_bindata(list, i, &bindata)) ; i++) write_bindata_rdata(buf, rd_def->type, bindata); } else if (!(rd_def->type & GETDNS_RDF_SPECIAL)) { /* Unknown rdata type */ return GETDNS_RETURN_GENERIC_ERROR; } else if (!(rd_def->type & GETDNS_RDF_REPEAT)) { rdf_len = gldns_buffer_remaining(buf); r = rd_def->special->dict2wire(rdata, rdata_start, gldns_buffer_current(buf), &rdf_len); if (r == GETDNS_RETURN_GOOD || r == GETDNS_RETURN_NEED_MORE_SPACE) gldns_buffer_skip(buf, rdf_len); if (r) return r; } else if ((r = getdns_dict_get_list(rdata, rd_def->name, &list))) { return r == GETDNS_RETURN_NO_SUCH_DICT_NAME ? GETDNS_RETURN_GOOD : r; } else for ( i = 0; r == GETDNS_RETURN_GOOD; i++ ) { rdf_len = gldns_buffer_remaining(buf); r = rd_def->special->list2wire(list, i, rdata_start, gldns_buffer_current(buf), &rdf_len); if (r == GETDNS_RETURN_GOOD || r == GETDNS_RETURN_NEED_MORE_SPACE) gldns_buffer_skip(buf, rdf_len); } return r != GETDNS_RETURN_NO_SUCH_LIST_ITEM ? r : GETDNS_RETURN_GOOD; } getdns_return_t _getdns_rr_dict2wire(const getdns_dict *rr_dict, gldns_buffer *buf) { getdns_return_t r = GETDNS_RETURN_GOOD; getdns_bindata *name; getdns_bindata *rdata_raw; getdns_dict *rdata; uint32_t rr_type; uint32_t rr_class = GETDNS_RRCLASS_IN; uint32_t rr_ttl = 0; const _getdns_rr_def *rr_def; const _getdns_rdata_def *rd_def, *rep_rd_def; int n_rdata_fields, rep_n_rdata_fields; size_t rdata_size_mark; uint8_t *rdata_start; getdns_list *list; size_t i; assert(rr_dict); assert(buf); if ((r = getdns_dict_get_bindata(rr_dict, "name", &name))) return r; gldns_buffer_write(buf, name->data, name->size); if ((r = getdns_dict_get_int(rr_dict, "type", &rr_type))) return r; gldns_buffer_write_u16(buf, (uint16_t)rr_type); (void) getdns_dict_get_int(rr_dict, "class", &rr_class); gldns_buffer_write_u16(buf, (uint16_t)rr_class); (void) getdns_dict_get_int(rr_dict, "ttl", &rr_ttl); gldns_buffer_write_u32(buf, rr_ttl); /* Does rdata contain compressed names? * Because rdata_raw is unusable then. */ rr_def = _getdns_rr_def_lookup(rr_type); for ( rd_def = rr_def->rdata , n_rdata_fields = rr_def->n_rdata_fields ; n_rdata_fields ; n_rdata_fields-- , rd_def++ ) { if (rd_def->type & GETDNS_RDF_COMPRESSED) break; } if ((r = getdns_dict_get_dict(rr_dict, "rdata", &rdata))) { if (r == GETDNS_RETURN_NO_SUCH_DICT_NAME) { gldns_buffer_write_u16(buf, 0); r = GETDNS_RETURN_GOOD; } } else if (n_rdata_fields == 0 && GETDNS_RETURN_GOOD == (r = getdns_dict_get_bindata(rdata, "rdata_raw", &rdata_raw))) { gldns_buffer_write_u16(buf, (uint16_t)rdata_raw->size); gldns_buffer_write(buf, rdata_raw->data, rdata_raw->size); } else if (n_rdata_fields || r == GETDNS_RETURN_NO_SUCH_DICT_NAME) { r = GETDNS_RETURN_GOOD; rdata_size_mark = gldns_buffer_position(buf); gldns_buffer_skip(buf, 2); rdata_start = gldns_buffer_current(buf); for ( rd_def = rr_def->rdata , n_rdata_fields = rr_def->n_rdata_fields ; n_rdata_fields ; n_rdata_fields-- , rd_def++ ) { if (rd_def->type == GETDNS_RDF_REPEAT) break; if ((r = write_rdata_field(buf, rdata_start, rd_def, rdata))) break; } if (n_rdata_fields == 0 || r) { /* pass */; } else if ((r = getdns_dict_get_list( rdata, rd_def->name, &list))) { /* pass */; } else for ( i = 0 ; r == GETDNS_RETURN_GOOD ; i++) { if ((r = getdns_list_get_dict(list, i, &rdata))) { if (r == GETDNS_RETURN_NO_SUCH_LIST_ITEM) r = GETDNS_RETURN_GOOD; break; } for ( rep_rd_def = rd_def + 1 , rep_n_rdata_fields = n_rdata_fields - 1 ; rep_n_rdata_fields ; rep_n_rdata_fields--, rep_rd_def++ ) { if ((r = write_rdata_field(buf, rdata_start, rep_rd_def, rdata))) break; } } gldns_buffer_write_u16_at(buf, rdata_size_mark, (uint16_t)(gldns_buffer_position(buf)-rdata_size_mark-2)); } return r; }