Libinfo-173.1.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 NULL;
378 }
379
380 v4addr = malloc(len);
381 memmove(v4addr, addr, len);
382 v4addr->s_addr = htonl(v4addr->s_addr);
383
384 address = (void *)v4addr;
385 proc = proc4;
386 break;
387 }
388
389 case AF_INET6:
390 {
391 if (proc6 < 0)
392 {
393 if (_lookup_link(_lu_port, "getipv6nodebyaddr", &proc6) != KERN_SUCCESS)
394 {
395 *error = NO_RECOVERY;
396 return MACH_PORT_NULL;
397 }
398 }
399
400 if (len != sizeof(struct in6_addr))
401 {
402 *error = NO_RECOVERY;
403 return NULL;
404 }
405
406 v6addr = malloc(len);
407 memmove(v6addr, addr, len);
408 v6addr->__u6_addr.__u6_addr32[0] = htonl(v6addr->__u6_addr.__u6_addr32[0]);
409 v6addr->__u6_addr.__u6_addr32[1] = htonl(v6addr->__u6_addr.__u6_addr32[1]);
410 v6addr->__u6_addr.__u6_addr32[2] = htonl(v6addr->__u6_addr.__u6_addr32[2]);
411 v6addr->__u6_addr.__u6_addr32[3] = htonl(v6addr->__u6_addr.__u6_addr32[3]);
412
413 address = (void *)v6addr;
414 proc = proc6;
415 break;
416 }
417
418 default:
419 *error = NO_RECOVERY;
420 return MACH_PORT_NULL;
421 }
422
423 request = malloc(sizeof(a_requests_t));
424 request->next = NULL;
425 request->retry = MAX_LOOKUP_ATTEMPTS;
426 request->request.proc = proc;
427 request->request.data = (ooline_data)address;
428 request->request.dataLen = len / BYTES_PER_XDR_UNIT;
429 request->request.want = want;
430 request->replyPort = MACH_PORT_NULL;
431 request->callout = callout;
432 request->context = context;
433 request->hent = NULL;
434
435 /*
436 * allocate reply port, send query to lookupd
437 */
438 if (_lookup_all_tx(_lu_port, request->request.proc, request->request.data, request->request.dataLen, &request->replyPort) == KERN_SUCCESS)
439 {
440 request_queue(request);
441 }
442 else
443 {
444 if (request->request.data) free(request->request.data);
445 free(request);
446 *error = NO_RECOVERY;
447 return MACH_PORT_NULL;
448 }
449
450 return request->replyPort;
451 }
452
453 static boolean_t
454 _gethostbyaddr_async_handleReply(void *replyMsg, a_requests_t **requestP, struct hostent **he, int *error)
455 {
456 int count;
457 ooline_data data;
458 unsigned int datalen;
459 XDR inxdr;
460 mach_msg_header_t *msg = (mach_msg_header_t *)replyMsg;
461 a_requests_t *request;
462 kern_return_t status;
463 security_token_t token;
464
465 request = request_extract(msg->msgh_local_port);
466 if (!request)
467 {
468 /* excuse me, what happenned to the request info? */
469 return FALSE;
470 }
471
472 *requestP = request;
473 *he = NULL;
474 *error = 0;
475
476 /* unpack the reply */
477 status = _lookup_all_rx(replyMsg, &data, &datalen, &token);
478 switch (status)
479 {
480 case KERN_SUCCESS:
481 break;
482
483 case MIG_SERVER_DIED:
484 if (--request->retry > 0)
485 {
486 /* retry the request */
487 if (_lookup_all_tx(_lu_port, request->request.proc, request->request.data, request->request.dataLen, &request->replyPort) == KERN_SUCCESS)
488 {
489 request_queue(request);
490 return FALSE;
491 }
492 }
493 /* fall through */
494
495 default:
496 *error = HOST_NOT_FOUND;
497 return TRUE;
498 }
499
500 datalen *= BYTES_PER_XDR_UNIT;
501
502 if (token.val[0] != 0)
503 {
504 vm_deallocate(mach_task_self(), (vm_address_t)data, datalen);
505 *error = NO_RECOVERY;
506 return TRUE;
507 }
508
509 xdrmem_create(&inxdr, data, datalen, XDR_DECODE);
510
511 count = 0;
512 if (!xdr_int(&inxdr, &count))
513 {
514 xdr_destroy(&inxdr);
515 vm_deallocate(mach_task_self(), (vm_address_t)data, datalen);
516 *error = NO_RECOVERY;
517 return TRUE;
518 }
519
520 if (count == 0)
521 {
522 xdr_destroy(&inxdr);
523 vm_deallocate(mach_task_self(), (vm_address_t)data, datalen);
524 *error = HOST_NOT_FOUND;
525 *he = NULL;
526 return TRUE;
527 }
528
529 *he = extract_host(&inxdr, request->request.want, error);
530 xdr_destroy(&inxdr);
531 vm_deallocate(mach_task_self(), (vm_address_t)data, datalen);
532 return TRUE;
533 }
534
535 mach_port_t
536 gethostbyaddr_async_start(const char *addr, int len, int type, gethostbyaddr_async_callback callout, void *context)
537 {
538 a_request_callout_t cb;
539 mach_port_t mp;
540
541 if (!_lu_running())
542 {
543 h_errno = NO_RECOVERY;
544 return MACH_PORT_NULL;
545 }
546
547 cb.hostAddr = callout;
548 mp = _gethostbyaddr_async_start(addr, len, type, cb, context, &h_errno);
549 return mp;
550 }
551
552 void
553 gethostbyaddr_async_cancel(mach_port_t port)
554 {
555 _async_cancel(port);
556 return;
557 }
558
559 void
560 gethostbyaddr_async_handleReply(void *replyMsg)
561 {
562 int error = 0;
563 struct hostent *he = NULL;
564 a_requests_t *request = NULL;
565
566 if (_gethostbyaddr_async_handleReply(replyMsg, &request, &he, &error))
567 {
568 /* if we have an answer to provide */
569 h_errno = error;
570 (request->callout.hostAddr)(he, request->context);
571
572 (void)mach_port_mod_refs(mach_task_self(), request->replyPort, MACH_PORT_RIGHT_RECEIVE, -1);
573 if (request->request.data) free(request->request.data);
574 free(request);
575 if (he != NULL) freehostent(he);
576 }
577
578 return;
579 }
580
581 mach_port_t
582 getipnodebyaddr_async_start(const void *addr, size_t len, int af, int *error, getipnodebyaddr_async_callback callout, void *context)
583 {
584 a_request_callout_t cb;
585 mach_port_t mp;
586
587 if (!_lu_running())
588 {
589 *error = NO_RECOVERY;
590 return MACH_PORT_NULL;
591 }
592
593 cb.nodeAddr = callout;
594 mp = _gethostbyaddr_async_start(addr, len, af, cb, context, error);
595 return mp;
596 }
597
598 void
599 getipnodebyaddr_async_cancel(mach_port_t port)
600 {
601 _async_cancel(port);
602 return;
603 }
604
605 void
606 getipnodebyaddr_async_handleReply(void *replyMsg)
607 {
608 int error = 0;
609 struct hostent *he = NULL;
610 a_requests_t *request = NULL;
611
612 if (_gethostbyaddr_async_handleReply(replyMsg, &request, &he, &error))
613 {
614 /* if we have an answer to provide */
615 (request->callout.nodeAddr)(he, error, request->context);
616
617 (void)mach_port_mod_refs(mach_task_self(), request->replyPort, MACH_PORT_RIGHT_RECEIVE, -1);
618 if (request->request.data) free(request->request.data);
619 free(request);
620 /*
621 * Note: it is up to the callback function to call
622 * freehostent().
623 */
624 }
625
626 return;
627 }
628
629 static mach_port_t
630 _gethostbyname_async_start(const char *name, int want, int *error, a_request_callout_t callout, void *context)
631 {
632 int af;
633 boolean_t is_addr = FALSE;
634 mach_port_t mp = MACH_PORT_NULL;
635 XDR outxdr;
636 static int proc;
637 a_requests_t *request;
638 static int proc4 = -1;
639 static int proc6 = -1;
640 struct in_addr v4addr;
641 struct in6_addr v6addr;
642
643 if ((name == NULL) || (name[0] == '\0'))
644 {
645 *error = NO_DATA;
646 return MACH_PORT_NULL;
647 }
648
649 af = (want == WANT_A4_ONLY) ? AF_INET: AF_INET6;
650
651 if ((af == AF_INET) || (want == WANT_MAPPED_A4_ONLY))
652 {
653 if (proc4 < 0)
654 {
655 if (_lookup_link(_lu_port, "gethostbyname", &proc4) != KERN_SUCCESS)
656 {
657 *error = NO_RECOVERY;
658 return MACH_PORT_NULL;
659 }
660 }
661 proc = proc4;
662 }
663 else /* if (af == AF_INET6) */
664 {
665 if (proc6 < 0)
666 {
667 if (_lookup_link(_lu_port, "getipv6nodebyname", &proc6) != KERN_SUCCESS)
668 {
669 *error = NO_RECOVERY;
670 return MACH_PORT_NULL;
671 }
672 }
673 proc = proc6;
674 }
675
676 request = malloc(sizeof(a_requests_t));
677 request->next = NULL;
678 request->retry = MAX_LOOKUP_ATTEMPTS;
679 request->request.proc = proc;
680 request->request.data = NULL;
681 request->request.dataLen = 0;
682 request->request.want = want;
683 request->replyPort = MACH_PORT_NULL;
684 request->callout = callout;
685 request->context = context;
686 request->hent = NULL;
687
688 switch (af)
689 {
690 case AF_INET:
691 {
692 memset(&v4addr, 0, sizeof(struct in_addr));
693 if (inet_aton(name, &v4addr) == 1)
694 {
695 /* return a fake hostent */
696 request->hent = fake_hostent(name, v4addr);
697 is_addr = TRUE;
698 }
699 break;
700 }
701
702 case AF_INET6:
703 {
704 memset(&v6addr, 0, sizeof(struct in6_addr));
705 if (inet_pton(af, name, &v6addr) == 1)
706 {
707 /* return a fake hostent */
708 request->hent = fake_hostent6(name, v6addr);
709 is_addr = TRUE;
710 break;
711 }
712
713 memset(&v4addr, 0, sizeof(struct in_addr));
714 if (inet_aton(name, &v4addr) == 1)
715 {
716 if (want == WANT_A4_ONLY)
717 {
718 free(request);
719 *error = HOST_NOT_FOUND;
720 return MACH_PORT_NULL;
721 }
722
723 v6addr.__u6_addr.__u6_addr32[0] = 0x00000000;
724 v6addr.__u6_addr.__u6_addr32[1] = 0x00000000;
725 v6addr.__u6_addr.__u6_addr32[2] = htonl(0x0000ffff);
726 memmove(&(v6addr.__u6_addr.__u6_addr32[3]), &(v4addr.s_addr), sizeof(struct in_addr));
727
728 /* return a fake hostent */
729 request->hent = fake_hostent6(name, v6addr);
730 is_addr = TRUE;
731 }
732 break;
733 }
734
735 default:
736 free(request);
737 *error = NO_RECOVERY;
738 return MACH_PORT_NULL;
739 }
740
741 if (is_addr)
742 {
743 /*
744 * queue reply message
745 */
746 if (sendCannedReply(request, error))
747 {
748 request_queue(request);
749 return request->replyPort;
750 }
751 else
752 {
753 freehostent(request->hent);
754 free(request);
755 return MACH_PORT_NULL;
756 }
757 }
758
759 request->request.dataLen = _LU_MAXLUSTRLEN + BYTES_PER_XDR_UNIT;
760 request->request.data = malloc(request->request.dataLen);
761
762 xdrmem_create(&outxdr, request->request.data, request->request.dataLen, XDR_ENCODE);
763 if (!xdr__lu_string(&outxdr, (_lu_string *)&name))
764 {
765 xdr_destroy(&outxdr);
766 free(request->request.data);
767 free(request);
768 *error = NO_RECOVERY;
769 return MACH_PORT_NULL;
770 }
771
772 request->request.dataLen = xdr_getpos(&outxdr) / BYTES_PER_XDR_UNIT;
773
774 /*
775 * allocate reply port, send query to lookupd
776 */
777 if (_lookup_all_tx(_lu_port, request->request.proc, request->request.data, request->request.dataLen, &request->replyPort) == KERN_SUCCESS)
778 {
779 request_queue(request);
780 mp = request->replyPort;
781 }
782 else
783 {
784 free(request->request.data);
785 free(request);
786 *error = NO_RECOVERY;
787 mp = NULL;
788 }
789
790 xdr_destroy(&outxdr);
791 return mp;
792 }
793
794 static boolean_t
795 _gethostbyname_async_handleReply(void *replyMsg, a_requests_t **requestP, struct hostent **he, int *error)
796 {
797 int count;
798 unsigned int datalen;
799 XDR inxdr;
800 ooline_data data;
801 mach_msg_header_t *msg = (mach_msg_header_t *)replyMsg;
802 a_requests_t *request;
803 kern_return_t status;
804 security_token_t token;
805 int want;
806
807 request = request_extract(msg->msgh_local_port);
808 if (!request)
809 {
810 /* excuse me, what happenned to the request info? */
811 return FALSE;
812 }
813
814 *requestP = request;
815 *he = NULL;
816 *error = 0;
817
818 if (request->hent)
819 {
820 /*
821 * if the reply was already available when the
822 * request was made
823 */
824 *he = request->hent;
825 return TRUE;
826 }
827
828 /* unpack the reply */
829 status = _lookup_all_rx(replyMsg, &data, &datalen, &token);
830 switch (status)
831 {
832 case KERN_SUCCESS:
833 break;
834
835 case MIG_SERVER_DIED:
836 if (--request->retry > 0)
837 {
838 /*
839 * retry the request
840 */
841 if (_lookup_all_tx(_lu_port, request->request.proc, request->request.data, request->request.dataLen, &request->replyPort) == KERN_SUCCESS)
842 {
843 request_queue(request);
844 return FALSE;
845 }
846 }
847 /* fall through */
848
849 default:
850 *error = HOST_NOT_FOUND;
851 return TRUE;
852 }
853
854 datalen *= BYTES_PER_XDR_UNIT;
855
856 if (token.val[0] != 0)
857 {
858 vm_deallocate(mach_task_self(), (vm_address_t)data, datalen);
859 *error = NO_RECOVERY;
860 return TRUE;
861 }
862
863 xdrmem_create(&inxdr, data, datalen, XDR_DECODE);
864
865 count = 0;
866 if (!xdr_int(&inxdr, &count))
867 {
868 xdr_destroy(&inxdr);
869 vm_deallocate(mach_task_self(), (vm_address_t)data, datalen);
870 *error = NO_RECOVERY;
871 return TRUE;
872 }
873
874 if (count == 0)
875 {
876 xdr_destroy(&inxdr);
877 vm_deallocate(mach_task_self(), (vm_address_t)data, datalen);
878 *error = HOST_NOT_FOUND;
879 return TRUE;
880 }
881
882 want = request->request.want;
883 if (want == WANT_A6_OR_MAPPED_A4_IF_NO_A6) want = WANT_A6_ONLY;
884
885 *he = extract_host(&inxdr, want, error);
886 xdr_destroy(&inxdr);
887 vm_deallocate(mach_task_self(), (vm_address_t)data, datalen);
888 return TRUE;
889 }
890
891 mach_port_t
892 gethostbyname_async_start(const char *name, gethostbyname_async_callback callout, void *context)
893 {
894 a_request_callout_t cb;
895 int error;
896 mach_port_t mp = MACH_PORT_NULL;
897
898 if (!_lu_running())
899 {
900 h_errno = NO_RECOVERY;
901 return MACH_PORT_NULL;
902 }
903
904 cb.hostName = callout;
905 mp = _gethostbyname_async_start(name, WANT_A4_ONLY, &error, cb, context);
906 if (mp == MACH_PORT_NULL)
907 {
908 h_errno = error;
909 }
910
911 return mp;
912 }
913
914 void
915 gethostbyname_async_cancel(mach_port_t port)
916 {
917 _async_cancel(port);
918 return;
919 }
920
921 void
922 gethostbyname_async_handleReply(void *replyMsg)
923 {
924 int error;
925 struct hostent *he;
926 a_requests_t *request;
927
928 if (_gethostbyname_async_handleReply(replyMsg, &request, &he, &error))
929 {
930 /* if we have an answer to provide */
931 h_errno = error;
932 (request->callout.hostAddr)(he, request->context);
933
934 (void)mach_port_mod_refs(mach_task_self(), request->replyPort, MACH_PORT_RIGHT_RECEIVE, -1);
935 if (request->request.data) free(request->request.data);
936 free(request);
937 if (he != NULL) freehostent(he);
938 }
939
940 return;
941 }
942
943 mach_port_t
944 getipnodebyname_async_start(const char *name, int af, int flags, int *error, getipnodebyname_async_callback callout, void *context)
945 {
946 a_request_callout_t cb;
947 int if4 = 0;
948 int if6 = 0;
949 mach_port_t mp = MACH_PORT_NULL;
950 int want = WANT_A4_ONLY;
951 struct ifaddrs *ifa, *ifap;
952
953 if (!_lu_running())
954 {
955 h_errno = NO_RECOVERY;
956 return MACH_PORT_NULL;
957 }
958
959 /*
960 * IF AI_ADDRCONFIG is set, we need to know what interface flavors we really have.
961 */
962 if (flags & AI_ADDRCONFIG)
963 {
964 if (getifaddrs(&ifa) < 0)
965 {
966 *error = NO_RECOVERY;
967 return MACH_PORT_NULL;
968 }
969
970 for (ifap = ifa; ifap != NULL; ifap = ifap->ifa_next)
971 {
972 if (ifap->ifa_addr == NULL) continue;
973 if ((ifap->ifa_flags & IFF_UP) == 0) continue;
974 if (ifap->ifa_addr->sa_family == AF_INET)
975 {
976 if4++;
977 }
978 else if (ifap->ifa_addr->sa_family == AF_INET6)
979 {
980 if6++;
981 }
982 }
983
984 freeifaddrs(ifa);
985
986 /* Bail out if there are no interfaces */
987 if ((if4 == 0) && (if6 == 0))
988 {
989 *error = NO_ADDRESS;
990 return MACH_PORT_NULL;
991 }
992 }
993
994 /*
995 * Figure out what we want.
996 * If user asked for AF_INET, we only want V4 addresses.
997 */
998 switch (af)
999 {
1000 case AF_INET:
1001 {
1002 want = WANT_A4_ONLY;
1003 if ((flags & AI_ADDRCONFIG) && (if4 == 0))
1004 {
1005 *error = NO_ADDRESS;
1006 return MACH_PORT_NULL;
1007 }
1008 }
1009 break;
1010
1011 case AF_INET6:
1012 {
1013 want = WANT_A6_ONLY;
1014 if (flags & (AI_V4MAPPED|AI_V4MAPPED_CFG))
1015 {
1016 if (flags & AI_ALL)
1017 {
1018 want = WANT_A6_PLUS_MAPPED_A4;
1019 }
1020 else
1021 {
1022 want = WANT_A6_OR_MAPPED_A4_IF_NO_A6;
1023 }
1024 }
1025 else
1026 {
1027 if ((flags & AI_ADDRCONFIG) && (if6 == 0))
1028 {
1029 *error = NO_ADDRESS;
1030 return MACH_PORT_NULL;
1031 }
1032 }
1033 }
1034 break;
1035 }
1036
1037 cb.nodeName = callout;
1038 mp = _gethostbyname_async_start(name, want, &h_errno, cb, context);
1039 return mp;
1040 }
1041
1042 void
1043 getipnodebyname_async_cancel(mach_port_t port)
1044 {
1045 _async_cancel(port);
1046 return;
1047 }
1048
1049 void
1050 getipnodebyname_async_handleReply(void *replyMsg)
1051 {
1052 int error = 0;
1053 struct hostent *he = NULL;
1054 a_requests_t *request = NULL;
1055 static int proc4 = -1;
1056
1057 if (_gethostbyname_async_handleReply(replyMsg, &request, &he, &error))
1058 {
1059 /*
1060 * we have an answer to provide
1061 */
1062 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)))
1063 {
1064 /*
1065 * no host found (yet), if requested we send a
1066 * followup query to lookupd.
1067 */
1068 if (proc4 < 0)
1069 {
1070 if (_lookup_link(_lu_port, "gethostbyname", &proc4) != KERN_SUCCESS)
1071 {
1072 error = NO_RECOVERY;
1073 goto answer;
1074 }
1075 }
1076
1077 request->request.proc = proc4;
1078 request->request.want = WANT_MAPPED_A4_ONLY;
1079 if (_lookup_all_tx(_lu_port, request->request.proc, request->request.data, request->request.dataLen, &request->replyPort) == KERN_SUCCESS)
1080 {
1081 request_queue(request);
1082 return;
1083 }
1084 else
1085 {
1086 error = NO_RECOVERY;
1087 }
1088 }
1089
1090 answer:
1091 (request->callout.nodeName)(he, error, request->context);
1092 (void)mach_port_mod_refs(mach_task_self(), request->replyPort, MACH_PORT_RIGHT_RECEIVE, -1);
1093 if (request->request.data != NULL) free(request->request.data);
1094 free(request);
1095 /*
1096 * Note: it is up to the callback function to call
1097 * freehostent().
1098 */
1099 }
1100
1101 return;
1102 }