Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(79)

Side by Side Diff: native_client_sdk/src/libraries/nacl_io/host_resolver.cc

Issue 99933002: [NaCl SDK] nacl_io: implement getaddrinfo() (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 6 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "nacl_io/host_resolver.h" 5 #include "nacl_io/host_resolver.h"
6 6
7 #include <assert.h>
7 #include <stdlib.h> 8 #include <stdlib.h>
8 #include <string.h> 9 #include <string.h>
9 10
10 #include "nacl_io/kernel_proxy.h" 11 #include "nacl_io/kernel_proxy.h"
11 #include "nacl_io/ossocket.h" 12 #include "nacl_io/ossocket.h"
12 #include "nacl_io/pepper_interface.h" 13 #include "nacl_io/pepper_interface.h"
13 14
14 #ifdef PROVIDES_SOCKET_API 15 #ifdef PROVIDES_SOCKET_API
15 16
17 namespace {
18
19 void HintsToPPHints(const addrinfo* hints, PP_HostResolver_Hint* pp_hints) {
20 memset(pp_hints, 0, sizeof(*pp_hints));
21
22 if (hints->ai_family == AF_INET)
23 pp_hints->family = PP_NETADDRESS_FAMILY_IPV4;
24 else if (hints->ai_family == AF_INET6)
25 pp_hints->family = PP_NETADDRESS_FAMILY_IPV6;
binji 2014/01/29 19:18:52 default value for else?
Sam Clegg 2014/01/29 22:06:13 Default value is UNSPECIFIED which is 0.
26
27 if (hints->ai_flags & AI_CANONNAME)
28 pp_hints->flags = PP_HOSTRESOLVER_FLAG_CANONNAME;
29 }
30
31 void CreateAddrInfo(const addrinfo* hints,
32 struct sockaddr* addr,
33 const char* name,
34 addrinfo** list_start,
35 addrinfo** list_end) {
36 addrinfo* ai = new addrinfo();
37 memset(ai, 0, sizeof(*ai));
binji 2014/01/29 19:18:52 Technically this is unnecessary because addrinfo i
Sam Clegg 2014/01/29 22:06:13 Very interesting. I removed the parens and kept t
38
39 if (hints && hints->ai_socktype)
40 ai->ai_socktype = hints->ai_socktype;
41 else
42 ai->ai_socktype = SOCK_STREAM;
43
44 if (hints && hints->ai_protocol)
45 ai->ai_protocol = hints->ai_protocol;
46
47 if (name)
48 ai->ai_canonname = strdup(name);
49
50 if (addr->sa_family == AF_INET6) {
51 sockaddr_in6* in = new sockaddr_in6();
52 *in = *(sockaddr_in6*)addr;
53 ai->ai_family = AF_INET6;
54 ai->ai_addr = reinterpret_cast<sockaddr*>(in);
55 ai->ai_addrlen = sizeof(*in);
56 } else if (addr->sa_family == AF_INET) {
57 sockaddr_in* in = new sockaddr_in();
58 *in = *(sockaddr_in*)addr;
59 ai->ai_family = AF_INET;
60 ai->ai_addr = reinterpret_cast<sockaddr*>(in);
61 ai->ai_addrlen = sizeof(*in);
62 }
binji 2014/01/29 19:18:52 default case?
Sam Clegg 2014/01/29 22:06:13 We only support these to families. I've added an
63
64 if (*list_start == NULL) {
65 *list_start = ai;
66 *list_end = ai;
67 return;
68 }
69
70 (*list_end)->ai_next = ai;
71 *list_end = ai;
72 }
73
74 } // namespace
75
16 namespace nacl_io { 76 namespace nacl_io {
17 77
18 HostResolver::HostResolver() : hostent_(), ppapi_(NULL) { 78 HostResolver::HostResolver() : hostent_(), ppapi_(NULL) {
19 } 79 }
20 80
21 HostResolver::~HostResolver() { 81 HostResolver::~HostResolver() {
22 hostent_cleanup(); 82 hostent_cleanup();
23 } 83 }
24 84
25 void HostResolver::Init(PepperInterface* ppapi) { 85 void HostResolver::Init(PepperInterface* ppapi) {
26 ppapi_ = ppapi; 86 ppapi_ = ppapi;
27 } 87 }
28 88
29 struct hostent* HostResolver::gethostbyname(const char* name) { 89 struct hostent* HostResolver::gethostbyname(const char* name) {
30 h_errno = NETDB_INTERNAL; 90 h_errno = NETDB_INTERNAL;
31 91
32 if (NULL == ppapi_) 92 struct addrinfo* ai;
33 return NULL; 93 struct addrinfo hints;
34 94 memset(&hints, 0, sizeof(hints));
35 HostResolverInterface* resolver_interface = 95 hints.ai_flags = AI_CANONNAME;
36 ppapi_->GetHostResolverInterface(); 96 int err = getaddrinfo(name, NULL, &hints, &ai);
37 VarInterface* var_interface = ppapi_->GetVarInterface();
38 NetAddressInterface* netaddr_iface = ppapi_->GetNetAddressInterface();
39
40 if (NULL == resolver_interface ||
41 NULL == netaddr_iface ||
42 NULL == var_interface)
43 return NULL;
44
45 ScopedResource resolver(ppapi_,
46 resolver_interface->Create(ppapi_->GetInstance()));
47
48 struct PP_CompletionCallback callback;
49 callback.func = NULL;
50 struct PP_HostResolver_Hint hint;
51 hint.family = PP_NETADDRESS_FAMILY_IPV4;
52 hint.flags = PP_HOSTRESOLVER_FLAG_CANONNAME;
53
54 int err = resolver_interface->Resolve(resolver.pp_resource(),
55 name, 0, &hint, callback);
56 if (err) { 97 if (err) {
57 switch (err) { 98 switch (err) {
58 case PP_ERROR_NOACCESS: 99 case PP_ERROR_NOACCESS:
binji 2014/01/29 19:18:52 getaddrinfo seems to return EAI_ errors, not PP_ e
Sam Clegg 2014/01/29 22:06:13 Good call. Added tests for that too.
59 h_errno = NO_RECOVERY; 100 h_errno = NO_RECOVERY;
60 break; 101 break;
61 case PP_ERROR_NAME_NOT_RESOLVED: 102 case PP_ERROR_NAME_NOT_RESOLVED:
62 h_errno = HOST_NOT_FOUND; 103 h_errno = HOST_NOT_FOUND;
63 break; 104 break;
64 default: 105 default:
65 h_errno = NETDB_INTERNAL; 106 h_errno = NETDB_INTERNAL;
66 break; 107 break;
67 } 108 }
68 return NULL; 109 return NULL;
69 } 110 }
70 111
71 // We use a single hostent struct for all calls to to gethostbyname 112 // We use a single hostent struct for all calls to to gethostbyname
72 // (as explicitly permitted by the spec - gethostbyname is NOT supposed to 113 // (as explicitly permitted by the spec - gethostbyname is NOT supposed to
73 // be threadsafe!), so the first thing we do is free all the malloced data 114 // be threadsafe!). However by using a lock around all the global data
74 // left over from the last call. 115 // manipulation we can at least ensure that the call doesn't crash.
116 AUTO_LOCK(gethostbyname_lock_);
117
118 // The first thing we do is free any malloced data left over from
119 // the last call.
75 hostent_cleanup(); 120 hostent_cleanup();
76 121
77 PP_Var name_var = 122 switch (ai->ai_family) {
78 resolver_interface->GetCanonicalName(resolver.pp_resource()); 123 case AF_INET:
79 if (PP_VARTYPE_STRING != name_var.type) 124 hostent_.h_addrtype = AF_INET;
80 return NULL; 125 hostent_.h_length = sizeof(in_addr);
81 126 break;
82 uint32_t len; 127 case AF_INET6:
83 const char* name_ptr = var_interface->VarToUtf8(name_var, &len); 128 hostent_.h_addrtype = AF_INET6;
84 if (NULL == name_ptr) 129 hostent_.h_length = sizeof(in6_addr);
85 return NULL; 130 break;
86 if (0 == len) { 131 default:
87 // Sometimes GetCanonicalName gives up more easily than gethostbyname should 132 return NULL;
88 // (for example, it returns "" when asked to resolve "localhost"), so if we
89 // get an empty string we copy over the input string instead.
90 len = strlen(name);
91 name_ptr = name;
92 } 133 }
93 hostent_.h_name = static_cast<char*>(malloc(len + 1)); 134 hostent_.h_name = strdup(ai->ai_canonname);
94 if (NULL == hostent_.h_name)
95 return NULL;
96 memcpy(hostent_.h_name, name_ptr, len);
97 hostent_.h_name[len] = '\0';
98
99 var_interface->Release(name_var);
100 135
101 // Aliases aren't supported at the moment, so we just make an empty list. 136 // Aliases aren't supported at the moment, so we just make an empty list.
102 hostent_.h_aliases = static_cast<char**>(malloc(sizeof(char*))); 137 hostent_.h_aliases = static_cast<char**>(malloc(sizeof(char*)));
103 if (NULL == hostent_.h_aliases) 138 if (NULL == hostent_.h_aliases)
104 return NULL; 139 return NULL;
105 hostent_.h_aliases[0] = NULL; 140 hostent_.h_aliases[0] = NULL;
106 141
107 ScopedResource addr(ppapi_); 142 // Count number of address in list
108 addr.Reset(resolver_interface->GetNetAddress(resolver.pp_resource(), 0)); 143 int num_addresses = 0;
109 if (!PP_ToBool(netaddr_iface->IsNetAddress(addr.pp_resource()))) 144 struct addrinfo* current = ai;
145 while (current != NULL) {
146 // Only count address that have the same type as first address
147 if (current->ai_family == hostent_.h_addrtype)
148 num_addresses++;
149 current = current->ai_next;
150 }
151
152 // Allocate address list
153 hostent_.h_addr_list = static_cast<char**>(calloc(num_addresses + 1,
154 sizeof(char*)));
155 if (NULL == hostent_.h_addr_list)
110 return NULL; 156 return NULL;
111 157
112 switch (netaddr_iface->GetFamily(addr.pp_resource())) { 158 // Copy all addresses of the relevant family.
113 case PP_NETADDRESS_FAMILY_IPV4: 159 current = ai;
114 hostent_.h_addrtype = AF_INET; 160 for (int i = 0; i < num_addresses; i++) {
binji 2014/01/29 19:18:52 Now that I look at it closely, this loop is wrong.
Sam Clegg 2014/01/29 22:06:13 Done.
115 hostent_.h_length = 4; 161 if (current->ai_family != hostent_.h_addrtype) {
116 break; 162 continue;
binji 2014/01/29 19:18:52 This continue is broken; it needs to always do cu
Sam Clegg 2014/01/29 22:06:13 Done.
117 case PP_NETADDRESS_FAMILY_IPV6: 163 }
118 hostent_.h_addrtype = AF_INET6; 164 hostent_.h_addr_list[i] = static_cast<char*>(malloc(hostent_.h_length));
119 hostent_.h_length = 16; 165 switch (current->ai_family) {
166 case AF_INET: {
167 sockaddr_in* in = reinterpret_cast<sockaddr_in*>(current->ai_addr);
168 memcpy(hostent_.h_addr_list[i],
169 &in->sin_addr.s_addr,
170 hostent_.h_length);
171 break;
172 }
173 case AF_INET6: {
174 sockaddr_in6* in6 = reinterpret_cast<sockaddr_in6*>(current->ai_addr);
175 memcpy(hostent_.h_addr_list[i],
176 &in6->sin6_addr.s6_addr,
177 hostent_.h_length);
178 break;
179 }
180 }
181 current = current->ai_next;
182 }
183
184 freeaddrinfo(ai);
185 return &hostent_;
186 }
187
188 void HostResolver::freeaddrinfo(struct addrinfo *res) {
189 while (res) {
190 struct addrinfo* cur = res;
191 res = res->ai_next;
192 free(cur);
binji 2014/01/29 19:18:52 move this below the other frees
Sam Clegg 2014/01/29 22:06:13 Done.
193 free(cur->ai_addr);
194 free(cur->ai_canonname);
195 }
196 }
197
198 int HostResolver::getaddrinfo(const char* node, const char* service,
199 const struct addrinfo* hints_in,
200 struct addrinfo** result) {
201 if (node == NULL && service == NULL)
202 return EAI_NONAME;
203
204 // Check the service name (port). Currently we only handle numeric
205 // services.
206 long port = 0;
207 if (service != NULL) {
208 char* cp;
209 port = strtol(service, &cp, 10);
210 if (port > 0 && port <= 65535 && *cp == '\0') {
211 port = htons(port);
212 } else {
213 return EAI_SERVICE;
214 }
215 }
216
217 struct addrinfo default_hints;
218 memset(&default_hints, 0, sizeof(default_hints));
219 const struct addrinfo* hints = hints_in ? hints_in : &default_hints;
220
221 // Verify values passed in hints structure
222 switch (hints->ai_family) {
223 case AF_INET6:
224 case AF_INET:
225 case 0:
binji 2014/01/29 19:18:52 AF_UNSPEC?
Sam Clegg 2014/01/29 22:06:13 Done.
120 break; 226 break;
121 default: 227 default:
122 return NULL; 228 return EAI_FAMILY;
123 } 229 }
124 230
125 const uint32_t num_addresses = 231 struct sockaddr_in addr_in;
126 resolver_interface->GetNetAddressCount(resolver.pp_resource()); 232 memset(&addr_in, 0, sizeof(addr_in));
233 addr_in.sin_family = AF_INET;
234 addr_in.sin_port = port;
235
236 struct sockaddr_in6 addr_in6;
237 memset(&addr_in6, 0, sizeof(addr_in6));
238 addr_in6.sin6_family = AF_INET6;
239 addr_in6.sin6_port = port;
240
241 *result = NULL;
binji 2014/01/29 19:18:52 set this to NULL earlier in the function?
Sam Clegg 2014/01/29 22:06:13 Done.
242 struct addrinfo* end = NULL;
243
244 if (node) {
245 // Handle numeric node name.
246 if (hints->ai_family == AF_INET || hints->ai_family == AF_UNSPEC) {
247 in_addr in;
248 if (inet_pton(AF_INET, node, &in)) {
249 addr_in.sin_addr = in;
250 CreateAddrInfo(hints, (sockaddr*)&addr_in, node, result, &end);
251 return 0;
252 }
253 }
254
255 if (hints->ai_family == AF_INET6 || hints->ai_family == AF_UNSPEC) {
256 in6_addr in6;
257 if (inet_pton(AF_INET6, node, &in6)) {
258 addr_in6.sin6_addr = in6;
259 CreateAddrInfo(hints, (sockaddr*)&addr_in6, node, result, &end);
260 return 0;
261 }
262 }
263 }
264
265 // Handle AI_PASSIVE (used for listening sockets, e.g. INADDR_ANY)
266 if (node == NULL && (hints->ai_flags & AI_PASSIVE)) {
267 if (hints->ai_family == AF_INET6 || hints->ai_family == 0) {
binji 2014/01/29 19:18:52 AF_UNSPEC?
Sam Clegg 2014/01/29 22:06:13 Done.
268 const in6_addr in6addr_any = IN6ADDR_ANY_INIT;
269 memcpy(&addr_in6.sin6_addr.s6_addr, &in6addr_any, sizeof(in6addr_any));
270 CreateAddrInfo(hints, (sockaddr*)&addr_in6, NULL, result, &end);
binji 2014/01/29 19:18:52 Is it correct to return both v4 and v6 addresses i
Sam Clegg 2014/01/29 22:06:13 Yes. At least this is what glibc/linux does.
271 }
272
273 if (hints->ai_family == AF_INET || hints->ai_family == 0) {
274 addr_in.sin_addr.s_addr = INADDR_ANY;
275 CreateAddrInfo(hints, (sockaddr*)&addr_in, NULL, result, &end);
276 }
277 return 0;
278 }
279
280 if (NULL == ppapi_)
281 return EAI_SYSTEM;
282
283 // Use PPAPI interface to resolve nodename
284 HostResolverInterface* resolver_iface = ppapi_->GetHostResolverInterface();
285 VarInterface* var_interface = ppapi_->GetVarInterface();
286 NetAddressInterface* netaddr_iface = ppapi_->GetNetAddressInterface();
287
288 if (NULL == resolver_iface || NULL == var_interface || NULL == netaddr_iface)
289 return EAI_SYSTEM;
290
291 ScopedResource scoped_resolver(ppapi_,
292 resolver_iface->Create(ppapi_->GetInstance()));
293 PP_Resource resolver = scoped_resolver.pp_resource();
294
295 struct PP_HostResolver_Hint pp_hints;
296 HintsToPPHints(hints, &pp_hints);
297
298 struct PP_CompletionCallback callback;
299 callback.func = NULL;
binji 2014/01/29 19:18:52 Just use PP_BlockUntilComplete()?
Sam Clegg 2014/01/29 22:06:13 Done.
300 int err = resolver_iface->Resolve(resolver, node, 0, &pp_hints, callback);
301 if (err) {
302 switch (err) {
303 case PP_ERROR_NOACCESS:
304 return EAI_SYSTEM;
305 case PP_ERROR_NAME_NOT_RESOLVED:
306 return EAI_NONAME;
307 default:
308 return EAI_SYSTEM;
309 }
310 }
311
312 char* canon_name = NULL;
313 if (hints != NULL && hints->ai_flags & AI_CANONNAME) {
314 PP_Var name_var = resolver_iface->GetCanonicalName(resolver);
315
316 uint32_t len = 0;
317 const char* tmp = var_interface->VarToUtf8(name_var, &len);
318 if (len > 0) {
319 // Copy and NULL-terminate the UTF8 string var.
320 canon_name = static_cast<char*>(alloca(len+1));
321 strncpy(canon_name, tmp, len);
322 canon_name[len] = '\0';
323 }
324 var_interface->Release(name_var);
325 }
326
327 ScopedResource addr(ppapi_);
328 addr.Reset(resolver_iface->GetNetAddress(resolver, 0));
binji 2014/01/29 19:18:52 just construct this directly, like above?
Sam Clegg 2014/01/29 22:06:13 Done.
329 if (addr.pp_resource() == 0 ||
330 !PP_ToBool(netaddr_iface->IsNetAddress(addr.pp_resource())))
331 return EAI_SYSTEM;
332
333 int num_addresses = resolver_iface->GetNetAddressCount(resolver);
127 if (0 == num_addresses) 334 if (0 == num_addresses)
128 return NULL; 335 return EAI_NONAME;
binji 2014/01/29 19:18:52 Maybe EAI_NODATA is more correct here?
Sam Clegg 2014/01/29 22:06:13 Done.
129 hostent_.h_addr_list = 336
130 static_cast<char**>(calloc(num_addresses + 1, sizeof(char*))); 337 // Convert address to sockaddr struct.
131 if (NULL == hostent_.h_addr_list) 338 for (int i = 0; i < num_addresses; i++) {
132 return NULL; 339 addr.Reset(resolver_iface->GetNetAddress(resolver, i));
133 340 struct sockaddr* sockaddr = NULL;
134 for (uint32_t i = 0; i < num_addresses; i++) { 341 switch (netaddr_iface->GetFamily(addr.pp_resource())) {
135 addr.Reset(resolver_interface->GetNetAddress(resolver.pp_resource(), i)); 342 case PP_NETADDRESS_FAMILY_IPV4: {
136 if (!PP_ToBool(netaddr_iface->IsNetAddress(addr.pp_resource()))) 343 struct PP_NetAddress_IPv4 addr_struct;
137 return NULL; 344 if (!netaddr_iface->DescribeAsIPv4Address(addr.pp_resource(),
138 if (AF_INET == hostent_.h_addrtype) { 345 &addr_struct)) {
139 struct PP_NetAddress_IPv4 addr_struct; 346 assert(false);
binji 2014/01/29 19:18:52 If this fires, it is a PPAPI failure, not ours. Ma
Sam Clegg 2014/01/29 22:06:13 This is resilient, at least in release builds it w
140 if (!netaddr_iface->DescribeAsIPv4Address(addr.pp_resource(), 347 break;
141 &addr_struct)) 348 }
142 return NULL; 349 memcpy(&addr_in.sin_addr, addr_struct.addr, sizeof(addr_in.sin_addr));
143 hostent_.h_addr_list[i] = static_cast<char*>(malloc(hostent_.h_length)); 350 sockaddr = (struct sockaddr*)&addr_in;
144 if (NULL == hostent_.h_addr_list[i]) 351 break;
145 return NULL; 352 }
146 memcpy(hostent_.h_addr_list[i], addr_struct.addr, hostent_.h_length); 353 case PP_NETADDRESS_FAMILY_IPV6: {
147 } else { // IPv6 354 struct PP_NetAddress_IPv6 addr_struct;
148 struct PP_NetAddress_IPv6 addr_struct; 355 if (!netaddr_iface->DescribeAsIPv6Address(addr.pp_resource(),
149 if (!netaddr_iface->DescribeAsIPv6Address(addr.pp_resource(), 356 &addr_struct)) {
150 &addr_struct)) 357 assert(false);
151 return NULL; 358 break;
152 hostent_.h_addr_list[i] = static_cast<char*>(malloc(hostent_.h_length)); 359 }
153 if (NULL == hostent_.h_addr_list[i]) 360 memcpy(&addr_in6.sin6_addr, addr_struct.addr,
154 return NULL; 361 sizeof(addr_in6.sin6_addr));
155 memcpy(hostent_.h_addr_list[i], addr_struct.addr, hostent_.h_length); 362 sockaddr = (struct sockaddr*)&addr_in6;
156 } 363 break;
157 } 364 }
158 365 default:
159 return &hostent_; 366 return EAI_SYSTEM;
367 }
368
369 if (sockaddr != NULL)
370 CreateAddrInfo(hints, sockaddr, canon_name, result, &end);
371 canon_name = NULL;
372 }
373
374 return 0;
160 } 375 }
161 376
162 // Frees all of the deep pointers in a hostent struct. Called between uses of 377 // Frees all of the deep pointers in a hostent struct. Called between uses of
163 // gethostbyname, and when the kernel_proxy object is destroyed. 378 // gethostbyname, and when the kernel_proxy object is destroyed.
164 void HostResolver::hostent_cleanup() { 379 void HostResolver::hostent_cleanup() {
165 if (NULL != hostent_.h_name) { 380 if (NULL != hostent_.h_name) {
166 free(hostent_.h_name); 381 free(hostent_.h_name);
167 } 382 }
168 if (NULL != hostent_.h_aliases) { 383 if (NULL != hostent_.h_aliases) {
169 for (int i = 0; NULL != hostent_.h_aliases[i]; i++) { 384 for (int i = 0; NULL != hostent_.h_aliases[i]; i++) {
170 free(hostent_.h_aliases[i]); 385 free(hostent_.h_aliases[i]);
171 } 386 }
172 free(hostent_.h_aliases); 387 free(hostent_.h_aliases);
173 } 388 }
174 if (NULL != hostent_.h_addr_list) { 389 if (NULL != hostent_.h_addr_list) {
175 for (int i = 0; NULL != hostent_.h_addr_list[i]; i++) { 390 for (int i = 0; NULL != hostent_.h_addr_list[i]; i++) {
176 free(hostent_.h_addr_list[i]); 391 free(hostent_.h_addr_list[i]);
177 } 392 }
178 free(hostent_.h_addr_list); 393 free(hostent_.h_addr_list);
179 } 394 }
180 hostent_.h_name = NULL; 395 hostent_.h_name = NULL;
181 hostent_.h_aliases = NULL; 396 hostent_.h_aliases = NULL;
182 hostent_.h_addr_list = NULL; 397 hostent_.h_addr_list = NULL;
183 } 398 }
184 399
185 } // namespace nacl_io 400 } // namespace nacl_io
186 401
187 #endif // PROVIDES_SOCKET_API 402 #endif // PROVIDES_SOCKET_API
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698