GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/smtpd/smtpd/../mproc.c Lines: 0 249 0.0 %
Date: 2017-11-07 Branches: 0 97 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: mproc.c,v 1.29 2017/03/17 20:57:57 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
	p->m_pos = 0;
299
	p->m_type = type;
300
	p->m_peerid = peerid;
301
	p->m_pid = pid;
302
	p->m_fd = fd;
303
}
304
305
void
306
m_add(struct mproc *p, const void *data, size_t len)
307
{
308
	size_t	 alloc;
309
	void	*tmp;
310
311
	if (p->m_pos + len + IMSG_HEADER_SIZE > MAX_IMSGSIZE) {
312
		log_warnx("warn: message to large");
313
		fatal(NULL);
314
	}
315
316
	alloc = p->m_alloc ? p->m_alloc : 128;
317
	while (p->m_pos + len > alloc)
318
		alloc *= 2;
319
	if (alloc != p->m_alloc) {
320
		log_trace(TRACE_MPROC, "mproc: %s -> %s: realloc %zu -> %zu",
321
		    proc_name(smtpd_process),
322
		    proc_name(p->proc),
323
		    p->m_alloc,
324
		    alloc);
325
326
		tmp = recallocarray(p->m_buf, p->m_alloc, alloc, 1);
327
		if (tmp == NULL)
328
			fatal("realloc");
329
		p->m_alloc = alloc;
330
		p->m_buf = tmp;
331
	}
332
333
	memmove(p->m_buf + p->m_pos, data, len);
334
	p->m_pos += len;
335
}
336
337
void
338
m_close(struct mproc *p)
339
{
340
	if (imsg_compose(&p->imsgbuf, p->m_type, p->m_peerid, p->m_pid, p->m_fd,
341
	    p->m_buf, p->m_pos) == -1)
342
		fatal("imsg_compose");
343
344
	log_trace(TRACE_MPROC, "mproc: %s -> %s : %zu %s",
345
		    proc_name(smtpd_process),
346
		    proc_name(p->proc),
347
		    p->m_pos,
348
		    imsg_to_str(p->m_type));
349
350
	mproc_event_add(p);
351
}
352
353
void
354
m_flush(struct mproc *p)
355
{
356
	if (imsg_compose(&p->imsgbuf, p->m_type, p->m_peerid, p->m_pid, p->m_fd,
357
	    p->m_buf, p->m_pos) == -1)
358
		fatal("imsg_compose");
359
360
	log_trace(TRACE_MPROC, "mproc: %s -> %s : %zu %s (flush)",
361
	    proc_name(smtpd_process),
362
	    proc_name(p->proc),
363
	    p->m_pos,
364
	    imsg_to_str(p->m_type));
365
366
	p->m_pos = 0;
367
368
	imsg_flush(&p->imsgbuf);
369
}
370
371
static struct imsg * current;
372
373
static void
374
m_error(const char *error)
375
{
376
	char	buf[512];
377
378
	(void)snprintf(buf, sizeof buf, "%s: %s: %s",
379
	    proc_name(smtpd_process),
380
	    imsg_to_str(current->hdr.type),
381
	    error);
382
	fatalx("%s", buf);
383
}
384
385
void
386
m_msg(struct msg *m, struct imsg *imsg)
387
{
388
	current = imsg;
389
	m->pos = imsg->data;
390
	m->end = m->pos + (imsg->hdr.len - sizeof(imsg->hdr));
391
}
392
393
void
394
m_end(struct msg *m)
395
{
396
	if (m->pos != m->end)
397
		m_error("not at msg end");
398
}
399
400
int
401
m_is_eom(struct msg *m)
402
{
403
	return (m->pos == m->end);
404
}
405
406
static inline void
407
m_get(struct msg *m, void *dst, size_t sz)
408
{
409
	if (sz > MAX_IMSGSIZE ||
410
	    m->end - m->pos < (ssize_t)sz)
411
		fatalx("msg too short");
412
413
	memmove(dst, m->pos, sz);
414
	m->pos += sz;
415
}
416
417
void
418
m_add_int(struct mproc *m, int v)
419
{
420
	m_add(m, &v, sizeof(v));
421
};
422
423
void
424
m_add_u32(struct mproc *m, uint32_t u32)
425
{
426
	m_add(m, &u32, sizeof(u32));
427
};
428
429
void
430
m_add_size(struct mproc *m, size_t sz)
431
{
432
	m_add(m, &sz, sizeof(sz));
433
};
434
435
void
436
m_add_time(struct mproc *m, time_t v)
437
{
438
	m_add(m, &v, sizeof(v));
439
};
440
441
void
442
m_add_string(struct mproc *m, const char *v)
443
{
444
	m_add(m, v, strlen(v) + 1);
445
};
446
447
void
448
m_add_data(struct mproc *m, const void *v, size_t len)
449
{
450
	m_add_size(m, len);
451
	m_add(m, v, len);
452
};
453
454
void
455
m_add_id(struct mproc *m, uint64_t v)
456
{
457
	m_add(m, &v, sizeof(v));
458
}
459
460
void
461
m_add_evpid(struct mproc *m, uint64_t v)
462
{
463
	m_add(m, &v, sizeof(v));
464
}
465
466
void
467
m_add_msgid(struct mproc *m, uint32_t v)
468
{
469
	m_add(m, &v, sizeof(v));
470
}
471
472
void
473
m_add_sockaddr(struct mproc *m, const struct sockaddr *sa)
474
{
475
	m_add_size(m, sa->sa_len);
476
	m_add(m, sa, sa->sa_len);
477
}
478
479
void
480
m_add_mailaddr(struct mproc *m, const struct mailaddr *maddr)
481
{
482
	m_add(m, maddr, sizeof(*maddr));
483
}
484
485
void
486
m_add_envelope(struct mproc *m, const struct envelope *evp)
487
{
488
	char	buf[sizeof(*evp)];
489
490
	envelope_dump_buffer(evp, buf, sizeof(buf));
491
	m_add_evpid(m, evp->id);
492
	m_add_string(m, buf);
493
}
494
495
void
496
m_add_params(struct mproc *m, struct dict *d)
497
{
498
	const char *key;
499
	char *value;
500
	void *iter;
501
502
	if (d == NULL) {
503
		m_add_size(m, 0);
504
		return;
505
	}
506
	m_add_size(m, dict_count(d));
507
	iter = NULL;
508
	while (dict_iter(d, &iter, &key, (void **)&value)) {
509
		m_add_string(m, key);
510
		m_add_string(m, value);
511
	}
512
}
513
514
void
515
m_get_int(struct msg *m, int *i)
516
{
517
	m_get(m, i, sizeof(*i));
518
}
519
520
void
521
m_get_u32(struct msg *m, uint32_t *u32)
522
{
523
	m_get(m, u32, sizeof(*u32));
524
}
525
526
void
527
m_get_size(struct msg *m, size_t *sz)
528
{
529
	m_get(m, sz, sizeof(*sz));
530
}
531
532
void
533
m_get_time(struct msg *m, time_t *t)
534
{
535
	m_get(m, t, sizeof(*t));
536
}
537
538
void
539
m_get_string(struct msg *m, const char **s)
540
{
541
	uint8_t	*end;
542
543
	if (m->pos >= m->end)
544
		m_error("msg too short");
545
546
	end = memchr(m->pos, 0, m->end - m->pos);
547
	if (end == NULL)
548
		m_error("unterminated string");
549
550
	*s = m->pos;
551
	m->pos = end + 1;
552
}
553
554
void
555
m_get_data(struct msg *m, const void **data, size_t *sz)
556
{
557
	m_get_size(m, sz);
558
559
	if (m->pos + *sz > m->end)
560
		m_error("msg too short");
561
562
	*data = m->pos;
563
	m->pos += *sz;
564
}
565
566
void
567
m_get_evpid(struct msg *m, uint64_t *evpid)
568
{
569
	m_get(m, evpid, sizeof(*evpid));
570
}
571
572
void
573
m_get_msgid(struct msg *m, uint32_t *msgid)
574
{
575
	m_get(m, msgid, sizeof(*msgid));
576
}
577
578
void
579
m_get_id(struct msg *m, uint64_t *id)
580
{
581
	m_get(m, id, sizeof(*id));
582
}
583
584
void
585
m_get_sockaddr(struct msg *m, struct sockaddr *sa)
586
{
587
	size_t len;
588
589
	m_get_size(m, &len);
590
	m_get(m, sa, len);
591
}
592
593
void
594
m_get_mailaddr(struct msg *m, struct mailaddr *maddr)
595
{
596
	m_get(m, maddr, sizeof(*maddr));
597
}
598
599
void
600
m_get_envelope(struct msg *m, struct envelope *evp)
601
{
602
	uint64_t	 evpid;
603
	const char	*buf;
604
605
	m_get_evpid(m, &evpid);
606
	m_get_string(m, &buf);
607
608
	if (!envelope_load_buffer(evp, buf, strlen(buf)))
609
		fatalx("failed to retrieve envelope");
610
	evp->id = evpid;
611
}
612
613
void
614
m_get_params(struct msg *m, struct dict *d)
615
{
616
	size_t	c;
617
	const char *key;
618
	const char *value;
619
	char *tmp;
620
621
	dict_init(d);
622
623
	m_get_size(m, &c);
624
625
	for (; c; c--) {
626
		m_get_string(m, &key);
627
		m_get_string(m, &value);
628
		if ((tmp = strdup(value)) == NULL)
629
			fatal("m_get_params");
630
		dict_set(d, key, tmp);
631
	}
632
}
633
634
void
635
m_clear_params(struct dict *d)
636
{
637
	char *value;
638
639
	while (dict_poproot(d, (void **)&value))
640
		free(value);
641
}