1/*	$OpenBSD: resolver.c,v 1.7 2021/06/14 17:58:16 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/socket.h>
20
21#include <netinet/in.h>
22
23#include <asr.h>
24#include <errno.h>
25#include <stdlib.h>
26#include <string.h>
27
28#include "smtpd.h"
29#include "log.h"
30
31#define p_resolver p_lka
32
33struct request {
34	SPLAY_ENTRY(request)	 entry;
35	uint32_t		 id;
36	void			(*cb_ai)(void *, int, struct addrinfo *);
37	void			(*cb_ni)(void *, int, const char *, const char *);
38	void			(*cb_res)(void *, int, int, int, const void *, int);
39	void			*arg;
40	struct addrinfo		*ai;
41};
42
43struct session {
44	uint32_t	 reqid;
45	struct mproc	*proc;
46	char		*host;
47	char		*serv;
48};
49
50SPLAY_HEAD(reqtree, request);
51
52static void resolver_init(void);
53static void resolver_getaddrinfo_cb(struct asr_result *, void *);
54static void resolver_getnameinfo_cb(struct asr_result *, void *);
55static void resolver_res_query_cb(struct asr_result *, void *);
56
57static int request_cmp(struct request *, struct request *);
58SPLAY_PROTOTYPE(reqtree, request, entry, request_cmp);
59
60static struct reqtree reqs;
61
62void
63resolver_getaddrinfo(const char *hostname, const char *servname,
64    const struct addrinfo *hints, void (*cb)(void *, int, struct addrinfo *),
65    void *arg)
66{
67	struct request *req;
68
69	resolver_init();
70
71	req = calloc(1, sizeof(*req));
72	if (req == NULL) {
73		cb(arg, EAI_MEMORY, NULL);
74		return;
75	}
76
77	while (req->id == 0 || SPLAY_FIND(reqtree, &reqs, req))
78		req->id = arc4random();
79	req->cb_ai = cb;
80	req->arg = arg;
81
82	SPLAY_INSERT(reqtree, &reqs, req);
83
84	m_create(p_resolver, IMSG_GETADDRINFO, req->id, 0, -1);
85	m_add_int(p_resolver, hints ? hints->ai_flags : 0);
86	m_add_int(p_resolver, hints ? hints->ai_family : 0);
87	m_add_int(p_resolver, hints ? hints->ai_socktype : 0);
88	m_add_int(p_resolver, hints ? hints->ai_protocol : 0);
89	m_add_string(p_resolver, hostname);
90	m_add_string(p_resolver, servname);
91	m_close(p_resolver);
92}
93
94void
95resolver_getnameinfo(const struct sockaddr *sa, int flags,
96    void(*cb)(void *, int, const char *, const char *), void *arg)
97{
98	struct request *req;
99
100	resolver_init();
101
102	req = calloc(1, sizeof(*req));
103	if (req == NULL) {
104		cb(arg, EAI_MEMORY, NULL, NULL);
105		return;
106	}
107
108	while (req->id == 0 || SPLAY_FIND(reqtree, &reqs, req))
109		req->id = arc4random();
110	req->cb_ni = cb;
111	req->arg = arg;
112
113	SPLAY_INSERT(reqtree, &reqs, req);
114
115	m_create(p_resolver, IMSG_GETNAMEINFO, req->id, 0, -1);
116	m_add_sockaddr(p_resolver, sa);
117	m_add_int(p_resolver, flags);
118	m_close(p_resolver);
119}
120
121void
122resolver_res_query(const char *dname, int class, int type,
123    void (*cb)(void *, int, int, int, const void *, int), void *arg)
124{
125	struct request *req;
126
127	resolver_init();
128
129	req = calloc(1, sizeof(*req));
130	if (req == NULL) {
131		cb(arg, NETDB_INTERNAL, 0, 0, NULL, 0);
132		return;
133	}
134
135	while (req->id == 0 || SPLAY_FIND(reqtree, &reqs, req))
136		req->id = arc4random();
137	req->cb_res = cb;
138	req->arg = arg;
139
140	SPLAY_INSERT(reqtree, &reqs, req);
141
142	m_create(p_resolver, IMSG_RES_QUERY, req->id, 0, -1);
143	m_add_string(p_resolver, dname);
144	m_add_int(p_resolver, class);
145	m_add_int(p_resolver, type);
146	m_close(p_resolver);
147}
148
149void
150resolver_dispatch_request(struct mproc *proc, struct imsg *imsg)
151{
152	const char *hostname, *servname, *dname;
153	struct session *s;
154	struct asr_query *q;
155	struct addrinfo hints;
156	struct sockaddr_storage ss;
157	struct sockaddr *sa;
158	struct msg m;
159	uint32_t reqid;
160	int class, type, flags, save_errno;
161
162	reqid = imsg->hdr.peerid;
163	m_msg(&m, imsg);
164
165	switch (imsg->hdr.type) {
166
167	case IMSG_GETADDRINFO:
168		servname = NULL;
169		memset(&hints, 0 , sizeof(hints));
170		m_get_int(&m, &hints.ai_flags);
171		m_get_int(&m, &hints.ai_family);
172		m_get_int(&m, &hints.ai_socktype);
173		m_get_int(&m, &hints.ai_protocol);
174		m_get_string(&m, &hostname);
175		m_get_string(&m, &servname);
176		m_end(&m);
177
178		s = NULL;
179		q = NULL;
180		if ((s = calloc(1, sizeof(*s))) &&
181		    (q = getaddrinfo_async(hostname, servname, &hints, NULL)) &&
182		    (event_asr_run(q, resolver_getaddrinfo_cb, s))) {
183			s->reqid = reqid;
184			s->proc = proc;
185			break;
186		}
187		save_errno = errno;
188
189		if (q)
190			asr_abort(q);
191		if (s)
192			free(s);
193
194		m_create(proc, IMSG_GETADDRINFO_END, reqid, 0, -1);
195		m_add_int(proc, EAI_SYSTEM);
196		m_add_int(proc, save_errno);
197		m_close(proc);
198		break;
199
200	case IMSG_GETNAMEINFO:
201		sa = (struct sockaddr*)&ss;
202		m_get_sockaddr(&m, sa);
203		m_get_int(&m, &flags);
204		m_end(&m);
205
206		s = NULL;
207		q = NULL;
208		if ((s = calloc(1, sizeof(*s))) &&
209		    (s->host = malloc(NI_MAXHOST)) &&
210		    (s->serv = malloc(NI_MAXSERV)) &&
211		    (q = getnameinfo_async(sa, sa->sa_len, s->host, NI_MAXHOST,
212			s->serv, NI_MAXSERV, flags, NULL)) &&
213		    (event_asr_run(q, resolver_getnameinfo_cb, s))) {
214			s->reqid = reqid;
215			s->proc = proc;
216			break;
217		}
218		save_errno = errno;
219
220		if (q)
221			asr_abort(q);
222		if (s) {
223			free(s->host);
224			free(s->serv);
225			free(s);
226		}
227
228		m_create(proc, IMSG_GETNAMEINFO, reqid, 0, -1);
229		m_add_int(proc, EAI_SYSTEM);
230		m_add_int(proc, save_errno);
231		m_add_string(proc, NULL);
232		m_add_string(proc, NULL);
233		m_close(proc);
234		break;
235
236	case IMSG_RES_QUERY:
237		m_get_string(&m, &dname);
238		m_get_int(&m, &class);
239		m_get_int(&m, &type);
240		m_end(&m);
241
242		s = NULL;
243		q = NULL;
244		if ((s = calloc(1, sizeof(*s))) &&
245		    (q = res_query_async(dname, class, type, NULL)) &&
246		    (event_asr_run(q, resolver_res_query_cb, s))) {
247			s->reqid = reqid;
248			s->proc = proc;
249			break;
250		}
251		save_errno = errno;
252
253		if (q)
254			asr_abort(q);
255		if (s)
256			free(s);
257
258		m_create(proc, IMSG_RES_QUERY, reqid, 0, -1);
259		m_add_int(proc, NETDB_INTERNAL);
260		m_add_int(proc, save_errno);
261		m_add_int(proc, 0);
262		m_add_int(proc, 0);
263		m_add_data(proc, NULL, 0);
264		m_close(proc);
265		break;
266
267	default:
268		fatalx("%s: %s", __func__, imsg_to_str(imsg->hdr.type));
269	}
270}
271
272static struct addrinfo *
273_alloc_addrinfo(const struct addrinfo *ai0, const struct sockaddr *sa,
274    const char *cname)
275{
276	struct addrinfo *ai;
277
278	ai = calloc(1, sizeof(*ai) + sa->sa_len);
279	if (ai == NULL) {
280		log_warn("%s: calloc", __func__);
281		return NULL;
282	}
283	*ai = *ai0;
284	ai->ai_addr = (void *)(ai + 1);
285	memcpy(ai->ai_addr, sa, sa->sa_len);
286
287	if (cname) {
288		ai->ai_canonname = strdup(cname);
289		if (ai->ai_canonname == NULL) {
290			log_warn("%s: strdup", __func__);
291			free(ai);
292			return NULL;
293		}
294	}
295
296	return ai;
297}
298
299void
300resolver_dispatch_result(struct mproc *proc, struct imsg *imsg)
301{
302	struct request key, *req;
303	struct sockaddr_storage ss;
304	struct addrinfo *ai, tai;
305	struct msg m;
306	const char *cname, *host, *serv;
307	const void *data;
308	size_t datalen;
309	int gai_errno, herrno, rcode, count;
310
311	key.id = imsg->hdr.peerid;
312	req = SPLAY_FIND(reqtree, &reqs, &key);
313	if (req == NULL)
314		fatalx("%s: unknown request %08x", __func__, imsg->hdr.peerid);
315
316	m_msg(&m, imsg);
317
318	switch (imsg->hdr.type) {
319
320	case IMSG_GETADDRINFO:
321		memset(&tai, 0, sizeof(tai));
322		m_get_int(&m, &tai.ai_flags);
323		m_get_int(&m, &tai.ai_family);
324		m_get_int(&m, &tai.ai_socktype);
325		m_get_int(&m, &tai.ai_protocol);
326		m_get_sockaddr(&m, (struct sockaddr *)&ss);
327		m_get_string(&m, &cname);
328		m_end(&m);
329
330		ai = _alloc_addrinfo(&tai, (struct sockaddr *)&ss, cname);
331		if (ai) {
332			ai->ai_next = req->ai;
333			req->ai = ai;
334		}
335		break;
336
337	case IMSG_GETADDRINFO_END:
338		m_get_int(&m, &gai_errno);
339		m_get_int(&m, &errno);
340		m_end(&m);
341
342		SPLAY_REMOVE(reqtree, &reqs, req);
343		req->cb_ai(req->arg, gai_errno, req->ai);
344		free(req);
345		break;
346
347	case IMSG_GETNAMEINFO:
348		m_get_int(&m, &gai_errno);
349		m_get_int(&m, &errno);
350		m_get_string(&m, &host);
351		m_get_string(&m, &serv);
352		m_end(&m);
353
354		SPLAY_REMOVE(reqtree, &reqs, req);
355		req->cb_ni(req->arg, gai_errno, host, serv);
356		free(req);
357		break;
358
359	case IMSG_RES_QUERY:
360		m_get_int(&m, &herrno);
361		m_get_int(&m, &errno);
362		m_get_int(&m, &rcode);
363		m_get_int(&m, &count);
364		m_get_data(&m, &data, &datalen);
365		m_end(&m);
366
367		SPLAY_REMOVE(reqtree, &reqs, req);
368		req->cb_res(req->arg, herrno, rcode, count, data, datalen);
369		free(req);
370		break;
371	}
372}
373
374static void
375resolver_init(void)
376{
377	static int init = 0;
378
379	if (init == 0) {
380		SPLAY_INIT(&reqs);
381		init = 1;
382	}
383}
384
385static void
386resolver_getaddrinfo_cb(struct asr_result *ar, void *arg)
387{
388	struct session *s = arg;
389	struct addrinfo *ai;
390
391	for (ai = ar->ar_addrinfo; ai; ai = ai->ai_next) {
392		m_create(s->proc, IMSG_GETADDRINFO, s->reqid, 0, -1);
393		m_add_int(s->proc, ai->ai_flags);
394		m_add_int(s->proc, ai->ai_family);
395		m_add_int(s->proc, ai->ai_socktype);
396		m_add_int(s->proc, ai->ai_protocol);
397		m_add_sockaddr(s->proc, ai->ai_addr);
398		m_add_string(s->proc, ai->ai_canonname);
399		m_close(s->proc);
400	}
401
402	m_create(s->proc, IMSG_GETADDRINFO_END, s->reqid, 0, -1);
403	m_add_int(s->proc, ar->ar_gai_errno);
404	m_add_int(s->proc, ar->ar_errno);
405	m_close(s->proc);
406
407	if (ar->ar_addrinfo)
408		freeaddrinfo(ar->ar_addrinfo);
409	free(s);
410}
411
412static void
413resolver_getnameinfo_cb(struct asr_result *ar, void *arg)
414{
415	struct session *s = arg;
416
417	m_create(s->proc, IMSG_GETNAMEINFO, s->reqid, 0, -1);
418	m_add_int(s->proc, ar->ar_gai_errno);
419	m_add_int(s->proc, ar->ar_errno);
420	m_add_string(s->proc, ar->ar_gai_errno ? NULL : s->host);
421	m_add_string(s->proc, ar->ar_gai_errno ? NULL : s->serv);
422	m_close(s->proc);
423
424	free(s->host);
425	free(s->serv);
426	free(s);
427}
428
429static void
430resolver_res_query_cb(struct asr_result *ar, void *arg)
431{
432	struct session *s = arg;
433
434	m_create(s->proc, IMSG_RES_QUERY, s->reqid, 0, -1);
435	m_add_int(s->proc, ar->ar_h_errno);
436	m_add_int(s->proc, ar->ar_errno);
437	m_add_int(s->proc, ar->ar_rcode);
438	m_add_int(s->proc, ar->ar_count);
439	m_add_data(s->proc, ar->ar_data, ar->ar_datalen);
440	m_close(s->proc);
441
442	free(ar->ar_data);
443	free(s);
444}
445
446static int
447request_cmp(struct request *a, struct request *b)
448{
449	if (a->id < b->id)
450		return -1;
451	if (a->id > b->id)
452		return 1;
453	return 0;
454}
455
456SPLAY_GENERATE(reqtree, request, entry, request_cmp);
457