usersmtp.c revision 43730
1/* 2 * Copyright (c) 1998 Sendmail, Inc. All rights reserved. 3 * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. 4 * Copyright (c) 1988, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * By using this file, you agree to the terms and conditions set 8 * forth in the LICENSE file which can be found at the top level of 9 * the sendmail distribution. 10 * 11 */ 12 13# include "sendmail.h" 14 15#ifndef lint 16#if SMTP 17static char sccsid[] = "@(#)usersmtp.c 8.111 (Berkeley) 2/3/1999 (with SMTP)"; 18#else 19static char sccsid[] = "@(#)usersmtp.c 8.111 (Berkeley) 2/3/1999 (without SMTP)"; 20#endif 21#endif /* not lint */ 22 23# include <sysexits.h> 24# include <errno.h> 25 26# if SMTP 27 28/* 29** USERSMTP -- run SMTP protocol from the user end. 30** 31** This protocol is described in RFC821. 32*/ 33 34#define REPLYTYPE(r) ((r) / 100) /* first digit of reply code */ 35#define REPLYCLASS(r) (((r) / 10) % 10) /* second digit of reply code */ 36#define SMTPCLOSING 421 /* "Service Shutting Down" */ 37 38char SmtpMsgBuffer[MAXLINE]; /* buffer for commands */ 39char SmtpReplyBuffer[MAXLINE]; /* buffer for replies */ 40char SmtpError[MAXLINE] = ""; /* save failure error messages */ 41bool SmtpNeedIntro; /* need "while talking" in transcript */ 42 43extern void smtpmessage __P((char *f, MAILER *m, MCI *mci, ...)); 44extern int reply __P((MAILER *, MCI *, ENVELOPE *, time_t, void (*)())); 45/* 46** SMTPINIT -- initialize SMTP. 47** 48** Opens the connection and sends the initial protocol. 49** 50** Parameters: 51** m -- mailer to create connection to. 52** pvp -- pointer to parameter vector to pass to 53** the mailer. 54** 55** Returns: 56** none. 57** 58** Side Effects: 59** creates connection and sends initial protocol. 60*/ 61 62void 63smtpinit(m, mci, e) 64 MAILER *m; 65 register MCI *mci; 66 ENVELOPE *e; 67{ 68 register int r; 69 register char *p; 70 extern void esmtp_check __P((char *, bool, MAILER *, MCI *, ENVELOPE *)); 71 extern void helo_options __P((char *, bool, MAILER *, MCI *, ENVELOPE *)); 72 73 if (tTd(18, 1)) 74 { 75 printf("smtpinit "); 76 mci_dump(mci, FALSE); 77 } 78 79 /* 80 ** Open the connection to the mailer. 81 */ 82 83 SmtpError[0] = '\0'; 84 CurHostName = mci->mci_host; /* XXX UGLY XXX */ 85 if (CurHostName == NULL) 86 CurHostName = MyHostName; 87 SmtpNeedIntro = TRUE; 88 switch (mci->mci_state) 89 { 90 case MCIS_ACTIVE: 91 /* need to clear old information */ 92 smtprset(m, mci, e); 93 /* fall through */ 94 95 case MCIS_OPEN: 96 return; 97 98 case MCIS_ERROR: 99 case MCIS_SSD: 100 /* shouldn't happen */ 101 smtpquit(m, mci, e); 102 /* fall through */ 103 104 case MCIS_CLOSED: 105 syserr("451 smtpinit: state CLOSED"); 106 return; 107 108 case MCIS_OPENING: 109 break; 110 } 111 112 mci->mci_state = MCIS_OPENING; 113 114 /* 115 ** Get the greeting message. 116 ** This should appear spontaneously. Give it five minutes to 117 ** happen. 118 */ 119 120 SmtpPhase = mci->mci_phase = "client greeting"; 121 sm_setproctitle(TRUE, "%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 122 r = reply(m, mci, e, TimeOuts.to_initial, esmtp_check); 123 if (r < 0) 124 goto tempfail1; 125 if (REPLYTYPE(r) == 4) 126 goto tempfail2; 127 if (REPLYTYPE(r) != 2) 128 goto unavailable; 129 130 /* 131 ** Send the HELO command. 132 ** My mother taught me to always introduce myself. 133 */ 134 135 if (bitnset(M_ESMTP, m->m_flags) || bitnset(M_LMTP, m->m_flags)) 136 mci->mci_flags |= MCIF_ESMTP; 137 138tryhelo: 139 if (bitnset(M_LMTP, m->m_flags)) 140 { 141 smtpmessage("LHLO %s", m, mci, MyHostName); 142 SmtpPhase = mci->mci_phase = "client LHLO"; 143 } 144 else if (bitset(MCIF_ESMTP, mci->mci_flags)) 145 { 146 smtpmessage("EHLO %s", m, mci, MyHostName); 147 SmtpPhase = mci->mci_phase = "client EHLO"; 148 } 149 else 150 { 151 smtpmessage("HELO %s", m, mci, MyHostName); 152 SmtpPhase = mci->mci_phase = "client HELO"; 153 } 154 sm_setproctitle(TRUE, "%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 155 r = reply(m, mci, e, TimeOuts.to_helo, helo_options); 156 if (r < 0) 157 goto tempfail1; 158 else if (REPLYTYPE(r) == 5) 159 { 160 if (bitset(MCIF_ESMTP, mci->mci_flags) && 161 !bitnset(M_LMTP, m->m_flags)) 162 { 163 /* try old SMTP instead */ 164 mci->mci_flags &= ~MCIF_ESMTP; 165 goto tryhelo; 166 } 167 goto unavailable; 168 } 169 else if (REPLYTYPE(r) != 2) 170 goto tempfail2; 171 172 /* 173 ** Check to see if we actually ended up talking to ourself. 174 ** This means we didn't know about an alias or MX, or we managed 175 ** to connect to an echo server. 176 */ 177 178 p = strchr(&SmtpReplyBuffer[4], ' '); 179 if (p != NULL) 180 *p = '\0'; 181 if (!bitnset(M_NOLOOPCHECK, m->m_flags) && 182 !bitnset(M_LMTP, m->m_flags) && 183 strcasecmp(&SmtpReplyBuffer[4], MyHostName) == 0) 184 { 185 syserr("553 %s config error: mail loops back to me (MX problem?)", 186 CurHostName); 187 mci_setstat(mci, EX_CONFIG, NULL, NULL); 188 mci->mci_errno = 0; 189 smtpquit(m, mci, e); 190 return; 191 } 192 193 /* 194 ** If this is expected to be another sendmail, send some internal 195 ** commands. 196 */ 197 198 if (bitnset(M_INTERNAL, m->m_flags)) 199 { 200 /* tell it to be verbose */ 201 smtpmessage("VERB", m, mci); 202 r = reply(m, mci, e, TimeOuts.to_miscshort, NULL); 203 if (r < 0) 204 goto tempfail1; 205 } 206 207 if (mci->mci_state != MCIS_CLOSED) 208 { 209 mci->mci_state = MCIS_OPEN; 210 return; 211 } 212 213 /* got a 421 error code during startup */ 214 215 tempfail1: 216 if (mci->mci_errno == 0) 217 mci->mci_errno = errno; 218 mci_setstat(mci, EX_TEMPFAIL, "4.4.2", NULL); 219 if (mci->mci_state != MCIS_CLOSED) 220 smtpquit(m, mci, e); 221 return; 222 223 tempfail2: 224 if (mci->mci_errno == 0) 225 mci->mci_errno = errno; 226 /* XXX should use code from other end iff ENHANCEDSTATUSCODES */ 227 mci_setstat(mci, EX_TEMPFAIL, "4.5.0", SmtpReplyBuffer); 228 if (mci->mci_state != MCIS_CLOSED) 229 smtpquit(m, mci, e); 230 return; 231 232 unavailable: 233 mci->mci_errno = errno; 234 mci_setstat(mci, EX_UNAVAILABLE, "5.5.0", SmtpReplyBuffer); 235 smtpquit(m, mci, e); 236 return; 237} 238/* 239** ESMTP_CHECK -- check to see if this implementation likes ESMTP protocol 240** 241** Parameters: 242** line -- the response line. 243** firstline -- set if this is the first line of the reply. 244** m -- the mailer. 245** mci -- the mailer connection info. 246** e -- the envelope. 247** 248** Returns: 249** none. 250*/ 251 252void 253esmtp_check(line, firstline, m, mci, e) 254 char *line; 255 bool firstline; 256 MAILER *m; 257 register MCI *mci; 258 ENVELOPE *e; 259{ 260 if (strstr(line, "ESMTP") != NULL) 261 mci->mci_flags |= MCIF_ESMTP; 262 if (strstr(line, "8BIT-OK") != NULL) 263 mci->mci_flags |= MCIF_8BITOK; 264} 265/* 266** HELO_OPTIONS -- process the options on a HELO line. 267** 268** Parameters: 269** line -- the response line. 270** firstline -- set if this is the first line of the reply. 271** m -- the mailer. 272** mci -- the mailer connection info. 273** e -- the envelope. 274** 275** Returns: 276** none. 277*/ 278 279void 280helo_options(line, firstline, m, mci, e) 281 char *line; 282 bool firstline; 283 MAILER *m; 284 register MCI *mci; 285 ENVELOPE *e; 286{ 287 register char *p; 288 289 if (firstline) 290 return; 291 292 if (strlen(line) < (SIZE_T) 5) 293 return; 294 line += 4; 295 p = strchr(line, ' '); 296 if (p != NULL) 297 *p++ = '\0'; 298 if (strcasecmp(line, "size") == 0) 299 { 300 mci->mci_flags |= MCIF_SIZE; 301 if (p != NULL) 302 mci->mci_maxsize = atol(p); 303 } 304 else if (strcasecmp(line, "8bitmime") == 0) 305 { 306 mci->mci_flags |= MCIF_8BITMIME; 307 mci->mci_flags &= ~MCIF_7BIT; 308 } 309 else if (strcasecmp(line, "expn") == 0) 310 mci->mci_flags |= MCIF_EXPN; 311 else if (strcasecmp(line, "dsn") == 0) 312 mci->mci_flags |= MCIF_DSN; 313} 314/* 315** SMTPMAILFROM -- send MAIL command 316** 317** Parameters: 318** m -- the mailer. 319** mci -- the mailer connection structure. 320** e -- the envelope (including the sender to specify). 321*/ 322 323int 324smtpmailfrom(m, mci, e) 325 MAILER *m; 326 MCI *mci; 327 ENVELOPE *e; 328{ 329 int r; 330 char *bufp; 331 char *bodytype; 332 char buf[MAXNAME + 1]; 333 char optbuf[MAXLINE]; 334 335 if (tTd(18, 2)) 336 printf("smtpmailfrom: CurHost=%s\n", CurHostName); 337 338 /* set up appropriate options to include */ 339 bufp = optbuf; 340 if (bitset(MCIF_SIZE, mci->mci_flags) && e->e_msgsize > 0) 341 snprintf(optbuf, sizeof optbuf, " SIZE=%ld", e->e_msgsize); 342 else 343 strcpy(optbuf, ""); 344 bufp = &optbuf[strlen(optbuf)]; 345 346 bodytype = e->e_bodytype; 347 if (bitset(MCIF_8BITMIME, mci->mci_flags)) 348 { 349 if (bodytype == NULL && 350 bitset(MM_MIME8BIT, MimeMode) && 351 bitset(EF_HAS8BIT, e->e_flags) && 352 !bitset(EF_DONT_MIME, e->e_flags) && 353 !bitnset(M_8BITS, m->m_flags)) 354 bodytype = "8BITMIME"; 355 if (bodytype != NULL && 356 SPACELEFT(optbuf, bufp) > strlen(bodytype) + 7) 357 { 358 snprintf(bufp, SPACELEFT(optbuf, bufp), 359 " BODY=%s", bodytype); 360 bufp += strlen(bufp); 361 } 362 } 363 else if (bitnset(M_8BITS, m->m_flags) || 364 !bitset(EF_HAS8BIT, e->e_flags) || 365 bitset(MCIF_8BITOK, mci->mci_flags)) 366 { 367 /* just pass it through */ 368 } 369#if MIME8TO7 370 else if (bitset(MM_CVTMIME, MimeMode) && 371 !bitset(EF_DONT_MIME, e->e_flags) && 372 (!bitset(MM_PASS8BIT, MimeMode) || 373 bitset(EF_IS_MIME, e->e_flags))) 374 { 375 /* must convert from 8bit MIME format to 7bit encoded */ 376 mci->mci_flags |= MCIF_CVT8TO7; 377 } 378#endif 379 else if (!bitset(MM_PASS8BIT, MimeMode)) 380 { 381 /* cannot just send a 8-bit version */ 382 extern char MsgBuf[]; 383 384 usrerr("%s does not support 8BITMIME", CurHostName); 385 mci_setstat(mci, EX_NOTSTICKY, "5.6.3", MsgBuf); 386 return EX_DATAERR; 387 } 388 389 if (bitset(MCIF_DSN, mci->mci_flags)) 390 { 391 if (e->e_envid != NULL && 392 SPACELEFT(optbuf, bufp) > strlen(e->e_envid) + 7) 393 { 394 snprintf(bufp, SPACELEFT(optbuf, bufp), 395 " ENVID=%s", e->e_envid); 396 bufp += strlen(bufp); 397 } 398 399 /* RET= parameter */ 400 if (bitset(EF_RET_PARAM, e->e_flags) && 401 SPACELEFT(optbuf, bufp) > 9) 402 { 403 snprintf(bufp, SPACELEFT(optbuf, bufp), 404 " RET=%s", 405 bitset(EF_NO_BODY_RETN, e->e_flags) ? 406 "HDRS" : "FULL"); 407 bufp += strlen(bufp); 408 } 409 } 410 411 /* 412 ** Send the MAIL command. 413 ** Designates the sender. 414 */ 415 416 mci->mci_state = MCIS_ACTIVE; 417 418 if (bitset(EF_RESPONSE, e->e_flags) && 419 !bitnset(M_NO_NULL_FROM, m->m_flags)) 420 (void) strcpy(buf, ""); 421 else 422 expand("\201g", buf, sizeof buf, e); 423 if (buf[0] == '<') 424 { 425 /* strip off <angle brackets> (put back on below) */ 426 bufp = &buf[strlen(buf) - 1]; 427 if (*bufp == '>') 428 *bufp = '\0'; 429 bufp = &buf[1]; 430 } 431 else 432 bufp = buf; 433 if (bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) || 434 !bitnset(M_FROMPATH, m->m_flags)) 435 { 436 smtpmessage("MAIL From:<%s>%s", m, mci, bufp, optbuf); 437 } 438 else 439 { 440 smtpmessage("MAIL From:<@%s%c%s>%s", m, mci, MyHostName, 441 *bufp == '@' ? ',' : ':', bufp, optbuf); 442 } 443 SmtpPhase = mci->mci_phase = "client MAIL"; 444 sm_setproctitle(TRUE, "%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 445 r = reply(m, mci, e, TimeOuts.to_mail, NULL); 446 if (r < 0) 447 { 448 /* communications failure */ 449 mci->mci_errno = errno; 450 mci_setstat(mci, EX_TEMPFAIL, "4.4.2", NULL); 451 smtpquit(m, mci, e); 452 return EX_TEMPFAIL; 453 } 454 else if (r == 421) 455 { 456 /* service shutting down */ 457 mci_setstat(mci, EX_TEMPFAIL, "4.5.0", SmtpReplyBuffer); 458 smtpquit(m, mci, e); 459 return EX_TEMPFAIL; 460 } 461 else if (REPLYTYPE(r) == 4) 462 { 463 mci_setstat(mci, EX_NOTSTICKY, smtptodsn(r), SmtpReplyBuffer); 464 return EX_TEMPFAIL; 465 } 466 else if (REPLYTYPE(r) == 2) 467 { 468 return EX_OK; 469 } 470 else if (r == 501) 471 { 472 /* syntax error in arguments */ 473 mci_setstat(mci, EX_NOTSTICKY, "5.5.2", SmtpReplyBuffer); 474 return EX_DATAERR; 475 } 476 else if (r == 553) 477 { 478 /* mailbox name not allowed */ 479 mci_setstat(mci, EX_NOTSTICKY, "5.1.3", SmtpReplyBuffer); 480 return EX_DATAERR; 481 } 482 else if (r == 552) 483 { 484 /* exceeded storage allocation */ 485 mci_setstat(mci, EX_NOTSTICKY, "5.3.4", SmtpReplyBuffer); 486 if (bitset(MCIF_SIZE, mci->mci_flags)) 487 e->e_flags |= EF_NO_BODY_RETN; 488 return EX_UNAVAILABLE; 489 } 490 else if (REPLYTYPE(r) == 5) 491 { 492 /* unknown error */ 493 mci_setstat(mci, EX_NOTSTICKY, "5.0.0", SmtpReplyBuffer); 494 return EX_UNAVAILABLE; 495 } 496 497 if (LogLevel > 1) 498 { 499 sm_syslog(LOG_CRIT, e->e_id, 500 "%.100s: SMTP MAIL protocol error: %s", 501 CurHostName, 502 shortenstring(SmtpReplyBuffer, 403)); 503 } 504 505 /* protocol error -- close up */ 506 mci_setstat(mci, EX_PROTOCOL, "5.5.1", SmtpReplyBuffer); 507 smtpquit(m, mci, e); 508 return EX_PROTOCOL; 509} 510/* 511** SMTPRCPT -- designate recipient. 512** 513** Parameters: 514** to -- address of recipient. 515** m -- the mailer we are sending to. 516** mci -- the connection info for this transaction. 517** e -- the envelope for this transaction. 518** 519** Returns: 520** exit status corresponding to recipient status. 521** 522** Side Effects: 523** Sends the mail via SMTP. 524*/ 525 526int 527smtprcpt(to, m, mci, e) 528 ADDRESS *to; 529 register MAILER *m; 530 MCI *mci; 531 ENVELOPE *e; 532{ 533 register int r; 534 char *bufp; 535 char optbuf[MAXLINE]; 536 537 strcpy(optbuf, ""); 538 bufp = &optbuf[strlen(optbuf)]; 539 if (bitset(MCIF_DSN, mci->mci_flags)) 540 { 541 /* NOTIFY= parameter */ 542 if (bitset(QHASNOTIFY, to->q_flags) && 543 bitset(QPRIMARY, to->q_flags) && 544 !bitnset(M_LOCALMAILER, m->m_flags)) 545 { 546 bool firstone = TRUE; 547 548 strcat(bufp, " NOTIFY="); 549 if (bitset(QPINGONSUCCESS, to->q_flags)) 550 { 551 strcat(bufp, "SUCCESS"); 552 firstone = FALSE; 553 } 554 if (bitset(QPINGONFAILURE, to->q_flags)) 555 { 556 if (!firstone) 557 strcat(bufp, ","); 558 strcat(bufp, "FAILURE"); 559 firstone = FALSE; 560 } 561 if (bitset(QPINGONDELAY, to->q_flags)) 562 { 563 if (!firstone) 564 strcat(bufp, ","); 565 strcat(bufp, "DELAY"); 566 firstone = FALSE; 567 } 568 if (firstone) 569 strcat(bufp, "NEVER"); 570 bufp += strlen(bufp); 571 } 572 573 /* ORCPT= parameter */ 574 if (to->q_orcpt != NULL && 575 SPACELEFT(optbuf, bufp) > strlen(to->q_orcpt) + 7) 576 { 577 snprintf(bufp, SPACELEFT(optbuf, bufp), 578 " ORCPT=%s", to->q_orcpt); 579 bufp += strlen(bufp); 580 } 581 } 582 583 smtpmessage("RCPT To:<%s>%s", m, mci, to->q_user, optbuf); 584 585 SmtpPhase = mci->mci_phase = "client RCPT"; 586 sm_setproctitle(TRUE, "%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 587 r = reply(m, mci, e, TimeOuts.to_rcpt, NULL); 588 to->q_rstatus = newstr(SmtpReplyBuffer); 589 to->q_status = smtptodsn(r); 590 to->q_statmta = mci->mci_host; 591 if (r < 0 || REPLYTYPE(r) == 4) 592 return EX_TEMPFAIL; 593 else if (REPLYTYPE(r) == 2) 594 return EX_OK; 595 else if (r == 550) 596 { 597 to->q_status = "5.1.1"; 598 return EX_NOUSER; 599 } 600 else if (r == 551) 601 { 602 to->q_status = "5.1.6"; 603 return EX_NOUSER; 604 } 605 else if (r == 553) 606 { 607 to->q_status = "5.1.3"; 608 return EX_NOUSER; 609 } 610 else if (REPLYTYPE(r) == 5) 611 { 612 return EX_UNAVAILABLE; 613 } 614 615 if (LogLevel > 1) 616 { 617 sm_syslog(LOG_CRIT, e->e_id, 618 "%.100s: SMTP RCPT protocol error: %s", 619 CurHostName, 620 shortenstring(SmtpReplyBuffer, 403)); 621 } 622 623 mci_setstat(mci, EX_PROTOCOL, "5.5.1", SmtpReplyBuffer); 624 return EX_PROTOCOL; 625} 626/* 627** SMTPDATA -- send the data and clean up the transaction. 628** 629** Parameters: 630** m -- mailer being sent to. 631** mci -- the mailer connection information. 632** e -- the envelope for this message. 633** 634** Returns: 635** exit status corresponding to DATA command. 636** 637** Side Effects: 638** none. 639*/ 640 641static jmp_buf CtxDataTimeout; 642static void datatimeout __P((void)); 643 644int 645smtpdata(m, mci, e) 646 MAILER *m; 647 register MCI *mci; 648 register ENVELOPE *e; 649{ 650 register int r; 651 register EVENT *ev; 652 int rstat; 653 int xstat; 654 time_t timeout; 655 656 /* 657 ** Send the data. 658 ** First send the command and check that it is ok. 659 ** Then send the data. 660 ** Follow it up with a dot to terminate. 661 ** Finally get the results of the transaction. 662 */ 663 664 /* send the command and check ok to proceed */ 665 smtpmessage("DATA", m, mci); 666 SmtpPhase = mci->mci_phase = "client DATA 354"; 667 sm_setproctitle(TRUE, "%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 668 r = reply(m, mci, e, TimeOuts.to_datainit, NULL); 669 if (r < 0 || REPLYTYPE(r) == 4) 670 { 671 smtpquit(m, mci, e); 672 return EX_TEMPFAIL; 673 } 674 else if (REPLYTYPE(r) == 5) 675 { 676 smtprset(m, mci, e); 677 return EX_UNAVAILABLE; 678 } 679 else if (REPLYTYPE(r) != 3) 680 { 681 if (LogLevel > 1) 682 { 683 sm_syslog(LOG_CRIT, e->e_id, 684 "%.100s: SMTP DATA-1 protocol error: %s", 685 CurHostName, 686 shortenstring(SmtpReplyBuffer, 403)); 687 } 688 smtprset(m, mci, e); 689 mci_setstat(mci, EX_PROTOCOL, "5.5.1", SmtpReplyBuffer); 690 return (EX_PROTOCOL); 691 } 692 693 /* 694 ** Set timeout around data writes. Make it at least large 695 ** enough for DNS timeouts on all recipients plus some fudge 696 ** factor. The main thing is that it should not be infinite. 697 */ 698 699 if (setjmp(CtxDataTimeout) != 0) 700 { 701 mci->mci_errno = errno; 702 mci->mci_state = MCIS_ERROR; 703 mci_setstat(mci, EX_TEMPFAIL, "4.4.2", NULL); 704 syserr("451 timeout writing message to %s", CurHostName); 705 smtpquit(m, mci, e); 706 return EX_TEMPFAIL; 707 } 708 709 timeout = e->e_msgsize / 16; 710 if (timeout < (time_t) 600) 711 timeout = (time_t) 600; 712 timeout += e->e_nrcpts * 300; 713 ev = setevent(timeout, datatimeout, 0); 714 715 /* 716 ** Output the actual message. 717 */ 718 719 (*e->e_puthdr)(mci, e->e_header, e, M87F_OUTER); 720 (*e->e_putbody)(mci, e, NULL); 721 722 /* 723 ** Cleanup after sending message. 724 */ 725 726 clrevent(ev); 727 728 if (ferror(mci->mci_out)) 729 { 730 /* error during processing -- don't send the dot */ 731 mci->mci_errno = EIO; 732 mci->mci_state = MCIS_ERROR; 733 mci_setstat(mci, EX_IOERR, "4.4.2", NULL); 734 smtpquit(m, mci, e); 735 return EX_IOERR; 736 } 737 738 /* terminate the message */ 739 fprintf(mci->mci_out, ".%s", m->m_eol); 740 if (TrafficLogFile != NULL) 741 fprintf(TrafficLogFile, "%05d >>> .\n", (int) getpid()); 742 if (Verbose) 743 nmessage(">>> ."); 744 745 /* check for the results of the transaction */ 746 SmtpPhase = mci->mci_phase = "client DATA status"; 747 sm_setproctitle(TRUE, "%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 748 if (bitnset(M_LMTP, m->m_flags)) 749 return EX_OK; 750 r = reply(m, mci, e, TimeOuts.to_datafinal, NULL); 751 if (r < 0) 752 { 753 smtpquit(m, mci, e); 754 return EX_TEMPFAIL; 755 } 756 mci->mci_state = MCIS_OPEN; 757 xstat = EX_NOTSTICKY; 758 if (r == 452) 759 rstat = EX_TEMPFAIL; 760 else if (REPLYTYPE(r) == 4) 761 rstat = xstat = EX_TEMPFAIL; 762 else if (REPLYCLASS(r) != 5) 763 rstat = xstat = EX_PROTOCOL; 764 else if (REPLYTYPE(r) == 2) 765 rstat = xstat = EX_OK; 766 else if (REPLYTYPE(r) == 5) 767 rstat = EX_UNAVAILABLE; 768 else 769 rstat = EX_PROTOCOL; 770 mci_setstat(mci, xstat, smtptodsn(r), SmtpReplyBuffer); 771 if (e->e_statmsg != NULL) 772 free(e->e_statmsg); 773 e->e_statmsg = newstr(&SmtpReplyBuffer[4]); 774 if (rstat != EX_PROTOCOL) 775 return rstat; 776 if (LogLevel > 1) 777 { 778 sm_syslog(LOG_CRIT, e->e_id, 779 "%.100s: SMTP DATA-2 protocol error: %s", 780 CurHostName, 781 shortenstring(SmtpReplyBuffer, 403)); 782 } 783 return rstat; 784} 785 786 787static void 788datatimeout() 789{ 790 longjmp(CtxDataTimeout, 1); 791} 792/* 793** SMTPGETSTAT -- get status code from DATA in LMTP 794** 795** Parameters: 796** m -- the mailer to which we are sending the message. 797** mci -- the mailer connection structure. 798** e -- the current envelope. 799** 800** Returns: 801** The exit status corresponding to the reply code. 802*/ 803 804int 805smtpgetstat(m, mci, e) 806 MAILER *m; 807 MCI *mci; 808 ENVELOPE *e; 809{ 810 int r; 811 int stat; 812 813 /* check for the results of the transaction */ 814 r = reply(m, mci, e, TimeOuts.to_datafinal, NULL); 815 if (r < 0) 816 { 817 smtpquit(m, mci, e); 818 return EX_TEMPFAIL; 819 } 820 if (e->e_statmsg != NULL) 821 free(e->e_statmsg); 822 e->e_statmsg = newstr(&SmtpReplyBuffer[4]); 823 if (REPLYTYPE(r) == 4) 824 stat = EX_TEMPFAIL; 825 else if (REPLYCLASS(r) != 5) 826 stat = EX_PROTOCOL; 827 else if (REPLYTYPE(r) == 2) 828 stat = EX_OK; 829 else if (REPLYTYPE(r) == 5) 830 stat = EX_UNAVAILABLE; 831 else 832 stat = EX_PROTOCOL; 833 mci_setstat(mci, stat, smtptodsn(r), SmtpReplyBuffer); 834 if (LogLevel > 1 && stat == EX_PROTOCOL) 835 { 836 sm_syslog(LOG_CRIT, e->e_id, 837 "%.100s: SMTP DATA-3 protocol error: %s", 838 CurHostName, 839 shortenstring(SmtpReplyBuffer, 403)); 840 } 841 return stat; 842} 843/* 844** SMTPQUIT -- close the SMTP connection. 845** 846** Parameters: 847** m -- a pointer to the mailer. 848** mci -- the mailer connection information. 849** e -- the current envelope. 850** 851** Returns: 852** none. 853** 854** Side Effects: 855** sends the final protocol and closes the connection. 856*/ 857 858void 859smtpquit(m, mci, e) 860 register MAILER *m; 861 register MCI *mci; 862 ENVELOPE *e; 863{ 864 bool oldSuprErrs = SuprErrs; 865 866 /* 867 ** Suppress errors here -- we may be processing a different 868 ** job when we do the quit connection, and we don't want the 869 ** new job to be penalized for something that isn't it's 870 ** problem. 871 */ 872 873 SuprErrs = TRUE; 874 875 /* send the quit message if we haven't gotten I/O error */ 876 if (mci->mci_state != MCIS_ERROR) 877 { 878 SmtpPhase = "client QUIT"; 879 smtpmessage("QUIT", m, mci); 880 (void) reply(m, mci, e, TimeOuts.to_quit, NULL); 881 SuprErrs = oldSuprErrs; 882 if (mci->mci_state == MCIS_CLOSED) 883 return; 884 } 885 886 /* now actually close the connection and pick up the zombie */ 887 (void) endmailer(mci, e, NULL); 888 889 SuprErrs = oldSuprErrs; 890} 891/* 892** SMTPRSET -- send a RSET (reset) command 893*/ 894 895void 896smtprset(m, mci, e) 897 register MAILER *m; 898 register MCI *mci; 899 ENVELOPE *e; 900{ 901 int r; 902 903 SmtpPhase = "client RSET"; 904 smtpmessage("RSET", m, mci); 905 r = reply(m, mci, e, TimeOuts.to_rset, NULL); 906 if (r < 0) 907 mci->mci_state = MCIS_ERROR; 908 else if (REPLYTYPE(r) == 2) 909 { 910 mci->mci_state = MCIS_OPEN; 911 return; 912 } 913 smtpquit(m, mci, e); 914} 915/* 916** SMTPPROBE -- check the connection state 917*/ 918 919int 920smtpprobe(mci) 921 register MCI *mci; 922{ 923 int r; 924 MAILER *m = mci->mci_mailer; 925 extern ENVELOPE BlankEnvelope; 926 ENVELOPE *e = &BlankEnvelope; 927 928 SmtpPhase = "client probe"; 929 smtpmessage("RSET", m, mci); 930 r = reply(m, mci, e, TimeOuts.to_miscshort, NULL); 931 if (r < 0 || REPLYTYPE(r) != 2) 932 smtpquit(m, mci, e); 933 return r; 934} 935/* 936** REPLY -- read arpanet reply 937** 938** Parameters: 939** m -- the mailer we are reading the reply from. 940** mci -- the mailer connection info structure. 941** e -- the current envelope. 942** timeout -- the timeout for reads. 943** pfunc -- processing function called on each line of response. 944** If null, no special processing is done. 945** 946** Returns: 947** reply code it reads. 948** 949** Side Effects: 950** flushes the mail file. 951*/ 952 953int 954reply(m, mci, e, timeout, pfunc) 955 MAILER *m; 956 MCI *mci; 957 ENVELOPE *e; 958 time_t timeout; 959 void (*pfunc)(); 960{ 961 register char *bufp; 962 register int r; 963 bool firstline = TRUE; 964 char junkbuf[MAXLINE]; 965 966 if (mci->mci_out != NULL) 967 (void) fflush(mci->mci_out); 968 969 if (tTd(18, 1)) 970 printf("reply\n"); 971 972 /* 973 ** Read the input line, being careful not to hang. 974 */ 975 976 bufp = SmtpReplyBuffer; 977 for (;;) 978 { 979 register char *p; 980 981 /* actually do the read */ 982 if (e->e_xfp != NULL) 983 (void) fflush(e->e_xfp); /* for debugging */ 984 985 /* if we are in the process of closing just give the code */ 986 if (mci->mci_state == MCIS_CLOSED) 987 return (SMTPCLOSING); 988 989 if (mci->mci_out != NULL) 990 fflush(mci->mci_out); 991 992 /* get the line from the other side */ 993 p = sfgets(bufp, MAXLINE, mci->mci_in, timeout, SmtpPhase); 994 mci->mci_lastuse = curtime(); 995 996 if (p == NULL) 997 { 998 bool oldholderrs; 999 extern char MsgBuf[]; 1000 1001 /* if the remote end closed early, fake an error */ 1002 if (errno == 0) 1003# ifdef ECONNRESET 1004 errno = ECONNRESET; 1005# else /* ECONNRESET */ 1006 errno = EPIPE; 1007# endif /* ECONNRESET */ 1008 1009 mci->mci_errno = errno; 1010 oldholderrs = HoldErrs; 1011 HoldErrs = TRUE; 1012 usrerr("451 reply: read error from %s", CurHostName); 1013 1014 /* errors on QUIT should not be persistent */ 1015 if (strncmp(SmtpMsgBuffer, "QUIT", 4) != 0) 1016 mci_setstat(mci, EX_TEMPFAIL, "4.4.2", MsgBuf); 1017 1018 /* if debugging, pause so we can see state */ 1019 if (tTd(18, 100)) 1020 pause(); 1021 mci->mci_state = MCIS_ERROR; 1022 smtpquit(m, mci, e); 1023#if XDEBUG 1024 { 1025 char wbuf[MAXLINE]; 1026 char *p = wbuf; 1027 int wbufleft = sizeof wbuf; 1028 1029 if (e->e_to != NULL) 1030 { 1031 int plen; 1032 1033 snprintf(p, wbufleft, "%s... ", 1034 shortenstring(e->e_to, MAXSHORTSTR)); 1035 plen = strlen(p); 1036 p += plen; 1037 wbufleft -= plen; 1038 } 1039 snprintf(p, wbufleft, "reply(%.100s) during %s", 1040 CurHostName == NULL ? "NO_HOST" : CurHostName, 1041 SmtpPhase); 1042 checkfd012(wbuf); 1043 } 1044#endif 1045 HoldErrs = oldholderrs; 1046 return (-1); 1047 } 1048 fixcrlf(bufp, TRUE); 1049 1050 /* EHLO failure is not a real error */ 1051 if (e->e_xfp != NULL && (bufp[0] == '4' || 1052 (bufp[0] == '5' && strncmp(SmtpMsgBuffer, "EHLO", 4) != 0))) 1053 { 1054 /* serious error -- log the previous command */ 1055 if (SmtpNeedIntro) 1056 { 1057 /* inform user who we are chatting with */ 1058 fprintf(CurEnv->e_xfp, 1059 "... while talking to %s:\n", 1060 CurHostName); 1061 SmtpNeedIntro = FALSE; 1062 } 1063 if (SmtpMsgBuffer[0] != '\0') 1064 fprintf(e->e_xfp, ">>> %s\n", SmtpMsgBuffer); 1065 SmtpMsgBuffer[0] = '\0'; 1066 1067 /* now log the message as from the other side */ 1068 fprintf(e->e_xfp, "<<< %s\n", bufp); 1069 } 1070 1071 /* display the input for verbose mode */ 1072 if (Verbose) 1073 nmessage("050 %s", bufp); 1074 1075 /* ignore improperly formated input */ 1076 if (!(isascii(bufp[0]) && isdigit(bufp[0])) || 1077 !(isascii(bufp[1]) && isdigit(bufp[1])) || 1078 !(isascii(bufp[2]) && isdigit(bufp[2])) || 1079 !(bufp[3] == ' ' || bufp[3] == '-' || bufp[3] == '\0')) 1080 continue; 1081 1082 /* process the line */ 1083 if (pfunc != NULL) 1084 (*pfunc)(bufp, firstline, m, mci, e); 1085 1086 firstline = FALSE; 1087 1088 /* decode the reply code */ 1089 r = atoi(bufp); 1090 1091 /* extra semantics: 0xx codes are "informational" */ 1092 if (r < 100) 1093 continue; 1094 1095 /* if no continuation lines, return this line */ 1096 if (bufp[3] != '-') 1097 break; 1098 1099 /* first line of real reply -- ignore rest */ 1100 bufp = junkbuf; 1101 } 1102 1103 /* 1104 ** Now look at SmtpReplyBuffer -- only care about the first 1105 ** line of the response from here on out. 1106 */ 1107 1108 /* save temporary failure messages for posterity */ 1109 if (SmtpReplyBuffer[0] == '4' && 1110 (bitnset(M_LMTP, m->m_flags) || SmtpError[0] == '\0')) 1111 snprintf(SmtpError, sizeof SmtpError, "%s", SmtpReplyBuffer); 1112 1113 /* reply code 421 is "Service Shutting Down" */ 1114 if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD) 1115 { 1116 /* send the quit protocol */ 1117 mci->mci_state = MCIS_SSD; 1118 smtpquit(m, mci, e); 1119 } 1120 1121 return (r); 1122} 1123/* 1124** SMTPMESSAGE -- send message to server 1125** 1126** Parameters: 1127** f -- format 1128** m -- the mailer to control formatting. 1129** a, b, c -- parameters 1130** 1131** Returns: 1132** none. 1133** 1134** Side Effects: 1135** writes message to mci->mci_out. 1136*/ 1137 1138/*VARARGS1*/ 1139void 1140#ifdef __STDC__ 1141smtpmessage(char *f, MAILER *m, MCI *mci, ...) 1142#else 1143smtpmessage(f, m, mci, va_alist) 1144 char *f; 1145 MAILER *m; 1146 MCI *mci; 1147 va_dcl 1148#endif 1149{ 1150 VA_LOCAL_DECL 1151 1152 VA_START(mci); 1153 (void) vsnprintf(SmtpMsgBuffer, sizeof SmtpMsgBuffer, f, ap); 1154 VA_END; 1155 1156 if (tTd(18, 1) || Verbose) 1157 nmessage(">>> %s", SmtpMsgBuffer); 1158 if (TrafficLogFile != NULL) 1159 fprintf(TrafficLogFile, "%05d >>> %s\n", 1160 (int) getpid(), SmtpMsgBuffer); 1161 if (mci->mci_out != NULL) 1162 { 1163 fprintf(mci->mci_out, "%s%s", SmtpMsgBuffer, 1164 m == NULL ? "\r\n" : m->m_eol); 1165 } 1166 else if (tTd(18, 1)) 1167 { 1168 printf("smtpmessage: NULL mci_out\n"); 1169 } 1170} 1171 1172# endif /* SMTP */ 1173