xref: /openbsd-src/usr.sbin/smtpd/smtp_client.c (revision 7350f337b9e3eb4461d99580e625c7ef148d107c)
1 /*	$OpenBSD: smtp_client.c,v 1.10 2019/06/12 17:42:53 eric Exp $	*/
2 
3 /*
4  * Copyright (c) 2018 Eric Faurot <eric@openbsd.org>
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 
22 #include <netinet/in.h>
23 
24 #include <ctype.h>
25 #include <errno.h>
26 #include <limits.h>
27 #include <resolv.h>
28 #include <stdarg.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 
33 #include "log.h"
34 #include "ioev.h"
35 #include "smtp.h"
36 
37 #define	TRACE_SMTPCLT	2
38 #define	TRACE_IO	3
39 
40 enum {
41 	STATE_INIT = 0,
42 	STATE_BANNER,
43 	STATE_EHLO,
44 	STATE_HELO,
45 	STATE_LHLO,
46 	STATE_STARTTLS,
47 	STATE_AUTH,
48 	STATE_AUTH_PLAIN,
49 	STATE_AUTH_LOGIN,
50 	STATE_AUTH_LOGIN_USER,
51 	STATE_AUTH_LOGIN_PASS,
52 	STATE_READY,
53 	STATE_MAIL,
54 	STATE_RCPT,
55 	STATE_DATA,
56 	STATE_BODY,
57 	STATE_EOM,
58 	STATE_RSET,
59 	STATE_QUIT,
60 
61 	STATE_LAST
62 };
63 
64 #define base64_encode	__b64_ntop
65 #define base64_decode	__b64_pton
66 
67 #define FLAG_TLS		0x01
68 #define FLAG_TLS_VERIFIED	0x02
69 
70 #define SMTP_EXT_STARTTLS	0x01
71 #define SMTP_EXT_PIPELINING	0x02
72 #define SMTP_EXT_AUTH		0x04
73 #define SMTP_EXT_AUTH_PLAIN     0x08
74 #define SMTP_EXT_AUTH_LOGIN     0x10
75 #define SMTP_EXT_DSN		0x20
76 #define SMTP_EXT_SIZE		0x40
77 
78 struct smtp_client {
79 	void			*tag;
80 	struct smtp_params	 params;
81 
82 	int			 state;
83 	int			 flags;
84 	int			 ext;
85 	size_t			 ext_size;
86 
87 	struct io		*io;
88 	char			*reply;
89 	size_t			 replysz;
90 
91 	struct smtp_mail	*mail;
92 	int			 rcptidx;
93 	int			 rcptok;
94 };
95 
96 void log_trace_verbose(int);
97 void log_trace(int, const char *, ...)
98     __attribute__((format (printf, 2, 3)));
99 
100 static void smtp_client_io(struct io *, int, void *);
101 static void smtp_client_free(struct smtp_client *);
102 static void smtp_client_state(struct smtp_client *, int);
103 static void smtp_client_abort(struct smtp_client *, int, const char *);
104 static void smtp_client_cancel(struct smtp_client *, int, const char *);
105 static void smtp_client_sendcmd(struct smtp_client *, char *, ...);
106 static void smtp_client_sendbody(struct smtp_client *);
107 static int smtp_client_readline(struct smtp_client *);
108 static int smtp_client_replycat(struct smtp_client *, const char *);
109 static void smtp_client_response(struct smtp_client *, const char *);
110 static void smtp_client_mail_abort(struct smtp_client *);
111 static void smtp_client_mail_status(struct smtp_client *, const char *);
112 static void smtp_client_rcpt_status(struct smtp_client *, struct smtp_rcpt *, const char *);
113 
114 static const char *strstate[STATE_LAST] = {
115 	"INIT",
116 	"BANNER",
117 	"EHLO",
118 	"HELO",
119 	"LHLO",
120 	"STARTTLS",
121 	"AUTH",
122 	"AUTH_PLAIN",
123 	"AUTH_LOGIN",
124 	"AUTH_LOGIN_USER",
125 	"AUTH_LOGIN_PASS",
126 	"READY",
127 	"MAIL",
128 	"RCPT",
129 	"DATA",
130 	"BODY",
131 	"EOM",
132 	"RSET",
133 	"QUIT",
134 };
135 
136 struct smtp_client *
137 smtp_connect(const struct smtp_params *params, void *tag)
138 {
139 	struct smtp_client *proto;
140 
141 	proto = calloc(1, sizeof *proto);
142 	if (proto == NULL)
143 		return NULL;
144 
145 	memmove(&proto->params, params, sizeof(*params));
146 	proto->tag = tag;
147 	proto->io = io_new();
148 	if (proto->io == NULL) {
149 		free(proto);
150 		return NULL;
151 	}
152 	io_set_callback(proto->io, smtp_client_io, proto);
153 	io_set_timeout(proto->io, proto->params.timeout);
154 
155 	if (io_connect(proto->io, proto->params.dst, proto->params.src) == -1) {
156 		smtp_client_abort(proto, FAIL_CONN, io_error(proto->io));
157 		return NULL;
158 	}
159 
160 	return proto;
161 }
162 
163 void
164 smtp_cert_verified(struct smtp_client *proto, int verified)
165 {
166 	if (verified == CERT_OK)
167 		proto->flags |= FLAG_TLS_VERIFIED;
168 
169 	else if (proto->params.tls_verify) {
170 		errno = EAUTH;
171 		smtp_client_cancel(proto, FAIL_CONN,
172 		    "Invalid server certificate");
173 		return;
174 	}
175 
176 	io_resume(proto->io, IO_IN);
177 
178 	if (proto->state == STATE_INIT)
179 		smtp_client_state(proto, STATE_BANNER);
180 	else {
181 		/* Clear extensions before re-issueing an EHLO command. */
182 		proto->ext = 0;
183 		smtp_client_state(proto, STATE_EHLO);
184 	}
185 }
186 
187 void
188 smtp_quit(struct smtp_client *proto)
189 {
190 	if (proto->state != STATE_READY)
191 		fatalx("connection is not ready");
192 
193 	smtp_client_state(proto, STATE_QUIT);
194 }
195 
196 void
197 smtp_sendmail(struct smtp_client *proto, struct smtp_mail *mail)
198 {
199 	if (proto->state != STATE_READY)
200 		fatalx("connection is not ready");
201 
202 	proto->mail = mail;
203 	smtp_client_state(proto, STATE_MAIL);
204 }
205 
206 static void
207 smtp_client_free(struct smtp_client *proto)
208 {
209 	if (proto->mail)
210 		fatalx("current task should have been deleted already");
211 
212 	smtp_closed(proto->tag, proto);
213 
214 	if (proto->io)
215 		io_free(proto->io);
216 
217 	free(proto->reply);
218 	free(proto);
219 }
220 
221 /*
222  * End the session immediatly.
223  */
224 static void
225 smtp_client_abort(struct smtp_client *proto, int err, const char *reason)
226 {
227 	smtp_failed(proto->tag, proto, err, reason);
228 
229 	if (proto->mail)
230 		smtp_client_mail_abort(proto);
231 
232 	smtp_client_free(proto);
233 }
234 
235 /*
236  * Properly close the session.
237  */
238 static void
239 smtp_client_cancel(struct smtp_client *proto, int err, const char *reason)
240 {
241 	if (proto->mail)
242 		fatal("not supposed to have a mail");
243 
244 	smtp_failed(proto->tag, proto, err, reason);
245 
246 	smtp_client_state(proto, STATE_QUIT);
247 }
248 
249 static void
250 smtp_client_state(struct smtp_client *proto, int newstate)
251 {
252 	struct smtp_rcpt *rcpt;
253 	char ibuf[LINE_MAX], obuf[LINE_MAX];
254 	size_t n;
255 	int oldstate;
256 
257 	if (proto->reply)
258 		proto->reply[0] = '\0';
259 
260     again:
261 	oldstate = proto->state;
262 	proto->state = newstate;
263 
264 	log_trace(TRACE_SMTPCLT, "%p: %s -> %s", proto,
265 	    strstate[oldstate],
266 	    strstate[newstate]);
267 
268 	/* don't try this at home! */
269 #define smtp_client_state(_s, _st) do { newstate = _st; goto again; } while (0)
270 
271 	switch (proto->state) {
272 	case STATE_BANNER:
273 		io_set_read(proto->io);
274 		break;
275 
276 	case STATE_EHLO:
277 		smtp_client_sendcmd(proto, "EHLO %s", proto->params.helo);
278 		break;
279 
280 	case STATE_HELO:
281 		smtp_client_sendcmd(proto, "HELO %s", proto->params.helo);
282 		break;
283 
284 	case STATE_LHLO:
285 		smtp_client_sendcmd(proto, "LHLO %s", proto->params.helo);
286 		break;
287 
288 	case STATE_STARTTLS:
289 		if (proto->params.tls_req == TLS_NO || proto->flags & FLAG_TLS)
290 			smtp_client_state(proto, STATE_AUTH);
291 		else if (proto->ext & SMTP_EXT_STARTTLS)
292 			smtp_client_sendcmd(proto, "STARTTLS");
293 		else if (proto->params.tls_req == TLS_FORCE)
294 			smtp_client_cancel(proto, FAIL_IMPL,
295 			    "TLS not supported by remote host");
296 		else
297 			smtp_client_state(proto, STATE_AUTH);
298 		break;
299 
300 	case STATE_AUTH:
301 		if (!proto->params.auth_user)
302 			smtp_client_state(proto, STATE_READY);
303 		else if ((proto->flags & FLAG_TLS) == 0)
304 			smtp_client_cancel(proto, FAIL_IMPL,
305 			    "Authentication requires TLS");
306 		else if ((proto->ext & SMTP_EXT_AUTH) == 0)
307 			smtp_client_cancel(proto, FAIL_IMPL,
308 			    "AUTH not supported by remote host");
309 		else if (proto->ext & SMTP_EXT_AUTH_PLAIN)
310 			smtp_client_state(proto, STATE_AUTH_PLAIN);
311 		else if (proto->ext & SMTP_EXT_AUTH_LOGIN)
312 			smtp_client_state(proto, STATE_AUTH_LOGIN);
313 		else
314 			smtp_client_cancel(proto, FAIL_IMPL,
315 			    "No supported AUTH method");
316 		break;
317 
318 	case STATE_AUTH_PLAIN:
319 		(void)strlcpy(ibuf, "-", sizeof(ibuf));
320 		(void)strlcat(ibuf, proto->params.auth_user, sizeof(ibuf));
321 		if (strlcat(ibuf, ":", sizeof(ibuf)) >= sizeof(ibuf)) {
322 			errno = EMSGSIZE;
323 			smtp_client_cancel(proto, FAIL_INTERNAL,
324 			    "credentials too large");
325 			break;
326 		}
327 		n = strlcat(ibuf, proto->params.auth_pass, sizeof(ibuf));
328 		if (n >= sizeof(ibuf)) {
329 			errno = EMSGSIZE;
330 			smtp_client_cancel(proto, FAIL_INTERNAL,
331 			    "credentials too large");
332 			break;
333 		}
334 		*strchr(ibuf, ':') = '\0';
335 		ibuf[0] = '\0';
336 		if (base64_encode(ibuf, n, obuf, sizeof(obuf)) == -1) {
337 			errno = EMSGSIZE;
338 			smtp_client_cancel(proto, FAIL_INTERNAL,
339 			    "credentials too large");
340 			break;
341 		}
342 		smtp_client_sendcmd(proto, "AUTH PLAIN %s", obuf);
343 		explicit_bzero(ibuf, sizeof ibuf);
344 		explicit_bzero(obuf, sizeof obuf);
345 		break;
346 
347 	case STATE_AUTH_LOGIN:
348 		smtp_client_sendcmd(proto, "AUTH LOGIN");
349 		break;
350 
351 	case STATE_AUTH_LOGIN_USER:
352 		if (base64_encode(proto->params.auth_user,
353 		    strlen(proto->params.auth_user), obuf,
354 		    sizeof(obuf)) == -1) {
355 			errno = EMSGSIZE;
356 			smtp_client_cancel(proto, FAIL_INTERNAL,
357 			    "credentials too large");
358 			break;
359 		}
360 		smtp_client_sendcmd(proto, "%s", obuf);
361 		explicit_bzero(obuf, sizeof obuf);
362 		break;
363 
364 	case STATE_AUTH_LOGIN_PASS:
365 		if (base64_encode(proto->params.auth_pass,
366 		    strlen(proto->params.auth_pass), obuf,
367 		    sizeof(obuf)) == -1) {
368 			errno = EMSGSIZE;
369 			smtp_client_cancel(proto, FAIL_INTERNAL,
370 			    "credentials too large");
371 			break;
372 		}
373 		smtp_client_sendcmd(proto, "%s", obuf);
374 		explicit_bzero(obuf, sizeof obuf);
375 		break;
376 
377 	case STATE_READY:
378 		smtp_ready(proto->tag, proto);
379 		break;
380 
381 	case STATE_MAIL:
382 		if (proto->ext & SMTP_EXT_DSN)
383 			smtp_client_sendcmd(proto, "MAIL FROM:<%s>%s%s%s%s",
384 			    proto->mail->from,
385 			    proto->mail->dsn_ret ? " RET=" : "",
386 			    proto->mail->dsn_ret ? proto->mail->dsn_ret : "",
387 			    proto->mail->dsn_envid ? " ENVID=" : "",
388 			    proto->mail->dsn_envid ? proto->mail->dsn_envid : "");
389 		else
390 			smtp_client_sendcmd(proto, "MAIL FROM:<%s>",
391 			    proto->mail->from);
392 		break;
393 
394 	case STATE_RCPT:
395 		if (proto->rcptidx == proto->mail->rcptcount) {
396 			smtp_client_state(proto, STATE_DATA);
397 			break;
398 		}
399 		rcpt = &proto->mail->rcpt[proto->rcptidx];
400 		if (proto->ext & SMTP_EXT_DSN)
401 			smtp_client_sendcmd(proto, "RCPT TO:<%s>%s%s%s%s",
402 			    rcpt->to,
403 			    rcpt->dsn_notify ? " NOTIFY=" : "",
404 			    rcpt->dsn_notify ? rcpt->dsn_notify : "",
405 			    rcpt->dsn_orcpt ? " ORCPT=" : "",
406 			    rcpt->dsn_orcpt ? rcpt->dsn_orcpt : "");
407 		else
408 			smtp_client_sendcmd(proto, "RCPT TO:<%s>", rcpt->to);
409 		break;
410 
411 	case STATE_DATA:
412 		if (proto->rcptok == 0) {
413 			smtp_client_mail_abort(proto);
414 			smtp_client_state(proto, STATE_RSET);
415 		}
416 		else
417 			smtp_client_sendcmd(proto, "DATA");
418 		break;
419 
420 	case STATE_BODY:
421 		fseek(proto->mail->fp, 0, SEEK_SET);
422 		smtp_client_sendbody(proto);
423 		break;
424 
425 	case STATE_EOM:
426 		smtp_client_sendcmd(proto, ".");
427 		break;
428 
429 	case STATE_RSET:
430 		smtp_client_sendcmd(proto, "RSET");
431 		break;
432 
433 	case STATE_QUIT:
434 		smtp_client_sendcmd(proto, "QUIT");
435 		break;
436 
437 	default:
438 		fatalx("%s: bad state %d", __func__, proto->state);
439 	}
440 #undef smtp_client_state
441 }
442 
443 /*
444  * Handle a response to an SMTP command
445  */
446 static void
447 smtp_client_response(struct smtp_client *proto, const char *line)
448 {
449 	struct smtp_rcpt *rcpt;
450 	int i, seen;
451 
452 	switch (proto->state) {
453 	case STATE_BANNER:
454 		if (line[0] != '2')
455 			smtp_client_abort(proto, FAIL_RESP, line);
456 		else if (proto->params.lmtp)
457 			smtp_client_state(proto, STATE_LHLO);
458 		else
459 			smtp_client_state(proto, STATE_EHLO);
460 		break;
461 
462 	case STATE_EHLO:
463 		if (line[0] != '2') {
464 			/*
465 			 * Either rejected or not implemented.  If we want to
466 			 * use EHLO extensions, report an SMTP error.
467 			 * Otherwise, fallback to using HELO.
468 			 */
469 			if ((proto->params.tls_req == TLS_FORCE) ||
470 			    (proto->params.auth_user))
471 				smtp_client_cancel(proto, FAIL_RESP, line);
472 			else
473 				smtp_client_state(proto, STATE_HELO);
474 			break;
475 		}
476 		smtp_client_state(proto, STATE_STARTTLS);
477 		break;
478 
479 	case STATE_HELO:
480 		if (line[0] != '2')
481 			smtp_client_cancel(proto, FAIL_RESP, line);
482 		else
483 			smtp_client_state(proto, STATE_READY);
484 		break;
485 
486 	case STATE_LHLO:
487 		if (line[0] != '2')
488 			smtp_client_cancel(proto, FAIL_RESP, line);
489 		else
490 			smtp_client_state(proto, STATE_READY);
491 		break;
492 
493 	case STATE_STARTTLS:
494 		if (line[0] != '2') {
495 			if ((proto->params.tls_req == TLS_FORCE) ||
496 			    (proto->params.auth_user)) {
497 				smtp_client_cancel(proto, FAIL_RESP, line);
498 				break;
499 			}
500 			smtp_client_state(proto, STATE_AUTH);
501 		}
502 		else
503 			io_start_tls(proto->io, proto->params.tls_ctx);
504 		break;
505 
506 	case STATE_AUTH_PLAIN:
507 		if (line[0] != '2')
508 			smtp_client_cancel(proto, FAIL_RESP, line);
509 		else
510 			smtp_client_state(proto, STATE_READY);
511 		break;
512 
513 	case STATE_AUTH_LOGIN:
514 		if (strncmp(line, "334 ", 4))
515 			smtp_client_cancel(proto, FAIL_RESP, line);
516 		else
517 			smtp_client_state(proto, STATE_AUTH_LOGIN_USER);
518 		break;
519 
520 	case STATE_AUTH_LOGIN_USER:
521 		if (strncmp(line, "334 ", 4))
522 			smtp_client_cancel(proto, FAIL_RESP, line);
523 		else
524 			smtp_client_state(proto, STATE_AUTH_LOGIN_PASS);
525 		break;
526 
527 	case STATE_AUTH_LOGIN_PASS:
528 		if (line[0] != '2')
529 			smtp_client_cancel(proto, FAIL_RESP, line);
530 		else
531 			smtp_client_state(proto, STATE_READY);
532 		break;
533 
534 	case STATE_MAIL:
535 		if (line[0] != '2') {
536 			smtp_client_mail_status(proto, line);
537 			smtp_client_state(proto, STATE_RSET);
538 		}
539 		else
540 			smtp_client_state(proto, STATE_RCPT);
541 		break;
542 
543 	case STATE_RCPT:
544 		rcpt = &proto->mail->rcpt[proto->rcptidx++];
545 		if (line[0] != '2')
546 			smtp_client_rcpt_status(proto, rcpt, line);
547 		else {
548 			proto->rcptok++;
549 			smtp_client_state(proto, STATE_RCPT);
550 		}
551 		break;
552 
553 	case STATE_DATA:
554 		if (line[0] != '2' && line[0] != '3') {
555 			smtp_client_mail_status(proto, line);
556 			smtp_client_state(proto, STATE_RSET);
557 		}
558 		else
559 			smtp_client_state(proto, STATE_BODY);
560 		break;
561 
562 	case STATE_EOM:
563 		if (proto->params.lmtp) {
564 			/*
565 			 * LMTP reports a status of each accepted RCPT.
566 			 * Report status for the first pending RCPT and read
567 			 * more lines if another rcpt needs a status.
568 			 */
569 			for (i = 0, seen = 0; i < proto->mail->rcptcount; i++) {
570 				rcpt = &proto->mail->rcpt[i];
571 				if (rcpt->done)
572 					continue;
573 				if (seen) {
574 					io_set_read(proto->io);
575 					return;
576 				}
577 				smtp_client_rcpt_status(proto,
578 				    &proto->mail->rcpt[i], line);
579 				seen = 1;
580 			}
581 		}
582 		smtp_client_mail_status(proto, line);
583 		smtp_client_state(proto, STATE_READY);
584 		break;
585 
586 	case STATE_RSET:
587 		if (line[0] != '2')
588 			smtp_client_cancel(proto, FAIL_RESP, line);
589 		else
590 			smtp_client_state(proto, STATE_READY);
591 		break;
592 
593 	case STATE_QUIT:
594 		smtp_client_free(proto);
595 		break;
596 
597 	default:
598 		fatalx("%s: bad state %d", __func__, proto->state);
599 	}
600 }
601 
602 static void
603 smtp_client_io(struct io *io, int evt, void *arg)
604 {
605 	struct smtp_client *proto = arg;
606 
607 	log_trace(TRACE_IO, "%p: %s %s", proto, io_strevent(evt), io_strio(io));
608 
609 	switch (evt) {
610 	case IO_CONNECTED:
611 		if (proto->params.tls_req == TLS_SMTPS) {
612 			io_set_write(io);
613 			io_start_tls(proto->io, proto->params.tls_ctx);
614 		}
615 		else
616 			smtp_client_state(proto, STATE_BANNER);
617 		break;
618 
619 	case IO_TLSREADY:
620 		proto->flags |= FLAG_TLS;
621 		io_pause(proto->io, IO_IN);
622 		smtp_verify_server_cert(proto->tag, proto, io_tls(proto->io));
623 		break;
624 
625 	case IO_DATAIN:
626 		while (smtp_client_readline(proto))
627 			;
628 		break;
629 
630 	case IO_LOWAT:
631 		if (proto->state == STATE_BODY)
632 			smtp_client_sendbody(proto);
633 		else
634 			io_set_read(io);
635 		break;
636 
637 	case IO_TIMEOUT:
638 		errno = ETIMEDOUT;
639 		smtp_client_abort(proto, FAIL_CONN, "Connection timeout");
640 		break;
641 
642 	case IO_ERROR:
643 		smtp_client_abort(proto, FAIL_CONN, io_error(io));
644 		break;
645 
646 	case IO_TLSERROR:
647 		smtp_client_abort(proto, FAIL_CONN, io_error(io));
648 		break;
649 
650 	case IO_DISCONNECTED:
651 		smtp_client_abort(proto, FAIL_CONN, io_error(io));
652 		break;
653 
654 	default:
655 		fatalx("%s: bad event %d", __func__, evt);
656 	}
657 }
658 
659 /*
660  * return 1 if a new  line is expected.
661  */
662 static int
663 smtp_client_readline(struct smtp_client *proto)
664 {
665 	const char *e;
666 	size_t len;
667 	char *line, *msg, *p;
668 	int cont;
669 
670 	line = io_getline(proto->io, &len);
671 	if (line == NULL) {
672 		if (io_datalen(proto->io) >= proto->params.linemax)
673 			smtp_client_abort(proto, FAIL_PROTO, "Line too long");
674 		return 0;
675 	}
676 
677 	log_trace(TRACE_SMTPCLT, "%p: <<< %s", proto, line);
678 
679 	/* Validate SMTP  */
680 	if (len > 3) {
681 		msg = line + 4;
682 		cont = (line[3] == '-');
683 	} else if (len == 3) {
684 		msg = line + 3;
685 		cont = 0;
686 	} else {
687 		smtp_client_abort(proto, FAIL_PROTO, "Response too short");
688 		return 0;
689 	}
690 
691 	/* Validate reply code. */
692 	if (line[0] < '2' || line[0] > '5' || !isdigit((unsigned char)line[1]) ||
693 	    !isdigit((unsigned char)line[2])) {
694 		smtp_client_abort(proto, FAIL_PROTO, "Invalid reply code");
695 		return 0;
696 	}
697 
698 	/* Validate reply message. */
699 	for (p = msg; *p; p++)
700 		if (!isprint((unsigned char)*p)) {
701 			smtp_client_abort(proto, FAIL_PROTO,
702 			    "Non-printable characters in response");
703 			return 0;
704 	}
705 
706 	/* Read extensions. */
707 	if (proto->state == STATE_EHLO) {
708 		if (strcmp(msg, "STARTTLS") == 0)
709 			proto->ext |= SMTP_EXT_STARTTLS;
710 		else if (strncmp(msg, "AUTH ", 5) == 0) {
711 			proto->ext |= SMTP_EXT_AUTH;
712 			if ((p = strstr(msg, " PLAIN")) &&
713 			    (*(p+6) == '\0' || *(p+6) == ' '))
714 				proto->ext |= SMTP_EXT_AUTH_PLAIN;
715 			if ((p = strstr(msg, " LOGIN")) &&
716 			    (*(p+6) == '\0' || *(p+6) == ' '))
717 				proto->ext |= SMTP_EXT_AUTH_LOGIN;
718 			}
719 		else if (strcmp(msg, "PIPELINING") == 0)
720 			proto->ext |= SMTP_EXT_PIPELINING;
721 		else if (strcmp(msg, "DSN") == 0)
722 			proto->ext |= SMTP_EXT_DSN;
723 		else if (strncmp(msg, "SIZE ", 5) == 0) {
724 			proto->ext_size = strtonum(msg + 5, 0, SIZE_T_MAX, &e);
725 			if (e == NULL)
726 				proto->ext |= SMTP_EXT_SIZE;
727 		}
728 	}
729 
730 	if (smtp_client_replycat(proto, line) == -1) {
731 		smtp_client_abort(proto, FAIL_INTERNAL, NULL);
732 		return 0;
733 	}
734 
735 	if (cont)
736 		return 1;
737 
738 	if (io_datalen(proto->io)) {
739 		/*
740 		 * There should be no pending data after a response is read,
741 		 * except for the multiple status lines after a LMTP message.
742 		 * It can also happen with pipelineing, but we don't do that
743 		 * for now.
744 		 */
745 		if (!(proto->params.lmtp && proto->state == STATE_EOM)) {
746 			smtp_client_abort(proto, FAIL_PROTO, "Trailing data");
747 			return 0;
748 		}
749 	}
750 
751 	io_set_write(proto->io);
752 	smtp_client_response(proto, proto->reply);
753 	return 0;
754 }
755 
756 /*
757  * Concatenate the given response line.
758  */
759 static int
760 smtp_client_replycat(struct smtp_client *proto, const char *line)
761 {
762 	size_t len;
763 	char *tmp;
764 	int first;
765 
766 	if (proto->reply && proto->reply[0]) {
767 		/*
768 		 * If the line is the continuation of an multi-line response,
769 		 * skip the status and ESC parts. First, skip the status, then
770 		 * skip the separator amd ESC if found.
771 		 */
772 		first = 0;
773 		line += 3;
774 		if (line[0]) {
775 			line += 1;
776 			if (isdigit((int)line[0]) && line[1] == '.' &&
777 			    isdigit((int)line[2]) && line[3] == '.' &&
778 			    isdigit((int)line[4]) && isspace((int)line[5]))
779 				line += 5;
780 		}
781 	} else
782 		first = 1;
783 
784 	if (proto->reply) {
785 		len = strlcat(proto->reply, line, proto->replysz);
786 		if (len < proto->replysz)
787 			return 0;
788 	}
789 	else
790 		len = strlen(line);
791 
792 	if (len > proto->params.ibufmax) {
793 		errno = EMSGSIZE;
794 		return -1;
795 	}
796 
797 	/* Allocate by multiples of 2^8 */
798 	len += (len % 256) ? (256 - (len % 256)) : 0;
799 
800 	tmp = realloc(proto->reply, len);
801 	if (tmp == NULL)
802 		return -1;
803 	if (proto->reply == NULL)
804 		tmp[0] = '\0';
805 
806 	proto->reply = tmp;
807 	proto->replysz = len;
808 	(void)strlcat(proto->reply, line, proto->replysz);
809 
810 	/* Replace the separator with a space for the first line. */
811 	if (first && proto->reply[3])
812 		proto->reply[3] = ' ';
813 
814 	return 0;
815 }
816 
817 static void
818 smtp_client_sendbody(struct smtp_client *proto)
819 {
820 	ssize_t len;
821 	size_t sz = 0, total, w;
822 	char *ln = NULL;
823 	int n;
824 
825 	total = io_queued(proto->io);
826 	w = 0;
827 
828 	while (total < proto->params.obufmax) {
829 		if ((len = getline(&ln, &sz, proto->mail->fp)) == -1)
830 			break;
831 		if (ln[len - 1] == '\n')
832 			ln[len - 1] = '\0';
833 		n = io_printf(proto->io, "%s%s\r\n", *ln == '.'?".":"", ln);
834 		if (n == -1) {
835 			free(ln);
836 			smtp_client_abort(proto, FAIL_INTERNAL, NULL);
837 			return;
838 		}
839 		total += n;
840 		w += n;
841 	}
842 	free(ln);
843 
844 	if (ferror(proto->mail->fp)) {
845 		smtp_client_abort(proto, FAIL_INTERNAL, "Cannot read message");
846 		return;
847 	}
848 
849 	log_trace(TRACE_SMTPCLT, "%p: >>> [...%zd bytes...]", proto, w);
850 
851 	if (feof(proto->mail->fp))
852 		smtp_client_state(proto, STATE_EOM);
853 }
854 
855 static void
856 smtp_client_sendcmd(struct smtp_client *proto, char *fmt, ...)
857 {
858 	va_list ap;
859 	char *p;
860 	int len;
861 
862 	va_start(ap, fmt);
863 	len = vasprintf(&p, fmt, ap);
864 	va_end(ap);
865 
866 	if (len == -1) {
867 		smtp_client_abort(proto, FAIL_INTERNAL, NULL);
868 		return;
869 	}
870 
871 	log_trace(TRACE_SMTPCLT, "mta: %p: >>> %s", proto, p);
872 
873 	len = io_printf(proto->io, "%s\r\n", p);
874 	free(p);
875 
876 	if (len == -1)
877 		smtp_client_abort(proto, FAIL_INTERNAL, NULL);
878 }
879 
880 static void
881 smtp_client_mail_status(struct smtp_client *proto, const char *status)
882 {
883 	int i;
884 
885 	for (i = 0; i < proto->mail->rcptcount; i++)
886 		smtp_client_rcpt_status(proto, &proto->mail->rcpt[i], status);
887 
888 	smtp_done(proto->tag, proto, proto->mail);
889 	proto->mail = NULL;
890 }
891 
892 static void
893 smtp_client_mail_abort(struct smtp_client *proto)
894 {
895 	smtp_done(proto->tag, proto, proto->mail);
896 	proto->mail = NULL;
897 }
898 
899 static void
900 smtp_client_rcpt_status(struct smtp_client *proto, struct smtp_rcpt *rcpt, const char *line)
901 {
902 	struct smtp_status status;
903 
904 	if (rcpt->done)
905 		return;
906 
907 	rcpt->done = 1;
908 	status.rcpt = rcpt;
909 	status.cmd = strstate[proto->state];
910 	status.status = line;
911 	smtp_status(proto->tag, proto, &status);
912 }
913