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