1*caf7b7a2Seric /* $OpenBSD: getnameinfo_async.c,v 1.15 2020/12/21 09:40:35 eric Exp $ */
2b44da627Seric /*
3b44da627Seric * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
4b44da627Seric *
5b44da627Seric * Permission to use, copy, modify, and distribute this software for any
6b44da627Seric * purpose with or without fee is hereby granted, provided that the above
7b44da627Seric * copyright notice and this permission notice appear in all copies.
8b44da627Seric *
9b44da627Seric * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10b44da627Seric * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11b44da627Seric * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12b44da627Seric * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13b44da627Seric * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14b44da627Seric * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15b44da627Seric * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16b44da627Seric */
1780f48568Seric
18b44da627Seric #include <sys/types.h>
19b44da627Seric #include <sys/socket.h>
2078a9ca1aSeric #include <net/if.h>
21b44da627Seric #include <netinet/in.h>
22b44da627Seric #include <arpa/inet.h>
23b44da627Seric #include <arpa/nameser.h>
24d216d6b1Seric #include <netdb.h>
25b44da627Seric
26d216d6b1Seric #include <asr.h>
27b44da627Seric #include <errno.h>
28b44da627Seric #include <stdlib.h>
29b44da627Seric #include <string.h>
30b44da627Seric #include <unistd.h>
31b44da627Seric
32b44da627Seric #include "asr_private.h"
33b44da627Seric
345be03f8fSeric static int getnameinfo_async_run(struct asr_query *, struct asr_result *);
355be03f8fSeric static int _servname(struct asr_query *);
365be03f8fSeric static int _numerichost(struct asr_query *);
37b44da627Seric
385be03f8fSeric 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)39b44da627Seric getnameinfo_async(const struct sockaddr *sa, socklen_t slen, char *host,
405be03f8fSeric size_t hostlen, char *serv, size_t servlen, int flags, void *asr)
41b44da627Seric {
42b44da627Seric struct asr_ctx *ac;
435be03f8fSeric struct asr_query *as;
44b44da627Seric
45253ef892Sderaadt ac = _asr_use_resolver(asr);
46253ef892Sderaadt if ((as = _asr_async_new(ac, ASR_GETNAMEINFO)) == NULL)
47b44da627Seric goto abort; /* errno set */
48b44da627Seric as->as_run = getnameinfo_async_run;
49b44da627Seric
50b44da627Seric if (sa->sa_family == AF_INET)
51b44da627Seric memmove(&as->as.ni.sa.sa, sa, sizeof (as->as.ni.sa.sain));
52b44da627Seric else if (sa->sa_family == AF_INET6)
53b44da627Seric memmove(&as->as.ni.sa.sa, sa, sizeof (as->as.ni.sa.sain6));
54b44da627Seric
55b44da627Seric as->as.ni.sa.sa.sa_len = slen;
56b44da627Seric as->as.ni.hostname = host;
57b44da627Seric as->as.ni.hostnamelen = hostlen;
58b44da627Seric as->as.ni.servname = serv;
59b44da627Seric as->as.ni.servnamelen = servlen;
60b44da627Seric as->as.ni.flags = flags;
61b44da627Seric
62253ef892Sderaadt _asr_ctx_unref(ac);
63b44da627Seric return (as);
64b44da627Seric
65b44da627Seric abort:
66b44da627Seric if (as)
67253ef892Sderaadt _asr_async_free(as);
68253ef892Sderaadt _asr_ctx_unref(ac);
69b44da627Seric return (NULL);
70b44da627Seric }
715826fd8cSguenther DEF_WEAK(getnameinfo_async);
72b44da627Seric
73b44da627Seric static int
getnameinfo_async_run(struct asr_query * as,struct asr_result * ar)745be03f8fSeric getnameinfo_async_run(struct asr_query *as, struct asr_result *ar)
75b44da627Seric {
76b44da627Seric void *addr;
77b44da627Seric socklen_t addrlen;
78b44da627Seric int r;
79b44da627Seric
80b44da627Seric next:
81b44da627Seric switch (as->as_state) {
82b44da627Seric
83b44da627Seric case ASR_STATE_INIT:
84b44da627Seric
85b44da627Seric /* Make sure the parameters are all valid. */
86b44da627Seric
87b44da627Seric if (as->as.ni.sa.sa.sa_family != AF_INET &&
88b44da627Seric as->as.ni.sa.sa.sa_family != AF_INET6) {
89b44da627Seric ar->ar_gai_errno = EAI_FAMILY;
90b44da627Seric async_set_state(as, ASR_STATE_HALT);
91b44da627Seric break;
92b44da627Seric }
93b44da627Seric
94b44da627Seric if ((as->as.ni.sa.sa.sa_family == AF_INET &&
95b44da627Seric (as->as.ni.sa.sa.sa_len != sizeof (as->as.ni.sa.sain))) ||
96b44da627Seric (as->as.ni.sa.sa.sa_family == AF_INET6 &&
97b44da627Seric (as->as.ni.sa.sa.sa_len != sizeof (as->as.ni.sa.sain6)))) {
98b44da627Seric ar->ar_gai_errno = EAI_FAIL;
99b44da627Seric async_set_state(as, ASR_STATE_HALT);
100b44da627Seric break;
101b44da627Seric }
102b44da627Seric
103b44da627Seric /* Set the service name first, if needed. */
104b44da627Seric if (_servname(as) == -1) {
105b44da627Seric ar->ar_gai_errno = EAI_OVERFLOW;
106b44da627Seric async_set_state(as, ASR_STATE_HALT);
107b44da627Seric break;
108b44da627Seric }
109b44da627Seric
110b44da627Seric if (as->as.ni.hostname == NULL || as->as.ni.hostnamelen == 0) {
111b44da627Seric ar->ar_gai_errno = 0;
112b44da627Seric async_set_state(as, ASR_STATE_HALT);
113b44da627Seric break;
114b44da627Seric }
115b44da627Seric
116b44da627Seric if (as->as.ni.flags & NI_NUMERICHOST) {
117b44da627Seric if (_numerichost(as) == -1) {
1184a508230Seric if (errno == ENOMEM)
119b44da627Seric ar->ar_gai_errno = EAI_MEMORY;
1204a508230Seric else if (errno == ENOSPC)
121b44da627Seric ar->ar_gai_errno = EAI_OVERFLOW;
1224a508230Seric else {
1234a508230Seric ar->ar_errno = errno;
124b44da627Seric ar->ar_gai_errno = EAI_SYSTEM;
1254a508230Seric }
126b44da627Seric } else
127b44da627Seric ar->ar_gai_errno = 0;
128b44da627Seric async_set_state(as, ASR_STATE_HALT);
129b44da627Seric break;
130b44da627Seric }
131b44da627Seric
132b44da627Seric if (as->as.ni.sa.sa.sa_family == AF_INET) {
133b44da627Seric addrlen = sizeof(as->as.ni.sa.sain.sin_addr);
134b44da627Seric addr = &as->as.ni.sa.sain.sin_addr;
135b44da627Seric } else {
136b44da627Seric addrlen = sizeof(as->as.ni.sa.sain6.sin6_addr);
137b44da627Seric addr = &as->as.ni.sa.sain6.sin6_addr;
138b44da627Seric }
139b44da627Seric
140b44da627Seric /*
141b44da627Seric * Create a subquery to lookup the address.
142b44da627Seric */
143f6f51dadSeric as->as_subq = _gethostbyaddr_async_ctx(addr, addrlen,
144b44da627Seric as->as.ni.sa.sa.sa_family,
145b44da627Seric as->as_ctx);
146f6f51dadSeric if (as->as_subq == NULL) {
147b44da627Seric ar->ar_gai_errno = EAI_MEMORY;
148b44da627Seric async_set_state(as, ASR_STATE_HALT);
149b44da627Seric break;
150b44da627Seric }
151b44da627Seric
152b44da627Seric async_set_state(as, ASR_STATE_SUBQUERY);
153b44da627Seric break;
154b44da627Seric
155b44da627Seric case ASR_STATE_SUBQUERY:
156b44da627Seric
157f6f51dadSeric if ((r = asr_run(as->as_subq, ar)) == ASYNC_COND)
158b44da627Seric return (ASYNC_COND);
159b44da627Seric
160b44da627Seric /*
161b44da627Seric * Request done.
162b44da627Seric */
163f6f51dadSeric as->as_subq = NULL;
164b44da627Seric
165b44da627Seric if (ar->ar_hostent == NULL) {
166b44da627Seric if (as->as.ni.flags & NI_NAMEREQD) {
167b44da627Seric ar->ar_gai_errno = EAI_NONAME;
168b44da627Seric } else if (_numerichost(as) == -1) {
1694a508230Seric if (errno == ENOMEM)
170b44da627Seric ar->ar_gai_errno = EAI_MEMORY;
1714a508230Seric else if (errno == ENOSPC)
172b44da627Seric ar->ar_gai_errno = EAI_OVERFLOW;
1734a508230Seric else {
1744a508230Seric ar->ar_errno = errno;
175b44da627Seric ar->ar_gai_errno = EAI_SYSTEM;
1764a508230Seric }
177b44da627Seric } else
178b44da627Seric ar->ar_gai_errno = 0;
179b44da627Seric } else {
180b44da627Seric if (strlcpy(as->as.ni.hostname,
181b44da627Seric ar->ar_hostent->h_name,
182b44da627Seric as->as.ni.hostnamelen) >= as->as.ni.hostnamelen)
183b44da627Seric ar->ar_gai_errno = EAI_OVERFLOW;
184b44da627Seric else
185b44da627Seric ar->ar_gai_errno = 0;
186836b804aSeric free(ar->ar_hostent);
187b44da627Seric }
188b44da627Seric
189b44da627Seric async_set_state(as, ASR_STATE_HALT);
190b44da627Seric break;
191b44da627Seric
192b44da627Seric case ASR_STATE_HALT:
193b44da627Seric return (ASYNC_DONE);
194b44da627Seric
195b44da627Seric default:
196b44da627Seric ar->ar_errno = EOPNOTSUPP;
197b44da627Seric ar->ar_gai_errno = EAI_SYSTEM;
198b44da627Seric async_set_state(as, ASR_STATE_HALT);
199b44da627Seric break;
200b44da627Seric }
201b44da627Seric goto next;
202b44da627Seric }
203b44da627Seric
204b44da627Seric
205b44da627Seric /*
206b44da627Seric * Set the service name on the result buffer is not NULL.
207b44da627Seric * return (-1) if the buffer is too small.
208b44da627Seric */
209b44da627Seric static int
_servname(struct asr_query * as)2105be03f8fSeric _servname(struct asr_query *as)
211b44da627Seric {
212b44da627Seric struct servent s;
213b44da627Seric struct servent_data sd;
214b44da627Seric int port, r;
215b44da627Seric char *buf = as->as.ni.servname;
216*caf7b7a2Seric size_t n, buflen = as->as.ni.servnamelen;
217b44da627Seric
218b44da627Seric if (as->as.ni.servname == NULL || as->as.ni.servnamelen == 0)
219b44da627Seric return (0);
220b44da627Seric
221b44da627Seric if (as->as.ni.sa.sa.sa_family == AF_INET)
222b44da627Seric port = as->as.ni.sa.sain.sin_port;
223b44da627Seric else
224b44da627Seric port = as->as.ni.sa.sain6.sin6_port;
225b44da627Seric
226b44da627Seric if (!(as->as.ni.flags & NI_NUMERICSERV)) {
227b44da627Seric memset(&sd, 0, sizeof (sd));
228*caf7b7a2Seric r = getservbyport_r(port, (as->as.ni.flags & NI_DGRAM) ?
229*caf7b7a2Seric "udp" : "tcp", &s, &sd);
230*caf7b7a2Seric if (r == 0)
231*caf7b7a2Seric n = strlcpy(buf, s.s_name, buflen);
232b44da627Seric endservent_r(&sd);
233*caf7b7a2Seric if (r == 0) {
234*caf7b7a2Seric if (n >= buflen)
235*caf7b7a2Seric return (-1);
236*caf7b7a2Seric return (0);
237b44da627Seric }
238b44da627Seric }
239b44da627Seric
240b44da627Seric r = snprintf(buf, buflen, "%u", ntohs(port));
241515e489cSderaadt if (r < 0 || r >= buflen)
242b44da627Seric return (-1);
243b44da627Seric
244b44da627Seric return (0);
245b44da627Seric }
246b44da627Seric
247b44da627Seric /*
248b44da627Seric * Write the numeric address
249b44da627Seric */
250b44da627Seric static int
_numerichost(struct asr_query * as)2515be03f8fSeric _numerichost(struct asr_query *as)
252b44da627Seric {
25378a9ca1aSeric unsigned int ifidx;
25478a9ca1aSeric char scope[IF_NAMESIZE + 1], *ifname;
255b44da627Seric void *addr;
256b44da627Seric char *buf = as->as.ni.hostname;
257b44da627Seric size_t buflen = as->as.ni.hostnamelen;
258b44da627Seric
259b44da627Seric if (as->as.ni.sa.sa.sa_family == AF_INET)
260b44da627Seric addr = &as->as.ni.sa.sain.sin_addr;
261b44da627Seric else
262b44da627Seric addr = &as->as.ni.sa.sain6.sin6_addr;
263b44da627Seric
264b44da627Seric if (inet_ntop(as->as.ni.sa.sa.sa_family, addr, buf, buflen) == NULL)
26580f48568Seric return (-1); /* errno set */
266b44da627Seric
26778a9ca1aSeric if (as->as.ni.sa.sa.sa_family == AF_INET6 &&
26878a9ca1aSeric as->as.ni.sa.sain6.sin6_scope_id) {
26978a9ca1aSeric
27078a9ca1aSeric scope[0] = SCOPE_DELIMITER;
27178a9ca1aSeric scope[1] = '\0';
27278a9ca1aSeric
27378a9ca1aSeric ifidx = as->as.ni.sa.sain6.sin6_scope_id;
27478a9ca1aSeric ifname = NULL;
27578a9ca1aSeric
27678a9ca1aSeric if (IN6_IS_ADDR_LINKLOCAL(&as->as.ni.sa.sain6.sin6_addr) ||
27778a9ca1aSeric IN6_IS_ADDR_MC_LINKLOCAL(&as->as.ni.sa.sain6.sin6_addr) ||
27878a9ca1aSeric IN6_IS_ADDR_MC_INTFACELOCAL(&as->as.ni.sa.sain6.sin6_addr))
27978a9ca1aSeric ifname = if_indextoname(ifidx, scope + 1);
28078a9ca1aSeric
28178a9ca1aSeric if (ifname == NULL)
28278a9ca1aSeric snprintf(scope + 1, sizeof(scope) - 1, "%u", ifidx);
28378a9ca1aSeric
28478a9ca1aSeric strlcat(buf, scope, buflen);
28578a9ca1aSeric }
28678a9ca1aSeric
287b44da627Seric return (0);
288b44da627Seric }
289