Libinfo-222.4.6.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 /* async gethostbyXXX function prototypes */
27 #include <netdb_async.h>
28 #include <pthread.h>
29 #include <stdlib.h>
30 #include <mach/mach.h>
31 #include <netinfo/_lu_types.h>
32 #include <netinfo/lookup.h>
33 #include <rpc/types.h>
34 #include <rpc/xdr.h>
35 #include <sys/socket.h>
36 #include <netinet/in.h>
37 #include <arpa/inet.h>
38 #include <ifaddrs.h>
39 #include <net/if.h>
40 #include <libkern/OSByteOrder.h>
41
42 #include "lu_host.h"
43 #include "lu_utils.h"
44
45 extern mach_port_t _lu_port;
46 extern int _lu_running(void);
47
48 extern int h_errno;
49
50 #define msgh_request_port msgh_remote_port
51 #define msgh_reply_port msgh_local_port
52
53
54 typedef union
55 {
56 gethostbyaddr_async_callback hostAddr;
57 gethostbyname_async_callback hostName;
58 getipnodebyaddr_async_callback nodeAddr;
59 getipnodebyname_async_callback nodeName;
60 } a_request_callout_t;
61
62 typedef struct a_requests
63 {
64 struct a_requests *next;
65 int retry;
66 struct
67 {
68 int proc;
69 ooline_data data;
70 unsigned int dataLen;
71 int want;
72 } request;
73 mach_port_t replyPort;
74 a_request_callout_t callout;
75 void *context;
76 struct hostent *hent; /* if reply known in XXX_start() */
77 } a_requests_t;
78
79 static a_requests_t *a_requests = NULL;
80 static pthread_mutex_t a_requests_lock = PTHREAD_MUTEX_INITIALIZER;
81
82 #define MAX_LOOKUP_ATTEMPTS 10
83
84 static kern_return_t
85 _lookup_all_tx(mach_port_t server, int proc, ooline_data indata, mach_msg_type_number_t indataCnt, mach_port_t *replyPort)
86 {
87 typedef struct
88 {
89 mach_msg_header_t Head;
90 NDR_record_t NDR;
91 int proc;
92 mach_msg_type_number_t indataCnt;
93 unit indata[4096];
94 } Request;
95
96 Request In;
97 register Request *InP = &In;
98 mach_msg_return_t mr;
99 unsigned int msgh_size;
100
101 if (indataCnt > 4096) return MIG_ARRAY_TOO_LARGE;
102
103 if (*replyPort == MACH_PORT_NULL)
104 {
105 mr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, replyPort);
106 if (mr != KERN_SUCCESS) return mr;
107 }
108
109 msgh_size = (sizeof(Request) - 16384) + ((4 * indataCnt));
110 InP->Head.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE);
111 // InP->Head.msgh_size = msgh_size; /* msgh_size passed as argument */
112 InP->Head.msgh_request_port = server;
113 InP->Head.msgh_reply_port = *replyPort;
114 InP->Head.msgh_id = 4241776;
115 InP->NDR = NDR_record;
116 InP->proc = proc;
117 InP->indataCnt = indataCnt;
118 memcpy((char *)InP->indata, (const char *)indata, 4 * indataCnt);
119
120 mr = mach_msg(&InP->Head, /* msg */
121 MACH_SEND_MSG, /* options */
122 msgh_size, /* send_size */
123 0, /* rcv_size */
124 MACH_PORT_NULL, /* rcv_name */
125 MACH_MSG_TIMEOUT_NONE, /* timeout */
126 MACH_PORT_NULL); /* notify */
127
128 switch (mr)
129 {
130 case MACH_MSG_SUCCESS:
131 mr = KERN_SUCCESS;
132 break;
133 case MACH_SEND_INVALID_REPLY :
134 (void)mach_port_mod_refs(mach_task_self(), *replyPort, MACH_PORT_RIGHT_RECEIVE, -1);
135 *replyPort = MACH_PORT_NULL;
136 break;
137 default:
138 break;
139 }
140
141 return mr;
142 }
143
144 static kern_return_t
145 _lookup_all_rx(void *msg, ooline_data *outdata, mach_msg_type_number_t *outdataCnt, security_token_t *token)
146 {
147 typedef struct
148 {
149 mach_msg_header_t Head;
150 mach_msg_body_t msgh_body;
151 mach_msg_ool_descriptor_t outdata;
152 NDR_record_t NDR;
153 mach_msg_type_number_t outdataCnt;
154 mach_msg_format_0_trailer_t trailer;
155 } Reply;
156
157 /*
158 * typedef struct {
159 * mach_msg_header_t Head;
160 * NDR_record_t NDR;
161 * kern_return_t RetCode;
162 * } mig_reply_error_t;
163 */
164
165 register Reply *OutP = msg;
166 mach_msg_format_0_trailer_t *TrailerP;
167 boolean_t msgh_simple;
168
169 if (OutP->Head.msgh_id != (4241776 + 100))
170 {
171 if (OutP->Head.msgh_id == MACH_NOTIFY_SEND_ONCE) return MIG_SERVER_DIED;
172 else return MIG_REPLY_MISMATCH;
173 }
174
175 msgh_simple = !(OutP->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX);
176
177 TrailerP = (mach_msg_format_0_trailer_t *)((vm_offset_t)OutP + round_msg(OutP->Head.msgh_size));
178 if (TrailerP->msgh_trailer_type != MACH_MSG_TRAILER_FORMAT_0) return MIG_TRAILER_ERROR;
179
180 if (OutP->NDR.int_rep != NDR_record.int_rep)
181 {
182 if (msgh_simple)
183 {
184 ((mig_reply_error_t *)OutP)->RetCode = OSReadSwapInt32(&(((mig_reply_error_t *)OutP)->RetCode), 0);
185 }
186 else
187 {
188 OutP->outdataCnt = OSReadSwapInt32(&(OutP->outdataCnt), 0);
189 }
190 }
191
192 if (msgh_simple && ((mig_reply_error_t *)OutP)->RetCode != KERN_SUCCESS) return ((mig_reply_error_t *)OutP)->RetCode;
193
194 *outdata = (ooline_data)(OutP->outdata.address);
195 *outdataCnt = OutP->outdataCnt;
196
197 *token = TrailerP->msgh_sender;
198
199 return KERN_SUCCESS;
200 }
201
202 static a_requests_t *
203 request_extract(mach_port_t port)
204 {
205 a_requests_t *request0, *request;
206
207 pthread_mutex_lock(&a_requests_lock);
208
209 request0 = NULL;
210 request = a_requests;
211
212 while (request != NULL)
213 {
214 if (port == request->replyPort)
215 {
216 /* request found, remove from list */
217 if (request0 != NULL)
218 {
219 request0->next = request->next;
220 }
221 else
222 {
223 a_requests = request->next;
224 }
225
226 break;
227 }
228 else
229 {
230 /* not this request, skip to next */
231 request0 = request;
232 request = request->next;
233 }
234 }
235
236 pthread_mutex_unlock(&a_requests_lock);
237
238 return request;
239 }
240
241 static void
242 request_queue(a_requests_t *request)
243 {
244 pthread_mutex_lock(&a_requests_lock);
245
246 request->next = a_requests;
247 a_requests = request;
248
249 pthread_mutex_unlock(&a_requests_lock);
250
251 return;
252 }
253
254 static boolean_t
255 sendCannedReply(a_requests_t *request, int *error)
256 {
257 /*
258 * typedef struct {
259 * mach_msg_header_t Head;
260 * NDR_record_t NDR;
261 * kern_return_t RetCode;
262 * } mig_reply_error_t;
263 */
264
265 mig_reply_error_t Out;
266 register mig_reply_error_t *OutP = &Out;
267 kern_return_t kr;
268 mach_msg_return_t mr;
269 unsigned int msgh_size;
270
271 mach_port_t sendPort;
272 mach_msg_type_name_t sendType;
273
274 /*
275 * allocate reply port
276 */
277 kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &request->replyPort);
278 if (kr != KERN_SUCCESS)
279 {
280 *error = NO_RECOVERY;
281 return FALSE;
282 }
283
284 kr = mach_port_extract_right(mach_task_self(), request->replyPort, MACH_MSG_TYPE_MAKE_SEND_ONCE, &sendPort, &sendType);
285 if (kr != KERN_SUCCESS)
286 {
287 (void)mach_port_destroy(mach_task_self(), request->replyPort);
288 request->replyPort = MACH_PORT_NULL;
289 *error = NO_RECOVERY;
290 return FALSE;
291 }
292
293 /*
294 * queue reply message
295 */
296 msgh_size = sizeof(Out);
297 OutP->Head.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE, 0);
298 // OutP->Head.msgh_size = msgh_size; /* msgh_size passed as argument */
299 OutP->Head.msgh_request_port = sendPort;
300 OutP->Head.msgh_reply_port = MACH_PORT_NULL;
301 OutP->Head.msgh_id = 4241776 + 100;
302 OutP->RetCode = MIG_REMOTE_ERROR;
303 OutP->NDR = NDR_record;
304
305 mr = mach_msg(&OutP->Head, MACH_SEND_MSG, msgh_size, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
306 if (mr != MACH_MSG_SUCCESS)
307 {
308 if (mr == MACH_SEND_INVALID_REPLY)
309 {
310 (void)mach_port_destroy(mach_task_self(), request->replyPort);
311 request->replyPort = MACH_PORT_NULL;
312 }
313
314 *error = NO_RECOVERY;
315 return FALSE;
316 }
317
318 return TRUE;
319 }
320
321 static void
322 _async_cancel(mach_port_t port)
323 {
324 a_requests_t *request;
325
326 request = request_extract(port);
327 if (request)
328 {
329 (void)mach_port_mod_refs(mach_task_self(), request->replyPort, MACH_PORT_RIGHT_RECEIVE, -1);
330 if (request->request.data) free(request->request.data);
331 if (request->hent) freehostent(request->hent);
332 free(request);
333 }
334
335 return;
336 }
337
338 static mach_port_t
339 _gethostbyaddr_async_start(const char *addr, int len, int type, a_request_callout_t callout, void *context, int *error)
340 {
341 void *address;
342 int proc;
343 a_requests_t *request;
344 int want;
345 static int proc4 = -1;
346 struct in_addr *v4addr;
347 static int proc6 = -1;
348 struct in6_addr *v6addr;
349
350 want = WANT_A4_ONLY;
351 if (type == AF_INET6) want = WANT_A6_ONLY;
352
353 if ((type == AF_INET6) && (len == 16) && (is_a4_mapped((const char *)addr) || is_a4_compat((const char *)addr)))
354 {
355 addr += 12;
356 len = 4;
357 type = AF_INET;
358 want = WANT_MAPPED_A4_ONLY;
359 }
360
361 switch (type)
362 {
363 case AF_INET:
364 {
365 if (proc4 < 0)
366 {
367 if (_lookup_link(_lu_port, "gethostbyaddr", &proc4) != KERN_SUCCESS)
368 {
369 *error = NO_RECOVERY;
370 return MACH_PORT_NULL;
371 }
372 }
373
374 if (len != sizeof(struct in_addr))
375 {
376 *error = NO_RECOVERY;
377 return MACH_PORT_NULL;
378 }
379
380 v4addr = malloc(len);
381 memmove(v4addr, addr, len);
382
383 address = (void *)v4addr;
384 proc = proc4;
385 break;
386 }
387
388 case AF_INET6:
389 {
390 if (proc6 < 0)
391 {
392 if (_lookup_link(_lu_port, "getipv6nodebyaddr", &proc6) != KERN_SUCCESS)
393 {
394 *error = NO_RECOVERY;
395 return MACH_PORT_NULL;
396 }
397 }
398
399 if (len != sizeof(struct in6_addr))
400 {
401 *error = NO_RECOVERY;
402 return MACH_PORT_NULL;
403 }
404
405 v6addr = malloc(len);
406 memmove(v6addr, addr, len);
407 v6addr->__u6_addr.__u6_addr32[0] = htonl(v6addr->__u6_addr.__u6_addr32[0]);
408 v6addr->__u6_addr.__u6_addr32[1] = htonl(v6addr->__u6_addr.__u6_addr32[1]);
409 v6addr->__u6_addr.__u6_addr32[2] = htonl(v6addr->__u6_addr.__u6_addr32[2]);
410 v6addr->__u6_addr.__u6_addr32[3] = htonl(v6addr->__u6_addr.__u6_addr32[3]);
411
412 address = (void *)v6addr;
413 proc = proc6;
414 break;
415 }
416
417 default:
418 *error = NO_RECOVERY;
419 return MACH_PORT_NULL;
420 }
421
422 request = malloc(sizeof(a_requests_t));
423 request->next = NULL;
424 request->retry = MAX_LOOKUP_ATTEMPTS;
425 request->request.proc = proc;
426 request->request.data = (ooline_data)address;
427 request->request.dataLen = len / BYTES_PER_XDR_UNIT;
428 request->request.want = want;
429 request->replyPort = MACH_PORT_NULL;
430 request->callout = callout;
431 request->context = context;
432 request->hent = NULL;
433
434 /*
435 * allocate reply port, send query to lookupd
436 */
437 if (_lookup_all_tx(_lu_port, request->request.proc, request->request.data, request->request.dataLen, &request->replyPort) == KERN_SUCCESS)
438 {
439 request_queue(request);
440 }
441 else
442 {
443 if (request->request.data) free(request->request.data);
444 free(request);
445 *error = NO_RECOVERY;
446 return MACH_PORT_NULL;
447 }
448
449 return request->replyPort;
450 }
451
452 static boolean_t
453 _gethostbyaddr_async_handleReply(void *replyMsg, a_requests_t **requestP, struct hostent **he, int *error)
454 {
455 int count;
456 ooline_data data;
457 unsigned int datalen;
458 XDR inxdr;
459 mach_msg_header_t *msg = (mach_msg_header_t *)replyMsg;
460 a_requests_t *request;
461 kern_return_t status;
462 security_token_t token;
463
464 request = request_extract(msg->msgh_local_port);
465 if (!request)
466 {
467 /* excuse me, what happenned to the request info? */
468 return FALSE;
469 }
470
471 *requestP = request;
472 *he = NULL;
473 *error = 0;
474
475 /* unpack the reply */
476 status = _lookup_all_rx(replyMsg, &data, &datalen, &token);
477 switch (status)
478 {
479 case KERN_SUCCESS:
480 break;
481
482 case MIG_SERVER_DIED:
483 if (--request->retry > 0)
484 {
485 /* retry the request */
486 if (_lookup_all_tx(_lu_port, request->request.proc, request->request.data, request->request.dataLen, &request->replyPort) == KERN_SUCCESS)
487 {
488 request_queue(request);
489 return FALSE;
490 }
491 }
492 /* fall through */
493
494 default:
495 *error = HOST_NOT_FOUND;
496 return TRUE;
497 }
498
499 datalen *= BYTES_PER_XDR_UNIT;
500
501 if (token.val[0] != 0)
502 {
503 vm_deallocate(mach_task_self(), (vm_address_t)data, datalen);
504 *error = NO_RECOVERY;
505 return TRUE;
506 }
507
508 xdrmem_create(&inxdr, data, datalen, XDR_DECODE);
509
510 count = 0;
511 if (!xdr_int(&inxdr, &count))
512 {
513 xdr_destroy(&inxdr);
514 vm_deallocate(mach_task_self(), (vm_address_t)data, datalen);
515 *error = NO_RECOVERY;
516 return TRUE;
517 }
518
519 if (count == 0)
520 {
521 xdr_destroy(&inxdr);
522 vm_deallocate(mach_task_self(), (vm_address_t)data, datalen);
523 *error = HOST_NOT_FOUND;
524 *he = NULL;
525 return TRUE;
526 }
527
528 *he = extract_host(&inxdr, request->request.want, error);
529 xdr_destroy(&inxdr);
530 vm_deallocate(mach_task_self(), (vm_address_t)data, datalen);
531 return TRUE;
532 }
533
534 mach_port_t
535 gethostbyaddr_async_start(const char *addr, int len, int type, gethostbyaddr_async_callback callout, void *context)
536 {
537 a_request_callout_t cb;
538 mach_port_t mp;
539
540 if (!_lu_running())
541 {
542 h_errno = NO_RECOVERY;
543 return MACH_PORT_NULL;
544 }
545
546 cb.hostAddr = callout;
547 mp = _gethostbyaddr_async_start(addr, len, type, cb, context, &h_errno);
548 return mp;
549 }
550
551 void
552 gethostbyaddr_async_cancel(mach_port_t port)
553 {
554 _async_cancel(port);
555 return;
556 }
557
558 void
559 gethostbyaddr_async_handleReply(void *replyMsg)
560 {
561 int error = 0;
562 struct hostent *he = NULL;
563 a_requests_t *request = NULL;
564
565 if (_gethostbyaddr_async_handleReply(replyMsg, &request, &he, &error))
566 {
567 /* if we have an answer to provide */
568 h_errno = error;
569 (request->callout.hostAddr)(he, request->context);
570
571 (void)mach_port_mod_refs(mach_task_self(), request->replyPort, MACH_PORT_RIGHT_RECEIVE, -1);
572 if (request->request.data) free(request->request.data);
573 free(request);
574 if (he != NULL) freehostent(he);
575 }
576
577 return;
578 }
579
580 mach_port_t
581 getipnodebyaddr_async_start(const void *addr, size_t len, int af, int *error, getipnodebyaddr_async_callback callout, void *context)
582 {
583 a_request_callout_t cb;
584 mach_port_t mp;
585
586 if (!_lu_running())
587 {
588 *error = NO_RECOVERY;
589 return MACH_PORT_NULL;
590 }
591
592 cb.nodeAddr = callout;
593 mp = _gethostbyaddr_async_start(addr, len, af, cb, context, error);
594 return mp;
595 }
596
597 void
598 getipnodebyaddr_async_cancel(mach_port_t port)
599 {
600 _async_cancel(port);
601 return;
602 }
603
604 void
605 getipnodebyaddr_async_handleReply(void *replyMsg)
606 {
607 int error = 0;
608 struct hostent *he = NULL;
609 a_requests_t *request = NULL;
610
611 if (_gethostbyaddr_async_handleReply(replyMsg, &request, &he, &error))
612 {
613 /* if we have an answer to provide */
614 (request->callout.nodeAddr)(he, error, request->context);
615
616 (void)mach_port_mod_refs(mach_task_self(), request->replyPort, MACH_PORT_RIGHT_RECEIVE, -1);
617 if (request->request.data) free(request->request.data);
618 free(request);
619 /*
620 * Note: it is up to the callback function to call
621 * freehostent().
622 */
623 }
624
625 return;
626 }
627
628 static mach_port_t
629 _gethostbyname_async_start(const char *name, int want, int *error, a_request_callout_t callout, void *context)
630 {
631 int af;
632 boolean_t is_addr = FALSE;
633 mach_port_t mp = MACH_PORT_NULL;
634 XDR outxdr;
635 static int proc;
636 a_requests_t *request;
637 static int proc4 = -1;
638 static int proc6 = -1;
639 struct in_addr v4addr;
640 struct in6_addr v6addr;
641
642 if ((name == NULL) || (name[0] == '\0'))
643 {
644 *error = NO_DATA;
645 return MACH_PORT_NULL;
646 }
647
648 af = (want == WANT_A4_ONLY) ? AF_INET: AF_INET6;
649
650 if ((af == AF_INET) || (want == WANT_MAPPED_A4_ONLY))
651 {
652 if (proc4 < 0)
653 {
654 if (_lookup_link(_lu_port, "gethostbyname", &proc4) != KERN_SUCCESS)
655 {
656 *error = NO_RECOVERY;
657 return MACH_PORT_NULL;
658 }
659 }
660 proc = proc4;
661 }
662 else /* if (af == AF_INET6) */
663 {
664 if (proc6 < 0)
665 {
666 if (_lookup_link(_lu_port, "getipv6nodebyname", &proc6) != KERN_SUCCESS)
667 {
668 *error = NO_RECOVERY;
669 return MACH_PORT_NULL;
670 }
671 }
672 proc = proc6;
673 }
674
675 request = malloc(sizeof(a_requests_t));
676 request->next = NULL;
677 request->retry = MAX_LOOKUP_ATTEMPTS;
678 request->request.proc = proc;
679 request->request.data = NULL;
680 request->request.dataLen = 0;
681 request->request.want = want;
682 request->replyPort = MACH_PORT_NULL;
683 request->callout = callout;
684 request->context = context;
685 request->hent = NULL;
686
687 switch (af)
688 {
689 case AF_INET:
690 {
691 memset(&v4addr, 0, sizeof(struct in_addr));
692 if (inet_aton(name, &v4addr) == 1)
693 {
694 /* return a fake hostent */
695 request->hent = fake_hostent(name, v4addr);
696 is_addr = TRUE;
697 }
698 break;
699 }
700
701 case AF_INET6:
702 {
703 memset(&v6addr, 0, sizeof(struct in6_addr));
704 if (inet_pton(af, name, &v6addr) == 1)
705 {
706 /* return a fake hostent */
707 request->hent = fake_hostent6(name, v6addr);
708 is_addr = TRUE;
709 break;
710 }
711
712 memset(&v4addr, 0, sizeof(struct in_addr));
713 if (inet_aton(name, &v4addr) == 1)
714 {
715 if (want == WANT_A4_ONLY)
716 {
717 free(request);
718 *error = HOST_NOT_FOUND;
719 return MACH_PORT_NULL;
720 }
721
722 v6addr.__u6_addr.__u6_addr32[0] = 0x00000000;
723 v6addr.__u6_addr.__u6_addr32[1] = 0x00000000;
724 v6addr.__u6_addr.__u6_addr32[2] = htonl(0x0000ffff);
725 memmove(&(v6addr.__u6_addr.__u6_addr32[3]), &(v4addr.s_addr), sizeof(struct in_addr));
726
727 /* return a fake hostent */
728 request->hent = fake_hostent6(name, v6addr);
729 is_addr = TRUE;
730 }
731 break;
732 }
733
734 default:
735 free(request);
736 *error = NO_RECOVERY;
737 return MACH_PORT_NULL;
738 }
739
740 if (is_addr)
741 {
742 /*
743 * queue reply message
744 */
745 if (sendCannedReply(request, error))
746 {
747 request_queue(request);
748 return request->replyPort;
749 }
750 else
751 {
752 freehostent(request->hent);
753 free(request);
754 return MACH_PORT_NULL;
755 }
756 }
757
758 request->request.dataLen = _LU_MAXLUSTRLEN + BYTES_PER_XDR_UNIT;
759 request->request.data = malloc(request->request.dataLen);
760
761 xdrmem_create(&outxdr, request->request.data, request->request.dataLen, XDR_ENCODE);
762 if (!xdr__lu_string(&outxdr, (_lu_string *)&name))
763 {
764 xdr_destroy(&outxdr);
765 free(request->request.data);
766 free(request);
767 *error = NO_RECOVERY;
768 return MACH_PORT_NULL;
769 }
770
771 request->request.dataLen = xdr_getpos(&outxdr) / BYTES_PER_XDR_UNIT;
772
773 /*
774 * allocate reply port, send query to lookupd
775 */
776 if (_lookup_all_tx(_lu_port, request->request.proc, request->request.data, request->request.dataLen, &request->replyPort) == KERN_SUCCESS)
777 {
778 request_queue(request);
779 mp = request->replyPort;
780 }
781 else
782 {
783 free(request->request.data);
784 free(request);
785 *error = NO_RECOVERY;
786 mp = MACH_PORT_NULL;
787 }
788
789 xdr_destroy(&outxdr);
790 return mp;
791 }
792
793 static boolean_t
794 _gethostbyname_async_handleReply(void *replyMsg, a_requests_t **requestP, struct hostent **he, int *error)
795 {
796 int count;
797 unsigned int datalen;
798 XDR inxdr;
799 ooline_data data;
800 mach_msg_header_t *msg = (mach_msg_header_t *)replyMsg;
801 a_requests_t *request;
802 kern_return_t status;
803 security_token_t token;
804 int want;
805
806 request = request_extract(msg->msgh_local_port);
807 if (!request)
808 {
809 /* excuse me, what happenned to the request info? */
810 return FALSE;
811 }
812
813 *requestP = request;
814 *he = NULL;
815 *error = 0;
816
817 if (request->hent)
818 {
819 /*
820 * if the reply was already available when the
821 * request was made
822 */
823 *he = request->hent;
824 return TRUE;
825 }
826
827 /* unpack the reply */
828 status = _lookup_all_rx(replyMsg, &data, &datalen, &token);
829 switch (status)
830 {
831 case KERN_SUCCESS:
832 break;
833
834 case MIG_SERVER_DIED:
835 if (--request->retry > 0)
836 {
837 /*
838 * retry the request
839 */
840 if (_lookup_all_tx(_lu_port, request->request.proc, request->request.data, request->request.dataLen, &request->replyPort) == KERN_SUCCESS)
841 {
842 request_queue(request);
843 return FALSE;
844 }
845 }
846 /* fall through */
847
848 default:
849 *error = HOST_NOT_FOUND;
850 return TRUE;
851 }
852
853 datalen *= BYTES_PER_XDR_UNIT;
854
855 if (token.val[0] != 0)
856 {
857 vm_deallocate(mach_task_self(), (vm_address_t)data, datalen);
858 *error = NO_RECOVERY;
859 return TRUE;
860 }
861
862 xdrmem_create(&inxdr, data, datalen, XDR_DECODE);
863
864 count = 0;
865 if (!xdr_int(&inxdr, &count))
866 {
867 xdr_destroy(&inxdr);
868 vm_deallocate(mach_task_self(), (vm_address_t)data, datalen);
869 *error = NO_RECOVERY;
870 return TRUE;
871 }
872
873 if (count == 0)
874 {
875 xdr_destroy(&inxdr);
876 vm_deallocate(mach_task_self(), (vm_address_t)data, datalen);
877 *error = HOST_NOT_FOUND;
878 return TRUE;
879 }
880
881 want = request->request.want;
882 if (want == WANT_A6_OR_MAPPED_A4_IF_NO_A6) want = WANT_A6_ONLY;
883
884 *he = extract_host(&inxdr, want, error);
885 xdr_destroy(&inxdr);
886 vm_deallocate(mach_task_self(), (vm_address_t)data, datalen);
887 return TRUE;
888 }
889
890 mach_port_t
891 gethostbyname_async_start(const char *name, gethostbyname_async_callback callout, void *context)
892 {
893 a_request_callout_t cb;
894 int error;
895 mach_port_t mp = MACH_PORT_NULL;
896
897 if (!_lu_running())
898 {
899 h_errno = NO_RECOVERY;
900 return MACH_PORT_NULL;
901 }
902
903 cb.hostName = callout;
904 mp = _gethostbyname_async_start(name, WANT_A4_ONLY, &error, cb, context);
905 if (mp == MACH_PORT_NULL)
906 {
907 h_errno = error;
908 }
909
910 return mp;
911 }
912
913 void
914 gethostbyname_async_cancel(mach_port_t port)
915 {
916 _async_cancel(port);
917 return;
918 }
919
920 void
921 gethostbyname_async_handleReply(void *replyMsg)
922 {
923 int error;
924 struct hostent *he;
925 a_requests_t *request;
926
927 if (_gethostbyname_async_handleReply(replyMsg, &request, &he, &error))
928 {
929 /* if we have an answer to provide */
930 h_errno = error;
931 (request->callout.hostAddr)(he, request->context);
932
933 (void)mach_port_mod_refs(mach_task_self(), request->replyPort, MACH_PORT_RIGHT_RECEIVE, -1);
934 if (request->request.data) free(request->request.data);
935 free(request);
936 if (he != NULL) freehostent(he);
937 }
938
939 return;
940 }
941
942 mach_port_t
943 getipnodebyname_async_start(const char *name, int af, int flags, int *error, getipnodebyname_async_callback callout, void *context)
944 {
945 a_request_callout_t cb;
946 int if4 = 0;
947 int if6 = 0;
948 mach_port_t mp = MACH_PORT_NULL;
949 int want = WANT_A4_ONLY;
950 struct ifaddrs *ifa, *ifap;
951
952 if (!_lu_running())
953 {
954 h_errno = NO_RECOVERY;
955 return MACH_PORT_NULL;
956 }
957
958 /*
959 * IF AI_ADDRCONFIG is set, we need to know what interface flavors we really have.
960 */
961 if (flags & AI_ADDRCONFIG)
962 {
963 if (getifaddrs(&ifa) < 0)
964 {
965 *error = NO_RECOVERY;
966 return MACH_PORT_NULL;
967 }
968
969 for (ifap = ifa; ifap != NULL; ifap = ifap->ifa_next)
970 {
971 if (ifap->ifa_addr == NULL) continue;
972 if ((ifap->ifa_flags & IFF_UP) == 0) continue;
973 if (ifap->ifa_addr->sa_family == AF_INET)
974 {
975 if4++;
976 }
977 else if (ifap->ifa_addr->sa_family == AF_INET6)
978 {
979 if6++;
980 }
981 }
982
983 freeifaddrs(ifa);
984
985 /* Bail out if there are no interfaces */
986 if ((if4 == 0) && (if6 == 0))
987 {
988 *error = NO_ADDRESS;
989 return MACH_PORT_NULL;
990 }
991 }
992
993 /*
994 * Figure out what we want.
995 * If user asked for AF_INET, we only want V4 addresses.
996 */
997 switch (af)
998 {
999 case AF_INET:
1000 {
1001 want = WANT_A4_ONLY;
1002 if ((flags & AI_ADDRCONFIG) && (if4 == 0))
1003 {
1004 *error = NO_ADDRESS;
1005 return MACH_PORT_NULL;
1006 }
1007 }
1008 break;
1009
1010 case AF_INET6:
1011 {
1012 want = WANT_A6_ONLY;
1013 if (flags & (AI_V4MAPPED|AI_V4MAPPED_CFG))
1014 {
1015 if (flags & AI_ALL)
1016 {
1017 want = WANT_A6_PLUS_MAPPED_A4;
1018 }
1019 else
1020 {
1021 want = WANT_A6_OR_MAPPED_A4_IF_NO_A6;
1022 }
1023 }
1024 else
1025 {
1026 if ((flags & AI_ADDRCONFIG) && (if6 == 0))
1027 {
1028 *error = NO_ADDRESS;
1029 return MACH_PORT_NULL;
1030 }
1031 }
1032 }
1033 break;
1034 }
1035
1036 cb.nodeName = callout;
1037 mp = _gethostbyname_async_start(name, want, &h_errno, cb, context);
1038 return mp;
1039 }
1040
1041 void
1042 getipnodebyname_async_cancel(mach_port_t port)
1043 {
1044 _async_cancel(port);
1045 return;
1046 }
1047
1048 void
1049 getipnodebyname_async_handleReply(void *replyMsg)
1050 {
1051 int error = 0;
1052 struct hostent *he = NULL;
1053 a_requests_t *request = NULL;
1054 static int proc4 = -1;
1055
1056 if (_gethostbyname_async_handleReply(replyMsg, &request, &he, &error))
1057 {
1058 /*
1059 * we have an answer to provide
1060 */
1061 if ((he == NULL) && (error == HOST_NOT_FOUND) && ((request->request.want == WANT_A6_PLUS_MAPPED_A4) || (request->request.want == WANT_A6_OR_MAPPED_A4_IF_NO_A6)))
1062 {
1063 /*
1064 * no host found (yet), if requested we send a
1065 * followup query to lookupd.
1066 */
1067 if (proc4 < 0)
1068 {
1069 if (_lookup_link(_lu_port, "gethostbyname", &proc4) != KERN_SUCCESS)
1070 {
1071 error = NO_RECOVERY;
1072 goto answer;
1073 }
1074 }
1075
1076 request->request.proc = proc4;
1077 request->request.want = WANT_MAPPED_A4_ONLY;
1078 if (_lookup_all_tx(_lu_port, request->request.proc, request->request.data, request->request.dataLen, &request->replyPort) == KERN_SUCCESS)
1079 {
1080 request_queue(request);
1081 return;
1082 }
1083 else
1084 {
1085 error = NO_RECOVERY;
1086 }
1087 }
1088
1089 answer:
1090 (request->callout.nodeName)(he, error, request->context);
1091 (void)mach_port_mod_refs(mach_task_self(), request->replyPort, MACH_PORT_RIGHT_RECEIVE, -1);
1092 if (request->request.data != NULL) free(request->request.data);
1093 free(request);
1094 /*
1095 * Note: it is up to the callback function to call
1096 * freehostent().
1097 */
1098 }
1099
1100 return;
1101 }