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