summaryrefslogtreecommitdiff
path: root/network_io
diff options
context:
space:
mode:
authorJoe Orton <jorton@apache.org>2017-11-28 08:53:13 +0000
committerJoe Orton <jorton@apache.org>2017-11-28 08:53:13 +0000
commite08c6d42bdf4165f066eb1e1326eeecd2bce6b65 (patch)
tree46a20c09edf04f7198f163262c487e6fefe5f738 /network_io
parent2046154c53149cd304ffd07e4991565b7a089361 (diff)
downloadapr-e08c6d42bdf4165f066eb1e1326eeecd2bce6b65.tar.gz
Support IPv6 link-local address scope/zone mapping.
* network_io/unix/sockaddr.c (apr_sockaddr_zone_set, apr_sockaddr_zone_get): New functions. (apr_sockaddr_ip_getbuf): Append %scope for link-local address. (apr_sockaddr_equal): Compare link-local address with different scopes as not equal. * include/apr_network_io.h: Add function declarations. * configure.in: Test for if_indextoname and if_nametoindex. * test/testsock.c (test_zone): New test case. * include/arch/win32/apr_private.h: Assume Windows supports if_nametoindex and if_indextoname. git-svn-id: https://svn.apache.org/repos/asf/apr/apr/trunk@1816527 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'network_io')
-rw-r--r--network_io/unix/sockaddr.c100
1 files changed, 97 insertions, 3 deletions
diff --git a/network_io/unix/sockaddr.c b/network_io/unix/sockaddr.c
index 57bc0cb7e..3b711fd5b 100644
--- a/network_io/unix/sockaddr.c
+++ b/network_io/unix/sockaddr.c
@@ -25,6 +25,10 @@
#include <stdlib.h>
#endif
+#ifdef HAVE_NET_IF_H
+#include <net/if.h>
+#endif
+
#define APR_WANT_STRFUNC
#include "apr_want.h"
@@ -125,9 +129,31 @@ APR_DECLARE(apr_status_t) apr_sockaddr_ip_getbuf(char *buf, apr_size_t buflen,
memmove(buf, buf + strlen("::ffff:"),
strlen(buf + strlen("::ffff:"))+1);
}
-#endif
+
/* ensure NUL termination if the buffer is too short */
buf[buflen-1] = '\0';
+
+#ifdef HAVE_IF_INDEXTONAME
+ /* Append scope name for link-local addresses. */
+ if (sockaddr->family == AF_INET6
+ && IN6_IS_ADDR_LINKLOCAL((struct in6_addr *)sockaddr->ipaddr_ptr)) {
+ char scbuf[IF_NAMESIZE], *p = buf + strlen(buf);
+
+ if (if_indextoname(sockaddr->sa.sin6.sin6_scope_id, scbuf) == scbuf) {
+ /* Space check, need room for buf + '%' + scope + '\0'.
+ * Assert: buflen >= strlen(buf) + strlen(scbuf) + 2
+ * Equiv: buflen >= (p-buf) + strlen(buf) + 2
+ * Thus, fail in inverse condition: */
+ if (buflen < strlen(scbuf) + (p - buf) + 2) {
+ return APR_ENOSPC;
+ }
+ *p++ = '%';
+ memcpy(p, scbuf, strlen(scbuf) + 1);
+ }
+ }
+#endif /* HAVE_IF_INDEXTONAME */
+#endif /* APR_HAVE_IPV6 */
+
return APR_SUCCESS;
}
@@ -899,11 +925,19 @@ APR_DECLARE(apr_status_t) apr_getservbyname(apr_sockaddr_t *sockaddr,
&((struct in6_addr *)(b)->ipaddr_ptr)->s6_addr[12], \
(a)->ipaddr_len))
+#if APR_HAVE_IPV6
+#define SCOPE_OR_ZERO(sa_) ((sa_)->family != AF_INET6 ? 0 : \
+ ((sa_)->sa.sin6.sin6_scope_id))
+#else
+#define SCOPE_OR_ZERO(sa_) (0)
+#endif
+
APR_DECLARE(int) apr_sockaddr_equal(const apr_sockaddr_t *addr1,
const apr_sockaddr_t *addr2)
{
- if (addr1->ipaddr_len == addr2->ipaddr_len &&
- !memcmp(addr1->ipaddr_ptr, addr2->ipaddr_ptr, addr1->ipaddr_len)) {
+ if (addr1->ipaddr_len == addr2->ipaddr_len
+ && !memcmp(addr1->ipaddr_ptr, addr2->ipaddr_ptr, addr1->ipaddr_len)
+ && SCOPE_OR_ZERO(addr1) == SCOPE_OR_ZERO(addr2)) {
return 1;
}
#if APR_HAVE_IPV6
@@ -1182,3 +1216,63 @@ APR_DECLARE(int) apr_ipsubnet_test(apr_ipsubnet_t *ipsub, apr_sockaddr_t *sa)
#endif /* APR_HAVE_IPV6 */
return 0; /* no match */
}
+
+APR_DECLARE(apr_status_t) apr_sockaddr_zone_set(apr_sockaddr_t *sa,
+ const char *zone_id)
+{
+#if !APR_HAVE_IPV6 || !defined(HAVE_IF_NAMETOINDEX)
+ return APR_ENOTIMPL;
+#else
+ unsigned int idx;
+
+ if (sa->family != APR_INET6) {
+ return APR_EBADIP;
+ }
+
+ idx = if_nametoindex(zone_id);
+ if (idx) {
+ sa->sa.sin6.sin6_scope_id = idx;
+ return APR_SUCCESS;
+ }
+
+ if (errno != ENODEV) {
+ return errno;
+ }
+ else {
+ char *endptr;
+ apr_int64_t i = apr_strtoi64(zone_id, &endptr, 10);
+
+ if (*endptr != '\0' || errno || i < 1 || i > APR_INT16_MAX) {
+ return APR_EGENERAL;
+ }
+
+ sa->sa.sin6.sin6_scope_id = i;
+ return APR_SUCCESS;
+ }
+#endif
+}
+
+APR_DECLARE(apr_status_t) apr_sockaddr_zone_get(const apr_sockaddr_t *sa,
+ const char **name,
+ apr_uint32_t *id,
+ apr_pool_t *p)
+{
+#if !APR_HAVE_IPV6 || !defined(HAVE_IF_INDEXTONAME)
+ return APR_ENOTIMPL;
+#else
+ if (sa->family != APR_INET6 || !sa->sa.sin6.sin6_scope_id) {
+ return APR_EBADIP;
+ }
+
+ if (name) {
+ char *buf = apr_palloc(p, IF_NAMESIZE);
+ if (if_indextoname(sa->sa.sin6.sin6_scope_id, buf) == NULL)
+ return errno;
+ *name = buf;
+ }
+
+ if (id) *id = sa->sa.sin6.sin6_scope_id;
+
+ return APR_SUCCESS;
+#endif
+}