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