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*52104Seric static char sccsid[] = "@(#)usersmtp.c 5.18 (Berkeley) 01/03/92 (with SMTP)"; 1433731Sbostic #else 15*52104Seric static char sccsid[] = "@(#)usersmtp.c 5.18 (Berkeley) 01/03/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; 734865Seric char buf[MAXNAME]; 7446928Sbostic 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) 127*52104Seric goto tempfail1; 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) 134*52104Seric goto tempfail1; 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) 146*52104Seric goto tempfail1; 1478005Seric else if (REPLYTYPE(r) == 5) 14814913Seric goto unavailable; 1497963Seric else if (REPLYTYPE(r) != 2) 150*52104Seric goto tempfail1; 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) 163*52104Seric goto tempfail2; 1649315Seric 1659315Seric /* tell it we will be sending one transaction only */ 16610175Seric smtpmessage("ONEX", m); 16710175Seric r = reply(m); 1689315Seric if (r < 0) 169*52104Seric goto tempfail2; 1709315Seric } 1719315Seric 1729315Seric /* 1734865Seric ** Send the MAIL command. 1744865Seric ** Designates the sender. 1754865Seric */ 1764796Seric 17751951Seric expand("\001<", 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) 192*52104Seric goto tempfail2; 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 */ 203*52104Seric tempfail1: 204*52104Seric #ifdef HOSTINFO 205*52104Seric { 206*52104Seric register STAB *st; 207*52104Seric extern STAB *stab(); 208*52104Seric 209*52104Seric /* log this as an error to avoid sure-to-be-void connections */ 210*52104Seric st = stab(CurHostName, ST_HOST, ST_ENTER); 211*52104Seric st->s_host.ho_exitstat = EX_TEMPFAIL; 212*52104Seric st->s_host.ho_errno = errno; 213*52104Seric } 214*52104Seric #endif /* HOSTINFO */ 215*52104Seric 216*52104Seric tempfail2: 21714913Seric smtpquit(m); 21814913Seric return (EX_TEMPFAIL); 21914913Seric 22014913Seric /* signal service unavailable */ 22114913Seric unavailable: 22214913Seric smtpquit(m); 22314913Seric return (EX_UNAVAILABLE); 2244684Seric } 22514886Seric 22614886Seric 22714886Seric static 22814886Seric greettimeout() 22914886Seric { 23014886Seric /* timeout reading the greeting message */ 23114886Seric longjmp(CtxGreeting, 1); 23214886Seric } 2334684Seric /* 2344976Seric ** SMTPRCPT -- designate recipient. 2354797Seric ** 2364797Seric ** Parameters: 2374865Seric ** to -- address of recipient. 23810175Seric ** m -- the mailer we are sending to. 2394797Seric ** 2404797Seric ** Returns: 2414865Seric ** exit status corresponding to recipient status. 2424797Seric ** 2434797Seric ** Side Effects: 2444865Seric ** Sends the mail via SMTP. 2454797Seric */ 2464797Seric 24710175Seric smtprcpt(to, m) 2484865Seric ADDRESS *to; 24910175Seric register MAILER *m; 2504797Seric { 2514797Seric register int r; 25210308Seric extern char *remotename(); 2534797Seric 25410308Seric smtpmessage("RCPT To:<%s>", m, remotename(to->q_user, m, FALSE, TRUE)); 2554865Seric 25624944Seric SmtpPhase = "RCPT wait"; 25736584Sbostic setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase); 25810175Seric r = reply(m); 2598005Seric if (r < 0 || REPLYTYPE(r) == 4) 2604865Seric return (EX_TEMPFAIL); 2617963Seric else if (REPLYTYPE(r) == 2) 2627963Seric return (EX_OK); 2637964Seric else if (r == 550 || r == 551 || r == 553) 2647964Seric return (EX_NOUSER); 2657964Seric else if (r == 552 || r == 554) 2667964Seric return (EX_UNAVAILABLE); 2677964Seric return (EX_PROTOCOL); 2684797Seric } 2694797Seric /* 27010175Seric ** SMTPDATA -- send the data and clean up the transaction. 2714684Seric ** 2724684Seric ** Parameters: 2734865Seric ** m -- mailer being sent to. 2746980Seric ** e -- the envelope for this message. 2754684Seric ** 2764684Seric ** Returns: 2774976Seric ** exit status corresponding to DATA command. 2784684Seric ** 2794684Seric ** Side Effects: 2804865Seric ** none. 2814684Seric */ 2824684Seric 28310175Seric smtpdata(m, e) 2844865Seric struct mailer *m; 2856980Seric register ENVELOPE *e; 2864684Seric { 2874684Seric register int r; 2884684Seric 2894797Seric /* 2904797Seric ** Send the data. 29110175Seric ** First send the command and check that it is ok. 29210175Seric ** Then send the data. 29310175Seric ** Follow it up with a dot to terminate. 29410175Seric ** Finally get the results of the transaction. 2954797Seric */ 2964797Seric 29710175Seric /* send the command and check ok to proceed */ 29810175Seric smtpmessage("DATA", m); 29924944Seric SmtpPhase = "DATA wait"; 30036584Sbostic setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase); 30110175Seric r = reply(m); 3028005Seric if (r < 0 || REPLYTYPE(r) == 4) 3034797Seric return (EX_TEMPFAIL); 3047963Seric else if (r == 554) 3057963Seric return (EX_UNAVAILABLE); 3067963Seric else if (r != 354) 3077964Seric return (EX_PROTOCOL); 30810175Seric 30910175Seric /* now output the actual message */ 31010175Seric (*e->e_puthdr)(SmtpOut, m, CurEnv); 31110175Seric putline("\n", SmtpOut, m); 31210175Seric (*e->e_putbody)(SmtpOut, m, CurEnv); 31310175Seric 31410175Seric /* terminate the message */ 31510328Seric fprintf(SmtpOut, ".%s", m->m_eol); 31610215Seric if (Verbose && !HoldErrs) 31710215Seric nmessage(Arpa_Info, ">>> ."); 31810175Seric 31910175Seric /* check for the results of the transaction */ 32024944Seric SmtpPhase = "result wait"; 32136584Sbostic setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase); 32210175Seric r = reply(m); 3238005Seric if (r < 0 || REPLYTYPE(r) == 4) 3244797Seric return (EX_TEMPFAIL); 3257963Seric else if (r == 250) 3267963Seric return (EX_OK); 3277963Seric else if (r == 552 || r == 554) 3287963Seric return (EX_UNAVAILABLE); 3297964Seric return (EX_PROTOCOL); 3304684Seric } 3314684Seric /* 3324865Seric ** SMTPQUIT -- close the SMTP connection. 3334865Seric ** 3344865Seric ** Parameters: 33515535Seric ** m -- a pointer to the mailer. 3364865Seric ** 3374865Seric ** Returns: 3384865Seric ** none. 3394865Seric ** 3404865Seric ** Side Effects: 3414865Seric ** sends the final protocol and closes the connection. 3424865Seric */ 3434865Seric 34415535Seric smtpquit(m) 34510175Seric register MAILER *m; 3464865Seric { 3479391Seric int i; 3484865Seric 34910148Seric /* if the connection is already closed, don't bother */ 35010148Seric if (SmtpIn == NULL) 35110148Seric return; 35210148Seric 35310148Seric /* send the quit message if not a forced quit */ 35411159Seric if (SmtpState == SMTP_OPEN || SmtpState == SMTP_SSD) 3559391Seric { 35610175Seric smtpmessage("QUIT", m); 35710175Seric (void) reply(m); 35811159Seric if (SmtpState == SMTP_CLOSED) 35910159Seric return; 3609391Seric } 3619391Seric 36210148Seric /* now actually close the connection */ 3637229Seric (void) fclose(SmtpIn); 3647229Seric (void) fclose(SmtpOut); 36510148Seric SmtpIn = SmtpOut = NULL; 36611159Seric SmtpState = SMTP_CLOSED; 36710148Seric 36810148Seric /* and pick up the zombie */ 36915535Seric i = endmailer(SmtpPid, m->m_argv[0]); 3709391Seric if (i != EX_OK) 37115535Seric syserr("smtpquit %s: stat %d", m->m_argv[0], i); 3724865Seric } 3734865Seric /* 3744684Seric ** REPLY -- read arpanet reply 3754684Seric ** 3764684Seric ** Parameters: 37710175Seric ** m -- the mailer we are reading the reply from. 3784684Seric ** 3794684Seric ** Returns: 3804684Seric ** reply code it reads. 3814684Seric ** 3824684Seric ** Side Effects: 3834684Seric ** flushes the mail file. 3844684Seric */ 3854684Seric 38610175Seric reply(m) 38710688Seric MAILER *m; 3884684Seric { 3894865Seric (void) fflush(SmtpOut); 3904684Seric 3917677Seric if (tTd(18, 1)) 3924796Seric printf("reply\n"); 3934796Seric 3947356Seric /* 3957356Seric ** Read the input line, being careful not to hang. 3967356Seric */ 3977356Seric 3984684Seric for (;;) 3994684Seric { 4004684Seric register int r; 4017356Seric register char *p; 4024684Seric 4037685Seric /* actually do the read */ 4049547Seric if (CurEnv->e_xfp != NULL) 4059547Seric (void) fflush(CurEnv->e_xfp); /* for debugging */ 4067356Seric 40710054Seric /* if we are in the process of closing just give the code */ 40811159Seric if (SmtpState == SMTP_CLOSED) 40910054Seric return (SMTPCLOSING); 41010054Seric 41110054Seric /* get the line from the other side */ 41210054Seric p = sfgets(SmtpReplyBuffer, sizeof SmtpReplyBuffer, SmtpIn); 41310054Seric if (p == NULL) 41410131Seric { 41510148Seric extern char MsgBuf[]; /* err.c */ 41610148Seric extern char Arpa_TSyserr[]; /* conf.c */ 41710148Seric 41821065Seric /* if the remote end closed early, fake an error */ 41921065Seric if (errno == 0) 42021065Seric # ifdef ECONNRESET 42121065Seric errno = ECONNRESET; 42221065Seric # else ECONNRESET 42321065Seric errno = EPIPE; 42421065Seric # endif ECONNRESET 42521065Seric 42610148Seric message(Arpa_TSyserr, "reply: read error"); 42710420Seric /* if debugging, pause so we can see state */ 42810420Seric if (tTd(18, 100)) 42910420Seric pause(); 43010148Seric # ifdef LOG 43136234Skarels syslog(LOG_INFO, "%s", &MsgBuf[4]); 43210148Seric # endif LOG 43311159Seric SmtpState = SMTP_CLOSED; 43415535Seric smtpquit(m); 43510054Seric return (-1); 43610131Seric } 43710054Seric fixcrlf(SmtpReplyBuffer, TRUE); 43810054Seric 43914900Seric if (CurEnv->e_xfp != NULL && index("45", SmtpReplyBuffer[0]) != NULL) 44014900Seric { 44114900Seric /* serious error -- log the previous command */ 44214900Seric if (SmtpMsgBuffer[0] != '\0') 44314900Seric fprintf(CurEnv->e_xfp, ">>> %s\n", SmtpMsgBuffer); 44414900Seric SmtpMsgBuffer[0] = '\0'; 44514900Seric 44614900Seric /* now log the message as from the other side */ 44714900Seric fprintf(CurEnv->e_xfp, "<<< %s\n", SmtpReplyBuffer); 44814900Seric } 44914900Seric 45014900Seric /* display the input for verbose mode */ 4517229Seric if (Verbose && !HoldErrs) 4529391Seric nmessage(Arpa_Info, "%s", SmtpReplyBuffer); 4537356Seric 4547356Seric /* if continuation is required, we can go on */ 4559391Seric if (SmtpReplyBuffer[3] == '-' || !isdigit(SmtpReplyBuffer[0])) 4564684Seric continue; 4577356Seric 4587356Seric /* decode the reply code */ 4599391Seric r = atoi(SmtpReplyBuffer); 4607356Seric 4617356Seric /* extra semantics: 0xx codes are "informational" */ 4624684Seric if (r < 100) 4634684Seric continue; 4647356Seric 4659391Seric /* reply code 421 is "Service Shutting Down" */ 46611159Seric if (r == SMTPCLOSING && SmtpState != SMTP_SSD) 4679391Seric { 46810054Seric /* send the quit protocol */ 46911159Seric SmtpState = SMTP_SSD; 47015535Seric smtpquit(m); 4719391Seric } 4729391Seric 47321065Seric /* save temporary failure messages for posterity */ 47421065Seric if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0') 47521065Seric (void) strcpy(SmtpError, &SmtpReplyBuffer[4]); 47621065Seric 4774684Seric return (r); 4784684Seric } 4794684Seric } 4804796Seric /* 4814865Seric ** SMTPMESSAGE -- send message to server 4824796Seric ** 4834796Seric ** Parameters: 4844796Seric ** f -- format 48510175Seric ** m -- the mailer to control formatting. 4864796Seric ** a, b, c -- parameters 4874796Seric ** 4884796Seric ** Returns: 4894796Seric ** none. 4904796Seric ** 4914796Seric ** Side Effects: 4924865Seric ** writes message to SmtpOut. 4934796Seric */ 4944796Seric 4954865Seric /*VARARGS1*/ 49610175Seric smtpmessage(f, m, a, b, c) 4974796Seric char *f; 49810175Seric MAILER *m; 4994796Seric { 50014900Seric (void) sprintf(SmtpMsgBuffer, f, a, b, c); 5017677Seric if (tTd(18, 1) || (Verbose && !HoldErrs)) 50214900Seric nmessage(Arpa_Info, ">>> %s", SmtpMsgBuffer); 50311159Seric if (SmtpOut != NULL) 50440997Sbostic fprintf(SmtpOut, "%s%s", SmtpMsgBuffer, 50540997Sbostic m == 0 ? "\r\n" : m->m_eol); 5064796Seric } 5075182Seric 5085182Seric # endif SMTP 509