1 /* 2 * Copyright (c) 1983 Eric P. Allman 3 * Copyright (c) 1988 Regents of the University of California. 4 * 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 6.20 (Berkeley) 03/30/93 (with SMTP)"; 14 #else 15 static char sccsid[] = "@(#)usersmtp.c 6.20 (Berkeley) 03/30/93 (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 39 #ifdef __STDC__ 40 extern smtpmessage(char *f, MAILER *m, MCI *mci, ...); 41 #endif 42 /* 43 ** SMTPINIT -- initialize SMTP. 44 ** 45 ** Opens the connection and sends the initial protocol. 46 ** 47 ** Parameters: 48 ** m -- mailer to create connection to. 49 ** pvp -- pointer to parameter vector to pass to 50 ** the mailer. 51 ** 52 ** Returns: 53 ** none. 54 ** 55 ** Side Effects: 56 ** creates connection and sends initial protocol. 57 */ 58 59 smtpinit(m, mci, e) 60 struct mailer *m; 61 register MCI *mci; 62 ENVELOPE *e; 63 { 64 register int r; 65 EVENT *gte; 66 extern STAB *stab(); 67 68 if (tTd(17, 1)) 69 { 70 printf("smtpinit "); 71 mci_dump(mci); 72 } 73 74 /* 75 ** Open the connection to the mailer. 76 */ 77 78 SmtpError[0] = '\0'; 79 CurHostName = mci->mci_host; /* XXX UGLY XXX */ 80 switch (mci->mci_state) 81 { 82 case MCIS_ACTIVE: 83 /* need to clear old information */ 84 smtprset(m, mci, e); 85 /* fall through */ 86 87 case MCIS_OPEN: 88 return; 89 90 case MCIS_ERROR: 91 case MCIS_SSD: 92 /* shouldn't happen */ 93 smtpquit(m, mci, e); 94 /* fall through */ 95 96 case MCIS_CLOSED: 97 syserr("451 smtpinit: state CLOSED"); 98 return; 99 100 case MCIS_OPENING: 101 break; 102 } 103 104 SmtpPhase = mci->mci_phase = "user open"; 105 mci->mci_state = MCIS_OPENING; 106 107 /* 108 ** Get the greeting message. 109 ** This should appear spontaneously. Give it five minutes to 110 ** happen. 111 */ 112 113 SmtpPhase = mci->mci_phase = "greeting wait"; 114 setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 115 r = reply(m, mci, e, TimeOuts.to_initial); 116 if (r < 0 || REPLYTYPE(r) != 2) 117 goto tempfail1; 118 119 /* 120 ** Send the HELO command. 121 ** My mother taught me to always introduce myself. 122 */ 123 124 smtpmessage("HELO %s", m, mci, MyHostName); 125 SmtpPhase = mci->mci_phase = "HELO wait"; 126 setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 127 r = reply(m, mci, e, TimeOuts.to_helo); 128 if (r < 0) 129 goto tempfail1; 130 else if (REPLYTYPE(r) == 5) 131 goto unavailable; 132 else if (REPLYTYPE(r) != 2) 133 goto tempfail1; 134 135 /* 136 ** If this is expected to be another sendmail, send some internal 137 ** commands. 138 */ 139 140 if (bitnset(M_INTERNAL, m->m_flags)) 141 { 142 /* tell it to be verbose */ 143 smtpmessage("VERB", m, mci); 144 r = reply(m, mci, e, TimeOuts.to_miscshort); 145 if (r < 0) 146 goto tempfail2; 147 } 148 149 mci->mci_state = MCIS_OPEN; 150 return; 151 152 tempfail1: 153 tempfail2: 154 mci->mci_exitstat = EX_TEMPFAIL; 155 if (mci->mci_errno == 0) 156 mci->mci_errno = errno; 157 if (mci->mci_state != MCIS_CLOSED) 158 smtpquit(m, mci, e); 159 return; 160 161 unavailable: 162 mci->mci_exitstat = EX_UNAVAILABLE; 163 mci->mci_errno = errno; 164 smtpquit(m, mci, e); 165 return; 166 } 167 168 smtpmailfrom(m, mci, e) 169 struct mailer *m; 170 MCI *mci; 171 ENVELOPE *e; 172 { 173 int r; 174 char buf[MAXNAME]; 175 176 if (tTd(17, 2)) 177 printf("smtpmailfrom: CurHost=%s\n", CurHostName); 178 179 /* 180 ** Send the MAIL command. 181 ** Designates the sender. 182 */ 183 184 mci->mci_state = MCIS_ACTIVE; 185 186 if (bitset(EF_RESPONSE, e->e_flags)) 187 (void) strcpy(buf, ""); 188 else 189 expand("\201g", buf, &buf[sizeof buf - 1], e); 190 if (e->e_from.q_mailer == LocalMailer || 191 !bitnset(M_FROMPATH, m->m_flags)) 192 { 193 smtpmessage("MAIL From:<%s>", m, mci, buf); 194 } 195 else 196 { 197 smtpmessage("MAIL From:<@%s%c%s>", m, mci, MyHostName, 198 buf[0] == '@' ? ',' : ':', buf); 199 } 200 SmtpPhase = mci->mci_phase = "MAIL wait"; 201 setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 202 r = reply(m, mci, e, TimeOuts.to_mail); 203 if (r < 0 || REPLYTYPE(r) == 4) 204 { 205 mci->mci_exitstat = EX_TEMPFAIL; 206 mci->mci_errno = errno; 207 smtpquit(m, mci, e); 208 return EX_TEMPFAIL; 209 } 210 else if (r == 250) 211 { 212 mci->mci_exitstat = EX_OK; 213 return EX_OK; 214 } 215 else if (r == 552) 216 { 217 /* signal service unavailable */ 218 mci->mci_exitstat = EX_UNAVAILABLE; 219 smtpquit(m, mci, e); 220 return EX_UNAVAILABLE; 221 } 222 223 #ifdef LOG 224 if (LogLevel > 1) 225 { 226 syslog(LOG_CRIT, "%s: SMTP MAIL protocol error: %s", 227 e->e_id, SmtpReplyBuffer); 228 } 229 #endif 230 231 /* protocol error -- close up */ 232 smtpquit(m, mci, e); 233 mci->mci_exitstat = EX_PROTOCOL; 234 return EX_PROTOCOL; 235 } 236 /* 237 ** SMTPRCPT -- designate recipient. 238 ** 239 ** Parameters: 240 ** to -- address of recipient. 241 ** m -- the mailer we are sending to. 242 ** mci -- the connection info for this transaction. 243 ** e -- the envelope for this transaction. 244 ** 245 ** Returns: 246 ** exit status corresponding to recipient status. 247 ** 248 ** Side Effects: 249 ** Sends the mail via SMTP. 250 */ 251 252 smtprcpt(to, m, mci, e) 253 ADDRESS *to; 254 register MAILER *m; 255 MCI *mci; 256 ENVELOPE *e; 257 { 258 register int r; 259 260 smtpmessage("RCPT To:<%s>", m, mci, to->q_user); 261 262 SmtpPhase = mci->mci_phase = "RCPT wait"; 263 setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 264 r = reply(m, mci, e, TimeOuts.to_rcpt); 265 if (r < 0 || REPLYTYPE(r) == 4) 266 return (EX_TEMPFAIL); 267 else if (REPLYTYPE(r) == 2) 268 return (EX_OK); 269 else if (r == 550 || r == 551 || r == 553) 270 return (EX_NOUSER); 271 else if (r == 552 || r == 554) 272 return (EX_UNAVAILABLE); 273 274 #ifdef LOG 275 if (LogLevel > 1) 276 { 277 syslog(LOG_CRIT, "%s: SMTP RCPT protocol error: %s", 278 e->e_id, SmtpReplyBuffer); 279 } 280 #endif 281 282 return (EX_PROTOCOL); 283 } 284 /* 285 ** SMTPDATA -- send the data and clean up the transaction. 286 ** 287 ** Parameters: 288 ** m -- mailer being sent to. 289 ** e -- the envelope for this message. 290 ** 291 ** Returns: 292 ** exit status corresponding to DATA command. 293 ** 294 ** Side Effects: 295 ** none. 296 */ 297 298 smtpdata(m, mci, e) 299 struct mailer *m; 300 register MCI *mci; 301 register ENVELOPE *e; 302 { 303 register int r; 304 305 /* 306 ** Send the data. 307 ** First send the command and check that it is ok. 308 ** Then send the data. 309 ** Follow it up with a dot to terminate. 310 ** Finally get the results of the transaction. 311 */ 312 313 /* send the command and check ok to proceed */ 314 smtpmessage("DATA", m, mci); 315 SmtpPhase = mci->mci_phase = "DATA wait"; 316 setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 317 r = reply(m, mci, e, TimeOuts.to_datainit); 318 if (r < 0 || REPLYTYPE(r) == 4) 319 { 320 smtpquit(m, mci, e); 321 return (EX_TEMPFAIL); 322 } 323 else if (r == 554) 324 { 325 smtprset(m, mci, e); 326 return (EX_UNAVAILABLE); 327 } 328 else if (r != 354) 329 { 330 #ifdef LOG 331 if (LogLevel > 1) 332 { 333 syslog(LOG_CRIT, "%s: SMTP DATA-1 protocol error: %s", 334 e->e_id, SmtpReplyBuffer); 335 } 336 #endif 337 smtprset(m, mci, e); 338 return (EX_PROTOCOL); 339 } 340 341 /* now output the actual message */ 342 (*e->e_puthdr)(mci->mci_out, m, e); 343 putline("\n", mci->mci_out, m); 344 (*e->e_putbody)(mci->mci_out, m, e); 345 346 /* terminate the message */ 347 fprintf(mci->mci_out, ".%s", m->m_eol); 348 if (Verbose) 349 nmessage(">>> ."); 350 351 /* check for the results of the transaction */ 352 SmtpPhase = mci->mci_phase = "result wait"; 353 setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 354 r = reply(m, mci, e, TimeOuts.to_datafinal); 355 if (r < 0) 356 { 357 smtpquit(m, mci, e); 358 return (EX_TEMPFAIL); 359 } 360 mci->mci_state = MCIS_OPEN; 361 e->e_message = newstr(&SmtpReplyBuffer[4]); 362 if (REPLYTYPE(r) == 4) 363 return (EX_TEMPFAIL); 364 else if (r == 250) 365 return (EX_OK); 366 else if (r == 552 || r == 554) 367 return (EX_UNAVAILABLE); 368 #ifdef LOG 369 if (LogLevel > 1) 370 { 371 syslog(LOG_CRIT, "%s: SMTP DATA-2 protocol error: %s", 372 e->e_id, SmtpReplyBuffer); 373 } 374 #endif 375 return (EX_PROTOCOL); 376 } 377 /* 378 ** SMTPQUIT -- close the SMTP connection. 379 ** 380 ** Parameters: 381 ** m -- a pointer to the mailer. 382 ** 383 ** Returns: 384 ** none. 385 ** 386 ** Side Effects: 387 ** sends the final protocol and closes the connection. 388 */ 389 390 smtpquit(m, mci, e) 391 register MAILER *m; 392 register MCI *mci; 393 ENVELOPE *e; 394 { 395 int i; 396 397 /* send the quit message if we haven't gotten I/O error */ 398 if (mci->mci_state != MCIS_ERROR) 399 { 400 smtpmessage("QUIT", m, mci); 401 (void) reply(m, mci, e, TimeOuts.to_quit); 402 if (mci->mci_state == MCIS_CLOSED) 403 return; 404 } 405 406 /* now actually close the connection and pick up the zombie */ 407 i = endmailer(mci, e, m->m_argv); 408 if (i != EX_OK) 409 syserr("451 smtpquit %s: stat %d", m->m_argv[0], i); 410 } 411 /* 412 ** SMTPRSET -- send a RSET (reset) command 413 */ 414 415 smtprset(m, mci, e) 416 register MAILER *m; 417 register MCI *mci; 418 ENVELOPE *e; 419 { 420 int r; 421 422 smtpmessage("RSET", m, mci); 423 r = reply(m, mci, e, TimeOuts.to_rset); 424 if (r < 0) 425 mci->mci_state = MCIS_ERROR; 426 else if (REPLYTYPE(r) == 2) 427 { 428 mci->mci_state = MCIS_OPEN; 429 return; 430 } 431 smtpquit(m, mci, e); 432 } 433 /* 434 ** SMTPPROBE -- check the connection state 435 */ 436 437 smtpprobe(mci) 438 register MCI *mci; 439 { 440 int r; 441 MAILER *m = mci->mci_mailer; 442 extern ENVELOPE BlankEnvelope; 443 ENVELOPE *e = &BlankEnvelope; 444 445 smtpmessage("RSET", m, mci); 446 r = reply(m, mci, e, TimeOuts.to_miscshort); 447 if (r < 0 || REPLYTYPE(r) != 2) 448 smtpquit(m, mci, e); 449 return r; 450 } 451 /* 452 ** REPLY -- read arpanet reply 453 ** 454 ** Parameters: 455 ** m -- the mailer we are reading the reply from. 456 ** mci -- the mailer connection info structure. 457 ** e -- the current envelope. 458 ** timeout -- the timeout for reads. 459 ** 460 ** Returns: 461 ** reply code it reads. 462 ** 463 ** Side Effects: 464 ** flushes the mail file. 465 */ 466 467 reply(m, mci, e, timeout) 468 MAILER *m; 469 MCI *mci; 470 ENVELOPE *e; 471 { 472 if (mci->mci_out != NULL) 473 (void) fflush(mci->mci_out); 474 475 if (tTd(18, 1)) 476 printf("reply\n"); 477 478 /* 479 ** Read the input line, being careful not to hang. 480 */ 481 482 for (;;) 483 { 484 register int r; 485 register char *p; 486 extern time_t curtime(); 487 488 /* actually do the read */ 489 if (e->e_xfp != NULL) 490 (void) fflush(e->e_xfp); /* for debugging */ 491 492 /* if we are in the process of closing just give the code */ 493 if (mci->mci_state == MCIS_CLOSED) 494 return (SMTPCLOSING); 495 496 if (mci->mci_out != NULL) 497 fflush(mci->mci_out); 498 499 /* get the line from the other side */ 500 p = sfgets(SmtpReplyBuffer, sizeof SmtpReplyBuffer, mci->mci_in, 501 timeout); 502 mci->mci_lastuse = curtime(); 503 504 if (p == NULL) 505 { 506 extern char MsgBuf[]; /* err.c */ 507 508 /* if the remote end closed early, fake an error */ 509 if (errno == 0) 510 # ifdef ECONNRESET 511 errno = ECONNRESET; 512 # else /* ECONNRESET */ 513 errno = EPIPE; 514 # endif /* ECONNRESET */ 515 516 mci->mci_errno = errno; 517 mci->mci_exitstat = EX_TEMPFAIL; 518 message("451 %s: reply: read error from %s", 519 e->e_id == NULL ? "NOQUEUE" : e->e_id, 520 mci->mci_host); 521 /* if debugging, pause so we can see state */ 522 if (tTd(18, 100)) 523 pause(); 524 # ifdef LOG 525 if (LogLevel > 1) 526 syslog(LOG_INFO, "%s", &MsgBuf[4]); 527 # endif /* LOG */ 528 mci->mci_state = MCIS_ERROR; 529 smtpquit(m, mci, e); 530 return (-1); 531 } 532 fixcrlf(SmtpReplyBuffer, TRUE); 533 534 if (e->e_xfp != NULL && strchr("45", SmtpReplyBuffer[0]) != NULL) 535 { 536 /* serious error -- log the previous command */ 537 if (SmtpMsgBuffer[0] != '\0') 538 fprintf(e->e_xfp, ">>> %s\n", SmtpMsgBuffer); 539 SmtpMsgBuffer[0] = '\0'; 540 541 /* now log the message as from the other side */ 542 fprintf(e->e_xfp, "<<< %s\n", SmtpReplyBuffer); 543 } 544 545 /* display the input for verbose mode */ 546 if (Verbose) 547 nmessage("%s", SmtpReplyBuffer); 548 549 /* if continuation is required, we can go on */ 550 if (SmtpReplyBuffer[3] == '-' || 551 !(isascii(SmtpReplyBuffer[0]) && isdigit(SmtpReplyBuffer[0]))) 552 continue; 553 554 /* decode the reply code */ 555 r = atoi(SmtpReplyBuffer); 556 557 /* extra semantics: 0xx codes are "informational" */ 558 if (r < 100) 559 continue; 560 561 /* save temporary failure messages for posterity */ 562 if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0') 563 (void) strcpy(SmtpError, SmtpReplyBuffer); 564 565 /* reply code 421 is "Service Shutting Down" */ 566 if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD) 567 { 568 /* send the quit protocol */ 569 mci->mci_state = MCIS_SSD; 570 smtpquit(m, mci, e); 571 } 572 573 return (r); 574 } 575 } 576 /* 577 ** SMTPMESSAGE -- send message to server 578 ** 579 ** Parameters: 580 ** f -- format 581 ** m -- the mailer to control formatting. 582 ** a, b, c -- parameters 583 ** 584 ** Returns: 585 ** none. 586 ** 587 ** Side Effects: 588 ** writes message to mci->mci_out. 589 */ 590 591 /*VARARGS1*/ 592 #ifdef __STDC__ 593 smtpmessage(char *f, MAILER *m, MCI *mci, ...) 594 #else 595 smtpmessage(f, m, mci, va_alist) 596 char *f; 597 MAILER *m; 598 MCI *mci; 599 va_dcl 600 #endif 601 { 602 VA_LOCAL_DECL 603 604 VA_START(mci); 605 (void) vsprintf(SmtpMsgBuffer, f, ap); 606 VA_END; 607 608 if (tTd(18, 1) || Verbose) 609 nmessage(">>> %s", SmtpMsgBuffer); 610 if (mci->mci_out != NULL) 611 { 612 fprintf(mci->mci_out, "%s%s", SmtpMsgBuffer, 613 m == NULL ? "\r\n" : m->m_eol); 614 } 615 else 616 { 617 syserr("smtpmessage: NULL mci_out"); 618 } 619 } 620 621 # endif /* SMTP */ 622