2 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Portions Copyright (c) 1999 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
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
22 * @APPLE_LICENSE_HEADER_END@
26 #include <sys/types.h>
29 #include <sys/socket.h>
31 #include <netinet/in.h>
32 #include <arpa/inet.h>
38 #include <rpc/types.h>
40 #include <mach/mach.h>
41 #include <servers/bootstrap.h>
44 #include "netdb_async.h"
47 #define IPPROTO_UNSPEC 0
49 #define LONG_STRING_LENGTH 8192
50 #define _LU_MAXLUSTRLEN 256
51 #define LU_QBUF_SIZE 8192
53 #define MAX_LOOKUP_ATTEMPTS 10
55 #define INET_NTOP_AF_INET_OFFSET 4
56 #define INET_NTOP_AF_INET6_OFFSET 8
58 extern mach_port_t
_lookupd_port();
60 static int gai_proc
= -1;
61 static int gni_proc
= -1;
63 static const int32_t supported_family
[] =
69 static const int32_t supported_family_count
= (sizeof(supported_family
) / sizeof(supported_family
[0]));
71 static const int32_t supported_socket
[] =
78 static const int32_t supported_socket_count
= (sizeof(supported_socket
) / sizeof(supported_socket
[0]));
80 static const int32_t supported_protocol
[] =
88 static const int32_t supported_protocol_count
= (sizeof(supported_protocol
) / sizeof(supported_protocol
[0]));
90 static const int32_t supported_socket_protocol_pair
[] =
92 SOCK_RAW
, IPPROTO_UNSPEC
,
93 SOCK_RAW
, IPPROTO_UDP
,
94 SOCK_RAW
, IPPROTO_TCP
,
95 SOCK_RAW
, IPPROTO_ICMP
,
96 SOCK_RAW
, IPPROTO_ICMPV6
,
97 SOCK_UNSPEC
, IPPROTO_UNSPEC
,
98 SOCK_UNSPEC
, IPPROTO_UDP
,
99 SOCK_UNSPEC
, IPPROTO_TCP
,
100 SOCK_UNSPEC
, IPPROTO_ICMP
,
101 SOCK_UNSPEC
, IPPROTO_ICMPV6
,
102 SOCK_DGRAM
, IPPROTO_UNSPEC
,
103 SOCK_DGRAM
, IPPROTO_UDP
,
104 SOCK_STREAM
, IPPROTO_UNSPEC
,
105 SOCK_STREAM
, IPPROTO_TCP
107 static const int32_t supported_socket_protocol_pair_count
= (sizeof(supported_socket_protocol_pair
) / (sizeof(supported_socket_protocol_pair
[0]) * 2));
110 gai_family_type_check(int32_t f
)
114 for (i
= 0; i
< supported_family_count
; i
++)
116 if (f
== supported_family
[i
]) return 0;
123 gai_socket_type_check(int32_t s
)
127 for (i
= 0; i
< supported_socket_count
; i
++)
129 if (s
== supported_socket
[i
]) return 0;
136 gai_protocol_type_check(int32_t p
)
140 for (i
= 0; i
< supported_protocol_count
; i
++)
142 if (p
== supported_protocol
[i
]) return 0;
149 gai_socket_protocol_type_check(int32_t s
, int32_t p
)
151 int32_t i
, j
, ss
, sp
;
153 for (i
= 0, j
= 0; i
< supported_socket_protocol_pair_count
; i
++, j
+=2)
155 ss
= supported_socket_protocol_pair
[j
];
156 sp
= supported_socket_protocol_pair
[j
+1];
157 if ((s
== ss
) && (p
== sp
)) return 0;
164 gai_strerror(int32_t err
)
168 case EAI_ADDRFAMILY
: return "Address family for nodename not supported";
169 case EAI_AGAIN
: return "Temporary failure in name resolution";
170 case EAI_BADFLAGS
: return "Invalid value for ai_flags";
171 case EAI_FAIL
: return "Non-recoverable failure in name resolution";
172 case EAI_FAMILY
: return "ai_family not supported";
173 case EAI_MEMORY
: return "Memory allocation failure";
174 case EAI_NODATA
: return "No address associated with nodename";
175 case EAI_NONAME
: return "nodename nor servname provided, or not known";
176 case EAI_SERVICE
: return "servname not supported for ai_socktype";
177 case EAI_SOCKTYPE
: return "ai_socktype not supported";
178 case EAI_SYSTEM
: return "System error";
179 case EAI_BADHINTS
: return "Bad hints";
180 case EAI_PROTOCOL
: return "ai_protocol not supported";
183 return "Unknown error";
187 append_addrinfo(struct addrinfo
**l
, struct addrinfo
*a
)
191 if (l
== NULL
) return;
192 if (a
== NULL
) return;
202 if (a
->ai_family
== PF_INET6
)
204 if (x
->ai_family
== PF_INET
)
211 while ((x
->ai_next
!= NULL
) && (x
->ai_next
->ai_family
!= PF_INET
)) x
= x
->ai_next
;
212 a
->ai_next
= x
->ai_next
;
217 while (x
->ai_next
!= NULL
) x
= x
->ai_next
;
224 encode_kv(XDR
*x
, const char *k
, const char *v
)
228 if (!xdr_string(x
, (char **)&k
, _LU_MAXLUSTRLEN
)) return 1;
229 if (!xdr_int(x
, &n
)) return 1;
230 if (!xdr_string(x
, (char **)&v
, _LU_MAXLUSTRLEN
)) return 1;
236 freeaddrinfo(struct addrinfo
*a
)
238 struct addrinfo
*next
;
243 if (a
->ai_addr
!= NULL
) free(a
->ai_addr
);
244 if (a
->ai_canonname
!= NULL
) free(a
->ai_canonname
);
250 static struct addrinfo
*
251 new_addrinfo_v4(int32_t flags
, int32_t sock
, int32_t proto
, uint16_t port
, struct in_addr addr
, uint32_t iface
, char *cname
)
254 struct sockaddr_in
*sa
;
257 a
= (struct addrinfo
*)calloc(1, sizeof(struct addrinfo
));
258 if (a
== NULL
) return NULL
;
263 a
->ai_family
= PF_INET
;
264 a
->ai_socktype
= sock
;
265 a
->ai_protocol
= proto
;
267 a
->ai_addrlen
= sizeof(struct sockaddr_in
);
269 sa
= (struct sockaddr_in
*)calloc(1, a
->ai_addrlen
);
276 sa
->sin_len
= a
->ai_addrlen
;
277 sa
->sin_family
= PF_INET
;
278 sa
->sin_port
= htons(port
);
281 /* Kludge: Jam the interface number into sin_zero. */
282 memmove(sa
->sin_zero
, &iface
, sizeof(uint32_t));
284 a
->ai_addr
= (struct sockaddr
*)sa
;
288 len
= strlen(cname
) + 1;
289 a
->ai_canonname
= malloc(len
);
290 memmove(a
->ai_canonname
, cname
, len
);
296 static struct addrinfo
*
297 new_addrinfo_v6(int32_t flags
, int32_t sock
, int32_t proto
, uint16_t port
, struct in6_addr addr
, uint32_t scopeid
, char *cname
)
300 struct sockaddr_in6
*sa
;
303 a
= (struct addrinfo
*)calloc(1, sizeof(struct addrinfo
));
304 if (a
== NULL
) return NULL
;
309 a
->ai_family
= PF_INET6
;
310 a
->ai_socktype
= sock
;
311 a
->ai_protocol
= proto
;
313 a
->ai_addrlen
= sizeof(struct sockaddr_in6
);
315 sa
= (struct sockaddr_in6
*)calloc(1, a
->ai_addrlen
);
322 sa
->sin6_len
= a
->ai_addrlen
;
323 sa
->sin6_family
= PF_INET6
;
324 sa
->sin6_port
= htons(port
);
325 sa
->sin6_addr
= addr
;
326 sa
->sin6_scope_id
= scopeid
;
327 a
->ai_addr
= (struct sockaddr
*)sa
;
331 len
= strlen(cname
) + 1;
332 a
->ai_canonname
= malloc(len
);
333 memmove(a
->ai_canonname
, cname
, len
);
340 * getaddrinfo support in lookupd
341 * Input dict may contain the following
345 * protocol: [IPPROTO_UNSPEC] | IPPROTO_UDP | IPPROTO_TCP
346 * socktype: [SOCK_UNSPEC] | SOCK_DGRAM | SOCK_STREAM
347 * family: [PF_UNSPEC] | PF_INET | PF_INET6
350 * numerichost: [0] | 1
352 * Output dictionary may contain the following
353 * All values are encoded as strings.
355 * flags: unsigned long
356 * family: unsigned long
357 * socktype: unsigned long
358 * protocol: unsigned long
359 * port: unsigned long
361 * scopeid: unsigned long
366 static struct addrinfo
*
367 gai_lookupd_process_dictionary(XDR
*inxdr
)
369 int32_t i
, nkeys
, nvals
;
371 uint32_t flags
, family
, socktype
, protocol
, longport
, scopeid
;
373 char *addr
, *canonname
;
380 socktype
= SOCK_UNSPEC
;
381 protocol
= IPPROTO_UNSPEC
;
387 if (!xdr_int(inxdr
, &nkeys
)) return NULL
;
389 for (i
= 0; i
< nkeys
; i
++)
394 if (!xdr_string(inxdr
, &key
, LONG_STRING_LENGTH
)) return NULL
;
395 if (!xdr_int(inxdr
, &nvals
))
407 if (!xdr_string(inxdr
, &val
, LONG_STRING_LENGTH
))
413 if (!strcmp(key
, "flags"))
417 else if (!strcmp(key
, "family"))
421 else if (!strcmp(key
, "socktype"))
423 socktype
= atoi(val
);
425 else if (!strcmp(key
, "protocol"))
427 protocol
= atoi(val
);
429 else if (!strcmp(key
, "port"))
431 longport
= atoi(val
);
434 else if (!strcmp(key
, "scopeid"))
438 else if (!strcmp(key
, "address")) addr
= strdup(val
);
439 else if (!strcmp(key
, "canonname")) canonname
= strdup(val
);
444 if (family
== PF_UNSPEC
)
446 if (addr
!= NULL
) free(addr
);
447 if (canonname
!= NULL
) free(canonname
);
452 if (family
== PF_INET
)
454 inet_aton(addr
, &a4
);
455 a
= new_addrinfo_v4(flags
, socktype
, protocol
, port
, a4
, scopeid
, canonname
);
457 else if (family
== PF_INET6
)
459 inet_pton(AF_INET6
, addr
, &a6
);
460 a
= new_addrinfo_v6(flags
, socktype
, protocol
, port
, a6
, scopeid
, canonname
);
463 if (addr
!= NULL
) free(addr
);
464 if (canonname
!= NULL
) free(canonname
);
470 gai_make_query(const char *nodename
, const char *servname
, const struct addrinfo
*hints
, char *buf
, uint32_t *len
)
472 int32_t numerichost
, family
, proto
, socktype
, canonname
, passive
, parallel
;
475 char str
[64], *cname
;
479 proto
= IPPROTO_UNSPEC
;
480 socktype
= SOCK_UNSPEC
;
488 family
= hints
->ai_family
;
489 if (hints
->ai_flags
& AI_NUMERICHOST
) numerichost
= 1;
490 if (hints
->ai_flags
& AI_CANONNAME
) canonname
= 1;
491 if (hints
->ai_flags
& AI_PASSIVE
) passive
= 1;
492 if (hints
->ai_flags
& AI_PARALLEL
) parallel
= 1;
494 proto
= hints
->ai_protocol
;
495 if (hints
->ai_socktype
== SOCK_DGRAM
)
497 socktype
= SOCK_DGRAM
;
500 if (hints
->ai_socktype
== SOCK_STREAM
)
502 socktype
= SOCK_STREAM
;
507 xdrmem_create(&outxdr
, buf
, *len
, XDR_ENCODE
);
509 /* Attribute count */
511 if (nodename
!= NULL
) na
++;
512 if (servname
!= NULL
) na
++;
513 if (proto
!= IPPROTO_UNSPEC
) na
++;
514 if (socktype
!= SOCK_UNSPEC
) na
++;
515 if (family
!= PF_UNSPEC
) na
++;
516 if (canonname
!= 0) na
++;
517 if (passive
!= 0) na
++;
518 if (parallel
!= 0) na
++;
519 if (numerichost
!= 0) na
++;
521 if (!xdr_int(&outxdr
, (int32_t *)&na
))
523 xdr_destroy(&outxdr
);
528 if (nodename
!= NULL
)
530 if (encode_kv(&outxdr
, "name", nodename
) != 0)
532 xdr_destroy(&outxdr
);
538 if (servname
!= NULL
)
540 if (encode_kv(&outxdr
, "service", servname
) != 0)
542 xdr_destroy(&outxdr
);
548 if (proto
!= IPPROTO_UNSPEC
)
550 snprintf(str
, 64, "%u", proto
);
551 if (encode_kv(&outxdr
, "protocol", str
) != 0)
553 xdr_destroy(&outxdr
);
559 if (socktype
!= SOCK_UNSPEC
)
561 snprintf(str
, 64, "%u", socktype
);
562 if (encode_kv(&outxdr
, "socktype", str
) != 0)
564 xdr_destroy(&outxdr
);
570 if (family
!= PF_UNSPEC
)
572 snprintf(str
, 64, "%u", family
);
573 if (encode_kv(&outxdr
, "family", str
) != 0)
575 xdr_destroy(&outxdr
);
583 if (encode_kv(&outxdr
, "canonname", "1") != 0)
585 xdr_destroy(&outxdr
);
593 if (encode_kv(&outxdr
, "passive", "1") != 0)
595 xdr_destroy(&outxdr
);
603 if (encode_kv(&outxdr
, "parallel", "1") != 0)
605 xdr_destroy(&outxdr
);
611 if (numerichost
!= 0)
613 if (encode_kv(&outxdr
, "numerichost", "1") != 0)
615 xdr_destroy(&outxdr
);
621 *len
= xdr_getpos(&outxdr
);
623 xdr_destroy(&outxdr
);
629 is_a_number(const char *s
)
633 if (s
== NULL
) return 0;
636 for (i
= 0; i
< len
; i
++)
638 if (isdigit(s
[i
]) == 0) return 0;
645 gai_trivial(struct in_addr
*in4
, struct in6_addr
*in6
, int16_t port
, const struct addrinfo
*hints
, struct addrinfo
**res
)
647 int32_t family
, wantv4
, wantv6
, proto
;
648 char *loopv4
, *loopv6
;
654 if (hints
!= NULL
) family
= hints
->ai_family
;
659 if (family
== PF_INET6
) wantv4
= 0;
660 if (family
== PF_INET
) wantv6
= 0;
662 memset(&a4
, 0, sizeof(struct in_addr
));
663 memset(&a6
, 0, sizeof(struct in6_addr
));
665 if ((in4
== NULL
) && (in6
== NULL
))
667 loopv4
= "127.0.0.1";
668 loopv6
= "0:0:0:0:0:0:0:1";
670 if ((hints
!= NULL
) && ((hints
->ai_flags
& AI_PASSIVE
) == 1))
673 loopv6
= "0:0:0:0:0:0:0:0";
676 if ((family
== PF_UNSPEC
) || (family
== PF_INET
))
678 inet_pton(AF_INET
, loopv4
, &a4
);
681 if ((family
== PF_UNSPEC
) || (family
== PF_INET6
))
683 inet_pton(AF_INET6
, loopv6
, &a6
);
686 else if (in4
== NULL
)
688 if (family
== PF_INET
) return EAI_BADHINTS
;
691 memcpy(&a6
, in6
, sizeof(struct in6_addr
));
693 else if (in6
== NULL
)
695 if (family
== PF_INET6
) return EAI_BADHINTS
;
698 memcpy(&a4
, in4
, sizeof(struct in_addr
));
705 proto
= IPPROTO_UNSPEC
;
709 proto
= hints
->ai_protocol
;
710 if (proto
== IPPROTO_UNSPEC
)
712 if (hints
->ai_socktype
== SOCK_DGRAM
) proto
= IPPROTO_UDP
;
713 else if (hints
->ai_socktype
== SOCK_STREAM
) proto
= IPPROTO_TCP
;
719 if ((proto
== IPPROTO_UNSPEC
) || (proto
== IPPROTO_UDP
))
721 a
= new_addrinfo_v4(0, SOCK_DGRAM
, IPPROTO_UDP
, port
, a4
, 0, NULL
);
722 append_addrinfo(res
, a
);
725 if ((proto
== IPPROTO_UNSPEC
) || (proto
== IPPROTO_TCP
))
727 a
= new_addrinfo_v4(0, SOCK_STREAM
, IPPROTO_TCP
, port
, a4
, 0, NULL
);
728 append_addrinfo(res
, a
);
731 if (proto
== IPPROTO_ICMP
)
733 a
= new_addrinfo_v4(0, SOCK_RAW
, IPPROTO_ICMP
, port
, a4
, 0, NULL
);
734 append_addrinfo(res
, a
);
740 if ((proto
== IPPROTO_UNSPEC
) || (proto
== IPPROTO_UDP
))
742 a
= new_addrinfo_v6(0, SOCK_DGRAM
, IPPROTO_UDP
, port
, a6
, 0, NULL
);
743 append_addrinfo(res
, a
);
746 if ((proto
== IPPROTO_UNSPEC
) || (proto
== IPPROTO_TCP
))
748 a
= new_addrinfo_v6(0, SOCK_STREAM
, IPPROTO_TCP
, port
, a6
, 0, NULL
);
749 append_addrinfo(res
, a
);
752 if (proto
== IPPROTO_ICMPV6
)
754 a
= new_addrinfo_v6(0, SOCK_RAW
, IPPROTO_ICMPV6
, port
, a6
, 0, NULL
);
755 append_addrinfo(res
, a
);
763 gai_files(const char *nodename
, const char *servname
, const struct addrinfo
*hints
, struct addrinfo
**res
)
765 int32_t i
, status
, numericserv
, numerichost
, family
, proto
, wantv4
, wantv6
;
769 char *protoname
, *loopv4
, *loopv6
;
775 if (servname
!= NULL
) numericserv
= is_a_number(servname
);
778 if (hints
!= NULL
) family
= hints
->ai_family
;
782 if (nodename
== NULL
)
786 loopv4
= "127.0.0.1";
787 loopv6
= "0:0:0:0:0:0:0:1";
789 if ((hints
!= NULL
) && ((hints
->ai_flags
& AI_PASSIVE
) == 1))
792 loopv6
= "0:0:0:0:0:0:0:0";
795 if ((family
== PF_UNSPEC
) || (family
== PF_INET
))
797 inet_pton(AF_INET
, loopv4
, &a4
);
800 if ((family
== PF_UNSPEC
) || (family
== PF_INET6
))
802 inet_pton(AF_INET6
, loopv6
, &a6
);
807 if ((family
== PF_UNSPEC
) || (family
== PF_INET
))
809 status
= inet_pton(AF_INET
, nodename
, &a4
);
813 if (family
== PF_UNSPEC
) family
= PF_INET
;
817 if ((family
== PF_UNSPEC
) || (family
== PF_INET6
))
819 status
= inet_pton(AF_INET6
, nodename
, &a6
);
823 if (family
== PF_UNSPEC
) family
= PF_INET6
;
830 if (family
== PF_INET6
) wantv4
= 0;
831 if (family
== PF_INET
) wantv6
= 0;
833 proto
= IPPROTO_UNSPEC
;
838 proto
= hints
->ai_protocol
;
839 if (proto
== IPPROTO_UNSPEC
)
841 if (hints
->ai_socktype
== SOCK_DGRAM
) proto
= IPPROTO_UDP
;
842 else if (hints
->ai_socktype
== SOCK_STREAM
) proto
= IPPROTO_TCP
;
846 if (proto
== IPPROTO_UDP
) protoname
= "udp";
847 else if (proto
== IPPROTO_TCP
) protoname
= "tcp";
852 if (numericserv
!= 0)
854 port
= htons(atoi(servname
));
856 else if (servname
!= NULL
)
858 s
= getservbyname(servname
, protoname
);
859 if (s
!= NULL
) port
= s
->s_port
;
862 /* new_addrinfo_v4 and new_addrinfo_v6 expect port in host byte order */
865 if (numerichost
!= 0)
869 if ((proto
== IPPROTO_UNSPEC
) || (proto
== IPPROTO_UDP
))
871 a
= new_addrinfo_v4(0, SOCK_DGRAM
, IPPROTO_UDP
, port
, a4
, 0, NULL
);
872 append_addrinfo(res
, a
);
875 if ((proto
== IPPROTO_UNSPEC
) || (proto
== IPPROTO_TCP
))
877 a
= new_addrinfo_v4(0, SOCK_STREAM
, IPPROTO_TCP
, port
, a4
, 0, NULL
);
878 append_addrinfo(res
, a
);
881 if (proto
== IPPROTO_ICMP
)
883 a
= new_addrinfo_v4(0, SOCK_RAW
, IPPROTO_ICMP
, port
, a4
, 0, NULL
);
884 append_addrinfo(res
, a
);
890 if ((proto
== IPPROTO_UNSPEC
) || (proto
== IPPROTO_UDP
))
892 a
= new_addrinfo_v6(0, SOCK_DGRAM
, IPPROTO_UDP
, port
, a6
, 0, NULL
);
893 append_addrinfo(res
, a
);
896 if ((proto
== IPPROTO_UNSPEC
) || (proto
== IPPROTO_TCP
))
898 a
= new_addrinfo_v6(0, SOCK_STREAM
, IPPROTO_TCP
, port
, a6
, 0, NULL
);
899 append_addrinfo(res
, a
);
902 if (proto
== IPPROTO_ICMPV6
)
904 a
= new_addrinfo_v6(0, SOCK_RAW
, IPPROTO_ICMPV6
, port
, a6
, 0, NULL
);
905 append_addrinfo(res
, a
);
914 h
= gethostbyname(nodename
);
915 if (h
== NULL
) return 0;
917 for (i
= 0; h
->h_addr_list
[i
] != 0; i
++)
919 memmove((void *)&a4
.s_addr
, h
->h_addr_list
[i
], h
->h_length
);
921 if ((proto
== IPPROTO_UNSPEC
) || (proto
== IPPROTO_UDP
))
923 a
= new_addrinfo_v4(0, SOCK_DGRAM
, IPPROTO_UDP
, port
, a4
, 0, NULL
);
924 append_addrinfo(res
, a
);
927 if ((proto
== IPPROTO_UNSPEC
) || (proto
== IPPROTO_TCP
))
929 a
= new_addrinfo_v4(0, SOCK_STREAM
, IPPROTO_TCP
, port
, a4
, 0, NULL
);
930 append_addrinfo(res
, a
);
939 gai_lookupd(const char *nodename
, const char *servname
, const struct addrinfo
*hints
, struct addrinfo
**res
)
941 uint32_t n
, i
, qlen
, rlen
;
943 char qbuf
[LU_QBUF_SIZE
];
946 mach_port_t server_port
;
947 kern_return_t status
;
950 server_port
= MACH_PORT_NULL
;
951 if (_lu_running()) server_port
= _lookupd_port(0);
952 if (server_port
== MACH_PORT_NULL
)
954 /* lookupd isn't available - fall back to the flat files */
955 return gai_files(nodename
, servname
, hints
, res
);
960 status
= _lookup_link(server_port
, "getaddrinfo", &gai_proc
);
961 if (status
!= KERN_SUCCESS
)
963 errno
= ECONNREFUSED
;
970 /* gai_make_query sets errno if it fails */
971 i
= gai_make_query(nodename
, servname
, hints
, qbuf
, &qlen
);
972 if (i
!= 0) return EAI_SYSTEM
;
974 qlen
/= BYTES_PER_XDR_UNIT
;
978 status
= _lookup_all(server_port
, gai_proc
, (unit
*)qbuf
, qlen
, &rbuf
, &rlen
);
979 if (status
!= KERN_SUCCESS
) return EAI_NODATA
;
981 rlen
*= BYTES_PER_XDR_UNIT
;
983 xdrmem_create(&inxdr
, rbuf
, rlen
, XDR_DECODE
);
985 if (!xdr_int(&inxdr
, (int32_t *)&n
))
993 for (i
= 0; i
< n
; i
++)
995 a
= gai_lookupd_process_dictionary(&inxdr
);
996 if ((cname
== NULL
) && (a
->ai_canonname
!= NULL
)) cname
= a
->ai_canonname
;
997 append_addrinfo(res
, a
);
1000 xdr_destroy(&inxdr
);
1001 if (rbuf
!= NULL
) vm_deallocate(mach_task_self(), (vm_address_t
)rbuf
, rlen
);
1003 if ((cname
!= NULL
) && (res
!= NULL
) && (res
[0] != NULL
) && (res
[0]->ai_canonname
== NULL
))
1005 res
[0]->ai_canonname
= strdup(cname
);
1012 gai_checkhints(const struct addrinfo
*hints
)
1014 if (hints
== NULL
) return 0;
1015 if (hints
->ai_addrlen
!= 0) return EAI_BADHINTS
;
1016 if (hints
->ai_canonname
!= NULL
) return EAI_BADHINTS
;
1017 if (hints
->ai_addr
!= NULL
) return EAI_BADHINTS
;
1018 if (hints
->ai_next
!= NULL
) return EAI_BADHINTS
;
1020 /* Check for supported protocol family */
1021 if (gai_family_type_check(hints
->ai_family
) != 0) return EAI_FAMILY
;
1023 /* Check for supported socket */
1024 if (gai_socket_type_check(hints
->ai_socktype
) != 0) return EAI_BADHINTS
;
1026 /* Check for supported protocol */
1027 if (gai_protocol_type_check(hints
->ai_protocol
) != 0) return EAI_BADHINTS
;
1029 /* Check that socket type is compatible with protocol */
1030 if (gai_socket_protocol_type_check(hints
->ai_socktype
, hints
->ai_protocol
) != 0) return EAI_BADHINTS
;
1036 getaddrinfo(const char * __restrict nodename
, const char * __restrict servname
, const struct addrinfo
* __restrict hints
, struct addrinfo
** __restrict res
)
1038 int32_t status
, nodenull
, servnull
;
1039 int32_t numericserv
, numerichost
, family
;
1041 struct in_addr a4
, *p4
;
1042 struct in6_addr a6
, *p6
;
1044 if (res
== NULL
) return 0;
1049 if ((nodename
== NULL
) || (nodename
[0] == '\0')) nodenull
= 1;
1052 if ((servname
== NULL
) || (servname
[0] == '\0')) servnull
= 1;
1054 if ((nodenull
== 1) && (servnull
== 1)) return EAI_NONAME
;
1056 status
= gai_checkhints(hints
);
1057 if (status
!= 0) return status
;
1060 * Trap the "trivial" cases that can be answered without a query.
1061 * (nodename == numeric) && (servname == NULL)
1062 * (nodename == numeric) && (servname == numeric)
1063 * (nodename == NULL) && (servname == numeric)
1068 memset(&a4
, 0, sizeof(struct in_addr
));
1069 memset(&a6
, 0, sizeof(struct in6_addr
));
1073 if (servnull
== 0) numericserv
= is_a_number(servname
);
1074 if (numericserv
== 1) port
= atoi(servname
);
1077 if (hints
!= NULL
) family
= hints
->ai_family
;
1082 if ((family
== PF_UNSPEC
) || (family
== PF_INET
))
1084 status
= inet_pton(AF_INET
, nodename
, &a4
);
1092 if ((family
== PF_UNSPEC
) || (family
== PF_INET6
))
1094 status
= inet_pton(AF_INET6
, nodename
, &a6
);
1103 if ((nodenull
== 1) && (numericserv
== 1)) return gai_trivial(NULL
, NULL
, port
, hints
, res
);
1104 if ((numerichost
== 1) && (numericserv
== 1)) return gai_trivial(p4
, p6
, port
, hints
, res
);
1105 if ((numerichost
== 1) && (servnull
== 1)) return gai_trivial(p4
, p6
, 0, hints
, res
);
1107 if (nodenull
== 1) status
= gai_lookupd(NULL
, servname
, hints
, res
);
1108 else if (servnull
== 1) status
= gai_lookupd(nodename
, NULL
, hints
, res
);
1109 else status
= gai_lookupd(nodename
, servname
, hints
, res
);
1111 if ((status
== 0) && (*res
== NULL
)) status
= EAI_NODATA
;
1117 getaddrinfo_async_start(mach_port_t
*p
, const char *nodename
, const char *servname
, const struct addrinfo
*hints
, getaddrinfo_async_callback callback
, void *context
)
1121 char qbuf
[LU_QBUF_SIZE
];
1122 mach_port_t server_port
;
1124 *p
= MACH_PORT_NULL
;
1126 if ((nodename
== NULL
) && (servname
== NULL
)) return EAI_NONAME
;
1128 status
= gai_checkhints(hints
);
1129 if (status
!= 0) return EAI_BADHINTS
;
1131 server_port
= MACH_PORT_NULL
;
1132 if (_lu_running()) server_port
= _lookupd_port(0);
1133 if (server_port
== MACH_PORT_NULL
)
1135 errno
= ECONNREFUSED
;
1141 status
= _lookup_link(server_port
, "getaddrinfo", &gai_proc
);
1142 if (status
!= KERN_SUCCESS
)
1144 errno
= ECONNREFUSED
;
1149 qlen
= LU_QBUF_SIZE
;
1151 /* gai_make_query sets errno if it fails */
1152 i
= gai_make_query(nodename
, servname
, hints
, qbuf
, &qlen
);
1153 if (i
!= 0) return EAI_SYSTEM
;
1155 qlen
/= BYTES_PER_XDR_UNIT
;
1157 status
= lu_async_start(p
, gai_proc
, qbuf
, qlen
, (void *)callback
, context
);
1160 errno
= ECONNREFUSED
;
1168 getaddrinfo_async_send(mach_port_t
*p
, const char *nodename
, const char *servname
, const struct addrinfo
*hints
)
1170 return getaddrinfo_async_start(p
, nodename
, servname
, hints
, NULL
, NULL
);
1174 gai_extract_data(char *buf
, uint32_t len
, struct addrinfo
**res
)
1183 if (buf
== NULL
) return EAI_NODATA
;
1184 if (len
== 0) return EAI_NODATA
;
1186 xdrmem_create(&xdr
, buf
, len
, XDR_DECODE
);
1188 if (!xdr_int(&xdr
, (int32_t *)&n
))
1196 for (i
= 0; i
< n
; i
++)
1198 a
= gai_lookupd_process_dictionary(&xdr
);
1199 if (a
== NULL
) break;
1201 if ((cname
== NULL
) && (a
->ai_canonname
!= NULL
)) cname
= a
->ai_canonname
;
1202 append_addrinfo(res
, a
);
1207 if ((cname
!= NULL
) && (res
!= NULL
) && (res
[0] != NULL
) && (res
[0]->ai_canonname
== NULL
))
1209 res
[0]->ai_canonname
= strdup(cname
);
1212 if (*res
== NULL
) return EAI_NODATA
;
1217 getaddrinfo_async_receive(mach_port_t p
, struct addrinfo
**res
)
1219 kern_return_t status
;
1223 if (res
== NULL
) return 0;
1229 status
= lu_async_receive(p
, &buf
, &len
);
1230 if (status
< 0) return EAI_FAIL
;
1232 status
= gai_extract_data(buf
, len
, res
);
1233 if (buf
!= NULL
) vm_deallocate(mach_task_self(), (vm_address_t
)buf
, len
);
1234 if (status
!= 0) return status
;
1236 if (*res
== NULL
) return EAI_NODATA
;
1242 getaddrinfo_async_handle_reply(void *msg
)
1244 getaddrinfo_async_callback callback
;
1249 struct addrinfo
*res
;
1251 callback
= (getaddrinfo_async_callback
)NULL
;
1257 status
= lu_async_handle_reply(msg
, &buf
, &len
, (void **)&callback
, &context
);
1258 if (status
!= KERN_SUCCESS
)
1260 if (status
== MIG_REPLY_MISMATCH
) return 0;
1261 if (callback
!= NULL
) callback(EAI_NODATA
, NULL
, context
);
1265 status
= gai_extract_data(buf
, len
, &res
);
1266 if (buf
!= NULL
) vm_deallocate(mach_task_self(), (vm_address_t
)buf
, len
);
1269 if (callback
!= NULL
) callback(status
, NULL
, context
);
1275 callback(EAI_NODATA
, NULL
, context
);
1279 callback(0, res
, context
);
1288 * getnameinfo support in lookupd
1289 * Input dict may contain the following
1291 * ip_address: node address
1292 * ipv6_address: node address
1293 * port: service number
1294 * protocol: [tcp] | udp
1296 * numerichost: [0] | 1
1297 * name_required: [0] | 1
1298 * numericserv: [0] | 1
1300 * Output dictionary may contain the following
1301 * All values are encoded as strings.
1308 gni_lookupd_process_dictionary(XDR
*inxdr
, char **host
, char **serv
)
1310 int32_t i
, j
, nkeys
, nvals
, status
;
1313 if ((host
== NULL
) || (serv
== NULL
))
1319 if (!xdr_int(inxdr
, &nkeys
))
1328 for (i
= 0; i
< nkeys
; i
++)
1334 status
= _lu_xdr_attribute(inxdr
, &key
, &vals
, (uint32_t *)&nvals
);
1347 if ((*host
== NULL
) && (!strcmp("name", key
)))
1350 for (j
= 1; j
< nvals
; j
++) free(vals
[j
]);
1353 else if ((*serv
== NULL
) && (!strcmp(key
, "service")))
1356 for (j
= 1; j
< nvals
; j
++) free(vals
[j
]);
1359 if (key
!= NULL
) free(key
);
1367 gni_make_query(const struct sockaddr
*sa
, size_t salen
, int wanthost
, int wantserv
, int flags
, char *buf
, uint32_t *len
)
1371 char str
[_LU_MAXLUSTRLEN
], *key
, ifname
[IF_NAMESIZE
];
1372 uint32_t a4
, ifnum
, offset
, na
, proto
, fqdn
, numerichost
, numericserv
, name_req
, isll
, issl
;
1373 struct sockaddr_in6
*s6
;
1375 if (sa
== NULL
) return EAI_FAIL
;
1376 if (sa
->sa_len
!= salen
) return EAI_FAMILY
;
1378 proto
= IPPROTO_TCP
;
1386 offset
= INET_NTOP_AF_INET_OFFSET
;
1390 if (sa
->sa_family
== PF_INET
)
1392 a4
= ntohl(((const struct sockaddr_in
*)sa
)->sin_addr
.s_addr
);
1393 if (IN_MULTICAST(a4
) || IN_EXPERIMENTAL(a4
)) flags
|= NI_NUMERICHOST
;
1394 a4
>>= IN_CLASSA_NSHIFT
;
1395 if (a4
== 0) flags
|= NI_NUMERICHOST
;
1397 port
= ((struct sockaddr_in
*)sa
)->sin_port
;
1399 else if (sa
->sa_family
== PF_INET6
)
1401 s6
= (struct sockaddr_in6
*)sa
;
1402 switch (s6
->sin6_addr
.s6_addr
[0])
1405 if (IN6_IS_ADDR_V4MAPPED(&s6
->sin6_addr
))
1408 else if (IN6_IS_ADDR_LOOPBACK(&s6
->sin6_addr
))
1413 flags
|= NI_NUMERICHOST
;
1417 if (IN6_IS_ADDR_LINKLOCAL(&s6
->sin6_addr
))
1421 else if (IN6_IS_ADDR_SITELOCAL(&s6
->sin6_addr
))
1425 else if (IN6_IS_ADDR_MULTICAST(&s6
->sin6_addr
))
1427 flags
|= NI_NUMERICHOST
;
1432 if ((isll
!= 0) || (issl
!= 0))
1434 ifnum
= s6
->sin6_addr
.__u6_addr
.__u6_addr16
[1];
1435 if (ifnum
== 0) ifnum
= s6
->sin6_scope_id
;
1436 else if ((s6
->sin6_scope_id
!= 0) && (ifnum
!= s6
->sin6_scope_id
)) return EAI_FAIL
;
1438 s6
->sin6_addr
.__u6_addr
.__u6_addr16
[1] = 0;
1439 s6
->sin6_scope_id
= ifnum
;
1440 if ((ifnum
!= 0) && (flags
& NI_NUMERICHOST
)) flags
|= NI_WITHSCOPEID
;
1443 offset
= INET_NTOP_AF_INET6_OFFSET
;
1444 key
= "ipv6_address";
1445 port
= s6
->sin6_port
;
1454 if (wanthost
!= 0) na
++;
1455 if (wantserv
!= 0) na
++;
1457 if (flags
& NI_NOFQDN
)
1463 if (flags
& NI_NUMERICHOST
)
1469 if (flags
& NI_NUMERICSERV
)
1475 if (flags
& NI_NAMEREQD
)
1481 if (flags
& NI_DGRAM
)
1483 proto
= IPPROTO_UDP
;
1487 xdrmem_create(&outxdr
, buf
, *len
, XDR_ENCODE
);
1489 if (!xdr_int(&outxdr
, (int32_t *)&na
))
1491 xdr_destroy(&outxdr
);
1498 inet_ntop(sa
->sa_family
, (char *)(sa
) + offset
, str
, _LU_MAXLUSTRLEN
);
1500 if ((flags
& NI_WITHSCOPEID
) && (sa
->sa_family
== AF_INET6
))
1502 ifnum
= ((struct sockaddr_in6
*)sa
)->sin6_scope_id
;
1503 if (if_indextoname(ifnum
, ifname
) != NULL
)
1506 strcat(str
, ifname
);
1510 if (encode_kv(&outxdr
, key
, str
) != 0)
1512 xdr_destroy(&outxdr
);
1520 snprintf(str
, _LU_MAXLUSTRLEN
, "%hu", port
);
1521 if (encode_kv(&outxdr
, "port", str
) != 0)
1523 xdr_destroy(&outxdr
);
1529 if (proto
== IPPROTO_UDP
)
1531 if (encode_kv(&outxdr
, "protocol", "udp") != 0)
1533 xdr_destroy(&outxdr
);
1541 if (encode_kv(&outxdr
, "fqdn", "0") != 0)
1543 xdr_destroy(&outxdr
);
1549 if (numerichost
== 1)
1551 if (encode_kv(&outxdr
, "numerichost", "1") != 0)
1553 xdr_destroy(&outxdr
);
1559 if (numericserv
== 1)
1561 if (encode_kv(&outxdr
, "numericserv", "1") != 0)
1563 xdr_destroy(&outxdr
);
1571 if (encode_kv(&outxdr
, "name_required", "1") != 0)
1573 xdr_destroy(&outxdr
);
1579 *len
= xdr_getpos(&outxdr
);
1581 xdr_destroy(&outxdr
);
1587 getnameinfo(const struct sockaddr
* __restrict sa
, socklen_t salen
, char * __restrict host
, socklen_t hostlen
, char * __restrict serv
, socklen_t servlen
, int flags
)
1589 uint32_t n
, i
, qlen
, rlen
;
1591 int wanth
, wants
, isll
, issl
;
1593 char qbuf
[LU_QBUF_SIZE
], ifname
[IF_NAMESIZE
];
1594 char *rbuf
, *hval
, *sval
;
1595 mach_port_t server_port
;
1596 kern_return_t status
;
1597 struct sockaddr_in
*s4
;
1598 struct sockaddr_in6
*s6
;
1601 if (sa
== NULL
) return EAI_FAIL
;
1607 if (sa
->sa_family
== AF_INET6
)
1609 s6
= (struct sockaddr_in6
*)sa
;
1611 if (IN6_IS_ADDR_LINKLOCAL(&s6
->sin6_addr
)) isll
= 1;
1612 if (IN6_IS_ADDR_SITELOCAL(&s6
->sin6_addr
)) issl
= 1;
1615 * Link-local and site-local IPv6 addresses may have a scope id
1616 * in s6->sin6_addr.__u6_addr.__u6_addr16[1] as well as in s6->sin6_scope_id.
1617 * If they are both non-zero, they must be equal.
1618 * We zero s6->sin6_addr.__u6_addr.__u6_addr16[1] and set s6->sin6_scope_id.
1620 if ((isll
!= 0) || (issl
!= 0))
1622 ifnum
= ntohs(s6
->sin6_addr
.__u6_addr
.__u6_addr16
[1]);
1623 if (ifnum
== 0) ifnum
= s6
->sin6_scope_id
;
1624 else if ((s6
->sin6_scope_id
!= 0) && (ifnum
!= s6
->sin6_scope_id
)) return EAI_FAIL
;
1626 s6
->sin6_addr
.__u6_addr
.__u6_addr16
[1] = 0;
1627 s6
->sin6_scope_id
= ifnum
;
1630 /* V4 mapped and compat addresses are converted to plain V4 */
1631 if ((IN6_IS_ADDR_V4MAPPED(&s6
->sin6_addr
)) || (IN6_IS_ADDR_V4COMPAT(&s6
->sin6_addr
)))
1633 s4
= (struct sockaddr_in
*)calloc(1, sizeof(struct sockaddr_in
));
1634 s4
->sin_len
= sizeof(struct sockaddr_in
);
1635 s4
->sin_family
= AF_INET
;
1636 s4
->sin_port
= s6
->sin6_port
;
1637 memcpy(&(s4
->sin_addr
.s_addr
), &(s6
->sin6_addr
.s6_addr
[12]), 4);
1639 i
= getnameinfo((const struct sockaddr
*)s4
, s4
->sin_len
, host
, hostlen
, serv
, servlen
, flags
);
1646 if ((host
!= NULL
) && (hostlen
!= 0)) wanth
= 1;
1649 if ((serv
!= NULL
) && (servlen
!= 0)) wants
= 1;
1651 if ((wanth
== 0) && (wants
== 0)) return 0;
1654 * Special cases handled by the library
1656 if ((wanth
== 1) && (flags
& NI_NUMERICHOST
))
1658 i
= INET_NTOP_AF_INET_OFFSET
;
1659 if (sa
->sa_family
== PF_INET6
) i
= INET_NTOP_AF_INET6_OFFSET
;
1660 if (inet_ntop(sa
->sa_family
, (char *)(sa
) + i
, host
, hostlen
) == NULL
) return EAI_FAIL
;
1662 if (((isll
!= 0) || (issl
!= 0)) && (ifnum
!= 0))
1664 /* append interface name */
1665 if (if_indextoname(ifnum
, ifname
) != NULL
)
1668 strcat(host
, ifname
);
1672 if (wants
== 0) return 0;
1675 if ((wants
== 1) && (flags
& NI_NUMERICSERV
))
1677 if (sa
->sa_family
== PF_INET
)
1679 s4
= (struct sockaddr_in
*)sa
;
1680 n
= snprintf(serv
, servlen
, "%hu", ntohs(s4
->sin_port
));
1681 if (n
>= servlen
) return EAI_FAIL
;
1683 else if (sa
->sa_family
== PF_INET6
)
1685 s6
= (struct sockaddr_in6
*)sa
;
1686 n
= snprintf(serv
, servlen
, "%hu", ntohs(s6
->sin6_port
));
1687 if (n
>= servlen
) return EAI_FAIL
;
1689 else return EAI_FAMILY
;
1691 if (wanth
== 0) return 0;
1694 if ((wanth
== 1) && (flags
& NI_NUMERICHOST
) && (wants
== 1) && (flags
& NI_NUMERICSERV
)) return 0;
1699 server_port
= MACH_PORT_NULL
;
1700 if (_lu_running()) server_port
= _lookupd_port(0);
1701 if (server_port
== MACH_PORT_NULL
)
1703 errno
= ECONNREFUSED
;
1709 status
= _lookup_link(server_port
, "getnameinfo", &gni_proc
);
1710 if (status
!= KERN_SUCCESS
)
1712 errno
= ECONNREFUSED
;
1717 qlen
= LU_QBUF_SIZE
;
1718 i
= gni_make_query(sa
, salen
, wanth
, wants
, flags
, qbuf
, &qlen
);
1719 if (i
!= 0) return i
;
1721 qlen
/= BYTES_PER_XDR_UNIT
;
1725 status
= _lookup_all(server_port
, gni_proc
, (unit
*)qbuf
, qlen
, &rbuf
, &rlen
);
1726 if (status
!= KERN_SUCCESS
) return EAI_NONAME
;
1728 rlen
*= BYTES_PER_XDR_UNIT
;
1730 xdrmem_create(&inxdr
, rbuf
, rlen
, XDR_DECODE
);
1732 if (!xdr_int(&inxdr
, (int32_t *)&n
))
1734 xdr_destroy(&inxdr
);
1741 xdr_destroy(&inxdr
);
1748 i
= gni_lookupd_process_dictionary(&inxdr
, &hval
, &sval
);
1750 xdr_destroy(&inxdr
);
1751 if (rbuf
!= NULL
) vm_deallocate(mach_task_self(), (vm_address_t
)rbuf
, rlen
);
1753 if (i
!= 0) return i
;
1756 if (hval
!= NULL
) i
= strlen(hval
) + 1;
1757 if ((host
!= NULL
) && (hostlen
!= 0) && (i
!= 0))
1759 if (i
> hostlen
) return EAI_FAIL
;
1760 memcpy(host
, hval
, i
);
1765 if (sval
!= NULL
) i
= strlen(sval
) + 1;
1766 if ((serv
!= NULL
) && (servlen
!= 0) && (i
!= 0))
1768 if (i
> servlen
) return EAI_FAIL
;
1769 memcpy(serv
, sval
, i
);
1777 getnameinfo_async_start(mach_port_t
*p
, const struct sockaddr
*sa
, size_t salen
, int flags
, getnameinfo_async_callback callback
, void *context
)
1780 char qbuf
[LU_QBUF_SIZE
];
1781 mach_port_t server_port
;
1782 kern_return_t status
;
1785 if (sa
== NULL
) return EAI_FAIL
;
1787 server_port
= MACH_PORT_NULL
;
1788 if (_lu_running()) server_port
= _lookupd_port(0);
1789 if (server_port
== MACH_PORT_NULL
)
1791 errno
= ECONNREFUSED
;
1797 status
= _lookup_link(server_port
, "getnameinfo", &gni_proc
);
1798 if (status
!= KERN_SUCCESS
)
1800 errno
= ECONNREFUSED
;
1805 qlen
= LU_QBUF_SIZE
;
1806 i
= gni_make_query(sa
, salen
, 1, 1, flags
, qbuf
, &qlen
);
1807 if (i
!= 0) return i
;
1809 qlen
/= BYTES_PER_XDR_UNIT
;
1811 status
= lu_async_start(p
, gni_proc
, qbuf
, qlen
, (void *)callback
, context
);
1814 errno
= ECONNREFUSED
;
1822 getnameinfo_async_send(mach_port_t
*p
, const struct sockaddr
*sa
, size_t salen
, int flags
)
1824 return getnameinfo_async_start(p
, sa
, salen
, flags
, NULL
, NULL
);
1828 gni_extract_data(char *buf
, uint32_t len
, char **host
, char **serv
)
1837 if (buf
== NULL
) return EAI_NODATA
;
1838 if (len
== 0) return EAI_NODATA
;
1840 xdrmem_create(&xdr
, buf
, len
, XDR_DECODE
);
1842 if (!xdr_int(&xdr
, (int32_t *)&n
))
1855 i
= gni_lookupd_process_dictionary(&xdr
, host
, serv
);
1861 getnameinfo_async_receive(mach_port_t p
, char **host
, char **serv
)
1863 kern_return_t status
;
1870 status
= lu_async_receive(p
, &buf
, &len
);
1871 if (status
< 0) return EAI_FAIL
;
1873 status
= gni_extract_data(buf
, len
, host
, serv
);
1874 if (buf
!= NULL
) vm_deallocate(mach_task_self(), (vm_address_t
)buf
, len
);
1875 if (status
!= 0) return status
;
1881 getnameinfo_async_handle_reply(void *msg
)
1883 getnameinfo_async_callback callback
;
1885 char *buf
, *hval
, *sval
;
1889 callback
= (getnameinfo_async_callback
)NULL
;
1894 status
= lu_async_handle_reply(msg
, &buf
, &len
, (void **)&callback
, &context
);
1895 if (status
!= KERN_SUCCESS
)
1897 if (status
== MIG_REPLY_MISMATCH
) return 0;
1898 if (callback
!= NULL
) callback(EAI_NONAME
, NULL
, NULL
, context
);
1905 status
= gni_extract_data(buf
, len
, &hval
, &sval
);
1906 if (buf
!= NULL
) vm_deallocate(mach_task_self(), (vm_address_t
)buf
, len
);
1909 if (callback
!= NULL
) callback(status
, NULL
, NULL
, context
);
1913 callback(0, hval
, sval
, context
);