xref: /openbsd-src/usr.sbin/smtpd/envelope.c (revision fc405d53b73a2d73393cb97f684863d17b583e38)
1 /*	$OpenBSD: envelope.c,v 1.51 2023/02/06 18:35:52 semarie 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 <arpa/inet.h>
21 
22 #include <ctype.h>
23 #include <stdlib.h>
24 #include <string.h>
25 
26 #include "smtpd.h"
27 #include "log.h"
28 
29 static int envelope_ascii_load(struct envelope *, struct dict *);
30 static void envelope_ascii_dump(const struct envelope *, char **, size_t *,
31     const char *);
32 
33 void
34 envelope_set_errormsg(struct envelope *e, char *fmt, ...)
35 {
36 	int ret;
37 	va_list ap;
38 
39 	va_start(ap, fmt);
40 	ret = vsnprintf(e->errorline, sizeof(e->errorline), fmt, ap);
41 	va_end(ap);
42 
43 	/* this should not happen */
44 	if (ret < 0)
45 		fatal("vsnprintf");
46 
47 	if ((size_t)ret >= sizeof(e->errorline))
48 		(void)strlcpy(e->errorline + (sizeof(e->errorline) - 4),
49 		    "...", 4);
50 }
51 
52 void
53 envelope_set_esc_class(struct envelope *e, enum enhanced_status_class class)
54 {
55 	e->esc_class = class;
56 }
57 
58 void
59 envelope_set_esc_code(struct envelope *e, enum enhanced_status_code code)
60 {
61 	e->esc_code = code;
62 }
63 
64 static int
65 envelope_buffer_to_dict(struct dict *d,  const char *ibuf, size_t buflen)
66 {
67 	static char	 lbuf[sizeof(struct envelope)];
68 	size_t		 len;
69 	char		*buf, *field, *nextline;
70 
71 	memset(lbuf, 0, sizeof lbuf);
72 	if (strlcpy(lbuf, ibuf, sizeof lbuf) >= sizeof lbuf)
73 		goto err;
74 	buf = lbuf;
75 
76 	while (buflen > 0) {
77 		len = strcspn(buf, "\n");
78 		buf[len] = '\0';
79 		nextline = buf + len + 1;
80 		buflen -= (nextline - buf);
81 
82 		field = buf;
83 		while (*buf && (isalnum((unsigned char)*buf) || *buf == '-'))
84 			buf++;
85 		if (!*buf)
86 			goto err;
87 
88 		/* skip whitespaces before separator */
89 		while (*buf && isspace((unsigned char)*buf))
90 			*buf++ = 0;
91 
92 		/* we *want* ':' */
93 		if (*buf != ':')
94 			goto err;
95 		*buf++ = 0;
96 
97 		/* skip whitespaces after separator */
98 		while (*buf && isspace((unsigned char)*buf))
99 			*buf++ = 0;
100 		dict_set(d, field, buf);
101 		buf = nextline;
102 	}
103 
104 	return (1);
105 
106 err:
107 	return (0);
108 }
109 
110 int
111 envelope_load_buffer(struct envelope *ep, const char *ibuf, size_t buflen)
112 {
113 	struct dict	 d;
114 	const char	*val, *errstr;
115 	long long	 version;
116 	int		 ret = 0;
117 
118 	dict_init(&d);
119 	if (!envelope_buffer_to_dict(&d, ibuf, buflen)) {
120 		log_debug("debug: cannot parse envelope to dict");
121 		goto end;
122 	}
123 
124 	val = dict_get(&d, "version");
125 	if (val == NULL) {
126 		log_debug("debug: envelope version not found");
127 		goto end;
128 	}
129 	version = strtonum(val, 1, 64, &errstr);
130 	if (errstr) {
131 		log_debug("debug: cannot parse envelope version: %s", val);
132 		goto end;
133 	}
134 
135 	if (version != SMTPD_ENVELOPE_VERSION) {
136 		log_debug("debug: bad envelope version %lld", version);
137 		goto end;
138 	}
139 
140 	memset(ep, 0, sizeof *ep);
141 	ret = envelope_ascii_load(ep, &d);
142 	if (ret)
143 		ep->version = SMTPD_ENVELOPE_VERSION;
144 end:
145 	while (dict_poproot(&d, NULL))
146 		;
147 	return (ret);
148 }
149 
150 int
151 envelope_dump_buffer(const struct envelope *ep, char *dest, size_t len)
152 {
153 	char	*p = dest;
154 
155 	envelope_ascii_dump(ep, &dest, &len, "version");
156 	envelope_ascii_dump(ep, &dest, &len, "dispatcher");
157 	envelope_ascii_dump(ep, &dest, &len, "tag");
158 	envelope_ascii_dump(ep, &dest, &len, "type");
159 	envelope_ascii_dump(ep, &dest, &len, "smtpname");
160 	envelope_ascii_dump(ep, &dest, &len, "helo");
161 	envelope_ascii_dump(ep, &dest, &len, "hostname");
162 	envelope_ascii_dump(ep, &dest, &len, "username");
163 	envelope_ascii_dump(ep, &dest, &len, "errorline");
164 	envelope_ascii_dump(ep, &dest, &len, "sockaddr");
165 	envelope_ascii_dump(ep, &dest, &len, "sender");
166 	envelope_ascii_dump(ep, &dest, &len, "rcpt");
167 	envelope_ascii_dump(ep, &dest, &len, "dest");
168 	envelope_ascii_dump(ep, &dest, &len, "ctime");
169 	envelope_ascii_dump(ep, &dest, &len, "last-try");
170 	envelope_ascii_dump(ep, &dest, &len, "last-bounce");
171 	envelope_ascii_dump(ep, &dest, &len, "ttl");
172 	envelope_ascii_dump(ep, &dest, &len, "retry");
173 	envelope_ascii_dump(ep, &dest, &len, "flags");
174 	envelope_ascii_dump(ep, &dest, &len, "dsn-notify");
175 	envelope_ascii_dump(ep, &dest, &len, "dsn-ret");
176 	envelope_ascii_dump(ep, &dest, &len, "dsn-envid");
177 	envelope_ascii_dump(ep, &dest, &len, "dsn-orcpt");
178 	envelope_ascii_dump(ep, &dest, &len, "esc-class");
179 	envelope_ascii_dump(ep, &dest, &len, "esc-code");
180 
181 	switch (ep->type) {
182 	case D_MDA:
183 		envelope_ascii_dump(ep, &dest, &len, "mda-exec");
184 		envelope_ascii_dump(ep, &dest, &len, "mda-subaddress");
185 		envelope_ascii_dump(ep, &dest, &len, "mda-user");
186 		break;
187 	case D_MTA:
188 		break;
189 	case D_BOUNCE:
190 		envelope_ascii_dump(ep, &dest, &len, "bounce-ttl");
191 		envelope_ascii_dump(ep, &dest, &len, "bounce-delay");
192 		envelope_ascii_dump(ep, &dest, &len, "bounce-type");
193 		break;
194 	default:
195 		return (0);
196 	}
197 
198 	if (dest == NULL)
199 		return (0);
200 
201 	return (dest - p);
202 }
203 
204 static int
205 ascii_load_uint8(uint8_t *dest, char *buf)
206 {
207 	const char *errstr;
208 
209 	*dest = strtonum(buf, 0, 0xff, &errstr);
210 	if (errstr)
211 		return 0;
212 	return 1;
213 }
214 
215 static int
216 ascii_load_uint16(uint16_t *dest, char *buf)
217 {
218 	const char *errstr;
219 
220 	*dest = strtonum(buf, 0, 0xffff, &errstr);
221 	if (errstr)
222 		return 0;
223 	return 1;
224 }
225 
226 static int
227 ascii_load_uint32(uint32_t *dest, char *buf)
228 {
229 	const char *errstr;
230 
231 	*dest = strtonum(buf, 0, 0xffffffff, &errstr);
232 	if (errstr)
233 		return 0;
234 	return 1;
235 }
236 
237 static int
238 ascii_load_time(time_t *dest, char *buf)
239 {
240 	const char *errstr;
241 
242 	*dest = strtonum(buf, 0, LLONG_MAX, &errstr);
243 	if (errstr)
244 		return 0;
245 	return 1;
246 }
247 
248 static int
249 ascii_load_type(enum delivery_type *dest, char *buf)
250 {
251 	if (strcasecmp(buf, "mda") == 0)
252 		*dest = D_MDA;
253 	else if (strcasecmp(buf, "mta") == 0)
254 		*dest = D_MTA;
255 	else if (strcasecmp(buf, "bounce") == 0)
256 		*dest = D_BOUNCE;
257 	else
258 		return 0;
259 	return 1;
260 }
261 
262 static int
263 ascii_load_string(char *dest, char *buf, size_t len)
264 {
265 	if (strlcpy(dest, buf, len) >= len)
266 		return 0;
267 	return 1;
268 }
269 
270 static int
271 ascii_load_sockaddr(struct sockaddr_storage *ss, char *buf)
272 {
273 	if (!strcmp("local", buf)) {
274 		ss->ss_family = AF_LOCAL;
275 	}
276 	else if (buf[0] == '[' && buf[strlen(buf)-1] == ']') {
277 		struct addrinfo hints, *res0;
278 
279 		buf[strlen(buf)-1] = '\0';
280 
281 		/* getaddrinfo() is used to support scoped addresses. */
282 		memset(&hints, 0, sizeof(hints));
283 		hints.ai_family = AF_INET6;
284 		hints.ai_flags = AI_NUMERICHOST;
285 		if (getaddrinfo(buf+1, NULL, &hints, &res0) != 0)
286 			return 0;
287 		memcpy(ss, res0->ai_addr, res0->ai_addrlen);
288 		ss->ss_len = res0->ai_addrlen;
289 		freeaddrinfo(res0);
290 	}
291 	else {
292 		struct sockaddr_in ssin;
293 
294 		memset(&ssin, 0, sizeof ssin);
295 		if (inet_pton(AF_INET, buf, &ssin.sin_addr) != 1)
296 			return 0;
297 		ssin.sin_family = AF_INET;
298 		memcpy(ss, &ssin, sizeof(ssin));
299 		ss->ss_len = sizeof(struct sockaddr_in);
300 	}
301 	return 1;
302 }
303 
304 static int
305 ascii_load_mailaddr(struct mailaddr *dest, char *buf)
306 {
307 	if (!text_to_mailaddr(dest, buf))
308 		return 0;
309 	return 1;
310 }
311 
312 static int
313 ascii_load_flags(enum envelope_flags *dest, char *buf)
314 {
315 	char *flag;
316 
317 	while ((flag = strsep(&buf, " ,|")) != NULL) {
318 		if (strcasecmp(flag, "authenticated") == 0)
319 			*dest |= EF_AUTHENTICATED;
320 		else if (strcasecmp(flag, "enqueued") == 0)
321 			;
322 		else if (strcasecmp(flag, "bounce") == 0)
323 			*dest |= EF_BOUNCE;
324 		else if (strcasecmp(flag, "internal") == 0)
325 			*dest |= EF_INTERNAL;
326 		else
327 			return 0;
328 	}
329 	return 1;
330 }
331 
332 static int
333 ascii_load_bounce_type(enum bounce_type *dest, char *buf)
334 {
335 	if (strcasecmp(buf, "error") == 0 || strcasecmp(buf, "failed") == 0)
336 		*dest = B_FAILED;
337 	else if (strcasecmp(buf, "warn") == 0 ||
338 	    strcasecmp(buf, "delayed") == 0)
339 		*dest = B_DELAYED;
340 	else if (strcasecmp(buf, "dsn") == 0 ||
341 	    strcasecmp(buf, "delivered") == 0)
342 		*dest = B_DELIVERED;
343 	else
344 		return 0;
345 	return 1;
346 }
347 
348 static int
349 ascii_load_dsn_ret(enum dsn_ret *ret, char *buf)
350 {
351 	if (strcasecmp(buf, "HDRS") == 0)
352 		*ret = DSN_RETHDRS;
353 	else if (strcasecmp(buf, "FULL") == 0)
354 		*ret = DSN_RETFULL;
355 	else
356 		return 0;
357 	return 1;
358 }
359 
360 static int
361 ascii_load_field(const char *field, struct envelope *ep, char *buf)
362 {
363 	if (strcasecmp("dispatcher", field) == 0)
364 		return ascii_load_string(ep->dispatcher, buf,
365 		    sizeof ep->dispatcher);
366 
367 	if (strcasecmp("bounce-delay", field) == 0)
368 		return ascii_load_time(&ep->agent.bounce.delay, buf);
369 
370 	if (strcasecmp("bounce-ttl", field) == 0)
371 		return ascii_load_time(&ep->agent.bounce.ttl, buf);
372 
373 	if (strcasecmp("bounce-type", field) == 0)
374 		return ascii_load_bounce_type(&ep->agent.bounce.type, buf);
375 
376 	if (strcasecmp("ctime", field) == 0)
377 		return ascii_load_time(&ep->creation, buf);
378 
379 	if (strcasecmp("dest", field) == 0)
380 		return ascii_load_mailaddr(&ep->dest, buf);
381 
382 	if (strcasecmp("username", field) == 0)
383 		return ascii_load_string(ep->username, buf, sizeof(ep->username));
384 
385 	if (strcasecmp("errorline", field) == 0)
386 		return ascii_load_string(ep->errorline, buf,
387 		    sizeof ep->errorline);
388 
389 	if (strcasecmp("ttl", field) == 0)
390 		return ascii_load_time(&ep->ttl, buf);
391 
392 	if (strcasecmp("flags", field) == 0)
393 		return ascii_load_flags(&ep->flags, buf);
394 
395 	if (strcasecmp("helo", field) == 0)
396 		return ascii_load_string(ep->helo, buf, sizeof ep->helo);
397 
398 	if (strcasecmp("hostname", field) == 0)
399 		return ascii_load_string(ep->hostname, buf,
400 		    sizeof ep->hostname);
401 
402 	if (strcasecmp("last-bounce", field) == 0)
403 		return ascii_load_time(&ep->lastbounce, buf);
404 
405 	if (strcasecmp("last-try", field) == 0)
406 		return ascii_load_time(&ep->lasttry, buf);
407 
408 	if (strcasecmp("retry", field) == 0)
409 		return ascii_load_uint16(&ep->retry, buf);
410 
411 	if (strcasecmp("rcpt", field) == 0)
412 		return ascii_load_mailaddr(&ep->rcpt, buf);
413 
414 	if (strcasecmp("mda-exec", field) == 0)
415 		return ascii_load_string(ep->mda_exec, buf, sizeof(ep->mda_exec));
416 
417 	if (strcasecmp("mda-subaddress", field) == 0)
418 		return ascii_load_string(ep->mda_subaddress, buf, sizeof(ep->mda_subaddress));
419 
420 	if (strcasecmp("mda-user", field) == 0)
421 		return ascii_load_string(ep->mda_user, buf, sizeof(ep->mda_user));
422 
423 	if (strcasecmp("sender", field) == 0)
424 		return ascii_load_mailaddr(&ep->sender, buf);
425 
426 	if (strcasecmp("smtpname", field) == 0)
427 		return ascii_load_string(ep->smtpname, buf,
428 		    sizeof(ep->smtpname));
429 
430 	if (strcasecmp("sockaddr", field) == 0)
431 		return ascii_load_sockaddr(&ep->ss, buf);
432 
433 	if (strcasecmp("tag", field) == 0)
434 		return ascii_load_string(ep->tag, buf, sizeof ep->tag);
435 
436 	if (strcasecmp("type", field) == 0)
437 		return ascii_load_type(&ep->type, buf);
438 
439 	if (strcasecmp("version", field) == 0)
440 		return ascii_load_uint32(&ep->version, buf);
441 
442 	if (strcasecmp("dsn-notify", field) == 0)
443 		return ascii_load_uint8(&ep->dsn_notify, buf);
444 
445 	if (strcasecmp("dsn-orcpt", field) == 0)
446 		return ascii_load_mailaddr(&ep->dsn_orcpt, buf);
447 
448 	if (strcasecmp("dsn-ret", field) == 0)
449 		return ascii_load_dsn_ret(&ep->dsn_ret, buf);
450 
451 	if (strcasecmp("dsn-envid", field) == 0)
452 		return ascii_load_string(ep->dsn_envid, buf,
453 		    sizeof(ep->dsn_envid));
454 
455 	if (strcasecmp("esc-class", field) == 0)
456 		return ascii_load_uint8(&ep->esc_class, buf);
457 
458 	if (strcasecmp("esc-code", field) == 0)
459 		return ascii_load_uint8(&ep->esc_code, buf);
460 
461 	return (0);
462 }
463 
464 static int
465 envelope_ascii_load(struct envelope *ep, struct dict *d)
466 {
467 	const char	       *field;
468 	char		       *value;
469 	void		       *hdl;
470 
471 	hdl = NULL;
472 	while (dict_iter(d, &hdl, &field, (void **)&value))
473 		if (!ascii_load_field(field, ep, value))
474 			goto err;
475 
476 	return (1);
477 
478 err:
479 	log_warnx("envelope: invalid field \"%s\"", field);
480 	return (0);
481 }
482 
483 
484 static int
485 ascii_dump_uint8(uint8_t src, char *dest, size_t len)
486 {
487 	return bsnprintf(dest, len, "%d", src);
488 }
489 
490 static int
491 ascii_dump_uint16(uint16_t src, char *dest, size_t len)
492 {
493 	return bsnprintf(dest, len, "%d", src);
494 }
495 
496 static int
497 ascii_dump_uint32(uint32_t src, char *dest, size_t len)
498 {
499 	return bsnprintf(dest, len, "%d", src);
500 }
501 
502 static int
503 ascii_dump_time(time_t src, char *dest, size_t len)
504 {
505 	return bsnprintf(dest, len, "%lld", (long long) src);
506 }
507 
508 static int
509 ascii_dump_string(const char *src, char *dest, size_t len)
510 {
511 	return bsnprintf(dest, len, "%s", src);
512 }
513 
514 static int
515 ascii_dump_type(enum delivery_type type, char *dest, size_t len)
516 {
517 	char *p = NULL;
518 
519 	switch (type) {
520 	case D_MDA:
521 		p = "mda";
522 		break;
523 	case D_MTA:
524 		p = "mta";
525 		break;
526 	case D_BOUNCE:
527 		p = "bounce";
528 		break;
529 	default:
530 		return 0;
531 	}
532 
533 	return bsnprintf(dest, len, "%s", p);
534 }
535 
536 static int
537 ascii_dump_mailaddr(const struct mailaddr *addr, char *dest, size_t len)
538 {
539 	return bsnprintf(dest, len, "%s@%s",
540 	    addr->user, addr->domain);
541 }
542 
543 static int
544 ascii_dump_flags(enum envelope_flags flags, char *buf, size_t len)
545 {
546 	size_t cpylen = 0;
547 
548 	buf[0] = '\0';
549 	if (flags) {
550 		if (flags & EF_AUTHENTICATED)
551 			cpylen = strlcat(buf, "authenticated", len);
552 		if (flags & EF_BOUNCE) {
553 			if (buf[0] != '\0')
554 				(void)strlcat(buf, " ", len);
555 			cpylen = strlcat(buf, "bounce", len);
556 		}
557 		if (flags & EF_INTERNAL) {
558 			if (buf[0] != '\0')
559 				(void)strlcat(buf, " ", len);
560 			cpylen = strlcat(buf, "internal", len);
561 		}
562 	}
563 
564 	return cpylen < len ? 1 : 0;
565 }
566 
567 static int
568 ascii_dump_bounce_type(enum bounce_type type, char *dest, size_t len)
569 {
570 	char *p = NULL;
571 
572 	switch (type) {
573 	case B_FAILED:
574 		p = "failed";
575 		break;
576 	case B_DELAYED:
577 		p = "delayed";
578 		break;
579 	case B_DELIVERED:
580 		p = "delivered";
581 		break;
582 	default:
583 		return 0;
584 	}
585 	return bsnprintf(dest, len, "%s", p);
586 }
587 
588 
589 static int
590 ascii_dump_dsn_ret(enum dsn_ret flag, char *dest, size_t len)
591 {
592 	size_t cpylen = 0;
593 
594 	dest[0] = '\0';
595 	if (flag == DSN_RETFULL)
596 		cpylen = strlcat(dest, "FULL", len);
597 	else if (flag == DSN_RETHDRS)
598 		cpylen = strlcat(dest, "HDRS", len);
599 
600 	return cpylen < len ? 1 : 0;
601 }
602 
603 static int
604 ascii_dump_field(const char *field, const struct envelope *ep,
605     char *buf, size_t len)
606 {
607 	if (strcasecmp(field, "dispatcher") == 0)
608 		return ascii_dump_string(ep->dispatcher, buf, len);
609 
610 	if (strcasecmp(field, "bounce-delay") == 0) {
611 		if (ep->agent.bounce.type != B_DELAYED)
612 			return (1);
613 		return ascii_dump_time(ep->agent.bounce.delay, buf, len);
614 	}
615 
616 	if (strcasecmp(field, "bounce-ttl") == 0) {
617 		if (ep->agent.bounce.type != B_DELAYED)
618 			return (1);
619 		return ascii_dump_time(ep->agent.bounce.ttl, buf, len);
620 	}
621 
622 	if (strcasecmp(field, "bounce-type") == 0)
623 		return ascii_dump_bounce_type(ep->agent.bounce.type, buf, len);
624 
625 	if (strcasecmp(field, "ctime") == 0)
626 		return ascii_dump_time(ep->creation, buf, len);
627 
628 	if (strcasecmp(field, "dest") == 0)
629 		return ascii_dump_mailaddr(&ep->dest, buf, len);
630 
631 	if (strcasecmp(field, "username") == 0) {
632 		if (ep->username[0])
633 			return ascii_dump_string(ep->username, buf, len);
634 		return 1;
635 	}
636 
637 	if (strcasecmp(field, "errorline") == 0)
638 		return ascii_dump_string(ep->errorline, buf, len);
639 
640 	if (strcasecmp(field, "ttl") == 0)
641 		return ascii_dump_time(ep->ttl, buf, len);
642 
643 	if (strcasecmp(field, "flags") == 0)
644 		return ascii_dump_flags(ep->flags, buf, len);
645 
646 	if (strcasecmp(field, "helo") == 0)
647 		return ascii_dump_string(ep->helo, buf, len);
648 
649 	if (strcasecmp(field, "hostname") == 0)
650 		return ascii_dump_string(ep->hostname, buf, len);
651 
652 	if (strcasecmp(field, "last-bounce") == 0)
653 		return ascii_dump_time(ep->lastbounce, buf, len);
654 
655 	if (strcasecmp(field, "last-try") == 0)
656 		return ascii_dump_time(ep->lasttry, buf, len);
657 
658 	if (strcasecmp(field, "retry") == 0)
659 		return ascii_dump_uint16(ep->retry, buf, len);
660 
661 	if (strcasecmp(field, "rcpt") == 0)
662 		return ascii_dump_mailaddr(&ep->rcpt, buf, len);
663 
664 	if (strcasecmp(field, "mda-exec") == 0) {
665 		if (ep->mda_exec[0])
666 			return ascii_dump_string(ep->mda_exec, buf, len);
667 		return 1;
668 	}
669 
670 	if (strcasecmp(field, "mda-subaddress") == 0) {
671 		if (ep->mda_subaddress[0])
672 			return ascii_dump_string(ep->mda_subaddress, buf, len);
673 		return 1;
674 	}
675 
676 	if (strcasecmp(field, "mda-user") == 0) {
677 		if (ep->mda_user[0])
678 			return ascii_dump_string(ep->mda_user, buf, len);
679 		return 1;
680 	}
681 
682 	if (strcasecmp(field, "sender") == 0)
683 		return ascii_dump_mailaddr(&ep->sender, buf, len);
684 
685 	if (strcasecmp(field, "smtpname") == 0)
686 		return ascii_dump_string(ep->smtpname, buf, len);
687 
688 	if (strcasecmp(field, "sockaddr") == 0)
689 		return ascii_dump_string(ss_to_text(&ep->ss), buf, len);
690 
691 	if (strcasecmp(field, "tag") == 0)
692 		return ascii_dump_string(ep->tag, buf, len);
693 
694 	if (strcasecmp(field, "type") == 0)
695 		return ascii_dump_type(ep->type, buf, len);
696 
697 	if (strcasecmp(field, "version") == 0)
698 		return ascii_dump_uint32(SMTPD_ENVELOPE_VERSION, buf, len);
699 
700 	if (strcasecmp(field, "dsn-notify") == 0)
701 		return ascii_dump_uint8(ep->dsn_notify, buf, len);
702 
703 	if (strcasecmp(field, "dsn-ret") == 0)
704 		return ascii_dump_dsn_ret(ep->dsn_ret, buf, len);
705 
706 	if (strcasecmp(field, "dsn-orcpt") == 0) {
707 		if (ep->dsn_orcpt.user[0] && ep->dsn_orcpt.domain[0])
708 			return ascii_dump_mailaddr(&ep->dsn_orcpt, buf, len);
709 		return 1;
710 	}
711 
712 	if (strcasecmp(field, "dsn-envid") == 0)
713 		return ascii_dump_string(ep->dsn_envid, buf, len);
714 
715 	if (strcasecmp(field, "esc-class") == 0) {
716 		if (ep->esc_class)
717 			return ascii_dump_uint8(ep->esc_class, buf, len);
718 		return 1;
719 	}
720 
721 	if (strcasecmp(field, "esc-code") == 0) {
722 		/* this is not a pasto, we dump esc_code if esc_class is !0 */
723 		if (ep->esc_class)
724 			return ascii_dump_uint8(ep->esc_code, buf, len);
725 		return 1;
726 	}
727 
728 	return (0);
729 }
730 
731 static void
732 envelope_ascii_dump(const struct envelope *ep, char **dest, size_t *len,
733     const char *field)
734 {
735 	char	buf[8192];
736 	int	l;
737 
738 	if (*dest == NULL)
739 		return;
740 
741 	memset(buf, 0, sizeof buf);
742 	if (!ascii_dump_field(field, ep, buf, sizeof buf))
743 		goto err;
744 	if (buf[0] == '\0')
745 		return;
746 
747 	l = snprintf(*dest, *len, "%s: %s\n", field, buf);
748 	if (l < 0 || (size_t) l >= *len)
749 		goto err;
750 	*dest += l;
751 	*len -= l;
752 
753 	return;
754 err:
755 	*dest = NULL;
756 }
757