]> granicus.if.org Git - libevent/commitdiff
Parse IPv6 scope IDs.
authorPhilip Homburg <phomburg@ripe.net>
Tue, 29 Oct 2019 14:48:53 +0000 (15:48 +0100)
committerAzat Khuzhin <azat@libevent.org>
Wed, 6 Nov 2019 18:07:16 +0000 (21:07 +0300)
CMakeLists.txt
Makefile.am
evutil.c
include/event2/util.h
test/regress_util.c

index 897ddf6f632915283cefdacef9dd9c657a1143de..c98b619adfdedce676363f643068fc5d5982cd7e 100644 (file)
@@ -966,6 +966,18 @@ add_event_library(event SOURCES ${SRC_CORE} ${SRC_EXTRA})
 
 set(WIN32_GETOPT)
 if (WIN32)
+    set(_TMPLIBS)
+    if (${EVENT_LIBRARY_STATIC})
+        list(APPEND _TMPLIBS event_core_static event_static)
+    endif()
+    if (${EVENT_LIBRARY_SHARED})
+        list(APPEND _TMPLIBS event_core_shared event_shared)
+    endif()
+    foreach(lib ${_TMPLIBS})
+        target_link_libraries(${lib} iphlpapi)
+    endforeach()
+    unset(_TMPLIBS)
+
     list(APPEND WIN32_GETOPT
          WIN32-Code/getopt.c
          WIN32-Code/getopt_long.c)
index 8211dc7f74ce9e0757e4edbec69ada12af936a56..c044e6a20c171c1739a6dbf7974d66d173191128 100644 (file)
@@ -270,11 +270,11 @@ AM_LDFLAGS = $(LIBEVENT_LDFLAGS)
 GENERIC_LDFLAGS = -version-info $(VERSION_INFO) $(RELEASE) $(NO_UNDEFINED) $(AM_LDFLAGS)
 
 libevent_la_SOURCES = $(CORE_SRC) $(EXTRAS_SRC)
-libevent_la_LIBADD = @LTLIBOBJS@ $(SYS_LIBS)
+libevent_la_LIBADD = @LTLIBOBJS@ $(SYS_LIBS) -liphlpapi
 libevent_la_LDFLAGS = $(GENERIC_LDFLAGS)
 
 libevent_core_la_SOURCES = $(CORE_SRC)
-libevent_core_la_LIBADD = @LTLIBOBJS@ $(SYS_LIBS)
+libevent_core_la_LIBADD = @LTLIBOBJS@ $(SYS_LIBS) -liphlpapi
 libevent_core_la_LDFLAGS = $(GENERIC_LDFLAGS)
 
 if PTHREADS
index 13631263d754c2c6d0895a2ccd93be039576da82..17962548cf3b14cbf4a4fc53ce8e2c12a3f42a60 100644 (file)
--- a/evutil.c
+++ b/evutil.c
@@ -44,6 +44,7 @@
 /* For structs needed by GetAdaptersAddresses */
 #define _WIN32_WINNT 0x0501
 #include <iphlpapi.h>
+#include <netioapi.h>
 #endif
 
 #include <sys/types.h>
@@ -77,6 +78,9 @@
 #endif
 #include <time.h>
 #include <sys/stat.h>
+#ifndef _WIN32
+#include <net/if.h>
+#endif
 #ifdef EVENT__HAVE_IFADDRS_H
 #include <ifaddrs.h>
 #endif
@@ -1171,6 +1175,7 @@ evutil_getaddrinfo_common_(const char *nodename, const char *servname,
     struct evutil_addrinfo *hints, struct evutil_addrinfo **res, int *portnum)
 {
        int port = 0;
+       unsigned int if_index;
        const char *pname;
 
        if (nodename == NULL && servname == NULL)
@@ -1244,10 +1249,12 @@ evutil_getaddrinfo_common_(const char *nodename, const char *servname,
        if (hints->ai_family == PF_INET6 || hints->ai_family == PF_UNSPEC) {
                struct sockaddr_in6 sin6;
                memset(&sin6, 0, sizeof(sin6));
-               if (1==evutil_inet_pton(AF_INET6, nodename, &sin6.sin6_addr)) {
+               if (1 == evutil_inet_pton_scope(
+                       AF_INET6, nodename, &sin6.sin6_addr, &if_index)) {
                        /* Got an ipv6 address. */
                        sin6.sin6_family = AF_INET6;
                        sin6.sin6_port = htons(port);
+                       sin6.sin6_scope_id = if_index;
                        *res = evutil_new_addrinfo_((struct sockaddr*)&sin6,
                            sizeof(sin6), hints);
                        if (!*res)
@@ -2162,6 +2169,41 @@ evutil_inet_ntop(int af, const void *src, char *dst, size_t len)
 #endif
 }
 
+int
+evutil_inet_pton_scope(int af, const char *src, void *dst, unsigned *indexp)
+{
+       int r;
+       unsigned if_index;
+       char *check, *cp, *tmp_src;
+
+       *indexp = 0; /* Reasonable default */
+
+       /* Bail out if not IPv6 */
+       if (af != AF_INET6)
+               return evutil_inet_pton(af, src, dst);
+
+       cp = strchr(src, '%');
+
+       /* Bail out if no zone ID */
+       if (cp == NULL)
+               return evutil_inet_pton(af, src, dst);
+
+       if_index = if_nametoindex(cp + 1);
+       if (if_index == 0) {
+               /* Could be numeric */
+               if_index = strtoul(cp + 1, &check, 10);
+               if (check[0] != '\0')
+                       return 0;
+       }
+       *indexp = if_index;
+       tmp_src = mm_strdup(src);
+       cp = strchr(tmp_src, '%');
+       *cp = '\0';
+       r = evutil_inet_pton(af, tmp_src, dst);
+       free(tmp_src);
+       return r;
+}
+
 int
 evutil_inet_pton(int af, const char *src, void *dst)
 {
@@ -2278,6 +2320,7 @@ int
 evutil_parse_sockaddr_port(const char *ip_as_string, struct sockaddr *out, int *outlen)
 {
        int port;
+       unsigned int if_index;
        char buf[128];
        const char *cp, *addr_part, *port_part;
        int is_ipv6;
@@ -2347,10 +2390,13 @@ evutil_parse_sockaddr_port(const char *ip_as_string, struct sockaddr *out, int *
 #endif
                sin6.sin6_family = AF_INET6;
                sin6.sin6_port = htons(port);
-               if (1 != evutil_inet_pton(AF_INET6, addr_part, &sin6.sin6_addr))
+               if (1 != evutil_inet_pton_scope(
+                       AF_INET6, addr_part, &sin6.sin6_addr, &if_index)) {
                        return -1;
+               }
                if ((int)sizeof(sin6) > *outlen)
                        return -1;
+               sin6.sin6_scope_id = if_index;
                memset(out, 0, *outlen);
                memcpy(out, &sin6, sizeof(sin6));
                *outlen = sizeof(sin6);
index 9ee6862fb4c6fac41052dc2aca551ef6059fd5c2..4cedfc3f9b444a8f8703908197c332ef43458c7a 100644 (file)
@@ -612,6 +612,12 @@ int evutil_vsnprintf(char *buf, size_t buflen, const char *format, va_list ap)
 /** Replacement for inet_ntop for platforms which lack it. */
 EVENT2_EXPORT_SYMBOL
 const char *evutil_inet_ntop(int af, const void *src, char *dst, size_t len);
+/** Variation of inet_pton that also parses IPv6 scopes. Public for
+    unit tests. No reason to call this directly.
+ */
+EVENT2_EXPORT_SYMBOL
+int evutil_inet_pton_scope(int af, const char *src, void *dst,
+       unsigned *indexp);
 /** Replacement for inet_pton for platforms which lack it. */
 EVENT2_EXPORT_SYMBOL
 int evutil_inet_pton(int af, const char *src, void *dst);
index 9422cce87340fdec5c7324e843081230410ad208..1459387a78337dcdc8121e3c1c21f301cdfce277 100644 (file)
@@ -215,6 +215,65 @@ regress_ipv6_parse(void *ptr)
 #endif
 }
 
+static struct ipv6_entry_scope {
+       const char *addr;
+       ev_uint32_t res[4];
+       unsigned scope;
+       enum entry_status status;
+} ipv6_entries_scope[] = {
+       { "2001:DB8::", { 0x20010db8, 0, 0 }, 0, NORMAL },
+       { "2001:DB8::%0", { 0x20010db8, 0, 0, 0 }, 0, NORMAL },
+       { "2001:DB8::%1", { 0x20010db8, 0, 0, 0 }, 1, NORMAL },
+       { "foobar.", { 0, 0, 0, 0 }, 0, BAD },
+       { "2001:DB8::%does-not-exist", { 0, 0, 0, 0 }, 0, BAD },
+       { NULL, { 0, 0, 0, 0,  }, 0, BAD },
+};
+static void
+regress_ipv6_parse_scope(void *ptr)
+{
+#ifdef AF_INET6
+       int i, j;
+       unsigned if_scope;
+
+       for (i = 0; ipv6_entries_scope[i].addr; ++i) {
+               struct ipv6_entry_scope *ent = &ipv6_entries_scope[i];
+               struct in6_addr in6;
+               int r;
+               r = evutil_inet_pton_scope(AF_INET6, ent->addr, &in6,
+                       &if_scope);
+               if (r == 0) {
+                       if (ent->status != BAD)
+                               TT_FAIL(("%s did not parse, but it's a good address!",
+                                       ent->addr));
+                       continue;
+               }
+               if (ent->status == BAD) {
+                       TT_FAIL(("%s parsed, but we expected an error", ent->addr));
+                       continue;
+               }
+               for (j = 0; j < 4; ++j) {
+                       /* Can't use s6_addr32 here; some don't have it. */
+                       ev_uint32_t u =
+                           ((ev_uint32_t)in6.s6_addr[j*4  ] << 24) |
+                           ((ev_uint32_t)in6.s6_addr[j*4+1] << 16) |
+                           ((ev_uint32_t)in6.s6_addr[j*4+2] << 8) |
+                           ((ev_uint32_t)in6.s6_addr[j*4+3]);
+                       if (u != ent->res[j]) {
+                               TT_FAIL(("%s did not parse as expected.", ent->addr));
+                               continue;
+                       }
+               }
+               if (if_scope != ent->scope) {
+                       TT_FAIL(("%s did not parse as expected.", ent->addr));
+                       continue;
+               }
+       }
+#else
+       TT_BLATHER(("Skipping IPv6 address parsing."));
+#endif
+}
+
+
 static struct sa_port_ent {
        const char *parse;
        int safamily;
@@ -1715,6 +1774,7 @@ end:
 struct testcase_t util_testcases[] = {
        { "ipv4_parse", regress_ipv4_parse, 0, NULL, NULL },
        { "ipv6_parse", regress_ipv6_parse, 0, NULL, NULL },
+       { "ipv6_parse_scope", regress_ipv6_parse_scope, 0, NULL, NULL },
        { "sockaddr_port_parse", regress_sockaddr_port_parse, 0, NULL, NULL },
        { "sockaddr_port_format", regress_sockaddr_port_format, 0, NULL, NULL },
        { "sockaddr_predicates", test_evutil_sockaddr_predicates, 0,NULL,NULL },