about summary refs log tree commit diff
path: root/inet/getnameinfo.c
diff options
context:
space:
mode:
Diffstat (limited to 'inet/getnameinfo.c')
-rw-r--r--inet/getnameinfo.c107
1 files changed, 78 insertions, 29 deletions
diff --git a/inet/getnameinfo.c b/inet/getnameinfo.c
index 9a0709229e..6ee3d4d2cc 100644
--- a/inet/getnameinfo.c
+++ b/inet/getnameinfo.c
@@ -42,20 +42,21 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 /* This software is Copyright 1996 by Craig Metz, All Rights Reserved.  */
 
-#include <sys/types.h>
-#include <sys/socket.h>
-
-#include <netinet/in.h>
-#include <sys/un.h>
-#include <sys/utsname.h>
-#include <netdb.h>
+#include <alloca.h>
 #include <errno.h>
-#include <string.h>
+#include <netdb.h>
 #include <stdio.h>
+#include <string.h>
 #include <unistd.h>
-#include <alloca.h>
-#include <bits/libc-lock.h>
 #include <arpa/inet.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <sys/utsname.h>
+#include <bits/libc-lock.h>
 
 #ifndef min
 # define min(x,y) (((x) > (y)) ? (y) : (x))
@@ -173,6 +174,7 @@ getnameinfo (const struct sockaddr *sa, socklen_t addrlen, char *host,
   char *tmpbuf = alloca (tmpbuflen);
   struct hostent th;
   socklen_t min_addrlen = 0;
+  int ok = 0;
 
   if (sa == NULL || addrlen < sizeof (sa_family_t))
     return -1;
@@ -257,36 +259,83 @@ getnameinfo (const struct sockaddr *sa, socklen_t addrlen, char *host,
 				 min(hostlen, (size_t) (c - h->h_name)));
 			host[min(hostlen - 1, (size_t) (c - h->h_name))]
 			  = '\0';
-			break;
+			ok = 1;
+		      }
+		    else
+		      {
+			strncpy (host, h->h_name, hostlen);
+			ok = 1;
 		      }
 		  }
 		strncpy (host, h->h_name, hostlen);
-		break;
+		ok = 1;
 	      }
 	  }
 
-	if (flags & NI_NAMEREQD)
+	if (!ok)
 	  {
-	    __set_errno (serrno);
-	    return -1;
-	  }
-	else
-	  {
-	    const char *c;
-	    if (sa->sa_family == AF_INET6)
-	      c = inet_ntop (AF_INET6,
-			     (void *) &(((struct sockaddr_in6 *) sa)->sin6_addr),
-			     host, hostlen);
-	    else
-	      c = inet_ntop (AF_INET,
-			     (void *) &(((struct sockaddr_in *) sa)->sin_addr),
-			     host, hostlen);
-
-	    if (c == NULL)
+	    if (flags & NI_NAMEREQD)
 	      {
 		__set_errno (serrno);
 		return -1;
 	      }
+	    else
+	      {
+		const char *c;
+		if (sa->sa_family == AF_INET6)
+		  {
+		    struct sockaddr_in6 *sin6p = (struct sockaddr_in6 *) sa;
+		    uint32_t scopeid;
+
+		    c = inet_ntop (AF_INET6,
+				   (void *) &sin6p->sin6_addr, host, hostlen);
+		    if (addrlen > sizeof (struct sockaddr_in6)
+			&& (scopeid = sin6p->sin6_scope_id))
+		      {
+			/* Buffer is >= IFNAMSIZ+1.  */
+			char scopebuf[MAXHOSTNAMELEN + 1];
+			int ni_numericscope = 0;
+
+			if (IN6_IS_ADDR_LINKLOCAL (&sin6p->sin6_addr)
+			    || IN6_IS_ADDR_MC_LINKLOCAL (&sin6p->sin6_addr))
+			  {
+			    if (if_indextoname (scopeid, scopebuf) == NULL)
+			      ++ni_numericscope;
+			  }
+			else
+			  ++ni_numericscope;
+
+			if (ni_numericscope)
+			  {
+			    char *scopeptr = &scopebuf[1];
+			    size_t real_hostlen;
+			    size_t scopelen;
+
+			    scopebuf[0] = SCOPE_DELIMITER;
+			    scopelen = 1 + snprintf (scopeptr,
+						     (scopebuf
+						      + sizeof scopebuf
+						      - scopeptr),
+						     "%u", scopeid);
+
+			    real_hostlen = __strnlen (host, hostlen);
+			    if (real_hostlen + scopelen + 1 > hostlen)
+			      return -1;
+			    memcpy (host + real_hostlen, scopebuf, scopelen);
+			  }
+		      }
+		  }
+		else
+		  c = inet_ntop (AF_INET,
+				 (void *) &(((struct sockaddr_in *) sa)->sin_addr),
+				 host, hostlen);
+		if (c == NULL)
+		  {
+		    __set_errno (serrno);
+		    return -1;
+		  }
+	      }
+	    ok = 1;
 	  }
 	break;