122716Sdist /* 234921Sbostic * Copyright (c) 1983 Eric P. Allman 333731Sbostic * Copyright (c) 1988 Regents of the University of California. 433731Sbostic * All rights reserved. 533731Sbostic * 642831Sbostic * %sccs.include.redist.c% 733731Sbostic */ 822716Sdist 933731Sbostic # include "sendmail.h" 1022716Sdist 1133731Sbostic #ifndef lint 1233731Sbostic #ifdef SMTP 13*52676Seric static char sccsid[] = "@(#)usersmtp.c 5.20.1.1 (Berkeley) 02/26/92 (with SMTP)"; 1433731Sbostic #else 15*52676Seric static char sccsid[] = "@(#)usersmtp.c 5.20.1.1 (Berkeley) 02/26/92 (without SMTP)"; 1633731Sbostic #endif 1733731Sbostic #endif /* not lint */ 1833731Sbostic 194684Seric # include <sysexits.h> 2021065Seric # include <errno.h> 214684Seric 2233731Sbostic # ifdef SMTP 234684Seric 244684Seric /* 259391Seric ** USERSMTP -- run SMTP protocol from the user end. 269391Seric ** 279391Seric ** This protocol is described in RFC821. 289391Seric */ 299391Seric 309391Seric #define REPLYTYPE(r) ((r) / 100) /* first digit of reply code */ 319391Seric #define REPLYCLASS(r) (((r) / 10) % 10) /* second digit of reply code */ 329391Seric #define SMTPCLOSING 421 /* "Service Shutting Down" */ 339391Seric 3414900Seric char SmtpMsgBuffer[MAXLINE]; /* buffer for commands */ 3510054Seric char SmtpReplyBuffer[MAXLINE]; /* buffer for replies */ 3621065Seric char SmtpError[MAXLINE] = ""; /* save failure error messages */ 3710054Seric int SmtpPid; /* pid of mailer */ 3811159Seric 3911159Seric /* following represents the state of the SMTP connection */ 4011159Seric int SmtpState; /* connection state, see below */ 4111159Seric 4211159Seric #define SMTP_CLOSED 0 /* connection is closed */ 4311159Seric #define SMTP_OPEN 1 /* connection is open for business */ 4411159Seric #define SMTP_SSD 2 /* service shutting down */ 459391Seric /* 464865Seric ** SMTPINIT -- initialize SMTP. 474684Seric ** 484865Seric ** Opens the connection and sends the initial protocol. 494684Seric ** 504684Seric ** Parameters: 514865Seric ** m -- mailer to create connection to. 524865Seric ** pvp -- pointer to parameter vector to pass to 534865Seric ** the mailer. 544684Seric ** 554684Seric ** Returns: 564865Seric ** appropriate exit status -- EX_OK on success. 5714913Seric ** If not EX_OK, it should close the connection. 584684Seric ** 594684Seric ** Side Effects: 604865Seric ** creates connection and sends initial protocol. 614684Seric */ 624684Seric 6314886Seric jmp_buf CtxGreeting; 6414886Seric 6510175Seric smtpinit(m, pvp) 664865Seric struct mailer *m; 674865Seric char **pvp; 684684Seric { 694865Seric register int r; 7014886Seric EVENT *gte; 7152107Seric register STAB *st; 724865Seric char buf[MAXNAME]; 7346928Sbostic static int greettimeout(); 7452107Seric extern STAB *stab(); 754684Seric 764865Seric /* 774865Seric ** Open the connection to the mailer. 784865Seric */ 794684Seric 8011159Seric if (SmtpState == SMTP_OPEN) 8111159Seric syserr("smtpinit: already open"); 8211159Seric 8311159Seric SmtpState = SMTP_CLOSED; 8421065Seric SmtpError[0] = '\0'; 8524944Seric SmtpPhase = "user open"; 8636584Sbostic setproctitle("%s %s: %s", CurEnv->e_id, pvp[1], SmtpPhase); 87*52676Seric mci = openmailer(m, pvp, (ADDRESS *) NULL, TRUE); 886051Seric if (SmtpPid < 0) 896051Seric { 907677Seric if (tTd(18, 1)) 919391Seric printf("smtpinit: cannot open %s: stat %d errno %d\n", 929391Seric pvp[0], ExitStat, errno); 9315139Seric if (CurEnv->e_xfp != NULL) 9415139Seric { 9521065Seric register char *p; 9615139Seric extern char *errstring(); 9721065Seric extern char *statstring(); 9815139Seric 9921065Seric if (errno == 0) 10021065Seric { 10121065Seric p = statstring(ExitStat); 10221065Seric fprintf(CurEnv->e_xfp, 10321065Seric "%.3s %s.%s... %s\n", 10421065Seric p, pvp[1], m->m_name, p); 10521065Seric } 10621065Seric else 10721065Seric { 10836584Sbostic r = errno; 10921065Seric fprintf(CurEnv->e_xfp, 11021065Seric "421 %s.%s... Deferred: %s\n", 11121065Seric pvp[1], m->m_name, errstring(errno)); 11236584Sbostic errno = r; 11321065Seric } 11415139Seric } 1156051Seric return (ExitStat); 1166051Seric } 11711159Seric SmtpState = SMTP_OPEN; 1184796Seric 1194865Seric /* 1204865Seric ** Get the greeting message. 12114913Seric ** This should appear spontaneously. Give it five minutes to 12214886Seric ** happen. 1234865Seric */ 1244797Seric 12514886Seric if (setjmp(CtxGreeting) != 0) 12652104Seric goto tempfail1; 12716141Seric gte = setevent((time_t) 300, greettimeout, 0); 12824944Seric SmtpPhase = "greeting wait"; 12936584Sbostic setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase); 13010175Seric r = reply(m); 13114886Seric clrevent(gte); 1328005Seric if (r < 0 || REPLYTYPE(r) != 2) 13352104Seric goto tempfail1; 1344684Seric 1354865Seric /* 1364976Seric ** Send the HELO command. 1377963Seric ** My mother taught me to always introduce myself. 1384976Seric */ 1394976Seric 14025050Seric smtpmessage("HELO %s", m, MyHostName); 14124944Seric SmtpPhase = "HELO wait"; 14236584Sbostic setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase); 14310175Seric r = reply(m); 1448005Seric if (r < 0) 14552104Seric goto tempfail1; 1468005Seric else if (REPLYTYPE(r) == 5) 14714913Seric goto unavailable; 1487963Seric else if (REPLYTYPE(r) != 2) 14952104Seric goto tempfail1; 1504976Seric 1514976Seric /* 1529315Seric ** If this is expected to be another sendmail, send some internal 1539315Seric ** commands. 1549315Seric */ 1559315Seric 15610688Seric if (bitnset(M_INTERNAL, m->m_flags)) 1579315Seric { 1589315Seric /* tell it to be verbose */ 15910175Seric smtpmessage("VERB", m); 16010175Seric r = reply(m); 1619315Seric if (r < 0) 16252104Seric goto tempfail2; 1639315Seric 1649315Seric /* tell it we will be sending one transaction only */ 16510175Seric smtpmessage("ONEX", m); 16610175Seric r = reply(m); 1679315Seric if (r < 0) 16852104Seric goto tempfail2; 1699315Seric } 1709315Seric 1719315Seric /* 1724865Seric ** Send the MAIL command. 1734865Seric ** Designates the sender. 1744865Seric */ 1754796Seric 17651951Seric expand("\001<", buf, &buf[sizeof buf - 1], CurEnv); 1778436Seric if (CurEnv->e_from.q_mailer == LocalMailer || 17810688Seric !bitnset(M_FROMPATH, m->m_flags)) 1798436Seric { 18010308Seric smtpmessage("MAIL From:<%s>", m, buf); 1818436Seric } 1828436Seric else 1838436Seric { 18425050Seric smtpmessage("MAIL From:<@%s%c%s>", m, MyHostName, 18510308Seric buf[0] == '@' ? ',' : ':', buf); 1868436Seric } 18724944Seric SmtpPhase = "MAIL wait"; 18836584Sbostic setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase); 18910175Seric r = reply(m); 1908005Seric if (r < 0 || REPLYTYPE(r) == 4) 19152104Seric goto tempfail2; 1927963Seric else if (r == 250) 1937963Seric return (EX_OK); 1947963Seric else if (r == 552) 19514913Seric goto unavailable; 19614913Seric 19714913Seric /* protocol error -- close up */ 19814913Seric smtpquit(m); 1997964Seric return (EX_PROTOCOL); 20014913Seric 20152104Seric tempfail1: 20252107Seric /* log this as an error to avoid sure-to-be-void connections */ 203*52676Seric st = stab(CurHostName, ST_MCONINFO + m->m_mno, ST_ENTER); 20452107Seric st->s_host.ho_exitstat = EX_TEMPFAIL; 20552107Seric st->s_host.ho_errno = errno; 20652104Seric 20752104Seric tempfail2: 20852107Seric /* signal a temporary failure */ 20914913Seric smtpquit(m); 21014913Seric return (EX_TEMPFAIL); 21114913Seric 21252107Seric unavailable: 21314913Seric /* signal service unavailable */ 21414913Seric smtpquit(m); 21514913Seric return (EX_UNAVAILABLE); 2164684Seric } 21714886Seric 21814886Seric 21914886Seric static 22014886Seric greettimeout() 22114886Seric { 22214886Seric /* timeout reading the greeting message */ 22314886Seric longjmp(CtxGreeting, 1); 22414886Seric } 2254684Seric /* 2264976Seric ** SMTPRCPT -- designate recipient. 2274797Seric ** 2284797Seric ** Parameters: 2294865Seric ** to -- address of recipient. 23010175Seric ** m -- the mailer we are sending to. 2314797Seric ** 2324797Seric ** Returns: 2334865Seric ** exit status corresponding to recipient status. 2344797Seric ** 2354797Seric ** Side Effects: 2364865Seric ** Sends the mail via SMTP. 2374797Seric */ 2384797Seric 23910175Seric smtprcpt(to, m) 2404865Seric ADDRESS *to; 24110175Seric register MAILER *m; 2424797Seric { 2434797Seric register int r; 24410308Seric extern char *remotename(); 2454797Seric 24652647Seric smtpmessage("RCPT To:<%s>", m, to->q_user); 2474865Seric 24824944Seric SmtpPhase = "RCPT wait"; 24936584Sbostic setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase); 25010175Seric r = reply(m); 2518005Seric if (r < 0 || REPLYTYPE(r) == 4) 2524865Seric return (EX_TEMPFAIL); 2537963Seric else if (REPLYTYPE(r) == 2) 2547963Seric return (EX_OK); 2557964Seric else if (r == 550 || r == 551 || r == 553) 2567964Seric return (EX_NOUSER); 2577964Seric else if (r == 552 || r == 554) 2587964Seric return (EX_UNAVAILABLE); 2597964Seric return (EX_PROTOCOL); 2604797Seric } 2614797Seric /* 26210175Seric ** SMTPDATA -- send the data and clean up the transaction. 2634684Seric ** 2644684Seric ** Parameters: 2654865Seric ** m -- mailer being sent to. 2666980Seric ** e -- the envelope for this message. 2674684Seric ** 2684684Seric ** Returns: 2694976Seric ** exit status corresponding to DATA command. 2704684Seric ** 2714684Seric ** Side Effects: 2724865Seric ** none. 2734684Seric */ 2744684Seric 27510175Seric smtpdata(m, e) 2764865Seric struct mailer *m; 2776980Seric register ENVELOPE *e; 2784684Seric { 2794684Seric register int r; 2804684Seric 2814797Seric /* 2824797Seric ** Send the data. 28310175Seric ** First send the command and check that it is ok. 28410175Seric ** Then send the data. 28510175Seric ** Follow it up with a dot to terminate. 28610175Seric ** Finally get the results of the transaction. 2874797Seric */ 2884797Seric 28910175Seric /* send the command and check ok to proceed */ 29010175Seric smtpmessage("DATA", m); 29124944Seric SmtpPhase = "DATA wait"; 29236584Sbostic setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase); 29310175Seric r = reply(m); 2948005Seric if (r < 0 || REPLYTYPE(r) == 4) 2954797Seric return (EX_TEMPFAIL); 2967963Seric else if (r == 554) 2977963Seric return (EX_UNAVAILABLE); 2987963Seric else if (r != 354) 2997964Seric return (EX_PROTOCOL); 30010175Seric 30110175Seric /* now output the actual message */ 30210175Seric (*e->e_puthdr)(SmtpOut, m, CurEnv); 30310175Seric putline("\n", SmtpOut, m); 30410175Seric (*e->e_putbody)(SmtpOut, m, CurEnv); 30510175Seric 30610175Seric /* terminate the message */ 30710328Seric fprintf(SmtpOut, ".%s", m->m_eol); 30810215Seric if (Verbose && !HoldErrs) 30910215Seric nmessage(Arpa_Info, ">>> ."); 31010175Seric 31110175Seric /* check for the results of the transaction */ 31224944Seric SmtpPhase = "result wait"; 31336584Sbostic setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase); 31410175Seric r = reply(m); 3158005Seric if (r < 0 || REPLYTYPE(r) == 4) 3164797Seric return (EX_TEMPFAIL); 3177963Seric else if (r == 250) 3187963Seric return (EX_OK); 3197963Seric else if (r == 552 || r == 554) 3207963Seric return (EX_UNAVAILABLE); 3217964Seric return (EX_PROTOCOL); 3224684Seric } 3234684Seric /* 3244865Seric ** SMTPQUIT -- close the SMTP connection. 3254865Seric ** 3264865Seric ** Parameters: 32715535Seric ** m -- a pointer to the mailer. 3284865Seric ** 3294865Seric ** Returns: 3304865Seric ** none. 3314865Seric ** 3324865Seric ** Side Effects: 3334865Seric ** sends the final protocol and closes the connection. 3344865Seric */ 3354865Seric 336*52676Seric smtpquit(mci) 337*52676Seric register MCONINFO *mci; 3384865Seric { 3399391Seric int i; 3404865Seric 34110148Seric /* if the connection is already closed, don't bother */ 342*52676Seric if (mci->mci_state == MCI_CLOSED) 34310148Seric return; 34410148Seric 34510148Seric /* send the quit message if not a forced quit */ 34611159Seric if (SmtpState == SMTP_OPEN || SmtpState == SMTP_SSD) 3479391Seric { 34810175Seric smtpmessage("QUIT", m); 34910175Seric (void) reply(m); 35011159Seric if (SmtpState == SMTP_CLOSED) 35110159Seric return; 3529391Seric } 3539391Seric 354*52676Seric /* now actually close the connection and pick up the zombie */ 355*52676Seric i = endmailer(mci, m->m_argv[0]); 3569391Seric if (i != EX_OK) 35715535Seric syserr("smtpquit %s: stat %d", m->m_argv[0], i); 3584865Seric } 3594865Seric /* 3604684Seric ** REPLY -- read arpanet reply 3614684Seric ** 3624684Seric ** Parameters: 36310175Seric ** m -- the mailer we are reading the reply from. 3644684Seric ** 3654684Seric ** Returns: 3664684Seric ** reply code it reads. 3674684Seric ** 3684684Seric ** Side Effects: 3694684Seric ** flushes the mail file. 3704684Seric */ 3714684Seric 37210175Seric reply(m) 37310688Seric MAILER *m; 3744684Seric { 3754865Seric (void) fflush(SmtpOut); 3764684Seric 3777677Seric if (tTd(18, 1)) 3784796Seric printf("reply\n"); 3794796Seric 3807356Seric /* 3817356Seric ** Read the input line, being careful not to hang. 3827356Seric */ 3837356Seric 3844684Seric for (;;) 3854684Seric { 3864684Seric register int r; 3877356Seric register char *p; 3884684Seric 3897685Seric /* actually do the read */ 3909547Seric if (CurEnv->e_xfp != NULL) 3919547Seric (void) fflush(CurEnv->e_xfp); /* for debugging */ 3927356Seric 39310054Seric /* if we are in the process of closing just give the code */ 39411159Seric if (SmtpState == SMTP_CLOSED) 39510054Seric return (SMTPCLOSING); 39610054Seric 39710054Seric /* get the line from the other side */ 39810054Seric p = sfgets(SmtpReplyBuffer, sizeof SmtpReplyBuffer, SmtpIn); 39910054Seric if (p == NULL) 40010131Seric { 40110148Seric extern char MsgBuf[]; /* err.c */ 40210148Seric extern char Arpa_TSyserr[]; /* conf.c */ 40310148Seric 40421065Seric /* if the remote end closed early, fake an error */ 40521065Seric if (errno == 0) 40621065Seric # ifdef ECONNRESET 40721065Seric errno = ECONNRESET; 40821065Seric # else ECONNRESET 40921065Seric errno = EPIPE; 41021065Seric # endif ECONNRESET 41121065Seric 41210148Seric message(Arpa_TSyserr, "reply: read error"); 41310420Seric /* if debugging, pause so we can see state */ 41410420Seric if (tTd(18, 100)) 41510420Seric pause(); 41610148Seric # ifdef LOG 41736234Skarels syslog(LOG_INFO, "%s", &MsgBuf[4]); 41810148Seric # endif LOG 41911159Seric SmtpState = SMTP_CLOSED; 42015535Seric smtpquit(m); 42110054Seric return (-1); 42210131Seric } 42310054Seric fixcrlf(SmtpReplyBuffer, TRUE); 42410054Seric 42514900Seric if (CurEnv->e_xfp != NULL && index("45", SmtpReplyBuffer[0]) != NULL) 42614900Seric { 42714900Seric /* serious error -- log the previous command */ 42814900Seric if (SmtpMsgBuffer[0] != '\0') 42914900Seric fprintf(CurEnv->e_xfp, ">>> %s\n", SmtpMsgBuffer); 43014900Seric SmtpMsgBuffer[0] = '\0'; 43114900Seric 43214900Seric /* now log the message as from the other side */ 43314900Seric fprintf(CurEnv->e_xfp, "<<< %s\n", SmtpReplyBuffer); 43414900Seric } 43514900Seric 43614900Seric /* display the input for verbose mode */ 4377229Seric if (Verbose && !HoldErrs) 4389391Seric nmessage(Arpa_Info, "%s", SmtpReplyBuffer); 4397356Seric 4407356Seric /* if continuation is required, we can go on */ 4419391Seric if (SmtpReplyBuffer[3] == '-' || !isdigit(SmtpReplyBuffer[0])) 4424684Seric continue; 4437356Seric 4447356Seric /* decode the reply code */ 4459391Seric r = atoi(SmtpReplyBuffer); 4467356Seric 4477356Seric /* extra semantics: 0xx codes are "informational" */ 4484684Seric if (r < 100) 4494684Seric continue; 4507356Seric 4519391Seric /* reply code 421 is "Service Shutting Down" */ 45211159Seric if (r == SMTPCLOSING && SmtpState != SMTP_SSD) 4539391Seric { 45410054Seric /* send the quit protocol */ 45511159Seric SmtpState = SMTP_SSD; 45615535Seric smtpquit(m); 4579391Seric } 4589391Seric 45921065Seric /* save temporary failure messages for posterity */ 46021065Seric if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0') 46121065Seric (void) strcpy(SmtpError, &SmtpReplyBuffer[4]); 46221065Seric 4634684Seric return (r); 4644684Seric } 4654684Seric } 4664796Seric /* 4674865Seric ** SMTPMESSAGE -- send message to server 4684796Seric ** 4694796Seric ** Parameters: 4704796Seric ** f -- format 47110175Seric ** m -- the mailer to control formatting. 4724796Seric ** a, b, c -- parameters 4734796Seric ** 4744796Seric ** Returns: 4754796Seric ** none. 4764796Seric ** 4774796Seric ** Side Effects: 4784865Seric ** writes message to SmtpOut. 4794796Seric */ 4804796Seric 4814865Seric /*VARARGS1*/ 48210175Seric smtpmessage(f, m, a, b, c) 4834796Seric char *f; 48410175Seric MAILER *m; 4854796Seric { 48614900Seric (void) sprintf(SmtpMsgBuffer, f, a, b, c); 4877677Seric if (tTd(18, 1) || (Verbose && !HoldErrs)) 48814900Seric nmessage(Arpa_Info, ">>> %s", SmtpMsgBuffer); 48911159Seric if (SmtpOut != NULL) 49040997Sbostic fprintf(SmtpOut, "%s%s", SmtpMsgBuffer, 49140997Sbostic m == 0 ? "\r\n" : m->m_eol); 4924796Seric } 4935182Seric 4945182Seric # endif SMTP 495