1 /*
2 * CDDL HEADER START
3 *
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
7 * with the License.
8 *
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.
13 *
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]
19 *
20 * CDDL HEADER END
21 */
22
23 /*
24 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
25 * Use is subject to license terms.
26 */
27
28 #pragma ident "%Z%%M% %I% %E% SMI"
29
30 #include <sys/types.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <strings.h>
34 #include <stdio.h>
35 #include <ctype.h>
36 #include <sys/socket.h>
37 #include <netdb.h>
38 #include <arpa/inet.h>
39 #include <nss_dbdefs.h>
40 #include <netinet/in.h>
41 #include <sys/socket.h>
42 #include <net/if.h>
43
44 #define sa2sin(x) ((struct sockaddr_in *)(x))
45 #define sa2sin6(x) ((struct sockaddr_in6 *)(x))
46
47 #define NI_MASK (NI_NOFQDN | NI_NUMERICHOST | NI_NAMEREQD | NI_NUMERICSERV | \
48 NI_DGRAM | NI_WITHSCOPEID)
49
50 static int addzoneid(const struct sockaddr_in6 *sa, char *host,
51 size_t hostlen);
52 static size_t getzonestr(const struct sockaddr_in6 *sa, char *zonestr,
53 size_t zonelen);
54 static const char *_inet_ntop_native();
55 /*
56 * getnameinfo:
57 *
58 * Purpose:
59 * Routine for performing Address-to-nodename in a
60 * protocol-independent fashion.
61 * Description:
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.
65 * Input Parameters:
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
74 * domain name (FQDN).
75 * NI_NUMERICHOST - Always return numeric form of the host's
76 * address.
77 * NI_NAMEREQD - If hostname cannot be located in database,
78 * don't return numeric form of address - return
79 * an error instead.
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".
85 * Output Parameters:
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.
90 * Return Value:
91 * This function indicates successful completion by a zero return
92 * value; a non-zero return value indicates failure.
93 */
94 int
getnameinfo(const struct sockaddr * sa,socklen_t salen,char * host,socklen_t hostlen,char * serv,socklen_t servlen,int flags)95 getnameinfo(const struct sockaddr *sa, socklen_t salen,
96 char *host, socklen_t hostlen,
97 char *serv, socklen_t servlen, int flags)
98 {
99 char *addr;
100 size_t alen, slen;
101 in_port_t port;
102 int errnum;
103 int err;
104
105 /* Verify correctness of buffer lengths */
106 if ((hostlen == 0) && (servlen == 0))
107 return (EAI_FAIL);
108 /* Verify correctness of possible flag settings */
109 if ((flags != 0) && (flags & ~NI_MASK))
110 return (EAI_BADFLAGS);
111 if (sa == NULL)
112 return (EAI_ADDRFAMILY);
113 switch (sa->sa_family) {
114 case AF_INET:
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 */
119 break;
120 case AF_INET6:
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 */
125 break;
126 default:
127 return (EAI_FAMILY);
128 }
129 if (salen != slen)
130 return (EAI_FAIL);
131 /*
132 * Case 1: if Caller sets hostlen != 0, then
133 * fill in "host" buffer that user passed in
134 * with appropriate text string.
135 */
136 if (hostlen != 0) {
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)
141 return (EAI_SYSTEM);
142 } else {
143 struct hostent *hp;
144
145 /* Caller wants the name of host */
146 hp = getipnodebyaddr(addr, alen, sa->sa_family,
147 &errnum);
148 if (hp != NULL) {
149 if (flags & NI_NOFQDN) {
150 char *dot;
151 /*
152 * Caller doesn't want fully-qualified
153 * name.
154 */
155 dot = strchr(hp->h_name, '.');
156 if (dot != NULL)
157 *dot = '\0';
158 }
159 if (strlen(hp->h_name) + 1 > hostlen) {
160 freehostent(hp);
161 return (EAI_OVERFLOW);
162 }
163 (void) strcpy(host, hp->h_name);
164 freehostent(hp);
165 } else {
166 /*
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
170 * numeric address.
171 */
172 if (flags & NI_NAMEREQD) {
173 switch (errnum) {
174 case HOST_NOT_FOUND:
175 return (EAI_NONAME);
176 case TRY_AGAIN:
177 return (EAI_AGAIN);
178 case NO_RECOVERY:
179 return (EAI_FAIL);
180 case NO_ADDRESS:
181 return (EAI_NODATA);
182 default:
183 return (EAI_SYSTEM);
184 }
185 }
186 if (_inet_ntop_native(sa->sa_family, addr,
187 host, hostlen) == NULL)
188 return (EAI_SYSTEM);
189 }
190 }
191
192 /*
193 * Check for a non-zero sin6_scope_id, indicating a
194 * zone-id needs to be appended to the resultant 'host'
195 * string.
196 */
197 if ((sa->sa_family == AF_INET6) &&
198 (sa2sin6(sa)->sin6_scope_id != 0)) {
199 /*
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
207 * EAI_NONAME.
208 */
209 if (IN6_IS_ADDR_LINKSCOPE(&(sa2sin6(sa)->sin6_addr))) {
210 if ((err = addzoneid(sa2sin6(sa), host,
211 hostlen)) != 0) {
212 return (err);
213 }
214 } else {
215 return (EAI_NONAME);
216 }
217 }
218 }
219 /*
220 * Case 2: if Caller sets servlen != 0, then
221 * fill in "serv" buffer that user passed in
222 * with appropriate text string.
223 */
224 if (servlen != 0) {
225 char port_buf[10];
226 int portlen;
227
228 if (flags & NI_NUMERICSERV) {
229 /* Caller wants the textual form of the port number */
230 portlen = snprintf(port_buf, sizeof (port_buf), "%hu",
231 ntohs(port));
232 if (servlen < portlen + 1)
233 return (EAI_OVERFLOW);
234 (void) strcpy(serv, port_buf);
235 } else {
236 struct servent *sp;
237 /*
238 * Caller wants the name of the service.
239 * If NI_DGRAM is set, get service name for
240 * specified port for udp.
241 */
242 sp = getservbyport(port,
243 flags & NI_DGRAM ? "udp" : "tcp");
244 if (sp != NULL) {
245 if (servlen < strlen(sp->s_name) + 1)
246 return (EAI_OVERFLOW);
247 (void) strcpy(serv, sp->s_name);
248 } else {
249 /*
250 * if service is not in the name server's
251 * database, fill buffer with numeric form for
252 * port number.
253 */
254 portlen = snprintf(port_buf, sizeof (port_buf),
255 "%hu", ntohs(port));
256 if (servlen < portlen + 1)
257 return (EAI_OVERFLOW);
258 (void) strcpy(serv, port_buf);
259 }
260 }
261 }
262 return (0);
263 }
264
265 /*
266 * addzoneid(sa, host, hostlen)
267 *
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.
272 *
273 * Return Values:
274 * 0 - on success
275 * EAI_MEMORY - an error occured when forming the output string
276 */
277 static int
addzoneid(const struct sockaddr_in6 * sa,char * host,size_t hostlen)278 addzoneid(const struct sockaddr_in6 *sa, char *host, size_t hostlen)
279 {
280 char zonestr[LIFNAMSIZ];
281 size_t zonelen;
282 size_t addrlen = strlen(host);
283
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)) {
287 return (EAI_MEMORY);
288 }
289
290 /* Create address string of form <addr>%<zoneid> */
291 host[addrlen] = '%'; /* place address-zoneid delimiter */
292 (void) strlcpy((host + addrlen + 1), zonestr, (zonelen + 1));
293 return (0);
294 }
295
296 /*
297 * getzonestr(sa, zonestr)
298 *
299 * parse zone string from input sockaddr_in6
300 *
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.
306 *
307 * Return values:
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.
311 */
312 static size_t
getzonestr(const struct sockaddr_in6 * sa,char * zonestr,size_t zonelen)313 getzonestr(const struct sockaddr_in6 *sa, char *zonestr, size_t zonelen)
314 {
315 const in6_addr_t *addr;
316 uint32_t ifindex;
317 char *retstr;
318
319 if (zonestr == NULL) {
320 return (0);
321 }
322
323 addr = &sa->sin6_addr;
324 /*
325 * Since this implementation only supports link scope addresses,
326 * there is a one-to-one mapping between interface index and
327 * sin6_scope_id.
328 */
329 ifindex = sa->sin6_scope_id;
330
331 if ((retstr = if_indextoname(ifindex, zonestr)) != NULL) {
332 return (strlen(retstr));
333 } else {
334 int n;
335
336 /*
337 * Failed to convert ifindex into an interface name,
338 * simply return the literal value of ifindex as
339 * a string.
340 */
341 if ((n = snprintf(zonestr, zonelen, "%u",
342 ifindex)) < 0) {
343 return (0);
344 } else {
345 if (n >= zonelen) {
346 return (0);
347 }
348 return (n);
349 }
350 }
351 }
352
353
354 /*
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().
359 */
360 static const char *
_inet_ntop_native(int af,const void * src,char * dst,size_t size)361 _inet_ntop_native(int af, const void *src, char *dst, size_t size)
362 {
363 struct in_addr src4;
364 const char *result;
365
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);
370 } else {
371 result = inet_ntop(AF_INET6, src, dst, size);
372 }
373 } else {
374 result = inet_ntop(af, src, dst, size);
375 }
376
377 return (result);
378 }
379