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