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