Add support for the URI DNS Resource Record type
authorNathaniel McCallum <npmccallum@redhat.com>
Tue, 16 Aug 2016 21:38:32 +0000 (17:38 -0400)
committerAndreas Schneider <asn@samba.org>
Thu, 18 Aug 2016 05:53:19 +0000 (07:53 +0200)
For more information, see RFC 7553.

Signed-off-by: Nathaniel McCallum <npmccallum@redhat.com>
Reviewed-by: Jakub Hrozek <jakub.hrozek@posteo.se>
Reviewed-by: Alexander Bokovoy <ab@samba.org>
Reviewed-by: Andreas Schneider <asn@samba.org>
doc/resolv_wrapper.1
doc/resolv_wrapper.1.txt
src/resolv_wrapper.c
tests/fake_hosts.in
tests/test_dns_fake.c

index 52197135d15a73d84e278f9425976f37ccf81747..fccdc5a7777e7a74af24e78f3454dac6ae095000 100644 (file)
@@ -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
index 12f96d0155f2ef981adf0d9837ace09d66e9c504..63e40d45eecb16a01c54ea4f6e2a8206b0d6e8fa 100644 (file)
@@ -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*::
 
index 48018be80398585d6c214ffccf71d3a3c6a852bd..a07086a8baa9fe946d5bf7624b550de63867a9ee 100644 (file)
@@ -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;
index 19f75510a98c146a4945643b7f23cec239f52aa4..d7bf9a27006d99a0765ad39dd86c756201ad4748 100644 (file)
@@ -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
index 8d1dd0c070e79bec7d5d5b78fe4329c2f2b7f0f3..0702dc8f2267744a515ea979b39afeac0dc73ad6 100644 (file)
@@ -49,6 +49,7 @@
 #include <resolv.h>
 
 #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),