1 /*
2  * Copyright (c) 1983 Eric P. Allman
3  * Copyright (c) 1988 Regents of the University of California.
4  * All rights reserved.
5  *
6  * %sccs.include.redist.c%
7  */
8 
9 # include "sendmail.h"
10 
11 #ifndef lint
12 #ifdef SMTP
13 static char sccsid[] = "@(#)usersmtp.c	5.20.1.1 (Berkeley) 02/26/92 (with SMTP)";
14 #else
15 static char sccsid[] = "@(#)usersmtp.c	5.20.1.1 (Berkeley) 02/26/92 (without SMTP)";
16 #endif
17 #endif /* not lint */
18 
19 # include <sysexits.h>
20 # include <errno.h>
21 
22 # ifdef SMTP
23 
24 /*
25 **  USERSMTP -- run SMTP protocol from the user end.
26 **
27 **	This protocol is described in RFC821.
28 */
29 
30 #define REPLYTYPE(r)	((r) / 100)		/* first digit of reply code */
31 #define REPLYCLASS(r)	(((r) / 10) % 10)	/* second digit of reply code */
32 #define SMTPCLOSING	421			/* "Service Shutting Down" */
33 
34 char	SmtpMsgBuffer[MAXLINE];		/* buffer for commands */
35 char	SmtpReplyBuffer[MAXLINE];	/* buffer for replies */
36 char	SmtpError[MAXLINE] = "";	/* save failure error messages */
37 int	SmtpPid;			/* pid of mailer */
38 
39 /* following represents the state of the SMTP connection */
40 int	SmtpState;			/* connection state, see below */
41 
42 #define SMTP_CLOSED	0		/* connection is closed */
43 #define SMTP_OPEN	1		/* connection is open for business */
44 #define SMTP_SSD	2		/* service shutting down */
45 /*
46 **  SMTPINIT -- initialize SMTP.
47 **
48 **	Opens the connection and sends the initial protocol.
49 **
50 **	Parameters:
51 **		m -- mailer to create connection to.
52 **		pvp -- pointer to parameter vector to pass to
53 **			the mailer.
54 **
55 **	Returns:
56 **		appropriate exit status -- EX_OK on success.
57 **		If not EX_OK, it should close the connection.
58 **
59 **	Side Effects:
60 **		creates connection and sends initial protocol.
61 */
62 
63 jmp_buf	CtxGreeting;
64 
65 smtpinit(m, pvp)
66 	struct mailer *m;
67 	char **pvp;
68 {
69 	register int r;
70 	EVENT *gte;
71 	register STAB *st;
72 	char buf[MAXNAME];
73 	static int greettimeout();
74 	extern STAB *stab();
75 
76 	/*
77 	**  Open the connection to the mailer.
78 	*/
79 
80 	if (SmtpState == SMTP_OPEN)
81 		syserr("smtpinit: already open");
82 
83 	SmtpState = SMTP_CLOSED;
84 	SmtpError[0] = '\0';
85 	SmtpPhase = "user open";
86 	setproctitle("%s %s: %s", CurEnv->e_id, pvp[1], SmtpPhase);
87 	mci = openmailer(m, pvp, (ADDRESS *) NULL, TRUE);
88 	if (SmtpPid < 0)
89 	{
90 		if (tTd(18, 1))
91 			printf("smtpinit: cannot open %s: stat %d errno %d\n",
92 			   pvp[0], ExitStat, errno);
93 		if (CurEnv->e_xfp != NULL)
94 		{
95 			register char *p;
96 			extern char *errstring();
97 			extern char *statstring();
98 
99 			if (errno == 0)
100 			{
101 				p = statstring(ExitStat);
102 				fprintf(CurEnv->e_xfp,
103 					"%.3s %s.%s... %s\n",
104 					p, pvp[1], m->m_name, p);
105 			}
106 			else
107 			{
108 				r = errno;
109 				fprintf(CurEnv->e_xfp,
110 					"421 %s.%s... Deferred: %s\n",
111 					pvp[1], m->m_name, errstring(errno));
112 				errno = r;
113 			}
114 		}
115 		return (ExitStat);
116 	}
117 	SmtpState = SMTP_OPEN;
118 
119 	/*
120 	**  Get the greeting message.
121 	**	This should appear spontaneously.  Give it five minutes to
122 	**	happen.
123 	*/
124 
125 	if (setjmp(CtxGreeting) != 0)
126 		goto tempfail1;
127 	gte = setevent((time_t) 300, greettimeout, 0);
128 	SmtpPhase = "greeting wait";
129 	setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
130 	r = reply(m);
131 	clrevent(gte);
132 	if (r < 0 || REPLYTYPE(r) != 2)
133 		goto tempfail1;
134 
135 	/*
136 	**  Send the HELO command.
137 	**	My mother taught me to always introduce myself.
138 	*/
139 
140 	smtpmessage("HELO %s", m, MyHostName);
141 	SmtpPhase = "HELO wait";
142 	setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
143 	r = reply(m);
144 	if (r < 0)
145 		goto tempfail1;
146 	else if (REPLYTYPE(r) == 5)
147 		goto unavailable;
148 	else if (REPLYTYPE(r) != 2)
149 		goto tempfail1;
150 
151 	/*
152 	**  If this is expected to be another sendmail, send some internal
153 	**  commands.
154 	*/
155 
156 	if (bitnset(M_INTERNAL, m->m_flags))
157 	{
158 		/* tell it to be verbose */
159 		smtpmessage("VERB", m);
160 		r = reply(m);
161 		if (r < 0)
162 			goto tempfail2;
163 
164 		/* tell it we will be sending one transaction only */
165 		smtpmessage("ONEX", m);
166 		r = reply(m);
167 		if (r < 0)
168 			goto tempfail2;
169 	}
170 
171 	/*
172 	**  Send the MAIL command.
173 	**	Designates the sender.
174 	*/
175 
176 	expand("\001<", buf, &buf[sizeof buf - 1], CurEnv);
177 	if (CurEnv->e_from.q_mailer == LocalMailer ||
178 	    !bitnset(M_FROMPATH, m->m_flags))
179 	{
180 		smtpmessage("MAIL From:<%s>", m, buf);
181 	}
182 	else
183 	{
184 		smtpmessage("MAIL From:<@%s%c%s>", m, MyHostName,
185 			buf[0] == '@' ? ',' : ':', buf);
186 	}
187 	SmtpPhase = "MAIL wait";
188 	setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
189 	r = reply(m);
190 	if (r < 0 || REPLYTYPE(r) == 4)
191 		goto tempfail2;
192 	else if (r == 250)
193 		return (EX_OK);
194 	else if (r == 552)
195 		goto unavailable;
196 
197 	/* protocol error -- close up */
198 	smtpquit(m);
199 	return (EX_PROTOCOL);
200 
201   tempfail1:
202 	/* log this as an error to avoid sure-to-be-void connections */
203 	st = stab(CurHostName, ST_MCONINFO + m->m_mno, ST_ENTER);
204 	st->s_host.ho_exitstat = EX_TEMPFAIL;
205 	st->s_host.ho_errno = errno;
206 
207   tempfail2:
208 	/* signal a temporary failure */
209 	smtpquit(m);
210 	return (EX_TEMPFAIL);
211 
212   unavailable:
213 	/* signal service unavailable */
214 	smtpquit(m);
215 	return (EX_UNAVAILABLE);
216 }
217 
218 
219 static
220 greettimeout()
221 {
222 	/* timeout reading the greeting message */
223 	longjmp(CtxGreeting, 1);
224 }
225 /*
226 **  SMTPRCPT -- designate recipient.
227 **
228 **	Parameters:
229 **		to -- address of recipient.
230 **		m -- the mailer we are sending to.
231 **
232 **	Returns:
233 **		exit status corresponding to recipient status.
234 **
235 **	Side Effects:
236 **		Sends the mail via SMTP.
237 */
238 
239 smtprcpt(to, m)
240 	ADDRESS *to;
241 	register MAILER *m;
242 {
243 	register int r;
244 	extern char *remotename();
245 
246 	smtpmessage("RCPT To:<%s>", m, to->q_user);
247 
248 	SmtpPhase = "RCPT wait";
249 	setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
250 	r = reply(m);
251 	if (r < 0 || REPLYTYPE(r) == 4)
252 		return (EX_TEMPFAIL);
253 	else if (REPLYTYPE(r) == 2)
254 		return (EX_OK);
255 	else if (r == 550 || r == 551 || r == 553)
256 		return (EX_NOUSER);
257 	else if (r == 552 || r == 554)
258 		return (EX_UNAVAILABLE);
259 	return (EX_PROTOCOL);
260 }
261 /*
262 **  SMTPDATA -- send the data and clean up the transaction.
263 **
264 **	Parameters:
265 **		m -- mailer being sent to.
266 **		e -- the envelope for this message.
267 **
268 **	Returns:
269 **		exit status corresponding to DATA command.
270 **
271 **	Side Effects:
272 **		none.
273 */
274 
275 smtpdata(m, e)
276 	struct mailer *m;
277 	register ENVELOPE *e;
278 {
279 	register int r;
280 
281 	/*
282 	**  Send the data.
283 	**	First send the command and check that it is ok.
284 	**	Then send the data.
285 	**	Follow it up with a dot to terminate.
286 	**	Finally get the results of the transaction.
287 	*/
288 
289 	/* send the command and check ok to proceed */
290 	smtpmessage("DATA", m);
291 	SmtpPhase = "DATA wait";
292 	setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
293 	r = reply(m);
294 	if (r < 0 || REPLYTYPE(r) == 4)
295 		return (EX_TEMPFAIL);
296 	else if (r == 554)
297 		return (EX_UNAVAILABLE);
298 	else if (r != 354)
299 		return (EX_PROTOCOL);
300 
301 	/* now output the actual message */
302 	(*e->e_puthdr)(SmtpOut, m, CurEnv);
303 	putline("\n", SmtpOut, m);
304 	(*e->e_putbody)(SmtpOut, m, CurEnv);
305 
306 	/* terminate the message */
307 	fprintf(SmtpOut, ".%s", m->m_eol);
308 	if (Verbose && !HoldErrs)
309 		nmessage(Arpa_Info, ">>> .");
310 
311 	/* check for the results of the transaction */
312 	SmtpPhase = "result wait";
313 	setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
314 	r = reply(m);
315 	if (r < 0 || REPLYTYPE(r) == 4)
316 		return (EX_TEMPFAIL);
317 	else if (r == 250)
318 		return (EX_OK);
319 	else if (r == 552 || r == 554)
320 		return (EX_UNAVAILABLE);
321 	return (EX_PROTOCOL);
322 }
323 /*
324 **  SMTPQUIT -- close the SMTP connection.
325 **
326 **	Parameters:
327 **		m -- a pointer to the mailer.
328 **
329 **	Returns:
330 **		none.
331 **
332 **	Side Effects:
333 **		sends the final protocol and closes the connection.
334 */
335 
336 smtpquit(mci)
337 	register MCONINFO *mci;
338 {
339 	int i;
340 
341 	/* if the connection is already closed, don't bother */
342 	if (mci->mci_state == MCI_CLOSED)
343 		return;
344 
345 	/* send the quit message if not a forced quit */
346 	if (SmtpState == SMTP_OPEN || SmtpState == SMTP_SSD)
347 	{
348 		smtpmessage("QUIT", m);
349 		(void) reply(m);
350 		if (SmtpState == SMTP_CLOSED)
351 			return;
352 	}
353 
354 	/* now actually close the connection and pick up the zombie */
355 	i = endmailer(mci, m->m_argv[0]);
356 	if (i != EX_OK)
357 		syserr("smtpquit %s: stat %d", m->m_argv[0], i);
358 }
359 /*
360 **  REPLY -- read arpanet reply
361 **
362 **	Parameters:
363 **		m -- the mailer we are reading the reply from.
364 **
365 **	Returns:
366 **		reply code it reads.
367 **
368 **	Side Effects:
369 **		flushes the mail file.
370 */
371 
372 reply(m)
373 	MAILER *m;
374 {
375 	(void) fflush(SmtpOut);
376 
377 	if (tTd(18, 1))
378 		printf("reply\n");
379 
380 	/*
381 	**  Read the input line, being careful not to hang.
382 	*/
383 
384 	for (;;)
385 	{
386 		register int r;
387 		register char *p;
388 
389 		/* actually do the read */
390 		if (CurEnv->e_xfp != NULL)
391 			(void) fflush(CurEnv->e_xfp);	/* for debugging */
392 
393 		/* if we are in the process of closing just give the code */
394 		if (SmtpState == SMTP_CLOSED)
395 			return (SMTPCLOSING);
396 
397 		/* get the line from the other side */
398 		p = sfgets(SmtpReplyBuffer, sizeof SmtpReplyBuffer, SmtpIn);
399 		if (p == NULL)
400 		{
401 			extern char MsgBuf[];		/* err.c */
402 			extern char Arpa_TSyserr[];	/* conf.c */
403 
404 			/* if the remote end closed early, fake an error */
405 			if (errno == 0)
406 # ifdef ECONNRESET
407 				errno = ECONNRESET;
408 # else ECONNRESET
409 				errno = EPIPE;
410 # endif ECONNRESET
411 
412 			message(Arpa_TSyserr, "reply: read error");
413 			/* if debugging, pause so we can see state */
414 			if (tTd(18, 100))
415 				pause();
416 # ifdef LOG
417 			syslog(LOG_INFO, "%s", &MsgBuf[4]);
418 # endif LOG
419 			SmtpState = SMTP_CLOSED;
420 			smtpquit(m);
421 			return (-1);
422 		}
423 		fixcrlf(SmtpReplyBuffer, TRUE);
424 
425 		if (CurEnv->e_xfp != NULL && index("45", SmtpReplyBuffer[0]) != NULL)
426 		{
427 			/* serious error -- log the previous command */
428 			if (SmtpMsgBuffer[0] != '\0')
429 				fprintf(CurEnv->e_xfp, ">>> %s\n", SmtpMsgBuffer);
430 			SmtpMsgBuffer[0] = '\0';
431 
432 			/* now log the message as from the other side */
433 			fprintf(CurEnv->e_xfp, "<<< %s\n", SmtpReplyBuffer);
434 		}
435 
436 		/* display the input for verbose mode */
437 		if (Verbose && !HoldErrs)
438 			nmessage(Arpa_Info, "%s", SmtpReplyBuffer);
439 
440 		/* if continuation is required, we can go on */
441 		if (SmtpReplyBuffer[3] == '-' || !isdigit(SmtpReplyBuffer[0]))
442 			continue;
443 
444 		/* decode the reply code */
445 		r = atoi(SmtpReplyBuffer);
446 
447 		/* extra semantics: 0xx codes are "informational" */
448 		if (r < 100)
449 			continue;
450 
451 		/* reply code 421 is "Service Shutting Down" */
452 		if (r == SMTPCLOSING && SmtpState != SMTP_SSD)
453 		{
454 			/* send the quit protocol */
455 			SmtpState = SMTP_SSD;
456 			smtpquit(m);
457 		}
458 
459 		/* save temporary failure messages for posterity */
460 		if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0')
461 			(void) strcpy(SmtpError, &SmtpReplyBuffer[4]);
462 
463 		return (r);
464 	}
465 }
466 /*
467 **  SMTPMESSAGE -- send message to server
468 **
469 **	Parameters:
470 **		f -- format
471 **		m -- the mailer to control formatting.
472 **		a, b, c -- parameters
473 **
474 **	Returns:
475 **		none.
476 **
477 **	Side Effects:
478 **		writes message to SmtpOut.
479 */
480 
481 /*VARARGS1*/
482 smtpmessage(f, m, a, b, c)
483 	char *f;
484 	MAILER *m;
485 {
486 	(void) sprintf(SmtpMsgBuffer, f, a, b, c);
487 	if (tTd(18, 1) || (Verbose && !HoldErrs))
488 		nmessage(Arpa_Info, ">>> %s", SmtpMsgBuffer);
489 	if (SmtpOut != NULL)
490 		fprintf(SmtpOut, "%s%s", SmtpMsgBuffer,
491 			m == 0 ? "\r\n" : m->m_eol);
492 }
493 
494 # endif SMTP
495