host_resolver.cc revision ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16
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#include "nacl_io/host_resolver.h"
6
7#include <stdlib.h>
8#include <string.h>
9
10#include "nacl_io/kernel_proxy.h"
11#include "nacl_io/ossocket.h"
12#include "nacl_io/pepper_interface.h"
13
14#ifdef PROVIDES_SOCKET_API
15
16namespace nacl_io {
17
18HostResolver::HostResolver() : hostent_(), ppapi_(NULL) {
19}
20
21HostResolver::~HostResolver() {
22  hostent_cleanup();
23}
24
25void HostResolver::Init(PepperInterface* ppapi) {
26  ppapi_ = ppapi;
27}
28
29struct hostent* HostResolver::gethostbyname(const char* name) {
30  h_errno = NETDB_INTERNAL;
31
32  if (NULL == ppapi_)
33    return NULL;
34
35  HostResolverInterface* resolver_interface =
36    ppapi_->GetHostResolverInterface();
37  ScopedResource resolver(ppapi_,
38                          resolver_interface->Create(ppapi_->GetInstance()));
39
40  struct PP_CompletionCallback callback;
41  callback.func = NULL;
42  struct PP_HostResolver_Hint hint;
43  hint.family = PP_NETADDRESS_FAMILY_IPV4;
44  hint.flags = PP_HOSTRESOLVER_FLAG_CANONNAME;
45
46  int err = resolver_interface->Resolve(resolver.pp_resource(),
47                                        name, 0, &hint, callback);
48  if (err) {
49    switch (err) {
50    case PP_ERROR_NOACCESS:
51      h_errno = NO_RECOVERY;
52      break;
53    case PP_ERROR_NAME_NOT_RESOLVED:
54      h_errno = HOST_NOT_FOUND;
55      break;
56    default:
57      h_errno = NETDB_INTERNAL;
58      break;
59    }
60    return NULL;
61  }
62
63  // We use a single hostent struct for all calls to to gethostbyname
64  // (as explicitly permitted by the spec - gethostbyname is NOT supposed to
65  // be threadsafe!), so the first thing we do is free all the malloced data
66  // left over from the last call.
67  hostent_cleanup();
68
69  PP_Var name_var =
70    resolver_interface->GetCanonicalName(resolver.pp_resource());
71  if (PP_VARTYPE_STRING != name_var.type)
72    return NULL;
73
74  uint32_t len;
75  const char* name_ptr = ppapi_->GetVarInterface()->VarToUtf8(name_var, &len);
76  if (NULL == name_ptr)
77    return NULL;
78  if (0 == len) {
79    // Sometimes GetCanonicalName gives up more easily than gethostbyname should
80    // (for example, it returns "" when asked to resolve "localhost"), so if we
81    // get an empty string we copy over the input string instead.
82    len = strlen(name);
83    name_ptr = name;
84  }
85  hostent_.h_name = static_cast<char*>(malloc(len + 1));
86  if (NULL == hostent_.h_name)
87    return NULL;
88  memcpy(hostent_.h_name, name_ptr, len);
89  hostent_.h_name[len] = '\0';
90
91  // Aliases aren't supported at the moment, so we just make an empty list.
92  hostent_.h_aliases = static_cast<char**>(malloc(sizeof(char*)));
93  if (NULL == hostent_.h_aliases)
94    return NULL;
95  hostent_.h_aliases[0] = NULL;
96
97  NetAddressInterface* netaddr_interface = ppapi_->GetNetAddressInterface();
98  PP_Resource addr =
99      resolver_interface->GetNetAddress(resolver.pp_resource(), 0);
100  if (!PP_ToBool(netaddr_interface->IsNetAddress(addr)))
101    return NULL;
102
103  switch (netaddr_interface->GetFamily(addr)) {
104    case PP_NETADDRESS_FAMILY_IPV4:
105      hostent_.h_addrtype = AF_INET;
106      hostent_.h_length = 4;
107      break;
108    case PP_NETADDRESS_FAMILY_IPV6:
109      hostent_.h_addrtype = AF_INET6;
110      hostent_.h_length = 16;
111      break;
112    default:
113      return NULL;
114  }
115
116  const uint32_t num_addresses =
117    resolver_interface->GetNetAddressCount(resolver.pp_resource());
118  if (0 == num_addresses)
119    return NULL;
120  hostent_.h_addr_list =
121    static_cast<char**>(calloc(num_addresses + 1, sizeof(char*)));
122  if (NULL == hostent_.h_addr_list)
123    return NULL;
124
125  for (uint32_t i = 0; i < num_addresses; i++) {
126    PP_Resource addr =
127      resolver_interface->GetNetAddress(resolver.pp_resource(), i);
128    if (!PP_ToBool(netaddr_interface->IsNetAddress(addr)))
129      return NULL;
130    if (AF_INET == hostent_.h_addrtype) {
131      struct PP_NetAddress_IPv4 addr_struct;
132      if (!netaddr_interface->DescribeAsIPv4Address(addr, &addr_struct)) {
133        return NULL;
134      }
135      hostent_.h_addr_list[i] = static_cast<char*>(malloc(hostent_.h_length));
136      if (NULL == hostent_.h_addr_list[i])
137        return NULL;
138      memcpy(hostent_.h_addr_list[i], addr_struct.addr, hostent_.h_length);
139    } else { // IPv6
140      struct PP_NetAddress_IPv6 addr_struct;
141      if (!netaddr_interface->DescribeAsIPv6Address(addr, &addr_struct))
142        return NULL;
143      hostent_.h_addr_list[i] = static_cast<char*>(malloc(hostent_.h_length));
144      if (NULL == hostent_.h_addr_list[i])
145        return NULL;
146      memcpy(hostent_.h_addr_list[i], addr_struct.addr, hostent_.h_length);
147    }
148  }
149  return &hostent_;
150}
151
152// Frees all of the deep pointers in a hostent struct. Called between uses of
153// gethostbyname, and when the kernel_proxy object is destroyed.
154void HostResolver::hostent_cleanup() {
155  if (NULL != hostent_.h_name) {
156    free(hostent_.h_name);
157  }
158  if (NULL != hostent_.h_aliases) {
159    for (int i = 0;  NULL != hostent_.h_aliases[i]; i++) {
160      free(hostent_.h_aliases[i]);
161    }
162    free(hostent_.h_aliases);
163  }
164  if (NULL != hostent_.h_addr_list) {
165    for (int i = 0;  NULL != hostent_.h_addr_list[i]; i++) {
166      free(hostent_.h_addr_list[i]);
167    }
168    free(hostent_.h_addr_list);
169  }
170  hostent_.h_name = NULL;
171  hostent_.h_aliases = NULL;
172  hostent_.h_addr_list = NULL;
173}
174
175}  // namespace nacl_io
176
177#endif  // PROVIDES_SOCKET_API
178