4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
24 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
25 * Use is subject to license terms.
28 #include <sys/types.h>
34 #include <sys/socket.h>
36 #include <arpa/inet.h>
37 #include <nss_dbdefs.h>
38 #include <netinet/in.h>
39 #include <sys/socket.h>
42 #define sa2sin(x) ((struct sockaddr_in *)(x))
43 #define sa2sin6(x) ((struct sockaddr_in6 *)(x))
45 #define NI_MASK (NI_NOFQDN | NI_NUMERICHOST | NI_NAMEREQD | NI_NUMERICSERV | \
46 NI_DGRAM | NI_WITHSCOPEID)
48 static int addzoneid(const struct sockaddr_in6
*sa
, char *host
,
50 static size_t getzonestr(const struct sockaddr_in6
*sa
, char *zonestr
,
52 static const char *_inet_ntop_native();
57 * Routine for performing Address-to-nodename in a
58 * protocol-independent fashion.
60 * This function looks up an IP address and port number provided
61 * by the caller in the name service database and returns the nodename
62 * and servname respectively in the buffers provided by the caller.
64 * sa - points to either a sockaddr_in structure (for
65 * IPv4) or a sockaddr_in6 structure (for IPv6).
66 * salen - length of the sockaddr_in or sockaddr_in6 structure.
67 * hostlen - length of caller supplied "host" buffer
68 * servlen - length of caller supplied "serv" buffer
69 * flags - changes default actions based on setting.
70 * Possible settings for "flags":
71 * NI_NOFQDN - Always return nodename portion of the fully-qualified
73 * NI_NUMERICHOST - Always return numeric form of the host's
75 * NI_NAMEREQD - If hostname cannot be located in database,
76 * don't return numeric form of address - return
78 * NI_NUMERICSERV - Always return numeric form of the service address
79 * instead of its name.
80 * NI_DGRAM - Specifies that the service is a datagram service, and
81 * causes getservbyport() to be called with a second
82 * argument of "udp" instead of its default "tcp".
84 * host - return the nodename associcated with the IP address in the
85 * buffer pointed to by the "host" argument.
86 * serv - return the service name associated with the port number
87 * in the buffer pointed to by the "serv" argument.
89 * This function indicates successful completion by a zero return
90 * value; a non-zero return value indicates failure.
93 getnameinfo(const struct sockaddr
*sa
, socklen_t salen
,
94 char *host
, socklen_t hostlen
,
95 char *serv
, socklen_t servlen
, int flags
)
103 /* Verify correctness of buffer lengths */
104 if ((hostlen
== 0) && (servlen
== 0))
106 /* Verify correctness of possible flag settings */
107 if ((flags
!= 0) && (flags
& ~NI_MASK
))
108 return (EAI_BADFLAGS
);
110 return (EAI_ADDRFAMILY
);
111 switch (sa
->sa_family
) {
113 addr
= (char *)&sa2sin(sa
)->sin_addr
;
114 alen
= sizeof (struct in_addr
);
115 slen
= sizeof (struct sockaddr_in
);
116 port
= (sa2sin(sa
)->sin_port
); /* network byte order */
119 addr
= (char *)&sa2sin6(sa
)->sin6_addr
;
120 alen
= sizeof (struct in6_addr
);
121 slen
= sizeof (struct sockaddr_in6
);
122 port
= (sa2sin6(sa
)->sin6_port
); /* network byte order */
130 * Case 1: if Caller sets hostlen != 0, then
131 * fill in "host" buffer that user passed in
132 * with appropriate text string.
135 if (flags
& NI_NUMERICHOST
) {
136 /* Caller wants the host's numeric address */
137 if (inet_ntop(sa
->sa_family
, addr
,
138 host
, hostlen
) == NULL
)
143 /* Caller wants the name of host */
144 hp
= getipnodebyaddr(addr
, alen
, sa
->sa_family
,
147 if (flags
& NI_NOFQDN
) {
150 * Caller doesn't want fully-qualified
153 dot
= strchr(hp
->h_name
, '.');
157 if (strlen(hp
->h_name
) + 1 > hostlen
) {
159 return (EAI_OVERFLOW
);
161 (void) strcpy(host
, hp
->h_name
);
165 * Host's name cannot be located in the name
166 * service database. If NI_NAMEREQD is set,
167 * return error; otherwise, return host's
170 if (flags
& NI_NAMEREQD
) {
184 if (_inet_ntop_native(sa
->sa_family
, addr
,
185 host
, hostlen
) == NULL
)
191 * Check for a non-zero sin6_scope_id, indicating a
192 * zone-id needs to be appended to the resultant 'host'
195 if ((sa
->sa_family
== AF_INET6
) &&
196 (sa2sin6(sa
)->sin6_scope_id
!= 0)) {
198 * According to draft-ietf-ipngwg-scoping-arch-XX, only
199 * non-global scope addresses can make use of the
200 * <addr>%<zoneid> format. This implemenation
201 * supports only link scope addresses, since the use of
202 * site-local addressing is not yet fully specified.
203 * If the address meets this criteria, attempt to add a
204 * zone-id to 'host'. If it does not, return
207 if (IN6_IS_ADDR_LINKSCOPE(&(sa2sin6(sa
)->sin6_addr
))) {
208 if ((err
= addzoneid(sa2sin6(sa
), host
,
218 * Case 2: if Caller sets servlen != 0, then
219 * fill in "serv" buffer that user passed in
220 * with appropriate text string.
226 if (flags
& NI_NUMERICSERV
) {
227 /* Caller wants the textual form of the port number */
228 portlen
= snprintf(port_buf
, sizeof (port_buf
), "%hu",
230 if (servlen
< portlen
+ 1)
231 return (EAI_OVERFLOW
);
232 (void) strcpy(serv
, port_buf
);
236 * Caller wants the name of the service.
237 * If NI_DGRAM is set, get service name for
238 * specified port for udp.
240 sp
= getservbyport(port
,
241 flags
& NI_DGRAM
? "udp" : "tcp");
243 if (servlen
< strlen(sp
->s_name
) + 1)
244 return (EAI_OVERFLOW
);
245 (void) strcpy(serv
, sp
->s_name
);
248 * if service is not in the name server's
249 * database, fill buffer with numeric form for
252 portlen
= snprintf(port_buf
, sizeof (port_buf
),
254 if (servlen
< portlen
+ 1)
255 return (EAI_OVERFLOW
);
256 (void) strcpy(serv
, port_buf
);
264 * addzoneid(sa, host, hostlen)
266 * Appends a zone-id to the input 'host' string if the input sin6_scope_id
267 * is non-zero. The resultant 'host' string would be of the form
268 * 'host'%'zone-id'. Where 'zone-id' can be either an interface name or a
269 * literal interface index.
273 * EAI_MEMORY - an error occured when forming the output string
276 addzoneid(const struct sockaddr_in6
*sa
, char *host
, size_t hostlen
)
278 char zonestr
[LIFNAMSIZ
];
280 size_t addrlen
= strlen(host
);
282 /* make sure zonelen is valid sizeof (<addr>%<zoneid>\0) */
283 if (((zonelen
= getzonestr(sa
, zonestr
, sizeof (zonestr
))) == 0) ||
284 ((addrlen
+ 1 + zonelen
+ 1) > hostlen
)) {
288 /* Create address string of form <addr>%<zoneid> */
289 host
[addrlen
] = '%'; /* place address-zoneid delimiter */
290 (void) strlcpy((host
+ addrlen
+ 1), zonestr
, (zonelen
+ 1));
295 * getzonestr(sa, zonestr)
297 * parse zone string from input sockaddr_in6
299 * Note: This function calls if_indextoname, a very poor interface,
300 * defined in RFC2553, for converting an interface index to an
301 * interface name. Callers of this function must be sure that
302 * zonestr is atleast LIFNAMSIZ in length, since this is the longest
303 * possible value if_indextoname will return.
306 * 0 an error with calling this function occured
307 * >0 zonestr is filled with a valid zoneid string and the return value is the
308 * length of that string.
311 getzonestr(const struct sockaddr_in6
*sa
, char *zonestr
, size_t zonelen
)
316 if (zonestr
== NULL
) {
321 * Since this implementation only supports link scope addresses,
322 * there is a one-to-one mapping between interface index and
325 ifindex
= sa
->sin6_scope_id
;
327 if ((retstr
= if_indextoname(ifindex
, zonestr
)) != NULL
) {
328 return (strlen(retstr
));
333 * Failed to convert ifindex into an interface name,
334 * simply return the literal value of ifindex as
337 if ((n
= snprintf(zonestr
, zonelen
, "%u",
351 * This is a wrapper function for inet_ntop(). In case the af is AF_INET6
352 * and the address pointed by src is a IPv4-mapped IPv6 address, it
353 * returns printable IPv4 address, not IPv4-mapped IPv6 address. In other cases
354 * it behaves just like inet_ntop().
357 _inet_ntop_native(int af
, const void *src
, char *dst
, size_t size
)
362 if (af
== AF_INET6
) {
363 if (IN6_IS_ADDR_V4MAPPED((struct in6_addr
*)src
)) {
364 IN6_V4MAPPED_TO_INADDR((struct in6_addr
*)src
, &src4
);
365 result
= inet_ntop(AF_INET
, &src4
, dst
, size
);
367 result
= inet_ntop(AF_INET6
, src
, dst
, size
);
370 result
= inet_ntop(af
, src
, dst
, size
);