From 2817157bb705e0f3e9ad4a83246a80d026866be3 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 3 Jun 2016 21:29:14 +0200 Subject: resolved: support IPv6 DNS servers on the local link Make sure we can parse DNS server addresses that use the "zone id" syntax for local link addresses, i.e. "fe80::c256:27ff:febb:12f%wlp3s0", when reading /etc/resolv.conf. Also make sure we spit this out correctly again when writing /etc/resolv.conf and via the bus. Fixes: #3359 --- src/basic/in-addr-util.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++++ src/basic/in-addr-util.h | 2 ++ 2 files changed, 87 insertions(+) (limited to 'src/basic') diff --git a/src/basic/in-addr-util.c b/src/basic/in-addr-util.c index 245107ebb8..e5a9daeab8 100644 --- a/src/basic/in-addr-util.c +++ b/src/basic/in-addr-util.c @@ -20,12 +20,14 @@ #include #include #include +#include #include #include #include "alloc-util.h" #include "in-addr-util.h" #include "macro.h" +#include "parse-util.h" #include "util.h" int in_addr_is_null(int family, const union in_addr_union *u) { @@ -224,6 +226,48 @@ int in_addr_to_string(int family, const union in_addr_union *u, char **ret) { return 0; } +int in_addr_ifindex_to_string(int family, const union in_addr_union *u, int ifindex, char **ret) { + size_t l; + char *x; + int r; + + assert(u); + assert(ret); + + /* Much like in_addr_to_string(), but optionally appends the zone interface index to the address, to properly + * handle IPv6 link-local addresses. */ + + if (family != AF_INET6) + goto fallback; + if (ifindex <= 0) + goto fallback; + + r = in_addr_is_link_local(family, u); + if (r < 0) + return r; + if (r == 0) + goto fallback; + + l = INET6_ADDRSTRLEN + 1 + DECIMAL_STR_MAX(ifindex) + 1; + x = new(char, l); + if (!x) + return -ENOMEM; + + errno = 0; + if (!inet_ntop(family, u, x, l)) { + free(x); + return errno > 0 ? -errno : -EINVAL; + } + + sprintf(strchr(x, 0), "%%%i", ifindex); + *ret = x; + + return 0; + +fallback: + return in_addr_to_string(family, u, ret); +} + int in_addr_from_string(int family, const char *s, union in_addr_union *ret) { assert(s); @@ -261,6 +305,47 @@ int in_addr_from_string_auto(const char *s, int *family, union in_addr_union *re return -EINVAL; } +int in_addr_ifindex_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex) { + const char *suffix; + int r, ifi = 0; + + assert(s); + assert(family); + assert(ret); + + /* Similar to in_addr_from_string_auto() but also parses an optionally appended IPv6 zone suffix ("scope id") + * if one is found. */ + + suffix = strchr(s, '%'); + if (suffix) { + + if (ifindex) { + /* If we shall return the interface index, try to parse it */ + r = parse_ifindex(suffix + 1, &ifi); + if (r < 0) { + unsigned u; + + u = if_nametoindex(suffix + 1); + if (u <= 0) + return -errno; + + ifi = (int) u; + } + } + + s = strndupa(s, suffix - s); + } + + r = in_addr_from_string_auto(s, family, ret); + if (r < 0) + return r; + + if (ifindex) + *ifindex = ifi; + + return r; +} + unsigned char in_addr_netmask_to_prefixlen(const struct in_addr *addr) { assert(addr); diff --git a/src/basic/in-addr-util.h b/src/basic/in-addr-util.h index 17798ce816..7fdf3a15fc 100644 --- a/src/basic/in-addr-util.h +++ b/src/basic/in-addr-util.h @@ -43,8 +43,10 @@ int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_ int in_addr_prefix_intersect(int family, const union in_addr_union *a, unsigned aprefixlen, const union in_addr_union *b, unsigned bprefixlen); int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen); int in_addr_to_string(int family, const union in_addr_union *u, char **ret); +int in_addr_ifindex_to_string(int family, const union in_addr_union *u, int ifindex, char **ret); int in_addr_from_string(int family, const char *s, union in_addr_union *ret); int in_addr_from_string_auto(const char *s, int *family, union in_addr_union *ret); +int in_addr_ifindex_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex); unsigned char in_addr_netmask_to_prefixlen(const struct in_addr *addr); struct in_addr* in_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned char prefixlen); int in_addr_default_prefixlen(const struct in_addr *addr, unsigned char *prefixlen); -- cgit v1.2.3-54-g00ecf