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