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 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 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 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 * 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