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