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 #pragma ident "%Z%%M% %I% %E% SMI"
30 #include <sys/types.h>
36 #include <sys/socket.h>
38 #include <arpa/inet.h>
39 #include <nss_dbdefs.h>
40 #include <netinet/in.h>
41 #include <sys/socket.h>
44 #define sa2sin(x) ((struct sockaddr_in *)(x))
45 #define sa2sin6(x) ((struct sockaddr_in6 *)(x))
47 #define NI_MASK (NI_NOFQDN | NI_NUMERICHOST | NI_NAMEREQD | NI_NUMERICSERV | \
48 NI_DGRAM | NI_WITHSCOPEID)
50 static int addzoneid(const struct sockaddr_in6
*sa
, char *host
,
52 static size_t getzonestr(const struct sockaddr_in6
*sa
, char *zonestr
,
54 static const char *_inet_ntop_native();
59 * Routine for performing Address-to-nodename in a
60 * protocol-independent fashion.
62 * This function looks up an IP address and port number provided
63 * by the caller in the name service database and returns the nodename
64 * and servname respectively in the buffers provided by the caller.
66 * sa - points to either a sockaddr_in structure (for
67 * IPv4) or a sockaddr_in6 structure (for IPv6).
68 * salen - length of the sockaddr_in or sockaddr_in6 structure.
69 * hostlen - length of caller supplied "host" buffer
70 * servlen - length of caller supplied "serv" buffer
71 * flags - changes default actions based on setting.
72 * Possible settings for "flags":
73 * NI_NOFQDN - Always return nodename portion of the fully-qualified
75 * NI_NUMERICHOST - Always return numeric form of the host's
77 * NI_NAMEREQD - If hostname cannot be located in database,
78 * don't return numeric form of address - return
80 * NI_NUMERICSERV - Always return numeric form of the service address
81 * instead of its name.
82 * NI_DGRAM - Specifies that the service is a datagram service, and
83 * causes getservbyport() to be called with a second
84 * argument of "udp" instead of its default "tcp".
86 * host - return the nodename associcated with the IP address in the
87 * buffer pointed to by the "host" argument.
88 * serv - return the service name associated with the port number
89 * in the buffer pointed to by the "serv" argument.
91 * This function indicates successful completion by a zero return
92 * value; a non-zero return value indicates failure.
95 getnameinfo(const struct sockaddr
*sa
, socklen_t salen
,
96 char *host
, socklen_t hostlen
,
97 char *serv
, socklen_t servlen
, int flags
)
105 /* Verify correctness of buffer lengths */
106 if ((hostlen
== 0) && (servlen
== 0))
108 /* Verify correctness of possible flag settings */
109 if ((flags
!= 0) && (flags
& ~NI_MASK
))
110 return (EAI_BADFLAGS
);
112 return (EAI_ADDRFAMILY
);
113 switch (sa
->sa_family
) {
115 addr
= (char *)&sa2sin(sa
)->sin_addr
;
116 alen
= sizeof (struct in_addr
);
117 slen
= sizeof (struct sockaddr_in
);
118 port
= (sa2sin(sa
)->sin_port
); /* network byte order */
121 addr
= (char *)&sa2sin6(sa
)->sin6_addr
;
122 alen
= sizeof (struct in6_addr
);
123 slen
= sizeof (struct sockaddr_in6
);
124 port
= (sa2sin6(sa
)->sin6_port
); /* network byte order */
132 * Case 1: if Caller sets hostlen != 0, then
133 * fill in "host" buffer that user passed in
134 * with appropriate text string.
137 if (flags
& NI_NUMERICHOST
) {
138 /* Caller wants the host's numeric address */
139 if (inet_ntop(sa
->sa_family
, addr
,
140 host
, hostlen
) == NULL
)
145 /* Caller wants the name of host */
146 hp
= getipnodebyaddr(addr
, alen
, sa
->sa_family
,
149 if (flags
& NI_NOFQDN
) {
152 * Caller doesn't want fully-qualified
155 dot
= strchr(hp
->h_name
, '.');
159 if (strlen(hp
->h_name
) + 1 > hostlen
) {
161 return (EAI_OVERFLOW
);
163 (void) strcpy(host
, hp
->h_name
);
167 * Host's name cannot be located in the name
168 * service database. If NI_NAMEREQD is set,
169 * return error; otherwise, return host's
172 if (flags
& NI_NAMEREQD
) {
186 if (_inet_ntop_native(sa
->sa_family
, addr
,
187 host
, hostlen
) == NULL
)
193 * Check for a non-zero sin6_scope_id, indicating a
194 * zone-id needs to be appended to the resultant 'host'
197 if ((sa
->sa_family
== AF_INET6
) &&
198 (sa2sin6(sa
)->sin6_scope_id
!= 0)) {
200 * According to draft-ietf-ipngwg-scoping-arch-XX, only
201 * non-global scope addresses can make use of the
202 * <addr>%<zoneid> format. This implemenation
203 * supports only link scope addresses, since the use of
204 * site-local addressing is not yet fully specified.
205 * If the address meets this criteria, attempt to add a
206 * zone-id to 'host'. If it does not, return
209 if (IN6_IS_ADDR_LINKSCOPE(&(sa2sin6(sa
)->sin6_addr
))) {
210 if ((err
= addzoneid(sa2sin6(sa
), host
,
220 * Case 2: if Caller sets servlen != 0, then
221 * fill in "serv" buffer that user passed in
222 * with appropriate text string.
228 if (flags
& NI_NUMERICSERV
) {
229 /* Caller wants the textual form of the port number */
230 portlen
= snprintf(port_buf
, sizeof (port_buf
), "%hu",
232 if (servlen
< portlen
+ 1)
233 return (EAI_OVERFLOW
);
234 (void) strcpy(serv
, port_buf
);
238 * Caller wants the name of the service.
239 * If NI_DGRAM is set, get service name for
240 * specified port for udp.
242 sp
= getservbyport(port
,
243 flags
& NI_DGRAM
? "udp" : "tcp");
245 if (servlen
< strlen(sp
->s_name
) + 1)
246 return (EAI_OVERFLOW
);
247 (void) strcpy(serv
, sp
->s_name
);
250 * if service is not in the name server's
251 * database, fill buffer with numeric form for
254 portlen
= snprintf(port_buf
, sizeof (port_buf
),
256 if (servlen
< portlen
+ 1)
257 return (EAI_OVERFLOW
);
258 (void) strcpy(serv
, port_buf
);
266 * addzoneid(sa, host, hostlen)
268 * Appends a zone-id to the input 'host' string if the input sin6_scope_id
269 * is non-zero. The resultant 'host' string would be of the form
270 * 'host'%'zone-id'. Where 'zone-id' can be either an interface name or a
271 * literal interface index.
275 * EAI_MEMORY - an error occured when forming the output string
278 addzoneid(const struct sockaddr_in6
*sa
, char *host
, size_t hostlen
)
280 char zonestr
[LIFNAMSIZ
];
282 size_t addrlen
= strlen(host
);
284 /* make sure zonelen is valid sizeof (<addr>%<zoneid>\0) */
285 if (((zonelen
= getzonestr(sa
, zonestr
, sizeof (zonestr
))) == 0) ||
286 ((addrlen
+ 1 + zonelen
+ 1) > hostlen
)) {
290 /* Create address string of form <addr>%<zoneid> */
291 host
[addrlen
] = '%'; /* place address-zoneid delimiter */
292 (void) strlcpy((host
+ addrlen
+ 1), zonestr
, (zonelen
+ 1));
297 * getzonestr(sa, zonestr)
299 * parse zone string from input sockaddr_in6
301 * Note: This function calls if_indextoname, a very poor interface,
302 * defined in RFC2553, for converting an interface index to an
303 * interface name. Callers of this function must be sure that
304 * zonestr is atleast LIFNAMSIZ in length, since this is the longest
305 * possible value if_indextoname will return.
308 * 0 an error with calling this function occured
309 * >0 zonestr is filled with a valid zoneid string and the return value is the
310 * length of that string.
313 getzonestr(const struct sockaddr_in6
*sa
, char *zonestr
, size_t zonelen
)
315 const in6_addr_t
*addr
;
319 if (zonestr
== NULL
) {
323 addr
= &sa
->sin6_addr
;
325 * Since this implementation only supports link scope addresses,
326 * there is a one-to-one mapping between interface index and
329 ifindex
= sa
->sin6_scope_id
;
331 if ((retstr
= if_indextoname(ifindex
, zonestr
)) != NULL
) {
332 return (strlen(retstr
));
337 * Failed to convert ifindex into an interface name,
338 * simply return the literal value of ifindex as
341 if ((n
= snprintf(zonestr
, zonelen
, "%u",
355 * This is a wrapper function for inet_ntop(). In case the af is AF_INET6
356 * and the address pointed by src is a IPv4-mapped IPv6 address, it
357 * returns printable IPv4 address, not IPv4-mapped IPv6 address. In other cases
358 * it behaves just like inet_ntop().
361 _inet_ntop_native(int af
, const void *src
, char *dst
, size_t size
)
366 if (af
== AF_INET6
) {
367 if (IN6_IS_ADDR_V4MAPPED((struct in6_addr
*)src
)) {
368 IN6_V4MAPPED_TO_INADDR((struct in6_addr
*)src
, &src4
);
369 result
= inet_ntop(AF_INET
, &src4
, dst
, size
);
371 result
= inet_ntop(AF_INET6
, src
, dst
, size
);
374 result
= inet_ntop(af
, src
, dst
, size
);