Home | History | Annotate | Download | only in c-ares
      1 
      2 /* Copyright 2005 by Dominick Meglio
      3  *
      4  * Permission to use, copy, modify, and distribute this
      5  * software and its documentation for any purpose and without
      6  * fee is hereby granted, provided that the above copyright
      7  * notice appear in all copies and that both that copyright
      8  * notice and this permission notice appear in supporting
      9  * documentation, and that the name of M.I.T. not be used in
     10  * advertising or publicity pertaining to distribution of the
     11  * software without specific, written prior permission.
     12  * M.I.T. makes no representations about the suitability of
     13  * this software for any purpose.  It is provided "as is"
     14  * without express or implied warranty.
     15  */
     16 #include "ares_setup.h"
     17 
     18 #ifdef HAVE_GETSERVBYPORT_R
     19 #  if !defined(GETSERVBYPORT_R_ARGS) || \
     20      (GETSERVBYPORT_R_ARGS < 4) || (GETSERVBYPORT_R_ARGS > 6)
     21 #    error "you MUST specifiy a valid number of arguments for getservbyport_r"
     22 #  endif
     23 #endif
     24 
     25 #ifdef HAVE_SYS_SOCKET_H
     26 #  include <sys/socket.h>
     27 #endif
     28 #ifdef HAVE_NETINET_IN_H
     29 #  include <netinet/in.h>
     30 #endif
     31 #ifdef HAVE_NETDB_H
     32 #  include <netdb.h>
     33 #endif
     34 #ifdef HAVE_ARPA_INET_H
     35 #  include <arpa/inet.h>
     36 #endif
     37 #ifdef HAVE_ARPA_NAMESER_H
     38 #  include <arpa/nameser.h>
     39 #else
     40 #  include "nameser.h"
     41 #endif
     42 #ifdef HAVE_ARPA_NAMESER_COMPAT_H
     43 #  include <arpa/nameser_compat.h>
     44 #endif
     45 
     46 #ifdef HAVE_NET_IF_H
     47 #include <net/if.h>
     48 #endif
     49 
     50 #ifdef HAVE_UNISTD_H
     51 #include <unistd.h>
     52 #endif
     53 
     54 #include <stdio.h>
     55 #include <stdlib.h>
     56 #include <string.h>
     57 
     58 #include "ares.h"
     59 #include "ares_ipv6.h"
     60 #include "inet_ntop.h"
     61 #include "ares_nowarn.h"
     62 #include "ares_private.h"
     63 
     64 struct nameinfo_query {
     65   ares_nameinfo_callback callback;
     66   void *arg;
     67   union {
     68     struct sockaddr_in addr4;
     69     struct sockaddr_in6 addr6;
     70   } addr;
     71   int family;
     72   int flags;
     73   int timeouts;
     74 };
     75 
     76 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
     77 #define IPBUFSIZ \
     78         (sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255") + IF_NAMESIZE)
     79 #else
     80 #define IPBUFSIZ \
     81         (sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"))
     82 #endif
     83 
     84 static void nameinfo_callback(void *arg, int status, int timeouts,
     85                               struct hostent *host);
     86 static char *lookup_service(unsigned short port, int flags,
     87                             char *buf, size_t buflen);
     88 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
     89 static void append_scopeid(struct sockaddr_in6 *addr6, unsigned int scopeid,
     90                            char *buf, size_t buflen);
     91 #endif
     92 static char *ares_striendstr(const char *s1, const char *s2);
     93 
     94 void ares_getnameinfo(ares_channel channel, const struct sockaddr *sa,
     95                       ares_socklen_t salen,
     96                       int flags, ares_nameinfo_callback callback, void *arg)
     97 {
     98   struct sockaddr_in *addr = NULL;
     99   struct sockaddr_in6 *addr6 = NULL;
    100   struct nameinfo_query *niquery;
    101   unsigned int port = 0;
    102 
    103   /* Validate socket address family and length */
    104   if ((sa->sa_family == AF_INET) &&
    105       (salen == sizeof(struct sockaddr_in)))
    106     {
    107       addr = (struct sockaddr_in *)sa;
    108       port = addr->sin_port;
    109     }
    110   else if ((sa->sa_family == AF_INET6) &&
    111            (salen == sizeof(struct sockaddr_in6)))
    112     {
    113       addr6 = (struct sockaddr_in6 *)sa;
    114       port = addr6->sin6_port;
    115     }
    116   else
    117     {
    118       callback(arg, ARES_ENOTIMP, 0, NULL, NULL);
    119       return;
    120     }
    121 
    122   /* If neither, assume they want a host */
    123   if (!(flags & ARES_NI_LOOKUPSERVICE) && !(flags & ARES_NI_LOOKUPHOST))
    124     flags |= ARES_NI_LOOKUPHOST;
    125 
    126   /* All they want is a service, no need for DNS */
    127   if ((flags & ARES_NI_LOOKUPSERVICE) && !(flags & ARES_NI_LOOKUPHOST))
    128     {
    129       char buf[33], *service;
    130 
    131       service = lookup_service((unsigned short)(port & 0xffff),
    132                                flags, buf, sizeof(buf));
    133       callback(arg, ARES_SUCCESS, 0, NULL, service);
    134       return;
    135     }
    136 
    137   /* They want a host lookup */
    138   if ((flags & ARES_NI_LOOKUPHOST))
    139     {
    140      /* A numeric host can be handled without DNS */
    141      if ((flags & ARES_NI_NUMERICHOST))
    142       {
    143         char ipbuf[IPBUFSIZ];
    144         char srvbuf[33];
    145         char *service = NULL;
    146         ipbuf[0] = 0;
    147 
    148         /* Specifying not to lookup a host, but then saying a host
    149          * is required has to be illegal.
    150          */
    151         if (flags & ARES_NI_NAMEREQD)
    152           {
    153             callback(arg, ARES_EBADFLAGS, 0, NULL, NULL);
    154             return;
    155           }
    156         if (salen == sizeof(struct sockaddr_in6))
    157           {
    158             ares_inet_ntop(AF_INET6, &addr6->sin6_addr, ipbuf, IPBUFSIZ);
    159             /* If the system supports scope IDs, use it */
    160 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
    161             append_scopeid(addr6, flags, ipbuf, sizeof(ipbuf));
    162 #endif
    163           }
    164         else
    165           {
    166             ares_inet_ntop(AF_INET, &addr->sin_addr, ipbuf, IPBUFSIZ);
    167           }
    168         /* They also want a service */
    169         if (flags & ARES_NI_LOOKUPSERVICE)
    170           service = lookup_service((unsigned short)(port & 0xffff),
    171                                    flags, srvbuf, sizeof(srvbuf));
    172         callback(arg, ARES_SUCCESS, 0, ipbuf, service);
    173         return;
    174       }
    175     /* This is where a DNS lookup becomes necessary */
    176     else
    177       {
    178         niquery = malloc(sizeof(struct nameinfo_query));
    179         if (!niquery)
    180           {
    181             callback(arg, ARES_ENOMEM, 0, NULL, NULL);
    182             return;
    183           }
    184         niquery->callback = callback;
    185         niquery->arg = arg;
    186         niquery->flags = flags;
    187         niquery->timeouts = 0;
    188         if (sa->sa_family == AF_INET)
    189           {
    190             niquery->family = AF_INET;
    191             memcpy(&niquery->addr.addr4, addr, sizeof(struct in_addr));
    192             ares_gethostbyaddr(channel, &addr->sin_addr,
    193                                sizeof(struct in_addr), AF_INET,
    194                                nameinfo_callback, niquery);
    195           }
    196         else
    197           {
    198             niquery->family = AF_INET6;
    199             memcpy(&niquery->addr.addr6, addr6, sizeof(struct ares_in6_addr));
    200             ares_gethostbyaddr(channel, &addr6->sin6_addr,
    201                                sizeof(struct ares_in6_addr), AF_INET6,
    202                                nameinfo_callback, niquery);
    203           }
    204       }
    205     }
    206 }
    207 
    208 static void nameinfo_callback(void *arg, int status, int timeouts,
    209                               struct hostent *host)
    210 {
    211   struct nameinfo_query *niquery = (struct nameinfo_query *) arg;
    212   char srvbuf[33];
    213   char *service = NULL;
    214 
    215   niquery->timeouts += timeouts;
    216   if (status == ARES_SUCCESS)
    217     {
    218       /* They want a service too */
    219       if (niquery->flags & ARES_NI_LOOKUPSERVICE)
    220         {
    221           if (niquery->family == AF_INET)
    222             service = lookup_service(niquery->addr.addr4.sin_port,
    223                                      niquery->flags, srvbuf, sizeof(srvbuf));
    224           else
    225             service = lookup_service(niquery->addr.addr6.sin6_port,
    226                                      niquery->flags, srvbuf, sizeof(srvbuf));
    227         }
    228       /* NOFQDN means we have to strip off the domain name portion.  We do
    229          this by determining our own domain name, then searching the string
    230          for this domain name and removing it.
    231        */
    232 #ifdef HAVE_GETHOSTNAME
    233       if (niquery->flags & ARES_NI_NOFQDN)
    234         {
    235            char buf[255];
    236            char *domain;
    237            gethostname(buf, 255);
    238            if ((domain = strchr(buf, '.')) != NULL)
    239              {
    240                char *end = ares_striendstr(host->h_name, domain);
    241                if (end)
    242                  *end = 0;
    243              }
    244         }
    245 #endif
    246       niquery->callback(niquery->arg, ARES_SUCCESS, niquery->timeouts,
    247                         (char *)(host->h_name),
    248                         service);
    249       free(niquery);
    250       return;
    251     }
    252   /* We couldn't find the host, but it's OK, we can use the IP */
    253   else if (status == ARES_ENOTFOUND && !(niquery->flags & ARES_NI_NAMEREQD))
    254     {
    255       char ipbuf[IPBUFSIZ];
    256       if (niquery->family == AF_INET)
    257         ares_inet_ntop(AF_INET, &niquery->addr.addr4.sin_addr, ipbuf,
    258                        IPBUFSIZ);
    259       else
    260         {
    261           ares_inet_ntop(AF_INET6, &niquery->addr.addr6.sin6_addr, ipbuf,
    262                          IPBUFSIZ);
    263 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
    264           append_scopeid(&niquery->addr.addr6, niquery->flags, ipbuf,
    265                          sizeof(ipbuf));
    266 #endif
    267         }
    268       /* They want a service too */
    269       if (niquery->flags & ARES_NI_LOOKUPSERVICE)
    270         {
    271           if (niquery->family == AF_INET)
    272             service = lookup_service(niquery->addr.addr4.sin_port,
    273                                      niquery->flags, srvbuf, sizeof(srvbuf));
    274           else
    275             service = lookup_service(niquery->addr.addr6.sin6_port,
    276                                      niquery->flags, srvbuf, sizeof(srvbuf));
    277         }
    278       niquery->callback(niquery->arg, ARES_SUCCESS, niquery->timeouts, ipbuf,
    279                         service);
    280       free(niquery);
    281       return;
    282     }
    283   niquery->callback(niquery->arg, status, niquery->timeouts, NULL, NULL);
    284   free(niquery);
    285 }
    286 
    287 static char *lookup_service(unsigned short port, int flags,
    288                             char *buf, size_t buflen)
    289 {
    290   const char *proto;
    291   struct servent *sep;
    292 #ifdef HAVE_GETSERVBYPORT_R
    293   struct servent se;
    294 #endif
    295   char tmpbuf[4096];
    296 
    297   if (port)
    298     {
    299       if (flags & ARES_NI_NUMERICSERV)
    300         sep = NULL;
    301       else
    302         {
    303           if (flags & ARES_NI_UDP)
    304             proto = "udp";
    305           else if (flags & ARES_NI_SCTP)
    306             proto = "sctp";
    307           else if (flags & ARES_NI_DCCP)
    308             proto = "dccp";
    309           else
    310             proto = "tcp";
    311 #ifdef HAVE_GETSERVBYPORT_R
    312           sep = &se;
    313           memset(tmpbuf, 0, sizeof(tmpbuf));
    314 #if GETSERVBYPORT_R_ARGS == 6
    315           if (getservbyport_r(port, proto, &se, (void *)tmpbuf,
    316                               sizeof(tmpbuf), &sep) != 0)
    317             sep = NULL;
    318 #elif GETSERVBYPORT_R_ARGS == 5
    319           sep = getservbyport_r(port, proto, &se, (void *)tmpbuf,
    320                                 sizeof(tmpbuf));
    321 #elif GETSERVBYPORT_R_ARGS == 4
    322           if (getservbyport_r(port, proto, &se, (void *)tmpbuf) != 0)
    323             sep = NULL;
    324 #else
    325           /* Lets just hope the OS uses TLS! */
    326           sep = getservbyport(port, proto);
    327 #endif
    328 #else
    329           /* Lets just hope the OS uses TLS! */
    330 #if (defined(NETWARE) && !defined(__NOVELL_LIBC__))
    331           sep = getservbyport(port, (char*)proto);
    332 #else
    333           sep = getservbyport(port, proto);
    334 #endif
    335 #endif
    336         }
    337       if (sep && sep->s_name)
    338         /* get service name */
    339         strcpy(tmpbuf, sep->s_name);
    340       else
    341         /* get port as a string */
    342         sprintf(tmpbuf, "%u", (unsigned int)ntohs(port));
    343       if (strlen(tmpbuf) < buflen)
    344         /* return it if buffer big enough */
    345         strcpy(buf, tmpbuf);
    346       else
    347         /* avoid reusing previous one */
    348         buf[0] = '\0';
    349       return buf;
    350     }
    351   buf[0] = '\0';
    352   return NULL;
    353 }
    354 
    355 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
    356 static void append_scopeid(struct sockaddr_in6 *addr6, unsigned int flags,
    357                            char *buf, size_t buflen)
    358 {
    359 #ifdef HAVE_IF_INDEXTONAME
    360   int is_ll, is_mcll;
    361 #endif
    362   static const char fmt_u[] = "%u";
    363   static const char fmt_lu[] = "%lu";
    364   char tmpbuf[IF_NAMESIZE + 2];
    365   size_t bufl;
    366   const char *fmt = (sizeof(addr6->sin6_scope_id) > sizeof(unsigned int))?
    367     fmt_lu:fmt_u;
    368 
    369   tmpbuf[0] = '%';
    370 
    371 #ifdef HAVE_IF_INDEXTONAME
    372   is_ll = IN6_IS_ADDR_LINKLOCAL(&addr6->sin6_addr);
    373   is_mcll = IN6_IS_ADDR_MC_LINKLOCAL(&addr6->sin6_addr);
    374   if ((flags & ARES_NI_NUMERICSCOPE) ||
    375       (!is_ll && !is_mcll))
    376     {
    377        sprintf(&tmpbuf[1], fmt, addr6->sin6_scope_id);
    378     }
    379   else
    380     {
    381       if (if_indextoname(addr6->sin6_scope_id, &tmpbuf[1]) == NULL)
    382         sprintf(&tmpbuf[1], fmt, addr6->sin6_scope_id);
    383     }
    384 #else
    385   sprintf(&tmpbuf[1], fmt, addr6->sin6_scope_id);
    386   (void) flags;
    387 #endif
    388   tmpbuf[IF_NAMESIZE + 1] = '\0';
    389   bufl = strlen(buf);
    390 
    391   if(bufl + strlen(tmpbuf) < buflen)
    392     /* only append the scopeid string if it fits in the target buffer */
    393     strcpy(&buf[bufl], tmpbuf);
    394 }
    395 #endif
    396 
    397 /* Determines if s1 ends with the string in s2 (case-insensitive) */
    398 static char *ares_striendstr(const char *s1, const char *s2)
    399 {
    400   const char *c1, *c2, *c1_begin;
    401   int lo1, lo2;
    402   size_t s1_len = strlen(s1), s2_len = strlen(s2);
    403 
    404   /* If the substr is longer than the full str, it can't match */
    405   if (s2_len > s1_len)
    406     return NULL;
    407 
    408   /* Jump to the end of s1 minus the length of s2 */
    409   c1_begin = s1+s1_len-s2_len;
    410   c1 = (const char *)c1_begin;
    411   c2 = s2;
    412   while (c2 < s2+s2_len)
    413     {
    414       lo1 = TOLOWER(*c1);
    415       lo2 = TOLOWER(*c2);
    416       if (lo1 != lo2)
    417         return NULL;
    418       else
    419         {
    420           c1++;
    421           c2++;
    422         }
    423     }
    424   if (c2 == c1 && c2 == NULL)
    425     return (char *)c1_begin;
    426   return NULL;
    427 }
    428