GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/smtpd/smtpctl/../envelope.c Lines: 0 451 0.0 %
Date: 2017-11-07 Branches: 0 334 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: envelope.c,v 1.37 2017/08/06 08:35:14 gilles Exp $	*/
2
3
/*
4
 * Copyright (c) 2013 Eric Faurot <eric@openbsd.org>
5
 * Copyright (c) 2011-2013 Gilles Chehade <gilles@poolp.org>
6
 *
7
 * Permission to use, copy, modify, and distribute this software for any
8
 * purpose with or without fee is hereby granted, provided that the above
9
 * copyright notice and this permission notice appear in all copies.
10
 *
11
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18
 */
19
20
#include <sys/types.h>
21
#include <sys/queue.h>
22
#include <sys/tree.h>
23
#include <sys/socket.h>
24
#include <sys/stat.h>
25
26
#include <netinet/in.h>
27
#include <arpa/inet.h>
28
29
#include <ctype.h>
30
#include <err.h>
31
#include <errno.h>
32
#include <event.h>
33
#include <fcntl.h>
34
#include <imsg.h>
35
#include <inttypes.h>
36
#include <libgen.h>
37
#include <pwd.h>
38
#include <limits.h>
39
#include <stdio.h>
40
#include <stdlib.h>
41
#include <string.h>
42
#include <time.h>
43
#include <unistd.h>
44
45
#include "smtpd.h"
46
#include "log.h"
47
48
static int envelope_ascii_load(struct envelope *, struct dict *);
49
static void envelope_ascii_dump(const struct envelope *, char **, size_t *,
50
    const char *);
51
52
void
53
envelope_set_errormsg(struct envelope *e, char *fmt, ...)
54
{
55
	int ret;
56
	va_list ap;
57
58
	va_start(ap, fmt);
59
	ret = vsnprintf(e->errorline, sizeof(e->errorline), fmt, ap);
60
	va_end(ap);
61
62
	/* this should not happen */
63
	if (ret == -1)
64
		err(1, "vsnprintf");
65
66
	if ((size_t)ret >= sizeof(e->errorline))
67
		(void)strlcpy(e->errorline + (sizeof(e->errorline) - 4),
68
		    "...", 4);
69
}
70
71
void
72
envelope_set_esc_class(struct envelope *e, enum enhanced_status_class class)
73
{
74
	e->esc_class = class;
75
}
76
77
void
78
envelope_set_esc_code(struct envelope *e, enum enhanced_status_code code)
79
{
80
	e->esc_code = code;
81
}
82
83
static int
84
envelope_buffer_to_dict(struct dict *d,  const char *ibuf, size_t buflen)
85
{
86
	static char	 lbuf[sizeof(struct envelope)];
87
	size_t		 len;
88
	char		*buf, *field, *nextline;
89
90
	memset(lbuf, 0, sizeof lbuf);
91
	if (strlcpy(lbuf, ibuf, sizeof lbuf) >= sizeof lbuf)
92
		goto err;
93
	buf = lbuf;
94
95
	while (buflen > 0) {
96
		len = strcspn(buf, "\n");
97
		buf[len] = '\0';
98
		nextline = buf + len + 1;
99
		buflen -= (nextline - buf);
100
101
		field = buf;
102
		while (*buf && (isalnum((unsigned char)*buf) || *buf == '-'))
103
			buf++;
104
		if (!*buf)
105
			goto err;
106
107
		/* skip whitespaces before separator */
108
		while (*buf && isspace((unsigned char)*buf))
109
			*buf++ = 0;
110
111
		/* we *want* ':' */
112
		if (*buf != ':')
113
			goto err;
114
		*buf++ = 0;
115
116
		/* skip whitespaces after separator */
117
		while (*buf && isspace((unsigned char)*buf))
118
			*buf++ = 0;
119
		dict_set(d, field, buf);
120
		buf = nextline;
121
	}
122
123
	return (1);
124
125
err:
126
	return (0);
127
}
128
129
int
130
envelope_load_buffer(struct envelope *ep, const char *ibuf, size_t buflen)
131
{
132
	struct dict	 d;
133
	const char	*val, *errstr;
134
	long long	 version;
135
	int		 ret = 0;
136
137
	dict_init(&d);
138
	if (!envelope_buffer_to_dict(&d, ibuf, buflen)) {
139
		log_debug("debug: cannot parse envelope to dict");
140
		goto end;
141
	}
142
143
	val = dict_get(&d, "version");
144
	if (val == NULL) {
145
		log_debug("debug: envelope version not found");
146
		goto end;
147
	}
148
	version = strtonum(val, 1, 64, &errstr);
149
	if (errstr) {
150
		log_debug("debug: cannot parse envelope version: %s", val);
151
		goto end;
152
	}
153
154
	if (version != 2) {
155
		log_debug("debug: bad envelope version %lld", version);
156
		goto end;
157
	}
158
159
	memset(ep, 0, sizeof *ep);
160
	ret = envelope_ascii_load(ep, &d);
161
	if (ret)
162
		ep->version = SMTPD_ENVELOPE_VERSION;
163
end:
164
	while (dict_poproot(&d, NULL))
165
		;
166
	return (ret);
167
}
168
169
int
170
envelope_dump_buffer(const struct envelope *ep, char *dest, size_t len)
171
{
172
	char	*p = dest;
173
174
	envelope_ascii_dump(ep, &dest, &len, "version");
175
	envelope_ascii_dump(ep, &dest, &len, "tag");
176
	envelope_ascii_dump(ep, &dest, &len, "type");
177
	envelope_ascii_dump(ep, &dest, &len, "smtpname");
178
	envelope_ascii_dump(ep, &dest, &len, "helo");
179
	envelope_ascii_dump(ep, &dest, &len, "hostname");
180
	envelope_ascii_dump(ep, &dest, &len, "errorline");
181
	envelope_ascii_dump(ep, &dest, &len, "sockaddr");
182
	envelope_ascii_dump(ep, &dest, &len, "sender");
183
	envelope_ascii_dump(ep, &dest, &len, "rcpt");
184
	envelope_ascii_dump(ep, &dest, &len, "dest");
185
	envelope_ascii_dump(ep, &dest, &len, "ctime");
186
	envelope_ascii_dump(ep, &dest, &len, "last-try");
187
	envelope_ascii_dump(ep, &dest, &len, "last-bounce");
188
	envelope_ascii_dump(ep, &dest, &len, "expire");
189
	envelope_ascii_dump(ep, &dest, &len, "retry");
190
	envelope_ascii_dump(ep, &dest, &len, "flags");
191
	envelope_ascii_dump(ep, &dest, &len, "dsn-notify");
192
	envelope_ascii_dump(ep, &dest, &len, "dsn-ret");
193
	envelope_ascii_dump(ep, &dest, &len, "dsn-envid");
194
	envelope_ascii_dump(ep, &dest, &len, "dsn-orcpt");
195
	envelope_ascii_dump(ep, &dest, &len, "esc-class");
196
	envelope_ascii_dump(ep, &dest, &len, "esc-code");
197
198
	switch (ep->type) {
199
	case D_MDA:
200
		envelope_ascii_dump(ep, &dest, &len, "mda-buffer");
201
		envelope_ascii_dump(ep, &dest, &len, "mda-method");
202
		envelope_ascii_dump(ep, &dest, &len, "mda-user");
203
		envelope_ascii_dump(ep, &dest, &len, "mda-usertable");
204
		envelope_ascii_dump(ep, &dest, &len, "mda-delivery-user");
205
		break;
206
	case D_MTA:
207
		envelope_ascii_dump(ep, &dest, &len, "mta-relay");
208
		envelope_ascii_dump(ep, &dest, &len, "mta-relay-auth");
209
		envelope_ascii_dump(ep, &dest, &len, "mta-relay-cert");
210
		envelope_ascii_dump(ep, &dest, &len, "mta-relay-ca");
211
		envelope_ascii_dump(ep, &dest, &len, "mta-relay-flags");
212
		envelope_ascii_dump(ep, &dest, &len, "mta-relay-heloname");
213
		envelope_ascii_dump(ep, &dest, &len, "mta-relay-helotable");
214
		envelope_ascii_dump(ep, &dest, &len, "mta-relay-source");
215
		break;
216
	case D_BOUNCE:
217
		envelope_ascii_dump(ep, &dest, &len, "bounce-expire");
218
		envelope_ascii_dump(ep, &dest, &len, "bounce-delay");
219
		envelope_ascii_dump(ep, &dest, &len, "bounce-type");
220
		break;
221
	default:
222
		return (0);
223
	}
224
225
	if (dest == NULL)
226
		return (0);
227
228
	return (dest - p);
229
}
230
231
static int
232
ascii_load_uint8(uint8_t *dest, char *buf)
233
{
234
	const char *errstr;
235
236
	*dest = strtonum(buf, 0, 0xff, &errstr);
237
	if (errstr)
238
		return 0;
239
	return 1;
240
}
241
242
static int
243
ascii_load_uint16(uint16_t *dest, char *buf)
244
{
245
	const char *errstr;
246
247
	*dest = strtonum(buf, 0, 0xffff, &errstr);
248
	if (errstr)
249
		return 0;
250
	return 1;
251
}
252
253
static int
254
ascii_load_uint32(uint32_t *dest, char *buf)
255
{
256
	const char *errstr;
257
258
	*dest = strtonum(buf, 0, 0xffffffff, &errstr);
259
	if (errstr)
260
		return 0;
261
	return 1;
262
}
263
264
static int
265
ascii_load_time(time_t *dest, char *buf)
266
{
267
	const char *errstr;
268
269
	*dest = strtonum(buf, 0, LLONG_MAX, &errstr);
270
	if (errstr)
271
		return 0;
272
	return 1;
273
}
274
275
static int
276
ascii_load_type(enum delivery_type *dest, char *buf)
277
{
278
	if (strcasecmp(buf, "mda") == 0)
279
		*dest = D_MDA;
280
	else if (strcasecmp(buf, "mta") == 0)
281
		*dest = D_MTA;
282
	else if (strcasecmp(buf, "bounce") == 0)
283
		*dest = D_BOUNCE;
284
	else
285
		return 0;
286
	return 1;
287
}
288
289
static int
290
ascii_load_string(char *dest, char *buf, size_t len)
291
{
292
	if (strlcpy(dest, buf, len) >= len)
293
		return 0;
294
	return 1;
295
}
296
297
static int
298
ascii_load_sockaddr(struct sockaddr_storage *ss, char *buf)
299
{
300
	struct sockaddr_in6 ssin6;
301
	struct sockaddr_in  ssin;
302
303
	memset(&ssin, 0, sizeof ssin);
304
	memset(&ssin6, 0, sizeof ssin6);
305
306
	if (!strcmp("local", buf)) {
307
		ss->ss_family = AF_LOCAL;
308
	}
309
	else if (strncasecmp("IPv6:", buf, 5) == 0) {
310
		if (inet_pton(AF_INET6, buf + 5, &ssin6.sin6_addr) != 1)
311
			return 0;
312
		ssin6.sin6_family = AF_INET6;
313
		memcpy(ss, &ssin6, sizeof(ssin6));
314
		ss->ss_len = sizeof(struct sockaddr_in6);
315
	}
316
	else {
317
		if (inet_pton(AF_INET, buf, &ssin.sin_addr) != 1)
318
			return 0;
319
		ssin.sin_family = AF_INET;
320
		memcpy(ss, &ssin, sizeof(ssin));
321
		ss->ss_len = sizeof(struct sockaddr_in);
322
	}
323
	return 1;
324
}
325
326
static int
327
ascii_load_mda_method(enum action_type *dest, char *buf)
328
{
329
	if (strcasecmp(buf, "mbox") == 0)
330
		*dest = A_MBOX;
331
	else if (strcasecmp(buf, "maildir") == 0)
332
		*dest = A_MAILDIR;
333
	else if (strcasecmp(buf, "filename") == 0)
334
		*dest = A_FILENAME;
335
	else if (strcasecmp(buf, "mda") == 0)
336
		*dest = A_MDA;
337
	else if (strcasecmp(buf, "lmtp") == 0)
338
		*dest = A_LMTP;
339
	else
340
		return 0;
341
	return 1;
342
}
343
344
static int
345
ascii_load_mailaddr(struct mailaddr *dest, char *buf)
346
{
347
	if (!text_to_mailaddr(dest, buf))
348
		return 0;
349
	return 1;
350
}
351
352
static int
353
ascii_load_flags(enum envelope_flags *dest, char *buf)
354
{
355
	char *flag;
356
357
	while ((flag = strsep(&buf, " ,|")) != NULL) {
358
		if (strcasecmp(flag, "authenticated") == 0)
359
			*dest |= EF_AUTHENTICATED;
360
		else if (strcasecmp(flag, "enqueued") == 0)
361
			;
362
		else if (strcasecmp(flag, "bounce") == 0)
363
			*dest |= EF_BOUNCE;
364
		else if (strcasecmp(flag, "internal") == 0)
365
			*dest |= EF_INTERNAL;
366
		else
367
			return 0;
368
	}
369
	return 1;
370
}
371
372
static int
373
ascii_load_mta_relay_url(struct relayhost *relay, char *buf)
374
{
375
	if (!text_to_relayhost(relay, buf))
376
		return 0;
377
	return 1;
378
}
379
380
static int
381
ascii_load_mta_relay_flags(uint16_t *dest, char *buf)
382
{
383
	char *flag;
384
385
	while ((flag = strsep(&buf, " ,|")) != NULL) {
386
		if (strcasecmp(flag, "verify") == 0)
387
			*dest |= F_TLS_VERIFY;
388
		else if (strcasecmp(flag, "tls") == 0)
389
			*dest |= F_STARTTLS;
390
		else
391
			return 0;
392
	}
393
394
	return 1;
395
}
396
397
static int
398
ascii_load_bounce_type(enum bounce_type *dest, char *buf)
399
{
400
	if (strcasecmp(buf, "error") == 0)
401
		*dest = B_ERROR;
402
	else if (strcasecmp(buf, "warn") == 0)
403
		*dest = B_WARNING;
404
	else if (strcasecmp(buf, "dsn") == 0)
405
		*dest = B_DSN;
406
	else
407
		return 0;
408
	return 1;
409
}
410
411
static int
412
ascii_load_dsn_ret(enum dsn_ret *ret, char *buf)
413
{
414
	if (strcasecmp(buf, "HDRS") == 0)
415
		*ret = DSN_RETHDRS;
416
	else if (strcasecmp(buf, "FULL") == 0)
417
		*ret = DSN_RETFULL;
418
	else
419
		return 0;
420
	return 1;
421
}
422
423
static int
424
ascii_load_field(const char *field, struct envelope *ep, char *buf)
425
{
426
	if (strcasecmp("bounce-delay", field) == 0)
427
		return ascii_load_time(&ep->agent.bounce.delay, buf);
428
429
	if (strcasecmp("bounce-expire", field) == 0)
430
		return ascii_load_time(&ep->agent.bounce.expire, buf);
431
432
	if (strcasecmp("bounce-type", field) == 0)
433
		return ascii_load_bounce_type(&ep->agent.bounce.type, buf);
434
435
	if (strcasecmp("ctime", field) == 0)
436
		return ascii_load_time(&ep->creation, buf);
437
438
	if (strcasecmp("dest", field) == 0)
439
		return ascii_load_mailaddr(&ep->dest, buf);
440
441
	if (strcasecmp("errorline", field) == 0)
442
		return ascii_load_string(ep->errorline, buf,
443
		    sizeof ep->errorline);
444
445
	if (strcasecmp("expire", field) == 0)
446
		return ascii_load_time(&ep->expire, buf);
447
448
	if (strcasecmp("flags", field) == 0)
449
		return ascii_load_flags(&ep->flags, buf);
450
451
	if (strcasecmp("helo", field) == 0)
452
		return ascii_load_string(ep->helo, buf, sizeof ep->helo);
453
454
	if (strcasecmp("hostname", field) == 0)
455
		return ascii_load_string(ep->hostname, buf,
456
		    sizeof ep->hostname);
457
458
	if (strcasecmp("last-bounce", field) == 0)
459
		return ascii_load_time(&ep->lastbounce, buf);
460
461
	if (strcasecmp("last-try", field) == 0)
462
		return ascii_load_time(&ep->lasttry, buf);
463
464
	if (strcasecmp("mda-buffer", field) == 0)
465
		return ascii_load_string(ep->agent.mda.buffer, buf,
466
		    sizeof ep->agent.mda.buffer);
467
468
	if (strcasecmp("mda-method", field) == 0)
469
		return ascii_load_mda_method(&ep->agent.mda.method, buf);
470
471
	if (strcasecmp("mda-user", field) == 0)
472
		return ascii_load_string(ep->agent.mda.username, buf,
473
		    sizeof ep->agent.mda.username);
474
475
	if (strcasecmp("mda-usertable", field) == 0)
476
		return ascii_load_string(ep->agent.mda.usertable, buf,
477
		    sizeof ep->agent.mda.usertable);
478
479
	if (strcasecmp("mda-delivery-user", field) == 0)
480
		return ascii_load_string(ep->agent.mda.delivery_user, buf,
481
		    sizeof ep->agent.mda.delivery_user);
482
483
	if (strcasecmp("mta-relay", field) == 0) {
484
		int ret;
485
		uint16_t flags = ep->agent.mta.relay.flags;
486
		ret = ascii_load_mta_relay_url(&ep->agent.mta.relay, buf);
487
		if (!ret)
488
			return (0);
489
		ep->agent.mta.relay.flags |= flags;
490
		return ret;
491
	}
492
493
	if (strcasecmp("mta-relay-auth", field) == 0)
494
		return ascii_load_string(ep->agent.mta.relay.authtable, buf,
495
		    sizeof ep->agent.mta.relay.authtable);
496
497
	if (strcasecmp("mta-relay-cert", field) == 0)
498
		return ascii_load_string(ep->agent.mta.relay.pki_name, buf,
499
		    sizeof ep->agent.mta.relay.pki_name);
500
501
	if (strcasecmp("mta-relay-ca", field) == 0)
502
		return ascii_load_string(ep->agent.mta.relay.ca_name, buf,
503
		    sizeof ep->agent.mta.relay.ca_name);
504
505
	if (strcasecmp("mta-relay-flags", field) == 0)
506
		return ascii_load_mta_relay_flags(&ep->agent.mta.relay.flags,
507
		    buf);
508
509
	if (strcasecmp("mta-relay-heloname", field) == 0)
510
		return ascii_load_string(ep->agent.mta.relay.heloname, buf,
511
		    sizeof ep->agent.mta.relay.heloname);
512
513
	if (strcasecmp("mta-relay-helotable", field) == 0)
514
		return ascii_load_string(ep->agent.mta.relay.helotable, buf,
515
		    sizeof ep->agent.mta.relay.helotable);
516
517
	if (strcasecmp("mta-relay-source", field) == 0)
518
		return ascii_load_string(ep->agent.mta.relay.sourcetable, buf,
519
		    sizeof ep->agent.mta.relay.sourcetable);
520
521
	if (strcasecmp("retry", field) == 0)
522
		return ascii_load_uint16(&ep->retry, buf);
523
524
	if (strcasecmp("rcpt", field) == 0)
525
		return ascii_load_mailaddr(&ep->rcpt, buf);
526
527
	if (strcasecmp("sender", field) == 0)
528
		return ascii_load_mailaddr(&ep->sender, buf);
529
530
	if (strcasecmp("smtpname", field) == 0)
531
		return ascii_load_string(ep->smtpname, buf,
532
		    sizeof(ep->smtpname));
533
534
	if (strcasecmp("sockaddr", field) == 0)
535
		return ascii_load_sockaddr(&ep->ss, buf);
536
537
	if (strcasecmp("tag", field) == 0)
538
		return ascii_load_string(ep->tag, buf, sizeof ep->tag);
539
540
	if (strcasecmp("type", field) == 0)
541
		return ascii_load_type(&ep->type, buf);
542
543
	if (strcasecmp("version", field) == 0)
544
		return ascii_load_uint32(&ep->version, buf);
545
546
	if (strcasecmp("dsn-notify", field) == 0)
547
		return ascii_load_uint8(&ep->dsn_notify, buf);
548
549
	if (strcasecmp("dsn-orcpt", field) == 0)
550
		return ascii_load_mailaddr(&ep->dsn_orcpt, buf);
551
552
	if (strcasecmp("dsn-ret", field) == 0)
553
		return ascii_load_dsn_ret(&ep->dsn_ret, buf);
554
555
	if (strcasecmp("dsn-envid", field) == 0)
556
		return ascii_load_string(ep->dsn_envid, buf,
557
		    sizeof(ep->dsn_envid));
558
559
	if (strcasecmp("esc-class", field) == 0)
560
		return ascii_load_uint8(&ep->esc_class, buf);
561
562
	if (strcasecmp("esc-code", field) == 0)
563
		return ascii_load_uint8(&ep->esc_code, buf);
564
565
	return (0);
566
}
567
568
static int
569
envelope_ascii_load(struct envelope *ep, struct dict *d)
570
{
571
	const char	       *field;
572
	char		       *value;
573
	void		       *hdl;
574
575
	hdl = NULL;
576
	while (dict_iter(d, &hdl, &field, (void **)&value))
577
		if (!ascii_load_field(field, ep, value))
578
			goto err;
579
580
	return (1);
581
582
err:
583
	log_warnx("envelope: invalid field \"%s\"", field);
584
	return (0);
585
}
586
587
588
static int
589
ascii_dump_uint8(uint8_t src, char *dest, size_t len)
590
{
591
	return bsnprintf(dest, len, "%d", src);
592
}
593
594
static int
595
ascii_dump_uint16(uint16_t src, char *dest, size_t len)
596
{
597
	return bsnprintf(dest, len, "%d", src);
598
}
599
600
static int
601
ascii_dump_uint32(uint32_t src, char *dest, size_t len)
602
{
603
	return bsnprintf(dest, len, "%d", src);
604
}
605
606
static int
607
ascii_dump_time(time_t src, char *dest, size_t len)
608
{
609
	return bsnprintf(dest, len, "%lld", (long long) src);
610
}
611
612
static int
613
ascii_dump_string(const char *src, char *dest, size_t len)
614
{
615
	return bsnprintf(dest, len, "%s", src);
616
}
617
618
static int
619
ascii_dump_type(enum delivery_type type, char *dest, size_t len)
620
{
621
	char *p = NULL;
622
623
	switch (type) {
624
	case D_MDA:
625
		p = "mda";
626
		break;
627
	case D_MTA:
628
		p = "mta";
629
		break;
630
	case D_BOUNCE:
631
		p = "bounce";
632
		break;
633
	default:
634
		return 0;
635
	}
636
637
	return bsnprintf(dest, len, "%s", p);
638
}
639
640
static int
641
ascii_dump_mda_method(enum action_type type, char *dest, size_t len)
642
{
643
	char *p = NULL;
644
645
	switch (type) {
646
	case A_LMTP:
647
		p = "lmtp";
648
		break;
649
	case A_MAILDIR:
650
		p = "maildir";
651
		break;
652
	case A_MBOX:
653
		p = "mbox";
654
		break;
655
	case A_FILENAME:
656
		p = "filename";
657
		break;
658
	case A_MDA:
659
		p = "mda";
660
		break;
661
	default:
662
		return 0;
663
	}
664
	return bsnprintf(dest, len, "%s", p);
665
}
666
667
static int
668
ascii_dump_mailaddr(const struct mailaddr *addr, char *dest, size_t len)
669
{
670
	return bsnprintf(dest, len, "%s@%s",
671
	    addr->user, addr->domain);
672
}
673
674
static int
675
ascii_dump_flags(enum envelope_flags flags, char *buf, size_t len)
676
{
677
	size_t cpylen = 0;
678
679
	buf[0] = '\0';
680
	if (flags) {
681
		if (flags & EF_AUTHENTICATED)
682
			cpylen = strlcat(buf, "authenticated", len);
683
		if (flags & EF_BOUNCE) {
684
			if (buf[0] != '\0')
685
				(void)strlcat(buf, " ", len);
686
			cpylen = strlcat(buf, "bounce", len);
687
		}
688
		if (flags & EF_INTERNAL) {
689
			if (buf[0] != '\0')
690
				(void)strlcat(buf, " ", len);
691
			cpylen = strlcat(buf, "internal", len);
692
		}
693
	}
694
695
	return cpylen < len ? 1 : 0;
696
}
697
698
static int
699
ascii_dump_mta_relay_url(const struct relayhost *relay, char *buf, size_t len)
700
{
701
	return bsnprintf(buf, len, "%s", relayhost_to_text(relay));
702
}
703
704
static int
705
ascii_dump_mta_relay_flags(uint16_t flags, char *buf, size_t len)
706
{
707
	size_t cpylen = 0;
708
709
	buf[0] = '\0';
710
	if (flags) {
711
		if (flags & F_TLS_VERIFY) {
712
			if (buf[0] != '\0')
713
				(void)strlcat(buf, " ", len);
714
			cpylen = strlcat(buf, "verify", len);
715
		}
716
		if (flags & F_STARTTLS) {
717
			if (buf[0] != '\0')
718
				(void)strlcat(buf, " ", len);
719
			cpylen = strlcat(buf, "tls", len);
720
		}
721
	}
722
723
	return cpylen < len ? 1 : 0;
724
}
725
726
static int
727
ascii_dump_bounce_type(enum bounce_type type, char *dest, size_t len)
728
{
729
	char *p = NULL;
730
731
	switch (type) {
732
	case B_ERROR:
733
		p = "error";
734
		break;
735
	case B_WARNING:
736
		p = "warn";
737
		break;
738
	case B_DSN:
739
		p = "dsn";
740
		break;
741
	default:
742
		return 0;
743
	}
744
	return bsnprintf(dest, len, "%s", p);
745
}
746
747
748
static int
749
ascii_dump_dsn_ret(enum dsn_ret flag, char *dest, size_t len)
750
{
751
	size_t cpylen = 0;
752
753
	dest[0] = '\0';
754
	if (flag == DSN_RETFULL)
755
		cpylen = strlcat(dest, "FULL", len);
756
	else if (flag == DSN_RETHDRS)
757
		cpylen = strlcat(dest, "HDRS", len);
758
759
	return cpylen < len ? 1 : 0;
760
}
761
762
static int
763
ascii_dump_field(const char *field, const struct envelope *ep,
764
    char *buf, size_t len)
765
{
766
	if (strcasecmp(field, "bounce-delay") == 0) {
767
		if (ep->agent.bounce.type != B_WARNING)
768
			return (1);
769
		return ascii_dump_time(ep->agent.bounce.delay, buf, len);
770
	}
771
772
	if (strcasecmp(field, "bounce-expire") == 0) {
773
		if (ep->agent.bounce.type != B_WARNING)
774
			return (1);
775
		return ascii_dump_time(ep->agent.bounce.expire, buf, len);
776
	}
777
778
	if (strcasecmp(field, "bounce-type") == 0)
779
		return ascii_dump_bounce_type(ep->agent.bounce.type, buf, len);
780
781
	if (strcasecmp(field, "ctime") == 0)
782
		return ascii_dump_time(ep->creation, buf, len);
783
784
	if (strcasecmp(field, "dest") == 0)
785
		return ascii_dump_mailaddr(&ep->dest, buf, len);
786
787
	if (strcasecmp(field, "errorline") == 0)
788
		return ascii_dump_string(ep->errorline, buf, len);
789
790
	if (strcasecmp(field, "expire") == 0)
791
		return ascii_dump_time(ep->expire, buf, len);
792
793
	if (strcasecmp(field, "flags") == 0)
794
		return ascii_dump_flags(ep->flags, buf, len);
795
796
	if (strcasecmp(field, "helo") == 0)
797
		return ascii_dump_string(ep->helo, buf, len);
798
799
	if (strcasecmp(field, "hostname") == 0)
800
		return ascii_dump_string(ep->hostname, buf, len);
801
802
	if (strcasecmp(field, "last-bounce") == 0)
803
		return ascii_dump_time(ep->lastbounce, buf, len);
804
805
	if (strcasecmp(field, "last-try") == 0)
806
		return ascii_dump_time(ep->lasttry, buf, len);
807
808
	if (strcasecmp(field, "mda-buffer") == 0)
809
		return ascii_dump_string(ep->agent.mda.buffer, buf, len);
810
811
	if (strcasecmp(field, "mda-method") == 0)
812
		return ascii_dump_mda_method(ep->agent.mda.method, buf, len);
813
814
	if (strcasecmp(field, "mda-user") == 0)
815
		return ascii_dump_string(ep->agent.mda.username, buf, len);
816
817
	if (strcasecmp(field, "mda-delivery-user") == 0)
818
		return ascii_dump_string(ep->agent.mda.delivery_user, buf, len);
819
820
	if (strcasecmp(field, "mda-usertable") == 0)
821
		return ascii_dump_string(ep->agent.mda.usertable, buf, len);
822
823
	if (strcasecmp(field, "mta-relay") == 0) {
824
		if (ep->agent.mta.relay.hostname[0])
825
			return ascii_dump_mta_relay_url(&ep->agent.mta.relay,
826
			    buf, len);
827
		return (1);
828
	}
829
830
	if (strcasecmp(field, "mta-relay-auth") == 0)
831
		return ascii_dump_string(ep->agent.mta.relay.authtable,
832
		    buf, len);
833
834
	if (strcasecmp(field, "mta-relay-cert") == 0)
835
		return ascii_dump_string(ep->agent.mta.relay.pki_name,
836
		    buf, len);
837
838
	if (strcasecmp(field, "mta-relay-ca") == 0)
839
		return ascii_dump_string(ep->agent.mta.relay.ca_name,
840
		    buf, len);
841
842
	if (strcasecmp(field, "mta-relay-flags") == 0)
843
		return ascii_dump_mta_relay_flags(ep->agent.mta.relay.flags,
844
		    buf, len);
845
846
	if (strcasecmp(field, "mta-relay-heloname") == 0)
847
		return ascii_dump_string(ep->agent.mta.relay.heloname,
848
		    buf, len);
849
850
	if (strcasecmp(field, "mta-relay-helotable") == 0)
851
		return ascii_dump_string(ep->agent.mta.relay.helotable,
852
		    buf, len);
853
854
	if (strcasecmp(field, "mta-relay-source") == 0)
855
		return ascii_dump_string(ep->agent.mta.relay.sourcetable,
856
		    buf, len);
857
858
	if (strcasecmp(field, "retry") == 0)
859
		return ascii_dump_uint16(ep->retry, buf, len);
860
861
	if (strcasecmp(field, "rcpt") == 0)
862
		return ascii_dump_mailaddr(&ep->rcpt, buf, len);
863
864
	if (strcasecmp(field, "sender") == 0)
865
		return ascii_dump_mailaddr(&ep->sender, buf, len);
866
867
	if (strcasecmp(field, "smtpname") == 0)
868
		return ascii_dump_string(ep->smtpname, buf, len);
869
870
	if (strcasecmp(field, "sockaddr") == 0)
871
		return ascii_dump_string(ss_to_text(&ep->ss), buf, len);
872
873
	if (strcasecmp(field, "tag") == 0)
874
		return ascii_dump_string(ep->tag, buf, len);
875
876
	if (strcasecmp(field, "type") == 0)
877
		return ascii_dump_type(ep->type, buf, len);
878
879
	if (strcasecmp(field, "version") == 0)
880
		return ascii_dump_uint32(SMTPD_ENVELOPE_VERSION, buf, len);
881
882
	if (strcasecmp(field, "dsn-notify") == 0)
883
		return ascii_dump_uint8(ep->dsn_notify, buf, len);
884
885
	if (strcasecmp(field, "dsn-ret") == 0)
886
		return ascii_dump_dsn_ret(ep->dsn_ret, buf, len);
887
888
	if (strcasecmp(field, "dsn-orcpt") == 0) {
889
		if (ep->dsn_orcpt.user[0] && ep->dsn_orcpt.domain[0])
890
			return ascii_dump_mailaddr(&ep->dsn_orcpt, buf, len);
891
		return 1;
892
	}
893
894
	if (strcasecmp(field, "dsn-envid") == 0)
895
		return ascii_dump_string(ep->dsn_envid, buf, len);
896
897
	if (strcasecmp(field, "esc-class") == 0) {
898
		if (ep->esc_class)
899
			return ascii_dump_uint8(ep->esc_class, buf, len);
900
		return 1;
901
	}
902
903
	if (strcasecmp(field, "esc-code") == 0) {
904
		/* this is not a pasto, we dump esc_code if esc_class is !0 */
905
		if (ep->esc_class)
906
			return ascii_dump_uint8(ep->esc_code, buf, len);
907
		return 1;
908
	}
909
910
	return (0);
911
}
912
913
static void
914
envelope_ascii_dump(const struct envelope *ep, char **dest, size_t *len,
915
    const char *field)
916
{
917
	char	buf[8192];
918
	int	l;
919
920
	if (*dest == NULL)
921
		return;
922
923
	memset(buf, 0, sizeof buf);
924
	if (!ascii_dump_field(field, ep, buf, sizeof buf))
925
		goto err;
926
	if (buf[0] == '\0')
927
		return;
928
929
	l = snprintf(*dest, *len, "%s: %s\n", field, buf);
930
	if (l == -1 || (size_t) l >= *len)
931
		goto err;
932
	*dest += l;
933
	*len -= l;
934
935
	return;
936
err:
937
	*dest = NULL;
938
}