xref: /openbsd/usr.sbin/smtpd/envelope.c (revision cd8603db)
1 /*	$OpenBSD: envelope.c,v 1.52 2024/01/03 08:11:15 op 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
envelope_set_errormsg(struct envelope * e,char * fmt,...)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
envelope_set_esc_class(struct envelope * e,enum enhanced_status_class class)53 envelope_set_esc_class(struct envelope *e, enum enhanced_status_class class)
54 {
55 	e->esc_class = class;
56 }
57 
58 void
envelope_set_esc_code(struct envelope * e,enum enhanced_status_code code)59 envelope_set_esc_code(struct envelope *e, enum enhanced_status_code code)
60 {
61 	e->esc_code = code;
62 }
63 
64 static int
envelope_buffer_to_dict(struct dict * d,const char * ibuf,size_t buflen)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
envelope_load_buffer(struct envelope * ep,const char * ibuf,size_t buflen)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
envelope_dump_buffer(const struct envelope * ep,char * dest,size_t len)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
ascii_load_uint8(uint8_t * dest,char * buf)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
ascii_load_uint16(uint16_t * dest,char * buf)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
ascii_load_uint32(uint32_t * dest,char * buf)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
ascii_load_time(time_t * dest,char * buf)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
ascii_load_type(enum delivery_type * dest,char * buf)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
ascii_load_string(char * dest,char * buf,size_t len)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
ascii_load_sockaddr(struct sockaddr_storage * ss,char * buf)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
ascii_load_mailaddr(struct mailaddr * dest,char * buf)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
ascii_load_flags(enum envelope_flags * dest,char * buf)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
ascii_load_bounce_type(enum bounce_type * dest,char * buf)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
ascii_load_dsn_ret(enum dsn_ret * ret,char * buf)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
ascii_load_field(const char * field,struct envelope * ep,char * buf)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_string(ep->dsn_orcpt, buf,
447 		    sizeof(ep->dsn_orcpt));
448 
449 	if (strcasecmp("dsn-ret", field) == 0)
450 		return ascii_load_dsn_ret(&ep->dsn_ret, buf);
451 
452 	if (strcasecmp("dsn-envid", field) == 0)
453 		return ascii_load_string(ep->dsn_envid, buf,
454 		    sizeof(ep->dsn_envid));
455 
456 	if (strcasecmp("esc-class", field) == 0)
457 		return ascii_load_uint8(&ep->esc_class, buf);
458 
459 	if (strcasecmp("esc-code", field) == 0)
460 		return ascii_load_uint8(&ep->esc_code, buf);
461 
462 	return (0);
463 }
464 
465 static int
envelope_ascii_load(struct envelope * ep,struct dict * d)466 envelope_ascii_load(struct envelope *ep, struct dict *d)
467 {
468 	const char	       *field;
469 	char		       *value;
470 	void		       *hdl;
471 
472 	hdl = NULL;
473 	while (dict_iter(d, &hdl, &field, (void **)&value))
474 		if (!ascii_load_field(field, ep, value))
475 			goto err;
476 
477 	return (1);
478 
479 err:
480 	log_warnx("envelope: invalid field \"%s\"", field);
481 	return (0);
482 }
483 
484 
485 static int
ascii_dump_uint8(uint8_t src,char * dest,size_t len)486 ascii_dump_uint8(uint8_t src, char *dest, size_t len)
487 {
488 	return bsnprintf(dest, len, "%d", src);
489 }
490 
491 static int
ascii_dump_uint16(uint16_t src,char * dest,size_t len)492 ascii_dump_uint16(uint16_t src, char *dest, size_t len)
493 {
494 	return bsnprintf(dest, len, "%d", src);
495 }
496 
497 static int
ascii_dump_uint32(uint32_t src,char * dest,size_t len)498 ascii_dump_uint32(uint32_t src, char *dest, size_t len)
499 {
500 	return bsnprintf(dest, len, "%d", src);
501 }
502 
503 static int
ascii_dump_time(time_t src,char * dest,size_t len)504 ascii_dump_time(time_t src, char *dest, size_t len)
505 {
506 	return bsnprintf(dest, len, "%lld", (long long) src);
507 }
508 
509 static int
ascii_dump_string(const char * src,char * dest,size_t len)510 ascii_dump_string(const char *src, char *dest, size_t len)
511 {
512 	return bsnprintf(dest, len, "%s", src);
513 }
514 
515 static int
ascii_dump_type(enum delivery_type type,char * dest,size_t len)516 ascii_dump_type(enum delivery_type type, char *dest, size_t len)
517 {
518 	char *p = NULL;
519 
520 	switch (type) {
521 	case D_MDA:
522 		p = "mda";
523 		break;
524 	case D_MTA:
525 		p = "mta";
526 		break;
527 	case D_BOUNCE:
528 		p = "bounce";
529 		break;
530 	default:
531 		return 0;
532 	}
533 
534 	return bsnprintf(dest, len, "%s", p);
535 }
536 
537 static int
ascii_dump_mailaddr(const struct mailaddr * addr,char * dest,size_t len)538 ascii_dump_mailaddr(const struct mailaddr *addr, char *dest, size_t len)
539 {
540 	return bsnprintf(dest, len, "%s@%s",
541 	    addr->user, addr->domain);
542 }
543 
544 static int
ascii_dump_flags(enum envelope_flags flags,char * buf,size_t len)545 ascii_dump_flags(enum envelope_flags flags, char *buf, size_t len)
546 {
547 	size_t cpylen = 0;
548 
549 	buf[0] = '\0';
550 	if (flags) {
551 		if (flags & EF_AUTHENTICATED)
552 			cpylen = strlcat(buf, "authenticated", len);
553 		if (flags & EF_BOUNCE) {
554 			if (buf[0] != '\0')
555 				(void)strlcat(buf, " ", len);
556 			cpylen = strlcat(buf, "bounce", len);
557 		}
558 		if (flags & EF_INTERNAL) {
559 			if (buf[0] != '\0')
560 				(void)strlcat(buf, " ", len);
561 			cpylen = strlcat(buf, "internal", len);
562 		}
563 	}
564 
565 	return cpylen < len ? 1 : 0;
566 }
567 
568 static int
ascii_dump_bounce_type(enum bounce_type type,char * dest,size_t len)569 ascii_dump_bounce_type(enum bounce_type type, char *dest, size_t len)
570 {
571 	char *p = NULL;
572 
573 	switch (type) {
574 	case B_FAILED:
575 		p = "failed";
576 		break;
577 	case B_DELAYED:
578 		p = "delayed";
579 		break;
580 	case B_DELIVERED:
581 		p = "delivered";
582 		break;
583 	default:
584 		return 0;
585 	}
586 	return bsnprintf(dest, len, "%s", p);
587 }
588 
589 
590 static int
ascii_dump_dsn_ret(enum dsn_ret flag,char * dest,size_t len)591 ascii_dump_dsn_ret(enum dsn_ret flag, char *dest, size_t len)
592 {
593 	size_t cpylen = 0;
594 
595 	dest[0] = '\0';
596 	if (flag == DSN_RETFULL)
597 		cpylen = strlcat(dest, "FULL", len);
598 	else if (flag == DSN_RETHDRS)
599 		cpylen = strlcat(dest, "HDRS", len);
600 
601 	return cpylen < len ? 1 : 0;
602 }
603 
604 static int
ascii_dump_field(const char * field,const struct envelope * ep,char * buf,size_t len)605 ascii_dump_field(const char *field, const struct envelope *ep,
606     char *buf, size_t len)
607 {
608 	if (strcasecmp(field, "dispatcher") == 0)
609 		return ascii_dump_string(ep->dispatcher, buf, len);
610 
611 	if (strcasecmp(field, "bounce-delay") == 0) {
612 		if (ep->agent.bounce.type != B_DELAYED)
613 			return (1);
614 		return ascii_dump_time(ep->agent.bounce.delay, buf, len);
615 	}
616 
617 	if (strcasecmp(field, "bounce-ttl") == 0) {
618 		if (ep->agent.bounce.type != B_DELAYED)
619 			return (1);
620 		return ascii_dump_time(ep->agent.bounce.ttl, buf, len);
621 	}
622 
623 	if (strcasecmp(field, "bounce-type") == 0)
624 		return ascii_dump_bounce_type(ep->agent.bounce.type, buf, len);
625 
626 	if (strcasecmp(field, "ctime") == 0)
627 		return ascii_dump_time(ep->creation, buf, len);
628 
629 	if (strcasecmp(field, "dest") == 0)
630 		return ascii_dump_mailaddr(&ep->dest, buf, len);
631 
632 	if (strcasecmp(field, "username") == 0) {
633 		if (ep->username[0])
634 			return ascii_dump_string(ep->username, buf, len);
635 		return 1;
636 	}
637 
638 	if (strcasecmp(field, "errorline") == 0)
639 		return ascii_dump_string(ep->errorline, buf, len);
640 
641 	if (strcasecmp(field, "ttl") == 0)
642 		return ascii_dump_time(ep->ttl, buf, len);
643 
644 	if (strcasecmp(field, "flags") == 0)
645 		return ascii_dump_flags(ep->flags, buf, len);
646 
647 	if (strcasecmp(field, "helo") == 0)
648 		return ascii_dump_string(ep->helo, buf, len);
649 
650 	if (strcasecmp(field, "hostname") == 0)
651 		return ascii_dump_string(ep->hostname, buf, len);
652 
653 	if (strcasecmp(field, "last-bounce") == 0)
654 		return ascii_dump_time(ep->lastbounce, buf, len);
655 
656 	if (strcasecmp(field, "last-try") == 0)
657 		return ascii_dump_time(ep->lasttry, buf, len);
658 
659 	if (strcasecmp(field, "retry") == 0)
660 		return ascii_dump_uint16(ep->retry, buf, len);
661 
662 	if (strcasecmp(field, "rcpt") == 0)
663 		return ascii_dump_mailaddr(&ep->rcpt, buf, len);
664 
665 	if (strcasecmp(field, "mda-exec") == 0) {
666 		if (ep->mda_exec[0])
667 			return ascii_dump_string(ep->mda_exec, buf, len);
668 		return 1;
669 	}
670 
671 	if (strcasecmp(field, "mda-subaddress") == 0) {
672 		if (ep->mda_subaddress[0])
673 			return ascii_dump_string(ep->mda_subaddress, buf, len);
674 		return 1;
675 	}
676 
677 	if (strcasecmp(field, "mda-user") == 0) {
678 		if (ep->mda_user[0])
679 			return ascii_dump_string(ep->mda_user, buf, len);
680 		return 1;
681 	}
682 
683 	if (strcasecmp(field, "sender") == 0)
684 		return ascii_dump_mailaddr(&ep->sender, buf, len);
685 
686 	if (strcasecmp(field, "smtpname") == 0)
687 		return ascii_dump_string(ep->smtpname, buf, len);
688 
689 	if (strcasecmp(field, "sockaddr") == 0)
690 		return ascii_dump_string(ss_to_text(&ep->ss), buf, len);
691 
692 	if (strcasecmp(field, "tag") == 0)
693 		return ascii_dump_string(ep->tag, buf, len);
694 
695 	if (strcasecmp(field, "type") == 0)
696 		return ascii_dump_type(ep->type, buf, len);
697 
698 	if (strcasecmp(field, "version") == 0)
699 		return ascii_dump_uint32(SMTPD_ENVELOPE_VERSION, buf, len);
700 
701 	if (strcasecmp(field, "dsn-notify") == 0)
702 		return ascii_dump_uint8(ep->dsn_notify, buf, len);
703 
704 	if (strcasecmp(field, "dsn-ret") == 0)
705 		return ascii_dump_dsn_ret(ep->dsn_ret, buf, len);
706 
707 	if (strcasecmp(field, "dsn-orcpt") == 0)
708 		return ascii_dump_string(ep->dsn_orcpt, buf, len);
709 
710 	if (strcasecmp(field, "dsn-envid") == 0)
711 		return ascii_dump_string(ep->dsn_envid, buf, len);
712 
713 	if (strcasecmp(field, "esc-class") == 0) {
714 		if (ep->esc_class)
715 			return ascii_dump_uint8(ep->esc_class, buf, len);
716 		return 1;
717 	}
718 
719 	if (strcasecmp(field, "esc-code") == 0) {
720 		/* this is not a pasto, we dump esc_code if esc_class is !0 */
721 		if (ep->esc_class)
722 			return ascii_dump_uint8(ep->esc_code, buf, len);
723 		return 1;
724 	}
725 
726 	return (0);
727 }
728 
729 static void
envelope_ascii_dump(const struct envelope * ep,char ** dest,size_t * len,const char * field)730 envelope_ascii_dump(const struct envelope *ep, char **dest, size_t *len,
731     const char *field)
732 {
733 	char	buf[8192];
734 	int	l;
735 
736 	if (*dest == NULL)
737 		return;
738 
739 	memset(buf, 0, sizeof buf);
740 	if (!ascii_dump_field(field, ep, buf, sizeof buf))
741 		goto err;
742 	if (buf[0] == '\0')
743 		return;
744 
745 	l = snprintf(*dest, *len, "%s: %s\n", field, buf);
746 	if (l < 0 || (size_t) l >= *len)
747 		goto err;
748 	*dest += l;
749 	*len -= l;
750 
751 	return;
752 err:
753 	*dest = NULL;
754 }
755