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