Back to index

glibc  2.9
getnameinfo.c
Go to the documentation of this file.
00001 /* The Inner Net License, Version 2.00
00002 
00003   The author(s) grant permission for redistribution and use in source and
00004 binary forms, with or without modification, of the software and documentation
00005 provided that the following conditions are met:
00006 
00007 0. If you receive a version of the software that is specifically labelled
00008    as not being for redistribution (check the version message and/or README),
00009    you are not permitted to redistribute that version of the software in any
00010    way or form.
00011 1. All terms of the all other applicable copyrights and licenses must be
00012    followed.
00013 2. Redistributions of source code must retain the authors' copyright
00014    notice(s), this list of conditions, and the following disclaimer.
00015 3. Redistributions in binary form must reproduce the authors' copyright
00016    notice(s), this list of conditions, and the following disclaimer in the
00017    documentation and/or other materials provided with the distribution.
00018 4. [The copyright holder has authorized the removal of this clause.]
00019 5. Neither the name(s) of the author(s) nor the names of its contributors
00020    may be used to endorse or promote products derived from this software
00021    without specific prior written permission.
00022 
00023 THIS SOFTWARE IS PROVIDED BY ITS AUTHORS AND CONTRIBUTORS ``AS IS'' AND ANY
00024 EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00025 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00026 DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY
00027 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00028 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00029 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
00030 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00031 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00032 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00033 
00034   If these license terms cause you a real problem, contact the author.  */
00035 
00036 /* This software is Copyright 1996 by Craig Metz, All Rights Reserved.  */
00037 
00038 #include <alloca.h>
00039 #include <errno.h>
00040 #include <netdb.h>
00041 #include <stddef.h>
00042 #include <stdlib.h>
00043 #include <stdio.h>
00044 #include <string.h>
00045 #include <unistd.h>
00046 #include <arpa/inet.h>
00047 #include <net/if.h>
00048 #include <netinet/in.h>
00049 #include <sys/param.h>
00050 #include <sys/socket.h>
00051 #include <sys/types.h>
00052 #include <sys/un.h>
00053 #include <sys/utsname.h>
00054 #include <bits/libc-lock.h>
00055 
00056 #ifdef HAVE_LIBIDN
00057 # include <libidn/idna.h>
00058 extern int __idna_to_unicode_lzlz (const char *input, char **output,
00059                                int flags);
00060 #endif
00061 
00062 #ifndef min
00063 # define min(x,y) (((x) > (y)) ? (y) : (x))
00064 #endif /* min */
00065 
00066 libc_freeres_ptr (static char *domain);
00067 
00068 
00069 static char *
00070 internal_function
00071 nrl_domainname (void)
00072 {
00073   static int not_first;
00074 
00075   if (! not_first)
00076     {
00077       __libc_lock_define_initialized (static, lock);
00078       __libc_lock_lock (lock);
00079 
00080       if (! not_first)
00081        {
00082          char *c;
00083          struct hostent *h, th;
00084          size_t tmpbuflen = 1024;
00085          char *tmpbuf = alloca (tmpbuflen);
00086          int herror;
00087 
00088          not_first = 1;
00089 
00090          while (__gethostbyname_r ("localhost", &th, tmpbuf, tmpbuflen, &h,
00091                                 &herror))
00092            {
00093              if (herror == NETDB_INTERNAL && errno == ERANGE)
00094               tmpbuf = extend_alloca (tmpbuf, tmpbuflen, 2 * tmpbuflen);
00095              else
00096               break;
00097            }
00098 
00099          if (h && (c = strchr (h->h_name, '.')))
00100            domain = __strdup (++c);
00101          else
00102            {
00103              /* The name contains no domain information.  Use the name
00104                now to get more information.  */
00105              while (__gethostname (tmpbuf, tmpbuflen))
00106               tmpbuf = extend_alloca (tmpbuf, tmpbuflen, 2 * tmpbuflen);
00107 
00108              if ((c = strchr (tmpbuf, '.')))
00109               domain = __strdup (++c);
00110              else
00111               {
00112                 /* We need to preserve the hostname.  */
00113                 const char *hstname = strdupa (tmpbuf);
00114 
00115                 while (__gethostbyname_r (hstname, &th, tmpbuf, tmpbuflen,
00116                                        &h, &herror))
00117                   {
00118                     if (herror == NETDB_INTERNAL && errno == ERANGE)
00119                      tmpbuf = extend_alloca (tmpbuf, tmpbuflen,
00120                                           2 * tmpbuflen);
00121                     else
00122                      break;
00123                   }
00124 
00125                 if (h && (c = strchr(h->h_name, '.')))
00126                   domain = __strdup (++c);
00127                 else
00128                   {
00129                     struct in_addr in_addr;
00130 
00131                     in_addr.s_addr = htonl (INADDR_LOOPBACK);
00132 
00133                     while (__gethostbyaddr_r ((const char *) &in_addr,
00134                                           sizeof (struct in_addr),
00135                                           AF_INET, &th, tmpbuf,
00136                                           tmpbuflen, &h, &herror))
00137                      {
00138                        if (herror == NETDB_INTERNAL && errno == ERANGE)
00139                          tmpbuf = extend_alloca (tmpbuf, tmpbuflen,
00140                                               2 * tmpbuflen);
00141                        else
00142                          break;
00143                      }
00144 
00145                     if (h && (c = strchr (h->h_name, '.')))
00146                      domain = __strdup (++c);
00147                   }
00148               }
00149            }
00150        }
00151 
00152       __libc_lock_unlock (lock);
00153     }
00154 
00155   return domain;
00156 };
00157 
00158 
00159 int
00160 getnameinfo (const struct sockaddr *sa, socklen_t addrlen, char *host,
00161             socklen_t hostlen, char *serv, socklen_t servlen,
00162             unsigned int flags)
00163 {
00164   int serrno = errno;
00165   int tmpbuflen = 1024;
00166   int herrno;
00167   char *tmpbuf = alloca (tmpbuflen);
00168   struct hostent th;
00169   int ok = 0;
00170 
00171   if (flags & ~(NI_NUMERICHOST|NI_NUMERICSERV|NI_NOFQDN|NI_NAMEREQD|NI_DGRAM
00172 #ifdef HAVE_LIBIDN
00173               |NI_IDN|NI_IDN_ALLOW_UNASSIGNED|NI_IDN_USE_STD3_ASCII_RULES
00174 #endif
00175               ))
00176     return EAI_BADFLAGS;
00177 
00178   if (sa == NULL || addrlen < sizeof (sa_family_t))
00179     return EAI_FAMILY;
00180 
00181   switch (sa->sa_family)
00182     {
00183     case AF_LOCAL:
00184       if (addrlen < (socklen_t) offsetof (struct sockaddr_un, sun_path))
00185        return EAI_FAMILY;
00186       break;
00187     case AF_INET:
00188       if (addrlen < sizeof (struct sockaddr_in))
00189        return EAI_FAMILY;
00190       break;
00191     case AF_INET6:
00192       if (addrlen < sizeof (struct sockaddr_in6))
00193        return EAI_FAMILY;
00194       break;
00195     default:
00196       return EAI_FAMILY;
00197     }
00198 
00199   if (host != NULL && hostlen > 0)
00200     switch (sa->sa_family)
00201       {
00202       case AF_INET:
00203       case AF_INET6:
00204        if (!(flags & NI_NUMERICHOST))
00205          {
00206            struct hostent *h = NULL;
00207            if (sa->sa_family == AF_INET6)
00208              {
00209               while (__gethostbyaddr_r ((const void *) &(((const struct sockaddr_in6 *) sa)->sin6_addr),
00210                                      sizeof(struct in6_addr),
00211                                      AF_INET6, &th, tmpbuf, tmpbuflen,
00212                                      &h, &herrno))
00213                 if (herrno == NETDB_INTERNAL && errno == ERANGE)
00214                   tmpbuf = extend_alloca (tmpbuf, tmpbuflen, 2 * tmpbuflen);
00215                 else
00216                   break;
00217              }
00218            else
00219              {
00220               while (__gethostbyaddr_r ((const void *) &(((const struct sockaddr_in *)sa)->sin_addr),
00221                                      sizeof(struct in_addr), AF_INET,
00222                                      &th, tmpbuf, tmpbuflen,
00223                                      &h, &herrno))
00224                 if (herrno == NETDB_INTERNAL && errno == ERANGE)
00225                   tmpbuf = extend_alloca (tmpbuf, tmpbuflen, 2 * tmpbuflen);
00226                 else
00227                   break;
00228              }
00229 
00230            if (h == NULL)
00231              {
00232               if (herrno == NETDB_INTERNAL)
00233                 {
00234                   __set_h_errno (herrno);
00235                   return EAI_SYSTEM;
00236                 }
00237               if (herrno == TRY_AGAIN)
00238                 {
00239                   __set_h_errno (herrno);
00240                   return EAI_AGAIN;
00241                 }
00242              }
00243 
00244            if (h)
00245              {
00246               char *c;
00247               if ((flags & NI_NOFQDN)
00248                   && (c = nrl_domainname ())
00249                   && (c = strstr (h->h_name, c))
00250                   && (c != h->h_name) && (*(--c) == '.'))
00251                 /* Terminate the string after the prefix.  */
00252                 *c = '\0';
00253 
00254 #ifdef HAVE_LIBIDN
00255               /* If requested, convert from the IDN format.  */
00256               if (flags & NI_IDN)
00257                 {
00258                   int idn_flags = 0;
00259                   if  (flags & NI_IDN_ALLOW_UNASSIGNED)
00260                     idn_flags |= IDNA_ALLOW_UNASSIGNED;
00261                   if (flags & NI_IDN_USE_STD3_ASCII_RULES)
00262                     idn_flags |= IDNA_USE_STD3_ASCII_RULES;
00263 
00264                   char *out;
00265                   int rc = __idna_to_unicode_lzlz (h->h_name, &out,
00266                                                idn_flags);
00267                   if (rc != IDNA_SUCCESS)
00268                     {
00269                      if (rc == IDNA_MALLOC_ERROR)
00270                        return EAI_MEMORY;
00271                      if (rc == IDNA_DLOPEN_ERROR)
00272                        return EAI_SYSTEM;
00273                      return EAI_IDN_ENCODE;
00274                     }
00275 
00276                   if (out != h->h_name)
00277                     {
00278                      h->h_name = strdupa (out);
00279                      free (out);
00280                     }
00281                 }
00282 #endif
00283 
00284               size_t len = strlen (h->h_name) + 1;
00285               if (len > hostlen)
00286                 return EAI_OVERFLOW;
00287 
00288               memcpy (host, h->h_name, len);
00289 
00290               ok = 1;
00291              }
00292          }
00293 
00294        if (!ok)
00295          {
00296            if (flags & NI_NAMEREQD)
00297              {
00298               __set_errno (serrno);
00299               return EAI_NONAME;
00300              }
00301            else
00302              {
00303               const char *c;
00304               if (sa->sa_family == AF_INET6)
00305                 {
00306                   const struct sockaddr_in6 *sin6p;
00307                   uint32_t scopeid;
00308 
00309                   sin6p = (const struct sockaddr_in6 *) sa;
00310 
00311                   c = inet_ntop (AF_INET6,
00312                                (const void *) &sin6p->sin6_addr, host, hostlen);
00313                   scopeid = sin6p->sin6_scope_id;
00314                   if (scopeid != 0)
00315                     {
00316                      /* Buffer is >= IFNAMSIZ+1.  */
00317                      char scopebuf[IFNAMSIZ + 1];
00318                      char *scopeptr;
00319                      int ni_numericscope = 0;
00320                      size_t real_hostlen = __strnlen (host, hostlen);
00321                      size_t scopelen = 0;
00322 
00323                      scopebuf[0] = SCOPE_DELIMITER;
00324                      scopebuf[1] = '\0';
00325                      scopeptr = &scopebuf[1];
00326 
00327                      if (IN6_IS_ADDR_LINKLOCAL (&sin6p->sin6_addr)
00328                          || IN6_IS_ADDR_MC_LINKLOCAL (&sin6p->sin6_addr))
00329                        {
00330                          if (if_indextoname (scopeid, scopeptr) == NULL)
00331                            ++ni_numericscope;
00332                          else
00333                            scopelen = strlen (scopebuf);
00334                        }
00335                      else
00336                        ++ni_numericscope;
00337 
00338                      if (ni_numericscope)
00339                        scopelen = 1 + __snprintf (scopeptr,
00340                                                (scopebuf
00341                                                 + sizeof scopebuf
00342                                                 - scopeptr),
00343                                                "%u", scopeid);
00344 
00345                      if (real_hostlen + scopelen + 1 > hostlen)
00346                        /* XXX We should not fail here.  Simply enlarge
00347                           the buffer or return with out of memory.  */
00348                        return EAI_SYSTEM;
00349                      memcpy (host + real_hostlen, scopebuf, scopelen + 1);
00350                     }
00351                 }
00352               else
00353                 c = inet_ntop (AF_INET,
00354                              (const void *) &(((const struct sockaddr_in *) sa)->sin_addr),
00355                              host, hostlen);
00356               if (c == NULL)
00357                 return EAI_SYSTEM;
00358              }
00359            ok = 1;
00360          }
00361        break;
00362 
00363       case AF_LOCAL:
00364        if (!(flags & NI_NUMERICHOST))
00365          {
00366            struct utsname utsname;
00367 
00368            if (!uname (&utsname))
00369              {
00370               strncpy (host, utsname.nodename, hostlen);
00371               break;
00372              };
00373          };
00374 
00375        if (flags & NI_NAMEREQD)
00376           {
00377            __set_errno (serrno);
00378            return EAI_NONAME;
00379          }
00380 
00381        strncpy (host, "localhost", hostlen);
00382        break;
00383 
00384       default:
00385         return EAI_FAMILY;
00386     }
00387 
00388   if (serv && (servlen > 0))
00389     switch (sa->sa_family)
00390       {
00391       case AF_INET:
00392       case AF_INET6:
00393        if (!(flags & NI_NUMERICSERV))
00394          {
00395            struct servent *s, ts;
00396            int e;
00397            while ((e = __getservbyport_r (((const struct sockaddr_in *) sa)->sin_port,
00398                                       ((flags & NI_DGRAM)
00399                                        ? "udp" : "tcp"),
00400                                       &ts, tmpbuf, tmpbuflen, &s)))
00401              {
00402               if (e == ERANGE)
00403                 tmpbuf = extend_alloca (tmpbuf, tmpbuflen, 2 * tmpbuflen);
00404               else
00405                 break;
00406              }
00407            if (s)
00408              {
00409               strncpy (serv, s->s_name, servlen);
00410               break;
00411              }
00412          }
00413 
00414        if (__snprintf (serv, servlen, "%d",
00415                      ntohs (((const struct sockaddr_in *) sa)->sin_port))
00416            + 1 > servlen)
00417          return EAI_OVERFLOW;
00418 
00419        break;
00420 
00421       case AF_LOCAL:
00422        strncpy (serv, ((const struct sockaddr_un *) sa)->sun_path, servlen);
00423        break;
00424     }
00425 
00426   if (host && (hostlen > 0))
00427     host[hostlen-1] = 0;
00428   if (serv && (servlen > 0))
00429     serv[servlen-1] = 0;
00430   errno = serrno;
00431   return 0;
00432 }
00433 libc_hidden_def (getnameinfo)