1 /* $OpenBSD: smtp_client.c,v 1.15 2021/03/05 12:37:32 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_connect_tls(proto->io, ctx, proto->params.tls_servname); 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 if (proto->state == STATE_INIT) 628 smtp_client_state(proto, STATE_BANNER); 629 else { 630 /* Clear extensions before re-issueing an EHLO command. */ 631 proto->ext = 0; 632 smtp_client_state(proto, STATE_EHLO); 633 } 634 break; 635 636 case IO_DATAIN: 637 while (smtp_client_readline(proto)) 638 ; 639 break; 640 641 case IO_LOWAT: 642 if (proto->state == STATE_BODY) 643 smtp_client_sendbody(proto); 644 else 645 io_set_read(io); 646 break; 647 648 case IO_TIMEOUT: 649 errno = ETIMEDOUT; 650 smtp_client_abort(proto, FAIL_CONN, "Connection timeout"); 651 break; 652 653 case IO_ERROR: 654 smtp_client_abort(proto, FAIL_CONN, io_error(io)); 655 break; 656 657 case IO_DISCONNECTED: 658 smtp_client_abort(proto, FAIL_CONN, io_error(io)); 659 break; 660 661 default: 662 fatalx("%s: bad event %d", __func__, evt); 663 } 664 } 665 666 /* 667 * return 1 if a new line is expected. 668 */ 669 static int 670 smtp_client_readline(struct smtp_client *proto) 671 { 672 const char *e; 673 size_t len; 674 char *line, *msg, *p; 675 int cont; 676 677 line = io_getline(proto->io, &len); 678 if (line == NULL) { 679 if (io_datalen(proto->io) >= proto->params.linemax) 680 smtp_client_abort(proto, FAIL_PROTO, "Line too long"); 681 return 0; 682 } 683 684 /* Strip trailing '\r' */ 685 if (len && line[len - 1] == '\r') 686 line[--len] = '\0'; 687 688 log_trace(TRACE_SMTPCLT, "%p: <<< %s", proto, line); 689 690 /* Validate SMTP */ 691 if (len > 3) { 692 msg = line + 4; 693 cont = (line[3] == '-'); 694 } else if (len == 3) { 695 msg = line + 3; 696 cont = 0; 697 } else { 698 smtp_client_abort(proto, FAIL_PROTO, "Response too short"); 699 return 0; 700 } 701 702 /* Validate reply code. */ 703 if (line[0] < '2' || line[0] > '5' || !isdigit((unsigned char)line[1]) || 704 !isdigit((unsigned char)line[2])) { 705 smtp_client_abort(proto, FAIL_PROTO, "Invalid reply code"); 706 return 0; 707 } 708 709 /* Validate reply message. */ 710 for (p = msg; *p; p++) 711 if (!isprint((unsigned char)*p)) { 712 smtp_client_abort(proto, FAIL_PROTO, 713 "Non-printable characters in response"); 714 return 0; 715 } 716 717 /* Read extensions. */ 718 if (proto->state == STATE_EHLO) { 719 if (strcmp(msg, "STARTTLS") == 0) 720 proto->ext |= SMTP_EXT_STARTTLS; 721 else if (strncmp(msg, "AUTH ", 5) == 0) { 722 proto->ext |= SMTP_EXT_AUTH; 723 if ((p = strstr(msg, " PLAIN")) && 724 (*(p+6) == '\0' || *(p+6) == ' ')) 725 proto->ext |= SMTP_EXT_AUTH_PLAIN; 726 if ((p = strstr(msg, " LOGIN")) && 727 (*(p+6) == '\0' || *(p+6) == ' ')) 728 proto->ext |= SMTP_EXT_AUTH_LOGIN; 729 } 730 else if (strcmp(msg, "PIPELINING") == 0) 731 proto->ext |= SMTP_EXT_PIPELINING; 732 else if (strcmp(msg, "DSN") == 0) 733 proto->ext |= SMTP_EXT_DSN; 734 else if (strncmp(msg, "SIZE ", 5) == 0) { 735 proto->ext_size = strtonum(msg + 5, 0, SIZE_T_MAX, &e); 736 if (e == NULL) 737 proto->ext |= SMTP_EXT_SIZE; 738 } 739 } 740 741 if (smtp_client_replycat(proto, line) == -1) { 742 smtp_client_abort(proto, FAIL_INTERNAL, NULL); 743 return 0; 744 } 745 746 if (cont) 747 return 1; 748 749 if (io_datalen(proto->io)) { 750 /* 751 * There should be no pending data after a response is read, 752 * except for the multiple status lines after a LMTP message. 753 * It can also happen with pipelineing, but we don't do that 754 * for now. 755 */ 756 if (!(proto->params.lmtp && proto->state == STATE_EOM)) { 757 smtp_client_abort(proto, FAIL_PROTO, "Trailing data"); 758 return 0; 759 } 760 } 761 762 io_set_write(proto->io); 763 smtp_client_response(proto, proto->reply); 764 return 0; 765 } 766 767 /* 768 * Concatenate the given response line. 769 */ 770 static int 771 smtp_client_replycat(struct smtp_client *proto, const char *line) 772 { 773 size_t len; 774 char *tmp; 775 int first; 776 777 if (proto->reply && proto->reply[0]) { 778 /* 779 * If the line is the continuation of an multi-line response, 780 * skip the status and ESC parts. First, skip the status, then 781 * skip the separator amd ESC if found. 782 */ 783 first = 0; 784 line += 3; 785 if (line[0]) { 786 line += 1; 787 if (isdigit((unsigned char)line[0]) && line[1] == '.' && 788 isdigit((unsigned char)line[2]) && line[3] == '.' && 789 isdigit((unsigned char)line[4]) && 790 isspace((unsigned char)line[5])) 791 line += 5; 792 } 793 } else 794 first = 1; 795 796 if (proto->reply) { 797 len = strlcat(proto->reply, line, proto->replysz); 798 if (len < proto->replysz) 799 return 0; 800 } 801 else 802 len = strlen(line); 803 804 if (len > proto->params.ibufmax) { 805 errno = EMSGSIZE; 806 return -1; 807 } 808 809 /* Allocate by multiples of 2^8 */ 810 len += (len % 256) ? (256 - (len % 256)) : 0; 811 812 tmp = realloc(proto->reply, len); 813 if (tmp == NULL) 814 return -1; 815 if (proto->reply == NULL) 816 tmp[0] = '\0'; 817 818 proto->reply = tmp; 819 proto->replysz = len; 820 (void)strlcat(proto->reply, line, proto->replysz); 821 822 /* Replace the separator with a space for the first line. */ 823 if (first && proto->reply[3]) 824 proto->reply[3] = ' '; 825 826 return 0; 827 } 828 829 static void 830 smtp_client_sendbody(struct smtp_client *proto) 831 { 832 ssize_t len; 833 size_t sz = 0, total, w; 834 char *ln = NULL; 835 int n; 836 837 total = io_queued(proto->io); 838 w = 0; 839 840 while (total < proto->params.obufmax) { 841 if ((len = getline(&ln, &sz, proto->mail->fp)) == -1) 842 break; 843 if (ln[len - 1] == '\n') 844 ln[len - 1] = '\0'; 845 n = io_printf(proto->io, "%s%s\r\n", *ln == '.'?".":"", ln); 846 if (n == -1) { 847 free(ln); 848 smtp_client_abort(proto, FAIL_INTERNAL, NULL); 849 return; 850 } 851 total += n; 852 w += n; 853 } 854 free(ln); 855 856 if (ferror(proto->mail->fp)) { 857 smtp_client_abort(proto, FAIL_INTERNAL, "Cannot read message"); 858 return; 859 } 860 861 log_trace(TRACE_SMTPCLT, "%p: >>> [...%zd bytes...]", proto, w); 862 863 if (feof(proto->mail->fp)) 864 smtp_client_state(proto, STATE_EOM); 865 } 866 867 static void 868 smtp_client_sendcmd(struct smtp_client *proto, char *fmt, ...) 869 { 870 va_list ap; 871 char *p; 872 int len; 873 874 va_start(ap, fmt); 875 len = vasprintf(&p, fmt, ap); 876 va_end(ap); 877 878 if (len == -1) { 879 smtp_client_abort(proto, FAIL_INTERNAL, NULL); 880 return; 881 } 882 883 log_trace(TRACE_SMTPCLT, "mta: %p: >>> %s", proto, p); 884 885 len = io_printf(proto->io, "%s\r\n", p); 886 free(p); 887 888 if (len == -1) 889 smtp_client_abort(proto, FAIL_INTERNAL, NULL); 890 } 891 892 static void 893 smtp_client_mail_status(struct smtp_client *proto, const char *status) 894 { 895 int i; 896 897 for (i = 0; i < proto->mail->rcptcount; i++) 898 smtp_client_rcpt_status(proto, &proto->mail->rcpt[i], status); 899 900 smtp_done(proto->tag, proto, proto->mail); 901 proto->mail = NULL; 902 } 903 904 static void 905 smtp_client_mail_abort(struct smtp_client *proto) 906 { 907 smtp_done(proto->tag, proto, proto->mail); 908 proto->mail = NULL; 909 } 910 911 static void 912 smtp_client_rcpt_status(struct smtp_client *proto, struct smtp_rcpt *rcpt, const char *line) 913 { 914 struct smtp_status status; 915 916 if (rcpt->done) 917 return; 918 919 rcpt->done = 1; 920 status.rcpt = rcpt; 921 status.cmd = strstate[proto->state]; 922 status.status = line; 923 smtp_status(proto->tag, proto, &status); 924 } 925