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