1*d3140113Seric /* $OpenBSD: resolver.c,v 1.7 2021/06/14 17:58:16 eric Exp $ */
201eba458Seric
301eba458Seric /*
401eba458Seric * Copyright (c) 2017-2018 Eric Faurot <eric@openbsd.org>
501eba458Seric *
601eba458Seric * Permission to use, copy, modify, and distribute this software for any
701eba458Seric * purpose with or without fee is hereby granted, provided that the above
801eba458Seric * copyright notice and this permission notice appear in all copies.
901eba458Seric *
1001eba458Seric * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1101eba458Seric * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1201eba458Seric * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1301eba458Seric * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1401eba458Seric * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1501eba458Seric * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1601eba458Seric * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1701eba458Seric */
1801eba458Seric
1901eba458Seric #include <sys/socket.h>
20*d3140113Seric
2101eba458Seric #include <netinet/in.h>
2201eba458Seric
2301eba458Seric #include <asr.h>
2401eba458Seric #include <errno.h>
2501eba458Seric #include <stdlib.h>
2601eba458Seric #include <string.h>
2701eba458Seric
2801eba458Seric #include "smtpd.h"
2901eba458Seric #include "log.h"
3001eba458Seric
3101eba458Seric #define p_resolver p_lka
3201eba458Seric
3301eba458Seric struct request {
3401eba458Seric SPLAY_ENTRY(request) entry;
3501eba458Seric uint32_t id;
3601eba458Seric void (*cb_ai)(void *, int, struct addrinfo *);
3701eba458Seric void (*cb_ni)(void *, int, const char *, const char *);
3801361951Seric void (*cb_res)(void *, int, int, int, const void *, int);
3901eba458Seric void *arg;
4001eba458Seric struct addrinfo *ai;
4101eba458Seric };
4201eba458Seric
4301eba458Seric struct session {
4401eba458Seric uint32_t reqid;
4501eba458Seric struct mproc *proc;
4601eba458Seric char *host;
4701eba458Seric char *serv;
4801eba458Seric };
4901eba458Seric
5001eba458Seric SPLAY_HEAD(reqtree, request);
5101eba458Seric
5201eba458Seric static void resolver_init(void);
5301eba458Seric static void resolver_getaddrinfo_cb(struct asr_result *, void *);
5401eba458Seric static void resolver_getnameinfo_cb(struct asr_result *, void *);
5501361951Seric static void resolver_res_query_cb(struct asr_result *, void *);
5601eba458Seric
5701eba458Seric static int request_cmp(struct request *, struct request *);
5801eba458Seric SPLAY_PROTOTYPE(reqtree, request, entry, request_cmp);
5901eba458Seric
6001eba458Seric static struct reqtree reqs;
6101eba458Seric
6201eba458Seric void
resolver_getaddrinfo(const char * hostname,const char * servname,const struct addrinfo * hints,void (* cb)(void *,int,struct addrinfo *),void * arg)6301eba458Seric resolver_getaddrinfo(const char *hostname, const char *servname,
6401eba458Seric const struct addrinfo *hints, void (*cb)(void *, int, struct addrinfo *),
6501eba458Seric void *arg)
6601eba458Seric {
6701eba458Seric struct request *req;
6801eba458Seric
6901eba458Seric resolver_init();
7001eba458Seric
7101eba458Seric req = calloc(1, sizeof(*req));
7201eba458Seric if (req == NULL) {
7301eba458Seric cb(arg, EAI_MEMORY, NULL);
7401eba458Seric return;
7501eba458Seric }
7601eba458Seric
7701eba458Seric while (req->id == 0 || SPLAY_FIND(reqtree, &reqs, req))
7801eba458Seric req->id = arc4random();
7901eba458Seric req->cb_ai = cb;
8001eba458Seric req->arg = arg;
8101eba458Seric
8201eba458Seric SPLAY_INSERT(reqtree, &reqs, req);
8301eba458Seric
8401eba458Seric m_create(p_resolver, IMSG_GETADDRINFO, req->id, 0, -1);
8501eba458Seric m_add_int(p_resolver, hints ? hints->ai_flags : 0);
8601eba458Seric m_add_int(p_resolver, hints ? hints->ai_family : 0);
8701eba458Seric m_add_int(p_resolver, hints ? hints->ai_socktype : 0);
8801eba458Seric m_add_int(p_resolver, hints ? hints->ai_protocol : 0);
8901eba458Seric m_add_string(p_resolver, hostname);
902c59994aSeric m_add_string(p_resolver, servname);
9101eba458Seric m_close(p_resolver);
9201eba458Seric }
9301eba458Seric
9401eba458Seric void
resolver_getnameinfo(const struct sockaddr * sa,int flags,void (* cb)(void *,int,const char *,const char *),void * arg)9501eba458Seric resolver_getnameinfo(const struct sockaddr *sa, int flags,
9601eba458Seric void(*cb)(void *, int, const char *, const char *), void *arg)
9701eba458Seric {
9801eba458Seric struct request *req;
9901eba458Seric
10001eba458Seric resolver_init();
10101eba458Seric
10201eba458Seric req = calloc(1, sizeof(*req));
10301eba458Seric if (req == NULL) {
10401eba458Seric cb(arg, EAI_MEMORY, NULL, NULL);
10501eba458Seric return;
10601eba458Seric }
10701eba458Seric
10801eba458Seric while (req->id == 0 || SPLAY_FIND(reqtree, &reqs, req))
10901eba458Seric req->id = arc4random();
11001eba458Seric req->cb_ni = cb;
11101eba458Seric req->arg = arg;
11201eba458Seric
11301eba458Seric SPLAY_INSERT(reqtree, &reqs, req);
11401eba458Seric
11501eba458Seric m_create(p_resolver, IMSG_GETNAMEINFO, req->id, 0, -1);
11601eba458Seric m_add_sockaddr(p_resolver, sa);
11701eba458Seric m_add_int(p_resolver, flags);
11801eba458Seric m_close(p_resolver);
11901eba458Seric }
12001eba458Seric
12101eba458Seric void
resolver_res_query(const char * dname,int class,int type,void (* cb)(void *,int,int,int,const void *,int),void * arg)12201361951Seric resolver_res_query(const char *dname, int class, int type,
12301361951Seric void (*cb)(void *, int, int, int, const void *, int), void *arg)
12401361951Seric {
12501361951Seric struct request *req;
12601361951Seric
12701361951Seric resolver_init();
12801361951Seric
12901361951Seric req = calloc(1, sizeof(*req));
13001361951Seric if (req == NULL) {
13101361951Seric cb(arg, NETDB_INTERNAL, 0, 0, NULL, 0);
13201361951Seric return;
13301361951Seric }
13401361951Seric
13501361951Seric while (req->id == 0 || SPLAY_FIND(reqtree, &reqs, req))
13601361951Seric req->id = arc4random();
13701361951Seric req->cb_res = cb;
13801361951Seric req->arg = arg;
13901361951Seric
14001361951Seric SPLAY_INSERT(reqtree, &reqs, req);
14101361951Seric
14201361951Seric m_create(p_resolver, IMSG_RES_QUERY, req->id, 0, -1);
14301361951Seric m_add_string(p_resolver, dname);
14401361951Seric m_add_int(p_resolver, class);
14501361951Seric m_add_int(p_resolver, type);
14601361951Seric m_close(p_resolver);
14701361951Seric }
14801361951Seric
14901361951Seric void
resolver_dispatch_request(struct mproc * proc,struct imsg * imsg)15001eba458Seric resolver_dispatch_request(struct mproc *proc, struct imsg *imsg)
15101eba458Seric {
15201361951Seric const char *hostname, *servname, *dname;
15301eba458Seric struct session *s;
15401eba458Seric struct asr_query *q;
15501eba458Seric struct addrinfo hints;
15601eba458Seric struct sockaddr_storage ss;
15701eba458Seric struct sockaddr *sa;
15801eba458Seric struct msg m;
15901eba458Seric uint32_t reqid;
16001361951Seric int class, type, flags, save_errno;
16101eba458Seric
16201eba458Seric reqid = imsg->hdr.peerid;
16301eba458Seric m_msg(&m, imsg);
16401eba458Seric
16501eba458Seric switch (imsg->hdr.type) {
16601eba458Seric
16701eba458Seric case IMSG_GETADDRINFO:
16801eba458Seric servname = NULL;
16901eba458Seric memset(&hints, 0 , sizeof(hints));
17001eba458Seric m_get_int(&m, &hints.ai_flags);
17101eba458Seric m_get_int(&m, &hints.ai_family);
17201eba458Seric m_get_int(&m, &hints.ai_socktype);
17301eba458Seric m_get_int(&m, &hints.ai_protocol);
17401eba458Seric m_get_string(&m, &hostname);
17501eba458Seric m_get_string(&m, &servname);
17601eba458Seric m_end(&m);
17701eba458Seric
17801eba458Seric s = NULL;
17901eba458Seric q = NULL;
18001eba458Seric if ((s = calloc(1, sizeof(*s))) &&
18101eba458Seric (q = getaddrinfo_async(hostname, servname, &hints, NULL)) &&
18201eba458Seric (event_asr_run(q, resolver_getaddrinfo_cb, s))) {
18301eba458Seric s->reqid = reqid;
18401eba458Seric s->proc = proc;
18501eba458Seric break;
18601eba458Seric }
18701eba458Seric save_errno = errno;
18801eba458Seric
18901eba458Seric if (q)
19001eba458Seric asr_abort(q);
19101eba458Seric if (s)
19201eba458Seric free(s);
19301eba458Seric
19401eba458Seric m_create(proc, IMSG_GETADDRINFO_END, reqid, 0, -1);
19501eba458Seric m_add_int(proc, EAI_SYSTEM);
19601eba458Seric m_add_int(proc, save_errno);
19701eba458Seric m_close(proc);
19801eba458Seric break;
19901eba458Seric
20001eba458Seric case IMSG_GETNAMEINFO:
20101eba458Seric sa = (struct sockaddr*)&ss;
20201eba458Seric m_get_sockaddr(&m, sa);
20301eba458Seric m_get_int(&m, &flags);
20401eba458Seric m_end(&m);
20501eba458Seric
20601eba458Seric s = NULL;
20701eba458Seric q = NULL;
20801eba458Seric if ((s = calloc(1, sizeof(*s))) &&
20901eba458Seric (s->host = malloc(NI_MAXHOST)) &&
21001eba458Seric (s->serv = malloc(NI_MAXSERV)) &&
21101eba458Seric (q = getnameinfo_async(sa, sa->sa_len, s->host, NI_MAXHOST,
21201eba458Seric s->serv, NI_MAXSERV, flags, NULL)) &&
21301eba458Seric (event_asr_run(q, resolver_getnameinfo_cb, s))) {
21401eba458Seric s->reqid = reqid;
21501eba458Seric s->proc = proc;
21601eba458Seric break;
21701eba458Seric }
21801eba458Seric save_errno = errno;
21901eba458Seric
22001eba458Seric if (q)
22101eba458Seric asr_abort(q);
22201eba458Seric if (s) {
22301eba458Seric free(s->host);
22401eba458Seric free(s->serv);
22501eba458Seric free(s);
22601eba458Seric }
22701eba458Seric
22801eba458Seric m_create(proc, IMSG_GETNAMEINFO, reqid, 0, -1);
22901eba458Seric m_add_int(proc, EAI_SYSTEM);
23001eba458Seric m_add_int(proc, save_errno);
2319ec9b5d0Seric m_add_string(proc, NULL);
2329ec9b5d0Seric m_add_string(proc, NULL);
23301eba458Seric m_close(proc);
23401eba458Seric break;
23501eba458Seric
23601361951Seric case IMSG_RES_QUERY:
23701361951Seric m_get_string(&m, &dname);
23801361951Seric m_get_int(&m, &class);
23901361951Seric m_get_int(&m, &type);
24001361951Seric m_end(&m);
24101361951Seric
24201361951Seric s = NULL;
24301361951Seric q = NULL;
24401361951Seric if ((s = calloc(1, sizeof(*s))) &&
24501361951Seric (q = res_query_async(dname, class, type, NULL)) &&
24601361951Seric (event_asr_run(q, resolver_res_query_cb, s))) {
24701361951Seric s->reqid = reqid;
24801361951Seric s->proc = proc;
24901361951Seric break;
25001361951Seric }
25101361951Seric save_errno = errno;
25201361951Seric
25301361951Seric if (q)
25401361951Seric asr_abort(q);
25501361951Seric if (s)
25601361951Seric free(s);
25701361951Seric
25801361951Seric m_create(proc, IMSG_RES_QUERY, reqid, 0, -1);
25901361951Seric m_add_int(proc, NETDB_INTERNAL);
26001361951Seric m_add_int(proc, save_errno);
26101361951Seric m_add_int(proc, 0);
26201361951Seric m_add_int(proc, 0);
26301361951Seric m_add_data(proc, NULL, 0);
26401361951Seric m_close(proc);
26501361951Seric break;
26601361951Seric
26701eba458Seric default:
26801eba458Seric fatalx("%s: %s", __func__, imsg_to_str(imsg->hdr.type));
26901eba458Seric }
27001eba458Seric }
27101eba458Seric
272ce5509e4Seric static struct addrinfo *
_alloc_addrinfo(const struct addrinfo * ai0,const struct sockaddr * sa,const char * cname)273ce5509e4Seric _alloc_addrinfo(const struct addrinfo *ai0, const struct sockaddr *sa,
274ce5509e4Seric const char *cname)
275ce5509e4Seric {
276ce5509e4Seric struct addrinfo *ai;
277ce5509e4Seric
278ce5509e4Seric ai = calloc(1, sizeof(*ai) + sa->sa_len);
279ce5509e4Seric if (ai == NULL) {
280ce5509e4Seric log_warn("%s: calloc", __func__);
281ce5509e4Seric return NULL;
282ce5509e4Seric }
283ce5509e4Seric *ai = *ai0;
284ce5509e4Seric ai->ai_addr = (void *)(ai + 1);
285ce5509e4Seric memcpy(ai->ai_addr, sa, sa->sa_len);
286ce5509e4Seric
287ce5509e4Seric if (cname) {
288ce5509e4Seric ai->ai_canonname = strdup(cname);
289ce5509e4Seric if (ai->ai_canonname == NULL) {
290ce5509e4Seric log_warn("%s: strdup", __func__);
291ce5509e4Seric free(ai);
292ce5509e4Seric return NULL;
293ce5509e4Seric }
294ce5509e4Seric }
295ce5509e4Seric
296ce5509e4Seric return ai;
297ce5509e4Seric }
298ce5509e4Seric
29901eba458Seric void
resolver_dispatch_result(struct mproc * proc,struct imsg * imsg)30001eba458Seric resolver_dispatch_result(struct mproc *proc, struct imsg *imsg)
30101eba458Seric {
30201eba458Seric struct request key, *req;
30301eba458Seric struct sockaddr_storage ss;
304ce5509e4Seric struct addrinfo *ai, tai;
30501eba458Seric struct msg m;
30601eba458Seric const char *cname, *host, *serv;
30701361951Seric const void *data;
30801361951Seric size_t datalen;
30901361951Seric int gai_errno, herrno, rcode, count;
31001eba458Seric
31101eba458Seric key.id = imsg->hdr.peerid;
31201eba458Seric req = SPLAY_FIND(reqtree, &reqs, &key);
31301eba458Seric if (req == NULL)
31401eba458Seric fatalx("%s: unknown request %08x", __func__, imsg->hdr.peerid);
31501eba458Seric
31601eba458Seric m_msg(&m, imsg);
31701eba458Seric
31801eba458Seric switch (imsg->hdr.type) {
31901eba458Seric
32001eba458Seric case IMSG_GETADDRINFO:
321ce5509e4Seric memset(&tai, 0, sizeof(tai));
322ce5509e4Seric m_get_int(&m, &tai.ai_flags);
323ce5509e4Seric m_get_int(&m, &tai.ai_family);
324ce5509e4Seric m_get_int(&m, &tai.ai_socktype);
325ce5509e4Seric m_get_int(&m, &tai.ai_protocol);
32601eba458Seric m_get_sockaddr(&m, (struct sockaddr *)&ss);
32701eba458Seric m_get_string(&m, &cname);
32801eba458Seric m_end(&m);
32901eba458Seric
330ce5509e4Seric ai = _alloc_addrinfo(&tai, (struct sockaddr *)&ss, cname);
331ce5509e4Seric if (ai) {
33201eba458Seric ai->ai_next = req->ai;
33301eba458Seric req->ai = ai;
334ce5509e4Seric }
33501eba458Seric break;
33601eba458Seric
33701eba458Seric case IMSG_GETADDRINFO_END:
33801eba458Seric m_get_int(&m, &gai_errno);
33901eba458Seric m_get_int(&m, &errno);
34001eba458Seric m_end(&m);
34101eba458Seric
34201eba458Seric SPLAY_REMOVE(reqtree, &reqs, req);
34301eba458Seric req->cb_ai(req->arg, gai_errno, req->ai);
34401eba458Seric free(req);
34501eba458Seric break;
34601eba458Seric
34701eba458Seric case IMSG_GETNAMEINFO:
34801eba458Seric m_get_int(&m, &gai_errno);
34901eba458Seric m_get_int(&m, &errno);
35001eba458Seric m_get_string(&m, &host);
35101eba458Seric m_get_string(&m, &serv);
35201eba458Seric m_end(&m);
35301eba458Seric
35401eba458Seric SPLAY_REMOVE(reqtree, &reqs, req);
3559ec9b5d0Seric req->cb_ni(req->arg, gai_errno, host, serv);
35601eba458Seric free(req);
35701eba458Seric break;
35801361951Seric
35901361951Seric case IMSG_RES_QUERY:
36001361951Seric m_get_int(&m, &herrno);
36101361951Seric m_get_int(&m, &errno);
36201361951Seric m_get_int(&m, &rcode);
36301361951Seric m_get_int(&m, &count);
36401361951Seric m_get_data(&m, &data, &datalen);
36501361951Seric m_end(&m);
36601361951Seric
36701361951Seric SPLAY_REMOVE(reqtree, &reqs, req);
36801361951Seric req->cb_res(req->arg, herrno, rcode, count, data, datalen);
36901361951Seric free(req);
37001361951Seric break;
37101eba458Seric }
37201eba458Seric }
37301eba458Seric
37401eba458Seric static void
resolver_init(void)37501eba458Seric resolver_init(void)
37601eba458Seric {
37701eba458Seric static int init = 0;
37801eba458Seric
37901eba458Seric if (init == 0) {
38001eba458Seric SPLAY_INIT(&reqs);
38101eba458Seric init = 1;
38201eba458Seric }
38301eba458Seric }
38401eba458Seric
38501eba458Seric static void
resolver_getaddrinfo_cb(struct asr_result * ar,void * arg)38601eba458Seric resolver_getaddrinfo_cb(struct asr_result *ar, void *arg)
38701eba458Seric {
38801eba458Seric struct session *s = arg;
38901eba458Seric struct addrinfo *ai;
39001eba458Seric
39101eba458Seric for (ai = ar->ar_addrinfo; ai; ai = ai->ai_next) {
39201eba458Seric m_create(s->proc, IMSG_GETADDRINFO, s->reqid, 0, -1);
39301eba458Seric m_add_int(s->proc, ai->ai_flags);
39401eba458Seric m_add_int(s->proc, ai->ai_family);
39501eba458Seric m_add_int(s->proc, ai->ai_socktype);
39601eba458Seric m_add_int(s->proc, ai->ai_protocol);
39701eba458Seric m_add_sockaddr(s->proc, ai->ai_addr);
3982c59994aSeric m_add_string(s->proc, ai->ai_canonname);
39901eba458Seric m_close(s->proc);
40001eba458Seric }
40101eba458Seric
40201eba458Seric m_create(s->proc, IMSG_GETADDRINFO_END, s->reqid, 0, -1);
40301eba458Seric m_add_int(s->proc, ar->ar_gai_errno);
40401eba458Seric m_add_int(s->proc, ar->ar_errno);
40501eba458Seric m_close(s->proc);
40601eba458Seric
4072c59994aSeric if (ar->ar_addrinfo)
40801eba458Seric freeaddrinfo(ar->ar_addrinfo);
40901eba458Seric free(s);
41001eba458Seric }
41101eba458Seric
41201eba458Seric static void
resolver_getnameinfo_cb(struct asr_result * ar,void * arg)41301eba458Seric resolver_getnameinfo_cb(struct asr_result *ar, void *arg)
41401eba458Seric {
41501eba458Seric struct session *s = arg;
41601eba458Seric
41701eba458Seric m_create(s->proc, IMSG_GETNAMEINFO, s->reqid, 0, -1);
41801eba458Seric m_add_int(s->proc, ar->ar_gai_errno);
41901eba458Seric m_add_int(s->proc, ar->ar_errno);
4209ec9b5d0Seric m_add_string(s->proc, ar->ar_gai_errno ? NULL : s->host);
4219ec9b5d0Seric m_add_string(s->proc, ar->ar_gai_errno ? NULL : s->serv);
42201eba458Seric m_close(s->proc);
42301eba458Seric
42401eba458Seric free(s->host);
42501eba458Seric free(s->serv);
42601eba458Seric free(s);
42701eba458Seric }
42801eba458Seric
42901361951Seric static void
resolver_res_query_cb(struct asr_result * ar,void * arg)43001361951Seric resolver_res_query_cb(struct asr_result *ar, void *arg)
43101361951Seric {
43201361951Seric struct session *s = arg;
43301361951Seric
43401361951Seric m_create(s->proc, IMSG_RES_QUERY, s->reqid, 0, -1);
43501361951Seric m_add_int(s->proc, ar->ar_h_errno);
43601361951Seric m_add_int(s->proc, ar->ar_errno);
43701361951Seric m_add_int(s->proc, ar->ar_rcode);
43801361951Seric m_add_int(s->proc, ar->ar_count);
43901361951Seric m_add_data(s->proc, ar->ar_data, ar->ar_datalen);
44001361951Seric m_close(s->proc);
44101361951Seric
44201361951Seric free(ar->ar_data);
44301361951Seric free(s);
44401361951Seric }
44501361951Seric
44601eba458Seric static int
request_cmp(struct request * a,struct request * b)44701eba458Seric request_cmp(struct request *a, struct request *b)
44801eba458Seric {
44901eba458Seric if (a->id < b->id)
45001eba458Seric return -1;
45101eba458Seric if (a->id > b->id)
45201eba458Seric return 1;
45301eba458Seric return 0;
45401eba458Seric }
45501eba458Seric
45601eba458Seric SPLAY_GENERATE(reqtree, request, entry, request_cmp);
457