xref: /openbsd/usr.sbin/smtpd/resolver.c (revision d3140113)
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 
33 struct 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 
43 struct session {
44 	uint32_t	 reqid;
45 	struct mproc	*proc;
46 	char		*host;
47 	char		*serv;
48 };
49 
50 SPLAY_HEAD(reqtree, request);
51 
52 static void resolver_init(void);
53 static void resolver_getaddrinfo_cb(struct asr_result *, void *);
54 static void resolver_getnameinfo_cb(struct asr_result *, void *);
55 static void resolver_res_query_cb(struct asr_result *, void *);
56 
57 static int request_cmp(struct request *, struct request *);
58 SPLAY_PROTOTYPE(reqtree, request, entry, request_cmp);
59 
60 static struct reqtree reqs;
61 
62 void
resolver_getaddrinfo(const char * hostname,const char * servname,const struct addrinfo * hints,void (* cb)(void *,int,struct addrinfo *),void * arg)63 resolver_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 
94 void
resolver_getnameinfo(const struct sockaddr * sa,int flags,void (* cb)(void *,int,const char *,const char *),void * arg)95 resolver_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 
121 void
resolver_res_query(const char * dname,int class,int type,void (* cb)(void *,int,int,int,const void *,int),void * arg)122 resolver_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 
149 void
resolver_dispatch_request(struct mproc * proc,struct imsg * imsg)150 resolver_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 
272 static struct addrinfo *
_alloc_addrinfo(const struct addrinfo * ai0,const struct sockaddr * sa,const char * cname)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 
299 void
resolver_dispatch_result(struct mproc * proc,struct imsg * imsg)300 resolver_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 
374 static void
resolver_init(void)375 resolver_init(void)
376 {
377 	static int init = 0;
378 
379 	if (init == 0) {
380 		SPLAY_INIT(&reqs);
381 		init = 1;
382 	}
383 }
384 
385 static void
resolver_getaddrinfo_cb(struct asr_result * ar,void * arg)386 resolver_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 
412 static void
resolver_getnameinfo_cb(struct asr_result * ar,void * arg)413 resolver_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 
429 static void
resolver_res_query_cb(struct asr_result * ar,void * arg)430 resolver_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 
446 static int
request_cmp(struct request * a,struct request * b)447 request_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 
456 SPLAY_GENERATE(reqtree, request, entry, request_cmp);
457