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