Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / native_client_sdk / src / libraries / nacl_io / host_resolver.cc
blob4dd50acfa26143fcfb47320c9b2883453774643c
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.
5 #ifndef __STDC_LIMIT_MACROS
6 #define __STDC_LIMIT_MACROS
7 #endif
9 #include "nacl_io/host_resolver.h"
11 #include <assert.h>
12 #include <stdint.h>
13 #include <stdlib.h>
14 #include <string.h>
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"
21 #ifdef PROVIDES_SOCKET_API
23 namespace {
25 void HintsToPPHints(const addrinfo* hints, PP_HostResolver_Hint* pp_hints) {
26 memset(pp_hints, 0, sizeof(*pp_hints));
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;
33 if (hints->ai_flags & AI_CANONNAME)
34 pp_hints->flags = PP_HOSTRESOLVER_FLAG_CANONNAME;
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));
45 if (hints && hints->ai_socktype)
46 ai->ai_socktype = hints->ai_socktype;
47 else
48 ai->ai_socktype = SOCK_STREAM;
50 if (hints && hints->ai_protocol)
51 ai->ai_protocol = hints->ai_protocol;
53 if (name)
54 ai->ai_canonname = strdup(name);
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;
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;
74 default:
75 assert(0);
76 return;
79 if (*list_start == NULL) {
80 *list_start = ai;
81 *list_end = ai;
82 return;
85 (*list_end)->ai_next = ai;
86 *list_end = ai;
89 } // namespace
91 namespace nacl_io {
93 HostResolver::HostResolver() : hostent_(), ppapi_(NULL) {
96 HostResolver::~HostResolver() {
97 hostent_cleanup();
100 void HostResolver::Init(PepperInterface* ppapi) {
101 ppapi_ = ppapi;
104 struct hostent* HostResolver::gethostbyname(const char* name) {
105 h_errno = NETDB_INTERNAL;
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;
125 return NULL;
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_);
134 // The first thing we do is free any malloced data left over from
135 // the last call.
136 hostent_cleanup();
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;
151 if (ai->ai_canonname != NULL)
152 hostent_.h_name = strdup(ai->ai_canonname);
153 else
154 hostent_.h_name = strdup(name);
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;
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;
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;
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;
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;
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;
199 current = current->ai_next;
200 hostent_addr++;
203 freeaddrinfo(ai);
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
211 return &hostent_;
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);
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;
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;
241 if (node == NULL && service == NULL) {
242 LOG_TRACE("node and service are NULL.");
243 return EAI_NONAME;
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;
260 struct addrinfo default_hints;
261 memset(&default_hints, 0, sizeof(default_hints));
262 const struct addrinfo* hints = hints_in ? hints_in : &default_hints;
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;
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;
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;
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;
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;
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);
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);
318 return 0;
321 if (NULL == ppapi_) {
322 LOG_ERROR("ppapi_ is NULL.");
323 return EAI_SYSTEM;
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();
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;
339 ScopedResource scoped_resolver(ppapi_,
340 resolver_iface->Create(ppapi_->GetInstance()));
341 PP_Resource resolver = scoped_resolver.pp_resource();
343 struct PP_HostResolver_Hint pp_hints;
344 HintsToPPHints(hints, &pp_hints);
346 int err = resolver_iface->Resolve(resolver,
347 node,
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;
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';
378 if (!canon_name)
379 canon_name = strdup(node);
380 var_iface->Release(name_var);
383 int num_addresses = resolver_iface->GetNetAddressCount(resolver);
384 if (0 == num_addresses)
385 return EAI_NODATA;
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;
401 memcpy(&addr_in.sin_addr.s_addr, pp_addr.addr, sizeof(in_addr_t));
402 sockaddr = (struct sockaddr*)&addr_in;
403 break;
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;
411 memcpy(&addr_in6.sin6_addr.s6_addr, pp_addr.addr, sizeof(in6_addr));
412 sockaddr = (struct sockaddr*)&addr_in6;
413 break;
415 default:
416 return EAI_SYSTEM;
419 if (sockaddr != NULL)
420 CreateAddrInfo(hints, sockaddr, canon_name, result, &end);
422 if (canon_name) {
423 free(canon_name);
424 canon_name = NULL;
428 return 0;
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);
437 if (NULL != hostent_.h_aliases) {
438 for (int i = 0; NULL != hostent_.h_aliases[i]; i++) {
439 free(hostent_.h_aliases[i]);
441 free(hostent_.h_aliases);
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]);
447 free(hostent_.h_addr_list);
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
458 } // namespace nacl_io
460 #endif // PROVIDES_SOCKET_API