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