From: Vladimír Čunát Date: Fri, 27 Jan 2017 15:57:16 +0000 (+0100) Subject: policy.FORWARD: support IPv6 link-local addresses X-Git-Tag: v1.3.0~23^2~88^2~3 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5ed52f46f966524053501728baae60c8fec266a8;p=thirdparty%2Fknot-resolver.git policy.FORWARD: support IPv6 link-local addresses These shouldn't make any problems: - the verbose messages don't print any scope, and - reputation cache doesn't consider scope. --- diff --git a/daemon/lua/kres-gen.lua b/daemon/lua/kres-gen.lua index 37ce82144..e62681e1a 100644 --- a/daemon/lua/kres-gen.lua +++ b/daemon/lua/kres-gen.lua @@ -174,7 +174,7 @@ knot_mm_t *kr_resolve_pool(struct kr_request *); struct kr_query *kr_rplan_push(struct kr_rplan *, struct kr_query *, const knot_dname_t *, uint16_t, uint16_t); int kr_rplan_pop(struct kr_rplan *, struct kr_query *); struct kr_query *kr_rplan_resolved(struct kr_rplan *); -int kr_nsrep_set(struct kr_query *, size_t, uint8_t *, size_t, int); +int kr_nsrep_set(struct kr_query *, size_t, const struct sockaddr *); unsigned int kr_rand_uint(unsigned int); int kr_pkt_put(knot_pkt_t *, const knot_dname_t *, uint32_t, uint16_t, uint16_t, const uint8_t *, uint16_t); int kr_pkt_recycle(knot_pkt_t *); @@ -185,6 +185,7 @@ int kr_straddr_family(const char *); int kr_straddr_subnet(void *, const char *); int kr_bitcmp(const char *, const char *, int); int kr_family_len(int); +struct sockaddr *kr_straddr_socket(const char *, int); int kr_rrarray_add(rr_array_t *, const knot_rrset_t *, knot_mm_t *); knot_rrset_t *kr_ta_get(map_t *, const knot_dname_t *); int kr_ta_add(map_t *, const knot_dname_t *, uint16_t, uint32_t, const uint8_t *, uint16_t); diff --git a/daemon/lua/kres-gen.sh b/daemon/lua/kres-gen.sh index 34e4424ca..6f1584a67 100755 --- a/daemon/lua/kres-gen.sh +++ b/daemon/lua/kres-gen.sh @@ -109,6 +109,7 @@ EOF kr_straddr_subnet kr_bitcmp kr_family_len + kr_straddr_socket kr_rrarray_add # Trust anchors kr_ta_get diff --git a/daemon/lua/kres.lua b/daemon/lua/kres.lua index a24a73a46..a14f6244f 100644 --- a/daemon/lua/kres.lua +++ b/daemon/lua/kres.lua @@ -227,11 +227,11 @@ ffi.metatype( kr_query_t, { nslist = function(qry, list) assert(#list <= 4, 'maximum of 4 addresses can be evaluated for each query') for i, ns in ipairs(list) do - C.kr_nsrep_set(qry, i - 1, ffi.cast(ub_t, ns[1]), #ns[1], ns[2] or 53) + assert(C.kr_nsrep_set(qry, i - 1, ns) == 0); end -- If less than maximum NSs, insert guard to terminate the list if #list < 4 then - C.kr_nsrep_set(qry, #list, nil, 0, 0) + assert(C.kr_nsrep_set(qry, #list, nil) == 0); end end, }, diff --git a/daemon/main.c b/daemon/main.c index 009fb6026..435a09275 100644 --- a/daemon/main.c +++ b/daemon/main.c @@ -250,6 +250,7 @@ static void signal_handler(uv_signal_t *handle, int signum) uv_signal_stop(handle); } +/** Split away port from the address. */ static const char *set_addr(char *addr, int *port) { char *p = strchr(addr, '#'); diff --git a/lib/nsrep.c b/lib/nsrep.c index be584d9fd..f3dd405af 100644 --- a/lib/nsrep.c +++ b/lib/nsrep.c @@ -172,7 +172,7 @@ static int eval_nsrep(const char *k, void *v, void *baton) return kr_ok(); } -int kr_nsrep_set(struct kr_query *qry, size_t index, uint8_t *addr, size_t addr_len, int port) +int kr_nsrep_set(struct kr_query *qry, size_t index, const struct sockaddr *sock) { if (!qry) { return kr_error(EINVAL); @@ -186,17 +186,33 @@ int kr_nsrep_set(struct kr_query *qry, size_t index, uint8_t *addr, size_t addr_ qry->ns.score = KR_NS_UNKNOWN; qry->ns.reputation = 0; } + + if (!sock) { + qry->ns.addr[index].ip.sa_family = AF_UNSPEC; + return kr_ok(); + } + + switch (sock->sa_family) { + case AF_INET: + qry->ns.addr[index].ip4 = *(const struct sockaddr_in *)sock; + break; + case AF_INET6: + qry->ns.addr[index].ip6 = *(const struct sockaddr_in6 *)sock; + break; + default: + qry->ns.addr[index].ip.sa_family = AF_UNSPEC; + return kr_error(EINVAL); + } + /* Retrieve RTT from cache */ - if (addr && addr_len > 0) { - struct kr_context *ctx = qry->ns.ctx; - unsigned *score = ctx - ? lru_get_try(ctx->cache_rtt, (const char *)addr, addr_len) - : NULL; - if (score) { - qry->ns.score = MIN(qry->ns.score, *score); - } + struct kr_context *ctx = qry->ns.ctx; + unsigned *score = ctx + ? lru_get_try(ctx->cache_rtt, kr_inaddr(sock), kr_family_len(sock->sa_family)) + : NULL; + if (score) { + qry->ns.score = MIN(qry->ns.score, *score); } - update_nsrep(&qry->ns, index, addr, addr_len, port); + return kr_ok(); } diff --git a/lib/nsrep.h b/lib/nsrep.h index e94f245ca..5e4eed838 100644 --- a/lib/nsrep.h +++ b/lib/nsrep.h @@ -96,13 +96,11 @@ struct kr_nsrep * Set given NS address. * @param qry updated query * @param index index of the updated target - * @param addr address bytes (struct in_addr or struct in6_addr) - * @param addr_len address bytes length (type will be derived from this) - * @param port address port (if <= 0, 53 will be used) + * @param sock socket address to use (sockaddr_in or sockaddr_in6 or NULL) * @return 0 or an error code */ KR_EXPORT -int kr_nsrep_set(struct kr_query *qry, size_t index, uint8_t *addr, size_t addr_len, int port); +int kr_nsrep_set(struct kr_query *qry, size_t index, const struct sockaddr *sock); /** * Elect best nameserver/address pair from the nsset. diff --git a/lib/utils.c b/lib/utils.c index 794119ebb..574e75a7e 100644 --- a/lib/utils.c +++ b/lib/utils.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "lib/defines.h" #include "lib/utils.h" @@ -291,6 +292,32 @@ int kr_family_len(int family) } } +struct sockaddr * kr_straddr_socket(const char *addr, int port) +{ + switch (kr_straddr_family(addr)) { + case AF_INET: { + struct sockaddr_in *res = malloc(sizeof(*res)); + if (uv_ip4_addr(addr, port, res) >= 0) { + return (struct sockaddr *)res; + } else { + free(res); + return NULL; + } + } + case AF_INET6: { + struct sockaddr_in6 *res = malloc(sizeof(*res)); + if (uv_ip6_addr(addr, port, res) >= 0) { + return (struct sockaddr *)res; + } else { + free(res); + return NULL; + } + } + default: + return NULL; + } +} + int kr_straddr_subnet(void *dst, const char *addr) { if (!dst || !addr) { diff --git a/lib/utils.h b/lib/utils.h index 24044438d..041edc017 100644 --- a/lib/utils.h +++ b/lib/utils.h @@ -152,6 +152,9 @@ int kr_straddr_family(const char *addr); /** Return address length in given family. */ KR_EXPORT KR_CONST int kr_family_len(int family); +/** Create a sockaddr* from string+port representation (also accepts IPv6 link-local). */ +KR_EXPORT +struct sockaddr * kr_straddr_socket(const char *addr, int port); /** Parse address and return subnet length (bits). * @warning 'dst' must be at least `sizeof(struct in6_addr)` long. */ KR_EXPORT diff --git a/modules/policy/policy.lua b/modules/policy/policy.lua index e5d020a6d..6c31954e1 100644 --- a/modules/policy/policy.lua +++ b/modules/policy/policy.lua @@ -42,6 +42,17 @@ local function parse_target(target) return addr, port end +local function parse_sock(target) + local addr, port = target:match '([^@]*)@?(.*)' + port = port and tonumber(port) or 53 + sock = ffi.gc(ffi.C.kr_straddr_socket(addr, port), ffi.C.free); + + if sock == nil then + error("target '"..target..'" is not a valid IP address') + end + return sock +end + -- Mirror request elsewhere, and continue solving local function mirror(target) local addr, port = parse_target(target) @@ -63,11 +74,11 @@ local function forward(target) local list = {} if type(target) == 'table' then for _, v in pairs(target) do - table.insert(list, {parse_target(v)}) + table.insert(list, parse_sock(v)) assert(#list <= 4, 'at most 4 FORWARD targets are supported') end else - table.insert(list, {parse_target(target)}) + table.insert(list, parse_sock(target)) end return function(state, req) req = kres.request_t(req)