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