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