Postfix3.3.1
smtp_stream.c
[詳解]
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 
174 static 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  */
186 }
187 
188 /* smtp_longjmp - raise an exception */
189 
190 static 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 
209 void 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,
219  CA_VSTREAM_CTL_TIMEOUT(maxtime),
220  enable_deadline ? CA_VSTREAM_CTL_START_DEADLINE
224 }
225 
226 /* smtp_flush - flush stream */
227 
228 void 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 
249 void 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 
272 void 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 
283 int 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 
305 int 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 
383 void 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 
408 void 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 
432 void 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 }
int msg_verbose
Definition: msg.c:177
#define VSTREAM_EOF
Definition: vstream.h:110
int smtp_fgetc(VSTREAM *stream)
Definition: smtp_stream.c:283
#define CA_VSTREAM_CTL_TIMEOUT(v)
Definition: vstream.h:163
#define NORETURN
Definition: sys_defs.h:1583
NORETURN msg_panic(const char *fmt,...)
Definition: msg.c:295
#define stat(p, s)
Definition: warn_stat.h:18
VSTREAM * vstream_vfprintf(VSTREAM *vp, const char *format, va_list ap)
Definition: vstream.c:1531
#define SMTP_ERR_TIME
Definition: smtp_stream.h:31
#define VSTREAM_GETC(vp)
Definition: vstream.h:108
#define vstream_longjmp(stream, val)
Definition: vstream.h:249
#define vstream_clearerr(vp)
Definition: vstream.h:125
int vstring_get(VSTRING *vp, VSTREAM *fp)
#define CA_VSTREAM_CTL_DOUBLE
Definition: vstream.h:159
VSTRING * vstring_truncate(VSTRING *vp, ssize_t len)
Definition: vstring.c:415
#define vstream_wr_error(vp)
Definition: vstream.h:119
int smtp_get(VSTRING *vp, VSTREAM *stream, ssize_t bound, int flags)
Definition: smtp_stream.c:305
#define VSTRING_LEN(vp)
Definition: vstring.h:72
#define VSTRING_TERMINATE(vp)
Definition: vstring.h:74
int const char * fmt
#define vstring_end(vp)
Definition: vstring.h:73
void smtp_vprintf(VSTREAM *stream, const char *fmt, va_list ap)
Definition: smtp_stream.c:249
#define VSTRING_ADDCH(vp, ch)
Definition: vstring.h:81
#define SMTP_ERR_EOF
Definition: smtp_stream.h:30
void smtp_fputc(int ch, VSTREAM *stream)
Definition: smtp_stream.c:432
#define CA_VSTREAM_CTL_START_DEADLINE
Definition: vstream.h:171
#define vstream_ungetc(vp, ch)
Definition: vstream.h:109
#define vstream_ftimeout(vp)
Definition: vstream.h:124
void smtp_stream_setup(VSTREAM *stream, int maxtime, int enable_deadline)
Definition: smtp_stream.c:209
void smtp_fputs(const char *cp, ssize_t todo, VSTREAM *stream)
Definition: smtp_stream.c:383
void smtp_printf(VSTREAM *stream, const char *fmt,...)
Definition: smtp_stream.c:272
#define VSTREAM_FLAG_DEADLINE
Definition: vstream.h:86
void smtp_fwrite(const char *cp, ssize_t todo, VSTREAM *stream)
Definition: smtp_stream.c:408
int vstream_fflush(VSTREAM *stream)
Definition: vstream.c:1257
void smtp_flush(VSTREAM *stream)
Definition: smtp_stream.c:228
#define vstream_fwrite(v, b, n)
Definition: vstream.h:105
#define vstream_fstat(vp, fl)
Definition: vstream.h:130
#define vstream_feof(vp)
Definition: vstream.h:121
#define CA_VSTREAM_CTL_EXCEPT
Definition: vstream.h:164
#define vstream_fileno(vp)
Definition: vstream.h:115
#define CA_VSTREAM_CTL_END
Definition: vstream.h:155
#define VSTREAM_PUTC(ch, vp)
Definition: vstream.h:107
#define SMTP_GET_FLAG_SKIP
Definition: smtp_stream.h:51
#define CA_VSTREAM_CTL_STOP_DEADLINE
Definition: vstream.h:172
void vstream_control(VSTREAM *stream, int name,...)
Definition: vstream.c:1372
#define vstream_ferror(vp)
Definition: vstream.h:120
int vstring_get_bound(VSTRING *vp, VSTREAM *fp, ssize_t bound)
int vstream_fputs(const char *str, VSTREAM *stream)
Definition: vstream.c:1360
void msg_info(const char *fmt,...)
Definition: msg.c:199