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