diff --git a/ChangeLog b/ChangeLog index dccc733d..b50e946c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,8 @@ * 2019-??-??: Version 1.?.? * Issue #419: Escape backslashed when printing in JSON format. Thanks boB Rudis + * DOA rr-type + * AMTRELAY rr-type * 2019-01-11: Version 1.5.1 * Introduce proof of concept GnuTLS implementation. Incomplete support diff --git a/src/const-info.c b/src/const-info.c index ebff80a4..6265c523 100644 --- a/src/const-info.c +++ b/src/const-info.c @@ -303,6 +303,7 @@ static struct const_name_info consts_name_info[] = { { "GETDNS_RRTYPE_A6", 38 }, { "GETDNS_RRTYPE_AAAA", 28 }, { "GETDNS_RRTYPE_AFSDB", 18 }, + { "GETDNS_RRTYPE_AMTRELAY", 260 }, { "GETDNS_RRTYPE_ANY", 255 }, { "GETDNS_RRTYPE_APL", 42 }, { "GETDNS_RRTYPE_ATMA", 34 }, diff --git a/src/getdns/getdns.h.in b/src/getdns/getdns.h.in index 5e4873b5..b2702d43 100644 --- a/src/getdns/getdns.h.in +++ b/src/getdns/getdns.h.in @@ -439,6 +439,7 @@ typedef enum getdns_callback_type_t { #define GETDNS_RRTYPE_CAA 257 #define GETDNS_RRTYPE_AVC 258 #define GETDNS_RRTYPE_DOA 259 +#define GETDNS_RRTYPE_AMTRELAY 260 #define GETDNS_RRTYPE_TA 32768 #define GETDNS_RRTYPE_DLV 32769 /** @} diff --git a/src/gldns/rrdef.c b/src/gldns/rrdef.c index 9f27a5a1..114f807e 100644 --- a/src/gldns/rrdef.c +++ b/src/gldns/rrdef.c @@ -232,6 +232,15 @@ static const gldns_rdf_type type_caa_wireformat[] = { GLDNS_RDF_TYPE_TAG, GLDNS_RDF_TYPE_LONG_STR }; +#ifdef DRAFT_RRTYPES +static const gldns_rdf_type type_doa_wireformat[] = { + GLDNS_RDF_TYPE_INT32, GLDNS_RDF_TYPE_INT32, GLDNS_RDF_TYPE_INT8, + GLDNS_RDF_TYPE_STR, GLDNS_RDF_TYPE_B64 +}; +static const gldns_rdf_type type_amtrelay_wireformat[] = { + GLDNS_RDF_TYPE_AMTRELAY +}; +#endif /* All RR's defined in 1035 are well known and can thus * be compressed. See RFC3597. These RR's are: @@ -608,8 +617,14 @@ static gldns_rr_descriptor rdata_field_descriptors[] = { #ifdef DRAFT_RRTYPES /* 258 */ {GLDNS_RR_TYPE_AVC, "AVC", 1, 0, NULL, GLDNS_RDF_TYPE_STR, GLDNS_RR_NO_COMPRESS, 0 }, + /* 259 */ + {GLDNS_RR_TYPE_DOA, "DOA", 1, 0, type_doa_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, + /* 260 */ + {GLDNS_RR_TYPE_AMTRELAY, "AMTRELAY", 1, 0, type_amtrelay_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, #else {GLDNS_RR_TYPE_NULL, "TYPE258", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE259", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, +{GLDNS_RR_TYPE_NULL, "TYPE260", 1, 1, type_0_wireformat, GLDNS_RDF_TYPE_NONE, GLDNS_RR_NO_COMPRESS, 0 }, #endif /* split in array, no longer contiguous */ diff --git a/src/gldns/rrdef.h b/src/gldns/rrdef.h index a11984b3..a393dc8e 100644 --- a/src/gldns/rrdef.h +++ b/src/gldns/rrdef.h @@ -38,7 +38,7 @@ extern "C" { #define GLDNS_KEY_REVOKE_KEY 0x0080 /* used to revoke KSK, rfc 5011 */ /* The first fields are contiguous and can be referenced instantly */ -#define GLDNS_RDATA_FIELD_DESCRIPTORS_COMMON 259 +#define GLDNS_RDATA_FIELD_DESCRIPTORS_COMMON 260 /** lookuptable for rr classes */ extern struct gldns_struct_lookup_table* gldns_rr_classes; @@ -226,7 +226,8 @@ enum gldns_enum_rr_type GLDNS_RR_TYPE_URI = 256, /* RFC 7553 */ GLDNS_RR_TYPE_CAA = 257, /* RFC 6844 */ GLDNS_RR_TYPE_AVC = 258, - GLDNS_RR_TYPE_DOA = 259, + GLDNS_RR_TYPE_DOA = 259, /* draft-durand-doa-over-dns */ + GLDNS_RR_TYPE_AMTRELAY= 260, /* draft-ietf-mboned-driad-amt-discovery */ /** DNSSEC Trust Authorities */ GLDNS_RR_TYPE_TA = 32768, @@ -351,6 +352,9 @@ enum gldns_enum_rdf_type */ GLDNS_RDF_TYPE_LONG_STR, + /* draft-ietf-mboned-driad-amt-discovery */ + GLDNS_RDF_TYPE_AMTRELAY, + /** TSIG extended 16bit error value */ GLDNS_RDF_TYPE_TSIGERROR, diff --git a/src/gldns/str2wire.c b/src/gldns/str2wire.c index 26c2ea6f..708df50e 100644 --- a/src/gldns/str2wire.c +++ b/src/gldns/str2wire.c @@ -997,6 +997,8 @@ int gldns_str2wire_rdf_buf(const char* str, uint8_t* rd, size_t* len, return gldns_str2wire_hip_buf(str, rd, len); case GLDNS_RDF_TYPE_INT16_DATA: return gldns_str2wire_int16_data_buf(str, rd, len); + case GLDNS_RDF_TYPE_AMTRELAY: + return gldns_str2wire_amtrelay_buf(str, rd, len); case GLDNS_RDF_TYPE_UNKNOWN: case GLDNS_RDF_TYPE_SERVICE: return GLDNS_WIREPARSE_ERR_NOT_IMPL; @@ -2118,3 +2120,77 @@ int gldns_str2wire_int16_data_buf(const char* str, uint8_t* rd, size_t* len) *len = ((size_t)n)+2; return GLDNS_WIREPARSE_ERR_OK; } + +int gldns_str2wire_amtrelay_buf(const char* str, uint8_t* rd, size_t* len) +{ + size_t relay_len = 0; + int s; + uint8_t relay_type; + char token[512]; + gldns_buffer strbuf; + gldns_buffer_init_frm_data(&strbuf, (uint8_t*)str, strlen(str)); + + if(*len < 2) + return GLDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL; + /* precedence */ + if(gldns_bget_token(&strbuf, token, "\t\n ", sizeof(token)) <= 0) + return RET_ERR(GLDNS_WIREPARSE_ERR_INVALID_STR, + gldns_buffer_position(&strbuf)); + rd[0] = (uint8_t)atoi(token); + /* discovery_optional */ + if(gldns_bget_token(&strbuf, token, "\t\n ", sizeof(token)) <= 0) + return RET_ERR(GLDNS_WIREPARSE_ERR_INVALID_STR, + gldns_buffer_position(&strbuf)); + if ((token[0] != '0' && token[0] != '1') || token[1] != 0) + return RET_ERR(GLDNS_WIREPARSE_ERR_INVALID_STR, + gldns_buffer_position(&strbuf)); + + rd[1] = *token == '1' ? 0x80 : 0x00; + /* relay_type */ + if(gldns_bget_token(&strbuf, token, "\t\n ", sizeof(token)) <= 0) + return RET_ERR(GLDNS_WIREPARSE_ERR_INVALID_STR, + gldns_buffer_position(&strbuf)); + relay_type = (uint8_t)atoi(token); + if (relay_type > 0x7F) + return RET_ERR(GLDNS_WIREPARSE_ERR_INVALID_STR, + gldns_buffer_position(&strbuf)); + rd[1] |= relay_type; + + if (relay_type == 0) { + *len = 2; + return GLDNS_WIREPARSE_ERR_OK; + } + /* relay */ + if(gldns_bget_token(&strbuf, token, "\t\n ", sizeof(token)) <= 0) + return RET_ERR(GLDNS_WIREPARSE_ERR_INVALID_STR, + gldns_buffer_position(&strbuf)); + if(relay_type == 1) { + /* IP4 */ + relay_len = *len - 2; + s = gldns_str2wire_a_buf(token, rd+2, &relay_len); + if(s) return RET_ERR_SHIFT(s, gldns_buffer_position(&strbuf)); + } else if(relay_type == 2) { + /* IP6 */ + relay_len = *len - 2; + s = gldns_str2wire_aaaa_buf(token, rd+2, &relay_len); + if(s) return RET_ERR_SHIFT(s, gldns_buffer_position(&strbuf)); + } else if(relay_type == 3) { + /* DNAME */ + relay_len = *len - 2; + s = gldns_str2wire_dname_buf(token, rd+2, &relay_len); + if(s) return RET_ERR_SHIFT(s, gldns_buffer_position(&strbuf)); + } else { + /* unknown gateway type */ + return RET_ERR(GLDNS_WIREPARSE_ERR_INVALID_STR, + gldns_buffer_position(&strbuf)); + } + /* double check for size */ + if(*len < 2 + relay_len) + return RET_ERR(GLDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL, + gldns_buffer_position(&strbuf)); + + *len = 2 + relay_len; + return GLDNS_WIREPARSE_ERR_OK; +} + + diff --git a/src/gldns/str2wire.h b/src/gldns/str2wire.h index 2aba0e10..293a4981 100644 --- a/src/gldns/str2wire.h +++ b/src/gldns/str2wire.h @@ -554,6 +554,15 @@ int gldns_str2wire_hip_buf(const char* str, uint8_t* rd, size_t* len); */ int gldns_str2wire_int16_data_buf(const char* str, uint8_t* rd, size_t* len); +/** + * Convert rdf of type GLDNS_RDF_TYPE_AMTRELAY from string to wireformat. + * @param str: the text to convert for this rdata element. + * @param rd: rdata buffer for the wireformat. + * @param len: length of rd buffer on input, used length on output. + * @return 0 on success, error on failure. + */ +int gldns_str2wire_amtrelay_buf(const char* str, uint8_t* rd, size_t* len); + /** * Strip whitespace from the start and the end of line. * @param line: modified with 0 to shorten it. diff --git a/src/gldns/wire2str.c b/src/gldns/wire2str.c index 54e336d8..28b7863b 100644 --- a/src/gldns/wire2str.c +++ b/src/gldns/wire2str.c @@ -1004,6 +1004,9 @@ int gldns_wire2str_rdf_scan(uint8_t** d, size_t* dlen, char** s, size_t* slen, return gldns_wire2str_tag_scan(d, dlen, s, slen); case GLDNS_RDF_TYPE_LONG_STR: return gldns_wire2str_long_str_scan(d, dlen, s, slen); + case GLDNS_RDF_TYPE_AMTRELAY: + return gldns_wire2str_amtrelay_scan(d, dlen, s, slen, pkt, + pktlen); case GLDNS_RDF_TYPE_TSIGERROR: return gldns_wire2str_tsigerror_scan(d, dlen, s, slen); } @@ -1707,6 +1710,61 @@ int gldns_wire2str_long_str_scan(uint8_t** d, size_t* dl, char** s, size_t* sl) return w; } +/* internal scan routine that can modify arguments on failure */ +static int gldns_wire2str_amtrelay_scan_internal(uint8_t** d, size_t* dl, + char** s, size_t* sl, uint8_t* pkt, size_t pktlen) +{ + /* https://www.ietf.org/id/draft-ietf-mboned-driad-amt-discovery-01.txt */ + uint8_t precedence, discovery_optional, relay_type; + int w = 0; + + if(*dl < 2) return -1; + precedence = (*d)[0]; + discovery_optional= (*d)[1] >> 7; + relay_type = (*d)[1] % 0x7F; + if(relay_type > 3) + return -1; /* unknown */ + (*d)+=2; + (*dl)-=2; + w += gldns_str_print(s, sl, "%d %d %d ", + (int)precedence, (int)discovery_optional, (int)relay_type); + + switch(relay_type) { + case 0: /* no relay */ + break; + case 1: /* ip4 */ + w += gldns_wire2str_a_scan(d, dl, s, sl); + break; + case 2: /* ip6 */ + w += gldns_wire2str_aaaa_scan(d, dl, s, sl); + break; + case 3: /* dname */ + w += gldns_wire2str_dname_scan(d, dl, s, sl, pkt, pktlen); + break; + default: /* unknown */ + return -1; + } + return w; +} + +int gldns_wire2str_amtrelay_scan(uint8_t** d, size_t* dl, char** s, size_t* sl, + uint8_t* pkt, size_t pktlen) +{ + uint8_t* od = *d; + char* os = *s; + size_t odl = *dl, osl = *sl; + int w=gldns_wire2str_amtrelay_scan_internal(d, dl, s, sl, pkt, pktlen); + if(w == -1) { + *d = od; + *s = os; + *dl = odl; + *sl = osl; + return -1; + } + return w; +} + + int gldns_wire2str_tsigerror_scan(uint8_t** d, size_t* dl, char** s, size_t* sl) { gldns_lookup_table *lt; diff --git a/src/gldns/wire2str.h b/src/gldns/wire2str.h index a7a7c930..99a737a1 100644 --- a/src/gldns/wire2str.h +++ b/src/gldns/wire2str.h @@ -916,6 +916,21 @@ int gldns_wire2str_tag_scan(uint8_t** data, size_t* data_len, char** str, int gldns_wire2str_long_str_scan(uint8_t** data, size_t* data_len, char** str, size_t* str_len); +/** + * Scan wireformat AMTRELAY field to string, with user buffers. + * It shifts the arguments to move along (see gldns_wire2str_pkt_scan). + * @param data: wireformat data. + * @param data_len: length of data buffer. + * @param str: string buffer. + * @param str_len: length of string buffer. + * @param pkt: packet for decompression, if NULL no decompression. + * @param pktlen: length of packet buffer. + * @return number of characters (except null) needed to print. + * Can return -1 on failure. + */ +int gldns_wire2str_amtrelay_scan(uint8_t** data, size_t* data_len, char** str, + size_t* str_len, uint8_t* pkt, size_t pktlen); + /** * Print EDNS LLQ option data to string. User buffers, moves string pointers. * @param str: string buffer. diff --git a/src/rr-dict.c b/src/rr-dict.c index 79fb7bcc..0ad75a43 100644 --- a/src/rr-dict.c +++ b/src/rr-dict.c @@ -431,6 +431,214 @@ static _getdns_rdf_special hip_public_key = { hip_public_key_dict2wire, NULL }; +static const uint8_t * +amtrelay_D_rdf_end(const uint8_t *pkt, const uint8_t *pkt_end, const uint8_t *rdf) +{ + (void)pkt; + return rdf < pkt_end ? rdf + 1 : NULL; +} +static getdns_return_t +amtrelay_D_wire2dict(getdns_dict *dict, const uint8_t *rdf) +{ + return getdns_dict_set_int(dict, "discovery_optional", (*rdf >> 7)); +} +static getdns_return_t +amtrelay_D_dict2wire(const getdns_dict *dict, + uint8_t *rdata, uint8_t *rdf, size_t *rdf_len) +{ + getdns_return_t r; + uint32_t value; + (void)rdata; /* unused parameter */ + + if ((r = getdns_dict_get_int(dict, "discovery_optional", &value))) + return r; + + *rdf_len = 1; + if (*rdf_len < 1) + return GETDNS_RETURN_NEED_MORE_SPACE; + + *rdf_len = 1; + *rdf = value ? 0x80 : 0x00; + return GETDNS_RETURN_GOOD; +} +static _getdns_rdf_special amtrelay_D = { + amtrelay_D_rdf_end, + amtrelay_D_wire2dict, NULL, + amtrelay_D_dict2wire, NULL +}; + +static const uint8_t * +amtrelay_rtype_rdf_end( + const uint8_t *pkt, const uint8_t *pkt_end, const uint8_t *rdf) +{ + return rdf; +} +static getdns_return_t +amtrelay_rtype_wire2dict(getdns_dict *dict, const uint8_t *rdf) +{ + return _getdns_dict_set_int( + dict, "replay_type", (rdf[-1] & 0x7F)); +} +static getdns_return_t +amtrelay_rtype_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, "relay_type", &value))) + return r; + + if (rdf - 1 < rdata) + return GETDNS_RETURN_GENERIC_ERROR; + + *rdf_len = 0; + rdf[-1] |= (value & 0x7F); + + return GETDNS_RETURN_GOOD; +} +static _getdns_rdf_special amtrelay_rtype = { + amtrelay_rtype_rdf_end, + amtrelay_rtype_wire2dict, NULL, + amtrelay_rtype_dict2wire, NULL +}; + +static const uint8_t * +amtrelay_relay_rdf_end( + const uint8_t *pkt, const uint8_t *pkt_end, const uint8_t *rdf) +{ + const uint8_t *end; + + if (rdf - 4 < pkt) + return NULL; + switch (rdf[-1] & 0x7F) { + 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 +amtrelay_relay_equip_const_bindata( + const uint8_t *rdf, size_t *size, const uint8_t **data) +{ + *data = rdf; + switch (rdf[-1] & 0x7F) { + 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 +amtrelay_relay_wire2dict(getdns_dict *dict, const uint8_t *rdf) +{ + size_t size; + const uint8_t *data; + + if (amtrelay_relay_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, "relay", size, data); +} +static getdns_return_t +amtrelay_relay_2wire( + const getdns_bindata *value, uint8_t *rdata, uint8_t *rdf, size_t *rdf_len) +{ + assert(rdf - 1 >= rdata && (rdf[-1] & 0x7F) > 0); + + switch (rdf[-1] & 0x7F) { + 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 +amtrelay_relay_dict2wire( + const getdns_dict *dict, uint8_t *rdata, uint8_t *rdf, size_t *rdf_len) +{ + getdns_return_t r; + getdns_bindata *value; + + if (rdf - 1 < rdata) + return GETDNS_RETURN_GENERIC_ERROR; + + else if ((rdf[-1] & 0x7F) == 0) { + *rdf_len = 0; + return GETDNS_RETURN_GOOD; + } + else if ((r = getdns_dict_get_bindata(dict, "relay", &value))) + return r; + else + return amtrelay_relay_2wire(value, rdata, rdf, rdf_len); +} +static _getdns_rdf_special amtrelay_relay = { + amtrelay_relay_rdf_end, + amtrelay_relay_wire2dict, NULL, + amtrelay_relay_dict2wire, NULL +}; + static _getdns_rdata_def a_rdata[] = { { "ipv4_address" , GETDNS_RDF_A , NULL }}; @@ -665,6 +873,17 @@ static _getdns_rdata_def dlv_rdata[] = { { "algorithm" , GETDNS_RDF_I1 , NULL }, { "digest_type" , GETDNS_RDF_I1 , NULL }, { "digest" , GETDNS_RDF_X , NULL }}; +static _getdns_rdata_def doa_rdata[] = { + { "enterprise" , GETDNS_RDF_I4 , NULL }, + { "type" , GETDNS_RDF_I4 , NULL }, + { "location" , GETDNS_RDF_I1 , NULL }, + { "media_type" , GETDNS_RDF_S , NULL }, + { "data" , GETDNS_RDF_B , NULL }}; +static _getdns_rdata_def amtrelay_rdata[] = { + { "precedence" , GETDNS_RDF_I1 , NULL }, + { "discovery_optional" , GETDNS_RDF_SPECIAL, &amtrelay_D}, + { "relay_type" , GETDNS_RDF_SPECIAL, &amtrelay_rtype }, + { "relay" , GETDNS_RDF_SPECIAL, &amtrelay_relay }}; static _getdns_rr_def _getdns_rr_defs[] = { { NULL, NULL, 0 }, @@ -926,7 +1145,8 @@ static _getdns_rr_def _getdns_rr_defs[] = { { "URI", uri_rdata, ALEN( uri_rdata) }, /* 256 - */ { "CAA", caa_rdata, ALEN( caa_rdata) }, { "AVC", txt_rdata, ALEN( txt_rdata) }, - { "DOA", UNKNOWN_RDATA, 0 }, /* - 259 */ + { "DOA", doa_rdata, ALEN( doa_rdata) }, + { "AMTRELAY", amtrelay_rdata, ALEN( amtrelay_rdata) }, /* - 260 */ { "TA", ds_rdata, ALEN( ds_rdata) }, /* 32768 */ { "DLV", dlv_rdata, ALEN( dlv_rdata) } /* 32769 */ }; @@ -934,12 +1154,12 @@ static _getdns_rr_def _getdns_rr_defs[] = { const _getdns_rr_def * _getdns_rr_def_lookup(uint16_t rr_type) { - if (rr_type <= 259) + if (rr_type <= 260) return &_getdns_rr_defs[rr_type]; else if (rr_type == 32768) - return &_getdns_rr_defs[260]; - else if (rr_type == 32769) return &_getdns_rr_defs[261]; + else if (rr_type == 32769) + return &_getdns_rr_defs[262]; return _getdns_rr_defs; }