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*52647Seric static char sccsid[] = "@(#)usersmtp.c 5.20 (Berkeley) 02/24/92 (with SMTP)"; 1433731Sbostic #else 15*52647Seric static char sccsid[] = "@(#)usersmtp.c 5.20 (Berkeley) 02/24/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 FILE *SmtpOut; /* output file */ 3810054Seric FILE *SmtpIn; /* input file */ 3910054Seric int SmtpPid; /* pid of mailer */ 4011159Seric 4111159Seric /* following represents the state of the SMTP connection */ 4211159Seric int SmtpState; /* connection state, see below */ 4311159Seric 4411159Seric #define SMTP_CLOSED 0 /* connection is closed */ 4511159Seric #define SMTP_OPEN 1 /* connection is open for business */ 4611159Seric #define SMTP_SSD 2 /* service shutting down */ 479391Seric /* 484865Seric ** SMTPINIT -- initialize SMTP. 494684Seric ** 504865Seric ** Opens the connection and sends the initial protocol. 514684Seric ** 524684Seric ** Parameters: 534865Seric ** m -- mailer to create connection to. 544865Seric ** pvp -- pointer to parameter vector to pass to 554865Seric ** the mailer. 564684Seric ** 574684Seric ** Returns: 584865Seric ** appropriate exit status -- EX_OK on success. 5914913Seric ** If not EX_OK, it should close the connection. 604684Seric ** 614684Seric ** Side Effects: 624865Seric ** creates connection and sends initial protocol. 634684Seric */ 644684Seric 6514886Seric jmp_buf CtxGreeting; 6614886Seric 6710175Seric smtpinit(m, pvp) 684865Seric struct mailer *m; 694865Seric char **pvp; 704684Seric { 714865Seric register int r; 7214886Seric EVENT *gte; 7352107Seric register STAB *st; 744865Seric char buf[MAXNAME]; 7546928Sbostic static int greettimeout(); 7652107Seric extern STAB *stab(); 774684Seric 784865Seric /* 794865Seric ** Open the connection to the mailer. 804865Seric */ 814684Seric 8211159Seric if (SmtpState == SMTP_OPEN) 8311159Seric syserr("smtpinit: already open"); 8411159Seric 856051Seric SmtpIn = SmtpOut = NULL; 8611159Seric SmtpState = SMTP_CLOSED; 8721065Seric SmtpError[0] = '\0'; 8824944Seric SmtpPhase = "user open"; 8936584Sbostic setproctitle("%s %s: %s", CurEnv->e_id, pvp[1], SmtpPhase); 9010175Seric SmtpPid = openmailer(m, pvp, (ADDRESS *) NULL, TRUE, &SmtpOut, &SmtpIn); 916051Seric if (SmtpPid < 0) 926051Seric { 937677Seric if (tTd(18, 1)) 949391Seric printf("smtpinit: cannot open %s: stat %d errno %d\n", 959391Seric pvp[0], ExitStat, errno); 9615139Seric if (CurEnv->e_xfp != NULL) 9715139Seric { 9821065Seric register char *p; 9915139Seric extern char *errstring(); 10021065Seric extern char *statstring(); 10115139Seric 10221065Seric if (errno == 0) 10321065Seric { 10421065Seric p = statstring(ExitStat); 10521065Seric fprintf(CurEnv->e_xfp, 10621065Seric "%.3s %s.%s... %s\n", 10721065Seric p, pvp[1], m->m_name, p); 10821065Seric } 10921065Seric else 11021065Seric { 11136584Sbostic r = errno; 11221065Seric fprintf(CurEnv->e_xfp, 11321065Seric "421 %s.%s... Deferred: %s\n", 11421065Seric pvp[1], m->m_name, errstring(errno)); 11536584Sbostic errno = r; 11621065Seric } 11715139Seric } 1186051Seric return (ExitStat); 1196051Seric } 12011159Seric SmtpState = SMTP_OPEN; 1214796Seric 1224865Seric /* 1234865Seric ** Get the greeting message. 12414913Seric ** This should appear spontaneously. Give it five minutes to 12514886Seric ** happen. 1264865Seric */ 1274797Seric 12814886Seric if (setjmp(CtxGreeting) != 0) 12952104Seric goto tempfail1; 13016141Seric gte = setevent((time_t) 300, greettimeout, 0); 13124944Seric SmtpPhase = "greeting wait"; 13236584Sbostic setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase); 13310175Seric r = reply(m); 13414886Seric clrevent(gte); 1358005Seric if (r < 0 || REPLYTYPE(r) != 2) 13652104Seric goto tempfail1; 1374684Seric 1384865Seric /* 1394976Seric ** Send the HELO command. 1407963Seric ** My mother taught me to always introduce myself. 1414976Seric */ 1424976Seric 14325050Seric smtpmessage("HELO %s", m, MyHostName); 14424944Seric SmtpPhase = "HELO wait"; 14536584Sbostic setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase); 14610175Seric r = reply(m); 1478005Seric if (r < 0) 14852104Seric goto tempfail1; 1498005Seric else if (REPLYTYPE(r) == 5) 15014913Seric goto unavailable; 1517963Seric else if (REPLYTYPE(r) != 2) 15252104Seric goto tempfail1; 1534976Seric 1544976Seric /* 1559315Seric ** If this is expected to be another sendmail, send some internal 1569315Seric ** commands. 1579315Seric */ 1589315Seric 15910688Seric if (bitnset(M_INTERNAL, m->m_flags)) 1609315Seric { 1619315Seric /* tell it to be verbose */ 16210175Seric smtpmessage("VERB", m); 16310175Seric r = reply(m); 1649315Seric if (r < 0) 16552104Seric goto tempfail2; 1669315Seric 1679315Seric /* tell it we will be sending one transaction only */ 16810175Seric smtpmessage("ONEX", m); 16910175Seric r = reply(m); 1709315Seric if (r < 0) 17152104Seric goto tempfail2; 1729315Seric } 1739315Seric 1749315Seric /* 1754865Seric ** Send the MAIL command. 1764865Seric ** Designates the sender. 1774865Seric */ 1784796Seric 17951951Seric expand("\001<", buf, &buf[sizeof buf - 1], CurEnv); 1808436Seric if (CurEnv->e_from.q_mailer == LocalMailer || 18110688Seric !bitnset(M_FROMPATH, m->m_flags)) 1828436Seric { 18310308Seric smtpmessage("MAIL From:<%s>", m, buf); 1848436Seric } 1858436Seric else 1868436Seric { 18725050Seric smtpmessage("MAIL From:<@%s%c%s>", m, MyHostName, 18810308Seric buf[0] == '@' ? ',' : ':', buf); 1898436Seric } 19024944Seric SmtpPhase = "MAIL wait"; 19136584Sbostic setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase); 19210175Seric r = reply(m); 1938005Seric if (r < 0 || REPLYTYPE(r) == 4) 19452104Seric goto tempfail2; 1957963Seric else if (r == 250) 1967963Seric return (EX_OK); 1977963Seric else if (r == 552) 19814913Seric goto unavailable; 19914913Seric 20014913Seric /* protocol error -- close up */ 20114913Seric smtpquit(m); 2027964Seric return (EX_PROTOCOL); 20314913Seric 20452104Seric tempfail1: 20552107Seric /* log this as an error to avoid sure-to-be-void connections */ 20652107Seric st = stab(CurHostName, ST_HOST, ST_ENTER); 20752107Seric st->s_host.ho_exitstat = EX_TEMPFAIL; 20852107Seric st->s_host.ho_errno = errno; 20952104Seric 21052104Seric tempfail2: 21152107Seric /* signal a temporary failure */ 21214913Seric smtpquit(m); 21314913Seric return (EX_TEMPFAIL); 21414913Seric 21552107Seric unavailable: 21614913Seric /* signal service unavailable */ 21714913Seric smtpquit(m); 21814913Seric return (EX_UNAVAILABLE); 2194684Seric } 22014886Seric 22114886Seric 22214886Seric static 22314886Seric greettimeout() 22414886Seric { 22514886Seric /* timeout reading the greeting message */ 22614886Seric longjmp(CtxGreeting, 1); 22714886Seric } 2284684Seric /* 2294976Seric ** SMTPRCPT -- designate recipient. 2304797Seric ** 2314797Seric ** Parameters: 2324865Seric ** to -- address of recipient. 23310175Seric ** m -- the mailer we are sending to. 2344797Seric ** 2354797Seric ** Returns: 2364865Seric ** exit status corresponding to recipient status. 2374797Seric ** 2384797Seric ** Side Effects: 2394865Seric ** Sends the mail via SMTP. 2404797Seric */ 2414797Seric 24210175Seric smtprcpt(to, m) 2434865Seric ADDRESS *to; 24410175Seric register MAILER *m; 2454797Seric { 2464797Seric register int r; 24710308Seric extern char *remotename(); 2484797Seric 249*52647Seric smtpmessage("RCPT To:<%s>", m, to->q_user); 2504865Seric 25124944Seric SmtpPhase = "RCPT wait"; 25236584Sbostic setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase); 25310175Seric r = reply(m); 2548005Seric if (r < 0 || REPLYTYPE(r) == 4) 2554865Seric return (EX_TEMPFAIL); 2567963Seric else if (REPLYTYPE(r) == 2) 2577963Seric return (EX_OK); 2587964Seric else if (r == 550 || r == 551 || r == 553) 2597964Seric return (EX_NOUSER); 2607964Seric else if (r == 552 || r == 554) 2617964Seric return (EX_UNAVAILABLE); 2627964Seric return (EX_PROTOCOL); 2634797Seric } 2644797Seric /* 26510175Seric ** SMTPDATA -- send the data and clean up the transaction. 2664684Seric ** 2674684Seric ** Parameters: 2684865Seric ** m -- mailer being sent to. 2696980Seric ** e -- the envelope for this message. 2704684Seric ** 2714684Seric ** Returns: 2724976Seric ** exit status corresponding to DATA command. 2734684Seric ** 2744684Seric ** Side Effects: 2754865Seric ** none. 2764684Seric */ 2774684Seric 27810175Seric smtpdata(m, e) 2794865Seric struct mailer *m; 2806980Seric register ENVELOPE *e; 2814684Seric { 2824684Seric register int r; 2834684Seric 2844797Seric /* 2854797Seric ** Send the data. 28610175Seric ** First send the command and check that it is ok. 28710175Seric ** Then send the data. 28810175Seric ** Follow it up with a dot to terminate. 28910175Seric ** Finally get the results of the transaction. 2904797Seric */ 2914797Seric 29210175Seric /* send the command and check ok to proceed */ 29310175Seric smtpmessage("DATA", m); 29424944Seric SmtpPhase = "DATA wait"; 29536584Sbostic setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase); 29610175Seric r = reply(m); 2978005Seric if (r < 0 || REPLYTYPE(r) == 4) 2984797Seric return (EX_TEMPFAIL); 2997963Seric else if (r == 554) 3007963Seric return (EX_UNAVAILABLE); 3017963Seric else if (r != 354) 3027964Seric return (EX_PROTOCOL); 30310175Seric 30410175Seric /* now output the actual message */ 30510175Seric (*e->e_puthdr)(SmtpOut, m, CurEnv); 30610175Seric putline("\n", SmtpOut, m); 30710175Seric (*e->e_putbody)(SmtpOut, m, CurEnv); 30810175Seric 30910175Seric /* terminate the message */ 31010328Seric fprintf(SmtpOut, ".%s", m->m_eol); 31110215Seric if (Verbose && !HoldErrs) 31210215Seric nmessage(Arpa_Info, ">>> ."); 31310175Seric 31410175Seric /* check for the results of the transaction */ 31524944Seric SmtpPhase = "result wait"; 31636584Sbostic setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase); 31710175Seric r = reply(m); 3188005Seric if (r < 0 || REPLYTYPE(r) == 4) 3194797Seric return (EX_TEMPFAIL); 3207963Seric else if (r == 250) 3217963Seric return (EX_OK); 3227963Seric else if (r == 552 || r == 554) 3237963Seric return (EX_UNAVAILABLE); 3247964Seric return (EX_PROTOCOL); 3254684Seric } 3264684Seric /* 3274865Seric ** SMTPQUIT -- close the SMTP connection. 3284865Seric ** 3294865Seric ** Parameters: 33015535Seric ** m -- a pointer to the mailer. 3314865Seric ** 3324865Seric ** Returns: 3334865Seric ** none. 3344865Seric ** 3354865Seric ** Side Effects: 3364865Seric ** sends the final protocol and closes the connection. 3374865Seric */ 3384865Seric 33915535Seric smtpquit(m) 34010175Seric register MAILER *m; 3414865Seric { 3429391Seric int i; 3434865Seric 34410148Seric /* if the connection is already closed, don't bother */ 34510148Seric if (SmtpIn == NULL) 34610148Seric return; 34710148Seric 34810148Seric /* send the quit message if not a forced quit */ 34911159Seric if (SmtpState == SMTP_OPEN || SmtpState == SMTP_SSD) 3509391Seric { 35110175Seric smtpmessage("QUIT", m); 35210175Seric (void) reply(m); 35311159Seric if (SmtpState == SMTP_CLOSED) 35410159Seric return; 3559391Seric } 3569391Seric 35710148Seric /* now actually close the connection */ 3587229Seric (void) fclose(SmtpIn); 3597229Seric (void) fclose(SmtpOut); 36010148Seric SmtpIn = SmtpOut = NULL; 36111159Seric SmtpState = SMTP_CLOSED; 36210148Seric 36310148Seric /* and pick up the zombie */ 36415535Seric i = endmailer(SmtpPid, m->m_argv[0]); 3659391Seric if (i != EX_OK) 36615535Seric syserr("smtpquit %s: stat %d", m->m_argv[0], i); 3674865Seric } 3684865Seric /* 3694684Seric ** REPLY -- read arpanet reply 3704684Seric ** 3714684Seric ** Parameters: 37210175Seric ** m -- the mailer we are reading the reply from. 3734684Seric ** 3744684Seric ** Returns: 3754684Seric ** reply code it reads. 3764684Seric ** 3774684Seric ** Side Effects: 3784684Seric ** flushes the mail file. 3794684Seric */ 3804684Seric 38110175Seric reply(m) 38210688Seric MAILER *m; 3834684Seric { 3844865Seric (void) fflush(SmtpOut); 3854684Seric 3867677Seric if (tTd(18, 1)) 3874796Seric printf("reply\n"); 3884796Seric 3897356Seric /* 3907356Seric ** Read the input line, being careful not to hang. 3917356Seric */ 3927356Seric 3934684Seric for (;;) 3944684Seric { 3954684Seric register int r; 3967356Seric register char *p; 3974684Seric 3987685Seric /* actually do the read */ 3999547Seric if (CurEnv->e_xfp != NULL) 4009547Seric (void) fflush(CurEnv->e_xfp); /* for debugging */ 4017356Seric 40210054Seric /* if we are in the process of closing just give the code */ 40311159Seric if (SmtpState == SMTP_CLOSED) 40410054Seric return (SMTPCLOSING); 40510054Seric 40610054Seric /* get the line from the other side */ 40710054Seric p = sfgets(SmtpReplyBuffer, sizeof SmtpReplyBuffer, SmtpIn); 40810054Seric if (p == NULL) 40910131Seric { 41010148Seric extern char MsgBuf[]; /* err.c */ 41110148Seric extern char Arpa_TSyserr[]; /* conf.c */ 41210148Seric 41321065Seric /* if the remote end closed early, fake an error */ 41421065Seric if (errno == 0) 41521065Seric # ifdef ECONNRESET 41621065Seric errno = ECONNRESET; 41721065Seric # else ECONNRESET 41821065Seric errno = EPIPE; 41921065Seric # endif ECONNRESET 42021065Seric 42110148Seric message(Arpa_TSyserr, "reply: read error"); 42210420Seric /* if debugging, pause so we can see state */ 42310420Seric if (tTd(18, 100)) 42410420Seric pause(); 42510148Seric # ifdef LOG 42636234Skarels syslog(LOG_INFO, "%s", &MsgBuf[4]); 42710148Seric # endif LOG 42811159Seric SmtpState = SMTP_CLOSED; 42915535Seric smtpquit(m); 43010054Seric return (-1); 43110131Seric } 43210054Seric fixcrlf(SmtpReplyBuffer, TRUE); 43310054Seric 43414900Seric if (CurEnv->e_xfp != NULL && index("45", SmtpReplyBuffer[0]) != NULL) 43514900Seric { 43614900Seric /* serious error -- log the previous command */ 43714900Seric if (SmtpMsgBuffer[0] != '\0') 43814900Seric fprintf(CurEnv->e_xfp, ">>> %s\n", SmtpMsgBuffer); 43914900Seric SmtpMsgBuffer[0] = '\0'; 44014900Seric 44114900Seric /* now log the message as from the other side */ 44214900Seric fprintf(CurEnv->e_xfp, "<<< %s\n", SmtpReplyBuffer); 44314900Seric } 44414900Seric 44514900Seric /* display the input for verbose mode */ 4467229Seric if (Verbose && !HoldErrs) 4479391Seric nmessage(Arpa_Info, "%s", SmtpReplyBuffer); 4487356Seric 4497356Seric /* if continuation is required, we can go on */ 4509391Seric if (SmtpReplyBuffer[3] == '-' || !isdigit(SmtpReplyBuffer[0])) 4514684Seric continue; 4527356Seric 4537356Seric /* decode the reply code */ 4549391Seric r = atoi(SmtpReplyBuffer); 4557356Seric 4567356Seric /* extra semantics: 0xx codes are "informational" */ 4574684Seric if (r < 100) 4584684Seric continue; 4597356Seric 4609391Seric /* reply code 421 is "Service Shutting Down" */ 46111159Seric if (r == SMTPCLOSING && SmtpState != SMTP_SSD) 4629391Seric { 46310054Seric /* send the quit protocol */ 46411159Seric SmtpState = SMTP_SSD; 46515535Seric smtpquit(m); 4669391Seric } 4679391Seric 46821065Seric /* save temporary failure messages for posterity */ 46921065Seric if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0') 47021065Seric (void) strcpy(SmtpError, &SmtpReplyBuffer[4]); 47121065Seric 4724684Seric return (r); 4734684Seric } 4744684Seric } 4754796Seric /* 4764865Seric ** SMTPMESSAGE -- send message to server 4774796Seric ** 4784796Seric ** Parameters: 4794796Seric ** f -- format 48010175Seric ** m -- the mailer to control formatting. 4814796Seric ** a, b, c -- parameters 4824796Seric ** 4834796Seric ** Returns: 4844796Seric ** none. 4854796Seric ** 4864796Seric ** Side Effects: 4874865Seric ** writes message to SmtpOut. 4884796Seric */ 4894796Seric 4904865Seric /*VARARGS1*/ 49110175Seric smtpmessage(f, m, a, b, c) 4924796Seric char *f; 49310175Seric MAILER *m; 4944796Seric { 49514900Seric (void) sprintf(SmtpMsgBuffer, f, a, b, c); 4967677Seric if (tTd(18, 1) || (Verbose && !HoldErrs)) 49714900Seric nmessage(Arpa_Info, ">>> %s", SmtpMsgBuffer); 49811159Seric if (SmtpOut != NULL) 49940997Sbostic fprintf(SmtpOut, "%s%s", SmtpMsgBuffer, 50040997Sbostic m == 0 ? "\r\n" : m->m_eol); 5014796Seric } 5025182Seric 5035182Seric # endif SMTP 504