Home | History | Annotate | Download | only in inet
      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
     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
    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
    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 *
    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