about summary refs log tree commit diff
path: root/inet
diff options
context:
space:
mode:
Diffstat (limited to 'inet')
-rw-r--r--inet/getnameinfo.c61
1 files changed, 48 insertions, 13 deletions
diff --git a/inet/getnameinfo.c b/inet/getnameinfo.c
index d735bbf9ea..447980d5d9 100644
--- a/inet/getnameinfo.c
+++ b/inet/getnameinfo.c
@@ -51,6 +51,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <sys/utsname.h>
 #include <bits/libc-lock.h>
 
+#ifdef HAVE_LIBIDN
+# include <libidn/idna.h>
+extern int __idna_to_unicode_lzlz (const char *input, char **output,
+				   int flags);
+#endif
+
 #ifndef min
 # define min(x,y) (((x) > (y)) ? (y) : (x))
 #endif /* min */
@@ -160,7 +166,11 @@ getnameinfo (const struct sockaddr *sa, socklen_t addrlen, char *host,
   struct hostent th;
   int ok = 0;
 
-  if (flags & ~(NI_NUMERICHOST|NI_NUMERICSERV|NI_NOFQDN|NI_NAMEREQD|NI_DGRAM))
+  if (flags & ~(NI_NUMERICHOST|NI_NUMERICSERV|NI_NOFQDN|NI_NAMEREQD|NI_DGRAM
+#ifdef HAVE_LIBIDN
+		|NI_IDN
+#endif
+		))
     return EAI_BADFLAGS;
 
   if (sa == NULL || addrlen < sizeof (sa_family_t))
@@ -244,18 +254,39 @@ getnameinfo (const struct sockaddr *sa, socklen_t addrlen, char *host,
 		    && (c = nrl_domainname ())
 		    && (c = strstr (h->h_name, c))
 		    && (c != h->h_name) && (*(--c) == '.'))
+		  /* Terminate the string after the prefix.  */
+		  *c = '\0';
+
+#ifdef HAVE_LIBIDN
+		/* If requested, convert from the IDN format.  */
+		if (flags & NI_IDN)
 		  {
-		    strncpy (host, h->h_name,
-			     min(hostlen, (size_t) (c - h->h_name)));
-		    host[min(hostlen - 1, (size_t) (c - h->h_name))]
-		      = '\0';
-		    ok = 1;
-		  }
-		else
-		  {
-		    strncpy (host, h->h_name, hostlen);
-		    ok = 1;
+		    char *out;
+		    int rc = __idna_to_unicode_lzlz (h->h_name, &out, 0);
+		    if (rc != IDNA_SUCCESS)
+		      {
+			if (rc == IDNA_MALLOC_ERROR)
+			  return EAI_MEMORY;
+			if (rc == IDNA_DLOPEN_ERROR)
+			  return EAI_SYSTEM;
+			return EAI_IDN_ENCODE;
+		      }
+
+		    if (out != h->h_name)
+		      {
+			h->h_name = strdupa (out);
+			free (out);
+		      }
 		  }
+#endif
+
+		size_t len = strlen (h->h_name) + 1;
+		if (len > hostlen)
+		  return EAI_OVERFLOW;
+
+		memcpy (host, h->h_name, len);
+
+		ok = 1;
 	      }
 	  }
 
@@ -390,8 +421,12 @@ getnameinfo (const struct sockaddr *sa, socklen_t addrlen, char *host,
 		break;
 	      }
 	  }
-	__snprintf (serv, servlen, "%d",
-		    ntohs (((const struct sockaddr_in *) sa)->sin_port));
+
+	if (__snprintf (serv, servlen, "%d",
+			ntohs (((const struct sockaddr_in *) sa)->sin_port))
+	    + 1 > servlen)
+	  return EAI_OVERFLOW;
+
 	break;
 
       case AF_LOCAL: