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