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