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