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