xref: /openbsd-src/usr.sbin/smtpd/resolver.c (revision d1df930ffab53da22f3324c32bed7ac5709915e6)
1 /*	$OpenBSD: resolver.c,v 1.2 2018/08/29 17:56:17 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 "smtpd.h"
37 #include "log.h"
38 
39 #define p_resolver p_lka
40 
41 struct request {
42 	SPLAY_ENTRY(request)	 entry;
43 	uint32_t		 id;
44 	void			(*cb_ai)(void *, int, struct addrinfo *);
45 	void			(*cb_ni)(void *, int, const char *, const char *);
46 	void			*arg;
47 	struct addrinfo		*ai;
48 };
49 
50 struct session {
51 	uint32_t	 reqid;
52 	struct mproc	*proc;
53 	char		*host;
54 	char		*serv;
55 };
56 
57 SPLAY_HEAD(reqtree, request);
58 
59 static void resolver_init(void);
60 static void resolver_getaddrinfo_cb(struct asr_result *, void *);
61 static void resolver_getnameinfo_cb(struct asr_result *, void *);
62 
63 static int request_cmp(struct request *, struct request *);
64 SPLAY_PROTOTYPE(reqtree, request, entry, request_cmp);
65 
66 static struct reqtree reqs;
67 
68 void
69 resolver_getaddrinfo(const char *hostname, const char *servname,
70     const struct addrinfo *hints, void (*cb)(void *, int, struct addrinfo *),
71     void *arg)
72 {
73 	struct request *req;
74 
75 	resolver_init();
76 
77 	req = calloc(1, sizeof(*req));
78 	if (req == NULL) {
79 		cb(arg, EAI_MEMORY, NULL);
80 		return;
81 	}
82 
83 	while (req->id == 0 || SPLAY_FIND(reqtree, &reqs, req))
84 		req->id = arc4random();
85 	req->cb_ai = cb;
86 	req->arg = arg;
87 
88 	SPLAY_INSERT(reqtree, &reqs, req);
89 
90 	m_create(p_resolver, IMSG_GETADDRINFO, req->id, 0, -1);
91 	m_add_int(p_resolver, hints ? hints->ai_flags : 0);
92 	m_add_int(p_resolver, hints ? hints->ai_family : 0);
93 	m_add_int(p_resolver, hints ? hints->ai_socktype : 0);
94 	m_add_int(p_resolver, hints ? hints->ai_protocol : 0);
95 	m_add_string(p_resolver, hostname);
96 	m_add_string(p_resolver, servname ? servname : "");
97 	m_close(p_resolver);
98 }
99 
100 void
101 resolver_getnameinfo(const struct sockaddr *sa, int flags,
102     void(*cb)(void *, int, const char *, const char *), void *arg)
103 {
104 	struct request *req;
105 
106 	resolver_init();
107 
108 	req = calloc(1, sizeof(*req));
109 	if (req == NULL) {
110 		cb(arg, EAI_MEMORY, NULL, NULL);
111 		return;
112 	}
113 
114 	while (req->id == 0 || SPLAY_FIND(reqtree, &reqs, req))
115 		req->id = arc4random();
116 	req->cb_ni = cb;
117 	req->arg = arg;
118 
119 	SPLAY_INSERT(reqtree, &reqs, req);
120 
121 	m_create(p_resolver, IMSG_GETNAMEINFO, req->id, 0, -1);
122 	m_add_sockaddr(p_resolver, sa);
123 	m_add_int(p_resolver, flags);
124 	m_close(p_resolver);
125 }
126 
127 void
128 resolver_dispatch_request(struct mproc *proc, struct imsg *imsg)
129 {
130 	const char *hostname, *servname;
131 	struct session *s;
132 	struct asr_query *q;
133 	struct addrinfo hints;
134 	struct sockaddr_storage ss;
135 	struct sockaddr *sa;
136 	struct msg m;
137 	uint32_t reqid;
138 	int flags, save_errno;
139 
140 	reqid = imsg->hdr.peerid;
141 	m_msg(&m, imsg);
142 
143 	switch (imsg->hdr.type) {
144 
145 	case IMSG_GETADDRINFO:
146 		servname = NULL;
147 		memset(&hints, 0 , sizeof(hints));
148 		m_get_int(&m, &hints.ai_flags);
149 		m_get_int(&m, &hints.ai_family);
150 		m_get_int(&m, &hints.ai_socktype);
151 		m_get_int(&m, &hints.ai_protocol);
152 		m_get_string(&m, &hostname);
153 		if (!m_is_eom(&m))
154 			m_get_string(&m, &servname);
155 		m_end(&m);
156 
157 		s = NULL;
158 		q = NULL;
159 		if ((s = calloc(1, sizeof(*s))) &&
160 		    (q = getaddrinfo_async(hostname, servname, &hints, NULL)) &&
161 		    (event_asr_run(q, resolver_getaddrinfo_cb, s))) {
162 			s->reqid = reqid;
163 			s->proc = proc;
164 			break;
165 		}
166 		save_errno = errno;
167 
168 		if (q)
169 			asr_abort(q);
170 		if (s)
171 			free(s);
172 
173 		m_create(proc, IMSG_GETADDRINFO_END, reqid, 0, -1);
174 		m_add_int(proc, EAI_SYSTEM);
175 		m_add_int(proc, save_errno);
176 		m_close(proc);
177 		break;
178 
179 	case IMSG_GETNAMEINFO:
180 		sa = (struct sockaddr*)&ss;
181 		m_get_sockaddr(&m, sa);
182 		m_get_int(&m, &flags);
183 		m_end(&m);
184 
185 		s = NULL;
186 		q = NULL;
187 		if ((s = calloc(1, sizeof(*s))) &&
188 		    (s->host = malloc(NI_MAXHOST)) &&
189 		    (s->serv = malloc(NI_MAXSERV)) &&
190 		    (q = getnameinfo_async(sa, sa->sa_len, s->host, NI_MAXHOST,
191 			s->serv, NI_MAXSERV, flags, NULL)) &&
192 		    (event_asr_run(q, resolver_getnameinfo_cb, s))) {
193 			s->reqid = reqid;
194 			s->proc = proc;
195 			break;
196 		}
197 		save_errno = errno;
198 
199 		if (q)
200 			asr_abort(q);
201 		if (s) {
202 			free(s->host);
203 			free(s->serv);
204 			free(s);
205 		}
206 
207 		m_create(proc, IMSG_GETNAMEINFO, 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 	default:
214 		fatalx("%s: %s", __func__, imsg_to_str(imsg->hdr.type));
215 	}
216 }
217 
218 void
219 resolver_dispatch_result(struct mproc *proc, struct imsg *imsg)
220 {
221 	struct request key, *req;
222 	struct sockaddr_storage ss;
223 	struct addrinfo *ai;
224 	struct msg m;
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 	m_msg(&m, imsg);
234 
235 	switch (imsg->hdr.type) {
236 
237 	case IMSG_GETADDRINFO:
238 		ai = calloc(1, sizeof(*ai));
239 		if (ai == NULL) {
240 			log_warn("%s: calloc", __func__);
241 			break;
242 		}
243 		m_get_int(&m, &ai->ai_flags);
244 		m_get_int(&m, &ai->ai_family);
245 		m_get_int(&m, &ai->ai_socktype);
246 		m_get_int(&m, &ai->ai_protocol);
247 		m_get_sockaddr(&m, (struct sockaddr *)&ss);
248 		m_get_string(&m, &cname);
249 		m_end(&m);
250 
251 		ai->ai_addr = malloc(ss.ss_len);
252 		if (ai->ai_addr == NULL) {
253 			log_warn("%s: malloc", __func__);
254 			free(ai);
255 			break;
256 		}
257 
258 		memmove(ai->ai_addr, &ss, ss.ss_len);
259 
260 		if (cname[0]) {
261 			ai->ai_canonname = strdup(cname);
262 			if (ai->ai_canonname == NULL) {
263 				log_warn("%s: strdup", __func__);
264 				free(ai->ai_addr);
265 				free(ai);
266 				break;
267 			}
268 		}
269 
270 		ai->ai_next = req->ai;
271 		req->ai = ai;
272 		break;
273 
274 	case IMSG_GETADDRINFO_END:
275 		m_get_int(&m, &gai_errno);
276 		m_get_int(&m, &errno);
277 		m_end(&m);
278 
279 		SPLAY_REMOVE(reqtree, &reqs, req);
280 		req->cb_ai(req->arg, gai_errno, req->ai);
281 		free(req);
282 		break;
283 
284 	case IMSG_GETNAMEINFO:
285 		m_get_int(&m, &gai_errno);
286 		m_get_int(&m, &errno);
287 		if (gai_errno == 0) {
288 			m_get_string(&m, &host);
289 			m_get_string(&m, &serv);
290 		}
291 		m_end(&m);
292 
293 		SPLAY_REMOVE(reqtree, &reqs, req);
294 		req->cb_ni(req->arg, gai_errno, gai_errno ? NULL : host,
295 		    gai_errno ? NULL : serv);
296 		free(req);
297 		break;
298 	}
299 }
300 
301 static void
302 resolver_init(void)
303 {
304 	static int init = 0;
305 
306 	if (init == 0) {
307 		SPLAY_INIT(&reqs);
308 		init = 1;
309 	}
310 }
311 
312 static void
313 resolver_getaddrinfo_cb(struct asr_result *ar, void *arg)
314 {
315 	struct session *s = arg;
316 	struct addrinfo *ai;
317 
318 	for (ai = ar->ar_addrinfo; ai; ai = ai->ai_next) {
319 		m_create(s->proc, IMSG_GETADDRINFO, s->reqid, 0, -1);
320 		m_add_int(s->proc, ai->ai_flags);
321 		m_add_int(s->proc, ai->ai_family);
322 		m_add_int(s->proc, ai->ai_socktype);
323 		m_add_int(s->proc, ai->ai_protocol);
324 		m_add_sockaddr(s->proc, ai->ai_addr);
325 		m_add_string(s->proc, ai->ai_canonname ?
326 		    ai->ai_canonname : "");
327 		m_close(s->proc);
328 	}
329 
330 	m_create(s->proc, IMSG_GETADDRINFO_END, s->reqid, 0, -1);
331 	m_add_int(s->proc, ar->ar_gai_errno);
332 	m_add_int(s->proc, ar->ar_errno);
333 	m_close(s->proc);
334 
335 	freeaddrinfo(ar->ar_addrinfo);
336 	free(s);
337 }
338 
339 static void
340 resolver_getnameinfo_cb(struct asr_result *ar, void *arg)
341 {
342 	struct session *s = arg;
343 
344 	m_create(s->proc, IMSG_GETNAMEINFO, s->reqid, 0, -1);
345 	m_add_int(s->proc, ar->ar_gai_errno);
346 	m_add_int(s->proc, ar->ar_errno);
347 	if (ar->ar_gai_errno == 0) {
348 		m_add_string(s->proc, s->host);
349 		m_add_string(s->proc, s->serv);
350 	}
351 	m_close(s->proc);
352 
353 	free(s->host);
354 	free(s->serv);
355 	free(s);
356 }
357 
358 static int
359 request_cmp(struct request *a, struct request *b)
360 {
361 	if (a->id < b->id)
362 		return -1;
363 	if (a->id > b->id)
364 		return 1;
365 	return 0;
366 }
367 
368 SPLAY_GENERATE(reqtree, request, entry, request_cmp);
369