host_resolver.cc revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
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
23namespace {
24
25void 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
37void 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
91namespace nacl_io {
92
93HostResolver::HostResolver() : hostent_(), ppapi_(NULL) {
94}
95
96HostResolver::~HostResolver() {
97  hostent_cleanup();
98}
99
100void HostResolver::Init(PepperInterface* ppapi) {
101  ppapi_ = ppapi;
102}
103
104struct 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
214void 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
224int 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
234int 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.
433void 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