Skip to content

Commit

Permalink
Parse IPv6 scope IDs.
Browse files Browse the repository at this point in the history
  • Loading branch information
PhilipHomburg authored and azat committed Nov 6, 2019
1 parent 4436287 commit 9fecb59
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 4 deletions.
12 changes: 12 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
50 changes: 48 additions & 2 deletions evutil.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
/* For structs needed by GetAdaptersAddresses */
#define _WIN32_WINNT 0x0501
#include <iphlpapi.h>
#include <netioapi.h>
#endif

#include <sys/types.h>
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
6 changes: 6 additions & 0 deletions include/event2/util.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
60 changes: 60 additions & 0 deletions test/regress_util.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -1715,6 +1774,7 @@ test_evutil_check_working_afunix(void *arg)
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 },
Expand Down

0 comments on commit 9fecb59

Please sign in to comment.