1 /* $OpenBSD: mproc.c,v 1.28 2016/09/14 08:59:56 eric Exp $ */ 2 3 /* 4 * Copyright (c) 2012 Eric Faurot <eric@faurot.net> 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 <sys/uio.h> 24 25 #include <netinet/in.h> 26 #include <arpa/inet.h> 27 #include <arpa/nameser.h> 28 29 #include <err.h> 30 #include <errno.h> 31 #include <event.h> 32 #include <imsg.h> 33 #include <stdio.h> 34 #include <stdlib.h> 35 #include <limits.h> 36 #include <string.h> 37 #include <unistd.h> 38 39 #include "smtpd.h" 40 #include "log.h" 41 42 static void mproc_dispatch(int, short, void *); 43 44 static ssize_t imsg_read_nofd(struct imsgbuf *); 45 46 int 47 mproc_fork(struct mproc *p, const char *path, char *argv[]) 48 { 49 int sp[2]; 50 51 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, sp) < 0) 52 return (-1); 53 54 io_set_nonblocking(sp[0]); 55 io_set_nonblocking(sp[1]); 56 57 if ((p->pid = fork()) == -1) 58 goto err; 59 60 if (p->pid == 0) { 61 /* child process */ 62 dup2(sp[0], STDIN_FILENO); 63 if (closefrom(STDERR_FILENO + 1) < 0) 64 exit(1); 65 66 execv(path, argv); 67 err(1, "execv: %s", path); 68 } 69 70 /* parent process */ 71 close(sp[0]); 72 mproc_init(p, sp[1]); 73 return (0); 74 75 err: 76 log_warn("warn: Failed to start process %s, instance of %s", argv[0], path); 77 close(sp[0]); 78 close(sp[1]); 79 return (-1); 80 } 81 82 void 83 mproc_init(struct mproc *p, int fd) 84 { 85 imsg_init(&p->imsgbuf, fd); 86 } 87 88 void 89 mproc_clear(struct mproc *p) 90 { 91 log_debug("debug: clearing p=%s, fd=%d, pid=%d", p->name, p->imsgbuf.fd, p->pid); 92 93 event_del(&p->ev); 94 close(p->imsgbuf.fd); 95 imsg_clear(&p->imsgbuf); 96 } 97 98 void 99 mproc_enable(struct mproc *p) 100 { 101 if (p->enable == 0) { 102 log_trace(TRACE_MPROC, "mproc: %s -> %s: enabled", 103 proc_name(smtpd_process), 104 proc_name(p->proc)); 105 p->enable = 1; 106 } 107 mproc_event_add(p); 108 } 109 110 void 111 mproc_disable(struct mproc *p) 112 { 113 if (p->enable == 1) { 114 log_trace(TRACE_MPROC, "mproc: %s -> %s: disabled", 115 proc_name(smtpd_process), 116 proc_name(p->proc)); 117 p->enable = 0; 118 } 119 mproc_event_add(p); 120 } 121 122 void 123 mproc_event_add(struct mproc *p) 124 { 125 short events; 126 127 if (p->enable) 128 events = EV_READ; 129 else 130 events = 0; 131 132 if (p->imsgbuf.w.queued) 133 events |= EV_WRITE; 134 135 if (p->events) 136 event_del(&p->ev); 137 138 p->events = events; 139 if (events) { 140 event_set(&p->ev, p->imsgbuf.fd, events, mproc_dispatch, p); 141 event_add(&p->ev, NULL); 142 } 143 } 144 145 static void 146 mproc_dispatch(int fd, short event, void *arg) 147 { 148 struct mproc *p = arg; 149 struct imsg imsg; 150 ssize_t n; 151 152 p->events = 0; 153 154 if (event & EV_READ) { 155 156 if (p->proc == PROC_CLIENT) 157 n = imsg_read_nofd(&p->imsgbuf); 158 else 159 n = imsg_read(&p->imsgbuf); 160 161 switch (n) { 162 case -1: 163 if (errno == EAGAIN) 164 break; 165 log_warn("warn: %s -> %s: imsg_read", 166 proc_name(smtpd_process), p->name); 167 fatal("exiting"); 168 /* NOTREACHED */ 169 case 0: 170 /* this pipe is dead, so remove the event handler */ 171 log_debug("debug: %s -> %s: pipe closed", 172 proc_name(smtpd_process), p->name); 173 p->handler(p, NULL); 174 return; 175 default: 176 break; 177 } 178 } 179 180 if (event & EV_WRITE) { 181 n = msgbuf_write(&p->imsgbuf.w); 182 if (n == 0 || (n == -1 && errno != EAGAIN)) { 183 /* this pipe is dead, so remove the event handler */ 184 log_debug("debug: %s -> %s: pipe closed", 185 proc_name(smtpd_process), p->name); 186 p->handler(p, NULL); 187 return; 188 } 189 } 190 191 for (;;) { 192 if ((n = imsg_get(&p->imsgbuf, &imsg)) == -1) { 193 194 if (smtpd_process == PROC_CONTROL && 195 p->proc == PROC_CLIENT) { 196 log_warnx("warn: client sent invalid imsg " 197 "over control socket"); 198 p->handler(p, NULL); 199 return; 200 } 201 log_warn("fatal: %s: error in imsg_get for %s", 202 proc_name(smtpd_process), p->name); 203 fatalx(NULL); 204 } 205 if (n == 0) 206 break; 207 208 p->handler(p, &imsg); 209 210 imsg_free(&imsg); 211 } 212 213 mproc_event_add(p); 214 } 215 216 /* This should go into libutil */ 217 static ssize_t 218 imsg_read_nofd(struct imsgbuf *ibuf) 219 { 220 ssize_t n; 221 char *buf; 222 size_t len; 223 224 buf = ibuf->r.buf + ibuf->r.wpos; 225 len = sizeof(ibuf->r.buf) - ibuf->r.wpos; 226 227 while ((n = recv(ibuf->fd, buf, len, 0)) == -1) { 228 if (errno != EINTR) 229 return (n); 230 } 231 232 ibuf->r.wpos += n; 233 return (n); 234 } 235 236 void 237 m_forward(struct mproc *p, struct imsg *imsg) 238 { 239 imsg_compose(&p->imsgbuf, imsg->hdr.type, imsg->hdr.peerid, 240 imsg->hdr.pid, imsg->fd, imsg->data, 241 imsg->hdr.len - sizeof(imsg->hdr)); 242 243 if (imsg->hdr.type != IMSG_STAT_DECREMENT && 244 imsg->hdr.type != IMSG_STAT_INCREMENT) 245 log_trace(TRACE_MPROC, "mproc: %s -> %s : %zu %s (forward)", 246 proc_name(smtpd_process), 247 proc_name(p->proc), 248 imsg->hdr.len - sizeof(imsg->hdr), 249 imsg_to_str(imsg->hdr.type)); 250 251 mproc_event_add(p); 252 } 253 254 void 255 m_compose(struct mproc *p, uint32_t type, uint32_t peerid, pid_t pid, int fd, 256 void *data, size_t len) 257 { 258 imsg_compose(&p->imsgbuf, type, peerid, pid, fd, data, len); 259 260 if (type != IMSG_STAT_DECREMENT && 261 type != IMSG_STAT_INCREMENT) 262 log_trace(TRACE_MPROC, "mproc: %s -> %s : %zu %s", 263 proc_name(smtpd_process), 264 proc_name(p->proc), 265 len, 266 imsg_to_str(type)); 267 268 mproc_event_add(p); 269 } 270 271 void 272 m_composev(struct mproc *p, uint32_t type, uint32_t peerid, pid_t pid, 273 int fd, const struct iovec *iov, int n) 274 { 275 size_t len; 276 int i; 277 278 imsg_composev(&p->imsgbuf, type, peerid, pid, fd, iov, n); 279 280 len = 0; 281 for (i = 0; i < n; i++) 282 len += iov[i].iov_len; 283 284 if (type != IMSG_STAT_DECREMENT && 285 type != IMSG_STAT_INCREMENT) 286 log_trace(TRACE_MPROC, "mproc: %s -> %s : %zu %s", 287 proc_name(smtpd_process), 288 proc_name(p->proc), 289 len, 290 imsg_to_str(type)); 291 292 mproc_event_add(p); 293 } 294 295 void 296 m_create(struct mproc *p, uint32_t type, uint32_t peerid, pid_t pid, int fd) 297 { 298 if (p->m_buf == NULL) { 299 p->m_alloc = 128; 300 log_trace(TRACE_MPROC, "mproc: %s -> %s: allocating %zu", 301 proc_name(smtpd_process), 302 proc_name(p->proc), 303 p->m_alloc); 304 p->m_buf = malloc(p->m_alloc); 305 if (p->m_buf == NULL) 306 fatal("warn: m_create: malloc"); 307 } 308 309 p->m_pos = 0; 310 p->m_type = type; 311 p->m_peerid = peerid; 312 p->m_pid = pid; 313 p->m_fd = fd; 314 } 315 316 void 317 m_add(struct mproc *p, const void *data, size_t len) 318 { 319 size_t alloc; 320 void *tmp; 321 322 if (p->m_pos + len + IMSG_HEADER_SIZE > MAX_IMSGSIZE) { 323 log_warnx("warn: message to large"); 324 fatal(NULL); 325 } 326 327 alloc = p->m_alloc; 328 while (p->m_pos + len > alloc) 329 alloc *= 2; 330 if (alloc != p->m_alloc) { 331 log_trace(TRACE_MPROC, "mproc: %s -> %s: realloc %zu -> %zu", 332 proc_name(smtpd_process), 333 proc_name(p->proc), 334 p->m_alloc, 335 alloc); 336 337 tmp = realloc(p->m_buf, alloc); 338 if (tmp == NULL) 339 fatal("realloc"); 340 p->m_alloc = alloc; 341 p->m_buf = tmp; 342 } 343 344 memmove(p->m_buf + p->m_pos, data, len); 345 p->m_pos += len; 346 } 347 348 void 349 m_close(struct mproc *p) 350 { 351 if (imsg_compose(&p->imsgbuf, p->m_type, p->m_peerid, p->m_pid, p->m_fd, 352 p->m_buf, p->m_pos) == -1) 353 fatal("imsg_compose"); 354 355 log_trace(TRACE_MPROC, "mproc: %s -> %s : %zu %s", 356 proc_name(smtpd_process), 357 proc_name(p->proc), 358 p->m_pos, 359 imsg_to_str(p->m_type)); 360 361 mproc_event_add(p); 362 } 363 364 void 365 m_flush(struct mproc *p) 366 { 367 if (imsg_compose(&p->imsgbuf, p->m_type, p->m_peerid, p->m_pid, p->m_fd, 368 p->m_buf, p->m_pos) == -1) 369 fatal("imsg_compose"); 370 371 log_trace(TRACE_MPROC, "mproc: %s -> %s : %zu %s (flush)", 372 proc_name(smtpd_process), 373 proc_name(p->proc), 374 p->m_pos, 375 imsg_to_str(p->m_type)); 376 377 p->m_pos = 0; 378 379 imsg_flush(&p->imsgbuf); 380 } 381 382 static struct imsg * current; 383 384 static void 385 m_error(const char *error) 386 { 387 char buf[512]; 388 389 (void)snprintf(buf, sizeof buf, "%s: %s: %s", 390 proc_name(smtpd_process), 391 imsg_to_str(current->hdr.type), 392 error); 393 fatalx("%s", buf); 394 } 395 396 void 397 m_msg(struct msg *m, struct imsg *imsg) 398 { 399 current = imsg; 400 m->pos = imsg->data; 401 m->end = m->pos + (imsg->hdr.len - sizeof(imsg->hdr)); 402 } 403 404 void 405 m_end(struct msg *m) 406 { 407 if (m->pos != m->end) 408 m_error("not at msg end"); 409 } 410 411 int 412 m_is_eom(struct msg *m) 413 { 414 return (m->pos == m->end); 415 } 416 417 static inline void 418 m_get(struct msg *m, void *dst, size_t sz) 419 { 420 if (sz > MAX_IMSGSIZE || 421 m->end - m->pos < (ssize_t)sz) 422 fatalx("msg too short"); 423 424 memmove(dst, m->pos, sz); 425 m->pos += sz; 426 } 427 428 void 429 m_add_int(struct mproc *m, int v) 430 { 431 m_add(m, &v, sizeof(v)); 432 }; 433 434 void 435 m_add_u32(struct mproc *m, uint32_t u32) 436 { 437 m_add(m, &u32, sizeof(u32)); 438 }; 439 440 void 441 m_add_size(struct mproc *m, size_t sz) 442 { 443 m_add(m, &sz, sizeof(sz)); 444 }; 445 446 void 447 m_add_time(struct mproc *m, time_t v) 448 { 449 m_add(m, &v, sizeof(v)); 450 }; 451 452 void 453 m_add_string(struct mproc *m, const char *v) 454 { 455 m_add(m, v, strlen(v) + 1); 456 }; 457 458 void 459 m_add_data(struct mproc *m, const void *v, size_t len) 460 { 461 m_add_size(m, len); 462 m_add(m, v, len); 463 }; 464 465 void 466 m_add_id(struct mproc *m, uint64_t v) 467 { 468 m_add(m, &v, sizeof(v)); 469 } 470 471 void 472 m_add_evpid(struct mproc *m, uint64_t v) 473 { 474 m_add(m, &v, sizeof(v)); 475 } 476 477 void 478 m_add_msgid(struct mproc *m, uint32_t v) 479 { 480 m_add(m, &v, sizeof(v)); 481 } 482 483 void 484 m_add_sockaddr(struct mproc *m, const struct sockaddr *sa) 485 { 486 m_add_size(m, sa->sa_len); 487 m_add(m, sa, sa->sa_len); 488 } 489 490 void 491 m_add_mailaddr(struct mproc *m, const struct mailaddr *maddr) 492 { 493 m_add(m, maddr, sizeof(*maddr)); 494 } 495 496 void 497 m_add_envelope(struct mproc *m, const struct envelope *evp) 498 { 499 char buf[sizeof(*evp)]; 500 501 envelope_dump_buffer(evp, buf, sizeof(buf)); 502 m_add_evpid(m, evp->id); 503 m_add_string(m, buf); 504 } 505 506 void 507 m_add_params(struct mproc *m, struct dict *d) 508 { 509 const char *key; 510 char *value; 511 void *iter; 512 513 if (d == NULL) { 514 m_add_size(m, 0); 515 return; 516 } 517 m_add_size(m, dict_count(d)); 518 iter = NULL; 519 while (dict_iter(d, &iter, &key, (void **)&value)) { 520 m_add_string(m, key); 521 m_add_string(m, value); 522 } 523 } 524 525 void 526 m_get_int(struct msg *m, int *i) 527 { 528 m_get(m, i, sizeof(*i)); 529 } 530 531 void 532 m_get_u32(struct msg *m, uint32_t *u32) 533 { 534 m_get(m, u32, sizeof(*u32)); 535 } 536 537 void 538 m_get_size(struct msg *m, size_t *sz) 539 { 540 m_get(m, sz, sizeof(*sz)); 541 } 542 543 void 544 m_get_time(struct msg *m, time_t *t) 545 { 546 m_get(m, t, sizeof(*t)); 547 } 548 549 void 550 m_get_string(struct msg *m, const char **s) 551 { 552 uint8_t *end; 553 554 if (m->pos >= m->end) 555 m_error("msg too short"); 556 557 end = memchr(m->pos, 0, m->end - m->pos); 558 if (end == NULL) 559 m_error("unterminated string"); 560 561 *s = m->pos; 562 m->pos = end + 1; 563 } 564 565 void 566 m_get_data(struct msg *m, const void **data, size_t *sz) 567 { 568 m_get_size(m, sz); 569 570 if (m->pos + *sz > m->end) 571 m_error("msg too short"); 572 573 *data = m->pos; 574 m->pos += *sz; 575 } 576 577 void 578 m_get_evpid(struct msg *m, uint64_t *evpid) 579 { 580 m_get(m, evpid, sizeof(*evpid)); 581 } 582 583 void 584 m_get_msgid(struct msg *m, uint32_t *msgid) 585 { 586 m_get(m, msgid, sizeof(*msgid)); 587 } 588 589 void 590 m_get_id(struct msg *m, uint64_t *id) 591 { 592 m_get(m, id, sizeof(*id)); 593 } 594 595 void 596 m_get_sockaddr(struct msg *m, struct sockaddr *sa) 597 { 598 size_t len; 599 600 m_get_size(m, &len); 601 m_get(m, sa, len); 602 } 603 604 void 605 m_get_mailaddr(struct msg *m, struct mailaddr *maddr) 606 { 607 m_get(m, maddr, sizeof(*maddr)); 608 } 609 610 void 611 m_get_envelope(struct msg *m, struct envelope *evp) 612 { 613 uint64_t evpid; 614 const char *buf; 615 616 m_get_evpid(m, &evpid); 617 m_get_string(m, &buf); 618 619 if (!envelope_load_buffer(evp, buf, strlen(buf))) 620 fatalx("failed to retrieve envelope"); 621 evp->id = evpid; 622 } 623 624 void 625 m_get_params(struct msg *m, struct dict *d) 626 { 627 size_t c; 628 const char *key; 629 const char *value; 630 char *tmp; 631 632 dict_init(d); 633 634 m_get_size(m, &c); 635 636 for (; c; c--) { 637 m_get_string(m, &key); 638 m_get_string(m, &value); 639 if ((tmp = strdup(value)) == NULL) 640 fatal("m_get_params"); 641 dict_set(d, key, tmp); 642 } 643 } 644 645 void 646 m_clear_params(struct dict *d) 647 { 648 char *value; 649 650 while (dict_poproot(d, (void **)&value)) 651 free(value); 652 } 653