1/*++
2/* NAME
3/*	smtp_stream 3
4/* SUMMARY
5/*	smtp stream I/O support
6/* SYNOPSIS
7/*	#include <smtp_stream.h>
8/*
9/*	void	smtp_stream_setup(stream, timeout, enable_deadline)
10/*	VSTREAM *stream;
11/*	int	timeout;
12/*	int	enable_deadline;
13/*
14/*	void	smtp_printf(stream, format, ...)
15/*	VSTREAM *stream;
16/*	const char *format;
17/*
18/*	void	smtp_flush(stream)
19/*	VSTREAM *stream;
20/*
21/*	int	smtp_fgetc(stream)
22/*	VSTREAM *stream;
23/*
24/*	int	smtp_get(vp, stream, maxlen, flags)
25/*	VSTRING	*vp;
26/*	VSTREAM *stream;
27/*	ssize_t	maxlen;
28/*	int	flags;
29/*
30/*	void	smtp_fputs(str, len, stream)
31/*	const char *str;
32/*	ssize_t	len;
33/*	VSTREAM *stream;
34/*
35/*	void	smtp_fwrite(str, len, stream)
36/*	const char *str;
37/*	ssize_t	len;
38/*	VSTREAM *stream;
39/*
40/*	void	smtp_fputc(ch, stream)
41/*	int	ch;
42/*	VSTREAM *stream;
43/*
44/*	void	smtp_vprintf(stream, format, ap)
45/*	VSTREAM *stream;
46/*	char	*format;
47/*	va_list	ap;
48/* LEGACY API
49/*	void	smtp_timeout_setup(stream, timeout)
50/*	VSTREAM *stream;
51/*	int	timeout;
52/*	int	enable_deadline;
53/* DESCRIPTION
54/*	This module reads and writes text records delimited by CR LF,
55/*	with error detection: timeouts or unexpected end-of-file.
56/*	A trailing CR LF is added upon writing and removed upon reading.
57/*
58/*	smtp_stream_setup() prepares the specified stream for SMTP read
59/*	and write operations described below.
60/*	This routine alters the behavior of streams as follows:
61/* .IP \(bu
62/*	When enable_deadline is non-zero, the stream is configured
63/*	to enforce a total time limit for each smtp_stream read/write
64/*	operation. Otherwise, the stream is configured to enforce
65/*	a time limit for each individual read/write system call.
66/* .IP \f(bu
67/*	The stream is configured to use double buffering.
68/* .IP \f(bu
69/*	The stream is configured to enable exception handling.
70/* .PP
71/*	smtp_printf() formats its arguments and writes the result to
72/*	the named stream, followed by a CR LF pair. The stream is NOT flushed.
73/*	Long lines of text are not broken.
74/*
75/*	smtp_flush() flushes the named stream.
76/*
77/*	smtp_fgetc() reads one character from the named stream.
78/*
79/*	smtp_get() reads the named stream up to and including
80/*	the next LF character and strips the trailing CR LF. The
81/*	\fImaxlen\fR argument limits the length of a line of text,
82/*	and protects the program against running out of memory.
83/*	Specify a zero bound to turn off bounds checking.
84/*	The result is the last character read, or VSTREAM_EOF.
85/*	The \fIflags\fR argument is either SMTP_GET_FLAG_NONE (no
86/*	special processing) or SMTP_GET_FLAG_SKIP (skip over input
87/*	in excess of \fImaxlen\fR). Either way, a result value of
88/*	'\n' means that the input did not exceed \fImaxlen\fR.
89/*
90/*	smtp_fputs() writes its string argument to the named stream.
91/*	Long strings are not broken. Each string is followed by a
92/*	CR LF pair. The stream is not flushed.
93/*
94/*	smtp_fwrite() writes its string argument to the named stream.
95/*	Long strings are not broken. No CR LF is appended. The stream
96/*	is not flushed.
97/*
98/*	smtp_fputc() writes one character to the named stream.
99/*	The stream is not flushed.
100/*
101/*	smtp_vprintf() is the machine underneath smtp_printf().
102/*
103/*	smtp_timeout_setup() is a backwards-compatibility interface
104/*	for programs that don't require per-record deadline support.
105/* DIAGNOSTICS
106/* .fi
107/* .ad
108/*	In case of error, a vstream_longjmp() call is performed to the
109/*	context specified with vstream_setjmp().
110/*	After write error, further writes to the socket are disabled.
111/*	This eliminates the need for clumsy code to avoid unwanted
112/*	I/O while shutting down a TLS engine or closing a VSTREAM.
113/*	Error codes passed along with vstream_longjmp() are:
114/* .IP SMTP_ERR_EOF
115/*	An I/O error happened, or the peer has disconnected unexpectedly.
116/* .IP SMTP_ERR_TIME
117/*	The time limit specified to smtp_stream_setup() was exceeded.
118/* .PP
119/*	Additional error codes that may be used by applications:
120/* .IP SMTP_ERR_QUIET
121/*	Perform silent cleanup; the error was already reported by
122/*	the application.
123/*	This error is never generated by the smtp_stream(3) module, but
124/*	is defined for application-specific use.
125/* .IP SMTP_ERR_DATA
126/*	Application data error - the program cannot proceed with this
127/*	SMTP session.
128/* .IP SMTP_ERR_NONE
129/*	A non-error code that makes setjmp()/longjmp() convenient
130/*	to use.
131/* BUGS
132/*	The timeout deadline affects all I/O on the named stream, not
133/*	just the I/O done on behalf of this module.
134/*
135/*	The timeout deadline overwrites any previously set up state on
136/*	the named stream.
137/* LICENSE
138/* .ad
139/* .fi
140/*	The Secure Mailer license must be distributed with this software.
141/* AUTHOR(S)
142/*	Wietse Venema
143/*	IBM T.J. Watson Research
144/*	P.O. Box 704
145/*	Yorktown Heights, NY 10598, USA
146/*--*/
147
148/* System library. */
149
150#include <sys_defs.h>
151#include <sys/socket.h>
152#include <sys/time.h>
153#include <setjmp.h>
154#include <stdlib.h>
155#include <stdarg.h>
156#include <unistd.h>
157#include <string.h>			/* FD_ZERO() needs bzero() prototype */
158#include <errno.h>
159
160/* Utility library. */
161
162#include <vstring.h>
163#include <vstream.h>
164#include <vstring_vstream.h>
165#include <msg.h>
166#include <iostuff.h>
167
168/* Application-specific. */
169
170#include "smtp_stream.h"
171
172/* smtp_timeout_reset - reset per-stream error flags, restart deadline timer */
173
174static void smtp_timeout_reset(VSTREAM *stream)
175{
176    vstream_clearerr(stream);
177
178    /*
179     * Important: the time limit feature must not introduce any system calls
180     * when the input is already in the buffer, or when the output still fits
181     * in the buffer. Such system calls would really hurt when receiving or
182     * sending body content one line at a time.
183     */
184    if (vstream_fstat(stream, VSTREAM_FLAG_DEADLINE))
185	vstream_control(stream, VSTREAM_CTL_START_DEADLINE, VSTREAM_CTL_END);
186}
187
188/* smtp_longjmp - raise an exception */
189
190static NORETURN smtp_longjmp(VSTREAM *stream, int err, const char *context)
191{
192
193    /*
194     * If we failed to write, don't bang our head against the wall another
195     * time when closing the stream. In the case of SMTP over TLS, poisoning
196     * the socket with shutdown() is more robust than purging the VSTREAM
197     * buffer or replacing the write function pointer with dummy_write().
198     */
199    if (msg_verbose)
200	msg_info("%s: %s", context, err == SMTP_ERR_TIME ? "timeout" : "EOF");
201    if (vstream_wr_error(stream))
202	/* Don't report ECONNRESET (hangup), EINVAL (already shut down), etc. */
203	(void) shutdown(vstream_fileno(stream), SHUT_WR);
204    vstream_longjmp(stream, err);
205}
206
207/* smtp_stream_setup - configure timeout trap */
208
209void    smtp_stream_setup(VSTREAM *stream, int maxtime, int enable_deadline)
210{
211    const char *myname = "smtp_stream_setup";
212
213    if (msg_verbose)
214	msg_info("%s: maxtime=%d enable_deadline=%d",
215		 myname, maxtime, enable_deadline);
216
217    vstream_control(stream,
218		    VSTREAM_CTL_DOUBLE,
219		    VSTREAM_CTL_TIMEOUT, maxtime,
220		    enable_deadline ? VSTREAM_CTL_START_DEADLINE
221		    : VSTREAM_CTL_STOP_DEADLINE,
222		    VSTREAM_CTL_EXCEPT,
223		    VSTREAM_CTL_END);
224}
225
226/* smtp_flush - flush stream */
227
228void    smtp_flush(VSTREAM *stream)
229{
230    int     err;
231
232    /*
233     * Do the I/O, protected against timeout.
234     */
235    smtp_timeout_reset(stream);
236    err = vstream_fflush(stream);
237
238    /*
239     * See if there was a problem.
240     */
241    if (vstream_ftimeout(stream))
242	smtp_longjmp(stream, SMTP_ERR_TIME, "smtp_flush");
243    if (err != 0)
244	smtp_longjmp(stream, SMTP_ERR_EOF, "smtp_flush");
245}
246
247/* smtp_vprintf - write one line to SMTP peer */
248
249void    smtp_vprintf(VSTREAM *stream, const char *fmt, va_list ap)
250{
251    int     err;
252
253    /*
254     * Do the I/O, protected against timeout.
255     */
256    smtp_timeout_reset(stream);
257    vstream_vfprintf(stream, fmt, ap);
258    vstream_fputs("\r\n", stream);
259    err = vstream_ferror(stream);
260
261    /*
262     * See if there was a problem.
263     */
264    if (vstream_ftimeout(stream))
265	smtp_longjmp(stream, SMTP_ERR_TIME, "smtp_vprintf");
266    if (err != 0)
267	smtp_longjmp(stream, SMTP_ERR_EOF, "smtp_vprintf");
268}
269
270/* smtp_printf - write one line to SMTP peer */
271
272void    smtp_printf(VSTREAM *stream, const char *fmt,...)
273{
274    va_list ap;
275
276    va_start(ap, fmt);
277    smtp_vprintf(stream, fmt, ap);
278    va_end(ap);
279}
280
281/* smtp_fgetc - read one character from SMTP peer */
282
283int     smtp_fgetc(VSTREAM *stream)
284{
285    int     ch;
286
287    /*
288     * Do the I/O, protected against timeout.
289     */
290    smtp_timeout_reset(stream);
291    ch = VSTREAM_GETC(stream);
292
293    /*
294     * See if there was a problem.
295     */
296    if (vstream_ftimeout(stream))
297	smtp_longjmp(stream, SMTP_ERR_TIME, "smtp_fgetc");
298    if (vstream_feof(stream) || vstream_ferror(stream))
299	smtp_longjmp(stream, SMTP_ERR_EOF, "smtp_fgetc");
300    return (ch);
301}
302
303/* smtp_get - read one line from SMTP peer */
304
305int     smtp_get(VSTRING *vp, VSTREAM *stream, ssize_t bound, int flags)
306{
307    int     last_char;
308    int     next_char;
309
310    /*
311     * It's painful to do I/O with records that may span multiple buffers.
312     * Allow for partial long lines (we will read the remainder later) and
313     * allow for lines ending in bare LF. The idea is to be liberal in what
314     * we accept, strict in what we send.
315     *
316     * XXX 2821: Section 4.1.1.4 says that an SMTP server must not recognize
317     * bare LF as record terminator.
318     */
319    smtp_timeout_reset(stream);
320    last_char = (bound == 0 ? vstring_get(vp, stream) :
321		 vstring_get_bound(vp, stream, bound));
322
323    switch (last_char) {
324
325	/*
326	 * Do some repair in the rare case that we stopped reading in the
327	 * middle of the CRLF record terminator.
328	 */
329    case '\r':
330	if ((next_char = VSTREAM_GETC(stream)) == '\n') {
331	    VSTRING_ADDCH(vp, '\n');
332	    last_char = '\n';
333	    /* FALLTRHOUGH */
334	} else {
335	    if (next_char != VSTREAM_EOF)
336		vstream_ungetc(stream, next_char);
337	    break;
338	}
339
340	/*
341	 * Strip off the record terminator: either CRLF or just bare LF.
342	 *
343	 * XXX RFC 2821 disallows sending bare CR everywhere. We remove bare CR
344	 * if received before CRLF, and leave it alone otherwise.
345	 */
346    case '\n':
347	vstring_truncate(vp, VSTRING_LEN(vp) - 1);
348	while (VSTRING_LEN(vp) > 0 && vstring_end(vp)[-1] == '\r')
349	    vstring_truncate(vp, VSTRING_LEN(vp) - 1);
350	VSTRING_TERMINATE(vp);
351	/* FALLTRHOUGH */
352
353	/*
354	 * Partial line: just read the remainder later. If we ran into EOF,
355	 * the next test will deal with it.
356	 */
357    default:
358	break;
359    }
360
361    /*
362     * Optionally, skip over excess input, protected by the same time limit.
363     */
364    if (last_char != '\n' && (flags & SMTP_GET_FLAG_SKIP)
365	&& vstream_feof(stream) == 0 && vstream_ferror(stream) == 0)
366	while ((next_char = VSTREAM_GETC(stream)) != VSTREAM_EOF
367	       && next_char != '\n')
368	     /* void */ ;
369
370    /*
371     * EOF is bad, whether or not it happens in the middle of a record. Don't
372     * allow data that was truncated because of EOF.
373     */
374    if (vstream_ftimeout(stream))
375	smtp_longjmp(stream, SMTP_ERR_TIME, "smtp_get");
376    if (vstream_feof(stream) || vstream_ferror(stream))
377	smtp_longjmp(stream, SMTP_ERR_EOF, "smtp_get");
378    return (last_char);
379}
380
381/* smtp_fputs - write one line to SMTP peer */
382
383void    smtp_fputs(const char *cp, ssize_t todo, VSTREAM *stream)
384{
385    ssize_t err;
386
387    if (todo < 0)
388	msg_panic("smtp_fputs: negative todo %ld", (long) todo);
389
390    /*
391     * Do the I/O, protected against timeout.
392     */
393    smtp_timeout_reset(stream);
394    err = (vstream_fwrite(stream, cp, todo) != todo
395	   || vstream_fputs("\r\n", stream) == VSTREAM_EOF);
396
397    /*
398     * See if there was a problem.
399     */
400    if (vstream_ftimeout(stream))
401	smtp_longjmp(stream, SMTP_ERR_TIME, "smtp_fputs");
402    if (err != 0)
403	smtp_longjmp(stream, SMTP_ERR_EOF, "smtp_fputs");
404}
405
406/* smtp_fwrite - write one string to SMTP peer */
407
408void    smtp_fwrite(const char *cp, ssize_t todo, VSTREAM *stream)
409{
410    ssize_t err;
411
412    if (todo < 0)
413	msg_panic("smtp_fwrite: negative todo %ld", (long) todo);
414
415    /*
416     * Do the I/O, protected against timeout.
417     */
418    smtp_timeout_reset(stream);
419    err = (vstream_fwrite(stream, cp, todo) != todo);
420
421    /*
422     * See if there was a problem.
423     */
424    if (vstream_ftimeout(stream))
425	smtp_longjmp(stream, SMTP_ERR_TIME, "smtp_fwrite");
426    if (err != 0)
427	smtp_longjmp(stream, SMTP_ERR_EOF, "smtp_fwrite");
428}
429
430/* smtp_fputc - write to SMTP peer */
431
432void    smtp_fputc(int ch, VSTREAM *stream)
433{
434    int     stat;
435
436    /*
437     * Do the I/O, protected against timeout.
438     */
439    smtp_timeout_reset(stream);
440    stat = VSTREAM_PUTC(ch, stream);
441
442    /*
443     * See if there was a problem.
444     */
445    if (vstream_ftimeout(stream))
446	smtp_longjmp(stream, SMTP_ERR_TIME, "smtp_fputc");
447    if (stat == VSTREAM_EOF)
448	smtp_longjmp(stream, SMTP_ERR_EOF, "smtp_fputc");
449}
450