122716Sdist /* 222716Sdist ** Sendmail 322716Sdist ** Copyright (c) 1983 Eric P. Allman 422716Sdist ** Berkeley, California 522716Sdist ** 622716Sdist ** Copyright (c) 1983 Regents of the University of California. 722716Sdist ** All rights reserved. The Berkeley software License Agreement 822716Sdist ** specifies the terms and conditions for redistribution. 922716Sdist */ 1022716Sdist 1122716Sdist 124796Seric # include <ctype.h> 134684Seric # include <sysexits.h> 1421065Seric # include <errno.h> 154865Seric # include "sendmail.h" 164684Seric 175182Seric # ifndef SMTP 1823124Seric # ifndef lint 19*24945Seric static char SccsId[] = "@(#)usersmtp.c 5.5 (Berkeley) 09/19/85 (no SMTP)"; 2023124Seric # endif not lint 215182Seric # else SMTP 224684Seric 2323124Seric # ifndef lint 24*24945Seric static char SccsId[] = "@(#)usersmtp.c 5.5 (Berkeley) 09/19/85"; 2523124Seric # endif not lint 265182Seric 279391Seric 289391Seric 294684Seric /* 309391Seric ** USERSMTP -- run SMTP protocol from the user end. 319391Seric ** 329391Seric ** This protocol is described in RFC821. 339391Seric */ 349391Seric 359391Seric #define REPLYTYPE(r) ((r) / 100) /* first digit of reply code */ 369391Seric #define REPLYCLASS(r) (((r) / 10) % 10) /* second digit of reply code */ 379391Seric #define SMTPCLOSING 421 /* "Service Shutting Down" */ 389391Seric 3914900Seric char SmtpMsgBuffer[MAXLINE]; /* buffer for commands */ 4010054Seric char SmtpReplyBuffer[MAXLINE]; /* buffer for replies */ 4121065Seric char SmtpError[MAXLINE] = ""; /* save failure error messages */ 4210054Seric FILE *SmtpOut; /* output file */ 4310054Seric FILE *SmtpIn; /* input file */ 4410054Seric int SmtpPid; /* pid of mailer */ 4511159Seric 4611159Seric /* following represents the state of the SMTP connection */ 4711159Seric int SmtpState; /* connection state, see below */ 4811159Seric 4911159Seric #define SMTP_CLOSED 0 /* connection is closed */ 5011159Seric #define SMTP_OPEN 1 /* connection is open for business */ 5111159Seric #define SMTP_SSD 2 /* service shutting down */ 529391Seric /* 534865Seric ** SMTPINIT -- initialize SMTP. 544684Seric ** 554865Seric ** Opens the connection and sends the initial protocol. 564684Seric ** 574684Seric ** Parameters: 584865Seric ** m -- mailer to create connection to. 594865Seric ** pvp -- pointer to parameter vector to pass to 604865Seric ** the mailer. 614684Seric ** 624684Seric ** Returns: 634865Seric ** appropriate exit status -- EX_OK on success. 6414913Seric ** If not EX_OK, it should close the connection. 654684Seric ** 664684Seric ** Side Effects: 674865Seric ** creates connection and sends initial protocol. 684684Seric */ 694684Seric 7014886Seric jmp_buf CtxGreeting; 7114886Seric 7210175Seric smtpinit(m, pvp) 734865Seric struct mailer *m; 744865Seric char **pvp; 754684Seric { 764865Seric register int r; 7714886Seric EVENT *gte; 784865Seric char buf[MAXNAME]; 7914886Seric extern greettimeout(); 804684Seric 814865Seric /* 824865Seric ** Open the connection to the mailer. 834865Seric */ 844684Seric 8511159Seric #ifdef DEBUG 8611159Seric if (SmtpState == SMTP_OPEN) 8711159Seric syserr("smtpinit: already open"); 8811159Seric #endif DEBUG 8911159Seric 906051Seric SmtpIn = SmtpOut = NULL; 9111159Seric SmtpState = SMTP_CLOSED; 9221065Seric SmtpError[0] = '\0'; 9324944Seric SmtpPhase = "user open"; 9410175Seric SmtpPid = openmailer(m, pvp, (ADDRESS *) NULL, TRUE, &SmtpOut, &SmtpIn); 956051Seric if (SmtpPid < 0) 966051Seric { 976051Seric # ifdef DEBUG 987677Seric if (tTd(18, 1)) 999391Seric printf("smtpinit: cannot open %s: stat %d errno %d\n", 1009391Seric pvp[0], ExitStat, errno); 1016051Seric # endif DEBUG 10215139Seric if (CurEnv->e_xfp != NULL) 10315139Seric { 10421065Seric register char *p; 10515139Seric extern char *errstring(); 10621065Seric extern char *statstring(); 10715139Seric 10821065Seric if (errno == 0) 10921065Seric { 11021065Seric p = statstring(ExitStat); 11121065Seric fprintf(CurEnv->e_xfp, 11221065Seric "%.3s %s.%s... %s\n", 11321065Seric p, pvp[1], m->m_name, p); 11421065Seric } 11521065Seric else 11621065Seric { 11721065Seric fprintf(CurEnv->e_xfp, 11821065Seric "421 %s.%s... Deferred: %s\n", 11921065Seric pvp[1], m->m_name, errstring(errno)); 12021065Seric } 12115139Seric } 1226051Seric return (ExitStat); 1236051Seric } 12411159Seric SmtpState = SMTP_OPEN; 1254796Seric 1264865Seric /* 1274865Seric ** Get the greeting message. 12814913Seric ** This should appear spontaneously. Give it five minutes to 12914886Seric ** happen. 1304865Seric */ 1314797Seric 13214886Seric if (setjmp(CtxGreeting) != 0) 13314913Seric goto tempfail; 13416141Seric gte = setevent((time_t) 300, greettimeout, 0); 13524944Seric SmtpPhase = "greeting wait"; 13610175Seric r = reply(m); 13714886Seric clrevent(gte); 1388005Seric if (r < 0 || REPLYTYPE(r) != 2) 13914913Seric goto tempfail; 1404684Seric 1414865Seric /* 1424976Seric ** Send the HELO command. 1437963Seric ** My mother taught me to always introduce myself. 1444976Seric */ 1454976Seric 14610175Seric smtpmessage("HELO %s", m, HostName); 14724944Seric SmtpPhase = "HELO wait"; 14810175Seric r = reply(m); 1498005Seric if (r < 0) 15014913Seric goto tempfail; 1518005Seric else if (REPLYTYPE(r) == 5) 15214913Seric goto unavailable; 1537963Seric else if (REPLYTYPE(r) != 2) 15414913Seric goto tempfail; 1554976Seric 1564976Seric /* 1579315Seric ** If this is expected to be another sendmail, send some internal 1589315Seric ** commands. 1599315Seric */ 1609315Seric 16110688Seric if (bitnset(M_INTERNAL, m->m_flags)) 1629315Seric { 1639315Seric /* tell it to be verbose */ 16410175Seric smtpmessage("VERB", m); 16510175Seric r = reply(m); 1669315Seric if (r < 0) 16714913Seric goto tempfail; 1689315Seric 1699315Seric /* tell it we will be sending one transaction only */ 17010175Seric smtpmessage("ONEX", m); 17110175Seric r = reply(m); 1729315Seric if (r < 0) 17314913Seric goto tempfail; 1749315Seric } 1759315Seric 1769315Seric /* 1774865Seric ** Send the MAIL command. 1784865Seric ** Designates the sender. 1794865Seric */ 1804796Seric 18116156Seric expand("\001g", buf, &buf[sizeof buf - 1], CurEnv); 1828436Seric if (CurEnv->e_from.q_mailer == LocalMailer || 18310688Seric !bitnset(M_FROMPATH, m->m_flags)) 1848436Seric { 18510308Seric smtpmessage("MAIL From:<%s>", m, buf); 1868436Seric } 1878436Seric else 1888436Seric { 18910175Seric smtpmessage("MAIL From:<@%s%c%s>", m, HostName, 19010308Seric buf[0] == '@' ? ',' : ':', buf); 1918436Seric } 19224944Seric SmtpPhase = "MAIL wait"; 19310175Seric r = reply(m); 1948005Seric if (r < 0 || REPLYTYPE(r) == 4) 19514913Seric goto tempfail; 1967963Seric else if (r == 250) 1977963Seric return (EX_OK); 1987963Seric else if (r == 552) 19914913Seric goto unavailable; 20014913Seric 20114913Seric /* protocol error -- close up */ 20214913Seric smtpquit(m); 2037964Seric return (EX_PROTOCOL); 20414913Seric 20514913Seric /* signal a temporary failure */ 20614913Seric tempfail: 20714913Seric smtpquit(m); 20816891Seric CurEnv->e_flags &= ~EF_FATALERRS; 20914913Seric return (EX_TEMPFAIL); 21014913Seric 21114913Seric /* signal service unavailable */ 21214913Seric unavailable: 21314913Seric smtpquit(m); 21414913Seric return (EX_UNAVAILABLE); 2154684Seric } 21614886Seric 21714886Seric 21814886Seric static 21914886Seric greettimeout() 22014886Seric { 22114886Seric /* timeout reading the greeting message */ 22214886Seric longjmp(CtxGreeting, 1); 22314886Seric } 2244684Seric /* 2254976Seric ** SMTPRCPT -- designate recipient. 2264797Seric ** 2274797Seric ** Parameters: 2284865Seric ** to -- address of recipient. 22910175Seric ** m -- the mailer we are sending to. 2304797Seric ** 2314797Seric ** Returns: 2324865Seric ** exit status corresponding to recipient status. 2334797Seric ** 2344797Seric ** Side Effects: 2354865Seric ** Sends the mail via SMTP. 2364797Seric */ 2374797Seric 23810175Seric smtprcpt(to, m) 2394865Seric ADDRESS *to; 24010175Seric register MAILER *m; 2414797Seric { 2424797Seric register int r; 24310308Seric extern char *remotename(); 2444797Seric 24510308Seric smtpmessage("RCPT To:<%s>", m, remotename(to->q_user, m, FALSE, TRUE)); 2464865Seric 24724944Seric SmtpPhase = "RCPT wait"; 24810175Seric r = reply(m); 2498005Seric if (r < 0 || REPLYTYPE(r) == 4) 2504865Seric return (EX_TEMPFAIL); 2517963Seric else if (REPLYTYPE(r) == 2) 2527963Seric return (EX_OK); 2537964Seric else if (r == 550 || r == 551 || r == 553) 2547964Seric return (EX_NOUSER); 2557964Seric else if (r == 552 || r == 554) 2567964Seric return (EX_UNAVAILABLE); 2577964Seric return (EX_PROTOCOL); 2584797Seric } 2594797Seric /* 26010175Seric ** SMTPDATA -- send the data and clean up the transaction. 2614684Seric ** 2624684Seric ** Parameters: 2634865Seric ** m -- mailer being sent to. 2646980Seric ** e -- the envelope for this message. 2654684Seric ** 2664684Seric ** Returns: 2674976Seric ** exit status corresponding to DATA command. 2684684Seric ** 2694684Seric ** Side Effects: 2704865Seric ** none. 2714684Seric */ 2724684Seric 27310175Seric smtpdata(m, e) 2744865Seric struct mailer *m; 2756980Seric register ENVELOPE *e; 2764684Seric { 2774684Seric register int r; 2784684Seric 2794797Seric /* 2804797Seric ** Send the data. 28110175Seric ** First send the command and check that it is ok. 28210175Seric ** Then send the data. 28310175Seric ** Follow it up with a dot to terminate. 28410175Seric ** Finally get the results of the transaction. 2854797Seric */ 2864797Seric 28710175Seric /* send the command and check ok to proceed */ 28810175Seric smtpmessage("DATA", m); 28924944Seric SmtpPhase = "DATA wait"; 29010175Seric r = reply(m); 2918005Seric if (r < 0 || REPLYTYPE(r) == 4) 2924797Seric return (EX_TEMPFAIL); 2937963Seric else if (r == 554) 2947963Seric return (EX_UNAVAILABLE); 2957963Seric else if (r != 354) 2967964Seric return (EX_PROTOCOL); 29710175Seric 29810175Seric /* now output the actual message */ 29910175Seric (*e->e_puthdr)(SmtpOut, m, CurEnv); 30010175Seric putline("\n", SmtpOut, m); 30110175Seric (*e->e_putbody)(SmtpOut, m, CurEnv); 30210175Seric 30310175Seric /* terminate the message */ 30410328Seric fprintf(SmtpOut, ".%s", m->m_eol); 30510215Seric if (Verbose && !HoldErrs) 30610215Seric nmessage(Arpa_Info, ">>> ."); 30710175Seric 30810175Seric /* check for the results of the transaction */ 30924944Seric SmtpPhase = "result wait"; 31010175Seric r = reply(m); 3118005Seric if (r < 0 || REPLYTYPE(r) == 4) 3124797Seric return (EX_TEMPFAIL); 3137963Seric else if (r == 250) 3147963Seric return (EX_OK); 3157963Seric else if (r == 552 || r == 554) 3167963Seric return (EX_UNAVAILABLE); 3177964Seric return (EX_PROTOCOL); 3184684Seric } 3194684Seric /* 3204865Seric ** SMTPQUIT -- close the SMTP connection. 3214865Seric ** 3224865Seric ** Parameters: 32315535Seric ** m -- a pointer to the mailer. 3244865Seric ** 3254865Seric ** Returns: 3264865Seric ** none. 3274865Seric ** 3284865Seric ** Side Effects: 3294865Seric ** sends the final protocol and closes the connection. 3304865Seric */ 3314865Seric 33215535Seric smtpquit(m) 33310175Seric register MAILER *m; 3344865Seric { 3359391Seric int i; 3364865Seric 33710148Seric /* if the connection is already closed, don't bother */ 33810148Seric if (SmtpIn == NULL) 33910148Seric return; 34010148Seric 34110148Seric /* send the quit message if not a forced quit */ 34211159Seric if (SmtpState == SMTP_OPEN || SmtpState == SMTP_SSD) 3439391Seric { 34410175Seric smtpmessage("QUIT", m); 34510175Seric (void) reply(m); 34611159Seric if (SmtpState == SMTP_CLOSED) 34710159Seric return; 3489391Seric } 3499391Seric 35010148Seric /* now actually close the connection */ 3517229Seric (void) fclose(SmtpIn); 3527229Seric (void) fclose(SmtpOut); 35310148Seric SmtpIn = SmtpOut = NULL; 35411159Seric SmtpState = SMTP_CLOSED; 35510148Seric 35610148Seric /* and pick up the zombie */ 35715535Seric i = endmailer(SmtpPid, m->m_argv[0]); 3589391Seric if (i != EX_OK) 35915535Seric syserr("smtpquit %s: stat %d", m->m_argv[0], i); 3604865Seric } 3614865Seric /* 3624684Seric ** REPLY -- read arpanet reply 3634684Seric ** 3644684Seric ** Parameters: 36510175Seric ** m -- the mailer we are reading the reply from. 3664684Seric ** 3674684Seric ** Returns: 3684684Seric ** reply code it reads. 3694684Seric ** 3704684Seric ** Side Effects: 3714684Seric ** flushes the mail file. 3724684Seric */ 3734684Seric 37410175Seric reply(m) 37510688Seric MAILER *m; 3764684Seric { 3774865Seric (void) fflush(SmtpOut); 3784684Seric 3797677Seric if (tTd(18, 1)) 3804796Seric printf("reply\n"); 3814796Seric 3827356Seric /* 3837356Seric ** Read the input line, being careful not to hang. 3847356Seric */ 3857356Seric 3864684Seric for (;;) 3874684Seric { 3884684Seric register int r; 3897356Seric register char *p; 3904684Seric 3917685Seric /* actually do the read */ 3929547Seric if (CurEnv->e_xfp != NULL) 3939547Seric (void) fflush(CurEnv->e_xfp); /* for debugging */ 3947356Seric 39510054Seric /* if we are in the process of closing just give the code */ 39611159Seric if (SmtpState == SMTP_CLOSED) 39710054Seric return (SMTPCLOSING); 39810054Seric 39910054Seric /* get the line from the other side */ 40010054Seric p = sfgets(SmtpReplyBuffer, sizeof SmtpReplyBuffer, SmtpIn); 40110054Seric if (p == NULL) 40210131Seric { 40310148Seric extern char MsgBuf[]; /* err.c */ 40410148Seric extern char Arpa_TSyserr[]; /* conf.c */ 40510148Seric 40621065Seric /* if the remote end closed early, fake an error */ 40721065Seric if (errno == 0) 40821065Seric # ifdef ECONNRESET 40921065Seric errno = ECONNRESET; 41021065Seric # else ECONNRESET 41121065Seric errno = EPIPE; 41221065Seric # endif ECONNRESET 41321065Seric 41410148Seric message(Arpa_TSyserr, "reply: read error"); 41510420Seric # ifdef DEBUG 41610420Seric /* if debugging, pause so we can see state */ 41710420Seric if (tTd(18, 100)) 41810420Seric pause(); 41910420Seric # endif DEBUG 42010148Seric # ifdef LOG 421*24945Seric syslog(LOG_ERR, "%s", &MsgBuf[4]); 42210148Seric # endif LOG 42311159Seric SmtpState = SMTP_CLOSED; 42415535Seric smtpquit(m); 42510054Seric return (-1); 42610131Seric } 42710054Seric fixcrlf(SmtpReplyBuffer, TRUE); 42810054Seric 42914900Seric if (CurEnv->e_xfp != NULL && index("45", SmtpReplyBuffer[0]) != NULL) 43014900Seric { 43114900Seric /* serious error -- log the previous command */ 43214900Seric if (SmtpMsgBuffer[0] != '\0') 43314900Seric fprintf(CurEnv->e_xfp, ">>> %s\n", SmtpMsgBuffer); 43414900Seric SmtpMsgBuffer[0] = '\0'; 43514900Seric 43614900Seric /* now log the message as from the other side */ 43714900Seric fprintf(CurEnv->e_xfp, "<<< %s\n", SmtpReplyBuffer); 43814900Seric } 43914900Seric 44014900Seric /* display the input for verbose mode */ 4417229Seric if (Verbose && !HoldErrs) 4429391Seric nmessage(Arpa_Info, "%s", SmtpReplyBuffer); 4437356Seric 4447356Seric /* if continuation is required, we can go on */ 4459391Seric if (SmtpReplyBuffer[3] == '-' || !isdigit(SmtpReplyBuffer[0])) 4464684Seric continue; 4477356Seric 4487356Seric /* decode the reply code */ 4499391Seric r = atoi(SmtpReplyBuffer); 4507356Seric 4517356Seric /* extra semantics: 0xx codes are "informational" */ 4524684Seric if (r < 100) 4534684Seric continue; 4547356Seric 4559391Seric /* reply code 421 is "Service Shutting Down" */ 45611159Seric if (r == SMTPCLOSING && SmtpState != SMTP_SSD) 4579391Seric { 45810054Seric /* send the quit protocol */ 45911159Seric SmtpState = SMTP_SSD; 46015535Seric smtpquit(m); 4619391Seric } 4629391Seric 46321065Seric /* save temporary failure messages for posterity */ 46421065Seric if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0') 46521065Seric (void) strcpy(SmtpError, &SmtpReplyBuffer[4]); 46621065Seric 4674684Seric return (r); 4684684Seric } 4694684Seric } 4704796Seric /* 4714865Seric ** SMTPMESSAGE -- send message to server 4724796Seric ** 4734796Seric ** Parameters: 4744796Seric ** f -- format 47510175Seric ** m -- the mailer to control formatting. 4764796Seric ** a, b, c -- parameters 4774796Seric ** 4784796Seric ** Returns: 4794796Seric ** none. 4804796Seric ** 4814796Seric ** Side Effects: 4824865Seric ** writes message to SmtpOut. 4834796Seric */ 4844796Seric 4854865Seric /*VARARGS1*/ 48610175Seric smtpmessage(f, m, a, b, c) 4874796Seric char *f; 48810175Seric MAILER *m; 4894796Seric { 49014900Seric (void) sprintf(SmtpMsgBuffer, f, a, b, c); 4917677Seric if (tTd(18, 1) || (Verbose && !HoldErrs)) 49214900Seric nmessage(Arpa_Info, ">>> %s", SmtpMsgBuffer); 49311159Seric if (SmtpOut != NULL) 49414900Seric fprintf(SmtpOut, "%s%s", SmtpMsgBuffer, m->m_eol); 4954796Seric } 4965182Seric 4975182Seric # endif SMTP 498