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