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