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