📄 usersmtp.c
字号:
/* * Copyright (c) 1988 Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that this notice is preserved and that due credit is given * to the University of California at Berkeley. The name of the University * may not be used to endorse or promote products derived from this * software without specific prior written permission. This software * is provided ``as is'' without express or implied warranty. * * Sendmail * Copyright (c) 1983 Eric P. Allman * Berkeley, California */# include <ctype.h># include <sysexits.h># include <errno.h># include "sendmail.h"SCCSID(@(#)usersmtp.c 1.1 92/07/30 SMI (no SMTP)); /* from UCB 5.9 3/13/88 */# ifdef SMTP/*** USERSMTP -- run SMTP protocol from the user end.**** This protocol is described in RFC821.*/#define REPLYTYPE(r) ((r) / 100) /* first digit of reply code */#define REPLYCLASS(r) (((r) / 10) % 10) /* second digit of reply code */#define SMTPCLOSING 421 /* "Service Shutting Down" */char SmtpMsgBuffer[MAXLINE]; /* buffer for commands */char SmtpReplyBuffer[MAXLINE]; /* buffer for replies */char SmtpError[MAXLINE] = ""; /* save failure error messages */bool SmtpLogged; /* set when logged "connected to" */FILE *SmtpOut; /* output file */FILE *SmtpIn; /* input file */int SmtpPid; /* pid of mailer */int SmtpFirstErrno; /* Meaningful error number *//* following represents the state of the SMTP connection */int SmtpState; /* connection state, see below */#define SMTP_CLOSED 0 /* connection is closed */#define SMTP_OPEN 1 /* connection is open for business */#define SMTP_SSD 2 /* service shutting down */#define SMTP_TIMEDOUT 3 /* never got greeting *//*** SMTPINIT -- initialize SMTP.**** Opens the connection and sends the initial protocol.**** Parameters:** m -- mailer to create connection to.** pvp -- pointer to parameter vector to pass to** the mailer.**** Returns:** appropriate exit status -- EX_OK on success.** If not EX_OK, it should close the connection.**** Side Effects:** creates connection and sends initial protocol.*/smtpinit(m, pvp) struct mailer *m; char **pvp;{ register int r; EVENT *gte; char buf[MAXNAME]; time_t tempTimeout; /* ** Open the connection to the mailer. */#ifdef DEBUG if (SmtpState == SMTP_OPEN) syserr("smtpinit: already open");#endif DEBUG SmtpIn = SmtpOut = NULL; SmtpFirstErrno = 0; SmtpState = SMTP_CLOSED; SmtpError[0] = '\0'; SmtpPhase = "user open"; SmtpLogged = FALSE; SmtpPid = openmailer(m, pvp, (ADDRESS *) NULL, TRUE, &SmtpOut, &SmtpIn); if (SmtpPid < 0) {# ifdef DEBUG if (tTd(18, 1)) printf("smtpinit: cannot open %s: stat %d errno %d\n", pvp[0], ExitStat, errno);# endif DEBUG if (CurEnv->e_xfp != NULL) { register char *p; extern char *errstring(); extern char *statstring(); extern char *pintvl(); long timeleft; if (ExitStat==EX_NOHOST) { fprintf(CurEnv->e_xfp, "421 Host %s not found for mailer %s.\n", pvp[1], m->m_name); return (ExitStat); } if (errno == 0) { p = statstring(ExitStat); fprintf(CurEnv->e_xfp, "%.3s %s via %s... %s", p, pvp[1], m->m_name, p); } else { fprintf(CurEnv->e_xfp, "421 %s: %s", pvp[1], errstring(errno)); } timeleft = CurEnv->e_ctime + TimeOut - curtime(); timeleft = (timeleft+1800)/3600; if (timeleft > 0) fprintf(CurEnv->e_xfp, ", will keep trying for %s", pintvl(timeleft*3600,FALSE) ); fprintf(CurEnv->e_xfp,"\n"); } return (ExitStat); } SmtpState = SMTP_OPEN; /* ** Get the greeting message. ** This should appear spontaneously. Give it less time to ** happen than the normal read timeout since it is more ** common, and we have less to lose since we have not started ** the SMTP conversation yet. */ SmtpPhase = "greeting wait"; tempTimeout = ReadTimeout; ReadTimeout = (time_t) 120; r = reply(m); ReadTimeout = tempTimeout; if (r < 0 || REPLYTYPE(r) != 2) { SmtpState = SMTP_TIMEDOUT; goto tempfail; } /* ** Send the HELO command. ** My mother taught me to always introduce myself. */ smtpmessage("HELO %s", m, MyHostName); SmtpPhase = "HELO wait"; r = reply(m); if (r < 0) goto tempfail; else if (REPLYTYPE(r) == 5) goto unavailable; else if (REPLYTYPE(r) != 2) goto tempfail; /* ** If this is expected to be another sendmail, send some internal ** commands. */ if (bitnset(M_INTERNAL, m->m_flags)) { /* tell it to be verbose */ smtpmessage("VERB", m); r = reply(m); if (r < 0) goto tempfail; /* tell it we will be sending one transaction only */ smtpmessage("ONEX", m); r = reply(m); if (r < 0) goto tempfail; } /* ** Send the MAIL command. ** Designates the sender. */ expand("\001g", buf, &buf[sizeof buf - 1], CurEnv); if (CurEnv->e_from.q_mailer == LocalMailer || !bitnset(M_FROMPATH, m->m_flags)) { smtpmessage("MAIL From:<%s>", m, buf); } else { smtpmessage("MAIL From:<@%s%c%s>", m, MyHostName, buf[0] == '@' ? ',' : ':', buf); } SmtpPhase = "MAIL wait"; r = reply(m); if (r < 0 || REPLYTYPE(r) == 4) goto tempfail; else if (r == 250) return (EX_OK); else if (r == 552) goto unavailable; /* protocol error -- close up */ smtpquit(m); return (EX_PROTOCOL); /* signal a temporary failure */ tempfail: smtpquit(m); if (SmtpFirstErrno) errno = SmtpFirstErrno; return (EX_TEMPFAIL); /* signal service unavailable */ unavailable: smtpquit(m); return (EX_UNAVAILABLE);}/*** SMTPRCPT -- designate recipient.**** Parameters:** to -- address of recipient.** m -- the mailer we are sending to.**** Returns:** exit status corresponding to recipient status.**** Side Effects:** Sends the mail via SMTP.*/smtprcpt(to, m) ADDRESS *to; register MAILER *m;{ register int r; extern char *remotename(); smtpmessage("RCPT To:<%s>", m, remotename(to->q_user, m, FALSE, TRUE)); SmtpPhase = "RCPT wait"; r = reply(m); if (r < 0 || REPLYTYPE(r) == 4) { if (SmtpFirstErrno) errno = SmtpFirstErrno; return (EX_TEMPFAIL); } else if (REPLYTYPE(r) == 2) return (EX_OK); else if (r == 550 || r == 551 || r == 553) return (EX_NOUSER); else if (r == 552 || r == 554) return (EX_UNAVAILABLE); return (EX_PROTOCOL);}/*** SMTPDATA -- send the data and clean up the transaction.**** Parameters:** m -- mailer being sent to.** e -- the envelope for this message.**** Returns:** exit status corresponding to DATA command.**** Side Effects:** none.*/smtpdata(m, e) struct mailer *m; register ENVELOPE *e;{ register int r; /* ** Send the data. ** First send the command and check that it is ok. ** Then send the data. ** Follow it up with a dot to terminate. ** Finally get the results of the transaction. */ /* send the command and check ok to proceed */ smtpmessage("DATA", m); SmtpPhase = "DATA wait"; r = reply(m); if (r < 0 || REPLYTYPE(r) == 4) { if (SmtpFirstErrno) errno = SmtpFirstErrno; return (EX_TEMPFAIL); } else if (r == 554) return (EX_UNAVAILABLE); else if (r != 354) return (EX_PROTOCOL); /* now output the actual message */ (*e->e_puthdr)(SmtpOut, m, CurEnv); putline("\n", SmtpOut, m); (*e->e_putbody)(SmtpOut, m, CurEnv); /* terminate the message */ fprintf(SmtpOut, ".%s", m->m_eol); if (Verbose && !HoldErrs) nmessage(Arpa_Info, ">>> ."); /* check for the results of the transaction */ SmtpPhase = "result wait"; r = reply(m); if (r < 0 || REPLYTYPE(r) == 4) { if (SmtpFirstErrno) errno = SmtpFirstErrno; return (EX_TEMPFAIL); } else if (r == 250) return (EX_OK); else if (r == 552 || r == 554) return (EX_UNAVAILABLE); return (EX_PROTOCOL);}/*** SMTPQUIT -- close the SMTP connection.**** Parameters:** m -- a pointer to the mailer.**** Returns:** none.**** Side Effects:** sends the final protocol and closes the connection.*/smtpquit(m) register MAILER *m;{ int i; /* if the connection is already closed, don't bother */ if (SmtpIn == NULL) return; /* send the quit message if not a forced quit */ if (SmtpState == SMTP_OPEN || SmtpState == SMTP_SSD) { SmtpPhase = "final wait"; smtpmessage("QUIT", m); (void) reply(m); if (SmtpState == SMTP_CLOSED) return; } /* now actually close the connection */ closeconnection(fileno(SmtpIn)); /* Mark closed in stab */ (void) fclose(SmtpIn); (void) fclose(SmtpOut); SmtpIn = SmtpOut = NULL; SmtpState = SMTP_CLOSED; /* and pick up the zombie */ i = endmailer(SmtpPid, m->m_argv[0]); if (i != EX_OK) syserr("smtpquit %s: stat %d", m->m_argv[0], i);}/*** REPLY -- read arpanet reply**** Parameters:** m -- the mailer we are reading the reply from.**** Returns:** reply code it reads.**** Side Effects:** flushes the mail file.*/reply(m) MAILER *m;{ if (SmtpOut == NULL || SmtpIn == NULL) return(SMTPCLOSING); (void) fflush(SmtpOut); if (tTd(18, 1)) printf("reply\n"); /* ** Read the input line, being careful not to hang. */ for (;;) { register int r; register char *p; /* actually do the read */ if (CurEnv->e_xfp != NULL) (void) fflush(CurEnv->e_xfp); /* for debugging */ /* if we are in the process of closing just give the code */ if (SmtpState == SMTP_CLOSED) return (SMTPCLOSING); /* get the line from the other side */ p = sfgets(SmtpReplyBuffer, sizeof SmtpReplyBuffer, SmtpIn); if (p == NULL) { /* * Make sure we produce a temporary error * if the remote end just closed early. */ if (errno==0) errno = ECONNRESET; if (SmtpFirstErrno==0) SmtpFirstErrno = errno; if (errno != ECONNRESET && errno != ETIMEDOUT) syserr("network read error");# ifdef DEBUG /* if debugging, pause so we can see state */ if (tTd(18, 100)) pause();# endif DEBUG SmtpState = SMTP_CLOSED; smtpquit(m); return (-1); } fixcrlf(SmtpReplyBuffer, TRUE); if (CurEnv->e_xfp != NULL && index("45", SmtpReplyBuffer[0]) != NULL) { /* * serious error -- log the previous command * and connection message if needed. */ if (SmtpLogged == FALSE && RealHostName) { SmtpLogged = TRUE; fprintf(CurEnv->e_xfp, "Connected to %s:\n", RealHostName); } if (SmtpMsgBuffer[0] != '\0') fprintf(CurEnv->e_xfp, ">>> %s\n", SmtpMsgBuffer); SmtpMsgBuffer[0] = '\0'; /* now log the message as from the other side */ fprintf(CurEnv->e_xfp, "<<< %s\n", SmtpReplyBuffer); } /* display the input for verbose mode */ if (Verbose && !HoldErrs) nmessage(Arpa_Info, "%s", SmtpReplyBuffer); /* if continuation is required, we can go on */ if (SmtpReplyBuffer[3] == '-' || !isdigit(SmtpReplyBuffer[0])) continue; /* decode the reply code */ r = atoi(SmtpReplyBuffer); /* extra semantics: 0xx codes are "informational" */ if (r < 100) continue; /* save temporary failure messages for posterity */ if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0') (void) strcpy(SmtpError, SmtpReplyBuffer+4); /* reply code 421 is "Service Shutting Down" */ if (r == SMTPCLOSING && SmtpState != SMTP_SSD) { /* send the quit protocol */ SmtpState = SMTP_SSD; smtpquit(m); } return (r); }}/*** SMTPMESSAGE -- send message to server**** Parameters:** f -- format** m -- the mailer to control formatting.** a, b, c -- parameters**** Returns:** none.**** Side Effects:** writes message to SmtpOut.*//*VARARGS1*/smtpmessage(f, m, a, b, c) char *f; MAILER *m;{ (void) sprintf(SmtpMsgBuffer, f, a, b, c); setproctitle("%s To %s: %s", CurEnv->e_id, RealHostName, SmtpMsgBuffer); if (tTd(18, 1) || (Verbose && !HoldErrs)) nmessage(Arpa_Info, ">>> %s", SmtpMsgBuffer); if (SmtpOut != NULL) fprintf(SmtpOut, "%s%s", SmtpMsgBuffer, m->m_eol);}# endif SMTP
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -