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