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*46928Sbostic static char sccsid[] = "@(#)usersmtp.c 5.16 (Berkeley) 03/02/91 (with SMTP)"; 1433731Sbostic #else 15*46928Sbostic static char sccsid[] = "@(#)usersmtp.c 5.16 (Berkeley) 03/02/91 (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; 734865Seric char buf[MAXNAME]; 74*46928Sbostic static int greettimeout(); 754684Seric 764865Seric /* 774865Seric ** Open the connection to the mailer. 784865Seric */ 794684Seric 8011159Seric if (SmtpState == SMTP_OPEN) 8111159Seric syserr("smtpinit: already open"); 8211159Seric 836051Seric SmtpIn = SmtpOut = NULL; 8411159Seric SmtpState = SMTP_CLOSED; 8521065Seric SmtpError[0] = '\0'; 8624944Seric SmtpPhase = "user open"; 8736584Sbostic setproctitle("%s %s: %s", CurEnv->e_id, pvp[1], SmtpPhase); 8810175Seric SmtpPid = openmailer(m, pvp, (ADDRESS *) NULL, TRUE, &SmtpOut, &SmtpIn); 896051Seric if (SmtpPid < 0) 906051Seric { 917677Seric if (tTd(18, 1)) 929391Seric printf("smtpinit: cannot open %s: stat %d errno %d\n", 939391Seric pvp[0], ExitStat, errno); 9415139Seric if (CurEnv->e_xfp != NULL) 9515139Seric { 9621065Seric register char *p; 9715139Seric extern char *errstring(); 9821065Seric extern char *statstring(); 9915139Seric 10021065Seric if (errno == 0) 10121065Seric { 10221065Seric p = statstring(ExitStat); 10321065Seric fprintf(CurEnv->e_xfp, 10421065Seric "%.3s %s.%s... %s\n", 10521065Seric p, pvp[1], m->m_name, p); 10621065Seric } 10721065Seric else 10821065Seric { 10936584Sbostic r = errno; 11021065Seric fprintf(CurEnv->e_xfp, 11121065Seric "421 %s.%s... Deferred: %s\n", 11221065Seric pvp[1], m->m_name, errstring(errno)); 11336584Sbostic errno = r; 11421065Seric } 11515139Seric } 1166051Seric return (ExitStat); 1176051Seric } 11811159Seric SmtpState = SMTP_OPEN; 1194796Seric 1204865Seric /* 1214865Seric ** Get the greeting message. 12214913Seric ** This should appear spontaneously. Give it five minutes to 12314886Seric ** happen. 1244865Seric */ 1254797Seric 12614886Seric if (setjmp(CtxGreeting) != 0) 12714913Seric goto tempfail; 12816141Seric gte = setevent((time_t) 300, greettimeout, 0); 12924944Seric SmtpPhase = "greeting wait"; 13036584Sbostic setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase); 13110175Seric r = reply(m); 13214886Seric clrevent(gte); 1338005Seric if (r < 0 || REPLYTYPE(r) != 2) 13414913Seric goto tempfail; 1354684Seric 1364865Seric /* 1374976Seric ** Send the HELO command. 1387963Seric ** My mother taught me to always introduce myself. 1394976Seric */ 1404976Seric 14125050Seric smtpmessage("HELO %s", m, MyHostName); 14224944Seric SmtpPhase = "HELO wait"; 14336584Sbostic setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase); 14410175Seric r = reply(m); 1458005Seric if (r < 0) 14614913Seric goto tempfail; 1478005Seric else if (REPLYTYPE(r) == 5) 14814913Seric goto unavailable; 1497963Seric else if (REPLYTYPE(r) != 2) 15014913Seric goto tempfail; 1514976Seric 1524976Seric /* 1539315Seric ** If this is expected to be another sendmail, send some internal 1549315Seric ** commands. 1559315Seric */ 1569315Seric 15710688Seric if (bitnset(M_INTERNAL, m->m_flags)) 1589315Seric { 1599315Seric /* tell it to be verbose */ 16010175Seric smtpmessage("VERB", m); 16110175Seric r = reply(m); 1629315Seric if (r < 0) 16314913Seric goto tempfail; 1649315Seric 1659315Seric /* tell it we will be sending one transaction only */ 16610175Seric smtpmessage("ONEX", m); 16710175Seric r = reply(m); 1689315Seric if (r < 0) 16914913Seric goto tempfail; 1709315Seric } 1719315Seric 1729315Seric /* 1734865Seric ** Send the MAIL command. 1744865Seric ** Designates the sender. 1754865Seric */ 1764796Seric 17716156Seric expand("\001g", buf, &buf[sizeof buf - 1], CurEnv); 1788436Seric if (CurEnv->e_from.q_mailer == LocalMailer || 17910688Seric !bitnset(M_FROMPATH, m->m_flags)) 1808436Seric { 18110308Seric smtpmessage("MAIL From:<%s>", m, buf); 1828436Seric } 1838436Seric else 1848436Seric { 18525050Seric smtpmessage("MAIL From:<@%s%c%s>", m, MyHostName, 18610308Seric buf[0] == '@' ? ',' : ':', buf); 1878436Seric } 18824944Seric SmtpPhase = "MAIL wait"; 18936584Sbostic setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase); 19010175Seric r = reply(m); 1918005Seric if (r < 0 || REPLYTYPE(r) == 4) 19214913Seric goto tempfail; 1937963Seric else if (r == 250) 1947963Seric return (EX_OK); 1957963Seric else if (r == 552) 19614913Seric goto unavailable; 19714913Seric 19814913Seric /* protocol error -- close up */ 19914913Seric smtpquit(m); 2007964Seric return (EX_PROTOCOL); 20114913Seric 20214913Seric /* signal a temporary failure */ 20314913Seric tempfail: 20414913Seric smtpquit(m); 20514913Seric return (EX_TEMPFAIL); 20614913Seric 20714913Seric /* signal service unavailable */ 20814913Seric unavailable: 20914913Seric smtpquit(m); 21014913Seric return (EX_UNAVAILABLE); 2114684Seric } 21214886Seric 21314886Seric 21414886Seric static 21514886Seric greettimeout() 21614886Seric { 21714886Seric /* timeout reading the greeting message */ 21814886Seric longjmp(CtxGreeting, 1); 21914886Seric } 2204684Seric /* 2214976Seric ** SMTPRCPT -- designate recipient. 2224797Seric ** 2234797Seric ** Parameters: 2244865Seric ** to -- address of recipient. 22510175Seric ** m -- the mailer we are sending to. 2264797Seric ** 2274797Seric ** Returns: 2284865Seric ** exit status corresponding to recipient status. 2294797Seric ** 2304797Seric ** Side Effects: 2314865Seric ** Sends the mail via SMTP. 2324797Seric */ 2334797Seric 23410175Seric smtprcpt(to, m) 2354865Seric ADDRESS *to; 23610175Seric register MAILER *m; 2374797Seric { 2384797Seric register int r; 23910308Seric extern char *remotename(); 2404797Seric 24110308Seric smtpmessage("RCPT To:<%s>", m, remotename(to->q_user, m, FALSE, TRUE)); 2424865Seric 24324944Seric SmtpPhase = "RCPT wait"; 24436584Sbostic setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase); 24510175Seric r = reply(m); 2468005Seric if (r < 0 || REPLYTYPE(r) == 4) 2474865Seric return (EX_TEMPFAIL); 2487963Seric else if (REPLYTYPE(r) == 2) 2497963Seric return (EX_OK); 2507964Seric else if (r == 550 || r == 551 || r == 553) 2517964Seric return (EX_NOUSER); 2527964Seric else if (r == 552 || r == 554) 2537964Seric return (EX_UNAVAILABLE); 2547964Seric return (EX_PROTOCOL); 2554797Seric } 2564797Seric /* 25710175Seric ** SMTPDATA -- send the data and clean up the transaction. 2584684Seric ** 2594684Seric ** Parameters: 2604865Seric ** m -- mailer being sent to. 2616980Seric ** e -- the envelope for this message. 2624684Seric ** 2634684Seric ** Returns: 2644976Seric ** exit status corresponding to DATA command. 2654684Seric ** 2664684Seric ** Side Effects: 2674865Seric ** none. 2684684Seric */ 2694684Seric 27010175Seric smtpdata(m, e) 2714865Seric struct mailer *m; 2726980Seric register ENVELOPE *e; 2734684Seric { 2744684Seric register int r; 2754684Seric 2764797Seric /* 2774797Seric ** Send the data. 27810175Seric ** First send the command and check that it is ok. 27910175Seric ** Then send the data. 28010175Seric ** Follow it up with a dot to terminate. 28110175Seric ** Finally get the results of the transaction. 2824797Seric */ 2834797Seric 28410175Seric /* send the command and check ok to proceed */ 28510175Seric smtpmessage("DATA", m); 28624944Seric SmtpPhase = "DATA wait"; 28736584Sbostic setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase); 28810175Seric r = reply(m); 2898005Seric if (r < 0 || REPLYTYPE(r) == 4) 2904797Seric return (EX_TEMPFAIL); 2917963Seric else if (r == 554) 2927963Seric return (EX_UNAVAILABLE); 2937963Seric else if (r != 354) 2947964Seric return (EX_PROTOCOL); 29510175Seric 29610175Seric /* now output the actual message */ 29710175Seric (*e->e_puthdr)(SmtpOut, m, CurEnv); 29810175Seric putline("\n", SmtpOut, m); 29910175Seric (*e->e_putbody)(SmtpOut, m, CurEnv); 30010175Seric 30110175Seric /* terminate the message */ 30210328Seric fprintf(SmtpOut, ".%s", m->m_eol); 30310215Seric if (Verbose && !HoldErrs) 30410215Seric nmessage(Arpa_Info, ">>> ."); 30510175Seric 30610175Seric /* check for the results of the transaction */ 30724944Seric SmtpPhase = "result wait"; 30836584Sbostic setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase); 30910175Seric r = reply(m); 3108005Seric if (r < 0 || REPLYTYPE(r) == 4) 3114797Seric return (EX_TEMPFAIL); 3127963Seric else if (r == 250) 3137963Seric return (EX_OK); 3147963Seric else if (r == 552 || r == 554) 3157963Seric return (EX_UNAVAILABLE); 3167964Seric return (EX_PROTOCOL); 3174684Seric } 3184684Seric /* 3194865Seric ** SMTPQUIT -- close the SMTP connection. 3204865Seric ** 3214865Seric ** Parameters: 32215535Seric ** m -- a pointer to the mailer. 3234865Seric ** 3244865Seric ** Returns: 3254865Seric ** none. 3264865Seric ** 3274865Seric ** Side Effects: 3284865Seric ** sends the final protocol and closes the connection. 3294865Seric */ 3304865Seric 33115535Seric smtpquit(m) 33210175Seric register MAILER *m; 3334865Seric { 3349391Seric int i; 3354865Seric 33610148Seric /* if the connection is already closed, don't bother */ 33710148Seric if (SmtpIn == NULL) 33810148Seric return; 33910148Seric 34010148Seric /* send the quit message if not a forced quit */ 34111159Seric if (SmtpState == SMTP_OPEN || SmtpState == SMTP_SSD) 3429391Seric { 34310175Seric smtpmessage("QUIT", m); 34410175Seric (void) reply(m); 34511159Seric if (SmtpState == SMTP_CLOSED) 34610159Seric return; 3479391Seric } 3489391Seric 34910148Seric /* now actually close the connection */ 3507229Seric (void) fclose(SmtpIn); 3517229Seric (void) fclose(SmtpOut); 35210148Seric SmtpIn = SmtpOut = NULL; 35311159Seric SmtpState = SMTP_CLOSED; 35410148Seric 35510148Seric /* and pick up the zombie */ 35615535Seric i = endmailer(SmtpPid, m->m_argv[0]); 3579391Seric if (i != EX_OK) 35815535Seric syserr("smtpquit %s: stat %d", m->m_argv[0], i); 3594865Seric } 3604865Seric /* 3614684Seric ** REPLY -- read arpanet reply 3624684Seric ** 3634684Seric ** Parameters: 36410175Seric ** m -- the mailer we are reading the reply from. 3654684Seric ** 3664684Seric ** Returns: 3674684Seric ** reply code it reads. 3684684Seric ** 3694684Seric ** Side Effects: 3704684Seric ** flushes the mail file. 3714684Seric */ 3724684Seric 37310175Seric reply(m) 37410688Seric MAILER *m; 3754684Seric { 3764865Seric (void) fflush(SmtpOut); 3774684Seric 3787677Seric if (tTd(18, 1)) 3794796Seric printf("reply\n"); 3804796Seric 3817356Seric /* 3827356Seric ** Read the input line, being careful not to hang. 3837356Seric */ 3847356Seric 3854684Seric for (;;) 3864684Seric { 3874684Seric register int r; 3887356Seric register char *p; 3894684Seric 3907685Seric /* actually do the read */ 3919547Seric if (CurEnv->e_xfp != NULL) 3929547Seric (void) fflush(CurEnv->e_xfp); /* for debugging */ 3937356Seric 39410054Seric /* if we are in the process of closing just give the code */ 39511159Seric if (SmtpState == SMTP_CLOSED) 39610054Seric return (SMTPCLOSING); 39710054Seric 39810054Seric /* get the line from the other side */ 39910054Seric p = sfgets(SmtpReplyBuffer, sizeof SmtpReplyBuffer, SmtpIn); 40010054Seric if (p == NULL) 40110131Seric { 40210148Seric extern char MsgBuf[]; /* err.c */ 40310148Seric extern char Arpa_TSyserr[]; /* conf.c */ 40410148Seric 40521065Seric /* if the remote end closed early, fake an error */ 40621065Seric if (errno == 0) 40721065Seric # ifdef ECONNRESET 40821065Seric errno = ECONNRESET; 40921065Seric # else ECONNRESET 41021065Seric errno = EPIPE; 41121065Seric # endif ECONNRESET 41221065Seric 41310148Seric message(Arpa_TSyserr, "reply: read error"); 41410420Seric /* if debugging, pause so we can see state */ 41510420Seric if (tTd(18, 100)) 41610420Seric pause(); 41710148Seric # ifdef LOG 41836234Skarels syslog(LOG_INFO, "%s", &MsgBuf[4]); 41910148Seric # endif LOG 42011159Seric SmtpState = SMTP_CLOSED; 42115535Seric smtpquit(m); 42210054Seric return (-1); 42310131Seric } 42410054Seric fixcrlf(SmtpReplyBuffer, TRUE); 42510054Seric 42614900Seric if (CurEnv->e_xfp != NULL && index("45", SmtpReplyBuffer[0]) != NULL) 42714900Seric { 42814900Seric /* serious error -- log the previous command */ 42914900Seric if (SmtpMsgBuffer[0] != '\0') 43014900Seric fprintf(CurEnv->e_xfp, ">>> %s\n", SmtpMsgBuffer); 43114900Seric SmtpMsgBuffer[0] = '\0'; 43214900Seric 43314900Seric /* now log the message as from the other side */ 43414900Seric fprintf(CurEnv->e_xfp, "<<< %s\n", SmtpReplyBuffer); 43514900Seric } 43614900Seric 43714900Seric /* display the input for verbose mode */ 4387229Seric if (Verbose && !HoldErrs) 4399391Seric nmessage(Arpa_Info, "%s", SmtpReplyBuffer); 4407356Seric 4417356Seric /* if continuation is required, we can go on */ 4429391Seric if (SmtpReplyBuffer[3] == '-' || !isdigit(SmtpReplyBuffer[0])) 4434684Seric continue; 4447356Seric 4457356Seric /* decode the reply code */ 4469391Seric r = atoi(SmtpReplyBuffer); 4477356Seric 4487356Seric /* extra semantics: 0xx codes are "informational" */ 4494684Seric if (r < 100) 4504684Seric continue; 4517356Seric 4529391Seric /* reply code 421 is "Service Shutting Down" */ 45311159Seric if (r == SMTPCLOSING && SmtpState != SMTP_SSD) 4549391Seric { 45510054Seric /* send the quit protocol */ 45611159Seric SmtpState = SMTP_SSD; 45715535Seric smtpquit(m); 4589391Seric } 4599391Seric 46021065Seric /* save temporary failure messages for posterity */ 46121065Seric if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0') 46221065Seric (void) strcpy(SmtpError, &SmtpReplyBuffer[4]); 46321065Seric 4644684Seric return (r); 4654684Seric } 4664684Seric } 4674796Seric /* 4684865Seric ** SMTPMESSAGE -- send message to server 4694796Seric ** 4704796Seric ** Parameters: 4714796Seric ** f -- format 47210175Seric ** m -- the mailer to control formatting. 4734796Seric ** a, b, c -- parameters 4744796Seric ** 4754796Seric ** Returns: 4764796Seric ** none. 4774796Seric ** 4784796Seric ** Side Effects: 4794865Seric ** writes message to SmtpOut. 4804796Seric */ 4814796Seric 4824865Seric /*VARARGS1*/ 48310175Seric smtpmessage(f, m, a, b, c) 4844796Seric char *f; 48510175Seric MAILER *m; 4864796Seric { 48714900Seric (void) sprintf(SmtpMsgBuffer, f, a, b, c); 4887677Seric if (tTd(18, 1) || (Verbose && !HoldErrs)) 48914900Seric nmessage(Arpa_Info, ">>> %s", SmtpMsgBuffer); 49011159Seric if (SmtpOut != NULL) 49140997Sbostic fprintf(SmtpOut, "%s%s", SmtpMsgBuffer, 49240997Sbostic m == 0 ? "\r\n" : m->m_eol); 4934796Seric } 4945182Seric 4955182Seric # endif SMTP 496