xref: /openbsd/lib/libc/asr/getnameinfo.c (revision 2c53affb)
1 /*	$OpenBSD: getnameinfo.c,v 1.11 2022/12/27 17:10:06 jmc Exp $	*/
2 /*
3  * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/types.h>
19 #include <sys/socket.h>
20 #include <net/if.h>
21 #include <arpa/inet.h>
22 #include <netinet/in.h>
23 #include <netdb.h>
24 
25 #include <asr.h>
26 #include <errno.h>
27 #include <resolv.h>
28 #include <string.h>
29 
30 static size_t asr_print_addr(const struct sockaddr *, char *, size_t);
31 static size_t asr_print_port(const struct sockaddr *, const char *, char *, size_t);
32 
33 #define SA_IN(sa) ((struct sockaddr_in*)(sa))
34 #define SA_IN6(sa) ((struct sockaddr_in6*)(sa))
35 
36 /*
37  * Print the textual representation (as given by inet_ntop(3)) of the address
38  * set in "sa".
39  *
40  * Return the total length of the string it tried to create or 0 if an error
41  * occurred, in which case errno is set.  On success, the constructed string
42  * is guaranteed to be NUL-terminated.  Overflow must be detected by checking
43  * the returned size against buflen.
44  *
45  */
46 static size_t
asr_print_addr(const struct sockaddr * sa,char * buf,size_t buflen)47 asr_print_addr(const struct sockaddr *sa, char *buf, size_t buflen)
48 {
49 	unsigned int ifidx;
50 	char tmp[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
51 	char scope[IF_NAMESIZE + 1], *ifname;
52 	const void *addr;
53 	size_t s;
54 
55 	switch(sa->sa_family) {
56 	case AF_INET:
57 		addr = &SA_IN(sa)->sin_addr;
58 		break;
59 	case AF_INET6:
60 		addr = &SA_IN6(sa)->sin6_addr;
61 		break;
62 	default:
63 		errno = EINVAL;
64 		return (0);
65 	}
66 
67 	if (inet_ntop(sa->sa_family, addr, tmp, sizeof(tmp)) == NULL)
68 		return (0); /* errno set */
69 
70 	s = strlcpy(buf, tmp, buflen);
71 
72 	if (sa->sa_family == AF_INET6 && SA_IN6(sa)->sin6_scope_id) {
73 
74 		scope[0] = SCOPE_DELIMITER;
75 		scope[1] = '\0';
76 
77 		ifidx = SA_IN6(sa)->sin6_scope_id;
78 		ifname = NULL;
79 
80 		if (IN6_IS_ADDR_LINKLOCAL(&(SA_IN6(sa)->sin6_addr)) ||
81 		    IN6_IS_ADDR_MC_LINKLOCAL(&(SA_IN6(sa)->sin6_addr)) ||
82 		    IN6_IS_ADDR_MC_INTFACELOCAL(&(SA_IN6(sa)->sin6_addr)))
83 			ifname = if_indextoname(ifidx, scope + 1);
84 
85 		if (ifname == NULL)
86 			(void)snprintf(scope + 1, sizeof(scope) - 1, "%u", ifidx);
87 
88 		if (s < buflen)
89 			(void)strlcat(buf, scope, buflen);
90 
91 		s += strlen(scope);
92 	}
93 
94 	return (s);
95 }
96 
97 /*
98  * Print the textual representation of the port set on "sa".
99  *
100  * If proto is not NULL, it is used as parameter to "getservbyport_r(3)" to
101  * return a service name. If it's not set, or if no matching service is found,
102  * it prints the portno.
103  *
104  * Return the total length of the string it tried to create or 0 if an error
105  * occurred, in which case errno is set.  On success, the constructed string
106  * is guaranteed to be NUL-terminated.  Overflow must be detected by checking
107  * the returned size against buflen.
108  */
109 static size_t
asr_print_port(const struct sockaddr * sa,const char * proto,char * buf,size_t buflen)110 asr_print_port(const struct sockaddr *sa, const char *proto, char *buf, size_t buflen)
111 {
112 	struct servent s;
113 	struct servent_data sd;
114 	int port, r, saved_errno;
115 	size_t n;
116 
117 	switch(sa->sa_family) {
118 	case AF_INET:
119 		port = SA_IN(sa)->sin_port;
120 		break;
121 	case AF_INET6:
122 		port = SA_IN6(sa)->sin6_port;
123 		break;
124 	default:
125 		errno = EINVAL;
126 		return (0);
127 	}
128 
129 	if (proto) {
130 		memset(&sd, 0, sizeof (sd));
131 		saved_errno = errno;
132 		r = getservbyport_r(port, proto, &s, &sd);
133 		if (r == 0)
134 			n = strlcpy(buf, s.s_name, buflen);
135 		endservent_r(&sd);
136 		errno = saved_errno;
137 		if (r == 0)
138 			return (n);
139 	}
140 
141 	r = snprintf(buf, buflen, "%u", ntohs(port));
142 	if (r < 0 || r >= buflen) 	/* Actually, this can not happen */
143 		return (0);
144 
145 	return (r);
146 }
147 
148 int
getnameinfo(const struct sockaddr * sa,socklen_t salen,char * host,size_t hostlen,char * serv,size_t servlen,int flags)149 getnameinfo(const struct sockaddr *sa, socklen_t salen, char *host,
150     size_t hostlen, char *serv, size_t servlen, int flags)
151 {
152 	struct asr_query *as;
153 	struct asr_result ar;
154 	int saved_errno = errno;
155 	const char *proto;
156 	size_t r;
157 
158 	/*
159 	 * Take a shortcut if we don't care about hostname,
160 	 * or if NI_NUMERICHOST is set.
161 	 */
162 	if (host == NULL || hostlen == 0 ||
163 	    (host && hostlen && (flags & NI_NUMERICHOST))) {
164 		if (host) {
165 			r = asr_print_addr(sa, host, hostlen);
166 			if (r == 0)
167 				return (EAI_SYSTEM); /* errno set */
168 			if (r >= hostlen)
169 				return (EAI_OVERFLOW);
170 		}
171 
172 		if (serv && servlen) {
173 			if (flags & NI_NUMERICSERV)
174 				proto = NULL;
175 			else
176 				proto = (flags & NI_DGRAM) ? "udp" : "tcp";
177 			r = asr_print_port(sa, proto, serv, servlen);
178 			if (r == 0)
179 				return (EAI_SYSTEM); /* errno set */
180 			if (r >= servlen)
181 				return (EAI_OVERFLOW);
182 		}
183 
184 		errno = saved_errno;
185 		return (0);
186 	}
187 
188 	res_init();
189 
190 	as = getnameinfo_async(sa, salen, host, hostlen, serv, servlen, flags,
191 	    NULL);
192 	if (as == NULL) {
193 		if (errno == ENOMEM) {
194 			errno = saved_errno;
195 			return (EAI_MEMORY);
196 		}
197 		return (EAI_SYSTEM);
198 	}
199 
200 	asr_run_sync(as, &ar);
201 	if (ar.ar_gai_errno == EAI_SYSTEM)
202 		errno = ar.ar_errno;
203 
204 	return (ar.ar_gai_errno);
205 }
206 DEF_WEAK(getnameinfo);
207