1*22716Sdist /* 2*22716Sdist ** Sendmail 3*22716Sdist ** Copyright (c) 1983 Eric P. Allman 4*22716Sdist ** Berkeley, California 5*22716Sdist ** 6*22716Sdist ** Copyright (c) 1983 Regents of the University of California. 7*22716Sdist ** All rights reserved. The Berkeley software License Agreement 8*22716Sdist ** specifies the terms and conditions for redistribution. 9*22716Sdist */ 10*22716Sdist 11*22716Sdist #ifndef lint 12*22716Sdist static char SccsId[] = "@(#)usersmtp.c 5.1 (Berkeley) 06/07/85"; 13*22716Sdist #endif not lint 14*22716Sdist 154796Seric # include <ctype.h> 164684Seric # include <sysexits.h> 1721065Seric # include <errno.h> 184865Seric # include "sendmail.h" 194684Seric 205182Seric # ifndef SMTP 21*22716Sdist SCCSID(@(#)usersmtp.c 5.1 06/07/85 (no SMTP)); 225182Seric # else SMTP 234684Seric 24*22716Sdist SCCSID(@(#)usersmtp.c 5.1 06/07/85); 255182Seric 269391Seric 279391Seric 284684Seric /* 299391Seric ** USERSMTP -- run SMTP protocol from the user end. 309391Seric ** 319391Seric ** This protocol is described in RFC821. 329391Seric */ 339391Seric 349391Seric #define REPLYTYPE(r) ((r) / 100) /* first digit of reply code */ 359391Seric #define REPLYCLASS(r) (((r) / 10) % 10) /* second digit of reply code */ 369391Seric #define SMTPCLOSING 421 /* "Service Shutting Down" */ 379391Seric 3814900Seric char SmtpMsgBuffer[MAXLINE]; /* buffer for commands */ 3910054Seric char SmtpReplyBuffer[MAXLINE]; /* buffer for replies */ 4021065Seric char SmtpError[MAXLINE] = ""; /* save failure error messages */ 4110054Seric FILE *SmtpOut; /* output file */ 4210054Seric FILE *SmtpIn; /* input file */ 4310054Seric int SmtpPid; /* pid of mailer */ 4411159Seric 4511159Seric /* following represents the state of the SMTP connection */ 4611159Seric int SmtpState; /* connection state, see below */ 4711159Seric 4811159Seric #define SMTP_CLOSED 0 /* connection is closed */ 4911159Seric #define SMTP_OPEN 1 /* connection is open for business */ 5011159Seric #define SMTP_SSD 2 /* service shutting down */ 519391Seric /* 524865Seric ** SMTPINIT -- initialize SMTP. 534684Seric ** 544865Seric ** Opens the connection and sends the initial protocol. 554684Seric ** 564684Seric ** Parameters: 574865Seric ** m -- mailer to create connection to. 584865Seric ** pvp -- pointer to parameter vector to pass to 594865Seric ** the mailer. 604684Seric ** 614684Seric ** Returns: 624865Seric ** appropriate exit status -- EX_OK on success. 6314913Seric ** If not EX_OK, it should close the connection. 644684Seric ** 654684Seric ** Side Effects: 664865Seric ** creates connection and sends initial protocol. 674684Seric */ 684684Seric 6914886Seric jmp_buf CtxGreeting; 7014886Seric 7110175Seric smtpinit(m, pvp) 724865Seric struct mailer *m; 734865Seric char **pvp; 744684Seric { 754865Seric register int r; 7614886Seric EVENT *gte; 774865Seric char buf[MAXNAME]; 7814886Seric extern greettimeout(); 794684Seric 804865Seric /* 814865Seric ** Open the connection to the mailer. 824865Seric */ 834684Seric 8411159Seric #ifdef DEBUG 8511159Seric if (SmtpState == SMTP_OPEN) 8611159Seric syserr("smtpinit: already open"); 8711159Seric #endif DEBUG 8811159Seric 896051Seric SmtpIn = SmtpOut = NULL; 9011159Seric SmtpState = SMTP_CLOSED; 9121065Seric SmtpError[0] = '\0'; 9210175Seric SmtpPid = openmailer(m, pvp, (ADDRESS *) NULL, TRUE, &SmtpOut, &SmtpIn); 936051Seric if (SmtpPid < 0) 946051Seric { 956051Seric # ifdef DEBUG 967677Seric if (tTd(18, 1)) 979391Seric printf("smtpinit: cannot open %s: stat %d errno %d\n", 989391Seric pvp[0], ExitStat, errno); 996051Seric # endif DEBUG 10015139Seric if (CurEnv->e_xfp != NULL) 10115139Seric { 10221065Seric register char *p; 10315139Seric extern char *errstring(); 10421065Seric extern char *statstring(); 10515139Seric 10621065Seric if (errno == 0) 10721065Seric { 10821065Seric p = statstring(ExitStat); 10921065Seric fprintf(CurEnv->e_xfp, 11021065Seric "%.3s %s.%s... %s\n", 11121065Seric p, pvp[1], m->m_name, p); 11221065Seric } 11321065Seric else 11421065Seric { 11521065Seric fprintf(CurEnv->e_xfp, 11621065Seric "421 %s.%s... Deferred: %s\n", 11721065Seric pvp[1], m->m_name, errstring(errno)); 11821065Seric } 11915139Seric } 1206051Seric return (ExitStat); 1216051Seric } 12211159Seric SmtpState = SMTP_OPEN; 1234796Seric 1244865Seric /* 1254865Seric ** Get the greeting message. 12614913Seric ** This should appear spontaneously. Give it five minutes to 12714886Seric ** happen. 1284865Seric */ 1294797Seric 13014886Seric if (setjmp(CtxGreeting) != 0) 13114913Seric goto tempfail; 13216141Seric gte = setevent((time_t) 300, greettimeout, 0); 13310175Seric r = reply(m); 13414886Seric clrevent(gte); 1358005Seric if (r < 0 || REPLYTYPE(r) != 2) 13614913Seric goto tempfail; 1374684Seric 1384865Seric /* 1394976Seric ** Send the HELO command. 1407963Seric ** My mother taught me to always introduce myself. 1414976Seric */ 1424976Seric 14310175Seric smtpmessage("HELO %s", m, HostName); 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 { 18510175Seric smtpmessage("MAIL From:<@%s%c%s>", m, HostName, 18610308Seric buf[0] == '@' ? ',' : ':', buf); 1878436Seric } 18810175Seric r = reply(m); 1898005Seric if (r < 0 || REPLYTYPE(r) == 4) 19014913Seric goto tempfail; 1917963Seric else if (r == 250) 1927963Seric return (EX_OK); 1937963Seric else if (r == 552) 19414913Seric goto unavailable; 19514913Seric 19614913Seric /* protocol error -- close up */ 19714913Seric smtpquit(m); 1987964Seric return (EX_PROTOCOL); 19914913Seric 20014913Seric /* signal a temporary failure */ 20114913Seric tempfail: 20214913Seric smtpquit(m); 20316891Seric CurEnv->e_flags &= ~EF_FATALERRS; 20414913Seric return (EX_TEMPFAIL); 20514913Seric 20614913Seric /* signal service unavailable */ 20714913Seric unavailable: 20814913Seric smtpquit(m); 20914913Seric return (EX_UNAVAILABLE); 2104684Seric } 21114886Seric 21214886Seric 21314886Seric static 21414886Seric greettimeout() 21514886Seric { 21614886Seric /* timeout reading the greeting message */ 21714886Seric longjmp(CtxGreeting, 1); 21814886Seric } 2194684Seric /* 2204976Seric ** SMTPRCPT -- designate recipient. 2214797Seric ** 2224797Seric ** Parameters: 2234865Seric ** to -- address of recipient. 22410175Seric ** m -- the mailer we are sending to. 2254797Seric ** 2264797Seric ** Returns: 2274865Seric ** exit status corresponding to recipient status. 2284797Seric ** 2294797Seric ** Side Effects: 2304865Seric ** Sends the mail via SMTP. 2314797Seric */ 2324797Seric 23310175Seric smtprcpt(to, m) 2344865Seric ADDRESS *to; 23510175Seric register MAILER *m; 2364797Seric { 2374797Seric register int r; 23810308Seric extern char *remotename(); 2394797Seric 24010308Seric smtpmessage("RCPT To:<%s>", m, remotename(to->q_user, m, FALSE, TRUE)); 2414865Seric 24210175Seric r = reply(m); 2438005Seric if (r < 0 || REPLYTYPE(r) == 4) 2444865Seric return (EX_TEMPFAIL); 2457963Seric else if (REPLYTYPE(r) == 2) 2467963Seric return (EX_OK); 2477964Seric else if (r == 550 || r == 551 || r == 553) 2487964Seric return (EX_NOUSER); 2497964Seric else if (r == 552 || r == 554) 2507964Seric return (EX_UNAVAILABLE); 2517964Seric return (EX_PROTOCOL); 2524797Seric } 2534797Seric /* 25410175Seric ** SMTPDATA -- send the data and clean up the transaction. 2554684Seric ** 2564684Seric ** Parameters: 2574865Seric ** m -- mailer being sent to. 2586980Seric ** e -- the envelope for this message. 2594684Seric ** 2604684Seric ** Returns: 2614976Seric ** exit status corresponding to DATA command. 2624684Seric ** 2634684Seric ** Side Effects: 2644865Seric ** none. 2654684Seric */ 2664684Seric 26710175Seric smtpdata(m, e) 2684865Seric struct mailer *m; 2696980Seric register ENVELOPE *e; 2704684Seric { 2714684Seric register int r; 2724684Seric 2734797Seric /* 2744797Seric ** Send the data. 27510175Seric ** First send the command and check that it is ok. 27610175Seric ** Then send the data. 27710175Seric ** Follow it up with a dot to terminate. 27810175Seric ** Finally get the results of the transaction. 2794797Seric */ 2804797Seric 28110175Seric /* send the command and check ok to proceed */ 28210175Seric smtpmessage("DATA", m); 28310175Seric r = reply(m); 2848005Seric if (r < 0 || REPLYTYPE(r) == 4) 2854797Seric return (EX_TEMPFAIL); 2867963Seric else if (r == 554) 2877963Seric return (EX_UNAVAILABLE); 2887963Seric else if (r != 354) 2897964Seric return (EX_PROTOCOL); 29010175Seric 29110175Seric /* now output the actual message */ 29210175Seric (*e->e_puthdr)(SmtpOut, m, CurEnv); 29310175Seric putline("\n", SmtpOut, m); 29410175Seric (*e->e_putbody)(SmtpOut, m, CurEnv); 29510175Seric 29610175Seric /* terminate the message */ 29710328Seric fprintf(SmtpOut, ".%s", m->m_eol); 29810215Seric if (Verbose && !HoldErrs) 29910215Seric nmessage(Arpa_Info, ">>> ."); 30010175Seric 30110175Seric /* check for the results of the transaction */ 30210175Seric r = reply(m); 3038005Seric if (r < 0 || REPLYTYPE(r) == 4) 3044797Seric return (EX_TEMPFAIL); 3057963Seric else if (r == 250) 3067963Seric return (EX_OK); 3077963Seric else if (r == 552 || r == 554) 3087963Seric return (EX_UNAVAILABLE); 3097964Seric return (EX_PROTOCOL); 3104684Seric } 3114684Seric /* 3124865Seric ** SMTPQUIT -- close the SMTP connection. 3134865Seric ** 3144865Seric ** Parameters: 31515535Seric ** m -- a pointer to the mailer. 3164865Seric ** 3174865Seric ** Returns: 3184865Seric ** none. 3194865Seric ** 3204865Seric ** Side Effects: 3214865Seric ** sends the final protocol and closes the connection. 3224865Seric */ 3234865Seric 32415535Seric smtpquit(m) 32510175Seric register MAILER *m; 3264865Seric { 3279391Seric int i; 3284865Seric 32910148Seric /* if the connection is already closed, don't bother */ 33010148Seric if (SmtpIn == NULL) 33110148Seric return; 33210148Seric 33310148Seric /* send the quit message if not a forced quit */ 33411159Seric if (SmtpState == SMTP_OPEN || SmtpState == SMTP_SSD) 3359391Seric { 33610175Seric smtpmessage("QUIT", m); 33710175Seric (void) reply(m); 33811159Seric if (SmtpState == SMTP_CLOSED) 33910159Seric return; 3409391Seric } 3419391Seric 34210148Seric /* now actually close the connection */ 3437229Seric (void) fclose(SmtpIn); 3447229Seric (void) fclose(SmtpOut); 34510148Seric SmtpIn = SmtpOut = NULL; 34611159Seric SmtpState = SMTP_CLOSED; 34710148Seric 34810148Seric /* and pick up the zombie */ 34915535Seric i = endmailer(SmtpPid, m->m_argv[0]); 3509391Seric if (i != EX_OK) 35115535Seric syserr("smtpquit %s: stat %d", m->m_argv[0], i); 3524865Seric } 3534865Seric /* 3544684Seric ** REPLY -- read arpanet reply 3554684Seric ** 3564684Seric ** Parameters: 35710175Seric ** m -- the mailer we are reading the reply from. 3584684Seric ** 3594684Seric ** Returns: 3604684Seric ** reply code it reads. 3614684Seric ** 3624684Seric ** Side Effects: 3634684Seric ** flushes the mail file. 3644684Seric */ 3654684Seric 36610175Seric reply(m) 36710688Seric MAILER *m; 3684684Seric { 3694865Seric (void) fflush(SmtpOut); 3704684Seric 3717677Seric if (tTd(18, 1)) 3724796Seric printf("reply\n"); 3734796Seric 3747356Seric /* 3757356Seric ** Read the input line, being careful not to hang. 3767356Seric */ 3777356Seric 3784684Seric for (;;) 3794684Seric { 3804684Seric register int r; 3817356Seric register char *p; 3824684Seric 3837685Seric /* actually do the read */ 3849547Seric if (CurEnv->e_xfp != NULL) 3859547Seric (void) fflush(CurEnv->e_xfp); /* for debugging */ 3867356Seric 38710054Seric /* if we are in the process of closing just give the code */ 38811159Seric if (SmtpState == SMTP_CLOSED) 38910054Seric return (SMTPCLOSING); 39010054Seric 39110054Seric /* get the line from the other side */ 39210054Seric p = sfgets(SmtpReplyBuffer, sizeof SmtpReplyBuffer, SmtpIn); 39310054Seric if (p == NULL) 39410131Seric { 39510148Seric extern char MsgBuf[]; /* err.c */ 39610148Seric extern char Arpa_TSyserr[]; /* conf.c */ 39710148Seric 39821065Seric /* if the remote end closed early, fake an error */ 39921065Seric if (errno == 0) 40021065Seric # ifdef ECONNRESET 40121065Seric errno = ECONNRESET; 40221065Seric # else ECONNRESET 40321065Seric errno = EPIPE; 40421065Seric # endif ECONNRESET 40521065Seric 40610148Seric message(Arpa_TSyserr, "reply: read error"); 40710420Seric # ifdef DEBUG 40810420Seric /* if debugging, pause so we can see state */ 40910420Seric if (tTd(18, 100)) 41010420Seric pause(); 41110420Seric # endif DEBUG 41210148Seric # ifdef LOG 41318573Smiriam syslog(LOG_MAIL, "%s", &MsgBuf[4]); 41410148Seric # endif LOG 41511159Seric SmtpState = SMTP_CLOSED; 41615535Seric smtpquit(m); 41710054Seric return (-1); 41810131Seric } 41910054Seric fixcrlf(SmtpReplyBuffer, TRUE); 42010054Seric 42114900Seric if (CurEnv->e_xfp != NULL && index("45", SmtpReplyBuffer[0]) != NULL) 42214900Seric { 42314900Seric /* serious error -- log the previous command */ 42414900Seric if (SmtpMsgBuffer[0] != '\0') 42514900Seric fprintf(CurEnv->e_xfp, ">>> %s\n", SmtpMsgBuffer); 42614900Seric SmtpMsgBuffer[0] = '\0'; 42714900Seric 42814900Seric /* now log the message as from the other side */ 42914900Seric fprintf(CurEnv->e_xfp, "<<< %s\n", SmtpReplyBuffer); 43014900Seric } 43114900Seric 43214900Seric /* display the input for verbose mode */ 4337229Seric if (Verbose && !HoldErrs) 4349391Seric nmessage(Arpa_Info, "%s", SmtpReplyBuffer); 4357356Seric 4367356Seric /* if continuation is required, we can go on */ 4379391Seric if (SmtpReplyBuffer[3] == '-' || !isdigit(SmtpReplyBuffer[0])) 4384684Seric continue; 4397356Seric 4407356Seric /* decode the reply code */ 4419391Seric r = atoi(SmtpReplyBuffer); 4427356Seric 4437356Seric /* extra semantics: 0xx codes are "informational" */ 4444684Seric if (r < 100) 4454684Seric continue; 4467356Seric 4479391Seric /* reply code 421 is "Service Shutting Down" */ 44811159Seric if (r == SMTPCLOSING && SmtpState != SMTP_SSD) 4499391Seric { 45010054Seric /* send the quit protocol */ 45111159Seric SmtpState = SMTP_SSD; 45215535Seric smtpquit(m); 4539391Seric } 4549391Seric 45521065Seric /* save temporary failure messages for posterity */ 45621065Seric if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0') 45721065Seric (void) strcpy(SmtpError, &SmtpReplyBuffer[4]); 45821065Seric 4594684Seric return (r); 4604684Seric } 4614684Seric } 4624796Seric /* 4634865Seric ** SMTPMESSAGE -- send message to server 4644796Seric ** 4654796Seric ** Parameters: 4664796Seric ** f -- format 46710175Seric ** m -- the mailer to control formatting. 4684796Seric ** a, b, c -- parameters 4694796Seric ** 4704796Seric ** Returns: 4714796Seric ** none. 4724796Seric ** 4734796Seric ** Side Effects: 4744865Seric ** writes message to SmtpOut. 4754796Seric */ 4764796Seric 4774865Seric /*VARARGS1*/ 47810175Seric smtpmessage(f, m, a, b, c) 4794796Seric char *f; 48010175Seric MAILER *m; 4814796Seric { 48214900Seric (void) sprintf(SmtpMsgBuffer, f, a, b, c); 4837677Seric if (tTd(18, 1) || (Verbose && !HoldErrs)) 48414900Seric nmessage(Arpa_Info, ">>> %s", SmtpMsgBuffer); 48511159Seric if (SmtpOut != NULL) 48614900Seric fprintf(SmtpOut, "%s%s", SmtpMsgBuffer, m->m_eol); 4874796Seric } 4885182Seric 4895182Seric # endif SMTP 490