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