blob: f059f9a9aea81800ed95855f1ee592325ec18cbc [file] [log] [blame]
Ben Murdochbb1529c2013-08-08 10:24:53 +01001// 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 <stdio.h>
8#include <stdlib.h>
9#include <string.h>
10
11#include <sstream>
12#include <string>
13
14#include "nacl_io/kernel_proxy.h"
15#include "nacl_io/ossocket.h"
16#include "nacl_io/pepper_interface.h"
17
18#ifdef PROVIDES_SOCKET_API
19
20namespace nacl_io {
21
22HostResolver::HostResolver() : hostent_(), ppapi_(NULL) {
23}
24
25HostResolver::~HostResolver() {
26 hostent_cleanup();
27}
28
29void HostResolver::Init(PepperInterface* ppapi) {
30 ppapi_ = ppapi;
31}
32
33struct hostent* HostResolver::gethostbyname(const char* name) {
34 h_errno = NETDB_INTERNAL;
35
36 if (NULL == ppapi_)
37 return NULL;
38
39 HostResolverInterface* resolver_interface =
40 ppapi_->GetHostResolverInterface();
41 ScopedResource resolver(ppapi_,
42 resolver_interface->Create(ppapi_->GetInstance()));
43
44 struct PP_CompletionCallback callback;
45 callback.func = NULL;
46 struct PP_HostResolver_Hint hint;
47 hint.family = PP_NETADDRESS_FAMILY_IPV4;
48 hint.flags = PP_HOSTRESOLVER_FLAG_CANONNAME;
49
50 int err = resolver_interface->Resolve(resolver.pp_resource(),
51 name, 0, &hint, callback);
52 if (err) {
53 switch (err) {
54 case PP_ERROR_NOACCESS:
55 h_errno = NO_RECOVERY;
56 break;
57 case PP_ERROR_NAME_NOT_RESOLVED:
58 h_errno = HOST_NOT_FOUND;
59 break;
60 default:
61 h_errno = NETDB_INTERNAL;
62 break;
63 }
64 return NULL;
65 }
66
67 // We use a single hostent struct for all calls to to gethostbyname
68 // (as explicitly permitted by the spec - gethostbyname is NOT supposed to
69 // be threadsafe!), so the first thing we do is free all the malloced data
70 // left over from the last call.
71 hostent_cleanup();
72
73 PP_Var name_var =
74 resolver_interface->GetCanonicalName(resolver.pp_resource());
75 if (PP_VARTYPE_STRING != name_var.type)
76 return NULL;
77
78 uint32_t len;
79 const char* name_ptr = ppapi_->GetVarInterface()->VarToUtf8(name_var, &len);
80 if (NULL == name_ptr)
81 return NULL;
82 if (0 == len) {
83 // Sometimes GetCanonicalName gives up more easily than gethostbyname should
84 // (for example, it returns "" when asked to resolve "localhost"), so if we
85 // get an empty string we copy over the input string instead.
86 len = strlen(name);
87 name_ptr = name;
88 }
89 hostent_.h_name = static_cast<char*>(malloc(len + 1));
90 if (NULL == hostent_.h_name)
91 return NULL;
92 memcpy(hostent_.h_name, name_ptr, len);
93 hostent_.h_name[len] = '\0';
94
95 // Aliases aren't supported at the moment, so we just make an empty list.
96 hostent_.h_aliases = static_cast<char**>(malloc(sizeof(char*)));
97 if (NULL == hostent_.h_aliases)
98 return NULL;
99 hostent_.h_aliases[0] = NULL;
100
101 NetAddressInterface* netaddr_interface = ppapi_->GetNetAddressInterface();
102 PP_Resource addr =
103 resolver_interface->GetNetAddress(resolver.pp_resource(), 0);
104 if (!PP_ToBool(netaddr_interface->IsNetAddress(addr)))
105 return NULL;
106
107 switch (netaddr_interface->GetFamily(addr)) {
108 case PP_NETADDRESS_FAMILY_IPV4:
109 hostent_.h_addrtype = AF_INET;
110 hostent_.h_length = 4;
111 break;
112 case PP_NETADDRESS_FAMILY_IPV6:
113 hostent_.h_addrtype = AF_INET6;
114 hostent_.h_length = 16;
115 break;
116 default:
117 return NULL;
118 }
119
120 const uint32_t num_addresses =
121 resolver_interface->GetNetAddressCount(resolver.pp_resource());
122 if (0 == num_addresses)
123 return NULL;
124 hostent_.h_addr_list =
125 static_cast<char**>(calloc(num_addresses + 1, sizeof(char*)));
126 if (NULL == hostent_.h_addr_list)
127 return NULL;
128
129 for (uint32_t i = 0; i < num_addresses; i++) {
130 PP_Resource addr =
131 resolver_interface->GetNetAddress(resolver.pp_resource(), i);
132 if (!PP_ToBool(netaddr_interface->IsNetAddress(addr)))
133 return NULL;
134 if (AF_INET == hostent_.h_addrtype) {
135 struct PP_NetAddress_IPv4 addr_struct;
136 if (!netaddr_interface->DescribeAsIPv4Address(addr, &addr_struct)) {
137 return NULL;
138 }
139 hostent_.h_addr_list[i] = static_cast<char*>(malloc(hostent_.h_length));
140 if (NULL == hostent_.h_addr_list[i])
141 return NULL;
142 memcpy(hostent_.h_addr_list[i], addr_struct.addr, hostent_.h_length);
143 } else { // IPv6
144 struct PP_NetAddress_IPv6 addr_struct;
145 if (!netaddr_interface->DescribeAsIPv6Address(addr, &addr_struct))
146 return NULL;
147 hostent_.h_addr_list[i] = static_cast<char*>(malloc(hostent_.h_length));
148 if (NULL == hostent_.h_addr_list[i])
149 return NULL;
150 memcpy(hostent_.h_addr_list[i], addr_struct.addr, hostent_.h_length);
151 }
152 }
153 return &hostent_;
154}
155
156// Frees all of the deep pointers in a hostent struct. Called between uses of
157// gethostbyname, and when the kernel_proxy object is destroyed.
158void HostResolver::hostent_cleanup() {
159 if (NULL != hostent_.h_name) {
160 free(hostent_.h_name);
161 }
162 if (NULL != hostent_.h_aliases) {
163 for (int i = 0; NULL != hostent_.h_aliases[i]; i++) {
164 free(hostent_.h_aliases[i]);
165 }
166 free(hostent_.h_aliases);
167 }
168 if (NULL != hostent_.h_addr_list) {
169 for (int i = 0; NULL != hostent_.h_addr_list[i]; i++) {
170 free(hostent_.h_addr_list[i]);
171 }
172 free(hostent_.h_addr_list);
173 }
174 hostent_.h_name = NULL;
175 hostent_.h_aliases = NULL;
176 hostent_.h_addr_list = NULL;
177}
178
179void HostResolver::herror(const char* s) {
180 if (s) {
181 fprintf(stderr, "%s: ", s);
182 }
183
184 fprintf(stderr, "%s\n", hstrerror(h_errno));
185}
186
187const char* HostResolver::hstrerror(int err) {
188 // These error message texts are taken straight from the man page
189 const char* host_not_found_msg =
190 "The specified host is unknown.";
191 const char* no_address_msg =
192 "The requested name is valid but does not have an IP address.";
193 const char* no_recovery_msg =
194 "A nonrecoverable name server error occurred.";
195 const char* try_again_msg =
196 "A temporary error occurred on an authoritative name server. "
197 "Try again later.";
198 const char* internal_msg =
199 "Internal error in gethostbyname.";
200 const char* unknown_msg_base =
201 "Unknown error in gethostbyname: ";
202
203 switch (err) {
204 case HOST_NOT_FOUND:
205 return host_not_found_msg;
206 case NO_ADDRESS:
207 return no_address_msg;
208 case NO_RECOVERY:
209 return no_recovery_msg;
210 case TRY_AGAIN:
211 return try_again_msg;
212 case NETDB_INTERNAL:
213 return internal_msg;
214 default:
215 std::stringstream msg;
216 msg << unknown_msg_base << err << ".";
217
218 static std::string unknown_msg;
219 unknown_msg.assign(msg.str());
220 return unknown_msg.c_str();
221 }
222}
223
224} // namespace nacl_io
225
226#endif // PROVIDES_SOCKET_API