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