Postfix3.3.1
smtp-sink.c
[詳解]
1 /*++
2 /* NAME
3 /* smtp-sink 1
4 /* SUMMARY
5 /* parallelized SMTP/LMTP test server
6 /* SYNOPSIS
7 /* .fi
8 /* \fBsmtp-sink\fR [\fIoptions\fR] [\fBinet:\fR][\fIhost\fR]:\fIport\fR
9 /* \fIbacklog\fR
10 /*
11 /* \fBsmtp-sink\fR [\fIoptions\fR] \fBunix:\fR\fIpathname\fR \fIbacklog\fR
12 /* DESCRIPTION
13 /* \fBsmtp-sink\fR listens on the named host (or address) and port.
14 /* It takes SMTP messages from the network and throws them away.
15 /* The purpose is to measure client performance, not protocol
16 /* compliance.
17 /*
18 /* \fBsmtp-sink\fR may also be configured to capture each mail
19 /* delivery transaction to file. Since disk latencies are large
20 /* compared to network delays, this mode of operation can
21 /* reduce the maximal performance by several orders of magnitude.
22 /*
23 /* Connections can be accepted on IPv4 or IPv6 endpoints, or on
24 /* UNIX-domain sockets.
25 /* IPv4 and IPv6 are the default.
26 /* This program is the complement of the \fBsmtp-source\fR(1) program.
27 /*
28 /* Note: this is an unsupported test program. No attempt is made
29 /* to maintain compatibility between successive versions.
30 /*
31 /* Arguments:
32 /* .IP \fB-4\fR
33 /* Support IPv4 only. This option has no effect when
34 /* Postfix is built without IPv6 support.
35 /* .IP \fB-6\fR
36 /* Support IPv6 only. This option is not available when
37 /* Postfix is built without IPv6 support.
38 /* .IP \fB-8\fR
39 /* Do not announce 8BITMIME support.
40 /* .IP \fB-a\fR
41 /* Do not announce SASL authentication support.
42 /* .IP "\fB-A \fIdelay\fR"
43 /* Wait \fIdelay\fR seconds after responding to DATA, then
44 /* abort prematurely with a 550 reply status. Do not read
45 /* further input from the client; this is an attempt to block
46 /* the client before it sends ".". Specify a zero delay value
47 /* to abort immediately.
48 /* .IP "\fB-b \fIsoft-bounce-reply\fR"
49 /* Use \fIsoft-bounce-reply\fR for soft reject responses. The
50 /* default reply is "450 4.3.0 Error: command failed".
51 /* .IP "\fB-B \fIhard-bounce-reply\fR"
52 /* Use \fIhard-bounce-reply\fR for hard reject responses. The
53 /* default reply is "500 5.3.0 Error: command failed".
54 /* .IP \fB-c\fR
55 /* Display running counters that are updated whenever an SMTP
56 /* session ends, a QUIT command is executed, or when "." is
57 /* received.
58 /* .IP \fB-C\fR
59 /* Disable XCLIENT support.
60 /* .IP "\fB-d \fIdump-template\fR"
61 /* Dump each mail transaction to a single-message file whose
62 /* name is created by expanding the \fIdump-template\fR via
63 /* strftime(3) and appending a pseudo-random hexadecimal number
64 /* (example: "%Y%m%d%H/%M." expands into "2006081203/05.809a62e3").
65 /* If the template contains "/" characters, missing directories
66 /* are created automatically. The message dump format is
67 /* described below.
68 /* .sp
69 /* Note: this option keeps one capture file open for every
70 /* mail transaction in progress.
71 /* .IP "\fB-D \fIdump-template\fR"
72 /* Append mail transactions to a multi-message dump file whose
73 /* name is created by expanding the \fIdump-template\fR via
74 /* strftime(3).
75 /* If the template contains "/" characters, missing directories
76 /* are created automatically. The message dump format is
77 /* described below.
78 /* .sp
79 /* Note: this option keeps one capture file open for every
80 /* mail transaction in progress.
81 /* .IP \fB-e\fR
82 /* Do not announce ESMTP support.
83 /* .IP \fB-E\fR
84 /* Do not announce ENHANCEDSTATUSCODES support.
85 /* .IP "\fB-f \fIcommand,command,...\fR"
86 /* Reject the specified commands with a hard (5xx) error code.
87 /* This option implies \fB-p\fR.
88 /* .sp
89 /* Examples of commands are CONNECT, HELO, EHLO, LHLO, MAIL, RCPT, VRFY,
90 /* DATA, ., RSET, NOOP, and QUIT. Separate command names by
91 /* white space or commas, and use quotes to protect white space
92 /* from the shell. Command names are case-insensitive.
93 /* .IP \fB-F\fR
94 /* Disable XFORWARD support.
95 /* .IP "\fB-h\fI hostname\fR"
96 /* Use \fIhostname\fR in the SMTP greeting, in the HELO response,
97 /* and in the EHLO response. The default hostname is "smtp-sink".
98 /* .IP "\fB-H\fI delay\fR"
99 /* Delay the first read operation after receiving DATA (time
100 /* in seconds). Combine with a large test message and a small
101 /* TCP window size (see the \fB-T\fR option) to test the Postfix
102 /* client write_wait() implementation.
103 /* .IP \fB-L\fR
104 /* Enable LMTP instead of SMTP.
105 /* .IP "\fB-m \fIcount\fR (default: 256)"
106 /* An upper bound on the maximal number of simultaneous
107 /* connections that \fBsmtp-sink\fR will handle. This prevents
108 /* the process from running out of file descriptors. Excess
109 /* connections will stay queued in the TCP/IP stack.
110 /* .IP "\fB-M \fIcount\fR"
111 /* Terminate after receiving \fIcount\fR messages.
112 /* .IP "\fB-n \fIcount\fR"
113 /* Terminate after \fIcount\fR sessions.
114 /* .IP \fB-N\fR
115 /* Do not announce support for DSN.
116 /* .IP \fB-p\fR
117 /* Do not announce support for ESMTP command pipelining.
118 /* .IP \fB-P\fR
119 /* Change the server greeting so that it appears to come through
120 /* a CISCO PIX system. Implies \fB-e\fR.
121 /* .IP "\fB-q \fIcommand,command,...\fR"
122 /* Disconnect (without replying) after receiving one of the
123 /* specified commands.
124 /* .sp
125 /* Examples of commands are CONNECT, HELO, EHLO, LHLO, MAIL, RCPT, VRFY,
126 /* DATA, ., RSET, NOOP, and QUIT. Separate command names by
127 /* white space or commas, and use quotes to protect white space
128 /* from the shell. Command names are case-insensitive.
129 /* .IP "\fB-Q \fIcommand,command,...\fR"
130 /* Send a 421 reply and disconnect after receiving one
131 /* of the specified commands.
132 /* .sp
133 /* Examples of commands are CONNECT, HELO, EHLO, LHLO, MAIL, RCPT, VRFY,
134 /* DATA, ., RSET, NOOP, and QUIT. Separate command names by
135 /* white space or commas, and use quotes to protect white space
136 /* from the shell. Command names are case-insensitive.
137 /* .IP "\fB-r \fIcommand,command,...\fR"
138 /* Reject the specified commands with a soft (4xx) error code.
139 /* This option implies \fB-p\fR.
140 /* .sp
141 /* Examples of commands are CONNECT, HELO, EHLO, LHLO, MAIL, RCPT, VRFY,
142 /* DATA, ., RSET, NOOP, and QUIT. Separate command names by
143 /* white space or commas, and use quotes to protect white space
144 /* from the shell. Command names are case-insensitive.
145 /* .IP "\fB-R \fIroot-directory\fR"
146 /* Change the process root directory to the specified location.
147 /* This option requires super-user privileges. See also the
148 /* \fB-u\fR option.
149 /* .IP "\fB-s \fIcommand,command,...\fR"
150 /* Log the named commands to syslogd.
151 /* .sp
152 /* Examples of commands are CONNECT, HELO, EHLO, LHLO, MAIL, RCPT, VRFY,
153 /* DATA, ., RSET, NOOP, and QUIT. Separate command names by
154 /* white space or commas, and use quotes to protect white space
155 /* from the shell. Command names are case-insensitive.
156 /* .IP "\fB-S start-string\fR"
157 /* An optional string that is prepended to each message that is
158 /* written to a dump file (see the dump file format description
159 /* below). The following C escape sequences are supported: \ea
160 /* (bell), \eb (backspace), \ef (formfeed), \en (newline), \er
161 /* (carriage return), \et (horizontal tab), \ev (vertical tab),
162 /* \e\fIddd\fR (up to three octal digits) and \e\e (the backslash
163 /* character).
164 /* .IP "\fB-t \fItimeout\fR (default: 100)"
165 /* Limit the time for receiving a command or sending a response.
166 /* The time limit is specified in seconds.
167 /* .IP "\fB-T \fIwindowsize\fR"
168 /* Override the default TCP window size. To work around
169 /* broken TCP window scaling implementations, specify a
170 /* value > 0 and < 65536.
171 /* .IP "\fB-u \fIusername\fR"
172 /* Switch to the specified user privileges after opening the
173 /* network socket and optionally changing the process root
174 /* directory. This option is required when the process runs
175 /* with super-user privileges. See also the \fB-R\fR option.
176 /* .IP \fB-v\fR
177 /* Show the SMTP conversations.
178 /* .IP "\fB-w \fIdelay\fR"
179 /* Wait \fIdelay\fR seconds before responding to a DATA command.
180 /* .IP "\fB-W \fIcommand:delay[:odds]\fR"
181 /* Wait \fIdelay\fR seconds before responding to \fIcommand\fR.
182 /* If \fIodds\fR is also specified (a number between 1-99
183 /* inclusive), wait for a random multiple of \fIdelay\fR. The
184 /* random multiplier is equal to the number of times the program
185 /* needs to roll a dice with a range of 0..99 inclusive, before
186 /* the dice produces a result greater than or equal to \fIodds\fR.
187 /* .IP [\fBinet:\fR][\fIhost\fR]:\fIport\fR
188 /* Listen on network interface \fIhost\fR (default: any interface)
189 /* TCP port \fIport\fR. Both \fIhost\fR and \fIport\fR may be
190 /* specified in numeric or symbolic form.
191 /* .IP \fBunix:\fR\fIpathname\fR
192 /* Listen on the UNIX-domain socket at \fIpathname\fR.
193 /* .IP \fIbacklog\fR
194 /* The maximum length the queue of pending connections,
195 /* as defined by the \fBlisten\fR(2) system call.
196 /* DUMP FILE FORMAT
197 /* .ad
198 /* .fi
199 /* Each dumped message contains a sequence of text lines,
200 /* terminated with the newline character. The sequence of
201 /* information is as follows:
202 /* .IP \(bu
203 /* The optional string specified with the \fB-S\fR option.
204 /* .IP \(bu
205 /* The \fBsmtp-sink\fR generated headers as documented below.
206 /* .IP \(bu
207 /* The message header and body as received from the SMTP client.
208 /* .IP \(bu
209 /* An empty line.
210 /* .PP
211 /* The format of the \fBsmtp-sink\fR generated headers is as
212 /* follows:
213 /* .IP "\fBX-Client-Addr: \fItext\fR"
214 /* The client IP address without enclosing []. An IPv6 address
215 /* is prefixed with "ipv6:". This record is always present.
216 /* .IP "\fBX-Client-Proto: \fItext\fR"
217 /* The client protocol: SMTP, ESMTP or LMTP. This record is
218 /* always present.
219 /* .IP "\fBX-Helo-Args: \fItext\fR"
220 /* The arguments of the last HELO or EHLO command before this
221 /* mail delivery transaction. This record is present only if
222 /* the client sent a recognizable HELO or EHLO command before
223 /* the DATA command.
224 /* .IP "\fBX-Mail-Args: \fItext\fR"
225 /* The arguments of the MAIL command that started this mail
226 /* delivery transaction. This record is present exactly once.
227 /* .IP "\fBX-Rcpt-Args: \fItext\fR"
228 /* The arguments of an RCPT command within this mail delivery
229 /* transaction. There is one record for each RCPT command, and
230 /* they are in the order as sent by the client.
231 /* .IP "\fBReceived: \fItext\fR"
232 /* A message header for compatibility with mail processing
233 /* software. This three-line header marks the end of the headers
234 /* provided by \fBsmtp-sink\fR, and is formatted as follows:
235 /* .RS
236 /* .IP "\fBfrom \fIhelo\fR ([\fIaddr\fR])"
237 /* The HELO or EHLO command argument and client IP address.
238 /* If the client did not send HELO or EHLO, the client IP
239 /* address is used instead.
240 /* .IP "\fBby \fIhost\fB (smtp-sink) with \fIproto\fB id \fIrandom\fB;\fR"
241 /* The hostname specified with the \fB-h\fR option, the client
242 /* protocol (see \fBX-Client-Proto\fR above), and the pseudo-random
243 /* portion of the per-message capture file name.
244 /* .IP \fItime-stamp\fR
245 /* A time stamp as defined in RFC 2822.
246 /* .RE
247 /* SEE ALSO
248 /* smtp-source(1), SMTP/LMTP message generator
249 /* LICENSE
250 /* .ad
251 /* .fi
252 /* The Secure Mailer license must be distributed with this software.
253 /* AUTHOR(S)
254 /* Wietse Venema
255 /* IBM T.J. Watson Research
256 /* P.O. Box 704
257 /* Yorktown Heights, NY 10598, USA
258 /*
259 /* Wietse Venema
260 /* Google, Inc.
261 /* 111 8th Avenue
262 /* New York, NY 10011, USA
263 /*--*/
264 
265 /* System library. */
266 
267 #include <sys_defs.h>
268 #include <sys/socket.h>
269 #include <sys/wait.h>
270 #include <sys/stat.h>
271 #include <unistd.h>
272 #include <string.h>
273 #include <stdlib.h>
274 #include <fcntl.h>
275 #include <syslog.h>
276 #include <signal.h>
277 #include <time.h>
278 #include <ctype.h>
279 
280 #ifdef STRCASECMP_IN_STRINGS_H
281 #include <strings.h>
282 #endif
283 
284 /* Utility library. */
285 
286 #include <msg.h>
287 #include <vstring.h>
288 #include <vstream.h>
289 #include <vstring_vstream.h>
290 #include <get_hostname.h>
291 #include <listen.h>
292 #include <events.h>
293 #include <mymalloc.h>
294 #include <iostuff.h>
295 #include <msg_vstream.h>
296 #include <stringops.h>
297 #include <sane_accept.h>
298 #include <inet_proto.h>
299 #include <myaddrinfo.h>
300 #include <make_dirs.h>
301 #include <myrand.h>
302 #include <chroot_uid.h>
303 
304 /* Global library. */
305 
306 #include <smtp_stream.h>
307 #include <mail_date.h>
308 #include <mail_version.h>
309 
310 /* Application-specific. */
311 
312 typedef struct SINK_STATE {
313  VSTREAM *stream;
316  int (*read_fn) (struct SINK_STATE *);
317  int in_mail;
318  int rcpts;
320  /* Capture file information for fake Received: header */
321  MAI_HOSTADDR_STR client_addr; /* IP address */
322  char *addr_prefix; /* ipv6: or empty */
323  char *helo_args; /* text after HELO or EHLO */
324  const char *client_proto; /* SMTP, ESMTP, LMTP */
325  time_t start_time; /* MAIL command time */
326  int id; /* pseudo-random */
327  VSTREAM *dump_file; /* dump file or null */
328  void (*delayed_response) (struct SINK_STATE *state, const char *);
330 } SINK_STATE;
331 
332 #define ST_ANY 0
333 #define ST_CR 1
334 #define ST_CR_LF 2
335 #define ST_CR_LF_DOT 3
336 #define ST_CR_LF_DOT_CR 4
337 #define ST_CR_LF_DOT_CR_LF 5
338 
339 #define PUSH_BACK_PEEK(state) (*(state)->push_back_ptr != 0)
340 #define PUSH_BACK_GET(state) (*(state)->push_back_ptr++)
341 #define PUSH_BACK_SET(state, text) ((state)->push_back_ptr = (text))
342 
343 #ifndef DEF_MAX_CLIENT_COUNT
344 #define DEF_MAX_CLIENT_COUNT 256
345 #endif
346 
347 #define SOFT_ERROR_RESP "450 4.3.0 Error: command failed"
348 #define HARD_ERROR_RESP "500 5.3.0 Error: command failed"
349 
350  /*
351  * We can't rely on vstream auto-flushing, so we have to prepare for the
352  * next read request.
353  */
354 #define SMTP_FLUSH(fp) do { \
355  if (vstream_peek(fp) <= 0 && readable(vstream_fileno(fp)) <= 0) \
356  smtp_flush(fp); \
357  } while (0)
358 
359 static int var_tmout = 100;
360 static int var_max_line_length = 2048;
361 static char *var_myhostname;
362 static char *soft_error_resp = SOFT_ERROR_RESP;
363 static char *hard_error_resp = HARD_ERROR_RESP;
364 static int command_read(SINK_STATE *);
365 static int data_read(SINK_STATE *);
366 static void disconnect(SINK_STATE *);
367 static void read_timeout(int, void *);
368 static void read_event(int, void *);
369 static int show_count;
370 static int sess_count;
371 static int quit_count;
372 static int mesg_count;
373 static int max_quit_count;
374 static int max_msg_quit_count;
375 static int disable_pipelining;
376 static int disable_8bitmime;
377 static int disable_esmtp;
378 static int enable_lmtp;
379 static int pretend_pix;
380 static int disable_saslauth;
381 static int disable_xclient;
382 static int disable_xforward;
383 static int disable_enh_status;
384 static int disable_dsn;
385 static int max_client_count = DEF_MAX_CLIENT_COUNT;
386 static int client_count;
387 static int sock;
388 static int abort_delay = -1;
389 static int data_read_delay = 0;
390 
391 static char *single_template; /* individual template */
392 static char *shared_template; /* shared template */
393 static VSTRING *start_string; /* dump content prefix */
394 
395 static INET_PROTO_INFO *proto_info;
396 
397 #define STR(x) vstring_str(x)
398 
399 /* do_stats - show counters */
400 
401 static void do_stats(void)
402 {
403  vstream_printf("sess=%d quit=%d mesg=%d\r",
404  sess_count, quit_count, mesg_count);
406 }
407 
408 /* hard_err_resp - generic hard error response */
409 
410 static void hard_err_resp(SINK_STATE *state)
411 {
412  smtp_printf(state->stream, "%s", hard_error_resp);
413  SMTP_FLUSH(state->stream);
414 }
415 
416 /* soft_err_resp - generic soft error response */
417 
418 static void soft_err_resp(SINK_STATE *state)
419 {
420  smtp_printf(state->stream, "%s", soft_error_resp);
421  SMTP_FLUSH(state->stream);
422 }
423 
424 /* exp_path_template - expand template pathname, static result */
425 
426 static VSTRING *exp_path_template(const char *template, time_t start_time)
427 {
428  static VSTRING *path_buf = 0;
429  struct tm *lt;
430 
431  if (path_buf == 0)
432  path_buf = vstring_alloc(100);
433  else
434  VSTRING_RESET(path_buf);
435  lt = localtime(&start_time);
436  while (strftime(STR(path_buf), vstring_avail(path_buf), template, lt) == 0)
437  VSTRING_SPACE(path_buf, vstring_avail(path_buf) + 100);
438  VSTRING_SKIP(path_buf);
439  return (path_buf);
440 }
441 
442 /* make_parent_dir - create parent directory or bust */
443 
444 static void make_parent_dir(const char *path, mode_t mode)
445 {
446  const char *parent;
447 
448  parent = sane_dirname((VSTRING *) 0, path);
449  if (make_dirs(parent, mode) < 0)
450  msg_fatal("mkdir %s: %m", parent);
451 }
452 
453 /* mail_file_open - open mail capture file */
454 
455 static void mail_file_open(SINK_STATE *state)
456 {
457  const char *myname = "mail_file_open";
458  VSTRING *path_buf;
459  ssize_t len;
460  int tries = 0;
461 
462  /*
463  * Save the start time for later.
464  */
465  time(&(state->start_time));
466 
467  /*
468  * Expand the per-message dumpfile pathname template.
469  */
470  path_buf = exp_path_template(single_template, state->start_time);
471 
472  /*
473  * Append a random hexadecimal string to the pathname and create a new
474  * file. Retry with a different path if the file already exists. Create
475  * intermediate directories on the fly when the template specifies
476  * multiple pathname segments.
477  */
478 #define ID_FORMAT "%08x"
479 
480  for (len = VSTRING_LEN(path_buf); /* void */ ; vstring_truncate(path_buf, len)) {
481  if (++tries > 100)
482  msg_fatal("%s: something is looping", myname);
483  state->id = myrand();
484  vstring_sprintf_append(path_buf, ID_FORMAT, state->id);
485  if ((state->dump_file = vstream_fopen(STR(path_buf),
486  O_RDWR | O_CREAT | O_EXCL,
487  0644)) != 0) {
488  break;
489  } else if (errno == EEXIST) {
490  continue;
491  } else if (errno == ENOENT) {
492  make_parent_dir(STR(path_buf), 0755);
493  continue;
494  } else {
495  msg_fatal("open %s: %m", STR(path_buf));
496  }
497  }
498 
499  /*
500  * Don't leave temporary files behind.
501  */
502  if (shared_template != 0 && unlink(STR(path_buf)) < 0)
503  msg_fatal("unlink %s: %m", STR(path_buf));
504 
505  /*
506  * Do initial header records.
507  */
508  if (start_string)
509  vstream_fprintf(state->dump_file, "%s", STR(start_string));
510  vstream_fprintf(state->dump_file, "X-Client-Addr: %s%s\n",
511  state->addr_prefix, state->client_addr.buf);
512  vstream_fprintf(state->dump_file, "X-Client-Proto: %s\n", state->client_proto);
513  if (state->helo_args)
514  vstream_fprintf(state->dump_file, "X-Helo-Args: %s\n", state->helo_args);
515  /* Note: there may be more than one recipient. */
516 }
517 
518 /* mail_file_finish_header - do final smtp-sink generated header records */
519 
520 static void mail_file_finish_header(SINK_STATE *state)
521 {
522  if (state->helo_args)
523  vstream_fprintf(state->dump_file, "Received: from %s ([%s%s])\n",
524  state->helo_args, state->addr_prefix,
525  state->client_addr.buf);
526  else
527  vstream_fprintf(state->dump_file, "Received: from [%s%s] ([%s%s])\n",
528  state->addr_prefix, state->client_addr.buf,
529  state->addr_prefix, state->client_addr.buf);
530  vstream_fprintf(state->dump_file, "\tby %s (smtp-sink)"
531  " with %s id " ID_FORMAT ";\n",
532  var_myhostname, state->client_proto, state->id);
533  vstream_fprintf(state->dump_file, "\t%s\n", mail_date(state->start_time));
534 }
535 
536 /* mail_file_cleanup - common cleanup for capture file */
537 
538 static void mail_file_cleanup(SINK_STATE *state)
539 {
540  (void) vstream_fclose(state->dump_file);
541  state->dump_file = 0;
542 }
543 
544 /* mail_file_finish - handle message completion for capture file */
545 
546 static void mail_file_finish(SINK_STATE *state)
547 {
548 
549  /*
550  * Optionally append the captured message to a shared dumpfile.
551  */
552  if (shared_template) {
553  const char *out_path;
554  VSTREAM *out_fp;
555  ssize_t count;
556 
557  /*
558  * Expand the shared dumpfile pathname template.
559  */
560  out_path = STR(exp_path_template(shared_template, state->start_time));
561 
562  /*
563  * Open the shared dump file.
564  */
565 #define OUT_OPEN_FLAGS (O_WRONLY | O_CREAT | O_APPEND)
566 #define OUT_OPEN_MODE 0644
567 
568  if ((out_fp = vstream_fopen(out_path, OUT_OPEN_FLAGS, OUT_OPEN_MODE))
569  == 0 && errno == ENOENT) {
570  make_parent_dir(out_path, 0755);
571  out_fp = vstream_fopen(out_path, OUT_OPEN_FLAGS, OUT_OPEN_MODE);
572  }
573  if (out_fp == 0)
574  msg_fatal("open %s: %m", out_path);
575 
576  /*
577  * Append message content from single-message dump file.
578  */
579  if (vstream_fseek(state->dump_file, 0L, SEEK_SET) < 0)
580  msg_fatal("seek file %s: %m", VSTREAM_PATH(state->dump_file));
581  VSTRING_RESET(state->buffer);
582  for (;;) {
583  count = vstream_fread(state->dump_file, STR(state->buffer),
584  vstring_avail(state->buffer));
585  if (count <= 0)
586  break;
587  if (vstream_fwrite(out_fp, STR(state->buffer), count) != count)
588  msg_fatal("append file %s: %m", out_path);
589  }
590  if (vstream_ferror(state->dump_file))
591  msg_fatal("read file %s: %m", VSTREAM_PATH(state->dump_file));
592  if (vstream_fclose(out_fp))
593  msg_fatal("append file %s: %m", out_path);
594  }
595  mail_file_cleanup(state);
596 }
597 
598 /* mail_file_reset - abort mail to capture file */
599 
600 static void mail_file_reset(SINK_STATE *state)
601 {
602  if (shared_template == 0
603  && unlink(VSTREAM_PATH(state->dump_file)) < 0
604  && errno != ENOENT)
605  msg_fatal("unlink %s: %m", VSTREAM_PATH(state->dump_file));
606  mail_file_cleanup(state);
607 }
608 
609 /* mail_cmd_reset - reset mail transaction information */
610 
611 static void mail_cmd_reset(SINK_STATE *state)
612 {
613  state->in_mail = 0;
614  /* Not: state->rcpts = 0. This breaks the DOT reply with LMTP. */
615  if (state->dump_file)
616  mail_file_reset(state);
617 }
618 
619 /* ehlo_response - respond to EHLO command */
620 
621 static void ehlo_response(SINK_STATE *state, const char *args)
622 {
623 #define SKIP(cp, cond) do { \
624  for (/* void */; *cp && (cond); cp++) \
625  /* void */; \
626  } while (0)
627 
628  /* EHLO aborts a mail transaction in progress. */
629  mail_cmd_reset(state);
630  if (enable_lmtp == 0)
631  state->client_proto = "ESMTP";
632  smtp_printf(state->stream, "250-%s", var_myhostname);
633  if (!disable_pipelining)
634  smtp_printf(state->stream, "250-PIPELINING");
635  if (!disable_8bitmime)
636  smtp_printf(state->stream, "250-8BITMIME");
637  if (!disable_saslauth)
638  smtp_printf(state->stream, "250-AUTH PLAIN LOGIN");
639  if (!disable_xclient)
640  smtp_printf(state->stream, "250-XCLIENT NAME HELO");
641  if (!disable_xforward)
642  smtp_printf(state->stream, "250-XFORWARD NAME ADDR PROTO HELO");
643  if (!disable_enh_status)
644  smtp_printf(state->stream, "250-ENHANCEDSTATUSCODES");
645  if (!disable_dsn)
646  smtp_printf(state->stream, "250-DSN");
647  /* RFC 821/2821/5321: Format is replycode<SPACE>optional-text<CRLF> */
648  smtp_printf(state->stream, "250 ");
649  SMTP_FLUSH(state->stream);
650  if (single_template) {
651  if (state->helo_args)
652  myfree(state->helo_args);
653  SKIP(args, ISSPACE(*args));
654  state->helo_args = mystrdup(args);
655  }
656 }
657 
658 /* helo_response - respond to HELO command */
659 
660 static void helo_response(SINK_STATE *state, const char *args)
661 {
662  /* HELO aborts a mail transaction in progress. */
663  mail_cmd_reset(state);
664  state->client_proto = "SMTP";
665  smtp_printf(state->stream, "250 %s", var_myhostname);
666  SMTP_FLUSH(state->stream);
667  if (single_template) {
668  if (state->helo_args)
669  myfree(state->helo_args);
670  SKIP(args, ISSPACE(*args));
671  state->helo_args = mystrdup(args);
672  }
673 }
674 
675 /* ok_response - send 250 OK */
676 
677 static void ok_response(SINK_STATE *state, const char *unused_args)
678 {
679  smtp_printf(state->stream, "250 2.0.0 Ok");
680  SMTP_FLUSH(state->stream);
681 }
682 
683 /* rset_response - reset, send 250 OK */
684 
685 static void rset_response(SINK_STATE *state, const char *unused_args)
686 {
687  mail_cmd_reset(state);
688  smtp_printf(state->stream, "250 2.1.0 Ok");
689  SMTP_FLUSH(state->stream);
690 }
691 
692 /* mail_response - reset recipient count, send 250 OK */
693 
694 static void mail_response(SINK_STATE *state, const char *args)
695 {
696  if (state->in_mail) {
697  smtp_printf(state->stream, "503 5.5.1 Error: nested MAIL command");
698  SMTP_FLUSH(state->stream);
699  return;
700  }
701  state->in_mail++;
702  state->rcpts = 0;
703  smtp_printf(state->stream, "250 2.1.0 Ok");
704  SMTP_FLUSH(state->stream);
705  if (single_template) {
706  mail_file_open(state);
707  SKIP(args, *args != ':');
708  SKIP(args, *args == ':');
709  SKIP(args, ISSPACE(*args));
710  vstream_fprintf(state->dump_file, "X-Mail-Args: %s\n", args);
711  }
712 }
713 
714 /* rcpt_response - bump recipient count, send 250 OK */
715 
716 static void rcpt_response(SINK_STATE *state, const char *args)
717 {
718  if (state->in_mail == 0) {
719  smtp_printf(state->stream, "503 5.5.1 Error: need MAIL command");
720  SMTP_FLUSH(state->stream);
721  return;
722  }
723  state->rcpts++;
724  smtp_printf(state->stream, "250 2.1.5 Ok");
725  SMTP_FLUSH(state->stream);
726  /* Note: there may be more than one recipient per mail transaction. */
727  if (state->dump_file) {
728  SKIP(args, *args != ':');
729  SKIP(args, *args == ':');
730  SKIP(args, ISSPACE(*args));
731  vstream_fprintf(state->dump_file, "X-Rcpt-Args: %s\n", args);
732  }
733 }
734 
735 /* abort_event - delayed abort after DATA command */
736 
737 static void abort_event(int unused_event, void *context)
738 {
739  SINK_STATE *state = (SINK_STATE *) context;
740 
741  smtp_printf(state->stream, "550 This violates SMTP");
742  SMTP_FLUSH(state->stream);
743  disconnect(state);
744 }
745 
746 /* delay_read_event - resume input event handling */
747 
748 static void delay_read_event(int event, void *context)
749 {
750  SINK_STATE *state = (SINK_STATE *) context;
751 
752  if (event != EVENT_TIME)
753  msg_panic("delay_read_event: non-timer event %d", event);
754 
755  event_enable_read(vstream_fileno(state->stream), read_event, (void *) state);
756  event_request_timer(read_timeout, (void *) state, var_tmout);
757 }
758 
759 /* delay_read - temporarily suspend input event handling */
760 
761 static void delay_read(SINK_STATE *state, int delay)
762 {
764  event_cancel_timer(read_timeout, (void *) state);
765  event_request_timer(delay_read_event, (void *) state, delay);
766 }
767 
768 /* data_response - respond to DATA command */
769 
770 static void data_response(SINK_STATE *state, const char *unused_args)
771 {
772  if (state->in_mail == 0 || state->rcpts == 0) {
773  smtp_printf(state->stream, "503 5.5.1 Error: need RCPT command");
774  SMTP_FLUSH(state->stream);
775  return;
776  }
777  /* Not: ST_ANY. */
778  state->data_state = ST_CR_LF;
779  smtp_printf(state->stream, "354 End data with <CR><LF>.<CR><LF>");
780  SMTP_FLUSH(state->stream);
781  if (abort_delay < 0) {
782  state->read_fn = data_read;
783  /* Todo: move into code that invokes the command response function. */
784  if (data_read_delay > 0)
785  delay_read(state, data_read_delay);
786  } else {
787  /* Stop reading, send premature 550, and disconnect. */
789  event_cancel_timer(read_event, (void *) state);
790  event_request_timer(abort_event, (void *) state, abort_delay);
791  }
792  if (state->dump_file)
793  mail_file_finish_header(state);
794 }
795 
796 /* dot_resp_hard - hard error response to . command */
797 
798 static void dot_resp_hard(SINK_STATE *state)
799 {
800  if (enable_lmtp) {
801  while (state->rcpts-- > 0) /* XXX this could block */
802  smtp_printf(state->stream, "%s", hard_error_resp);
803  } else {
804  smtp_printf(state->stream, "%s", hard_error_resp);
805  }
806  SMTP_FLUSH(state->stream);
807 }
808 
809 /* dot_resp_soft - soft error response to . command */
810 
811 static void dot_resp_soft(SINK_STATE *state)
812 {
813  if (enable_lmtp) {
814  while (state->rcpts-- > 0) /* XXX this could block */
815  smtp_printf(state->stream, "%s", soft_error_resp);
816  } else {
817  smtp_printf(state->stream, "%s", soft_error_resp);
818  }
819  SMTP_FLUSH(state->stream);
820 }
821 
822 /* dot_response - response to . command */
823 
824 static void dot_response(SINK_STATE *state, const char *unused_args)
825 {
826  if (enable_lmtp) {
827  while (state->rcpts-- > 0) /* XXX this could block */
828  smtp_printf(state->stream, "250 2.2.0 Ok");
829  } else {
830  smtp_printf(state->stream, "250 2.0.0 Ok");
831  }
832  SMTP_FLUSH(state->stream);
833 }
834 
835 /* quit_response - respond to QUIT command */
836 
837 static void quit_response(SINK_STATE *state, const char *unused_args)
838 {
839  smtp_printf(state->stream, "221 Bye");
840  smtp_flush(state->stream); /* not: SMTP_FLUSH */
841  if (show_count)
842  quit_count++;
843 }
844 
845 /* conn_response - respond to connect command */
846 
847 static void conn_response(SINK_STATE *state, const char *unused_args)
848 {
849  if (pretend_pix)
850  smtp_printf(state->stream, "220 ********");
851  else if (disable_esmtp)
852  smtp_printf(state->stream, "220 %s", var_myhostname);
853  else
854  smtp_printf(state->stream, "220 %s ESMTP", var_myhostname);
855  SMTP_FLUSH(state->stream);
856 }
857 
858 /* delay_event - delayed command response */
859 
860 static void delay_event(int unused_event, void *context)
861 {
862  SINK_STATE *state = (SINK_STATE *) context;
863 
864  switch (vstream_setjmp(state->stream)) {
865 
866  default:
867  msg_panic("unknown read/write error");
868  /* NOTREACHED */
869 
870  case SMTP_ERR_TIME:
871  msg_warn("write timeout");
872  disconnect(state);
873  return;
874 
875  case SMTP_ERR_EOF:
876  msg_warn("lost connection");
877  disconnect(state);
878  return;
879 
880  case 0:
881  state->delayed_response(state, state->delayed_args);
882  myfree(state->delayed_args);
883  state->delayed_args = 0;
884  break;
885  }
886 
887  if (state->delayed_response == quit_response) {
888  disconnect(state);
889  return;
890  }
891  state->delayed_response = 0;
892 
893  /* Resume input event handling after the delayed response. */
894  event_enable_read(vstream_fileno(state->stream), read_event, (void *) state);
895  event_request_timer(read_timeout, (void *) state, var_tmout);
896 }
897 
898 /* data_read - read data from socket */
899 
900 static int data_read(SINK_STATE *state)
901 {
902  int ch;
903  struct data_trans {
904  int state;
905  int want;
906  int next_state;
907  };
908  static struct data_trans data_trans[] = {
909  ST_ANY, '\r', ST_CR,
910  ST_CR, '\n', ST_CR_LF,
911  ST_CR_LF, '.', ST_CR_LF_DOT,
914  };
915  struct data_trans *dp;
916 
917  /*
918  * A read may result in EOF, but is never supposed to time out - a time
919  * out means that we were trying to read when no data was available.
920  */
921  for (;;) {
922  if ((ch = VSTREAM_GETC(state->stream)) == VSTREAM_EOF)
923  return (-1);
924  for (dp = data_trans; dp->state != state->data_state; dp++)
925  /* void */ ;
926 
927  /*
928  * Try to match the current character desired by the state machine.
929  * If that fails, try to restart the machine with a match for its
930  * first state. This covers the case of a CR/LF/CR/LF sequence
931  * (empty line) right before the end of the message data.
932  */
933  if (ch == dp->want)
934  state->data_state = dp->next_state;
935  else if (ch == data_trans[0].want)
936  state->data_state = data_trans[0].next_state;
937  else
938  state->data_state = ST_ANY;
939  if (state->dump_file) {
940  if (ch != '\r' && state->data_state != ST_CR_LF_DOT)
941  VSTREAM_PUTC(ch, state->dump_file);
942  if (vstream_ferror(state->dump_file))
943  msg_fatal("append file %s: %m", VSTREAM_PATH(state->dump_file));
944  }
945  if (state->data_state == ST_CR_LF_DOT_CR_LF) {
946  PUSH_BACK_SET(state, ".\r\n");
947  state->read_fn = command_read;
948  state->data_state = ST_ANY;
949  if (state->dump_file)
950  mail_file_finish(state);
951  mail_cmd_reset(state);
952  if (show_count || max_msg_quit_count > 0) {
953  mesg_count++;
954  if (show_count)
955  do_stats();
956  if (max_msg_quit_count > 0 && mesg_count >= max_msg_quit_count)
957  exit(0);
958  }
959  break;
960  }
961 
962  /*
963  * We must avoid blocking I/O, so get out of here as soon as both the
964  * VSTREAM and kernel read buffers dry up.
965  */
966  if (vstream_peek(state->stream) <= 0
967  && readable(vstream_fileno(state->stream)) <= 0)
968  return (0);
969  }
970  return (0);
971 }
972 
973  /*
974  * The table of all SMTP commands that we can handle.
975  */
976 typedef struct SINK_COMMAND {
977  const char *name;
978  void (*response) (SINK_STATE *, const char *);
981  int flags;
982  int delay;
984 } SINK_COMMAND;
985 
986 #define FLAG_ENABLE (1<<0) /* command is enabled */
987 #define FLAG_SYSLOG (1<<1) /* log the command */
988 #define FLAG_HARD_ERR (1<<2) /* report hard error */
989 #define FLAG_SOFT_ERR (1<<3) /* report soft error */
990 #define FLAG_DISCONNECT (1<<4) /* disconnect */
991 #define FLAG_CLOSE (1<<5) /* say goodbye and disconnect */
992 
993 static SINK_COMMAND command_table[] = {
994  "connect", conn_response, hard_err_resp, soft_err_resp, 0, 0, 0,
995  "helo", helo_response, hard_err_resp, soft_err_resp, 0, 0, 0,
996  "ehlo", ehlo_response, hard_err_resp, soft_err_resp, 0, 0, 0,
997  "lhlo", ehlo_response, hard_err_resp, soft_err_resp, 0, 0, 0,
998  "xclient", ok_response, hard_err_resp, soft_err_resp, FLAG_ENABLE, 0, 0,
999  "xforward", ok_response, hard_err_resp, soft_err_resp, FLAG_ENABLE, 0, 0,
1000  "auth", ok_response, hard_err_resp, soft_err_resp, FLAG_ENABLE, 0, 0,
1001  "mail", mail_response, hard_err_resp, soft_err_resp, FLAG_ENABLE, 0, 0,
1002  "rcpt", rcpt_response, hard_err_resp, soft_err_resp, FLAG_ENABLE, 0, 0,
1003  "data", data_response, hard_err_resp, soft_err_resp, FLAG_ENABLE, 0, 0,
1004  ".", dot_response, dot_resp_hard, dot_resp_soft, FLAG_ENABLE, 0, 0,
1005  "rset", rset_response, hard_err_resp, soft_err_resp, FLAG_ENABLE, 0, 0,
1006  "noop", ok_response, hard_err_resp, soft_err_resp, FLAG_ENABLE, 0, 0,
1007  "vrfy", ok_response, hard_err_resp, soft_err_resp, FLAG_ENABLE, 0, 0,
1008  "quit", quit_response, hard_err_resp, soft_err_resp, FLAG_ENABLE, 0, 0,
1009  0,
1010 };
1011 
1012 /* reset_cmd_flags - reset per-command command flags */
1013 
1014 static void reset_cmd_flags(const char *cmd, int flags)
1015 {
1016  SINK_COMMAND *cmdp;
1017 
1018  for (cmdp = command_table; cmdp->name != 0; cmdp++)
1019  if (strcasecmp(cmd, cmdp->name) == 0)
1020  break;
1021  if (cmdp->name == 0)
1022  msg_fatal("unknown command: %s", cmd);
1023  cmdp->flags &= ~flags;
1024 }
1025 
1026 /* set_cmd_flags - set per-command command flags */
1027 
1028 static void set_cmd_flags(const char *cmd, int flags)
1029 {
1030  SINK_COMMAND *cmdp;
1031 
1032  for (cmdp = command_table; cmdp->name != 0; cmdp++)
1033  if (strcasecmp(cmd, cmdp->name) == 0)
1034  break;
1035  if (cmdp->name == 0)
1036  msg_fatal("unknown command: %s", cmd);
1037  cmdp->flags |= flags;
1038 }
1039 
1040 /* set_cmds_flags - set per-command flags for multiple commands */
1041 
1042 static void set_cmds_flags(const char *cmds, int flags)
1043 {
1044  char *saved_cmds;
1045  char *cp;
1046  char *cmd;
1047 
1048  saved_cmds = cp = mystrdup(cmds);
1049  while ((cmd = mystrtok(&cp, CHARS_COMMA_SP)) != 0)
1050  set_cmd_flags(cmd, flags);
1051  myfree(saved_cmds);
1052 }
1053 
1054 /* set_cmd_delay - set per-command delay */
1055 
1056 static void set_cmd_delay(const char *cmd, int delay, int odds)
1057 {
1058  SINK_COMMAND *cmdp;
1059 
1060  for (cmdp = command_table; cmdp->name != 0; cmdp++)
1061  if (strcasecmp(cmd, cmdp->name) == 0)
1062  break;
1063  if (cmdp->name == 0)
1064  msg_fatal("unknown command: %s", cmd);
1065 
1066  if (delay <= 0)
1067  msg_fatal("non-positive '%s' delay", cmd);
1068  if (odds < 0 || odds > 99)
1069  msg_fatal("delay odds for '%s' out of range", cmd);
1070 
1071  cmdp->delay = delay;
1072  cmdp->delay_odds = odds;
1073 }
1074 
1075 /* set_cmd_delay_arg - set per-command delay from option argument */
1076 
1077 static void set_cmd_delay_arg(char *arg)
1078 {
1079  char *cp;
1080  char *saved_arg;
1081  char *cmd;
1082  char *delay;
1083  char *odds;
1084 
1085  saved_arg = cp = mystrdup(arg);
1086  cmd = mystrtok(&cp, ":");
1087  delay = mystrtok(&cp, ":");
1088  if (cmd == 0 || delay == 0)
1089  msg_fatal("invalid command delay argument: %s", arg);
1090  odds = mystrtok(&cp, "");
1091  set_cmd_delay(cmd, atoi(delay), odds ? atoi(odds) : 0);
1092  myfree(saved_arg);
1093 }
1094 
1095 /* command_resp - respond to command */
1096 
1097 static int command_resp(SINK_STATE *state, SINK_COMMAND *cmdp,
1098  const char *command, const char *args)
1099 {
1100  /* We use raw syslog. Sanitize data content and length. */
1101  if (cmdp->flags & FLAG_SYSLOG)
1102  syslog(LOG_INFO, "%s %.100s", command, args);
1103  if (cmdp->flags & FLAG_DISCONNECT)
1104  return (-1);
1105  if (cmdp->flags & FLAG_CLOSE) {
1106  smtp_printf(state->stream, "421 4.0.0 Server closing connection");
1107  return (-1);
1108  }
1109  if (cmdp->flags & FLAG_HARD_ERR) {
1110  cmdp->hard_response(state);
1111  return (0);
1112  }
1113  if (cmdp->flags & FLAG_SOFT_ERR) {
1114  cmdp->soft_response(state);
1115  return (0);
1116  }
1117  if (cmdp->delay > 0) {
1118  int delay = cmdp->delay;
1119 
1120  if (cmdp->delay_odds > 0)
1121  for (delay = 0;
1122  ((int) (100.0 * rand() / (RAND_MAX + 1.0))) < cmdp->delay_odds;
1123  delay += cmdp->delay)
1124  /* NOP */ ;
1125  /* Suspend input event handling while delaying the command response. */
1127  event_cancel_timer(read_timeout, (void *) state);
1128  event_request_timer(delay_event, (void *) state, delay);
1129  state->delayed_response = cmdp->response;
1130  state->delayed_args = mystrdup(args);
1131  } else {
1132  cmdp->response(state, args);
1133  if (cmdp->response == quit_response)
1134  return (-1);
1135  }
1136  return (0);
1137 }
1138 
1139 /* command_read - talk the SMTP protocol, server side */
1140 
1141 static int command_read(SINK_STATE *state)
1142 {
1143  char *command;
1144  SINK_COMMAND *cmdp;
1145  int ch;
1146  struct cmd_trans {
1147  int state;
1148  int want;
1149  int next_state;
1150  };
1151  static struct cmd_trans cmd_trans[] = {
1152  ST_ANY, '\r', ST_CR,
1153  ST_CR, '\n', ST_CR_LF,
1154  0, 0, 0,
1155  };
1156  struct cmd_trans *cp;
1157  char *ptr;
1158 
1159  /*
1160  * A read may result in EOF, but is never supposed to time out - a time
1161  * out means that we were trying to read when no data was available.
1162  */
1163 #define NEXT_CHAR(state) \
1164  (PUSH_BACK_PEEK(state) ? PUSH_BACK_GET(state) : VSTREAM_GETC(state->stream))
1165 
1166  if (state->data_state == ST_CR_LF)
1167  state->data_state = ST_ANY; /* XXX */
1168  for (;;) {
1169  if ((ch = NEXT_CHAR(state)) == VSTREAM_EOF)
1170  return (-1);
1171 
1172  /*
1173  * Sanity check. We don't want to store infinitely long commands.
1174  */
1175  if (VSTRING_LEN(state->buffer) >= var_max_line_length) {
1176  msg_warn("command line too long");
1177  return (-1);
1178  }
1179  VSTRING_ADDCH(state->buffer, ch);
1180 
1181  /*
1182  * Try to match the current character desired by the state machine.
1183  * If that fails, try to restart the machine with a match for its
1184  * first state.
1185  */
1186  for (cp = cmd_trans; cp->state != state->data_state; cp++)
1187  if (cp->want == 0)
1188  msg_panic("command_read: unknown state: %d", state->data_state);
1189  if (ch == cp->want)
1190  state->data_state = cp->next_state;
1191  else if (ch == cmd_trans[0].want)
1192  state->data_state = cmd_trans[0].next_state;
1193  else
1194  state->data_state = ST_ANY;
1195  if (state->data_state == ST_CR_LF)
1196  break;
1197 
1198  /*
1199  * We must avoid blocking I/O, so get out of here as soon as both the
1200  * VSTREAM and kernel read buffers dry up.
1201  *
1202  * XXX Solaris non-blocking read() may fail on a socket when ioctl
1203  * FIONREAD reports there is unread data. Diagnosis by Max Pashkov.
1204  * As a workaround we use readable() (which uses poll or select())
1205  * instead of peek_fd() (which uses ioctl FIONREAD). Workaround added
1206  * 20020604.
1207  */
1208  if (PUSH_BACK_PEEK(state) == 0 && vstream_peek(state->stream) <= 0
1209  && readable(vstream_fileno(state->stream)) <= 0)
1210  return (0);
1211  }
1212 
1213  /*
1214  * Properly terminate the result, and reset the buffer write pointer for
1215  * reading the next command. This is ugly, but not as ugly as trying to
1216  * deal with all the early returns below.
1217  */
1218  vstring_truncate(state->buffer, VSTRING_LEN(state->buffer) - 2);
1219  VSTRING_TERMINATE(state->buffer);
1220  state->data_state = ST_CR_LF;
1221  VSTRING_RESET(state->buffer);
1222 
1223  /*
1224  * Got a complete command line. Parse it.
1225  */
1226  ptr = vstring_str(state->buffer);
1227  if (msg_verbose)
1228  msg_info("%s", ptr);
1229  if ((command = mystrtok(&ptr, " \t")) == 0) {
1230  smtp_printf(state->stream, "500 5.5.2 Error: unknown command");
1231  SMTP_FLUSH(state->stream);
1232  return (0);
1233  }
1234  for (cmdp = command_table; cmdp->name != 0; cmdp++)
1235  if (strcasecmp(command, cmdp->name) == 0)
1236  break;
1237  if (cmdp->name == 0 || (cmdp->flags & FLAG_ENABLE) == 0) {
1238  smtp_printf(state->stream, "500 5.5.1 Error: unknown command");
1239  SMTP_FLUSH(state->stream);
1240  return (0);
1241  }
1242  return (command_resp(state, cmdp, command, printable(ptr, '?')));
1243 }
1244 
1245 /* read_timeout - handle timer event */
1246 
1247 static void read_timeout(int unused_event, void *context)
1248 {
1249  SINK_STATE *state = (SINK_STATE *) context;
1250 
1251  /*
1252  * We don't send anything to the client, because we would have to set up
1253  * an smtp_stream exception handler first. And that is just too much
1254  * trouble.
1255  */
1256  msg_warn("read timeout");
1257  disconnect(state);
1258 }
1259 
1260 /* read_event - handle command or data read events */
1261 
1262 static void read_event(int unused_event, void *context)
1263 {
1264  SINK_STATE *state = (SINK_STATE *) context;
1265 
1266  /*
1267  * The input reading routine not only reads input (with vstream calls)
1268  * but also produces output (with smtp_stream calls). Because the output
1269  * routines can raise timeout or EOF exceptions with vstream_longjmp(),
1270  * the input reading routine needs to set up corresponding exception
1271  * handlers with vstream_setjmp(). Guarding the input operations in the
1272  * same manner is not useful: we must read input in non-blocking mode, so
1273  * we never get called when the socket stays unreadable too long. And EOF
1274  * is already trivial to detect with the vstream calls.
1275  */
1276  do {
1277  switch (vstream_setjmp(state->stream)) {
1278 
1279  default:
1280  msg_panic("unknown read/write error");
1281  /* NOTREACHED */
1282 
1283  case SMTP_ERR_TIME:
1284  msg_warn("write timeout");
1285  disconnect(state);
1286  return;
1287 
1288  case SMTP_ERR_EOF:
1289  msg_warn("lost connection");
1290  disconnect(state);
1291  return;
1292 
1293  case 0:
1294  if (state->read_fn(state) < 0) {
1295  if (msg_verbose)
1296  msg_info("disconnect");
1297  disconnect(state);
1298  return;
1299  }
1300  }
1301  } while (PUSH_BACK_PEEK(state) != 0 || vstream_peek(state->stream) > 0);
1302 
1303  /*
1304  * Reset the idle timer. Wait until the next input event, or until the
1305  * idle timer goes off.
1306  */
1307  event_request_timer(read_timeout, (void *) state, var_tmout);
1308 }
1309 
1310 static void connect_event(int, void *);
1311 
1312 /* disconnect - handle disconnection events */
1313 
1314 static void disconnect(SINK_STATE *state)
1315 {
1317  event_cancel_timer(read_timeout, (void *) state);
1318  if (show_count) {
1319  sess_count++;
1320  do_stats();
1321  }
1322  vstream_fclose(state->stream);
1323  vstring_free(state->buffer);
1324  /* Clean up file capture attributes. */
1325  if (state->helo_args)
1326  myfree(state->helo_args);
1327  /* Delete incomplete mail transaction. */
1328  mail_cmd_reset(state);
1329  if (state->delayed_args)
1330  myfree(state->delayed_args);
1331  myfree((void *) state);
1332  if (max_quit_count > 0 && quit_count >= max_quit_count)
1333  exit(0);
1334  if (client_count-- == max_client_count)
1335  event_enable_read(sock, connect_event, (void *) 0);
1336 }
1337 
1338 /* connect_event - handle connection events */
1339 
1340 static void connect_event(int unused_event, void *unused_context)
1341 {
1342  struct sockaddr_storage ss;
1343  SOCKADDR_SIZE len = sizeof(ss);
1344  struct sockaddr *sa = (struct sockaddr *) &ss;
1345  SINK_STATE *state;
1346  int fd;
1347 
1348  if ((fd = sane_accept(sock, sa, &len)) >= 0) {
1349  /* Safety: limit the number of open sockets and capture files. */
1350  if (++client_count == max_client_count)
1352  state = (SINK_STATE *) mymalloc(sizeof(*state));
1353  if (strchr((char *) proto_info->sa_family_list, sa->sa_family))
1354  SOCKADDR_TO_HOSTADDR(sa, len, &state->client_addr,
1355  (MAI_SERVPORT_STR *) 0, sa->sa_family);
1356  else
1357  strncpy(state->client_addr.buf, "local", sizeof("local"));
1358  if (msg_verbose)
1359  msg_info("connect (%s %s)",
1360 #ifdef AF_LOCAL
1361  sa->sa_family == AF_LOCAL ? "AF_LOCAL" :
1362 #else
1363  sa->sa_family == AF_UNIX ? "AF_UNIX" :
1364 #endif
1365  sa->sa_family == AF_INET ? "AF_INET" :
1366 #ifdef AF_INET6
1367  sa->sa_family == AF_INET6 ? "AF_INET6" :
1368 #endif
1369  "unknown protocol family",
1370  state->client_addr.buf);
1372  state->stream = vstream_fdopen(fd, O_RDWR);
1373  vstream_tweak_sock(state->stream);
1374  state->buffer = vstring_alloc(1024);
1375  state->read_fn = command_read;
1376  state->data_state = ST_ANY;
1377  PUSH_BACK_SET(state, "");
1378  smtp_timeout_setup(state->stream, var_tmout);
1379  state->in_mail = 0;
1380  state->rcpts = 0;
1381  state->delayed_response = 0;
1382  state->delayed_args = 0;
1383  /* Initialize file capture attributes. */
1384 #ifdef AF_INET6
1385  if (sa->sa_family == AF_INET6)
1386  state->addr_prefix = "ipv6:";
1387  else
1388 #endif
1389  state->addr_prefix = "";
1390 
1391  state->helo_args = 0;
1392  state->client_proto = enable_lmtp ? "LMTP" : "SMTP";
1393  state->start_time = 0;
1394  state->id = 0;
1395  state->dump_file = 0;
1396 
1397  /*
1398  * We use the smtp_stream module to produce output. That module
1399  * throws an exception via vstream_longjmp() in case of a timeout or
1400  * lost connection error. Therefore we must prepare to handle these
1401  * exceptions with vstream_setjmp().
1402  */
1403  switch (vstream_setjmp(state->stream)) {
1404 
1405  default:
1406  msg_panic("unknown read/write error");
1407  /* NOTREACHED */
1408 
1409  case SMTP_ERR_TIME:
1410  msg_warn("write timeout");
1411  disconnect(state);
1412  return;
1413 
1414  case SMTP_ERR_EOF:
1415  msg_warn("lost connection");
1416  disconnect(state);
1417  return;
1418 
1419  case 0:
1420  if (command_resp(state, command_table, "connect", "") < 0)
1421  disconnect(state);
1422  else if (command_table->delay == 0) {
1423  event_enable_read(fd, read_event, (void *) state);
1424  event_request_timer(read_timeout, (void *) state, var_tmout);
1425  }
1426  }
1427  }
1428 }
1429 
1430 /* usage - explain */
1431 
1432 static void usage(char *myname)
1433 {
1434  msg_fatal("usage: %s [-468acCeEFLpPv] [-A abort_delay] [-b soft_bounce_reply] [-B hard_bounce_reply] [-d dump-template] [-D dump-template] [-f commands] [-h hostname] [-m max_concurrency] [-M message_quit_count] [-n quit_count] [-q commands] [-r commands] [-R root-dir] [-s commands] [-S start-string] [-u user_privs] [-w delay] [host]:port backlog", myname);
1435 }
1436 
1438 
1439 int main(int argc, char **argv)
1440 {
1441  int backlog;
1442  int ch;
1443  int delay;
1444  const char *protocols = INET_PROTO_NAME_ALL;
1445  const char *root_dir = 0;
1446  const char *user_privs = 0;
1447 
1448  /*
1449  * Fingerprint executables and core dumps.
1450  */
1452 
1453  /*
1454  * Fix 20051207.
1455  */
1456  signal(SIGPIPE, SIG_IGN);
1457 
1458  /*
1459  * Initialize diagnostics.
1460  */
1461  msg_vstream_init(argv[0], VSTREAM_ERR);
1462 
1463  /*
1464  * Parse JCL.
1465  */
1466  while ((ch = GETOPT(argc, argv, "468aA:b:B:cCd:D:eEf:Fh:H:Ln:m:M:NpPq:Q:r:R:s:S:t:T:u:vw:W:")) > 0) {
1467  switch (ch) {
1468  case '4':
1469  protocols = INET_PROTO_NAME_IPV4;
1470  break;
1471  case '6':
1472  protocols = INET_PROTO_NAME_IPV6;
1473  break;
1474  case '8':
1475  disable_8bitmime = 1;
1476  break;
1477  case 'a':
1478  disable_saslauth = 1;
1479  break;
1480  case 'A':
1481  if (!alldig(optarg) || (abort_delay = atoi(optarg)) < 0)
1482  usage(argv[0]);
1483  break;
1484  case 'b':
1485  if (optarg[0] != '4' || strspn(optarg, "0123456789") != 3) {
1486  msg_error("bad soft error reply: %s", optarg);
1487  usage(argv[0]);
1488  } else
1489  soft_error_resp = optarg;
1490  break;
1491  case 'B':
1492  if (optarg[0] != '5' || strspn(optarg, "0123456789") != 3) {
1493  msg_error("bad hard error reply: %s", optarg);
1494  usage(argv[0]);
1495  } else
1496  hard_error_resp = optarg;
1497  break;
1498  case 'c':
1499  show_count++;
1500  break;
1501  case 'C':
1502  disable_xclient = 1;
1503  reset_cmd_flags("xclient", FLAG_ENABLE);
1504  break;
1505  case 'd':
1506  single_template = optarg;
1507  break;
1508  case 'D':
1509  shared_template = optarg;
1510  break;
1511  case 'e':
1512  disable_esmtp = 1;
1513  break;
1514  case 'E':
1515  disable_enh_status = 1;
1516  break;
1517  case 'f':
1518  set_cmds_flags(optarg, FLAG_HARD_ERR);
1519  disable_pipelining = 1;
1520  break;
1521  case 'F':
1522  disable_xforward = 1;
1523  reset_cmd_flags("xforward", FLAG_ENABLE);
1524  break;
1525  case 'h':
1526  var_myhostname = optarg;
1527  break;
1528  case 'H':
1529  if ((data_read_delay = atoi(optarg)) <= 0)
1530  msg_fatal("bad data read delay: %s", optarg);
1531  break;
1532  case 'L':
1533  enable_lmtp = 1;
1534  break;
1535  case 'm':
1536  if ((max_client_count = atoi(optarg)) <= 0)
1537  msg_fatal("bad concurrency limit: %s", optarg);
1538  break;
1539  case 'M':
1540  if ((max_msg_quit_count = atoi(optarg)) <= 0)
1541  msg_fatal("bad message quit count: %s", optarg);
1542  break;
1543  case 'n':
1544  if ((max_quit_count = atoi(optarg)) <= 0)
1545  msg_fatal("bad quit count: %s", optarg);
1546  break;
1547  case 'N':
1548  disable_dsn = 1;
1549  break;
1550  case 'p':
1551  disable_pipelining = 1;
1552  break;
1553  case 'P':
1554  pretend_pix = 1;
1555  disable_esmtp = 1;
1556  break;
1557  case 'q':
1558  set_cmds_flags(optarg, FLAG_DISCONNECT);
1559  break;
1560  case 'Q':
1561  set_cmds_flags(optarg, FLAG_CLOSE);
1562  break;
1563  case 'r':
1564  set_cmds_flags(optarg, FLAG_SOFT_ERR);
1565  disable_pipelining = 1;
1566  break;
1567  case 'R':
1568  root_dir = optarg;
1569  break;
1570  case 's':
1571  openlog(basename(argv[0]), LOG_PID, LOG_MAIL);
1572  set_cmds_flags(optarg, FLAG_SYSLOG);
1573  break;
1574  case 'S':
1575  start_string = vstring_alloc(10);
1576  unescape(start_string, optarg);
1577  break;
1578  case 't':
1579  if ((var_tmout = atoi(optarg)) <= 0)
1580  msg_fatal("bad timeout: %s", optarg);
1581  break;
1582  case 'T':
1583  if ((inet_windowsize = atoi(optarg)) <= 0)
1584  msg_fatal("bad TCP window size: %s", optarg);
1585  break;
1586  case 'u':
1587  user_privs = optarg;
1588  break;
1589  case 'v':
1590  msg_verbose++;
1591  break;
1592  case 'w':
1593  if ((delay = atoi(optarg)) <= 0)
1594  usage(argv[0]);
1595  set_cmd_delay("data", delay, 0);
1596  break;
1597  case 'W':
1598  set_cmd_delay_arg(optarg);
1599  break;
1600  default:
1601  usage(argv[0]);
1602  }
1603  }
1604  if (argc - optind != 2)
1605  usage(argv[0]);
1606  if ((backlog = atoi(argv[optind + 1])) <= 0)
1607  usage(argv[0]);
1608  if (single_template && shared_template)
1609  msg_fatal("use only one of -d or -D, but not both");
1610  if (geteuid() == 0 && user_privs == 0)
1611  msg_fatal("-u option is required if running as root");
1612 
1613  /*
1614  * Initialize.
1615  */
1616  if (var_myhostname == 0)
1617  var_myhostname = "smtp-sink";
1618  set_cmds_flags(enable_lmtp ? "lhlo" :
1619  disable_esmtp ? "helo" :
1620  "helo, ehlo", FLAG_ENABLE);
1621  proto_info = inet_proto_init("protocols", protocols);
1622  if (strncmp(argv[optind], "unix:", 5) == 0) {
1623  sock = unix_listen(argv[optind] + 5, backlog, BLOCKING);
1624  } else {
1625  if (strncmp(argv[optind], "inet:", 5) == 0)
1626  argv[optind] += 5;
1627  sock = inet_listen(argv[optind], backlog, BLOCKING);
1628  }
1629  if (user_privs)
1630  chroot_uid(root_dir, user_privs);
1631 
1632  if (single_template)
1633  mysrand((int) time((time_t *) 0));
1634  else if (shared_template)
1635  single_template = shared_template;
1636 
1637  /*
1638  * Start the event handler.
1639  */
1640  event_enable_read(sock, connect_event, (void *) 0);
1641  for (;;)
1642  event_loop(-1);
1643 }
int msg_verbose
Definition: msg.c:177
void event_enable_read(int fd, EVENT_NOTIFY_RDWR_FN callback, void *context)
Definition: events.c:729
void msg_error(const char *fmt,...)
Definition: msg.c:231
VSTREAM * stream
Definition: qmqp-sink.c:92
#define VSTREAM_EOF
Definition: vstream.h:110
int data_state
Definition: smtp-sink.c:315
void myfree(void *ptr)
Definition: mymalloc.c:207
#define PUSH_BACK_SET(state, text)
Definition: smtp-sink.c:341
#define FLAG_SOFT_ERR
Definition: smtp-sink.c:989
char * mystrdup(const char *str)
Definition: mymalloc.c:225
int inet_listen(const char *addr, int backlog, int block_mode)
Definition: inet_listen.c:82
struct SINK_COMMAND SINK_COMMAND
const char * mail_date(time_t when)
Definition: mail_date.c:54
struct SINK_STATE SINK_STATE
MAI_HOSTADDR_STR client_addr
Definition: smtp-sink.c:321
NORETURN msg_panic(const char *fmt,...)
Definition: msg.c:295
#define vstring_str(vp)
Definition: vstring.h:71
#define VSTREAM_OUT
Definition: vstream.h:67
#define FLAG_HARD_ERR
Definition: smtp-sink.c:988
void chroot_uid(const char *root_dir, const char *user_name)
Definition: chroot_uid.c:43
#define ST_CR_LF
Definition: smtp-sink.c:334
#define RAND_MAX
Definition: myrand.h:18
#define SMTP_ERR_TIME
Definition: smtp_stream.h:31
#define VSTREAM_GETC(vp)
Definition: vstream.h:108
INET_PROTO_INFO * inet_proto_init(const char *context, const char *protocols)
Definition: inet_proto.c:180
#define FLAG_CLOSE
Definition: smtp-sink.c:991
MAIL_VERSION_STAMP_DECLARE
Definition: smtp-sink.c:1437
VSTRING * vstring_truncate(VSTRING *vp, ssize_t len)
Definition: vstring.c:415
#define SOCKADDR_TO_HOSTADDR(sa, salen, host, port, sock)
Definition: myaddrinfo.h:197
#define INET_PROTO_NAME_ALL
Definition: mail_params.h:992
#define HARD_ERROR_RESP
Definition: smtp-sink.c:348
#define VSTREAM_PATH(vp)
Definition: vstream.h:126
#define ST_CR
Definition: smtp-sink.c:333
int alldig(const char *string)
Definition: alldig.c:38
#define vstream_setjmp(stream)
Definition: vstream.h:248
#define FLAG_DISCONNECT
Definition: smtp-sink.c:990
char * mystrtok(char **src, const char *sep)
Definition: mystrtok.c:54
#define VSTRING_LEN(vp)
Definition: vstring.h:72
#define readable(fd)
Definition: iostuff.h:36
#define DEF_MAX_CLIENT_COUNT
Definition: smtp-sink.c:344
char * sane_dirname(VSTRING *bp, const char *path)
char * delayed_args
Definition: smtp-sink.c:329
const char * client_proto
Definition: smtp-sink.c:324
VSTREAM * vstream_fopen(const char *path, int flags, mode_t mode)
Definition: vstream.c:1241
#define VSTRING_TERMINATE(vp)
Definition: vstring.h:74
#define FLAG_ENABLE
Definition: smtp-sink.c:986
#define SOCKADDR_SIZE
Definition: sys_defs.h:1411
#define STR(x)
Definition: smtp-sink.c:397
#define VSTRING_ADDCH(vp, ch)
Definition: vstring.h:81
char buf[MAI_HOSTADDR_STRSIZE]
Definition: myaddrinfo.h:146
VSTRING * vstring_sprintf_append(VSTRING *vp, const char *format,...)
Definition: vstring.c:624
int in_mail
Definition: smtp-sink.c:317
#define SMTP_ERR_EOF
Definition: smtp_stream.h:30
VSTREAM * vstream_fprintf(VSTREAM *stream, const char *fmt,...)
Definition: vstream.c:1348
#define SOFT_ERROR_RESP
Definition: smtp-sink.c:347
void(* soft_response)(SINK_STATE *)
Definition: smtp-sink.c:980
int main(int argc, char **argv)
Definition: smtp-sink.c:1439
void(* hard_response)(SINK_STATE *)
Definition: smtp-sink.c:979
#define OUT_OPEN_MODE
#define VSTRING_SKIP(vp)
Definition: vstring.h:82
int vstream_fclose(VSTREAM *stream)
Definition: vstream.c:1268
time_t start_time
Definition: smtp-sink.c:325
#define ST_ANY
Definition: smtp-sink.c:332
void event_loop(int delay)
Definition: events.c:998
VSTREAM * vstream_printf(const char *fmt,...)
Definition: vstream.c:1335
#define VSTRING_RESET(vp)
Definition: vstring.h:77
#define ST_CR_LF_DOT
Definition: smtp-sink.c:335
int inet_windowsize
void msg_warn(const char *fmt,...)
Definition: msg.c:215
int(* read_fn)(struct SINK_STATE *)
Definition: smtp-sink.c:316
VSTRING * buffer
Definition: smtp-sink.c:314
VSTRING * vstring_alloc(ssize_t len)
Definition: vstring.c:353
#define smtp_timeout_setup(stream, timeout)
Definition: smtp_stream.h:47
unsigned char * sa_family_list
Definition: inet_proto.h:21
#define MAIL_VERSION_STAMP_ALLOCATE
Definition: mail_version.h:67
VSTREAM * dump_file
Definition: smtp-sink.c:327
int sane_accept(int sock, struct sockaddr *sa, SOCKADDR_SIZE *len)
Definition: sane_accept.c:47
void mysrand(int seed)
Definition: myrand.c:50
void smtp_printf(VSTREAM *stream, const char *fmt,...)
Definition: smtp_stream.c:272
#define OUT_OPEN_FLAGS
#define vstring_avail(vp)
Definition: vstring.h:86
#define INET_PROTO_NAME_IPV6
Definition: mail_params.h:991
#define NEXT_CHAR(state)
#define FLAG_SYSLOG
Definition: smtp-sink.c:987
NORETURN msg_fatal(const char *fmt,...)
Definition: msg.c:249
#define PUSH_BACK_PEEK(state)
Definition: smtp-sink.c:339
off_t vstream_fseek(VSTREAM *stream, off_t offset, int whence)
Definition: vstream.c:1093
void(* response)(SINK_STATE *, const char *)
Definition: smtp-sink.c:978
#define CHARS_COMMA_SP
Definition: sys_defs.h:1761
#define vstream_fread(v, b, n)
Definition: vstream.h:104
int vstream_fflush(VSTREAM *stream)
Definition: vstream.c:1257
void smtp_flush(VSTREAM *stream)
Definition: smtp_stream.c:228
#define SMTP_FLUSH(fp)
Definition: smtp-sink.c:354
#define GETOPT(argc, argv, str)
Definition: sys_defs.h:1313
#define vstream_fwrite(v, b, n)
Definition: vstream.h:105
#define vstream_peek(vp)
Definition: vstream.h:232
#define EVENT_TIME
Definition: events.h:43
#define NON_BLOCKING
Definition: iostuff.h:49
int int
Definition: smtpd_proxy.h:21
#define VSTRING_SPACE(vp, len)
Definition: vstring.h:70
#define SKIP(cp, cond)
int myrand(void)
Definition: myrand.c:58
int strcasecmp(const char *s1, const char *s2)
Definition: strcasecmp.c:41
#define ST_CR_LF_DOT_CR_LF
Definition: smtp-sink.c:337
int non_blocking(int, int)
Definition: non_blocking.c:55
int vstream_tweak_sock(VSTREAM *)
Definition: vstream_tweak.c:60
char * helo_args
Definition: smtp-sink.c:323
VSTRING * vstring_free(VSTRING *vp)
Definition: vstring.c:380
time_t event_request_timer(EVENT_NOTIFY_TIME_FN callback, void *context, int delay)
Definition: events.c:894
int unix_listen(const char *, int, int)
Definition: unix_listen.c:66
#define vstream_fileno(vp)
Definition: vstream.h:115
void msg_vstream_init(const char *name, VSTREAM *vp)
Definition: msg_vstream.c:77
char * addr_prefix
Definition: smtp-sink.c:322
int make_dirs(const char *path, int perms)
Definition: make_dirs.c:52
const char * name
Definition: smtp-sink.c:977
#define ISSPACE(c)
Definition: sys_defs.h:1753
#define VSTREAM_PUTC(ch, vp)
Definition: vstream.h:107
char * var_myhostname
Definition: mail_params.c:223
char * push_back_ptr
Definition: smtp-sink.c:319
char * printable(char *string, int replacement)
Definition: printable.c:49
void event_disable_readwrite(int fd)
Definition: events.c:839
void(* delayed_response)(struct SINK_STATE *state, const char *)
Definition: smtp-sink.c:328
#define ID_FORMAT
#define vstream_ferror(vp)
Definition: vstream.h:120
#define INET_PROTO_NAME_IPV4
Definition: mail_params.h:990
#define BLOCKING
Definition: iostuff.h:48
int event_cancel_timer(EVENT_NOTIFY_TIME_FN callback, void *context)
Definition: events.c:965
#define basename
Definition: stringops.h:36
#define ST_CR_LF_DOT_CR
Definition: smtp-sink.c:336
#define VSTREAM_ERR
Definition: vstream.h:68
VSTREAM * vstream_fdopen(int fd, int flags)
Definition: vstream.c:1204
int delay_odds
Definition: smtp-sink.c:983
void * mymalloc(ssize_t len)
Definition: mymalloc.c:150
VSTRING * unescape(VSTRING *, const char *)
Definition: unescape.c:69
void msg_info(const char *fmt,...)
Definition: msg.c:199