From b06c3f5694c5db3e38dd832f482d3809aced7c41 Mon Sep 17 00:00:00 2001 From: Jakub Hrozek Date: Sat, 4 Oct 2014 16:26:58 +0200 Subject: [PATCH] resolv: Implement SRV faking Signed-off-by: Jakub Hrozek Reviewed-by: Andreas Schneider Reviewed-by: Michael Adam --- src/resolv_wrapper.c | 95 ++++++++++++++++++++++++++++++----- tests/fake_hosts.in | 2 + tests/test_dns_fake.c | 112 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 197 insertions(+), 12 deletions(-) diff --git a/src/resolv_wrapper.c b/src/resolv_wrapper.c index 8fd6729..60781ef 100644 --- a/src/resolv_wrapper.c +++ b/src/resolv_wrapper.c @@ -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; } } diff --git a/tests/fake_hosts.in b/tests/fake_hosts.in index 4697b7b..7e28052 100644 --- a/tests/fake_hosts.in +++ b/tests/fake_hosts.in @@ -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 diff --git a/tests/test_dns_fake.c b/tests/test_dns_fake.c index 49d685c..2897893 100644 --- a/tests/test_dns_fake.c +++ b/tests/test_dns_fake.c @@ -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); -- 2.34.1