From 02535da4b92bfafbd671cb52c3a86d38572b2066 Mon Sep 17 00:00:00 2001 From: Nathaniel McCallum Date: Tue, 16 Aug 2016 17:38:32 -0400 Subject: Add support for the URI DNS Resource Record type For more information, see RFC 7553. Signed-off-by: Nathaniel McCallum Reviewed-by: Jakub Hrozek Reviewed-by: Alexander Bokovoy Reviewed-by: Andreas Schneider --- doc/resolv_wrapper.1 | 1 + doc/resolv_wrapper.1.txt | 1 + src/resolv_wrapper.c | 95 +++++++++++++++++++++++++++++++++++++++++ tests/fake_hosts.in | 2 + tests/test_dns_fake.c | 107 +++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 206 insertions(+) diff --git a/doc/resolv_wrapper.1 b/doc/resolv_wrapper.1 index 5219713..fccdc5a 100644 --- a/doc/resolv_wrapper.1 +++ b/doc/resolv_wrapper.1 @@ -92,6 +92,7 @@ A dc\&.cwrap\&.org 127\&.0\&.0\&.10 AAAA dc\&.cwrap\&.org fd00::5357:5f0a CNAME kerberos\&.cwrap\&.org dc\&.cwrap\&.org SRV _kerberos\&._tcp\&.cwrap\&.org kerberos\&.cwrap\&.org 88 +URI _vpn\&.cwrap\&.org https://vpn\&.cwrap\&.org/VPN .fi .if n \{\ .RE diff --git a/doc/resolv_wrapper.1.txt b/doc/resolv_wrapper.1.txt index 12f96d0..63e40d4 100644 --- a/doc/resolv_wrapper.1.txt +++ b/doc/resolv_wrapper.1.txt @@ -47,6 +47,7 @@ For example: AAAA dc.cwrap.org fd00::5357:5f0a CNAME kerberos.cwrap.org dc.cwrap.org SRV _kerberos._tcp.cwrap.org kerberos.cwrap.org 88 + URI _vpn.cwrap.org https://vpn.cwrap.org/VPN *RESOLV_WRAPPER_DEBUGLEVEL*:: diff --git a/src/resolv_wrapper.c b/src/resolv_wrapper.c index 48018be..a07086a 100644 --- a/src/resolv_wrapper.c +++ b/src/resolv_wrapper.c @@ -73,6 +73,8 @@ #define ns_name_compress dn_comp #endif +#define ns_t_uri 256 + enum rwrap_dbglvl_e { RWRAP_LOG_ERROR = 0, RWRAP_LOG_WARN, @@ -156,6 +158,8 @@ static void rwrap_log(enum rwrap_dbglvl_e dbglvl, */ #define DFL_SRV_PRIO 1 #define DFL_SRV_WEIGHT 100 +#define DFL_URI_PRIO 1 +#define DFL_URI_WEIGHT 100 struct rwrap_srv_rrdata { uint16_t port; @@ -164,6 +168,12 @@ struct rwrap_srv_rrdata { char hostname[MAXDNAME]; }; +struct rwrap_uri_rrdata { + uint16_t prio; + uint16_t weight; + char uri[MAXDNAME]; +}; + struct rwrap_soa_rrdata { uint32_t serial; uint32_t refresh; @@ -179,6 +189,7 @@ struct rwrap_fake_rr { struct in_addr a_rec; struct in6_addr aaaa_rec; struct rwrap_srv_rrdata srv_rec; + struct rwrap_uri_rrdata uri_rec; struct rwrap_soa_rrdata soa_rec; char cname_rec[MAXDNAME]; } rrdata; @@ -280,6 +291,42 @@ static int rwrap_create_fake_srv_rr(const char *key, return 0; } +static int rwrap_create_fake_uri_rr(const char *key, + const char *value, + struct rwrap_fake_rr *rr) +{ + char *str_prio; + char *str_weight; + const char *uri; + + /* parse the value into priority, weight, and uri + * and check the validity */ + uri = value; + NEXT_KEY(uri, str_prio); + NEXT_KEY(str_prio, str_weight); + if (uri == NULL) { + RWRAP_LOG(RWRAP_LOG_ERROR, + "Malformed URI entry [%s]\n", value); + return -1; + } + + if (str_prio) { + rr->rrdata.uri_rec.prio = atoi(str_prio); + } else { + rr->rrdata.uri_rec.prio = DFL_URI_PRIO; + } + if (str_weight) { + rr->rrdata.uri_rec.weight = atoi(str_weight); + } else { + rr->rrdata.uri_rec.weight = DFL_URI_WEIGHT; + } + memcpy(rr->rrdata.uri_rec.uri, uri, strlen(uri) + 1); + + memcpy(rr->key, key, strlen(key) + 1); + rr->type = ns_t_uri; + return 0; +} + static int rwrap_create_fake_soa_rr(const char *key, const char *value, struct rwrap_fake_rr *rr) @@ -564,6 +611,46 @@ static ssize_t rwrap_fake_srv(struct rwrap_fake_rr *rr, return resp_size; } +static ssize_t rwrap_fake_uri(struct rwrap_fake_rr *rr, + uint8_t *answer, + size_t anslen) +{ + uint8_t *a = answer; + ssize_t resp_size; + size_t rdata_size; + unsigned char uri_compressed[MAXDNAME]; + ssize_t compressed_len; + + if (rr == NULL || rr->type != ns_t_uri) { + RWRAP_LOG(RWRAP_LOG_ERROR, + "Malformed record, no or wrong value!\n"); + return -1; + } + RWRAP_LOG(RWRAP_LOG_TRACE, "Adding URI RR"); + rdata_size = 3 * sizeof(uint16_t); + + /* Prepare the data to write */ + compressed_len = ns_name_compress(rr->rrdata.uri_rec.uri, + uri_compressed, MAXDNAME, + NULL, NULL); + if (compressed_len < 0) { + return -1; + } + rdata_size += compressed_len; + + resp_size = rwrap_fake_rdata_common(ns_t_uri, rdata_size, + rr->key, anslen, &a); + if (resp_size < 0) { + return -1; + } + + NS_PUT16(rr->rrdata.uri_rec.prio, a); + NS_PUT16(rr->rrdata.uri_rec.weight, a); + memcpy(a, uri_compressed, compressed_len); + + return resp_size; +} + static ssize_t rwrap_fake_soa(struct rwrap_fake_rr *rr, uint8_t *answer, size_t anslen) @@ -770,6 +857,10 @@ static int rwrap_get_record(const char *hostfile, unsigned recursion, rr + 1); } break; + } else if (TYPE_MATCH(type, ns_t_uri, + rec_type, "URI", key, query)) { + rc = rwrap_create_fake_uri_rr(key, value, rr); + break; } else if (TYPE_MATCH(type, ns_t_soa, rec_type, "SOA", key, query)) { rc = rwrap_create_fake_soa_rr(key, value, rr); @@ -837,6 +928,7 @@ static inline bool rwrap_known_type(int type) case ns_t_aaaa: case ns_t_ns: case ns_t_srv: + case ns_t_uri: case ns_t_soa: case ns_t_cname: return true; @@ -901,6 +993,9 @@ static ssize_t rwrap_add_rr(struct rwrap_fake_rr *rr, case ns_t_srv: resp_data = rwrap_fake_srv(rr, answer, anslen); break; + case ns_t_uri: + resp_data = rwrap_fake_uri(rr, answer, anslen); + break; case ns_t_soa: resp_data = rwrap_fake_soa(rr, answer, anslen); break; diff --git a/tests/fake_hosts.in b/tests/fake_hosts.in index 19f7551..d7bf9a2 100644 --- a/tests/fake_hosts.in +++ b/tests/fake_hosts.in @@ -12,3 +12,5 @@ A www.cwrap.org 127.0.0.22 A krb5.cwrap.org 127.0.0.23 A ns1.cwrap.org 127.0.0.24 A ns2.cwrap.org 127.0.0.25 +URI _vpn.cwrap.org https://vpn.cwrap.org/VPN 2 5 +URI _ftp.cwrap.org ftp://ftp.cwrap.org/public diff --git a/tests/test_dns_fake.c b/tests/test_dns_fake.c index 8d1dd0c..0702dc8 100644 --- a/tests/test_dns_fake.c +++ b/tests/test_dns_fake.c @@ -49,6 +49,7 @@ #include #define ANSIZE 256 +#define ns_t_uri 256 static void test_res_fake_a_query(void **state) { @@ -351,6 +352,110 @@ static void test_res_fake_srv_query_minimal(void **state) assert_string_equal(addr, "127.0.0.23"); } +static void test_res_fake_uri_query(void **state) +{ + int rv; + struct __res_state dnsstate; + unsigned char answer[ANSIZE]; + ns_msg handle; + ns_rr rr; /* expanded resource record */ + const uint8_t *rrdata; + int prio; + int weight; + char uri[MAXDNAME]; + + (void) state; /* unused */ + + memset(&dnsstate, 0, sizeof(struct __res_state)); + rv = res_ninit(&dnsstate); + assert_int_equal(rv, 0); + + rv = res_nquery(&dnsstate, "_vpn.cwrap.org", ns_c_in, ns_t_uri, + answer, sizeof(answer)); + assert_in_range(rv, 1, 100); + + ns_initparse(answer, sizeof(answer), &handle); + + /* + * The query must finish w/o an error, have one answer and the answer + * must be a parseable RR of type SRV and have the priority, weight, + * port and hostname as in the fake hosts file + */ + assert_int_equal(ns_msg_getflag(handle, ns_f_rcode), ns_r_noerror); + assert_int_equal(ns_msg_count(handle, ns_s_an), 1); + assert_int_equal(ns_parserr(&handle, ns_s_an, 0, &rr), 0); + assert_int_equal(ns_rr_type(rr), ns_t_uri); + + rrdata = ns_rr_rdata(rr); + NS_GET16(prio, rrdata); + NS_GET16(weight, rrdata); + + rv = ns_name_uncompress(ns_msg_base(handle), + ns_msg_end(handle), + rrdata, + uri, MAXDNAME); + assert_int_not_equal(rv, -1); + + assert_int_equal(prio, 2); + assert_int_equal(weight, 5); + assert_string_equal(uri, "https://vpn.cwrap.org/VPN"); +} + +/* + * Test the case of a URI record query where the + * fake hosts file entry is minimal in the sense + * that it omits the priority and weight entries. + * The server then fills in some default values. + */ +static void test_res_fake_uri_query_minimal(void **state) +{ + int rv; + struct __res_state dnsstate; + unsigned char answer[ANSIZE]; + ns_msg handle; + ns_rr rr; /* expanded resource record */ + const uint8_t *rrdata; + int prio; + int weight; + char uri[MAXDNAME]; + + (void) state; /* unused */ + + memset(&dnsstate, 0, sizeof(struct __res_state)); + rv = res_ninit(&dnsstate); + assert_int_equal(rv, 0); + + rv = res_nquery(&dnsstate, "_ftp.cwrap.org", ns_c_in, ns_t_uri, + answer, sizeof(answer)); + assert_in_range(rv, 1, 256); + + ns_initparse(answer, sizeof(answer), &handle); + + /* + * The query must finish w/o an error, have one answer and the answer + * must be a parseable RR of type SRV and have the priority, weight, + * port and hostname as in the fake hosts file + */ + assert_int_equal(ns_msg_getflag(handle, ns_f_rcode), ns_r_noerror); + assert_int_equal(ns_msg_count(handle, ns_s_an), 1); + assert_int_equal(ns_parserr(&handle, ns_s_an, 0, &rr), 0); + assert_int_equal(ns_rr_type(rr), ns_t_uri); + + rrdata = ns_rr_rdata(rr); + NS_GET16(prio, rrdata); + NS_GET16(weight, rrdata); + + rv = ns_name_uncompress(ns_msg_base(handle), + ns_msg_end(handle), + rrdata, + uri, MAXDNAME); + assert_int_not_equal(rv, -1); + + assert_int_equal(prio, 1); + assert_int_equal(weight, 100); + assert_string_equal(uri, "ftp://ftp.cwrap.org/public"); +} + static void test_res_fake_soa_query(void **state) { int rv; @@ -572,6 +677,8 @@ int main(void) cmocka_unit_test(test_res_fake_aaaa_query_notfound), cmocka_unit_test(test_res_fake_srv_query), cmocka_unit_test(test_res_fake_srv_query_minimal), + cmocka_unit_test(test_res_fake_uri_query), + cmocka_unit_test(test_res_fake_uri_query_minimal), cmocka_unit_test(test_res_fake_soa_query), cmocka_unit_test(test_res_fake_cname_query), cmocka_unit_test(test_res_fake_a_via_cname), -- cgit v1.2.3