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