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