Skip to content

Commit

Permalink
Add low-level handling of scoped IPv6 link-local addresses:
Browse files Browse the repository at this point in the history
* src/ne_socket.c (ne_iaddr_set_scope, ne_iaddr_get_scope):
  New functions.

* src/ne_socket.h: Declare above; note that ne_iaddr_cmp() ignores
  scopes.

* macros/neon.m4 (NEON_COMMON_CHECKS): Check for if_nametoindex &
  if_indextoname functions, <net/if.h>.

* src/neon.vers: Add new functions for 0.34.

* test/uri-tests.c (parse): Test parsing RFC 6874 syntax.

* src/ne_uri.c (ne_uri_parse): Comment updates only.

* test/socket.c (scopes): Add minimal tests.
  • Loading branch information
notroj committed Jul 28, 2024
1 parent 43a2932 commit 85451d3
Show file tree
Hide file tree
Showing 7 changed files with 81 additions and 6 deletions.
5 changes: 3 additions & 2 deletions macros/neon.m4
Original file line number Diff line number Diff line change
Expand Up @@ -672,7 +672,7 @@ AC_REQUIRE([AC_FUNC_STRERROR_R])
AC_CHECK_HEADERS([sys/time.h limits.h sys/select.h arpa/inet.h libintl.h \
signal.h sys/socket.h netinet/in.h netinet/tcp.h netdb.h sys/poll.h \
sys/limits.h fcntl.h iconv.h],,,
sys/limits.h fcntl.h iconv.h net/if.h],,,
[AC_INCLUDES_DEFAULT
/* netinet/tcp.h requires netinet/in.h on some platforms. */
#ifdef HAVE_NETINET_IN_H
Expand All @@ -697,7 +697,8 @@ NE_LARGEFILE
AC_REPLACE_FUNCS(strcasecmp)
AC_CHECK_FUNCS([signal setvbuf setsockopt stpcpy poll fcntl getsockopt \
explicit_bzero sendmsg gettimeofday gmtime_r])
explicit_bzero sendmsg gettimeofday gmtime_r if_nametoindex \
if_indextoname])
if test "x${ac_cv_func_poll}${ac_cv_header_sys_poll_h}y" = "xyesyesy"; then
AC_DEFINE([NE_USE_POLL], 1, [Define if poll() should be used])
Expand Down
37 changes: 37 additions & 0 deletions src/ne_socket.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@
#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif
#ifdef HAVE_NET_IF_H
#include <net/if.h>
#endif

#ifdef WIN32
#include <winsock2.h>
Expand Down Expand Up @@ -1258,6 +1261,40 @@ int ne_iaddr_reverse(const ne_inet_addr *ia, char *buf, size_t bufsiz)
#endif
}

int ne_iaddr_set_scope(ne_inet_addr *ia, const char *scope)
{
#ifdef HAVE_IF_NAMETOINDEX
unsigned int idx;

if (ia->ai_family != AF_INET6) return EINVAL;

idx = if_nametoindex(scope);
if (idx) {
struct sockaddr_in6 *in6 = SACAST(in6, ia->ai_addr);
in6->sin6_scope_id = idx;
return 0;
}
else
return errno;
#else
return ENODEV;
#endif
}

char *ne_iaddr_get_scope(const ne_inet_addr *ia)
{
#ifdef HAVE_IF_INDEXTONAME
struct sockaddr_in6 *in6 = SACAST(in6, ia->ai_addr);
char buf[IF_NAMESIZE];

if (ia->ai_family != AF_INET6) return NULL;

if (in6->sin6_scope_id && if_indextoname(in6->sin6_scope_id, buf) == buf)
return ne_strdup(buf);
#endif
return NULL;
}

void ne_addr_destroy(ne_sock_addr *addr)
{
#ifdef USE_GETADDRINFO
Expand Down
10 changes: 9 additions & 1 deletion src/ne_socket.h
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,9 @@ typedef enum {
ne_inet_addr *ne_iaddr_make(ne_iaddr_type type, const unsigned char *raw);

/* Compare two network address objects i1 and i2; returns zero if they
* are equivalent or non-zero otherwise. */
* are equivalent or non-zero otherwise. For an IPv6 literal, the
* scope is ignored by this function if set, so must be compared
* seperately if required. */
int ne_iaddr_cmp(const ne_inet_addr *i1, const ne_inet_addr *i2);

/* Return the type of the given network address object. */
Expand All @@ -139,6 +141,12 @@ int ne_iaddr_reverse(const ne_inet_addr *ia, char *buf, size_t bufsiz);
* non-NULL, return value must be freed using ne_iaddr_free. */
ne_inet_addr *ne_iaddr_parse(const char *addr, ne_iaddr_type type);

/* Set the scope_id for an IPv6 address from 'scope'. */
int ne_iaddr_set_scope(ne_inet_addr *ia, const char *scope);

/* Returns the scope_id for an IPv6 address. */
char *ne_iaddr_get_scope(const ne_inet_addr *ia);

/* Destroy a network address object created using ne_iaddr_make or
* ne_iaddr_parse. */
void ne_iaddr_free(ne_inet_addr *addr);
Expand Down
9 changes: 6 additions & 3 deletions src/ne_uri.c
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,9 @@ int ne_uri_parse(const char *uri, ne_uri *parsed)
if (s[0] == '[') {
p = s + 1;

/* This allows any characters in IP-literal which is too
* broad, however e.g. zone identifiers per RFC 6874 are
* allowed through. */
while (*p != ']' && p < pa)
p++;

Expand All @@ -206,14 +209,14 @@ int ne_uri_parse(const char *uri, ne_uri *parsed)
return -1;
}

p++; /* => p = colon */
p++; /* => p = [ ':' port ] */
} else {
/* Find the colon. */
/* Find any colon before reaching path-abempty. */
p = s;
while (*p != ':' && p < pa)
p++;
}
/* => p = colon */
/* => p = [ ":" port ] */

parsed->host = ne_strndup(s, p - s);

Expand Down
3 changes: 3 additions & 0 deletions src/neon.vers
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,7 @@ NEON_0_33 {

NEON_0_34 {
ne_get_response_location;
ne_iaddr_set_scope;
ne_iaddr_get_scope;
};

19 changes: 19 additions & 0 deletions test/socket.c
Original file line number Diff line number Diff line change
Expand Up @@ -1560,6 +1560,24 @@ static int fail_socks(void)
return OK;
}

static int scopes(void)
{
ne_inet_addr *ia;

#ifdef TEST_IPV6
ia = ne_iaddr_parse("fe80::cafe", ne_iaddr_ipv6);
#else
ia = ne_iaddr_parse("127.0.0.1", ne_iaddr_ipv4);
#endif

(void) ne_iaddr_set_scope(ia, "foobar");
(void) ne_iaddr_get_scope(ia);

ne_iaddr_free(ia);

return OK;
}

ne_test tests[] = {
T(multi_init),
T_LEAKY(resolve),
Expand Down Expand Up @@ -1620,5 +1638,6 @@ ne_test tests[] = {
T(block_timeout),
T(socks_proxy),
T(fail_socks),
T(scopes),
T(NULL)
};
4 changes: 4 additions & 0 deletions test/uri-tests.c
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,10 @@ static int parse(void)
{ "http://[::1]:8080/bar", "http", "[::1]", 8080, "/bar", NULL, NULL, NULL },
{ "ftp://[feed::cafe]:555", "ftp", "[feed::cafe]", 555, "/", NULL, NULL, NULL },

/* Test RFC 6874 syntax, an extension of RFC 3987. */
{ "http://[fe80::cafe%25eth0]:555", "http", "[fe80::cafe%25eth0]", 555, "/", NULL, NULL, NULL },
{ "http://[fe80::cafe%251]:555", "http", "[fe80::cafe%251]", 555, "/", NULL, NULL, NULL },

{ "DAV:", "DAV", NULL, 0, "", NULL, NULL, NULL },

/* Some odd cases: heir-part and relative-ref will both match
Expand Down

0 comments on commit 85451d3

Please sign in to comment.