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