From patchwork Fri Apr 29 15:35:22 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florian Weimer X-Patchwork-Id: 616870 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3qxHpW0J7hz9t3k for ; Sat, 30 Apr 2016 01:35:46 +1000 (AEST) Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; secure) header.d=sourceware.org header.i=@sourceware.org header.b=nbY+EseY; dkim-atps=neutral DomainKey-Signature: a=rsa-sha1; c=nofws; d=sourceware.org; h=list-id :list-unsubscribe:list-subscribe:list-archive:list-post :list-help:sender:to:from:subject:message-id:date:mime-version :content-type; q=dns; s=default; b=kjJHFhmb/gEkPsrUHBukIm17nKX9b lc1Xjwp20ln3nlFZMD8nwf7Yx7ZOOC7SwVIYW/Q2bEJVNzVAbopjkCIFEyuuNe8K yGIcitjnUi2tsMppZY81y4igVFr/AlKPUrvwwQNJO4imhvtPqwUeuxm4LrQsy1Bm EwO0G579zCfIWk= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=sourceware.org; h=list-id :list-unsubscribe:list-subscribe:list-archive:list-post :list-help:sender:to:from:subject:message-id:date:mime-version :content-type; s=default; bh=D5gGk6rqNDMVys4WwoLiyt8h5/E=; b=nbY +EseYlWfM6WCpnlxaXdaUP6iRTZwS2qWdUOR8bh/BT26O5cjfXorleXg5DoGu9K+ KMK8D1rkRIPT1oJRjT70Q4T62yVCFMDWbSjuuPuZAfBDDB5ISX5c5uX4XxBYXjJe uwt/ySQoL5HpQGn6XTT/3avU0aCXfu65x05wVBYM= Received: (qmail 94175 invoked by alias); 29 Apr 2016 15:35:36 -0000 Mailing-List: contact libc-alpha-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: libc-alpha-owner@sourceware.org Delivered-To: mailing list libc-alpha@sourceware.org Received: (qmail 94160 invoked by uid 89); 29 Apr 2016 15:35:36 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-1.9 required=5.0 tests=BAYES_00, KAM_LAZY_DOMAIN_SECURITY, RP_MATCHES_RCVD, SPF_HELO_PASS autolearn=ham version=3.3.2 spammy=numeric, __file__, __FILE__, Determine X-HELO: mx1.redhat.com To: GNU C Library From: Florian Weimer Subject: [PATCH COMMITTED] getnameinfo: Refactor and fix memory leak [BZ #19642] Message-ID: <57237F3A.3000003@redhat.com> Date: Fri, 29 Apr 2016 17:35:22 +0200 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:38.0) Gecko/20100101 Thunderbird/38.7.1 MIME-Version: 1.0 Split getnameinfo into separate functions for host and service lookups, and for different address families. I preserved some pre-existing long lines for now. Validated with the new and attached nss_files test (which depends on the nss_files testing framework I posted earlier) and with the external libresolv test suite. Florian /* Test getnameinfo using nss_files. Copyright (C) 2016 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, see . */ #include "tst-nss-aux.h" #include "tst-nss-files-aux.h" #include #include #include #include /* create_file_callback for /etc/hosts. */ static void create_hosts (const char *name, FILE *out, void *closure) { fputs ("127.0.0.1 localhost localhost.localdomain localhost4\n", out); fputs ("::1 localhost localhost.localdomain localhost6\n", out); fputs ("192.0.2.1 reverse-ipv4 reverse-ipv4.example\n", out); fputs ("2001:db8::1 reverse-ipv6 reverse-ipv6.example\n", out); fputs ("192.0.2.2 reverse reverse.example\n", out); fputs ("2001:db8::2 reverse reverse.example\n", out); fputs ("192.0.2.3 other-ipv4.example other-ipv4\n", out); fputs ("2001:db8::3 other-ipv6.example other-ipv6\n", out); fputs ("192.0.2.4 other.example other\n", out); fputs ("2001:db8::4 other.example other\n", out); } static void create_services (const char *name, FILE *out, void *closure) { fputs ("www 80/tcp\n", out); fputs ("exec 512/tcp\n", out); fputs ("biff 512/udp comsat\n", out); } static bool check_gni_match (int line, const char *kind, int flags, const char *expected, const char *actual) { if (expected != NULL && strcmp (actual, expected) != 0) { record_delayed_failure (); printf ("%1$s:%2$d: error: %3$s mismatch (flags 0x%6$x)\n" "%1$s:%2$d: error: expected: %4$s\n" "%1$s:%2$d: error: actual: %5$s\n", __FILE__, line, kind, expected, actual, flags); return false; } return true; } static void check_getnameinfo (const void *addr, size_t addrlen, int flags, const char *expected_host, const char *expected_service, int line) { size_t hostlen = 0; char *host = NULL; if (expected_host != NULL) { hostlen = strlen (expected_host) + 1; host = xmalloc (hostlen); } size_t servlen = 0; char *serv = NULL; if (expected_service != NULL) { servlen = strlen (expected_service) + 1; serv = xmalloc (servlen); } int ret = getnameinfo (addr, addrlen, host, hostlen, serv, servlen, flags); if (ret != 0) { record_delayed_failure (); printf ("%s:%d: error: unexpected error %s (%d) with flags 0x%x\n", __FILE__, line, gai_strerror (ret), ret, flags); } else { check_gni_match (line, "host", flags, expected_host, host); check_gni_match (line, "service", flags, expected_service, serv); } free (serv); free (host); } #define CHECK_GETNAMEINFO(addr, addrlen, flags, exp_host, exp_service) \ check_getnameinfo (addr, addrlen, flags, exp_host, exp_service, __LINE__) static void check_getnameinfo_error (const void *addr, size_t addrlen, int flags, int expected_error, int line) { char host[NI_MAXHOST]; char serv[NI_MAXSERV]; int ret = getnameinfo (addr, addrlen, host, sizeof (host), serv, sizeof (serv), flags); if (ret != expected_error) { record_delayed_failure (); printf ("%s:%d: error: expected error %s (%d), got %s (%d)," " with flags 0x%x\n", __FILE__, line, gai_strerror (expected_error), expected_error, gai_strerror (ret), ret, flags); } } #define CHECK_GETNAMEINFO_ERROR(addr, addrlen, flags, expected_error) \ check_getnameinfo_error (addr, addrlen, flags, expected_error, __LINE__) static void set_host_name (void) { if (is_in_uts_namespace ()) { const char *name = "host.example"; if (sethostname (name, strlen (name)) != 0) printf ("warning: sethostname (\"%s\"): %m\n", name); } } static void test_getnameinfo_inet (void) { set_host_name (); struct sockaddr_in sin = { .sin_family = AF_INET, .sin_addr = {ntohl (0xc0000200)}, .sin_port = ntohs (512) }; struct sockaddr_in6 sin6 = { .sin6_family = AF_INET6, .sin6_port = ntohs (512) }; memcpy (&sin6.sin6_addr, "\x20\x01\x0d\xb8\0\0\0\0\0\0\0\0\0\0\0\0", 16); for (int use_ipv6 = 0; use_ipv6 < 2; ++use_ipv6) for (int address = 1; address <= 5; ++address) for (int use_numerichost = 0; use_numerichost < 2; ++use_numerichost) for (int use_namereqd = 0; use_namereqd < 2; ++use_namereqd) for (int use_numericserv = 0; use_numericserv < 2; ++use_numericserv) for (int use_nofqdn = 0; use_nofqdn < 2; ++use_nofqdn) for (int use_dgram = 0; use_dgram < 2; ++use_dgram) { /* Address without reverse lookup. */ bool nxdomain = address == 5; ((char *)&sin.sin_addr.s_addr)[3] = address; ((char *)&sin6.sin6_addr.s6_addr)[15] = address; void *sa; socklen_t salen; if (use_ipv6) { sa = &sin6; salen = sizeof (sin6); } else { sa = &sin; salen = sizeof (sin); } char numeric_host[] = "192.0.2.0"; numeric_host[strlen (numeric_host) - 1] += address; char numeric_host6[] = "2001:db8::0"; numeric_host6[strlen (numeric_host6) - 1] += address; int flags = 0; if (use_numerichost) flags |= NI_NUMERICHOST; if (use_namereqd) flags |= NI_NAMEREQD; if (use_numericserv) flags |= NI_NUMERICSERV; if (use_nofqdn) flags |= NI_NOFQDN; if (use_dgram) flags |= NI_DGRAM; /* NI_NUMERICHOST with NI_NAMEREQD is not allowed. NI_NAMEREQD without a reverse lookup results in EAI_NONAME. */ if ((use_numerichost && use_namereqd) || (use_namereqd && nxdomain)) { CHECK_GETNAMEINFO_ERROR (sa, salen, flags, EAI_NONAME); continue; } const char *exp_host; if (use_numerichost || nxdomain) { /* Number output requested, or no reverse lookup. */ if (use_ipv6) exp_host = numeric_host6; else exp_host = numeric_host; } else /* Determine the expected address. 1 & 3 have different names for IPv4 and IPv6. 1/2 and 3/4 have different order of the aliases, and the first entry counts. */ switch (address) { case 1: if (use_ipv6) exp_host = "reverse-ipv6"; else exp_host = "reverse-ipv4"; break; case 2: exp_host = "reverse"; break; case 3: if (use_nofqdn) { if (use_ipv6) exp_host = "other-ipv6"; else exp_host = "other-ipv4"; } else { if (use_ipv6) exp_host = "other-ipv6.example"; else exp_host = "other-ipv4.example"; } break; case 4: if (use_nofqdn) exp_host = "other"; else exp_host = "other.example"; break; default: abort (); } const char *exp_serv; if (use_numericserv) exp_serv = "512"; else if (use_dgram) exp_serv = "biff"; else exp_serv = "exec"; CHECK_GETNAMEINFO (sa, salen, flags, exp_host, exp_serv); } } static void test_getnameinfo_local (void) { set_host_name (); char *expected_hostname = xgethostname (); struct sockaddr_un sun = { .sun_family = AF_LOCAL, .sun_path = "/tmp/.X11-unix/X0" }; for (int use_numerichost = 0; use_numerichost < 2; ++use_numerichost) for (int use_namereqd = 0; use_namereqd < 2; ++use_namereqd) for (int use_numericserv = 0; use_numericserv < 2; ++use_numericserv) for (int use_nofqdn = 0; use_nofqdn < 2; ++use_nofqdn) for (int use_dgram = 0; use_dgram < 2; ++use_dgram) { int flags = 0; const char *exp_host = expected_hostname; if (use_numerichost) { flags |= NI_NUMERICHOST; exp_host = "localhost"; } if (use_namereqd) flags |= NI_NAMEREQD; if (use_numericserv) flags |= NI_NUMERICSERV; if (use_nofqdn) flags |= NI_NOFQDN; if (use_dgram) flags |= NI_DGRAM; if (use_numerichost && use_namereqd) { CHECK_GETNAMEINFO_ERROR (&sun, sizeof (sun), flags, EAI_NONAME); continue; } CHECK_GETNAMEINFO (&sun, sizeof (sun), flags, exp_host, "/tmp/.X11-unix/X0"); } free (expected_hostname); } static int do_test (void) { configure_nss_files (); char *root = create_chroot (); create_etc_file (root, "hosts", create_hosts, NULL); create_etc_file (root, "services", create_services, NULL); isolate_in_subprocess (root, test_getnameinfo_inet); isolate_in_subprocess (root, test_getnameinfo_local); free (root); return 0; } #define TEST_FUNCTION do_test () #include "../test-skeleton.c" 2016-04-29 Florian Weimer [BZ #19642] * inet/getnameinfo.c (gni_host_inet_name, gni_host_inet_numeric) (gni_host_inet, gni_host_local, gni_host, gni_serv_inet) (gni_serv_local, gni_serv): New functions extracted from getnameinfo. (getnameinfo): Call gni_host and gni_serv to perform the processing. Always free scratch buffer. diff --git a/inet/getnameinfo.c b/inet/getnameinfo.c index 9b1847b..ce05dda 100644 --- a/inet/getnameinfo.c +++ b/inet/getnameinfo.c @@ -1,3 +1,21 @@ +/* Convert socket address to string using Name Service Switch modules. + Copyright (C) 1997-2016 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + /* The Inner Net License, Version 2.00 The author(s) grant permission for redistribution and use in source and @@ -169,19 +187,323 @@ nrl_domainname (void) return domain; }; +/* Convert host name, AF_INET/AF_INET6 case, name only. */ +static int +gni_host_inet_name (struct scratch_buffer *tmpbuf, + const struct sockaddr *sa, socklen_t addrlen, + char *host, socklen_t hostlen, int flags) +{ + int herrno; + struct hostent th; + struct hostent *h = NULL; + if (sa->sa_family == AF_INET6) + { + while (__gethostbyaddr_r ((const void *) &(((const struct sockaddr_in6 *) sa)->sin6_addr), + sizeof(struct in6_addr), + AF_INET6, &th, + tmpbuf->data, tmpbuf->length, + &h, &herrno)) + if (herrno == NETDB_INTERNAL && errno == ERANGE) + { + if (!scratch_buffer_grow (tmpbuf)) + { + __set_h_errno (herrno); + return EAI_MEMORY; + } + } + else + break; + } + else + { + while (__gethostbyaddr_r ((const void *) &(((const struct sockaddr_in *)sa)->sin_addr), + sizeof(struct in_addr), + AF_INET, &th, + tmpbuf->data, tmpbuf->length, + &h, &herrno)) + if (herrno == NETDB_INTERNAL && errno == ERANGE) + { + if (!scratch_buffer_grow (tmpbuf)) + { + __set_h_errno (herrno); + return EAI_MEMORY; + } + } + else + break; + } + + if (h == NULL) + { + if (herrno == NETDB_INTERNAL) + { + __set_h_errno (herrno); + return EAI_SYSTEM; + } + if (herrno == TRY_AGAIN) + { + __set_h_errno (herrno); + return EAI_AGAIN; + } + } + + if (h) + { + char *c; + if ((flags & NI_NOFQDN) + && (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) + { + int idn_flags = 0; + if (flags & NI_IDN_ALLOW_UNASSIGNED) + idn_flags |= IDNA_ALLOW_UNASSIGNED; + if (flags & NI_IDN_USE_STD3_ASCII_RULES) + idn_flags |= IDNA_USE_STD3_ASCII_RULES; + + char *out; + int rc = __idna_to_unicode_lzlz (h->h_name, &out, + idn_flags); + 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); + + return 0; + } + + return EAI_NONAME; +} + +/* Convert host name, AF_INET/AF_INET6 case, numeric conversion. */ +static int +gni_host_inet_numeric (struct scratch_buffer *tmpbuf, + const struct sockaddr *sa, socklen_t addrlen, + char *host, socklen_t hostlen, int flags) +{ + const char *c; + if (sa->sa_family == AF_INET6) + { + const struct sockaddr_in6 *sin6p; + uint32_t scopeid; + + sin6p = (const struct sockaddr_in6 *) sa; + + c = inet_ntop (AF_INET6, + (const void *) &sin6p->sin6_addr, host, hostlen); + scopeid = sin6p->sin6_scope_id; + if (scopeid != 0) + { + /* Buffer is >= IFNAMSIZ+1. */ + char scopebuf[IFNAMSIZ + 1]; + char *scopeptr; + int ni_numericscope = 0; + size_t real_hostlen = __strnlen (host, hostlen); + size_t scopelen = 0; + + scopebuf[0] = SCOPE_DELIMITER; + scopebuf[1] = '\0'; + scopeptr = &scopebuf[1]; + + if (IN6_IS_ADDR_LINKLOCAL (&sin6p->sin6_addr) + || IN6_IS_ADDR_MC_LINKLOCAL (&sin6p->sin6_addr)) + { + if (if_indextoname (scopeid, scopeptr) == NULL) + ++ni_numericscope; + else + scopelen = strlen (scopebuf); + } + else + ++ni_numericscope; + + if (ni_numericscope) + scopelen = 1 + __snprintf (scopeptr, + (scopebuf + + sizeof scopebuf + - scopeptr), + "%u", scopeid); + + if (real_hostlen + scopelen + 1 > hostlen) + /* Signal the buffer is too small. This is + what inet_ntop does. */ + c = NULL; + else + memcpy (host + real_hostlen, scopebuf, scopelen + 1); + } + } + else + c = inet_ntop (AF_INET, + (const void *) &(((const struct sockaddr_in *) sa)->sin_addr), + host, hostlen); + if (c == NULL) + return EAI_OVERFLOW; + return 0; +} + +static int +gni_host_inet (struct scratch_buffer *tmpbuf, + const struct sockaddr *sa, socklen_t addrlen, + char *host, socklen_t hostlen, int flags) +{ + if (!(flags & NI_NUMERICHOST)) + { + int result = gni_host_inet_name + (tmpbuf, sa, addrlen, host, hostlen, flags); + if (result != EAI_NONAME) + return result; + } + + if (flags & NI_NAMEREQD) + return EAI_NONAME; + else + return gni_host_inet_numeric + (tmpbuf, sa, addrlen, host, hostlen, flags); +} + +static int +gni_host_local (struct scratch_buffer *tmpbuf, + const struct sockaddr *sa, socklen_t addrlen, + char *host, socklen_t hostlen, int flags) +{ + + if (!(flags & NI_NUMERICHOST)) + { + struct utsname utsname; + + if (!uname (&utsname)) + { + strncpy (host, utsname.nodename, hostlen); + return 0; + } + } + + if (flags & NI_NAMEREQD) + return EAI_NONAME; + + strncpy (host, "localhost", hostlen); + return 0; +} + +static int +gni_host (struct scratch_buffer *tmpbuf, + const struct sockaddr *sa, socklen_t addrlen, + char *host, socklen_t hostlen, int flags) +{ + switch (sa->sa_family) + { + case AF_INET: + case AF_INET6: + return gni_host_inet (tmpbuf, sa, addrlen, host, hostlen, flags); + + case AF_LOCAL: + return gni_host_local (tmpbuf, sa, addrlen, host, hostlen, flags); + + default: + return EAI_FAMILY; + } +} + +/* Convert service to string, AF_INET and AF_INET6 variant. */ +static int +gni_serv_inet (struct scratch_buffer *tmpbuf, + const struct sockaddr *sa, socklen_t addrlen, + char *serv, socklen_t servlen, int flags) +{ + _Static_assert + (offsetof (struct sockaddr_in, sin_port) + == offsetof (struct sockaddr_in6, sin6_port) + && sizeof (((struct sockaddr_in) {}).sin_port) == sizeof (in_port_t) + && sizeof (((struct sockaddr_in6) {}).sin6_port) == sizeof (in_port_t), + "AF_INET and AF_INET6 port consistency"); + if (!(flags & NI_NUMERICSERV)) + { + struct servent *s, ts; + int e; + while ((e = __getservbyport_r (((const struct sockaddr_in *) sa)->sin_port, + ((flags & NI_DGRAM) + ? "udp" : "tcp"), &ts, + tmpbuf->data, tmpbuf->length, &s))) + { + if (e == ERANGE) + { + if (!scratch_buffer_grow (tmpbuf)) + return EAI_MEMORY; + } + else + break; + } + if (s) + { + strncpy (serv, s->s_name, servlen); + return 0; + } + /* Fall through to numeric conversion. */ + } + if (__snprintf (serv, servlen, "%d", + ntohs (((const struct sockaddr_in *) sa)->sin_port)) + + 1 > servlen) + return EAI_OVERFLOW; + return 0; +} + +/* Convert service to string, AF_LOCAL variant. */ +static int +gni_serv_local (struct scratch_buffer *tmpbuf, + const struct sockaddr *sa, socklen_t addrlen, + char *serv, socklen_t servlen, int flags) +{ + strncpy (serv, ((const struct sockaddr_un *) sa)->sun_path, servlen); + return 0; +} + +/* Convert service to string, dispatching to the implementations + above. */ +static int +gni_serv (struct scratch_buffer *tmpbuf, + const struct sockaddr *sa, socklen_t addrlen, + char *serv, socklen_t servlen, int flags) +{ + switch (sa->sa_family) + { + case AF_INET: + case AF_INET6: + return gni_serv_inet (tmpbuf, sa, addrlen, serv, servlen, flags); + case AF_LOCAL: + return gni_serv_local (tmpbuf, sa, addrlen, serv, servlen, flags); + default: + return EAI_FAMILY; + } +} int getnameinfo (const struct sockaddr *sa, socklen_t addrlen, char *host, socklen_t hostlen, char *serv, socklen_t servlen, int flags) { - int herrno; - struct hostent th; - int ok = 0; - struct scratch_buffer tmpbuf; - - scratch_buffer_init (&tmpbuf); - if (flags & ~(NI_NUMERICHOST|NI_NUMERICSERV|NI_NOFQDN|NI_NAMEREQD|NI_DGRAM #ifdef HAVE_LIBIDN |NI_IDN|NI_IDN_ALLOW_UNASSIGNED|NI_IDN_USE_STD3_ASCII_RULES @@ -213,249 +535,34 @@ getnameinfo (const struct sockaddr *sa, socklen_t addrlen, char *host, return EAI_FAMILY; } - if (host != NULL && hostlen > 0) - switch (sa->sa_family) - { - case AF_INET: - case AF_INET6: - if (!(flags & NI_NUMERICHOST)) - { - struct hostent *h = NULL; - if (sa->sa_family == AF_INET6) - { - while (__gethostbyaddr_r ((const void *) &(((const struct sockaddr_in6 *) sa)->sin6_addr), - sizeof(struct in6_addr), - AF_INET6, &th, - tmpbuf.data, tmpbuf.length, - &h, &herrno)) - if (herrno == NETDB_INTERNAL && errno == ERANGE) - { - if (!scratch_buffer_grow (&tmpbuf)) - { - __set_h_errno (herrno); - return EAI_MEMORY; - } - } - else - break; - } - else - { - while (__gethostbyaddr_r ((const void *) &(((const struct sockaddr_in *)sa)->sin_addr), - sizeof(struct in_addr), - AF_INET, &th, - tmpbuf.data, tmpbuf.length, - &h, &herrno)) - if (herrno == NETDB_INTERNAL && errno == ERANGE) - { - if (!scratch_buffer_grow (&tmpbuf)) - { - __set_h_errno (herrno); - return EAI_MEMORY; - } - } - else - break; - } - - if (h == NULL) - { - if (herrno == NETDB_INTERNAL) - { - __set_h_errno (herrno); - return EAI_SYSTEM; - } - if (herrno == TRY_AGAIN) - { - __set_h_errno (herrno); - return EAI_AGAIN; - } - } - - if (h) - { - char *c; - if ((flags & NI_NOFQDN) - && (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) - { - int idn_flags = 0; - if (flags & NI_IDN_ALLOW_UNASSIGNED) - idn_flags |= IDNA_ALLOW_UNASSIGNED; - if (flags & NI_IDN_USE_STD3_ASCII_RULES) - idn_flags |= IDNA_USE_STD3_ASCII_RULES; - - char *out; - int rc = __idna_to_unicode_lzlz (h->h_name, &out, - idn_flags); - 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; - } - } - - if (!ok) - { - if (flags & NI_NAMEREQD) - return EAI_NONAME; - else - { - const char *c; - if (sa->sa_family == AF_INET6) - { - const struct sockaddr_in6 *sin6p; - uint32_t scopeid; - - sin6p = (const struct sockaddr_in6 *) sa; - - c = inet_ntop (AF_INET6, - (const void *) &sin6p->sin6_addr, host, hostlen); - scopeid = sin6p->sin6_scope_id; - if (scopeid != 0) - { - /* Buffer is >= IFNAMSIZ+1. */ - char scopebuf[IFNAMSIZ + 1]; - char *scopeptr; - int ni_numericscope = 0; - size_t real_hostlen = __strnlen (host, hostlen); - size_t scopelen = 0; - - scopebuf[0] = SCOPE_DELIMITER; - scopebuf[1] = '\0'; - scopeptr = &scopebuf[1]; - - if (IN6_IS_ADDR_LINKLOCAL (&sin6p->sin6_addr) - || IN6_IS_ADDR_MC_LINKLOCAL (&sin6p->sin6_addr)) - { - if (if_indextoname (scopeid, scopeptr) == NULL) - ++ni_numericscope; - else - scopelen = strlen (scopebuf); - } - else - ++ni_numericscope; - - if (ni_numericscope) - scopelen = 1 + __snprintf (scopeptr, - (scopebuf - + sizeof scopebuf - - scopeptr), - "%u", scopeid); - - if (real_hostlen + scopelen + 1 > hostlen) - /* Signal the buffer is too small. This is - what inet_ntop does. */ - c = NULL; - else - memcpy (host + real_hostlen, scopebuf, scopelen + 1); - } - } - else - c = inet_ntop (AF_INET, - (const void *) &(((const struct sockaddr_in *) sa)->sin_addr), - host, hostlen); - if (c == NULL) - return EAI_OVERFLOW; - } - ok = 1; - } - break; - - case AF_LOCAL: - if (!(flags & NI_NUMERICHOST)) - { - struct utsname utsname; - - if (!uname (&utsname)) - { - strncpy (host, utsname.nodename, hostlen); - break; - }; - }; - - if (flags & NI_NAMEREQD) - return EAI_NONAME; - - strncpy (host, "localhost", hostlen); - break; + struct scratch_buffer tmpbuf; + scratch_buffer_init (&tmpbuf); - default: - return EAI_FAMILY; + if (host != NULL && hostlen > 0) + { + int result = gni_host (&tmpbuf, sa, addrlen, host, hostlen, flags); + if (result != 0) + { + scratch_buffer_free (&tmpbuf); + return result; + } } if (serv && (servlen > 0)) - switch (sa->sa_family) - { - case AF_INET: - case AF_INET6: - if (!(flags & NI_NUMERICSERV)) - { - struct servent *s, ts; - int e; - while ((e = __getservbyport_r (((const struct sockaddr_in *) sa)->sin_port, - ((flags & NI_DGRAM) - ? "udp" : "tcp"), &ts, - tmpbuf.data, tmpbuf.length, &s))) - { - if (e == ERANGE) - { - if (!scratch_buffer_grow (&tmpbuf)) - return EAI_MEMORY; - } - else - break; - } - if (s) - { - strncpy (serv, s->s_name, servlen); - break; - } - } - - if (__snprintf (serv, servlen, "%d", - ntohs (((const struct sockaddr_in *) sa)->sin_port)) - + 1 > servlen) - return EAI_OVERFLOW; - - break; - - case AF_LOCAL: - strncpy (serv, ((const struct sockaddr_un *) sa)->sun_path, servlen); - break; + { + int result = gni_serv (&tmpbuf, sa, addrlen, serv, servlen, flags); + if (result != 0) + { + scratch_buffer_free (&tmpbuf); + return result; + } } if (host && (hostlen > 0)) host[hostlen-1] = 0; if (serv && (servlen > 0)) serv[servlen-1] = 0; + scratch_buffer_free (&tmpbuf); return 0; } libc_hidden_def (getnameinfo)