1 /* $OpenBSD: smtp_client.c,v 1.14 2020/04/24 11:34:07 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_abort(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_set_tls(struct smtp_client *proto, void *ctx) 189 { 190 io_start_tls(proto->io, ctx); 191 } 192 193 void 194 smtp_quit(struct smtp_client *proto) 195 { 196 if (proto->state != STATE_READY) 197 fatalx("connection is not ready"); 198 199 smtp_client_state(proto, STATE_QUIT); 200 } 201 202 void 203 smtp_sendmail(struct smtp_client *proto, struct smtp_mail *mail) 204 { 205 if (proto->state != STATE_READY) 206 fatalx("connection is not ready"); 207 208 proto->mail = mail; 209 smtp_client_state(proto, STATE_MAIL); 210 } 211 212 static void 213 smtp_client_free(struct smtp_client *proto) 214 { 215 if (proto->mail) 216 fatalx("current task should have been deleted already"); 217 218 smtp_closed(proto->tag, proto); 219 220 if (proto->io) 221 io_free(proto->io); 222 223 free(proto->reply); 224 free(proto); 225 } 226 227 /* 228 * End the session immediatly. 229 */ 230 static void 231 smtp_client_abort(struct smtp_client *proto, int err, const char *reason) 232 { 233 smtp_failed(proto->tag, proto, err, reason); 234 235 if (proto->mail) 236 smtp_client_mail_abort(proto); 237 238 smtp_client_free(proto); 239 } 240 241 /* 242 * Properly close the session. 243 */ 244 static void 245 smtp_client_cancel(struct smtp_client *proto, int err, const char *reason) 246 { 247 if (proto->mail) 248 fatal("not supposed to have a mail"); 249 250 smtp_failed(proto->tag, proto, err, reason); 251 252 smtp_client_state(proto, STATE_QUIT); 253 } 254 255 static void 256 smtp_client_state(struct smtp_client *proto, int newstate) 257 { 258 struct smtp_rcpt *rcpt; 259 char ibuf[LINE_MAX], obuf[LINE_MAX]; 260 size_t n; 261 int oldstate; 262 263 if (proto->reply) 264 proto->reply[0] = '\0'; 265 266 again: 267 oldstate = proto->state; 268 proto->state = newstate; 269 270 log_trace(TRACE_SMTPCLT, "%p: %s -> %s", proto, 271 strstate[oldstate], 272 strstate[newstate]); 273 274 /* don't try this at home! */ 275 #define smtp_client_state(_s, _st) do { newstate = _st; goto again; } while (0) 276 277 switch (proto->state) { 278 case STATE_BANNER: 279 io_set_read(proto->io); 280 break; 281 282 case STATE_EHLO: 283 smtp_client_sendcmd(proto, "EHLO %s", proto->params.helo); 284 break; 285 286 case STATE_HELO: 287 smtp_client_sendcmd(proto, "HELO %s", proto->params.helo); 288 break; 289 290 case STATE_LHLO: 291 smtp_client_sendcmd(proto, "LHLO %s", proto->params.helo); 292 break; 293 294 case STATE_STARTTLS: 295 if (proto->params.tls_req == TLS_NO || proto->flags & FLAG_TLS) 296 smtp_client_state(proto, STATE_AUTH); 297 else if (proto->ext & SMTP_EXT_STARTTLS) 298 smtp_client_sendcmd(proto, "STARTTLS"); 299 else if (proto->params.tls_req == TLS_FORCE) 300 smtp_client_cancel(proto, FAIL_IMPL, 301 "TLS not supported by remote host"); 302 else 303 smtp_client_state(proto, STATE_AUTH); 304 break; 305 306 case STATE_AUTH: 307 if (!proto->params.auth_user) 308 smtp_client_state(proto, STATE_READY); 309 else if ((proto->flags & FLAG_TLS) == 0) 310 smtp_client_cancel(proto, FAIL_IMPL, 311 "Authentication requires TLS"); 312 else if ((proto->ext & SMTP_EXT_AUTH) == 0) 313 smtp_client_cancel(proto, FAIL_IMPL, 314 "AUTH not supported by remote host"); 315 else if (proto->ext & SMTP_EXT_AUTH_PLAIN) 316 smtp_client_state(proto, STATE_AUTH_PLAIN); 317 else if (proto->ext & SMTP_EXT_AUTH_LOGIN) 318 smtp_client_state(proto, STATE_AUTH_LOGIN); 319 else 320 smtp_client_cancel(proto, FAIL_IMPL, 321 "No supported AUTH method"); 322 break; 323 324 case STATE_AUTH_PLAIN: 325 (void)strlcpy(ibuf, "-", sizeof(ibuf)); 326 (void)strlcat(ibuf, proto->params.auth_user, sizeof(ibuf)); 327 if (strlcat(ibuf, ":", sizeof(ibuf)) >= sizeof(ibuf)) { 328 errno = EMSGSIZE; 329 smtp_client_cancel(proto, FAIL_INTERNAL, 330 "credentials too large"); 331 break; 332 } 333 n = strlcat(ibuf, proto->params.auth_pass, sizeof(ibuf)); 334 if (n >= sizeof(ibuf)) { 335 errno = EMSGSIZE; 336 smtp_client_cancel(proto, FAIL_INTERNAL, 337 "credentials too large"); 338 break; 339 } 340 *strchr(ibuf, ':') = '\0'; 341 ibuf[0] = '\0'; 342 if (base64_encode(ibuf, n, obuf, sizeof(obuf)) == -1) { 343 errno = EMSGSIZE; 344 smtp_client_cancel(proto, FAIL_INTERNAL, 345 "credentials too large"); 346 break; 347 } 348 smtp_client_sendcmd(proto, "AUTH PLAIN %s", obuf); 349 explicit_bzero(ibuf, sizeof ibuf); 350 explicit_bzero(obuf, sizeof obuf); 351 break; 352 353 case STATE_AUTH_LOGIN: 354 smtp_client_sendcmd(proto, "AUTH LOGIN"); 355 break; 356 357 case STATE_AUTH_LOGIN_USER: 358 if (base64_encode(proto->params.auth_user, 359 strlen(proto->params.auth_user), obuf, 360 sizeof(obuf)) == -1) { 361 errno = EMSGSIZE; 362 smtp_client_cancel(proto, FAIL_INTERNAL, 363 "credentials too large"); 364 break; 365 } 366 smtp_client_sendcmd(proto, "%s", obuf); 367 explicit_bzero(obuf, sizeof obuf); 368 break; 369 370 case STATE_AUTH_LOGIN_PASS: 371 if (base64_encode(proto->params.auth_pass, 372 strlen(proto->params.auth_pass), obuf, 373 sizeof(obuf)) == -1) { 374 errno = EMSGSIZE; 375 smtp_client_cancel(proto, FAIL_INTERNAL, 376 "credentials too large"); 377 break; 378 } 379 smtp_client_sendcmd(proto, "%s", obuf); 380 explicit_bzero(obuf, sizeof obuf); 381 break; 382 383 case STATE_READY: 384 smtp_ready(proto->tag, proto); 385 break; 386 387 case STATE_MAIL: 388 if (proto->ext & SMTP_EXT_DSN) 389 smtp_client_sendcmd(proto, "MAIL FROM:<%s>%s%s%s%s", 390 proto->mail->from, 391 proto->mail->dsn_ret ? " RET=" : "", 392 proto->mail->dsn_ret ? proto->mail->dsn_ret : "", 393 proto->mail->dsn_envid ? " ENVID=" : "", 394 proto->mail->dsn_envid ? proto->mail->dsn_envid : ""); 395 else 396 smtp_client_sendcmd(proto, "MAIL FROM:<%s>", 397 proto->mail->from); 398 break; 399 400 case STATE_RCPT: 401 if (proto->rcptidx == proto->mail->rcptcount) { 402 smtp_client_state(proto, STATE_DATA); 403 break; 404 } 405 rcpt = &proto->mail->rcpt[proto->rcptidx]; 406 if (proto->ext & SMTP_EXT_DSN) 407 smtp_client_sendcmd(proto, "RCPT TO:<%s>%s%s%s%s", 408 rcpt->to, 409 rcpt->dsn_notify ? " NOTIFY=" : "", 410 rcpt->dsn_notify ? rcpt->dsn_notify : "", 411 rcpt->dsn_orcpt ? " ORCPT=" : "", 412 rcpt->dsn_orcpt ? rcpt->dsn_orcpt : ""); 413 else 414 smtp_client_sendcmd(proto, "RCPT TO:<%s>", rcpt->to); 415 break; 416 417 case STATE_DATA: 418 if (proto->rcptok == 0) { 419 smtp_client_mail_abort(proto); 420 smtp_client_state(proto, STATE_RSET); 421 } 422 else 423 smtp_client_sendcmd(proto, "DATA"); 424 break; 425 426 case STATE_BODY: 427 fseek(proto->mail->fp, 0, SEEK_SET); 428 smtp_client_sendbody(proto); 429 break; 430 431 case STATE_EOM: 432 smtp_client_sendcmd(proto, "."); 433 break; 434 435 case STATE_RSET: 436 smtp_client_sendcmd(proto, "RSET"); 437 break; 438 439 case STATE_QUIT: 440 smtp_client_sendcmd(proto, "QUIT"); 441 break; 442 443 default: 444 fatalx("%s: bad state %d", __func__, proto->state); 445 } 446 #undef smtp_client_state 447 } 448 449 /* 450 * Handle a response to an SMTP command 451 */ 452 static void 453 smtp_client_response(struct smtp_client *proto, const char *line) 454 { 455 struct smtp_rcpt *rcpt; 456 int i, seen; 457 458 switch (proto->state) { 459 case STATE_BANNER: 460 if (line[0] != '2') 461 smtp_client_abort(proto, FAIL_RESP, line); 462 else if (proto->params.lmtp) 463 smtp_client_state(proto, STATE_LHLO); 464 else 465 smtp_client_state(proto, STATE_EHLO); 466 break; 467 468 case STATE_EHLO: 469 if (line[0] != '2') { 470 /* 471 * Either rejected or not implemented. If we want to 472 * use EHLO extensions, report an SMTP error. 473 * Otherwise, fallback to using HELO. 474 */ 475 if ((proto->params.tls_req == TLS_FORCE) || 476 (proto->params.auth_user)) 477 smtp_client_cancel(proto, FAIL_RESP, line); 478 else 479 smtp_client_state(proto, STATE_HELO); 480 break; 481 } 482 smtp_client_state(proto, STATE_STARTTLS); 483 break; 484 485 case STATE_HELO: 486 if (line[0] != '2') 487 smtp_client_cancel(proto, FAIL_RESP, line); 488 else 489 smtp_client_state(proto, STATE_READY); 490 break; 491 492 case STATE_LHLO: 493 if (line[0] != '2') 494 smtp_client_cancel(proto, FAIL_RESP, line); 495 else 496 smtp_client_state(proto, STATE_READY); 497 break; 498 499 case STATE_STARTTLS: 500 if (line[0] != '2') { 501 if ((proto->params.tls_req == TLS_FORCE) || 502 (proto->params.auth_user)) { 503 smtp_client_cancel(proto, FAIL_RESP, line); 504 break; 505 } 506 smtp_client_state(proto, STATE_AUTH); 507 } 508 else 509 smtp_require_tls(proto->tag, proto); 510 break; 511 512 case STATE_AUTH_PLAIN: 513 if (line[0] != '2') 514 smtp_client_cancel(proto, FAIL_RESP, line); 515 else 516 smtp_client_state(proto, STATE_READY); 517 break; 518 519 case STATE_AUTH_LOGIN: 520 if (strncmp(line, "334 ", 4)) 521 smtp_client_cancel(proto, FAIL_RESP, line); 522 else 523 smtp_client_state(proto, STATE_AUTH_LOGIN_USER); 524 break; 525 526 case STATE_AUTH_LOGIN_USER: 527 if (strncmp(line, "334 ", 4)) 528 smtp_client_cancel(proto, FAIL_RESP, line); 529 else 530 smtp_client_state(proto, STATE_AUTH_LOGIN_PASS); 531 break; 532 533 case STATE_AUTH_LOGIN_PASS: 534 if (line[0] != '2') 535 smtp_client_cancel(proto, FAIL_RESP, line); 536 else 537 smtp_client_state(proto, STATE_READY); 538 break; 539 540 case STATE_MAIL: 541 if (line[0] != '2') { 542 smtp_client_mail_status(proto, line); 543 smtp_client_state(proto, STATE_RSET); 544 } 545 else 546 smtp_client_state(proto, STATE_RCPT); 547 break; 548 549 case STATE_RCPT: 550 rcpt = &proto->mail->rcpt[proto->rcptidx++]; 551 if (line[0] != '2') 552 smtp_client_rcpt_status(proto, rcpt, line); 553 else { 554 proto->rcptok++; 555 smtp_client_state(proto, STATE_RCPT); 556 } 557 break; 558 559 case STATE_DATA: 560 if (line[0] != '2' && line[0] != '3') { 561 smtp_client_mail_status(proto, line); 562 smtp_client_state(proto, STATE_RSET); 563 } 564 else 565 smtp_client_state(proto, STATE_BODY); 566 break; 567 568 case STATE_EOM: 569 if (proto->params.lmtp) { 570 /* 571 * LMTP reports a status of each accepted RCPT. 572 * Report status for the first pending RCPT and read 573 * more lines if another rcpt needs a status. 574 */ 575 for (i = 0, seen = 0; i < proto->mail->rcptcount; i++) { 576 rcpt = &proto->mail->rcpt[i]; 577 if (rcpt->done) 578 continue; 579 if (seen) { 580 io_set_read(proto->io); 581 return; 582 } 583 smtp_client_rcpt_status(proto, 584 &proto->mail->rcpt[i], line); 585 seen = 1; 586 } 587 } 588 smtp_client_mail_status(proto, line); 589 smtp_client_state(proto, STATE_READY); 590 break; 591 592 case STATE_RSET: 593 if (line[0] != '2') 594 smtp_client_cancel(proto, FAIL_RESP, line); 595 else 596 smtp_client_state(proto, STATE_READY); 597 break; 598 599 case STATE_QUIT: 600 smtp_client_free(proto); 601 break; 602 603 default: 604 fatalx("%s: bad state %d", __func__, proto->state); 605 } 606 } 607 608 static void 609 smtp_client_io(struct io *io, int evt, void *arg) 610 { 611 struct smtp_client *proto = arg; 612 613 log_trace(TRACE_IO, "%p: %s %s", proto, io_strevent(evt), io_strio(io)); 614 615 switch (evt) { 616 case IO_CONNECTED: 617 if (proto->params.tls_req == TLS_SMTPS) { 618 io_set_write(io); 619 smtp_require_tls(proto->tag, proto); 620 } 621 else 622 smtp_client_state(proto, STATE_BANNER); 623 break; 624 625 case IO_TLSREADY: 626 proto->flags |= FLAG_TLS; 627 io_pause(proto->io, IO_IN); 628 smtp_verify_server_cert(proto->tag, proto, io_tls(proto->io)); 629 break; 630 631 case IO_DATAIN: 632 while (smtp_client_readline(proto)) 633 ; 634 break; 635 636 case IO_LOWAT: 637 if (proto->state == STATE_BODY) 638 smtp_client_sendbody(proto); 639 else 640 io_set_read(io); 641 break; 642 643 case IO_TIMEOUT: 644 errno = ETIMEDOUT; 645 smtp_client_abort(proto, FAIL_CONN, "Connection timeout"); 646 break; 647 648 case IO_ERROR: 649 smtp_client_abort(proto, FAIL_CONN, io_error(io)); 650 break; 651 652 case IO_TLSERROR: 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