Libinfo-278.tar.gz
[apple/libinfo.git] / lookup.subproj / lu_host_async.c
1 /*
2 * Copyright (c) 2002 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Portions Copyright (c) 2002 Apple Computer, Inc. All Rights
7 * Reserved. This file contains Original Code and/or Modifications of
8 * Original Code as defined in and that are subject to the Apple Public
9 * Source License Version 1.1 (the "License"). You may not use this file
10 * except in compliance with the License. Please obtain a copy of the
11 * License at http://www.apple.com/publicsource and read it before using
12 * this file.
13 *
14 * The Original Code and all software distributed under the License are
15 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE OR NON- INFRINGEMENT. Please see the
19 * License for the specific language governing rights and limitations
20 * under the License.
21 *
22 * @APPLE_LICENSE_HEADER_END@
23 */
24
25 #include <netdb.h>
26 #include <netdb_async.h>
27 #include <pthread.h>
28 #include <stdlib.h>
29 #include <mach/mach.h>
30 #include <sys/socket.h>
31 #include <netinet/in.h>
32 #include <arpa/inet.h>
33 #include <ifaddrs.h>
34 #include <net/if.h>
35 #include <errno.h>
36
37 #include "lu_host.h"
38 #include "lu_utils.h"
39
40 #define IPV6_ADDR_LEN 16
41 #define IPV4_ADDR_LEN 4
42
43 typedef struct
44 {
45 void *user_context;
46 int want;
47 } my_context_t;
48
49 mach_port_t
50 gethostbyaddr_async_start(const char *addr, int len, int family, gethostbyaddr_async_callback callback, void *context)
51 {
52 static int proc = 1;
53 int32_t want, status;
54 kvbuf_t *request;
55 mach_port_t mp;
56 my_context_t *my_context;
57
58 mp = MACH_PORT_NULL;
59
60 if (addr == NULL) return MACH_PORT_NULL;
61 if (len == 0) return MACH_PORT_NULL;
62 if ((family != AF_INET) && (family != AF_INET6)) return MACH_PORT_NULL;
63
64 want = WANT_A4_ONLY;
65 if (family == AF_INET6) want = WANT_A6_ONLY;
66
67 if ((family == AF_INET6) && (len == IPV6_ADDR_LEN) && (is_a4_mapped((const char *)addr) || is_a4_compat((const char *)addr)))
68 {
69 addr += 12;
70 len = 4;
71 family = AF_INET;
72 want = WANT_MAPPED_A4_ONLY;
73 }
74
75 if (proc < 0)
76 {
77 status = LI_DSLookupGetProcedureNumber("gethostbyaddr", &proc);
78 if (status != KERN_SUCCESS) return MACH_PORT_NULL;
79 }
80
81 request = kvbuf_query("ksku", "address", addr, "family", want);
82 if (request == NULL) return MACH_PORT_NULL;
83
84 my_context = (my_context_t *)calloc(1, sizeof(my_context_t));
85 if (my_context == NULL) return MACH_PORT_NULL;
86
87 my_context->user_context = context;
88 my_context->want = want;
89
90 status = LI_async_start(&mp, proc, request, (void *)callback, my_context);
91
92 kvbuf_free(request);
93 return mp;
94 }
95
96 void
97 gethostbyaddr_async_cancel(mach_port_t port)
98 {
99 my_context_t *my_context;
100
101 my_context = NULL;
102
103 LI_async_call_cancel(port, (void **)&my_context);
104
105 if (my_context != NULL) free(my_context);
106 }
107
108 void
109 gethostbyaddr_async_handleReply(void *msg)
110 {
111 gethostbyaddr_async_callback callback;
112 struct hostent *out;
113 uint32_t len, want;
114 int status;
115 kvarray_t *reply;
116 my_context_t *my_context;
117 void *context;
118
119 callback = (gethostbyaddr_async_callback)NULL;
120 my_context = NULL;
121 context = NULL;
122 len = 0;
123 reply = NULL;
124
125 status = LI_async_handle_reply(msg, &reply, (void **)&callback, (void **)&my_context);
126 if ((status != KERN_SUCCESS) || (reply == NULL))
127 {
128 if (status == MIG_REPLY_MISMATCH) return;
129 if (callback != NULL)
130 {
131 if (my_context != NULL) context = my_context->user_context;
132 callback(NULL, context);
133 free(my_context);
134 return;
135 }
136 }
137
138 want = WANT_A4_ONLY;
139 if (my_context != NULL)
140 {
141 context = my_context->user_context;
142 want = my_context->want;
143 free(my_context);
144 }
145
146 out = extract_host(reply, want);
147 kvarray_free(reply);
148
149 callback(out, context);
150 }
151
152 mach_port_t
153 getipnodebyaddr_async_start(const void *addr, size_t len, int family, int *error, getipnodebyaddr_async_callback callback, void *context)
154 {
155 static int proc = 1;
156 int32_t want, status;
157 kvbuf_t *request;
158 mach_port_t mp;
159 my_context_t *my_context;
160
161 mp = MACH_PORT_NULL;
162
163 if (addr == NULL) return MACH_PORT_NULL;
164 if (len == 0) return MACH_PORT_NULL;
165 if ((family != AF_INET) && (family != AF_INET6)) return MACH_PORT_NULL;
166
167 want = WANT_A4_ONLY;
168 if (family == AF_INET6) want = WANT_A6_ONLY;
169
170 if ((family == AF_INET6) && (len == IPV6_ADDR_LEN) && (is_a4_mapped((const char *)addr) || is_a4_compat((const char *)addr)))
171 {
172 addr += 12;
173 len = 4;
174 family = AF_INET;
175 want = WANT_MAPPED_A4_ONLY;
176 }
177
178 if (proc < 0)
179 {
180 status = LI_DSLookupGetProcedureNumber("gethostbyaddr", &proc);
181 if (status != KERN_SUCCESS) return MACH_PORT_NULL;
182 }
183
184 request = kvbuf_query("ksku", "address", addr, "family", want);
185 if (request == NULL) return MACH_PORT_NULL;
186
187 my_context = (my_context_t *)calloc(1, sizeof(my_context_t));
188 if (my_context == NULL) return MACH_PORT_NULL;
189
190 my_context->user_context = context;
191 my_context->want = want;
192
193 status = LI_async_start(&mp, proc, request, (void *)callback, my_context);
194
195 kvbuf_free(request);
196 return mp;
197 }
198
199 void
200 getipnodebyaddr_async_cancel(mach_port_t port)
201 {
202 my_context_t *my_context;
203
204 my_context = NULL;
205
206 LI_async_call_cancel(port, (void **)&my_context);
207
208 if (my_context != NULL) free(my_context);
209 }
210
211 void
212 getipnodebyaddr_async_handleReply(void *msg)
213 {
214 getipnodebyaddr_async_callback callback;
215 struct hostent *out;
216 uint32_t len, want;
217 int status;
218 kvarray_t *reply;
219 my_context_t *my_context;
220 void *context;
221
222 callback = (getipnodebyaddr_async_callback)NULL;
223 my_context = NULL;
224 context = NULL;
225 len = 0;
226 reply = NULL;
227
228 status = LI_async_handle_reply(msg, &reply, (void **)&callback, (void **)&my_context);
229 if ((status != KERN_SUCCESS) || (reply == NULL))
230 {
231 if (status == MIG_REPLY_MISMATCH) return;
232 if (callback != NULL)
233 {
234 if (my_context != NULL) context = my_context->user_context;
235 callback(NULL, NO_RECOVERY, context);
236 free(my_context);
237 return;
238 }
239 }
240
241 want = WANT_A4_ONLY;
242 if (my_context != NULL)
243 {
244 context = my_context->user_context;
245 want = my_context->want;
246 free(my_context);
247 }
248
249 out = extract_host(reply, want);
250 kvarray_free(reply);
251
252 if (out == NULL)
253 {
254 callback(NULL, HOST_NOT_FOUND, context);
255 return;
256 }
257
258 callback(out, 0, context);
259 }
260
261 mach_port_t
262 gethostbyname_async_start(const char *name, gethostbyname_async_callback callback, void *context)
263 {
264 static int proc = 1;
265 int32_t status;
266 kvbuf_t *request;
267 mach_port_t mp;
268
269 mp = MACH_PORT_NULL;
270
271 if (name == NULL) return MACH_PORT_NULL;
272
273 if (proc < 0)
274 {
275 status = LI_DSLookupGetProcedureNumber("gethostbyname", &proc);
276 if (status != KERN_SUCCESS) return MACH_PORT_NULL;
277 }
278
279 request = kvbuf_query("ksksks", "name", name, "ipv4", "1", "ipv6", "0");
280 if (request == NULL) return MACH_PORT_NULL;
281
282 status = LI_async_start(&mp, proc, request, (void *)callback, context);
283
284 kvbuf_free(request);
285 return mp;
286 }
287
288 void
289 gethostbyname_async_cancel(mach_port_t port)
290 {
291 LI_async_call_cancel(port, NULL);
292 }
293
294 void
295 gethostbyname_async_handleReply(void *msg)
296 {
297 gethostbyname_async_callback callback;
298 struct hostent *out;
299 uint32_t len;
300 int status;
301 kvarray_t *reply;
302 void *context;
303
304 callback = (gethostbyname_async_callback)NULL;
305 context = NULL;
306 len = 0;
307 reply = NULL;
308
309 status = LI_async_handle_reply(msg, &reply, (void **)&callback, (void **)&context);
310 if ((status != KERN_SUCCESS) || (reply == NULL))
311 {
312 if (status == MIG_REPLY_MISMATCH) return;
313 if (callback != NULL)
314 {
315 callback(NULL, context);
316 return;
317 }
318 }
319
320 out = extract_host(reply, AF_INET);
321 kvarray_free(reply);
322
323 callback(out, context);
324 }
325
326 mach_port_t
327 getipnodebyname_async_start(const char *name, int family, int flags, int *err, getipnodebyname_async_callback callback, void *context)
328 {
329 static int proc = 1;
330 int32_t status, want, want4, want6, if4, if6;
331 kvbuf_t *request;
332 mach_port_t mp;
333 struct ifaddrs *ifa, *ifap;
334 struct in_addr addr4;
335 struct in6_addr addr6;
336 my_context_t *my_context;
337
338 if (name == NULL) return MACH_PORT_NULL;
339
340 if (err != NULL) *err = 0;
341
342 if4 = 0;
343 if6 = 0;
344 mp = MACH_PORT_NULL;
345 memset(&addr4, 0, sizeof(struct in_addr));
346 memset(&addr6, 0, sizeof(struct in6_addr));
347
348 if (flags & AI_ADDRCONFIG)
349 {
350 if (getifaddrs(&ifa) < 0)
351 {
352 if (err != NULL) *err = NO_RECOVERY;
353 return MACH_PORT_NULL;
354 }
355
356 for (ifap = ifa; ifap != NULL; ifap = ifap->ifa_next)
357 {
358 if (ifap->ifa_addr == NULL) continue;
359 if ((ifap->ifa_flags & IFF_UP) == 0) continue;
360 if (ifap->ifa_addr->sa_family == AF_INET) if4++;
361 else if (ifap->ifa_addr->sa_family == AF_INET6) if6++;
362 }
363
364 freeifaddrs(ifa);
365
366 /* Bail out if there are no interfaces */
367 if ((if4 == 0) && (if6 == 0))
368 {
369 if (err != NULL) *err = NO_RECOVERY;
370 return MACH_PORT_NULL;
371 }
372 }
373
374 /*
375 * Figure out what we want.
376 * If user asked for AF_INET, we only want V4 addresses.
377 */
378 want = WANT_A4_ONLY;
379
380 if (family == AF_INET)
381 {
382 if ((flags & AI_ADDRCONFIG) && (if4 == 0))
383 {
384 if (err != NULL) *err = NO_RECOVERY;
385 return MACH_PORT_NULL;
386 }
387 }
388 else
389 {
390 /* family == AF_INET6 */
391 want = WANT_A6_ONLY;
392
393 if (flags & (AI_V4MAPPED | AI_V4MAPPED_CFG))
394 {
395 if (flags & AI_ALL)
396 {
397 want = WANT_A6_PLUS_MAPPED_A4;
398 }
399 else
400 {
401 want = WANT_A6_OR_MAPPED_A4_IF_NO_A6;
402 }
403 }
404 else
405 {
406 if ((flags & AI_ADDRCONFIG) && (if6 == 0))
407 {
408 if (err != NULL) *err = NO_RECOVERY;
409 return MACH_PORT_NULL;
410 }
411 }
412 }
413
414 if (proc < 0)
415 {
416 status = LI_DSLookupGetProcedureNumber("gethostbyname", &proc);
417 if (status != KERN_SUCCESS) return MACH_PORT_NULL;
418 }
419
420 my_context = (my_context_t *)calloc(1, sizeof(my_context_t));
421 if (my_context == NULL)
422 {
423 *err = NO_RECOVERY;
424 return MACH_PORT_NULL;
425 }
426
427 my_context->user_context = context;
428 my_context->want = want;
429
430 want4 = 1;
431 want6 = 1;
432
433 if (want == WANT_A4_ONLY) want6 = 0;
434 else if (want == WANT_A6_ONLY) want4 = 0;
435 else if (WANT_MAPPED_A4_ONLY) want6 = 0;
436
437 request = kvbuf_query("kskuku", "name", name, "ipv4", want4, "ipv6", want6);
438 if (request == NULL) return MACH_PORT_NULL;
439
440 status = LI_async_start(&mp, proc, request, (void *)callback, my_context);
441
442 kvbuf_free(request);
443 return mp;
444 }
445
446 void
447 getipnodebyname_async_cancel(mach_port_t port)
448 {
449 my_context_t *my_context;
450
451 my_context = NULL;
452
453 LI_async_call_cancel(port, (void **)&my_context);
454
455 if (my_context != NULL) free(my_context);
456 }
457
458 void
459 getipnodebyname_async_handleReply(void *msg)
460 {
461 getipnodebyname_async_callback callback;
462 struct hostent *out;
463 uint32_t len, want;
464 int status, err;
465 kvarray_t *reply;
466 my_context_t *my_context;
467 void *context;
468
469 callback = (getipnodebyname_async_callback)NULL;
470 my_context = NULL;
471 context = NULL;
472 len = 0;
473 reply = NULL;
474
475 status = LI_async_handle_reply(msg, &reply, (void **)&callback, (void **)&my_context);
476 if ((status != KERN_SUCCESS) || (reply == NULL))
477 {
478 if (status == MIG_REPLY_MISMATCH) return;
479 if (callback != NULL)
480 {
481 if (my_context != NULL) context = my_context->user_context;
482 callback(NULL, NO_RECOVERY, context);
483 free(my_context);
484 return;
485 }
486 }
487
488 want = WANT_A4_ONLY;
489 if (my_context != NULL)
490 {
491 context = my_context->user_context;
492 want = my_context->want;
493 free(my_context);
494 }
495
496 out = extract_host(reply, want);
497 kvarray_free(reply);
498
499 if (out == NULL)
500 {
501 err = HOST_NOT_FOUND;
502 callback(NULL, err, context);
503 return;
504 }
505
506 callback(out, 0, context);
507 }