14796Seric # include <ctype.h> 24684Seric # include <sysexits.h> 3*21065Seric # include <errno.h> 44865Seric # include "sendmail.h" 54684Seric 65182Seric # ifndef SMTP 7*21065Seric SCCSID(@(#)usersmtp.c 4.12 05/24/85 (no SMTP)); 85182Seric # else SMTP 94684Seric 10*21065Seric SCCSID(@(#)usersmtp.c 4.12 05/24/85); 115182Seric 129391Seric 139391Seric 144684Seric /* 159391Seric ** USERSMTP -- run SMTP protocol from the user end. 169391Seric ** 179391Seric ** This protocol is described in RFC821. 189391Seric */ 199391Seric 209391Seric #define REPLYTYPE(r) ((r) / 100) /* first digit of reply code */ 219391Seric #define REPLYCLASS(r) (((r) / 10) % 10) /* second digit of reply code */ 229391Seric #define SMTPCLOSING 421 /* "Service Shutting Down" */ 239391Seric 2414900Seric char SmtpMsgBuffer[MAXLINE]; /* buffer for commands */ 2510054Seric char SmtpReplyBuffer[MAXLINE]; /* buffer for replies */ 26*21065Seric char SmtpError[MAXLINE] = ""; /* save failure error messages */ 2710054Seric FILE *SmtpOut; /* output file */ 2810054Seric FILE *SmtpIn; /* input file */ 2910054Seric int SmtpPid; /* pid of mailer */ 3011159Seric 3111159Seric /* following represents the state of the SMTP connection */ 3211159Seric int SmtpState; /* connection state, see below */ 3311159Seric 3411159Seric #define SMTP_CLOSED 0 /* connection is closed */ 3511159Seric #define SMTP_OPEN 1 /* connection is open for business */ 3611159Seric #define SMTP_SSD 2 /* service shutting down */ 379391Seric /* 384865Seric ** SMTPINIT -- initialize SMTP. 394684Seric ** 404865Seric ** Opens the connection and sends the initial protocol. 414684Seric ** 424684Seric ** Parameters: 434865Seric ** m -- mailer to create connection to. 444865Seric ** pvp -- pointer to parameter vector to pass to 454865Seric ** the mailer. 464684Seric ** 474684Seric ** Returns: 484865Seric ** appropriate exit status -- EX_OK on success. 4914913Seric ** If not EX_OK, it should close the connection. 504684Seric ** 514684Seric ** Side Effects: 524865Seric ** creates connection and sends initial protocol. 534684Seric */ 544684Seric 5514886Seric jmp_buf CtxGreeting; 5614886Seric 5710175Seric smtpinit(m, pvp) 584865Seric struct mailer *m; 594865Seric char **pvp; 604684Seric { 614865Seric register int r; 6214886Seric EVENT *gte; 634865Seric char buf[MAXNAME]; 6414886Seric extern greettimeout(); 654684Seric 664865Seric /* 674865Seric ** Open the connection to the mailer. 684865Seric */ 694684Seric 7011159Seric #ifdef DEBUG 7111159Seric if (SmtpState == SMTP_OPEN) 7211159Seric syserr("smtpinit: already open"); 7311159Seric #endif DEBUG 7411159Seric 756051Seric SmtpIn = SmtpOut = NULL; 7611159Seric SmtpState = SMTP_CLOSED; 77*21065Seric SmtpError[0] = '\0'; 7810175Seric SmtpPid = openmailer(m, pvp, (ADDRESS *) NULL, TRUE, &SmtpOut, &SmtpIn); 796051Seric if (SmtpPid < 0) 806051Seric { 816051Seric # ifdef DEBUG 827677Seric if (tTd(18, 1)) 839391Seric printf("smtpinit: cannot open %s: stat %d errno %d\n", 849391Seric pvp[0], ExitStat, errno); 856051Seric # endif DEBUG 8615139Seric if (CurEnv->e_xfp != NULL) 8715139Seric { 88*21065Seric register char *p; 8915139Seric extern char *errstring(); 90*21065Seric extern char *statstring(); 9115139Seric 92*21065Seric if (errno == 0) 93*21065Seric { 94*21065Seric p = statstring(ExitStat); 95*21065Seric fprintf(CurEnv->e_xfp, 96*21065Seric "%.3s %s.%s... %s\n", 97*21065Seric p, pvp[1], m->m_name, p); 98*21065Seric } 99*21065Seric else 100*21065Seric { 101*21065Seric fprintf(CurEnv->e_xfp, 102*21065Seric "421 %s.%s... Deferred: %s\n", 103*21065Seric pvp[1], m->m_name, errstring(errno)); 104*21065Seric } 10515139Seric } 1066051Seric return (ExitStat); 1076051Seric } 10811159Seric SmtpState = SMTP_OPEN; 1094796Seric 1104865Seric /* 1114865Seric ** Get the greeting message. 11214913Seric ** This should appear spontaneously. Give it five minutes to 11314886Seric ** happen. 1144865Seric */ 1154797Seric 11614886Seric if (setjmp(CtxGreeting) != 0) 11714913Seric goto tempfail; 11816141Seric gte = setevent((time_t) 300, greettimeout, 0); 11910175Seric r = reply(m); 12014886Seric clrevent(gte); 1218005Seric if (r < 0 || REPLYTYPE(r) != 2) 12214913Seric goto tempfail; 1234684Seric 1244865Seric /* 1254976Seric ** Send the HELO command. 1267963Seric ** My mother taught me to always introduce myself. 1274976Seric */ 1284976Seric 12910175Seric smtpmessage("HELO %s", m, HostName); 13010175Seric r = reply(m); 1318005Seric if (r < 0) 13214913Seric goto tempfail; 1338005Seric else if (REPLYTYPE(r) == 5) 13414913Seric goto unavailable; 1357963Seric else if (REPLYTYPE(r) != 2) 13614913Seric goto tempfail; 1374976Seric 1384976Seric /* 1399315Seric ** If this is expected to be another sendmail, send some internal 1409315Seric ** commands. 1419315Seric */ 1429315Seric 14310688Seric if (bitnset(M_INTERNAL, m->m_flags)) 1449315Seric { 1459315Seric /* tell it to be verbose */ 14610175Seric smtpmessage("VERB", m); 14710175Seric r = reply(m); 1489315Seric if (r < 0) 14914913Seric goto tempfail; 1509315Seric 1519315Seric /* tell it we will be sending one transaction only */ 15210175Seric smtpmessage("ONEX", m); 15310175Seric r = reply(m); 1549315Seric if (r < 0) 15514913Seric goto tempfail; 1569315Seric } 1579315Seric 1589315Seric /* 1594865Seric ** Send the MAIL command. 1604865Seric ** Designates the sender. 1614865Seric */ 1624796Seric 16316156Seric expand("\001g", buf, &buf[sizeof buf - 1], CurEnv); 1648436Seric if (CurEnv->e_from.q_mailer == LocalMailer || 16510688Seric !bitnset(M_FROMPATH, m->m_flags)) 1668436Seric { 16710308Seric smtpmessage("MAIL From:<%s>", m, buf); 1688436Seric } 1698436Seric else 1708436Seric { 17110175Seric smtpmessage("MAIL From:<@%s%c%s>", m, HostName, 17210308Seric buf[0] == '@' ? ',' : ':', buf); 1738436Seric } 17410175Seric r = reply(m); 1758005Seric if (r < 0 || REPLYTYPE(r) == 4) 17614913Seric goto tempfail; 1777963Seric else if (r == 250) 1787963Seric return (EX_OK); 1797963Seric else if (r == 552) 18014913Seric goto unavailable; 18114913Seric 18214913Seric /* protocol error -- close up */ 18314913Seric smtpquit(m); 1847964Seric return (EX_PROTOCOL); 18514913Seric 18614913Seric /* signal a temporary failure */ 18714913Seric tempfail: 18814913Seric smtpquit(m); 18916891Seric CurEnv->e_flags &= ~EF_FATALERRS; 19014913Seric return (EX_TEMPFAIL); 19114913Seric 19214913Seric /* signal service unavailable */ 19314913Seric unavailable: 19414913Seric smtpquit(m); 19514913Seric return (EX_UNAVAILABLE); 1964684Seric } 19714886Seric 19814886Seric 19914886Seric static 20014886Seric greettimeout() 20114886Seric { 20214886Seric /* timeout reading the greeting message */ 20314886Seric longjmp(CtxGreeting, 1); 20414886Seric } 2054684Seric /* 2064976Seric ** SMTPRCPT -- designate recipient. 2074797Seric ** 2084797Seric ** Parameters: 2094865Seric ** to -- address of recipient. 21010175Seric ** m -- the mailer we are sending to. 2114797Seric ** 2124797Seric ** Returns: 2134865Seric ** exit status corresponding to recipient status. 2144797Seric ** 2154797Seric ** Side Effects: 2164865Seric ** Sends the mail via SMTP. 2174797Seric */ 2184797Seric 21910175Seric smtprcpt(to, m) 2204865Seric ADDRESS *to; 22110175Seric register MAILER *m; 2224797Seric { 2234797Seric register int r; 22410308Seric extern char *remotename(); 2254797Seric 22610308Seric smtpmessage("RCPT To:<%s>", m, remotename(to->q_user, m, FALSE, TRUE)); 2274865Seric 22810175Seric r = reply(m); 2298005Seric if (r < 0 || REPLYTYPE(r) == 4) 2304865Seric return (EX_TEMPFAIL); 2317963Seric else if (REPLYTYPE(r) == 2) 2327963Seric return (EX_OK); 2337964Seric else if (r == 550 || r == 551 || r == 553) 2347964Seric return (EX_NOUSER); 2357964Seric else if (r == 552 || r == 554) 2367964Seric return (EX_UNAVAILABLE); 2377964Seric return (EX_PROTOCOL); 2384797Seric } 2394797Seric /* 24010175Seric ** SMTPDATA -- send the data and clean up the transaction. 2414684Seric ** 2424684Seric ** Parameters: 2434865Seric ** m -- mailer being sent to. 2446980Seric ** e -- the envelope for this message. 2454684Seric ** 2464684Seric ** Returns: 2474976Seric ** exit status corresponding to DATA command. 2484684Seric ** 2494684Seric ** Side Effects: 2504865Seric ** none. 2514684Seric */ 2524684Seric 25310175Seric smtpdata(m, e) 2544865Seric struct mailer *m; 2556980Seric register ENVELOPE *e; 2564684Seric { 2574684Seric register int r; 2584684Seric 2594797Seric /* 2604797Seric ** Send the data. 26110175Seric ** First send the command and check that it is ok. 26210175Seric ** Then send the data. 26310175Seric ** Follow it up with a dot to terminate. 26410175Seric ** Finally get the results of the transaction. 2654797Seric */ 2664797Seric 26710175Seric /* send the command and check ok to proceed */ 26810175Seric smtpmessage("DATA", m); 26910175Seric r = reply(m); 2708005Seric if (r < 0 || REPLYTYPE(r) == 4) 2714797Seric return (EX_TEMPFAIL); 2727963Seric else if (r == 554) 2737963Seric return (EX_UNAVAILABLE); 2747963Seric else if (r != 354) 2757964Seric return (EX_PROTOCOL); 27610175Seric 27710175Seric /* now output the actual message */ 27810175Seric (*e->e_puthdr)(SmtpOut, m, CurEnv); 27910175Seric putline("\n", SmtpOut, m); 28010175Seric (*e->e_putbody)(SmtpOut, m, CurEnv); 28110175Seric 28210175Seric /* terminate the message */ 28310328Seric fprintf(SmtpOut, ".%s", m->m_eol); 28410215Seric if (Verbose && !HoldErrs) 28510215Seric nmessage(Arpa_Info, ">>> ."); 28610175Seric 28710175Seric /* check for the results of the transaction */ 28810175Seric r = reply(m); 2898005Seric if (r < 0 || REPLYTYPE(r) == 4) 2904797Seric return (EX_TEMPFAIL); 2917963Seric else if (r == 250) 2927963Seric return (EX_OK); 2937963Seric else if (r == 552 || r == 554) 2947963Seric return (EX_UNAVAILABLE); 2957964Seric return (EX_PROTOCOL); 2964684Seric } 2974684Seric /* 2984865Seric ** SMTPQUIT -- close the SMTP connection. 2994865Seric ** 3004865Seric ** Parameters: 30115535Seric ** m -- a pointer to the mailer. 3024865Seric ** 3034865Seric ** Returns: 3044865Seric ** none. 3054865Seric ** 3064865Seric ** Side Effects: 3074865Seric ** sends the final protocol and closes the connection. 3084865Seric */ 3094865Seric 31015535Seric smtpquit(m) 31110175Seric register MAILER *m; 3124865Seric { 3139391Seric int i; 3144865Seric 31510148Seric /* if the connection is already closed, don't bother */ 31610148Seric if (SmtpIn == NULL) 31710148Seric return; 31810148Seric 31910148Seric /* send the quit message if not a forced quit */ 32011159Seric if (SmtpState == SMTP_OPEN || SmtpState == SMTP_SSD) 3219391Seric { 32210175Seric smtpmessage("QUIT", m); 32310175Seric (void) reply(m); 32411159Seric if (SmtpState == SMTP_CLOSED) 32510159Seric return; 3269391Seric } 3279391Seric 32810148Seric /* now actually close the connection */ 3297229Seric (void) fclose(SmtpIn); 3307229Seric (void) fclose(SmtpOut); 33110148Seric SmtpIn = SmtpOut = NULL; 33211159Seric SmtpState = SMTP_CLOSED; 33310148Seric 33410148Seric /* and pick up the zombie */ 33515535Seric i = endmailer(SmtpPid, m->m_argv[0]); 3369391Seric if (i != EX_OK) 33715535Seric syserr("smtpquit %s: stat %d", m->m_argv[0], i); 3384865Seric } 3394865Seric /* 3404684Seric ** REPLY -- read arpanet reply 3414684Seric ** 3424684Seric ** Parameters: 34310175Seric ** m -- the mailer we are reading the reply from. 3444684Seric ** 3454684Seric ** Returns: 3464684Seric ** reply code it reads. 3474684Seric ** 3484684Seric ** Side Effects: 3494684Seric ** flushes the mail file. 3504684Seric */ 3514684Seric 35210175Seric reply(m) 35310688Seric MAILER *m; 3544684Seric { 3554865Seric (void) fflush(SmtpOut); 3564684Seric 3577677Seric if (tTd(18, 1)) 3584796Seric printf("reply\n"); 3594796Seric 3607356Seric /* 3617356Seric ** Read the input line, being careful not to hang. 3627356Seric */ 3637356Seric 3644684Seric for (;;) 3654684Seric { 3664684Seric register int r; 3677356Seric register char *p; 3684684Seric 3697685Seric /* actually do the read */ 3709547Seric if (CurEnv->e_xfp != NULL) 3719547Seric (void) fflush(CurEnv->e_xfp); /* for debugging */ 3727356Seric 37310054Seric /* if we are in the process of closing just give the code */ 37411159Seric if (SmtpState == SMTP_CLOSED) 37510054Seric return (SMTPCLOSING); 37610054Seric 37710054Seric /* get the line from the other side */ 37810054Seric p = sfgets(SmtpReplyBuffer, sizeof SmtpReplyBuffer, SmtpIn); 37910054Seric if (p == NULL) 38010131Seric { 38110148Seric extern char MsgBuf[]; /* err.c */ 38210148Seric extern char Arpa_TSyserr[]; /* conf.c */ 38310148Seric 384*21065Seric /* if the remote end closed early, fake an error */ 385*21065Seric if (errno == 0) 386*21065Seric # ifdef ECONNRESET 387*21065Seric errno = ECONNRESET; 388*21065Seric # else ECONNRESET 389*21065Seric errno = EPIPE; 390*21065Seric # endif ECONNRESET 391*21065Seric 39210148Seric message(Arpa_TSyserr, "reply: read error"); 39310420Seric # ifdef DEBUG 39410420Seric /* if debugging, pause so we can see state */ 39510420Seric if (tTd(18, 100)) 39610420Seric pause(); 39710420Seric # endif DEBUG 39810148Seric # ifdef LOG 39918573Smiriam syslog(LOG_MAIL, "%s", &MsgBuf[4]); 40010148Seric # endif LOG 40111159Seric SmtpState = SMTP_CLOSED; 40215535Seric smtpquit(m); 40310054Seric return (-1); 40410131Seric } 40510054Seric fixcrlf(SmtpReplyBuffer, TRUE); 40610054Seric 40714900Seric if (CurEnv->e_xfp != NULL && index("45", SmtpReplyBuffer[0]) != NULL) 40814900Seric { 40914900Seric /* serious error -- log the previous command */ 41014900Seric if (SmtpMsgBuffer[0] != '\0') 41114900Seric fprintf(CurEnv->e_xfp, ">>> %s\n", SmtpMsgBuffer); 41214900Seric SmtpMsgBuffer[0] = '\0'; 41314900Seric 41414900Seric /* now log the message as from the other side */ 41514900Seric fprintf(CurEnv->e_xfp, "<<< %s\n", SmtpReplyBuffer); 41614900Seric } 41714900Seric 41814900Seric /* display the input for verbose mode */ 4197229Seric if (Verbose && !HoldErrs) 4209391Seric nmessage(Arpa_Info, "%s", SmtpReplyBuffer); 4217356Seric 4227356Seric /* if continuation is required, we can go on */ 4239391Seric if (SmtpReplyBuffer[3] == '-' || !isdigit(SmtpReplyBuffer[0])) 4244684Seric continue; 4257356Seric 4267356Seric /* decode the reply code */ 4279391Seric r = atoi(SmtpReplyBuffer); 4287356Seric 4297356Seric /* extra semantics: 0xx codes are "informational" */ 4304684Seric if (r < 100) 4314684Seric continue; 4327356Seric 4339391Seric /* reply code 421 is "Service Shutting Down" */ 43411159Seric if (r == SMTPCLOSING && SmtpState != SMTP_SSD) 4359391Seric { 43610054Seric /* send the quit protocol */ 43711159Seric SmtpState = SMTP_SSD; 43815535Seric smtpquit(m); 4399391Seric } 4409391Seric 441*21065Seric /* save temporary failure messages for posterity */ 442*21065Seric if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0') 443*21065Seric (void) strcpy(SmtpError, &SmtpReplyBuffer[4]); 444*21065Seric 4454684Seric return (r); 4464684Seric } 4474684Seric } 4484796Seric /* 4494865Seric ** SMTPMESSAGE -- send message to server 4504796Seric ** 4514796Seric ** Parameters: 4524796Seric ** f -- format 45310175Seric ** m -- the mailer to control formatting. 4544796Seric ** a, b, c -- parameters 4554796Seric ** 4564796Seric ** Returns: 4574796Seric ** none. 4584796Seric ** 4594796Seric ** Side Effects: 4604865Seric ** writes message to SmtpOut. 4614796Seric */ 4624796Seric 4634865Seric /*VARARGS1*/ 46410175Seric smtpmessage(f, m, a, b, c) 4654796Seric char *f; 46610175Seric MAILER *m; 4674796Seric { 46814900Seric (void) sprintf(SmtpMsgBuffer, f, a, b, c); 4697677Seric if (tTd(18, 1) || (Verbose && !HoldErrs)) 47014900Seric nmessage(Arpa_Info, ">>> %s", SmtpMsgBuffer); 47111159Seric if (SmtpOut != NULL) 47214900Seric fprintf(SmtpOut, "%s%s", SmtpMsgBuffer, m->m_eol); 4734796Seric } 4745182Seric 4755182Seric # endif SMTP 476