resolv: Implement SRV faking
authorJakub Hrozek <jhrozek@redhat.com>
Sat, 4 Oct 2014 14:26:58 +0000 (16:26 +0200)
committerMichael Adam <obnox@samba.org>
Tue, 21 Oct 2014 11:39:39 +0000 (13:39 +0200)
Signed-off-by: Jakub Hrozek <jhrozek@redhat.com>
Reviewed-by: Andreas Schneider <asn@samba.org>
Reviewed-by: Michael Adam <obnox@samba.org>
src/resolv_wrapper.c
tests/fake_hosts.in
tests/test_dns_fake.c

index 8fd672970924b364ada7d6015224e734f88f2b51..60781effb0a306294b759f792a7f1a9e4f079b8d 100644 (file)
@@ -79,6 +79,18 @@ enum rwrap_dbglvl_e {
 static void rwrap_log(enum rwrap_dbglvl_e dbglvl, const char *func, const char *format, ...) PRINTF_ATTRIBUTE(3, 4);
 # define RWRAP_LOG(dbglvl, ...) rwrap_log((dbglvl), __func__, __VA_ARGS__)
 
+#define NEXT_KEY(buf, key) do {                                        \
+       (key) = (buf) ? strpbrk((buf), " \t") : NULL;           \
+       if ((key) != NULL) {                                    \
+               (key)[0] = '\0';                                \
+               (key)++;                                        \
+       }                                                       \
+       while ((key) != NULL                                    \
+              && (isblank((int)(key)[0]))) {                   \
+               (key)++;                                        \
+       }                                                       \
+} while(0);
+
 static void rwrap_log(enum rwrap_dbglvl_e dbglvl,
                      const char *func,
                      const char *format, ...)
@@ -326,6 +338,73 @@ static int rwrap_fake_aaaa(const char *key,
        return 0;
 }
 
+/*
+ * Priority and weight can be omitted from the hosts file, but need to be part
+ * of the output
+ */
+#define DFL_SRV_PRIO   1
+#define DFL_SRV_WEIGHT 100
+
+static int rwrap_fake_srv(const char *key,
+                         const char *value,
+                         uint8_t *answer,
+                         size_t anslen)
+{
+       uint8_t *a = answer;
+       int rv;
+       size_t rdata_size;
+       char *str_prio;
+       char *str_weight;
+       char *str_port;
+       const char *hostname;
+       unsigned char hostname_compressed[MAXDNAME];
+       ssize_t compressed_len;
+
+       /*
+        * Parse the value into priority, weight, port and hostname
+        * and check the validity.
+        */
+       hostname = value;
+       NEXT_KEY(hostname, str_port);
+       NEXT_KEY(str_port, str_prio);
+       NEXT_KEY(str_prio, str_weight);
+       if (str_port == NULL || hostname == NULL) {
+               RWRAP_LOG(RWRAP_LOG_ERROR,
+                         "Malformed SRV entry [%s]\n", value);
+               return -1;
+       }
+       rdata_size = 3 * sizeof(uint16_t);
+
+       /* Prepare the data to write */
+       compressed_len = ns_name_compress(hostname,
+                                         hostname_compressed, MAXDNAME,
+                                         NULL, NULL);
+       if (compressed_len < 0) {
+               return -1;
+       }
+       rdata_size += compressed_len;
+
+       rv = rwrap_fake_common(ns_t_srv, key, rdata_size, &a, anslen);
+       if (rv < 0) {
+               return -1;
+       }
+
+       if (str_prio) {
+               NS_PUT16(atoi(str_prio), a);
+       } else {
+               NS_PUT16(DFL_SRV_PRIO, a);
+       }
+       if (str_weight) {
+               NS_PUT16(atoi(str_weight), a);
+       } else {
+               NS_PUT16(DFL_SRV_WEIGHT, a);
+       }
+       NS_PUT16(atoi(str_port), a);
+       memcpy(a, hostname_compressed, compressed_len);
+
+       return 0;
+}
+
 static int rwrap_fake_empty_query(const char *key,
                                  uint16_t type,
                                  uint8_t *answer,
@@ -346,18 +425,6 @@ static int rwrap_fake_empty_query(const char *key,
        (line[sizeof(name) - 1] == ' ' || \
         line[sizeof(name) - 1] == '\t'))
 
-#define NEXT_KEY(buf, key) do {                        \
-       (key) = strpbrk(buf, " \t");            \
-       if ((key) != NULL) {                    \
-               (key)[0] = '\0';                \
-               (key)++;                        \
-       }                                       \
-       while ((key) != NULL                    \
-              && (isblank((int)(key)[0]))) {   \
-               (key)++;                        \
-       }                                       \
-} while(0);
-
 #define TYPE_MATCH(type, ns_type, rec_type, str_type, key, query) \
        ((type) == (ns_type) && \
         (strncmp((rec_type), (str_type), sizeof(str_type)) == 0) && \
@@ -423,6 +490,10 @@ static int rwrap_res_fake_hosts(const char *hostfile,
                                      rec_type, "AAAA", key, query)) {
                        rc = rwrap_fake_aaaa(key, value, answer, anslen);
                        break;
+               } else if (TYPE_MATCH(type, ns_t_srv,
+                                     rec_type, "SRV", key, query)) {
+                       rc = rwrap_fake_srv(key, value, answer, anslen);
+                       break;
                }
        }
 
index 4697b7b7bafaa6e458321d338d124c0532afe7fb..7e28052bc28665bbfa7fa68cf32afdd9117fc09a 100644 (file)
@@ -1,2 +1,4 @@
 A cwrap.org 127.0.0.21
 AAAA cwrap6.org 2a00:1450:4013:c01::63
+SRV _ldap._tcp.cwrap.org ldap.cwrap.org 389 1 5
+SRV _krb5._tcp.cwrap.org krb5.cwrap.org 88
index 49d685cf060806046b41b7acf4fb7ab20c4ecba8..2897893b7b32da59e26836ef4be88b06cbcb1642 100644 (file)
@@ -160,6 +160,116 @@ static void test_res_fake_aaaa_query_notfound(void **state)
        assert_int_equal(ns_msg_count(handle, ns_s_an), 0);
 }
 
+static void test_res_fake_srv_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;
+       int port;
+       char hostname[MAXDNAME];
+
+       (void) state; /* unused */
+
+       memset(&dnsstate, 0, sizeof(struct __res_state));
+       rv = res_ninit(&dnsstate);
+       assert_int_equal(rv, 0);
+
+       rv = res_nquery(&dnsstate, "_ldap._tcp.cwrap.org", ns_c_in, ns_t_srv,
+                       answer, ANSIZE);
+       assert_int_not_equal(rv, -1);
+
+       ns_initparse(answer, 256, &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_srv);
+
+       rrdata = ns_rr_rdata(rr);
+       NS_GET16(prio, rrdata);
+       NS_GET16(weight, rrdata);
+       NS_GET16(port, rrdata);
+
+       rv = ns_name_uncompress(ns_msg_base(handle),
+                               ns_msg_end(handle),
+                               rrdata,
+                               hostname, MAXDNAME);
+       assert_int_not_equal(rv, -1);
+
+       assert_int_equal(prio, 1);
+       assert_int_equal(weight, 5);
+       assert_int_equal(port, 389);
+       assert_string_equal(hostname, "ldap.cwrap.org");
+}
+
+/*
+ * Test the case of a SRV 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_srv_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;
+       int port;
+       char hostname[MAXDNAME];
+
+       (void) state; /* unused */
+
+       memset(&dnsstate, 0, sizeof(struct __res_state));
+       rv = res_ninit(&dnsstate);
+       assert_int_equal(rv, 0);
+
+       rv = res_nquery(&dnsstate, "_krb5._tcp.cwrap.org", ns_c_in, ns_t_srv,
+                       answer, ANSIZE);
+       assert_int_not_equal(rv, -1);
+
+       ns_initparse(answer, 256, &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_srv);
+
+       rrdata = ns_rr_rdata(rr);
+       NS_GET16(prio, rrdata);
+       NS_GET16(weight, rrdata);
+       NS_GET16(port, rrdata);
+
+       rv = ns_name_uncompress(ns_msg_base(handle),
+                               ns_msg_end(handle),
+                               rrdata,
+                               hostname, MAXDNAME);
+       assert_int_not_equal(rv, -1);
+
+       assert_int_equal(prio, 1);
+       assert_int_equal(weight, 100);
+       assert_int_equal(port, 88);
+       assert_string_equal(hostname, "krb5.cwrap.org");
+}
+
 int main(void)
 {
        int rc;
@@ -169,6 +279,8 @@ int main(void)
                unit_test(test_res_fake_a_query_notfound),
                unit_test(test_res_fake_aaaa_query),
                unit_test(test_res_fake_aaaa_query_notfound),
+               unit_test(test_res_fake_srv_query),
+               unit_test(test_res_fake_srv_query_minimal),
        };
 
        rc = run_tests(tests);