add IPv6 link-local support to nanomsg (#1055)

diff --git a/src/transports/utils/literal.c b/src/transports/utils/literal.c
index d329cdf..8184fae 100644
--- a/src/transports/utils/literal.c
+++ b/src/transports/utils/literal.c
@@ -25,6 +25,7 @@
 #include "../../utils/err.h"
 #include "../../utils/fast.h"
 
+#include <netdb.h>
 #include <string.h>
 
 #ifndef NN_HAVE_WINDOWS
@@ -32,6 +33,24 @@
 #include <netinet/in.h>
 #endif
 
+void nn_literal_link_local_resolve(struct in6_addr *in6addr, int64_t *sin6_scope_id, char *addr)
+{
+    if (! IN6_IS_ADDR_LINKLOCAL(in6addr)) {
+        return;
+    }
+    struct addrinfo hints, *res;
+    memset (&hints, 0, sizeof (hints));
+    hints.ai_family = AF_INET6;
+    hints.ai_socktype = 0;
+    hints.ai_flags = AI_NUMERICHOST;
+    int rc = getaddrinfo(addr, NULL, &hints, &res);
+    if (0 == rc && res) {
+        struct sockaddr_in6 * curr = ((struct sockaddr_in6 *) res->ai_addr);
+        // set scope_id
+        *sin6_scope_id = (int64_t) curr->sin6_scope_id;
+    }
+}
+
 int nn_literal_resolve (const char *addr, size_t addrlen,
     int ipv4only, struct sockaddr_storage *result, size_t *resultlen)
 {
@@ -40,6 +59,8 @@
         INET6_ADDRSTRLEN :  INET_ADDRSTRLEN];
     struct in_addr inaddr;
     struct in6_addr in6addr;
+    int64_t sin6_scope_id = -1;
+    char *ifname_dindex = NULL;     // pointer to ifname's delimiter, '%'
 
     /*  Try to treat the address as a literal string. If the size of
         the address is larger than longest possible literal, skip the step.
@@ -61,11 +82,25 @@
 
     /*  Try to interpret the literal as an IPv6 address. */
     if (!ipv4only) {
-        rc = inet_pton (AF_INET6, addrz, &in6addr);
-        if (rc == 1) {
+        // check if '%' exists in addrz (i.e. '$addr%$ifname')
+        ifname_dindex = strstr(addrz, "%");
+        if (NULL == ifname_dindex) {
+            rc = inet_pton (AF_INET6, addrz, &in6addr);
+        } else {
+            // set addrz[index] = '\0', so that inet_pton won't fail
+            *ifname_dindex = '\0';
+            rc = inet_pton (AF_INET6, addrz, &in6addr);
+            // re-add the '%' so we'll be able to get the scope_id for link-local IPv6
+            *ifname_dindex = '%';
+        }
+        if (1 == rc) {
             if (result) {
+                nn_literal_link_local_resolve(&in6addr, &sin6_scope_id, addrz);
                 result->ss_family = AF_INET6;
                 ((struct sockaddr_in6*) result)->sin6_addr = in6addr;
+                if (-1 < sin6_scope_id) {
+                    ((struct sockaddr_in6*) result)->sin6_scope_id = (uint32_t) sin6_scope_id;
+                }
              }
              if (resultlen)
                 *resultlen = sizeof (struct sockaddr_in6);