xref: /openbsd-src/lib/libc/asr/getnameinfo.c (revision 2c53affbcc0119d6480b86c18f2790523b6a0aad)
1*2c53affbSjmc /*	$OpenBSD: getnameinfo.c,v 1.11 2022/12/27 17:10:06 jmc Exp $	*/
28082e013Seric /*
38082e013Seric  * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
48082e013Seric  *
58082e013Seric  * Permission to use, copy, modify, and distribute this software for any
68082e013Seric  * purpose with or without fee is hereby granted, provided that the above
78082e013Seric  * copyright notice and this permission notice appear in all copies.
88082e013Seric  *
98082e013Seric  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
108082e013Seric  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
118082e013Seric  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
128082e013Seric  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
138082e013Seric  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
148082e013Seric  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
158082e013Seric  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
168082e013Seric  */
178082e013Seric 
188082e013Seric #include <sys/types.h>
19d216d6b1Seric #include <sys/socket.h>
204945baa2Seric #include <net/if.h>
214945baa2Seric #include <arpa/inet.h>
228082e013Seric #include <netinet/in.h>
23d216d6b1Seric #include <netdb.h>
248082e013Seric 
25d216d6b1Seric #include <asr.h>
268082e013Seric #include <errno.h>
278082e013Seric #include <resolv.h>
284945baa2Seric #include <string.h>
294945baa2Seric 
304945baa2Seric static size_t asr_print_addr(const struct sockaddr *, char *, size_t);
314945baa2Seric static size_t asr_print_port(const struct sockaddr *, const char *, char *, size_t);
324945baa2Seric 
334945baa2Seric #define SA_IN(sa) ((struct sockaddr_in*)(sa))
344945baa2Seric #define SA_IN6(sa) ((struct sockaddr_in6*)(sa))
354945baa2Seric 
364945baa2Seric /*
374945baa2Seric  * Print the textual representation (as given by inet_ntop(3)) of the address
384945baa2Seric  * set in "sa".
394945baa2Seric  *
404945baa2Seric  * Return the total length of the string it tried to create or 0 if an error
41*2c53affbSjmc  * occurred, in which case errno is set.  On success, the constructed string
424945baa2Seric  * is guaranteed to be NUL-terminated.  Overflow must be detected by checking
434945baa2Seric  * the returned size against buflen.
444945baa2Seric  *
454945baa2Seric  */
464945baa2Seric static size_t
asr_print_addr(const struct sockaddr * sa,char * buf,size_t buflen)474945baa2Seric asr_print_addr(const struct sockaddr *sa, char *buf, size_t buflen)
484945baa2Seric {
494945baa2Seric 	unsigned int ifidx;
504945baa2Seric 	char tmp[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
514945baa2Seric 	char scope[IF_NAMESIZE + 1], *ifname;
524945baa2Seric 	const void *addr;
534945baa2Seric 	size_t s;
544945baa2Seric 
554945baa2Seric 	switch(sa->sa_family) {
564945baa2Seric 	case AF_INET:
574945baa2Seric 		addr = &SA_IN(sa)->sin_addr;
584945baa2Seric 		break;
594945baa2Seric 	case AF_INET6:
604945baa2Seric 		addr = &SA_IN6(sa)->sin6_addr;
614945baa2Seric 		break;
624945baa2Seric 	default:
634945baa2Seric 		errno = EINVAL;
644945baa2Seric 		return (0);
654945baa2Seric 	}
664945baa2Seric 
674945baa2Seric 	if (inet_ntop(sa->sa_family, addr, tmp, sizeof(tmp)) == NULL)
684945baa2Seric 		return (0); /* errno set */
694945baa2Seric 
704945baa2Seric 	s = strlcpy(buf, tmp, buflen);
714945baa2Seric 
724945baa2Seric 	if (sa->sa_family == AF_INET6 && SA_IN6(sa)->sin6_scope_id) {
734945baa2Seric 
744945baa2Seric 		scope[0] = SCOPE_DELIMITER;
754945baa2Seric 		scope[1] = '\0';
764945baa2Seric 
774945baa2Seric 		ifidx = SA_IN6(sa)->sin6_scope_id;
784945baa2Seric 		ifname = NULL;
794945baa2Seric 
804945baa2Seric 		if (IN6_IS_ADDR_LINKLOCAL(&(SA_IN6(sa)->sin6_addr)) ||
814945baa2Seric 		    IN6_IS_ADDR_MC_LINKLOCAL(&(SA_IN6(sa)->sin6_addr)) ||
824945baa2Seric 		    IN6_IS_ADDR_MC_INTFACELOCAL(&(SA_IN6(sa)->sin6_addr)))
834945baa2Seric 			ifname = if_indextoname(ifidx, scope + 1);
844945baa2Seric 
854945baa2Seric 		if (ifname == NULL)
864945baa2Seric 			(void)snprintf(scope + 1, sizeof(scope) - 1, "%u", ifidx);
874945baa2Seric 
884945baa2Seric 		if (s < buflen)
894945baa2Seric 			(void)strlcat(buf, scope, buflen);
904945baa2Seric 
914945baa2Seric 		s += strlen(scope);
924945baa2Seric 	}
934945baa2Seric 
944945baa2Seric 	return (s);
954945baa2Seric }
964945baa2Seric 
974945baa2Seric /*
984945baa2Seric  * Print the textual representation of the port set on "sa".
994945baa2Seric  *
1004945baa2Seric  * If proto is not NULL, it is used as parameter to "getservbyport_r(3)" to
1014945baa2Seric  * return a service name. If it's not set, or if no matching service is found,
1024945baa2Seric  * it prints the portno.
1034945baa2Seric  *
1044945baa2Seric  * Return the total length of the string it tried to create or 0 if an error
105*2c53affbSjmc  * occurred, in which case errno is set.  On success, the constructed string
1064945baa2Seric  * is guaranteed to be NUL-terminated.  Overflow must be detected by checking
1074945baa2Seric  * the returned size against buflen.
1084945baa2Seric  */
1094945baa2Seric static size_t
asr_print_port(const struct sockaddr * sa,const char * proto,char * buf,size_t buflen)1104945baa2Seric asr_print_port(const struct sockaddr *sa, const char *proto, char *buf, size_t buflen)
1114945baa2Seric {
1124945baa2Seric 	struct servent s;
1134945baa2Seric 	struct servent_data sd;
1144945baa2Seric 	int port, r, saved_errno;
1154945baa2Seric 	size_t n;
1164945baa2Seric 
1174945baa2Seric 	switch(sa->sa_family) {
1184945baa2Seric 	case AF_INET:
1194945baa2Seric 		port = SA_IN(sa)->sin_port;
1204945baa2Seric 		break;
1214945baa2Seric 	case AF_INET6:
1224945baa2Seric 		port = SA_IN6(sa)->sin6_port;
1234945baa2Seric 		break;
1244945baa2Seric 	default:
1254945baa2Seric 		errno = EINVAL;
1264945baa2Seric 		return (0);
1274945baa2Seric 	}
1284945baa2Seric 
1294945baa2Seric 	if (proto) {
1304945baa2Seric 		memset(&sd, 0, sizeof (sd));
1314945baa2Seric 		saved_errno = errno;
132caf7b7a2Seric 		r = getservbyport_r(port, proto, &s, &sd);
133caf7b7a2Seric 		if (r == 0)
1344945baa2Seric 			n = strlcpy(buf, s.s_name, buflen);
1354945baa2Seric 		endservent_r(&sd);
1364945baa2Seric 		errno = saved_errno;
137caf7b7a2Seric 		if (r == 0)
138caf7b7a2Seric 			return (n);
1394945baa2Seric 	}
1404945baa2Seric 
1414945baa2Seric 	r = snprintf(buf, buflen, "%u", ntohs(port));
142515e489cSderaadt 	if (r < 0 || r >= buflen) 	/* Actually, this can not happen */
1434945baa2Seric 		return (0);
1444945baa2Seric 
1454945baa2Seric 	return (r);
1464945baa2Seric }
1478082e013Seric 
1488082e013Seric int
getnameinfo(const struct sockaddr * sa,socklen_t salen,char * host,size_t hostlen,char * serv,size_t servlen,int flags)1498082e013Seric getnameinfo(const struct sockaddr *sa, socklen_t salen, char *host,
1508082e013Seric     size_t hostlen, char *serv, size_t servlen, int flags)
1518082e013Seric {
1525be03f8fSeric 	struct asr_query *as;
1535be03f8fSeric 	struct asr_result ar;
1548082e013Seric 	int saved_errno = errno;
1554945baa2Seric 	const char *proto;
1564945baa2Seric 	size_t r;
1574945baa2Seric 
1585f304f5aSderaadt 	/*
1595f304f5aSderaadt 	 * Take a shortcut if we don't care about hostname,
1605f304f5aSderaadt 	 * or if NI_NUMERICHOST is set.
1615f304f5aSderaadt 	 */
1625f304f5aSderaadt 	if (host == NULL || hostlen == 0 ||
1635f304f5aSderaadt 	    (host && hostlen && (flags & NI_NUMERICHOST))) {
1644945baa2Seric 		if (host) {
1654945baa2Seric 			r = asr_print_addr(sa, host, hostlen);
1664945baa2Seric 			if (r == 0)
1674945baa2Seric 				return (EAI_SYSTEM); /* errno set */
1684945baa2Seric 			if (r >= hostlen)
1694945baa2Seric 				return (EAI_OVERFLOW);
1704945baa2Seric 		}
1714945baa2Seric 
1724945baa2Seric 		if (serv && servlen) {
1734945baa2Seric 			if (flags & NI_NUMERICSERV)
1744945baa2Seric 				proto = NULL;
1754945baa2Seric 			else
1764945baa2Seric 				proto = (flags & NI_DGRAM) ? "udp" : "tcp";
1774945baa2Seric 			r = asr_print_port(sa, proto, serv, servlen);
1784945baa2Seric 			if (r == 0)
1794945baa2Seric 				return (EAI_SYSTEM); /* errno set */
1804945baa2Seric 			if (r >= servlen)
1814945baa2Seric 				return (EAI_OVERFLOW);
1824945baa2Seric 		}
1834945baa2Seric 
1844945baa2Seric 		errno = saved_errno;
1854945baa2Seric 		return (0);
1864945baa2Seric 	}
1878082e013Seric 
1881ed934d0Seric 	res_init();
1891ed934d0Seric 
1908082e013Seric 	as = getnameinfo_async(sa, salen, host, hostlen, serv, servlen, flags,
1918082e013Seric 	    NULL);
1928082e013Seric 	if (as == NULL) {
1938082e013Seric 		if (errno == ENOMEM) {
1948082e013Seric 			errno = saved_errno;
1958082e013Seric 			return (EAI_MEMORY);
1968082e013Seric 		}
1978082e013Seric 		return (EAI_SYSTEM);
1988082e013Seric 	}
1998082e013Seric 
2005be03f8fSeric 	asr_run_sync(as, &ar);
2018082e013Seric 	if (ar.ar_gai_errno == EAI_SYSTEM)
2028082e013Seric 		errno = ar.ar_errno;
2038082e013Seric 
2048082e013Seric 	return (ar.ar_gai_errno);
2058082e013Seric }
20606201dd4Sguenther DEF_WEAK(getnameinfo);
207