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 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 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 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 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 * 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 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 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 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 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 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 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