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