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