1/*	$OpenBSD: resolver.c,v 1.4 2019/04/06 10:35:48 eric Exp $	*/
2
3/*
4 * Copyright (c) 2017-2018 Eric Faurot <eric@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <sys/types.h>
20#include <sys/socket.h>
21#include <sys/tree.h>
22#include <sys/queue.h>
23#include <netinet/in.h>
24
25#include <asr.h>
26#include <ctype.h>
27#include <errno.h>
28#include <imsg.h>
29#include <limits.h>
30#include <stdarg.h>
31#include <stdio.h>
32#include <stdlib.h>
33#include <string.h>
34#include <unistd.h>
35
36#include "lpd.h"
37
38#include "log.h"
39#include "proc.h"
40
41#define p_resolver p_engine
42
43struct request {
44	SPLAY_ENTRY(request)	 entry;
45	uint32_t		 id;
46	void			(*cb_ai)(void *, int, struct addrinfo *);
47	void			(*cb_ni)(void *, int, const char *, const char *);
48	void			*arg;
49	struct addrinfo		*ai;
50};
51
52struct session {
53	uint32_t	 reqid;
54	struct imsgproc	*proc;
55	char		*host;
56	char		*serv;
57};
58
59SPLAY_HEAD(reqtree, request);
60
61static void resolver_init(void);
62static void resolver_getaddrinfo_cb(struct asr_result *, void *);
63static void resolver_getnameinfo_cb(struct asr_result *, void *);
64
65static int request_cmp(struct request *, struct request *);
66SPLAY_PROTOTYPE(reqtree, request, entry, request_cmp);
67
68static struct reqtree reqs;
69
70void
71resolver_getaddrinfo(const char *hostname, const char *servname,
72    const struct addrinfo *hints, void (*cb)(void *, int, struct addrinfo *),
73    void *arg)
74{
75	struct request *req;
76
77	resolver_init();
78
79	req = calloc(1, sizeof(*req));
80	if (req == NULL) {
81		cb(arg, EAI_MEMORY, NULL);
82		return;
83	}
84
85	while (req->id == 0 || SPLAY_FIND(reqtree, &reqs, req))
86		req->id = arc4random();
87	req->cb_ai = cb;
88	req->arg = arg;
89
90	SPLAY_INSERT(reqtree, &reqs, req);
91
92	m_create(p_resolver, IMSG_GETADDRINFO, req->id, 0, -1);
93	m_add_int(p_resolver, hints ? hints->ai_flags : 0);
94	m_add_int(p_resolver, hints ? hints->ai_family : 0);
95	m_add_int(p_resolver, hints ? hints->ai_socktype : 0);
96	m_add_int(p_resolver, hints ? hints->ai_protocol : 0);
97	m_add_string(p_resolver, hostname);
98	m_add_string(p_resolver, servname);
99	m_close(p_resolver);
100}
101
102void
103resolver_getnameinfo(const struct sockaddr *sa, int flags,
104    void(*cb)(void *, int, const char *, const char *), void *arg)
105{
106	struct request *req;
107
108	resolver_init();
109
110	req = calloc(1, sizeof(*req));
111	if (req == NULL) {
112		cb(arg, EAI_MEMORY, NULL, NULL);
113		return;
114	}
115
116	while (req->id == 0 || SPLAY_FIND(reqtree, &reqs, req))
117		req->id = arc4random();
118	req->cb_ni = cb;
119	req->arg = arg;
120
121	SPLAY_INSERT(reqtree, &reqs, req);
122
123	m_create(p_resolver, IMSG_GETNAMEINFO, req->id, 0, -1);
124	m_add_sockaddr(p_resolver, sa);
125	m_add_int(p_resolver, flags);
126	m_close(p_resolver);
127}
128
129void
130resolver_dispatch_request(struct imsgproc *proc, struct imsg *imsg)
131{
132	const char *hostname, *servname;
133	struct session *s;
134	struct asr_query *q;
135	struct addrinfo hints;
136	struct sockaddr_storage ss;
137	struct sockaddr *sa;
138	uint32_t reqid;
139	int flags, save_errno;
140
141	reqid = imsg->hdr.peerid;
142
143	switch (imsg->hdr.type) {
144
145	case IMSG_GETADDRINFO:
146		servname = NULL;
147		memset(&hints, 0 , sizeof(hints));
148		m_get_int(proc, &hints.ai_flags);
149		m_get_int(proc, &hints.ai_family);
150		m_get_int(proc, &hints.ai_socktype);
151		m_get_int(proc, &hints.ai_protocol);
152		m_get_string(proc, &hostname);
153		m_get_string(proc, &servname);
154		m_end(proc);
155
156		s = NULL;
157		q = NULL;
158		if ((s = calloc(1, sizeof(*s))) &&
159		    (q = getaddrinfo_async(hostname, servname, &hints, NULL)) &&
160		    (event_asr_run(q, resolver_getaddrinfo_cb, s))) {
161			s->reqid = reqid;
162			s->proc = proc;
163			break;
164		}
165		save_errno = errno;
166
167		if (q)
168			asr_abort(q);
169		if (s)
170			free(s);
171
172		m_create(proc, IMSG_GETADDRINFO_END, reqid, 0, -1);
173		m_add_int(proc, EAI_SYSTEM);
174		m_add_int(proc, save_errno);
175		m_close(proc);
176		break;
177
178	case IMSG_GETNAMEINFO:
179		sa = (struct sockaddr*)&ss;
180		m_get_sockaddr(proc, sa);
181		m_get_int(proc, &flags);
182		m_end(proc);
183
184		s = NULL;
185		q = NULL;
186		if ((s = calloc(1, sizeof(*s))) &&
187		    (s->host = malloc(NI_MAXHOST)) &&
188		    (s->serv = malloc(NI_MAXSERV)) &&
189		    (q = getnameinfo_async(sa, sa->sa_len, s->host, NI_MAXHOST,
190			s->serv, NI_MAXSERV, flags, NULL)) &&
191		    (event_asr_run(q, resolver_getnameinfo_cb, s))) {
192			s->reqid = reqid;
193			s->proc = proc;
194			break;
195		}
196		save_errno = errno;
197
198		if (q)
199			asr_abort(q);
200		if (s) {
201			free(s->host);
202			free(s->serv);
203			free(s);
204		}
205
206		m_create(proc, IMSG_GETNAMEINFO, reqid, 0, -1);
207		m_add_int(proc, EAI_SYSTEM);
208		m_add_int(proc, save_errno);
209		m_add_string(proc, NULL);
210		m_add_string(proc, NULL);
211		m_close(proc);
212		break;
213
214	default:
215		fatalx("%s: %s", __func__, log_fmt_imsgtype(imsg->hdr.type));
216	}
217}
218
219void
220resolver_dispatch_result(struct imsgproc *proc, struct imsg *imsg)
221{
222	struct request key, *req;
223	struct sockaddr_storage ss;
224	struct addrinfo *ai;
225	const char *cname, *host, *serv;
226	int gai_errno;
227
228	key.id = imsg->hdr.peerid;
229	req = SPLAY_FIND(reqtree, &reqs, &key);
230	if (req == NULL)
231		fatalx("%s: unknown request %08x", __func__, imsg->hdr.peerid);
232
233	switch (imsg->hdr.type) {
234
235	case IMSG_GETADDRINFO:
236		ai = calloc(1, sizeof(*ai));
237		if (ai == NULL) {
238			log_warn("%s: calloc", __func__);
239			break;
240		}
241		m_get_int(proc, &ai->ai_flags);
242		m_get_int(proc, &ai->ai_family);
243		m_get_int(proc, &ai->ai_socktype);
244		m_get_int(proc, &ai->ai_protocol);
245		m_get_sockaddr(proc, (struct sockaddr *)&ss);
246		m_get_string(proc, &cname);
247		m_end(proc);
248
249		ai->ai_addr = malloc(ss.ss_len);
250		if (ai->ai_addr == NULL) {
251			log_warn("%s: malloc", __func__);
252			free(ai);
253			break;
254		}
255
256		memmove(ai->ai_addr, &ss, ss.ss_len);
257
258		if (cname) {
259			ai->ai_canonname = strdup(cname);
260			if (ai->ai_canonname == NULL) {
261				log_warn("%s: strdup", __func__);
262				free(ai->ai_addr);
263				free(ai);
264				break;
265			}
266		}
267
268		ai->ai_next = req->ai;
269		req->ai = ai;
270		break;
271
272	case IMSG_GETADDRINFO_END:
273		m_get_int(proc, &gai_errno);
274		m_get_int(proc, &errno);
275		m_end(proc);
276
277		SPLAY_REMOVE(reqtree, &reqs, req);
278		req->cb_ai(req->arg, gai_errno, req->ai);
279		free(req);
280		break;
281
282	case IMSG_GETNAMEINFO:
283		m_get_int(proc, &gai_errno);
284		m_get_int(proc, &errno);
285		m_get_string(proc, &host);
286		m_get_string(proc, &serv);
287		m_end(proc);
288
289		SPLAY_REMOVE(reqtree, &reqs, req);
290		req->cb_ni(req->arg, gai_errno, host, serv);
291		free(req);
292		break;
293	}
294}
295
296static void
297resolver_init(void)
298{
299	static int init = 0;
300
301	if (init == 0) {
302		SPLAY_INIT(&reqs);
303		init = 1;
304	}
305}
306
307static void
308resolver_getaddrinfo_cb(struct asr_result *ar, void *arg)
309{
310	struct session *s = arg;
311	struct addrinfo *ai;
312
313	for (ai = ar->ar_addrinfo; ai; ai = ai->ai_next) {
314		m_create(s->proc, IMSG_GETADDRINFO, s->reqid, 0, -1);
315		m_add_int(s->proc, ai->ai_flags);
316		m_add_int(s->proc, ai->ai_family);
317		m_add_int(s->proc, ai->ai_socktype);
318		m_add_int(s->proc, ai->ai_protocol);
319		m_add_sockaddr(s->proc, ai->ai_addr);
320		m_add_string(s->proc, ai->ai_canonname);
321		m_close(s->proc);
322	}
323
324	m_create(s->proc, IMSG_GETADDRINFO_END, s->reqid, 0, -1);
325	m_add_int(s->proc, ar->ar_gai_errno);
326	m_add_int(s->proc, ar->ar_errno);
327	m_close(s->proc);
328
329	if (ar->ar_addrinfo)
330		freeaddrinfo(ar->ar_addrinfo);
331	free(s);
332}
333
334static void
335resolver_getnameinfo_cb(struct asr_result *ar, void *arg)
336{
337	struct session *s = arg;
338
339	m_create(s->proc, IMSG_GETNAMEINFO, s->reqid, 0, -1);
340	m_add_int(s->proc, ar->ar_gai_errno);
341	m_add_int(s->proc, ar->ar_errno);
342	m_add_string(s->proc, ar->ar_gai_errno ? NULL : s->host);
343	m_add_string(s->proc, ar->ar_gai_errno ? NULL : s->serv);
344	m_close(s->proc);
345
346	free(s->host);
347	free(s->serv);
348	free(s);
349}
350
351static int
352request_cmp(struct request *a, struct request *b)
353{
354	if (a->id < b->id)
355		return -1;
356	if (a->id > b->id)
357		return 1;
358	return 0;
359}
360
361SPLAY_GENERATE(reqtree, request, entry, request_cmp);
362