From: Per Förlin <pe...@axis.com>

Package: busybox
Version: master (1_25_0+)
Severity: medium

The purpose of this draft patch is to highlight an
issue concerning address scope in IPv6 and CIFS kernel implementation.

Without this patch the following command fails:
mount -t cifs //fe80::6a05:caff:fe3e:dbf5%eth0/test test

Based on this post
http://patchwork.sourceware.org/patch/1629/
it seems like this topic has been up for discussion before.
However it looks like the NI_NUMERICSCOPE flag for getnaminfo()
never made it to glibc.

getnameinfo() always returns the address scope using the
name representation but the CIFS kernel implementation
expects a numeric scope ID.

There are different ways to approach this.
1. Parse the host string manually and converting the scope
   using if_nametoindex().
2. Convert the host address to binary and back again.

This patch uses alternative #2.
Re-create the host address using the numeric scope ID retrieved
by getaddrinfo().
---
 include/libbb.h    |  1 +
 libbb/xconnect.c   | 57 ++++++++++++++++++++++++++++++++++++++++++++++++------
 util-linux/mount.c |  2 +-
 3 files changed, 53 insertions(+), 7 deletions(-)

diff --git a/include/libbb.h b/include/libbb.h
index 3752df9..48e110a 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -688,6 +688,7 @@ char* xmalloc_sockaddr2hostonly_noport(const struct 
sockaddr *sa) FAST_FUNC RETU
 /* inet_[ap]ton on steroids */
 char* xmalloc_sockaddr2dotted(const struct sockaddr *sa) FAST_FUNC 
RETURNS_MALLOC;
 char* xmalloc_sockaddr2dotted_noport(const struct sockaddr *sa) FAST_FUNC 
RETURNS_MALLOC;
+char* xmalloc_sockaddr2dotted_noport_num(const struct sockaddr *sa) FAST_FUNC 
RETURNS_MALLOC;
 // "old" (ipv4 only) API
 // users: traceroute.c hostname.c - use _list_ of all IPs
 struct hostent *xgethostbyname(const char *name) FAST_FUNC;
diff --git a/libbb/xconnect.c b/libbb/xconnect.c
index 6e78e63..5095c35 100644
--- a/libbb/xconnect.c
+++ b/libbb/xconnect.c
@@ -433,10 +433,45 @@ int FAST_FUNC xconnect_stream(const len_and_sockaddr *lsa)
        return fd;
 }
 
+/* Make sure the host is specified with a numeric scope.
+ * Numeric scope is required by the CIFS kernel implementation. */
+static int host_make_numeric_scope(char * host, int host_size) {
+       struct addrinfo *addr = NULL;
+       struct sockaddr_in6 *sin6;
+       int res = -1;
+
+       if (getaddrinfo(host, NULL, NULL, &addr) != 0)
+               goto out;
+
+       if (addr->ai_family != AF_INET6)
+               goto out;
+
+       sin6 = (struct sockaddr_in6 *)addr->ai_addr;
+       if (!sin6->sin6_scope_id) {
+               res = 0; /* no scope to be processed */
+               goto out;
+       }
+
+       if (inet_ntop(AF_INET6, &sin6->sin6_addr, host, host_size) != NULL) {
+               int len = strnlen(host, host_size);
+               if (snprintf(host + len, host_size - len,
+                            "%%%u", sin6->sin6_scope_id) > 1)
+                       res = 0;
+       }
+
+out:
+       if (res != 0)
+               bb_perror_msg("Failed to convert %s to numeric scope", host);
+
+       freeaddrinfo(addr);
+       return res;
+}
+
 /* We hijack this constant to mean something else */
 /* It doesn't hurt because we will add this bit anyway */
 #define IGNORE_PORT NI_NUMERICSERV
-static char* FAST_FUNC sockaddr2str(const struct sockaddr *sa, int flags)
+static char* FAST_FUNC sockaddr2str(const struct sockaddr *sa, int flags,
+                                   int num_scope)
 {
        char host[128];
        char serv[16];
@@ -464,6 +499,10 @@ static char* FAST_FUNC sockaddr2str(const struct sockaddr 
*sa, int flags)
                        /* do not resolve port# into service _name_ */
                        flags | NI_NUMERICSERV
        );
+       if (num_scope && sa->sa_family == AF_INET6 &&
+           host_make_numeric_scope(host, sizeof(host)) != 0)
+               return NULL;
+
        if (rc)
                return NULL;
        if (flags & IGNORE_PORT)
@@ -484,24 +523,30 @@ static char* FAST_FUNC sockaddr2str(const struct sockaddr 
*sa, int flags)
 
 char* FAST_FUNC xmalloc_sockaddr2host(const struct sockaddr *sa)
 {
-       return sockaddr2str(sa, 0);
+       return sockaddr2str(sa, 0, false);
 }
 
 char* FAST_FUNC xmalloc_sockaddr2host_noport(const struct sockaddr *sa)
 {
-       return sockaddr2str(sa, IGNORE_PORT);
+       return sockaddr2str(sa, IGNORE_PORT, false);
 }
 
 char* FAST_FUNC xmalloc_sockaddr2hostonly_noport(const struct sockaddr *sa)
 {
-       return sockaddr2str(sa, NI_NAMEREQD | IGNORE_PORT);
+       return sockaddr2str(sa, NI_NAMEREQD | IGNORE_PORT, false);
 }
 char* FAST_FUNC xmalloc_sockaddr2dotted(const struct sockaddr *sa)
 {
-       return sockaddr2str(sa, NI_NUMERICHOST);
+       return sockaddr2str(sa, NI_NUMERICHOST, false);
 }
 
 char* FAST_FUNC xmalloc_sockaddr2dotted_noport(const struct sockaddr *sa)
 {
-       return sockaddr2str(sa, NI_NUMERICHOST | IGNORE_PORT);
+       return sockaddr2str(sa, NI_NUMERICHOST | IGNORE_PORT, false);
+}
+
+char* FAST_FUNC xmalloc_sockaddr2dotted_noport_num(const struct sockaddr *sa)
+{
+       return sockaddr2str(sa, NI_NUMERICHOST | IGNORE_PORT,
+                           true /* numeric scope */);
 }
diff --git a/util-linux/mount.c b/util-linux/mount.c
index 13590ce..b87be90 100644
--- a/util-linux/mount.c
+++ b/util-linux/mount.c
@@ -1972,7 +1972,7 @@ static int singlemount(struct mntent *mp, int ignore_busy)
                        goto report_error;
 
                // Insert "ip=..." option into options
-               dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
+               dotted = xmalloc_sockaddr2dotted_noport_num(&lsa->u.sa);
                if (ENABLE_FEATURE_CLEAN_UP) free(lsa);
                ip = xasprintf("ip=%s", dotted);
                if (ENABLE_FEATURE_CLEAN_UP) free(dotted);
-- 
2.1.4

_______________________________________________
busybox mailing list
busybox@busybox.net
http://lists.busybox.net/mailman/listinfo/busybox

Reply via email to