summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLorenzo Colitti <lorenzo@google.com>2015-11-25 22:49:55 +0900
committerLorenzo Colitti <lorenzo@google.com>2015-11-26 10:45:39 +0900
commit513aae2733f6a7fa18d564934a79875dd73bc0a0 (patch)
treee23a5001a9902d91067edcb4741e272b2c58345f
parent14d6844fd311cac51fdf5f3e0d800927dbe651bc (diff)
downloadandroid_external_dnsmasq-513aae2733f6a7fa18d564934a79875dd73bc0a0.tar.gz
android_external_dnsmasq-513aae2733f6a7fa18d564934a79875dd73bc0a0.tar.bz2
android_external_dnsmasq-513aae2733f6a7fa18d564934a79875dd73bc0a0.zip
Improve address parsing/printing and support scoped addresses.
1. Add a parse_addr function that parses an IP address. This simplifies the code and also supports scoped addresses. 2. Make prettyprint_addr use getnameinfo instead of inet_ntop so it can support scoped addresses. 3. Modify the IPv6 address enumeration code to set the scope ID only if the address is link-local, since scope IDs are only meaningful for link-local addresses Also, we do not want to think that two addresses are different if they differ only in scope ID, because the kernel will treat them as if they are the same. 4. Modify the address equality checks in sockaddr_isequal and iface_check to check the scope ID as well. Bug: 9580643 Change-Id: I84b6b94868e50d7f6c32148b673bed7c080a4456
-rwxr-xr-xsrc/dnsmasq.h3
-rwxr-xr-xsrc/network.c47
-rwxr-xr-xsrc/option.c43
-rwxr-xr-xsrc/util.c53
4 files changed, 79 insertions, 67 deletions
diff --git a/src/dnsmasq.h b/src/dnsmasq.h
index 9d8c6dc..2887546 100755
--- a/src/dnsmasq.h
+++ b/src/dnsmasq.h
@@ -742,8 +742,9 @@ int hostname_isequal(char *a, char *b);
time_t dnsmasq_time(void);
int is_same_net(struct in_addr a, struct in_addr b, struct in_addr mask);
int retry_send(void);
+int parse_addr(int family, const char *addrstr, union mysockaddr *addr);
void prettyprint_time(char *buf, unsigned int t);
-int prettyprint_addr(union mysockaddr *addr, char *buf);
+int prettyprint_addr(const union mysockaddr *addr, char *buf);
int parse_hex(char *in, unsigned char *out, int maxlen,
unsigned int *wildcard_mask, int *mac_type);
int memcmp_masked(unsigned char *a, unsigned char *b, int len,
diff --git a/src/network.c b/src/network.c
index a71570b..858e7b4 100755
--- a/src/network.c
+++ b/src/network.c
@@ -104,7 +104,9 @@ int iface_check(int family, struct all_addr *addr, char *name, int *indexp)
#ifdef HAVE_IPV6
else if (family == AF_INET6 &&
IN6_ARE_ADDR_EQUAL(&tmp->addr.in6.sin6_addr,
- &addr->addr.addr6))
+ &addr->addr.addr6) &&
+ (!IN6_IS_ADDR_LINKLOCAL(&addr->addr.addr6) ||
+ (tmp->addr.in6.sin6_scope_id == (uint32_t) *indexp)))
ret = tmp->used = 1;
#endif
}
@@ -184,8 +186,9 @@ static int iface_allowed(struct irec **irecp, int if_index,
dhcp_ok = 0;
#ifdef HAVE_IPV6
+ int ifindex = (int) addr->in6.sin6_scope_id;
if (addr->sa.sa_family == AF_INET6 &&
- !iface_check(AF_INET6, (struct all_addr *)&addr->in6.sin6_addr, ifr.ifr_name, NULL))
+ !iface_check(AF_INET6, (struct all_addr *)&addr->in6.sin6_addr, ifr.ifr_name, &ifindex))
return 1;
#endif
@@ -221,7 +224,15 @@ static int iface_allowed_v6(struct in6_addr *local,
addr.in6.sin6_family = AF_INET6;
addr.in6.sin6_addr = *local;
addr.in6.sin6_port = htons(daemon->port);
- addr.in6.sin6_scope_id = scope;
+ /**
+ * Only populate the scope ID if the address is link-local.
+ * Scope IDs are not meaningful for global addresses. Also, we do not want to
+ * think that two addresses are different if they differ only in scope ID,
+ * because the kernel will treat them as if they are the same.
+ */
+ if (IN6_IS_ADDR_LINKLOCAL(local)) {
+ addr.in6.sin6_scope_id = scope;
+ }
return iface_allowed((struct irec **)vparam, if_index, &addr, netmask);
}
@@ -1104,24 +1115,18 @@ int set_servers(const char *servers)
memset(&addr, 0, sizeof(addr));
memset(&source_addr, 0, sizeof(source_addr));
- if ((addr.in.sin_addr.s_addr = inet_addr(saddr)) != (in_addr_t) -1)
+ if (parse_addr(AF_INET, saddr, &addr) == 0)
{
-#ifdef HAVE_SOCKADDR_SA_LEN
- source_addr.in.sin_len = addr.in.sin_len = sizeof(source_addr.in);
-#endif
- source_addr.in.sin_family = addr.in.sin_family = AF_INET;
addr.in.sin_port = htons(NAMESERVER_PORT);
+ source_addr.in.sin_family = AF_INET;
source_addr.in.sin_addr.s_addr = INADDR_ANY;
source_addr.in.sin_port = htons(daemon->query_port);
}
#ifdef HAVE_IPV6
- else if (inet_pton(AF_INET6, saddr, &addr.in6.sin6_addr) > 0)
+ else if (parse_addr(AF_INET6, saddr, &addr) == 0)
{
-#ifdef HAVE_SOCKADDR_SA_LEN
- source_addr.in6.sin6_len = addr.in6.sin6_len = sizeof(source_addr.in6);
-#endif
- source_addr.in6.sin6_family = addr.in6.sin6_family = AF_INET6;
addr.in6.sin6_port = htons(NAMESERVER_PORT);
+ source_addr.in6.sin6_family = AF_INET6;
source_addr.in6.sin6_addr = in6addr_any;
source_addr.in6.sin6_port = htons(daemon->query_port);
}
@@ -1217,25 +1222,19 @@ int reload_servers(char *fname)
memset(&addr, 0, sizeof(addr));
memset(&source_addr, 0, sizeof(source_addr));
-
- if ((addr.in.sin_addr.s_addr = inet_addr(token)) != (in_addr_t) -1)
+
+ if (parse_addr(AF_INET, token, &addr) == 0)
{
-#ifdef HAVE_SOCKADDR_SA_LEN
- source_addr.in.sin_len = addr.in.sin_len = sizeof(source_addr.in);
-#endif
- source_addr.in.sin_family = addr.in.sin_family = AF_INET;
addr.in.sin_port = htons(NAMESERVER_PORT);
+ source_addr.in.sin_family = AF_INET;
source_addr.in.sin_addr.s_addr = INADDR_ANY;
source_addr.in.sin_port = htons(daemon->query_port);
}
#ifdef HAVE_IPV6
- else if (inet_pton(AF_INET6, token, &addr.in6.sin6_addr) > 0)
+ else if (parse_addr(AF_INET6, token, &addr) == 0)
{
-#ifdef HAVE_SOCKADDR_SA_LEN
- source_addr.in6.sin6_len = addr.in6.sin6_len = sizeof(source_addr.in6);
-#endif
- source_addr.in6.sin6_family = addr.in6.sin6_family = AF_INET6;
addr.in6.sin6_port = htons(NAMESERVER_PORT);
+ source_addr.in6.sin6_family = AF_INET6;
source_addr.in6.sin6_addr = in6addr_any;
source_addr.in6.sin6_port = htons(daemon->query_port);
}
diff --git a/src/option.c b/src/option.c
index 49b6f3f..9e7ac2f 100755
--- a/src/option.c
+++ b/src/option.c
@@ -1352,25 +1352,9 @@ static char *one_opt(int option, char *arg, char *gen_prob, int nest)
comma = split(arg);
unhide_metas(arg);
new->next = daemon->if_addrs;
- if (arg && (new->addr.in.sin_addr.s_addr = inet_addr(arg)) != (in_addr_t)-1)
- {
- new->addr.sa.sa_family = AF_INET;
-#ifdef HAVE_SOCKADDR_SA_LEN
- new->addr.in.sin_len = sizeof(new->addr.in);
-#endif
- }
-#ifdef HAVE_IPV6
- else if (arg && inet_pton(AF_INET6, arg, &new->addr.in6.sin6_addr) > 0)
- {
- new->addr.sa.sa_family = AF_INET6;
- new->addr.in6.sin6_flowinfo = 0;
- new->addr.in6.sin6_scope_id = 0;
-#ifdef HAVE_SOCKADDR_SA_LEN
- new->addr.in6.sin6_len = sizeof(new->addr.in6);
-#endif
- }
-#endif
- else
+ if (arg &&
+ parse_addr(AF_INET, arg, &new->addr) != 0 &&
+ parse_addr(AF_INET6, arg, &new->addr) != 0)
{
option = '?'; /* error */
break;
@@ -1449,18 +1433,16 @@ static char *one_opt(int option, char *arg, char *gen_prob, int nest)
!atoi_check16(portno, &serv_port))
problem = _("bad port");
- if ((newlist->addr.in.sin_addr.s_addr = inet_addr(arg)) != (in_addr_t) -1)
+ if (parse_addr(AF_INET, arg, &newlist->addr) == 0)
{
newlist->addr.in.sin_port = htons(serv_port);
- newlist->source_addr.in.sin_port = htons(source_port);
- newlist->addr.sa.sa_family = newlist->source_addr.sa.sa_family = AF_INET;
#ifdef HAVE_SOCKADDR_SA_LEN
newlist->source_addr.in.sin_len = newlist->addr.in.sin_len = sizeof(struct sockaddr_in);
#endif
if (source)
{
newlist->flags |= SERV_HAS_SOURCE;
- if ((newlist->source_addr.in.sin_addr.s_addr = inet_addr(source)) == (in_addr_t) -1)
+ if (parse_addr(AF_INET, source, &newlist->addr) != 0)
{
#if defined(SO_BINDTODEVICE)
newlist->source_addr.in.sin_addr.s_addr = INADDR_ANY;
@@ -1472,20 +1454,18 @@ static char *one_opt(int option, char *arg, char *gen_prob, int nest)
}
else
newlist->source_addr.in.sin_addr.s_addr = INADDR_ANY;
+
+ newlist->source_addr.in.sin_port = htons(source_port);
+ newlist->source_addr.sa.sa_family = AF_INET;
}
#ifdef HAVE_IPV6
- else if (inet_pton(AF_INET6, arg, &newlist->addr.in6.sin6_addr) > 0)
+ else if (parse_addr(AF_INET6, arg, &newlist->addr) == 0)
{
newlist->addr.in6.sin6_port = htons(serv_port);
- newlist->source_addr.in6.sin6_port = htons(source_port);
- newlist->addr.sa.sa_family = newlist->source_addr.sa.sa_family = AF_INET6;
-#ifdef HAVE_SOCKADDR_SA_LEN
- newlist->addr.in6.sin6_len = newlist->source_addr.in6.sin6_len = sizeof(newlist->addr.in6);
-#endif
if (source)
{
newlist->flags |= SERV_HAS_SOURCE;
- if (inet_pton(AF_INET6, source, &newlist->source_addr.in6.sin6_addr) == 0)
+ if (parse_addr(AF_INET6, source, &newlist->source_addr) != 0)
{
#if defined(SO_BINDTODEVICE)
newlist->source_addr.in6.sin6_addr = in6addr_any;
@@ -1497,6 +1477,9 @@ static char *one_opt(int option, char *arg, char *gen_prob, int nest)
}
else
newlist->source_addr.in6.sin6_addr = in6addr_any;
+
+ newlist->source_addr.in6.sin6_port = htons(source_port);
+ newlist->source_addr.sa.sa_family = AF_INET6;
}
#endif
else
diff --git a/src/util.c b/src/util.c
index ce77f05..e44b8fa 100755
--- a/src/util.c
+++ b/src/util.c
@@ -20,6 +20,8 @@
#include "dnsmasq.h"
+#include <netdb.h>
+
#ifdef HAVE_BROKEN_RTC
#include <sys/times.h>
#endif
@@ -260,7 +262,9 @@ int sockaddr_isequal(union mysockaddr *s1, union mysockaddr *s2)
#ifdef HAVE_IPV6
if (s1->sa.sa_family == AF_INET6 &&
s1->in6.sin6_port == s2->in6.sin6_port &&
- IN6_ARE_ADDR_EQUAL(&s1->in6.sin6_addr, &s2->in6.sin6_addr))
+ IN6_ARE_ADDR_EQUAL(&s1->in6.sin6_addr, &s2->in6.sin6_addr) &&
+ (!IN6_IS_ADDR_LINKLOCAL(&s1->in6.sin6_addr) ||
+ (s1->in6.sin6_scope_id == s2->in6.sin6_scope_id)))
return 1;
#endif
}
@@ -322,22 +326,47 @@ int is_same_net(struct in_addr a, struct in_addr b, struct in_addr mask)
return (a.s_addr & mask.s_addr) == (b.s_addr & mask.s_addr);
}
+int parse_addr(int family, const char *addrstr, union mysockaddr *addr)
+{
+ struct addrinfo *res, hints = {
+ .ai_flags = AI_NUMERICHOST,
+ .ai_family = family,
+ .ai_socktype = SOCK_DGRAM,
+ };
+
+ int ret = getaddrinfo(addrstr, NULL, &hints, &res);
+ if (ret) {
+ return ret;
+ }
+
+ switch (res->ai_family) {
+ case AF_INET:
+ addr->in = *((struct sockaddr_in *) res->ai_addr);
+ break;
+#ifdef HAVE_IPV6
+ case AF_INET6:
+ addr->in6 = *((struct sockaddr_in6 *) res->ai_addr);
+ break;
+#endif
+ default:
+ errno = EAFNOSUPPORT;
+ ret = -1;
+ break;
+ }
+ freeaddrinfo(res);
+ return ret;
+}
+
/* returns port number from address */
-int prettyprint_addr(union mysockaddr *addr, char *buf)
+int prettyprint_addr(const union mysockaddr *addr, char *buf)
{
int port = 0;
#ifdef HAVE_IPV6
- if (addr->sa.sa_family == AF_INET)
- {
- inet_ntop(AF_INET, &addr->in.sin_addr, buf, ADDRSTRLEN);
- port = ntohs(addr->in.sin_port);
- }
- else if (addr->sa.sa_family == AF_INET6)
- {
- inet_ntop(AF_INET6, &addr->in6.sin6_addr, buf, ADDRSTRLEN);
- port = ntohs(addr->in6.sin6_port);
- }
+ char portstr[strlen("65535")];
+ getnameinfo((const struct sockaddr *) addr, sizeof(*addr), buf, ADDRSTRLEN,
+ portstr, sizeof(portstr), NI_NUMERICHOST | NI_NUMERICSERV);
+ port = atoi(portstr);
#else
strcpy(buf, inet_ntoa(addr->in.sin_addr));
port = ntohs(addr->in.sin_port);