xref: /openbsd/lib/libc/asr/getnameinfo_async.c (revision caf7b7a2)
1 /*	$OpenBSD: getnameinfo_async.c,v 1.15 2020/12/21 09:40:35 eric 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 <netinet/in.h>
22 #include <arpa/inet.h>
23 #include <arpa/nameser.h>
24 #include <netdb.h>
25 
26 #include <asr.h>
27 #include <errno.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <unistd.h>
31 
32 #include "asr_private.h"
33 
34 static int getnameinfo_async_run(struct asr_query *, struct asr_result *);
35 static int _servname(struct asr_query *);
36 static int _numerichost(struct asr_query *);
37 
38 struct asr_query *
getnameinfo_async(const struct sockaddr * sa,socklen_t slen,char * host,size_t hostlen,char * serv,size_t servlen,int flags,void * asr)39 getnameinfo_async(const struct sockaddr *sa, socklen_t slen, char *host,
40     size_t hostlen, char *serv, size_t servlen, int flags, void *asr)
41 {
42 	struct asr_ctx	 *ac;
43 	struct asr_query *as;
44 
45 	ac = _asr_use_resolver(asr);
46 	if ((as = _asr_async_new(ac, ASR_GETNAMEINFO)) == NULL)
47 		goto abort; /* errno set */
48 	as->as_run = getnameinfo_async_run;
49 
50 	if (sa->sa_family == AF_INET)
51 		memmove(&as->as.ni.sa.sa, sa, sizeof (as->as.ni.sa.sain));
52 	else if (sa->sa_family == AF_INET6)
53 		memmove(&as->as.ni.sa.sa, sa, sizeof (as->as.ni.sa.sain6));
54 
55 	as->as.ni.sa.sa.sa_len = slen;
56 	as->as.ni.hostname = host;
57 	as->as.ni.hostnamelen = hostlen;
58 	as->as.ni.servname = serv;
59 	as->as.ni.servnamelen = servlen;
60 	as->as.ni.flags = flags;
61 
62 	_asr_ctx_unref(ac);
63 	return (as);
64 
65     abort:
66 	if (as)
67 		_asr_async_free(as);
68 	_asr_ctx_unref(ac);
69 	return (NULL);
70 }
71 DEF_WEAK(getnameinfo_async);
72 
73 static int
getnameinfo_async_run(struct asr_query * as,struct asr_result * ar)74 getnameinfo_async_run(struct asr_query *as, struct asr_result *ar)
75 {
76 	void		*addr;
77 	socklen_t	 addrlen;
78 	int		 r;
79 
80     next:
81 	switch (as->as_state) {
82 
83 	case ASR_STATE_INIT:
84 
85 		/* Make sure the parameters are all valid. */
86 
87 		if (as->as.ni.sa.sa.sa_family != AF_INET &&
88 		    as->as.ni.sa.sa.sa_family != AF_INET6) {
89 			ar->ar_gai_errno = EAI_FAMILY;
90 			async_set_state(as, ASR_STATE_HALT);
91 			break;
92 		}
93 
94 		if ((as->as.ni.sa.sa.sa_family == AF_INET &&
95 		    (as->as.ni.sa.sa.sa_len != sizeof (as->as.ni.sa.sain))) ||
96 		    (as->as.ni.sa.sa.sa_family == AF_INET6 &&
97 		    (as->as.ni.sa.sa.sa_len != sizeof (as->as.ni.sa.sain6)))) {
98 			ar->ar_gai_errno = EAI_FAIL;
99 			async_set_state(as, ASR_STATE_HALT);
100 			break;
101 		}
102 
103 		/* Set the service name first, if needed. */
104 		if (_servname(as) == -1) {
105 			ar->ar_gai_errno = EAI_OVERFLOW;
106 			async_set_state(as, ASR_STATE_HALT);
107 			break;
108 		}
109 
110 		if (as->as.ni.hostname == NULL || as->as.ni.hostnamelen == 0) {
111 			ar->ar_gai_errno = 0;
112 			async_set_state(as, ASR_STATE_HALT);
113 			break;
114 		}
115 
116 		if (as->as.ni.flags & NI_NUMERICHOST) {
117 			if (_numerichost(as) == -1) {
118 				if (errno == ENOMEM)
119 					ar->ar_gai_errno = EAI_MEMORY;
120 				else if (errno == ENOSPC)
121 					ar->ar_gai_errno = EAI_OVERFLOW;
122 				else {
123 					ar->ar_errno = errno;
124 					ar->ar_gai_errno = EAI_SYSTEM;
125 				}
126 			} else
127 				ar->ar_gai_errno = 0;
128 			async_set_state(as, ASR_STATE_HALT);
129 			break;
130 		}
131 
132 		if (as->as.ni.sa.sa.sa_family == AF_INET) {
133 			addrlen = sizeof(as->as.ni.sa.sain.sin_addr);
134 			addr = &as->as.ni.sa.sain.sin_addr;
135 		} else {
136 			addrlen = sizeof(as->as.ni.sa.sain6.sin6_addr);
137 			addr = &as->as.ni.sa.sain6.sin6_addr;
138 		}
139 
140 		/*
141 		 * Create a subquery to lookup the address.
142 		 */
143 		as->as_subq = _gethostbyaddr_async_ctx(addr, addrlen,
144 		    as->as.ni.sa.sa.sa_family,
145 		    as->as_ctx);
146 		if (as->as_subq == NULL) {
147 			ar->ar_gai_errno = EAI_MEMORY;
148 			async_set_state(as, ASR_STATE_HALT);
149 			break;
150 		}
151 
152 		async_set_state(as, ASR_STATE_SUBQUERY);
153 		break;
154 
155 	case ASR_STATE_SUBQUERY:
156 
157 		if ((r = asr_run(as->as_subq, ar)) == ASYNC_COND)
158 			return (ASYNC_COND);
159 
160 		/*
161 		 * Request done.
162 		 */
163 		as->as_subq = NULL;
164 
165 		if (ar->ar_hostent == NULL) {
166 			if (as->as.ni.flags & NI_NAMEREQD) {
167 				ar->ar_gai_errno = EAI_NONAME;
168 			} else if (_numerichost(as) == -1) {
169 				if (errno == ENOMEM)
170 					ar->ar_gai_errno = EAI_MEMORY;
171 				else if (errno == ENOSPC)
172 					ar->ar_gai_errno = EAI_OVERFLOW;
173 				else {
174 					ar->ar_errno = errno;
175 					ar->ar_gai_errno = EAI_SYSTEM;
176 				}
177 			} else
178 				ar->ar_gai_errno = 0;
179 		} else {
180 			if (strlcpy(as->as.ni.hostname,
181 			    ar->ar_hostent->h_name,
182 			    as->as.ni.hostnamelen) >= as->as.ni.hostnamelen)
183 				ar->ar_gai_errno = EAI_OVERFLOW;
184 			else
185 				ar->ar_gai_errno = 0;
186 			free(ar->ar_hostent);
187 		}
188 
189 		async_set_state(as, ASR_STATE_HALT);
190 		break;
191 
192 	case ASR_STATE_HALT:
193 		return (ASYNC_DONE);
194 
195 	default:
196 		ar->ar_errno = EOPNOTSUPP;
197 		ar->ar_gai_errno = EAI_SYSTEM;
198 		async_set_state(as, ASR_STATE_HALT);
199 		break;
200 	}
201 	goto next;
202 }
203 
204 
205 /*
206  * Set the service name on the result buffer is not NULL.
207  * return (-1) if the buffer is too small.
208  */
209 static int
_servname(struct asr_query * as)210 _servname(struct asr_query *as)
211 {
212 	struct servent		 s;
213 	struct servent_data	 sd;
214 	int			 port, r;
215 	char			*buf = as->as.ni.servname;
216 	size_t			 n, buflen = as->as.ni.servnamelen;
217 
218 	if (as->as.ni.servname == NULL || as->as.ni.servnamelen == 0)
219 		return (0);
220 
221 	if (as->as.ni.sa.sa.sa_family == AF_INET)
222 		port = as->as.ni.sa.sain.sin_port;
223 	else
224 		port = as->as.ni.sa.sain6.sin6_port;
225 
226 	if (!(as->as.ni.flags & NI_NUMERICSERV)) {
227 		memset(&sd, 0, sizeof (sd));
228 		r = getservbyport_r(port, (as->as.ni.flags & NI_DGRAM) ?
229 		    "udp" : "tcp", &s, &sd);
230 		if (r == 0)
231 			n = strlcpy(buf, s.s_name, buflen);
232 		endservent_r(&sd);
233 		if (r == 0) {
234 			if (n >= buflen)
235 				return (-1);
236 			return (0);
237 		}
238 	}
239 
240 	r = snprintf(buf, buflen, "%u", ntohs(port));
241 	if (r < 0 || r >= buflen)
242 		return (-1);
243 
244 	return (0);
245 }
246 
247 /*
248  * Write the numeric address
249  */
250 static int
_numerichost(struct asr_query * as)251 _numerichost(struct asr_query *as)
252 {
253 	unsigned int	ifidx;
254 	char		scope[IF_NAMESIZE + 1], *ifname;
255 	void		*addr;
256 	char		*buf = as->as.ni.hostname;
257 	size_t		 buflen = as->as.ni.hostnamelen;
258 
259 	if (as->as.ni.sa.sa.sa_family == AF_INET)
260 		addr = &as->as.ni.sa.sain.sin_addr;
261 	else
262 		addr = &as->as.ni.sa.sain6.sin6_addr;
263 
264 	if (inet_ntop(as->as.ni.sa.sa.sa_family, addr, buf, buflen) == NULL)
265 		return (-1); /* errno set */
266 
267 	if (as->as.ni.sa.sa.sa_family == AF_INET6 &&
268 	    as->as.ni.sa.sain6.sin6_scope_id) {
269 
270 		scope[0] = SCOPE_DELIMITER;
271 		scope[1] = '\0';
272 
273 		ifidx = as->as.ni.sa.sain6.sin6_scope_id;
274 		ifname = NULL;
275 
276 		if (IN6_IS_ADDR_LINKLOCAL(&as->as.ni.sa.sain6.sin6_addr) ||
277 		    IN6_IS_ADDR_MC_LINKLOCAL(&as->as.ni.sa.sain6.sin6_addr) ||
278 		    IN6_IS_ADDR_MC_INTFACELOCAL(&as->as.ni.sa.sain6.sin6_addr))
279 			ifname = if_indextoname(ifidx, scope + 1);
280 
281 		if (ifname == NULL)
282 			snprintf(scope + 1, sizeof(scope) - 1, "%u", ifidx);
283 
284 		strlcat(buf, scope, buflen);
285 	}
286 
287 	return (0);
288 }
289