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