xref: /openbsd-src/lib/libc/asr/getnameinfo_async.c (revision b44da6278dfc5a9ac4bcab6662bc7def6c347a68)
1*b44da627Seric /*	$OpenBSD: getnameinfo_async.c,v 1.1 2012/04/14 09:24:18 eric Exp $	*/
2*b44da627Seric /*
3*b44da627Seric  * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
4*b44da627Seric  *
5*b44da627Seric  * Permission to use, copy, modify, and distribute this software for any
6*b44da627Seric  * purpose with or without fee is hereby granted, provided that the above
7*b44da627Seric  * copyright notice and this permission notice appear in all copies.
8*b44da627Seric  *
9*b44da627Seric  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10*b44da627Seric  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11*b44da627Seric  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12*b44da627Seric  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13*b44da627Seric  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14*b44da627Seric  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15*b44da627Seric  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16*b44da627Seric  */
17*b44da627Seric #include <sys/types.h>
18*b44da627Seric #include <sys/socket.h>
19*b44da627Seric #include <netinet/in.h>
20*b44da627Seric #include <arpa/inet.h>
21*b44da627Seric #include <arpa/nameser.h>
22*b44da627Seric 
23*b44da627Seric #include <err.h>
24*b44da627Seric #include <errno.h>
25*b44da627Seric #include <stdlib.h>
26*b44da627Seric #include <string.h>
27*b44da627Seric #include <unistd.h>
28*b44da627Seric 
29*b44da627Seric #include "asr.h"
30*b44da627Seric #include "asr_private.h"
31*b44da627Seric 
32*b44da627Seric static int getnameinfo_async_run(struct async *, struct async_res *);
33*b44da627Seric static int _servname(struct async *);
34*b44da627Seric static int _numerichost(struct async *);
35*b44da627Seric 
36*b44da627Seric struct async *
37*b44da627Seric getnameinfo_async(const struct sockaddr *sa, socklen_t slen, char *host,
38*b44da627Seric     size_t hostlen, char *serv, size_t servlen, int flags, struct asr *asr)
39*b44da627Seric {
40*b44da627Seric 	struct asr_ctx	*ac;
41*b44da627Seric 	struct async	*as;
42*b44da627Seric 
43*b44da627Seric 	ac = asr_use_resolver(asr);
44*b44da627Seric 	if ((as = async_new(ac, ASR_GETNAMEINFO)) == NULL)
45*b44da627Seric 		goto abort; /* errno set */
46*b44da627Seric 	as->as_run = getnameinfo_async_run;
47*b44da627Seric 
48*b44da627Seric 	if (sa->sa_family == AF_INET)
49*b44da627Seric 		memmove(&as->as.ni.sa.sa, sa, sizeof (as->as.ni.sa.sain));
50*b44da627Seric 	else if (sa->sa_family == AF_INET6)
51*b44da627Seric 		memmove(&as->as.ni.sa.sa, sa, sizeof (as->as.ni.sa.sain6));
52*b44da627Seric 
53*b44da627Seric 	as->as.ni.sa.sa.sa_len = slen;
54*b44da627Seric 	as->as.ni.hostname = host;
55*b44da627Seric 	as->as.ni.hostnamelen = hostlen;
56*b44da627Seric 	as->as.ni.servname = serv;
57*b44da627Seric 	as->as.ni.servnamelen = servlen;
58*b44da627Seric 	as->as.ni.flags = flags;
59*b44da627Seric 
60*b44da627Seric 	asr_ctx_unref(ac);
61*b44da627Seric 	return (as);
62*b44da627Seric 
63*b44da627Seric     abort:
64*b44da627Seric 	if (as)
65*b44da627Seric 		async_free(as);
66*b44da627Seric 	asr_ctx_unref(ac);
67*b44da627Seric 	return (NULL);
68*b44da627Seric }
69*b44da627Seric 
70*b44da627Seric static int
71*b44da627Seric getnameinfo_async_run(struct async *as, struct async_res *ar)
72*b44da627Seric {
73*b44da627Seric 	void		*addr;
74*b44da627Seric 	socklen_t	 addrlen;
75*b44da627Seric 	int		 r;
76*b44da627Seric 
77*b44da627Seric     next:
78*b44da627Seric 	switch(as->as_state) {
79*b44da627Seric 
80*b44da627Seric 	case ASR_STATE_INIT:
81*b44da627Seric 
82*b44da627Seric 		/* Make sure the parameters are all valid. */
83*b44da627Seric 
84*b44da627Seric 		if (as->as.ni.sa.sa.sa_family != AF_INET &&
85*b44da627Seric 		    as->as.ni.sa.sa.sa_family != AF_INET6) {
86*b44da627Seric 			ar->ar_gai_errno = EAI_FAMILY;
87*b44da627Seric 			async_set_state(as, ASR_STATE_HALT);
88*b44da627Seric 			break;
89*b44da627Seric 		}
90*b44da627Seric 
91*b44da627Seric 		if ((as->as.ni.sa.sa.sa_family == AF_INET &&
92*b44da627Seric 		    (as->as.ni.sa.sa.sa_len != sizeof (as->as.ni.sa.sain))) ||
93*b44da627Seric 		    (as->as.ni.sa.sa.sa_family == AF_INET6 &&
94*b44da627Seric 		    (as->as.ni.sa.sa.sa_len != sizeof (as->as.ni.sa.sain6)))) {
95*b44da627Seric 			ar->ar_gai_errno = EAI_FAIL;
96*b44da627Seric 			async_set_state(as, ASR_STATE_HALT);
97*b44da627Seric 			break;
98*b44da627Seric 		}
99*b44da627Seric 
100*b44da627Seric 		/* Set the service name first, if needed. */
101*b44da627Seric 		if (_servname(as) == -1) {
102*b44da627Seric 			ar->ar_gai_errno = EAI_OVERFLOW;
103*b44da627Seric 			async_set_state(as, ASR_STATE_HALT);
104*b44da627Seric 			break;
105*b44da627Seric 		}
106*b44da627Seric 
107*b44da627Seric 		if (as->as.ni.hostname == NULL || as->as.ni.hostnamelen == 0) {
108*b44da627Seric 			ar->ar_gai_errno = 0;
109*b44da627Seric 			async_set_state(as, ASR_STATE_HALT);
110*b44da627Seric 			break;
111*b44da627Seric 		}
112*b44da627Seric 
113*b44da627Seric 		if (as->as.ni.flags & NI_NUMERICHOST) {
114*b44da627Seric 			if (_numerichost(as) == -1) {
115*b44da627Seric 				ar->ar_errno = errno;
116*b44da627Seric 				if (ar->ar_errno == ENOMEM)
117*b44da627Seric 					ar->ar_gai_errno = EAI_MEMORY;
118*b44da627Seric 				else if (ar->ar_errno == ENOSPC)
119*b44da627Seric 					ar->ar_gai_errno = EAI_OVERFLOW;
120*b44da627Seric 				else
121*b44da627Seric 					ar->ar_gai_errno = EAI_SYSTEM;
122*b44da627Seric 			} else
123*b44da627Seric 				ar->ar_gai_errno = 0;
124*b44da627Seric 			async_set_state(as, ASR_STATE_HALT);
125*b44da627Seric 			break;
126*b44da627Seric 		}
127*b44da627Seric 
128*b44da627Seric 		if (as->as.ni.sa.sa.sa_family == AF_INET) {
129*b44da627Seric 			addrlen = sizeof(as->as.ni.sa.sain.sin_addr);
130*b44da627Seric 			addr = &as->as.ni.sa.sain.sin_addr;
131*b44da627Seric 		} else {
132*b44da627Seric 			addrlen = sizeof(as->as.ni.sa.sain6.sin6_addr);
133*b44da627Seric 			addr = &as->as.ni.sa.sain6.sin6_addr;
134*b44da627Seric 		}
135*b44da627Seric 
136*b44da627Seric 		/*
137*b44da627Seric 		 * Create a subquery to lookup the address.
138*b44da627Seric 		 */
139*b44da627Seric 		as->as.ni.subq = gethostbyaddr_async_ctx(addr, addrlen,
140*b44da627Seric 		    as->as.ni.sa.sa.sa_family,
141*b44da627Seric 		    as->as_ctx);
142*b44da627Seric 		if (as->as.ni.subq == NULL) {
143*b44da627Seric 			ar->ar_errno = errno;
144*b44da627Seric 			ar->ar_gai_errno = EAI_MEMORY;
145*b44da627Seric 			async_set_state(as, ASR_STATE_HALT);
146*b44da627Seric 			break;
147*b44da627Seric 		}
148*b44da627Seric 
149*b44da627Seric 		async_set_state(as, ASR_STATE_SUBQUERY);
150*b44da627Seric 		break;
151*b44da627Seric 
152*b44da627Seric 	case ASR_STATE_SUBQUERY:
153*b44da627Seric 
154*b44da627Seric 		if ((r = async_run(as->as.ni.subq, ar)) == ASYNC_COND)
155*b44da627Seric 			return (ASYNC_COND);
156*b44da627Seric 
157*b44da627Seric 		/*
158*b44da627Seric 		 * Request done.
159*b44da627Seric 		 */
160*b44da627Seric 		as->as.ni.subq = NULL;
161*b44da627Seric 
162*b44da627Seric 		if (ar->ar_hostent == NULL) {
163*b44da627Seric 			if (as->as.ni.flags & NI_NAMEREQD) {
164*b44da627Seric 				ar->ar_gai_errno = EAI_NONAME;
165*b44da627Seric 			} else if (_numerichost(as) == -1) {
166*b44da627Seric 				ar->ar_errno = errno;
167*b44da627Seric 				if (ar->ar_errno == ENOMEM)
168*b44da627Seric 					ar->ar_gai_errno = EAI_MEMORY;
169*b44da627Seric 				else if (ar->ar_errno == ENOSPC)
170*b44da627Seric 					ar->ar_gai_errno = EAI_OVERFLOW;
171*b44da627Seric 				else
172*b44da627Seric 					ar->ar_gai_errno = EAI_SYSTEM;
173*b44da627Seric 			} else
174*b44da627Seric 				ar->ar_gai_errno = 0;
175*b44da627Seric 		} else {
176*b44da627Seric 			if (strlcpy(as->as.ni.hostname,
177*b44da627Seric 			    ar->ar_hostent->h_name,
178*b44da627Seric 			    as->as.ni.hostnamelen) >= as->as.ni.hostnamelen)
179*b44da627Seric 				ar->ar_gai_errno = EAI_OVERFLOW;
180*b44da627Seric 			else
181*b44da627Seric 				ar->ar_gai_errno = 0;
182*b44da627Seric 			freehostent(ar->ar_hostent);
183*b44da627Seric 		}
184*b44da627Seric 
185*b44da627Seric 		async_set_state(as, ASR_STATE_HALT);
186*b44da627Seric 		break;
187*b44da627Seric 
188*b44da627Seric 	case ASR_STATE_HALT:
189*b44da627Seric 		return (ASYNC_DONE);
190*b44da627Seric 
191*b44da627Seric 	default:
192*b44da627Seric 		ar->ar_errno = EOPNOTSUPP;
193*b44da627Seric 		ar->ar_h_errno = NETDB_INTERNAL;
194*b44da627Seric 		ar->ar_gai_errno = EAI_SYSTEM;
195*b44da627Seric 		async_set_state(as, ASR_STATE_HALT);
196*b44da627Seric                 break;
197*b44da627Seric 	}
198*b44da627Seric 	goto next;
199*b44da627Seric }
200*b44da627Seric 
201*b44da627Seric 
202*b44da627Seric /*
203*b44da627Seric  * Set the service name on the result buffer is not NULL.
204*b44da627Seric  * return (-1) if the buffer is too small.
205*b44da627Seric  */
206*b44da627Seric static int
207*b44da627Seric _servname(struct async *as)
208*b44da627Seric {
209*b44da627Seric 	struct servent		 s;
210*b44da627Seric 	struct servent_data	 sd;
211*b44da627Seric 	int			 port, r;
212*b44da627Seric 	char			*buf = as->as.ni.servname;
213*b44da627Seric 	size_t			 buflen = as->as.ni.servnamelen;
214*b44da627Seric 
215*b44da627Seric 	if (as->as.ni.servname == NULL || as->as.ni.servnamelen == 0)
216*b44da627Seric 		return (0);
217*b44da627Seric 
218*b44da627Seric 	if (as->as.ni.sa.sa.sa_family == AF_INET)
219*b44da627Seric 		port = as->as.ni.sa.sain.sin_port;
220*b44da627Seric 	else
221*b44da627Seric 		port = as->as.ni.sa.sain6.sin6_port;
222*b44da627Seric 
223*b44da627Seric 	if (!(as->as.ni.flags & NI_NUMERICSERV)) {
224*b44da627Seric 		memset(&sd, 0, sizeof (sd));
225*b44da627Seric 		if (getservbyport_r(port,
226*b44da627Seric 		    (as->as.ni.flags & NI_DGRAM) ? "udp" : "tcp",
227*b44da627Seric 		    &s, &sd) != -1) {
228*b44da627Seric 			r = strlcpy(buf, s.s_name, buflen) >= buflen;
229*b44da627Seric 			endservent_r(&sd);
230*b44da627Seric 			return (r ? -1 : 0);
231*b44da627Seric 		}
232*b44da627Seric 	}
233*b44da627Seric 
234*b44da627Seric 	r = snprintf(buf, buflen, "%u", ntohs(port));
235*b44da627Seric 	if (r == -1 || r >= buflen)
236*b44da627Seric 		return (-1);
237*b44da627Seric 
238*b44da627Seric 	return (0);
239*b44da627Seric }
240*b44da627Seric 
241*b44da627Seric /*
242*b44da627Seric  * Write the numeric address
243*b44da627Seric  */
244*b44da627Seric static int
245*b44da627Seric _numerichost(struct async *as)
246*b44da627Seric {
247*b44da627Seric 	void	*addr;
248*b44da627Seric 	char	*buf = as->as.ni.hostname;
249*b44da627Seric 	size_t	 buflen = as->as.ni.hostnamelen;
250*b44da627Seric 
251*b44da627Seric 	if (as->as.ni.sa.sa.sa_family == AF_INET)
252*b44da627Seric 		addr = &as->as.ni.sa.sain.sin_addr;
253*b44da627Seric 	else
254*b44da627Seric 		addr = &as->as.ni.sa.sain6.sin6_addr;
255*b44da627Seric 
256*b44da627Seric 	if (inet_ntop(as->as.ni.sa.sa.sa_family, addr, buf, buflen) == NULL)
257*b44da627Seric 		/* errno set */
258*b44da627Seric 		return (-1);
259*b44da627Seric 
260*b44da627Seric 	return (0);
261*b44da627Seric }
262