Home | History | Annotate | Download | only in nacl_io
      1 // Copyright 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #ifndef __STDC_LIMIT_MACROS
      6 #define __STDC_LIMIT_MACROS
      7 #endif
      8 
      9 #include "nacl_io/host_resolver.h"
     10 
     11 #include <assert.h>
     12 #include <stdint.h>
     13 #include <stdlib.h>
     14 #include <string.h>
     15 
     16 #include "nacl_io/kernel_proxy.h"
     17 #include "nacl_io/log.h"
     18 #include "nacl_io/ossocket.h"
     19 #include "nacl_io/pepper_interface.h"
     20 
     21 #ifdef PROVIDES_SOCKET_API
     22 
     23 namespace {
     24 
     25 void HintsToPPHints(const addrinfo* hints, PP_HostResolver_Hint* pp_hints) {
     26   memset(pp_hints, 0, sizeof(*pp_hints));
     27 
     28   if (hints->ai_family == AF_INET)
     29     pp_hints->family = PP_NETADDRESS_FAMILY_IPV4;
     30   else if (hints->ai_family == AF_INET6)
     31     pp_hints->family = PP_NETADDRESS_FAMILY_IPV6;
     32 
     33   if (hints->ai_flags & AI_CANONNAME)
     34     pp_hints->flags = PP_HOSTRESOLVER_FLAG_CANONNAME;
     35 }
     36 
     37 void CreateAddrInfo(const addrinfo* hints,
     38                     struct sockaddr* addr,
     39                     const char* name,
     40                     addrinfo** list_start,
     41                     addrinfo** list_end) {
     42   addrinfo* ai = static_cast<addrinfo*>(malloc(sizeof(addrinfo)));
     43   memset(ai, 0, sizeof(*ai));
     44 
     45   if (hints && hints->ai_socktype)
     46     ai->ai_socktype = hints->ai_socktype;
     47   else
     48     ai->ai_socktype = SOCK_STREAM;
     49 
     50   if (hints && hints->ai_protocol)
     51     ai->ai_protocol = hints->ai_protocol;
     52 
     53   if (name)
     54     ai->ai_canonname = strdup(name);
     55 
     56   switch (addr->sa_family) {
     57     case AF_INET6: {
     58       sockaddr_in6* in =
     59           static_cast<sockaddr_in6*>(malloc(sizeof(sockaddr_in6)));
     60       *in = *(sockaddr_in6*)addr;
     61       ai->ai_family = AF_INET6;
     62       ai->ai_addr = reinterpret_cast<sockaddr*>(in);
     63       ai->ai_addrlen = sizeof(*in);
     64       break;
     65     }
     66     case AF_INET: {
     67       sockaddr_in* in = static_cast<sockaddr_in*>(malloc(sizeof(sockaddr_in)));
     68       *in = *(sockaddr_in*)addr;
     69       ai->ai_family = AF_INET;
     70       ai->ai_addr = reinterpret_cast<sockaddr*>(in);
     71       ai->ai_addrlen = sizeof(*in);
     72       break;
     73     }
     74     default:
     75       assert(0);
     76       return;
     77   }
     78 
     79   if (*list_start == NULL) {
     80     *list_start = ai;
     81     *list_end = ai;
     82     return;
     83   }
     84 
     85   (*list_end)->ai_next = ai;
     86   *list_end = ai;
     87 }
     88 
     89 }  // namespace
     90 
     91 namespace nacl_io {
     92 
     93 HostResolver::HostResolver() : hostent_(), ppapi_(NULL) {
     94 }
     95 
     96 HostResolver::~HostResolver() {
     97   hostent_cleanup();
     98 }
     99 
    100 void HostResolver::Init(PepperInterface* ppapi) {
    101   ppapi_ = ppapi;
    102 }
    103 
    104 struct hostent* HostResolver::gethostbyname(const char* name) {
    105   h_errno = NETDB_INTERNAL;
    106 
    107   struct addrinfo* ai;
    108   struct addrinfo hints;
    109   memset(&hints, 0, sizeof(hints));
    110   hints.ai_flags = AI_CANONNAME;
    111   hints.ai_family = AF_INET;
    112   int err = getaddrinfo(name, NULL, &hints, &ai);
    113   if (err) {
    114     switch (err) {
    115       case EAI_SYSTEM:
    116         h_errno = NO_RECOVERY;
    117         break;
    118       case EAI_NONAME:
    119         h_errno = HOST_NOT_FOUND;
    120         break;
    121       default:
    122         h_errno = NETDB_INTERNAL;
    123         break;
    124     }
    125     return NULL;
    126   }
    127 
    128   // We use a single hostent struct for all calls to to gethostbyname
    129   // (as explicitly permitted by the spec - gethostbyname is NOT supposed to
    130   // be threadsafe!).  However by using a lock around all the global data
    131   // manipulation we can at least ensure that the call doesn't crash.
    132   AUTO_LOCK(gethostbyname_lock_);
    133 
    134   // The first thing we do is free any malloced data left over from
    135   // the last call.
    136   hostent_cleanup();
    137 
    138   switch (ai->ai_family) {
    139     case AF_INET:
    140       hostent_.h_addrtype = AF_INET;
    141       hostent_.h_length = sizeof(in_addr);
    142       break;
    143     case AF_INET6:
    144       hostent_.h_addrtype = AF_INET6;
    145       hostent_.h_length = sizeof(in6_addr);
    146       break;
    147     default:
    148       return NULL;
    149   }
    150 
    151   if (ai->ai_canonname != NULL)
    152     hostent_.h_name = strdup(ai->ai_canonname);
    153   else
    154     hostent_.h_name = strdup(name);
    155 
    156   // Aliases aren't supported at the moment, so we just make an empty list.
    157   hostent_.h_aliases = static_cast<char**>(malloc(sizeof(char*)));
    158   if (NULL == hostent_.h_aliases)
    159     return NULL;
    160   hostent_.h_aliases[0] = NULL;
    161 
    162   // Count number of address in list
    163   int num_addresses = 0;
    164   struct addrinfo* current = ai;
    165   while (current != NULL) {
    166     // Only count address that have the same type as first address
    167     if (current->ai_family == hostent_.h_addrtype)
    168       num_addresses++;
    169     current = current->ai_next;
    170   }
    171 
    172   // Allocate address list
    173   hostent_.h_addr_list = static_cast<char**>(calloc(num_addresses + 1,
    174                                                     sizeof(char*)));
    175   if (NULL == hostent_.h_addr_list)
    176     return NULL;
    177 
    178   // Copy all addresses of the relevant family.
    179   current = ai;
    180   char** hostent_addr = hostent_.h_addr_list;
    181   while (current != NULL) {
    182     if (current->ai_family != hostent_.h_addrtype) {
    183       current = current->ai_next;
    184       continue;
    185     }
    186     *hostent_addr = static_cast<char*>(malloc(hostent_.h_length));
    187     switch (current->ai_family) {
    188       case AF_INET: {
    189         sockaddr_in* in = reinterpret_cast<sockaddr_in*>(current->ai_addr);
    190         memcpy(*hostent_addr, &in->sin_addr.s_addr, hostent_.h_length);
    191         break;
    192       }
    193       case AF_INET6: {
    194         sockaddr_in6* in6 = reinterpret_cast<sockaddr_in6*>(current->ai_addr);
    195         memcpy(*hostent_addr, &in6->sin6_addr.s6_addr, hostent_.h_length);
    196         break;
    197       }
    198     }
    199     current = current->ai_next;
    200     hostent_addr++;
    201   }
    202 
    203   freeaddrinfo(ai);
    204 
    205 #if !defined(h_addr)
    206   // Copy element zero of h_addr_list to h_addr when h_addr is not defined
    207   // as in some libc's h_addr may be a separate member instead of a macro.
    208   hostent_.h_addr = hostent_.h_addr_list[0];
    209 #endif
    210 
    211   return &hostent_;
    212 }
    213 
    214 void HostResolver::freeaddrinfo(struct addrinfo* res) {
    215   while (res) {
    216     struct addrinfo* cur = res;
    217     res = res->ai_next;
    218     free(cur->ai_addr);
    219     free(cur->ai_canonname);
    220     free(cur);
    221   }
    222 }
    223 
    224 int HostResolver::getnameinfo(const struct sockaddr *sa,
    225                               socklen_t salen,
    226                               char *host,
    227                               size_t hostlen,
    228                               char *serv,
    229                               size_t servlen,
    230                               int flags) {
    231   return ENOSYS;
    232 }
    233 
    234 int HostResolver::getaddrinfo(const char* node,
    235                               const char* service,
    236                               const struct addrinfo* hints_in,
    237                               struct addrinfo** result) {
    238   *result = NULL;
    239   struct addrinfo* end = NULL;
    240 
    241   if (node == NULL && service == NULL) {
    242     LOG_TRACE("node and service are NULL.");
    243     return EAI_NONAME;
    244   }
    245 
    246   // Check the service name (port).  Currently we only handle numeric
    247   // services.
    248   long port = 0;
    249   if (service != NULL) {
    250     char* cp;
    251     port = strtol(service, &cp, 10);
    252     if (port >= 0 && port <= UINT16_MAX && *cp == '\0') {
    253       port = htons(port);
    254     } else {
    255       LOG_TRACE("Service \"%s\" not supported.", service);
    256       return EAI_SERVICE;
    257     }
    258   }
    259 
    260   struct addrinfo default_hints;
    261   memset(&default_hints, 0, sizeof(default_hints));
    262   const struct addrinfo* hints = hints_in ? hints_in : &default_hints;
    263 
    264   // Verify values passed in hints structure
    265   switch (hints->ai_family) {
    266     case AF_INET6:
    267     case AF_INET:
    268     case AF_UNSPEC:
    269       break;
    270     default:
    271       LOG_TRACE("Unknown family: %d.", hints->ai_family);
    272       return EAI_FAMILY;
    273   }
    274 
    275   struct sockaddr_in addr_in;
    276   memset(&addr_in, 0, sizeof(addr_in));
    277   addr_in.sin_family = AF_INET;
    278   addr_in.sin_port = port;
    279 
    280   struct sockaddr_in6 addr_in6;
    281   memset(&addr_in6, 0, sizeof(addr_in6));
    282   addr_in6.sin6_family = AF_INET6;
    283   addr_in6.sin6_port = port;
    284 
    285   if (node) {
    286     // Handle numeric node name.
    287     if (hints->ai_family == AF_INET || hints->ai_family == AF_UNSPEC) {
    288       in_addr in;
    289       if (inet_pton(AF_INET, node, &in)) {
    290         addr_in.sin_addr = in;
    291         CreateAddrInfo(hints, (sockaddr*)&addr_in, node, result, &end);
    292         return 0;
    293       }
    294     }
    295 
    296     if (hints->ai_family == AF_INET6 || hints->ai_family == AF_UNSPEC) {
    297       in6_addr in6;
    298       if (inet_pton(AF_INET6, node, &in6)) {
    299         addr_in6.sin6_addr = in6;
    300         CreateAddrInfo(hints, (sockaddr*)&addr_in6, node, result, &end);
    301         return 0;
    302       }
    303     }
    304   }
    305 
    306   // Handle AI_PASSIVE (used for listening sockets, e.g. INADDR_ANY)
    307   if (node == NULL && (hints->ai_flags & AI_PASSIVE)) {
    308     if (hints->ai_family == AF_INET6 || hints->ai_family == AF_UNSPEC) {
    309       const in6_addr in6addr_any = IN6ADDR_ANY_INIT;
    310       memcpy(&addr_in6.sin6_addr.s6_addr, &in6addr_any, sizeof(in6addr_any));
    311       CreateAddrInfo(hints, (sockaddr*)&addr_in6, NULL, result, &end);
    312     }
    313 
    314     if (hints->ai_family == AF_INET || hints->ai_family == AF_UNSPEC) {
    315       addr_in.sin_addr.s_addr = INADDR_ANY;
    316       CreateAddrInfo(hints, (sockaddr*)&addr_in, NULL, result, &end);
    317     }
    318     return 0;
    319   }
    320 
    321   if (NULL == ppapi_) {
    322     LOG_ERROR("ppapi_ is NULL.");
    323     return EAI_SYSTEM;
    324   }
    325 
    326   // Use PPAPI interface to resolve nodename
    327   HostResolverInterface* resolver_iface = ppapi_->GetHostResolverInterface();
    328   VarInterface* var_iface = ppapi_->GetVarInterface();
    329   NetAddressInterface* netaddr_iface = ppapi_->GetNetAddressInterface();
    330 
    331   if (!(resolver_iface && var_iface && netaddr_iface)) {
    332     LOG_ERROR("Got NULL interface(s): %s%s%s",
    333               resolver_iface ? "" : "HostResolver ",
    334               var_iface ? "" : "Var ",
    335               netaddr_iface ? "" : "NetAddress");
    336     return EAI_SYSTEM;
    337   }
    338 
    339   ScopedResource scoped_resolver(ppapi_,
    340                                  resolver_iface->Create(ppapi_->GetInstance()));
    341   PP_Resource resolver = scoped_resolver.pp_resource();
    342 
    343   struct PP_HostResolver_Hint pp_hints;
    344   HintsToPPHints(hints, &pp_hints);
    345 
    346   int err = resolver_iface->Resolve(resolver,
    347                                     node,
    348                                     0,
    349                                     &pp_hints,
    350                                     PP_BlockUntilComplete());
    351   if (err) {
    352     switch (err) {
    353       case PP_ERROR_NOACCESS:
    354         return EAI_SYSTEM;
    355       case PP_ERROR_NAME_NOT_RESOLVED:
    356         return EAI_NONAME;
    357       default:
    358         return EAI_SYSTEM;
    359     }
    360   }
    361 
    362   char* canon_name = NULL;
    363   if (hints->ai_flags & AI_CANONNAME) {
    364     PP_Var name_var = resolver_iface->GetCanonicalName(resolver);
    365     if (PP_VARTYPE_STRING == name_var.type) {
    366       uint32_t len = 0;
    367       const char* tmp = var_iface->VarToUtf8(name_var, &len);
    368       // For some reason GetCanonicalName alway returns an empty
    369       // string so this condition is never true.
    370       // TODO(sbc): investigate this issue with PPAPI team.
    371       if (len > 0) {
    372         // Copy and NULL-terminate the UTF8 string var.
    373         canon_name = static_cast<char*>(malloc(len + 1));
    374         strncpy(canon_name, tmp, len);
    375         canon_name[len] = '\0';
    376       }
    377     }
    378     if (!canon_name)
    379       canon_name = strdup(node);
    380     var_iface->Release(name_var);
    381   }
    382 
    383   int num_addresses = resolver_iface->GetNetAddressCount(resolver);
    384   if (0 == num_addresses)
    385     return EAI_NODATA;
    386 
    387   // Convert address to sockaddr struct.
    388   for (int i = 0; i < num_addresses; i++) {
    389     ScopedResource addr(ppapi_, resolver_iface->GetNetAddress(resolver, i));
    390     PP_Resource resource = addr.pp_resource();
    391     assert(resource != 0);
    392     assert(PP_ToBool(netaddr_iface->IsNetAddress(resource)));
    393     struct sockaddr* sockaddr = NULL;
    394     switch (netaddr_iface->GetFamily(resource)) {
    395       case PP_NETADDRESS_FAMILY_IPV4: {
    396         struct PP_NetAddress_IPv4 pp_addr;
    397         if (!netaddr_iface->DescribeAsIPv4Address(resource, &pp_addr)) {
    398           assert(false);
    399           break;
    400         }
    401         memcpy(&addr_in.sin_addr.s_addr, pp_addr.addr, sizeof(in_addr_t));
    402         sockaddr = (struct sockaddr*)&addr_in;
    403         break;
    404       }
    405       case PP_NETADDRESS_FAMILY_IPV6: {
    406         struct PP_NetAddress_IPv6 pp_addr;
    407         if (!netaddr_iface->DescribeAsIPv6Address(resource, &pp_addr)) {
    408           assert(false);
    409           break;
    410         }
    411         memcpy(&addr_in6.sin6_addr.s6_addr, pp_addr.addr, sizeof(in6_addr));
    412         sockaddr = (struct sockaddr*)&addr_in6;
    413         break;
    414       }
    415       default:
    416         return EAI_SYSTEM;
    417     }
    418 
    419     if (sockaddr != NULL)
    420       CreateAddrInfo(hints, sockaddr, canon_name, result, &end);
    421 
    422     if (canon_name) {
    423       free(canon_name);
    424       canon_name = NULL;
    425     }
    426   }
    427 
    428   return 0;
    429 }
    430 
    431 // Frees all of the deep pointers in a hostent struct. Called between uses of
    432 // gethostbyname, and when the kernel_proxy object is destroyed.
    433 void HostResolver::hostent_cleanup() {
    434   if (NULL != hostent_.h_name) {
    435     free(hostent_.h_name);
    436   }
    437   if (NULL != hostent_.h_aliases) {
    438     for (int i = 0; NULL != hostent_.h_aliases[i]; i++) {
    439       free(hostent_.h_aliases[i]);
    440     }
    441     free(hostent_.h_aliases);
    442   }
    443   if (NULL != hostent_.h_addr_list) {
    444     for (int i = 0; NULL != hostent_.h_addr_list[i]; i++) {
    445       free(hostent_.h_addr_list[i]);
    446     }
    447     free(hostent_.h_addr_list);
    448   }
    449   hostent_.h_name = NULL;
    450   hostent_.h_aliases = NULL;
    451   hostent_.h_addr_list = NULL;
    452 #if !defined(h_addr)
    453   // Initialize h_addr separately in the case where it is not a macro.
    454   hostent_.h_addr = NULL;
    455 #endif
    456 }
    457 
    458 }  // namespace nacl_io
    459 
    460 #endif  // PROVIDES_SOCKET_API
    461