7 /* multi-threaded SMTP/LMTP test server
10 /* \fBsmtp-sink\fR [\fIoptions\fR] [\fBinet:\fR][\fIhost\fR]:\fIport\fR
13 /* \fBsmtp-sink\fR [\fIoptions\fR] \fBunix:\fR\fIpathname\fR \fIbacklog\fR
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
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.
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.
30 /* Note: this is an unsupported test program. No attempt is made
31 /* to maintain compatibility between successive versions.
35 /* Support IPv4 only. This option has no effect when
36 /* Postfix is built without IPv6 support.
38 /* Support IPv6 only. This option is not available when
39 /* Postfix is built without IPv6 support.
41 /* Do not announce 8BITMIME support.
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.
51 /* Display running counters that are updated whenever an SMTP
52 /* session ends, a QUIT command is executed, or when "." is
55 /* Disable XCLIENT support.
56 /* .IP "\fB-d \fIdump-template\fR"
57 /* Dump each mail transaction to a single-message file whose
58 /* name is created by expanding the \fIdump-template\fR via
59 /* strftime(3) and appending a pseudo-random hexadecimal number
60 /* (example: "%Y%m%d%H/%M." expands into "2006081203/05.809a62e3").
61 /* If the template contains "/" characters, missing directories
62 /* are created automatically. The message dump format is
65 /* Note: this option keeps one capture file open for every
66 /* mail transaction in progress.
67 /* .IP "\fB-D \fIdump-template\fR"
68 /* Append mail transactions to a multi-message dump file whose
69 /* name is created by expanding the \fIdump-template\fR via
71 /* If the template contains "/" characters, missing directories
72 /* are created automatically. The message dump format is
75 /* Note: this option keeps one capture file open for every
76 /* mail transaction in progress.
78 /* Do not announce ESMTP support.
80 /* Do not announce ENHANCEDSTATUSCODES support.
81 /* .IP "\fB-f \fIcommand,command,...\fR"
82 /* Reject the specified commands with a hard (5xx) error code.
83 /* This option implies \fB-p\fR.
85 /* Examples of commands are CONNECT, HELO, EHLO, LHLO, MAIL, RCPT, VRFY,
86 /* DATA, ., RSET, NOOP, and QUIT. Separate command names by
87 /* white space or commas, and use quotes to protect white space
88 /* from the shell. Command names are case-insensitive.
90 /* Disable XFORWARD support.
91 /* .IP "\fB-h\fI hostname\fR"
92 /* Use \fIhostname\fR in the SMTP greeting, in the HELO response,
93 /* and in the EHLO response. The default hostname is "smtp-sink".
95 /* Enable LMTP instead of SMTP.
96 /* .IP "\fB-m \fIcount\fR (default: 256)"
97 /* An upper bound on the maximal number of simultaneous
98 /* connections that \fBsmtp-sink\fR will handle. This prevents
99 /* the process from running out of file descriptors. Excess
100 /* connections will stay queued in the TCP/IP stack.
101 /* .IP "\fB-M \fIcount\fR"
102 /* Terminate after receiving \fIcount\fR messages.
103 /* .IP "\fB-n \fIcount\fR"
104 /* Terminate after \fIcount\fR sessions.
106 /* Do not announce support for ESMTP command pipelining.
108 /* Change the server greeting so that it appears to come through
109 /* a CISCO PIX system. Implies \fB-e\fR.
110 /* .IP "\fB-q \fIcommand,command,...\fR"
111 /* Disconnect (without replying) after receiving one of the
112 /* specified commands.
114 /* Examples of commands are CONNECT, HELO, EHLO, LHLO, MAIL, RCPT, VRFY,
115 /* DATA, ., RSET, NOOP, and QUIT. Separate command names by
116 /* white space or commas, and use quotes to protect white space
117 /* from the shell. Command names are case-insensitive.
118 /* .IP "\fB-Q \fIcommand,command,...\fR"
119 /* Send a 421 reply and disconnect after receiving one
120 /* of the specified commands.
122 /* Examples of commands are CONNECT, HELO, EHLO, LHLO, MAIL, RCPT, VRFY,
123 /* DATA, ., RSET, NOOP, and QUIT. Separate command names by
124 /* white space or commas, and use quotes to protect white space
125 /* from the shell. Command names are case-insensitive.
126 /* .IP "\fB-r \fIcommand,command,...\fR"
127 /* Reject the specified commands with a soft (4xx) error code.
128 /* This option implies \fB-p\fR.
130 /* Examples of commands are CONNECT, HELO, EHLO, LHLO, MAIL, RCPT, VRFY,
131 /* DATA, ., RSET, NOOP, and QUIT. Separate command names by
132 /* white space or commas, and use quotes to protect white space
133 /* from the shell. Command names are case-insensitive.
134 /* .IP "\fB-R \fIroot-directory\fR"
135 /* Change the process root directory to the specified location.
136 /* This option requires super-user privileges. See also the
138 /* .IP "\fB-s \fIcommand,command,...\fR"
139 /* Log the named commands to syslogd.
141 /* Examples of commands are CONNECT, HELO, EHLO, LHLO, MAIL, RCPT, VRFY,
142 /* DATA, ., RSET, NOOP, and QUIT. Separate command names by
143 /* white space or commas, and use quotes to protect white space
144 /* from the shell. Command names are case-insensitive.
145 /* .IP "\fB-S start-string\fR"
146 /* An optional string that is prepended to each message that is
147 /* written to a dump file (see the dump file format description
148 /* below). The following C escape sequences are supported: \ea
149 /* (bell), \eb (backslace), \ef (formfeed), \en (newline), \er
150 /* (carriage return), \et (horizontal tab), \ev (vertical tab),
151 /* \e\fIddd\fR (up to three octal digits) and \e\e (the backslash
153 /* .IP "\fB-t \fItimeout\fR (default: 100)"
154 /* Limit the time for receiving a command or sending a response.
155 /* The time limit is specified in seconds.
156 /* .IP "\fB-T \fIwindowsize\fR"
157 /* Override the default TCP window size. To work around
158 /* broken TCP window scaling implementations, specify a
159 /* value > 0 and < 65536.
160 /* .IP "\fB-u \fIusername\fR"
161 /* Switch to the specified user privileges after opening the
162 /* network socket and optionally changing the process root
163 /* directory. This option is required when the process runs
164 /* with super-user privileges. See also the \fB-R\fR option.
166 /* Show the SMTP conversations.
167 /* .IP "\fB-w \fIdelay\fR"
168 /* Wait \fIdelay\fR seconds before responding to a DATA command.
169 /* .IP "\fB-W \fIcommand:delay[:odds]\fR"
170 /* Wait \fIdelay\fR seconds before responding to \fIcommand\fR.
171 /* If \fIodds\fR is also specified (a number between 1-99
172 /* inclusive), wait for a random multiple of \fIdelay\fR. The
173 /* random multiplier is equal to the number of times the program
174 /* needs to roll a dice with a range of 0..99 inclusive, before
175 /* the dice produces a result greater than or equal to \fIodds\fR.
176 /* .IP [\fBinet:\fR][\fIhost\fR]:\fIport\fR
177 /* Listen on network interface \fIhost\fR (default: any interface)
178 /* TCP port \fIport\fR. Both \fIhost\fR and \fIport\fR may be
179 /* specified in numeric or symbolic form.
180 /* .IP \fBunix:\fR\fIpathname\fR
181 /* Listen on the UNIX-domain socket at \fIpathname\fR.
183 /* The maximum length the queue of pending connections,
184 /* as defined by the \fBlisten\fR(2) system call.
188 /* Each dumped message contains a sequence of text lines,
189 /* terminated with the newline character. The sequence of
190 /* information is as follows:
192 /* The optional string specified with the \fB-S\fR option.
194 /* The \fBsmtp-sink\fR generated headers as documented below.
196 /* The message header and body as received from the SMTP client.
200 /* The format of the \fBsmtp-sink\fR generated headers is as
202 /* .IP "\fBX-Client-Addr: \fItext\fR"
203 /* The client IP address without enclosing []. An IPv6 address
204 /* is prefixed with "ipv6:". This record is always present.
205 /* .IP "\fBX-Client-Proto: \fItext\fR"
206 /* The client protocol: SMTP, ESMTP or LMTP. This record is
208 /* .IP "\fBX-Helo-Args: \fItext\fR"
209 /* The arguments of the last HELO or EHLO command before this
210 /* mail delivery transaction. This record is present only if
211 /* the client sent a recognizable HELO or EHLO command before
213 /* .IP "\fBX-Mail-Args: \fItext\fR"
214 /* The arguments of the MAIL command that started this mail
215 /* delivery transaction. This record is present exactly once.
216 /* .IP "\fBX-Rcpt-Args: \fItext\fR"
217 /* The arguments of an RCPT command within this mail delivery
218 /* transaction. There is one record for each RCPT command, and
219 /* they are in the order as sent by the client.
220 /* .IP "\fBReceived: \fItext\fR"
221 /* A message header for compatibility with mail processing
222 /* software. This three-line header marks the end of the headers
223 /* provided by \fBsmtp-sink\fR, and is formatted as follows:
225 /* .IP "\fBfrom \fIhelo\fB ([\fIaddr\fB])\fR"
226 /* The HELO or EHLO command argument and client IP address.
227 /* If the client did not send HELO or EHLO, the client IP
228 /* address is used instead.
229 /* .IP "\fBby \fIhost\fB (smtp-sink) with \fIproto\fB id \fIrandom\fB;\fR"
230 /* The hostname specified with the \fB-h\fR option, the client
231 /* protocol (see \fBX-Client-Proto\fR above), and the pseudo-random
232 /* portion of the per-message capture file name.
233 /* .IP \fItime-stamp\fR
234 /* A time stamp as defined in RFC 2822.
237 /* smtp-source(1), SMTP/LMTP message generator
241 /* The Secure Mailer license must be distributed with this software.
244 /* IBM T.J. Watson Research
246 /* Yorktown Heights, NY 10598, USA
249 /* System library. */
251 #include <sys_defs.h>
252 #include <sys/socket.h>
253 #include <sys/wait.h>
254 #include <sys/stat.h>
264 #ifdef STRCASECMP_IN_STRINGS_H
268 /* Utility library. */
273 #include <vstring_vstream.h>
274 #include <get_hostname.h>
277 #include <mymalloc.h>
279 #include <msg_vstream.h>
280 #include <stringops.h>
281 #include <sane_accept.h>
282 #include <inet_proto.h>
283 #include <myaddrinfo.h>
284 #include <make_dirs.h>
286 #include <chroot_uid.h>
288 /* Global library. */
290 #include <smtp_stream.h>
291 #include <mail_date.h>
292 #include <mail_version.h>
294 /* Application-specific. */
296 typedef struct SINK_STATE
{
300 int (*read_fn
) (struct SINK_STATE
*);
304 /* Capture file information for fake Received: header */
305 MAI_HOSTADDR_STR client_addr
; /* IP address */
306 char *addr_prefix
; /* ipv6: or empty */
307 char *helo_args
; /* text after HELO or EHLO */
308 const char *client_proto
; /* SMTP, ESMTP, LMTP */
309 time_t start_time
; /* MAIL command time */
310 int id
; /* pseudo-random */
311 VSTREAM
*dump_file
; /* dump file or null */
312 void (*delayed_response
) (struct SINK_STATE
*state
, const char *);
319 #define ST_CR_LF_DOT 3
320 #define ST_CR_LF_DOT_CR 4
321 #define ST_CR_LF_DOT_CR_LF 5
323 #define PUSH_BACK_PEEK(state) (*(state)->push_back_ptr != 0)
324 #define PUSH_BACK_GET(state) (*(state)->push_back_ptr++)
325 #define PUSH_BACK_SET(state, text) ((state)->push_back_ptr = (text))
327 #ifndef DEF_MAX_CLIENT_COUNT
328 #define DEF_MAX_CLIENT_COUNT 256
331 static int var_tmout
= 100;
332 static int var_max_line_length
= 2048;
333 static char *var_myhostname
;
334 static int command_read(SINK_STATE
*);
335 static int data_read(SINK_STATE
*);
336 static void disconnect(SINK_STATE
*);
337 static void read_timeout(int, char *);
338 static void read_event(int, char *);
340 static int sess_count
;
341 static int quit_count
;
342 static int mesg_count
;
343 static int max_quit_count
;
344 static int max_msg_quit_count
;
345 static int disable_pipelining
;
346 static int disable_8bitmime
;
347 static int disable_esmtp
;
348 static int enable_lmtp
;
349 static int pretend_pix
;
350 static int disable_saslauth
;
351 static int disable_xclient
;
352 static int disable_xforward
;
353 static int disable_enh_status
;
354 static int max_client_count
= DEF_MAX_CLIENT_COUNT
;
355 static int client_count
;
357 static int abort_delay
= -1;
359 static char *single_template
; /* individual template */
360 static char *shared_template
; /* shared template */
361 static VSTRING
*start_string
; /* dump content prefix */
363 static INET_PROTO_INFO
*proto_info
;
365 #define SOFT_ERROR_RESP "450 4.3.0 Error: command failed"
366 #define HARD_ERROR_RESP "500 5.3.0 Error: command failed"
368 #define STR(x) vstring_str(x)
370 /* do_stats - show counters */
372 static void do_stats(void)
374 vstream_printf("sess=%d quit=%d mesg=%d\r",
375 sess_count
, quit_count
, mesg_count
);
376 vstream_fflush(VSTREAM_OUT
);
379 /* hard_err_resp - generic hard error response */
381 static void hard_err_resp(SINK_STATE
*state
)
383 smtp_printf(state
->stream
, HARD_ERROR_RESP
);
384 smtp_flush(state
->stream
);
387 /* soft_err_resp - generic soft error response */
389 static void soft_err_resp(SINK_STATE
*state
)
391 smtp_printf(state
->stream
, SOFT_ERROR_RESP
);
392 smtp_flush(state
->stream
);
395 /* exp_path_template - expand template pathname, static result */
397 static VSTRING
*exp_path_template(const char *template, time_t start_time
)
399 static VSTRING
*path_buf
= 0;
403 path_buf
= vstring_alloc(100);
405 VSTRING_RESET(path_buf
);
406 lt
= localtime(&start_time
);
407 while (strftime(STR(path_buf
), vstring_avail(path_buf
), template, lt
) == 0)
408 VSTRING_SPACE(path_buf
, vstring_avail(path_buf
) + 100);
409 VSTRING_SKIP(path_buf
);
413 /* make_parent_dir - create parent directory or bust */
415 static void make_parent_dir(const char *path
, mode_t mode
)
419 parent
= sane_dirname((VSTRING
*) 0, path
);
420 if (make_dirs(parent
, mode
) < 0)
421 msg_fatal("mkdir %s: %m", parent
);
424 /* mail_file_open - open mail capture file */
426 static void mail_file_open(SINK_STATE
*state
)
428 const char *myname
= "mail_file_open";
434 * Save the start time for later.
436 time(&(state
->start_time
));
439 * Expand the per-message dumpfile pathname template.
441 path_buf
= exp_path_template(single_template
, state
->start_time
);
444 * Append a random hexadecimal string to the pathname and create a new
445 * file. Retry with a different path if the file already exists. Create
446 * intermediate directories on the fly when the template specifies
447 * multiple pathname segments.
449 #define ID_FORMAT "%08x"
451 for (len
= VSTRING_LEN(path_buf
); /* void */ ; vstring_truncate(path_buf
, len
)) {
453 msg_fatal("%s: something is looping", myname
);
454 state
->id
= myrand();
455 vstring_sprintf_append(path_buf
, ID_FORMAT
, state
->id
);
456 if ((state
->dump_file
= vstream_fopen(STR(path_buf
),
457 O_RDWR
| O_CREAT
| O_EXCL
,
460 } else if (errno
== EEXIST
) {
462 } else if (errno
== ENOENT
) {
463 make_parent_dir(STR(path_buf
), 0755);
466 msg_fatal("open %s: %m", STR(path_buf
));
471 * Don't leave temporary files behind.
473 if (shared_template
!= 0 && unlink(STR(path_buf
)) < 0)
474 msg_fatal("unlink %s: %m", STR(path_buf
));
477 * Do initial header records.
480 vstream_fprintf(state
->dump_file
, "%s", STR(start_string
));
481 vstream_fprintf(state
->dump_file
, "X-Client-Addr: %s%s\n",
482 state
->addr_prefix
, state
->client_addr
.buf
);
483 vstream_fprintf(state
->dump_file
, "X-Client-Proto: %s\n", state
->client_proto
);
484 if (state
->helo_args
)
485 vstream_fprintf(state
->dump_file
, "X-Helo-Args: %s\n", state
->helo_args
);
486 /* Note: there may be more than one recipient. */
489 /* mail_file_finish_header - do final smtp-sink generated header records */
491 static void mail_file_finish_header(SINK_STATE
*state
)
493 if (state
->helo_args
)
494 vstream_fprintf(state
->dump_file
, "Received: from %s ([%s%s])\n",
495 state
->helo_args
, state
->addr_prefix
,
496 state
->client_addr
.buf
);
498 vstream_fprintf(state
->dump_file
, "Received: from [%s%s] ([%s%s])\n",
499 state
->addr_prefix
, state
->client_addr
.buf
,
500 state
->addr_prefix
, state
->client_addr
.buf
);
501 vstream_fprintf(state
->dump_file
, "\tby %s (smtp-sink)"
502 " with %s id " ID_FORMAT
";\n",
503 var_myhostname
, state
->client_proto
, state
->id
);
504 vstream_fprintf(state
->dump_file
, "\t%s\n", mail_date(state
->start_time
));
507 /* mail_file_cleanup - common cleanup for capture file */
509 static void mail_file_cleanup(SINK_STATE
*state
)
511 (void) vstream_fclose(state
->dump_file
);
512 state
->dump_file
= 0;
515 /* mail_file_finish - handle message completion for capture file */
517 static void mail_file_finish(SINK_STATE
*state
)
521 * Optionally append the captured message to a shared dumpfile.
523 if (shared_template
) {
524 const char *out_path
;
529 * Expand the shared dumpfile pathname template.
531 out_path
= STR(exp_path_template(shared_template
, state
->start_time
));
534 * Open the shared dump file.
536 #define OUT_OPEN_FLAGS (O_WRONLY | O_CREAT | O_APPEND)
537 #define OUT_OPEN_MODE 0644
539 if ((out_fp
= vstream_fopen(out_path
, OUT_OPEN_FLAGS
, OUT_OPEN_MODE
))
540 == 0 && errno
== ENOENT
) {
541 make_parent_dir(out_path
, 0755);
542 out_fp
= vstream_fopen(out_path
, OUT_OPEN_FLAGS
, OUT_OPEN_MODE
);
545 msg_fatal("open %s: %m", out_path
);
548 * Append message content from single-message dump file.
550 if (vstream_fseek(state
->dump_file
, 0L, SEEK_SET
) < 0)
551 msg_fatal("seek file %s: %m", VSTREAM_PATH(state
->dump_file
));
552 VSTRING_RESET(state
->buffer
);
554 count
= vstream_fread(state
->dump_file
, STR(state
->buffer
),
555 vstring_avail(state
->buffer
));
558 if (vstream_fwrite(out_fp
, STR(state
->buffer
), count
) != count
)
559 msg_fatal("append file %s: %m", out_path
);
561 if (vstream_ferror(state
->dump_file
))
562 msg_fatal("read file %s: %m", VSTREAM_PATH(state
->dump_file
));
563 if (vstream_fclose(out_fp
))
564 msg_fatal("append file %s: %m", out_path
);
566 mail_file_cleanup(state
);
569 /* mail_file_reset - abort mail to capture file */
571 static void mail_file_reset(SINK_STATE
*state
)
573 if (shared_template
== 0
574 && unlink(VSTREAM_PATH(state
->dump_file
)) < 0
576 msg_fatal("unlink %s: %m", VSTREAM_PATH(state
->dump_file
));
577 mail_file_cleanup(state
);
580 /* mail_cmd_reset - reset mail transaction information */
582 static void mail_cmd_reset(SINK_STATE
*state
)
585 /* Not: state->rcpts = 0. This breaks the DOT reply with LMTP. */
586 if (state
->dump_file
)
587 mail_file_reset(state
);
590 /* ehlo_response - respond to EHLO command */
592 static void ehlo_response(SINK_STATE
*state
, const char *args
)
594 #define SKIP(cp, cond) for (/* void */; *cp && (cond); cp++)
596 /* EHLO aborts a mail transaction in progress. */
597 mail_cmd_reset(state
);
598 if (enable_lmtp
== 0)
599 state
->client_proto
= "ESMTP";
600 smtp_printf(state
->stream
, "250-%s", var_myhostname
);
601 if (!disable_pipelining
)
602 smtp_printf(state
->stream
, "250-PIPELINING");
603 if (!disable_8bitmime
)
604 smtp_printf(state
->stream
, "250-8BITMIME");
605 if (!disable_saslauth
)
606 smtp_printf(state
->stream
, "250-AUTH PLAIN LOGIN");
607 if (!disable_xclient
)
608 smtp_printf(state
->stream
, "250-XCLIENT NAME HELO");
609 if (!disable_xforward
)
610 smtp_printf(state
->stream
, "250-XFORWARD NAME ADDR PROTO HELO");
611 if (!disable_enh_status
)
612 smtp_printf(state
->stream
, "250-ENHANCEDSTATUSCODES");
613 smtp_printf(state
->stream
, "250 ");
614 smtp_flush(state
->stream
);
615 if (single_template
) {
616 if (state
->helo_args
)
617 myfree(state
->helo_args
);
618 SKIP(args
, ISSPACE(*args
));
619 state
->helo_args
= mystrdup(args
);
623 /* helo_response - respond to HELO command */
625 static void helo_response(SINK_STATE
*state
, const char *args
)
627 /* HELO aborts a mail transaction in progress. */
628 mail_cmd_reset(state
);
629 state
->client_proto
= "SMTP";
630 smtp_printf(state
->stream
, "250 %s", var_myhostname
);
631 smtp_flush(state
->stream
);
632 if (single_template
) {
633 if (state
->helo_args
)
634 myfree(state
->helo_args
);
635 SKIP(args
, ISSPACE(*args
));
636 state
->helo_args
= mystrdup(args
);
640 /* ok_response - send 250 OK */
642 static void ok_response(SINK_STATE
*state
, const char *unused_args
)
644 smtp_printf(state
->stream
, "250 2.0.0 Ok");
645 smtp_flush(state
->stream
);
648 /* rset_response - reset, send 250 OK */
650 static void rset_response(SINK_STATE
*state
, const char *unused_args
)
652 mail_cmd_reset(state
);
653 smtp_printf(state
->stream
, "250 2.1.0 Ok");
654 smtp_flush(state
->stream
);
657 /* mail_response - reset recipient count, send 250 OK */
659 static void mail_response(SINK_STATE
*state
, const char *args
)
661 if (state
->in_mail
) {
662 smtp_printf(state
->stream
, "503 5.5.1 Error: nested MAIL command");
663 smtp_flush(state
->stream
);
668 smtp_printf(state
->stream
, "250 2.1.0 Ok");
669 smtp_flush(state
->stream
);
670 if (single_template
) {
671 mail_file_open(state
);
672 SKIP(args
, *args
!= ':');
673 SKIP(args
, *args
== ':');
674 SKIP(args
, ISSPACE(*args
));
675 vstream_fprintf(state
->dump_file
, "X-Mail-Args: %s\n", args
);
679 /* rcpt_response - bump recipient count, send 250 OK */
681 static void rcpt_response(SINK_STATE
*state
, const char *args
)
683 if (state
->in_mail
== 0) {
684 smtp_printf(state
->stream
, "503 5.5.1 Error: need MAIL command");
685 smtp_flush(state
->stream
);
689 smtp_printf(state
->stream
, "250 2.1.5 Ok");
690 smtp_flush(state
->stream
);
691 /* Note: there may be more than one recipient per mail transaction. */
692 if (state
->dump_file
) {
693 SKIP(args
, *args
!= ':');
694 SKIP(args
, *args
== ':');
695 SKIP(args
, ISSPACE(*args
));
696 vstream_fprintf(state
->dump_file
, "X-Rcpt-Args: %s\n", args
);
700 /* abort_event - delayed abort after DATA command */
702 static void abort_event(int unused_event
, char *context
)
704 SINK_STATE
*state
= (SINK_STATE
*) context
;
706 smtp_printf(state
->stream
, "550 This violates SMTP");
707 smtp_flush(state
->stream
);
711 /* data_response - respond to DATA command */
713 static void data_response(SINK_STATE
*state
, const char *unused_args
)
715 if (state
->in_mail
== 0 || state
->rcpts
== 0) {
716 smtp_printf(state
->stream
, "503 5.5.1 Error: need RCPT command");
717 smtp_flush(state
->stream
);
721 state
->data_state
= ST_CR_LF
;
722 smtp_printf(state
->stream
, "354 End data with <CR><LF>.<CR><LF>");
723 smtp_flush(state
->stream
);
724 if (abort_delay
< 0) {
725 state
->read_fn
= data_read
;
727 /* Stop reading, send premature 550, and disconnect. */
728 event_disable_readwrite(vstream_fileno(state
->stream
));
729 event_cancel_timer(read_event
, (char *) state
);
730 event_request_timer(abort_event
, (char *) state
, abort_delay
);
732 if (state
->dump_file
)
733 mail_file_finish_header(state
);
736 /* dot_resp_hard - hard error response to . command */
738 static void dot_resp_hard(SINK_STATE
*state
)
741 while (state
->rcpts
-- > 0) /* XXX this could block */
742 smtp_printf(state
->stream
, HARD_ERROR_RESP
);
744 smtp_printf(state
->stream
, HARD_ERROR_RESP
);
746 smtp_flush(state
->stream
);
749 /* dot_resp_soft - soft error response to . command */
751 static void dot_resp_soft(SINK_STATE
*state
)
754 while (state
->rcpts
-- > 0) /* XXX this could block */
755 smtp_printf(state
->stream
, SOFT_ERROR_RESP
);
757 smtp_printf(state
->stream
, SOFT_ERROR_RESP
);
759 smtp_flush(state
->stream
);
762 /* dot_response - response to . command */
764 static void dot_response(SINK_STATE
*state
, const char *unused_args
)
767 while (state
->rcpts
-- > 0) /* XXX this could block */
768 smtp_printf(state
->stream
, "250 2.2.0 Ok");
770 smtp_printf(state
->stream
, "250 2.0.0 Ok");
772 smtp_flush(state
->stream
);
775 /* quit_response - respond to QUIT command */
777 static void quit_response(SINK_STATE
*state
, const char *unused_args
)
779 smtp_printf(state
->stream
, "221 Bye");
780 smtp_flush(state
->stream
);
785 /* conn_response - respond to connect command */
787 static void conn_response(SINK_STATE
*state
, const char *unused_args
)
790 smtp_printf(state
->stream
, "220 ********");
791 else if (disable_esmtp
)
792 smtp_printf(state
->stream
, "220 %s", var_myhostname
);
794 smtp_printf(state
->stream
, "220 %s ESMTP", var_myhostname
);
795 smtp_flush(state
->stream
);
798 /* delay_event - delayed command response */
800 static void delay_event(int unused_event
, char *context
)
802 SINK_STATE
*state
= (SINK_STATE
*) context
;
804 switch (vstream_setjmp(state
->stream
)) {
807 msg_panic("unknown read/write error");
811 msg_warn("write timeout");
816 msg_warn("lost connection");
821 state
->delayed_response(state
, state
->delayed_args
);
822 myfree(state
->delayed_args
);
823 state
->delayed_args
= 0;
827 if (state
->delayed_response
== quit_response
) {
831 state
->delayed_response
= 0;
833 /* Resume input event handling after the delayed response. */
834 event_enable_read(vstream_fileno(state
->stream
), read_event
, (char *) state
);
835 event_request_timer(read_timeout
, (char *) state
, var_tmout
);
838 /* data_read - read data from socket */
840 static int data_read(SINK_STATE
*state
)
848 static struct data_trans data_trans
[] = {
850 ST_CR
, '\n', ST_CR_LF
,
851 ST_CR_LF
, '.', ST_CR_LF_DOT
,
852 ST_CR_LF_DOT
, '\r', ST_CR_LF_DOT_CR
,
853 ST_CR_LF_DOT_CR
, '\n', ST_CR_LF_DOT_CR_LF
,
855 struct data_trans
*dp
;
858 * A read may result in EOF, but is never supposed to time out - a time
859 * out means that we were trying to read when no data was available.
862 if ((ch
= VSTREAM_GETC(state
->stream
)) == VSTREAM_EOF
)
864 for (dp
= data_trans
; dp
->state
!= state
->data_state
; dp
++)
868 * Try to match the current character desired by the state machine.
869 * If that fails, try to restart the machine with a match for its
870 * first state. This covers the case of a CR/LF/CR/LF sequence
871 * (empty line) right before the end of the message data.
874 state
->data_state
= dp
->next_state
;
875 else if (ch
== data_trans
[0].want
)
876 state
->data_state
= data_trans
[0].next_state
;
878 state
->data_state
= ST_ANY
;
879 if (state
->dump_file
) {
880 if (ch
!= '\r' && state
->data_state
!= ST_CR_LF_DOT
)
881 VSTREAM_PUTC(ch
, state
->dump_file
);
882 if (vstream_ferror(state
->dump_file
))
883 msg_fatal("append file %s: %m", VSTREAM_PATH(state
->dump_file
));
885 if (state
->data_state
== ST_CR_LF_DOT_CR_LF
) {
886 PUSH_BACK_SET(state
, ".\r\n");
887 state
->read_fn
= command_read
;
888 state
->data_state
= ST_ANY
;
889 if (state
->dump_file
)
890 mail_file_finish(state
);
891 mail_cmd_reset(state
);
892 if (count
|| max_msg_quit_count
> 0) {
896 if (max_msg_quit_count
> 0 && mesg_count
>= max_msg_quit_count
)
903 * We must avoid blocking I/O, so get out of here as soon as both the
904 * VSTREAM and kernel read buffers dry up.
906 if (vstream_peek(state
->stream
) <= 0
907 && readable(vstream_fileno(state
->stream
)) <= 0)
914 * The table of all SMTP commands that we can handle.
916 typedef struct SINK_COMMAND
{
918 void (*response
) (SINK_STATE
*, const char *);
919 void (*hard_response
) (SINK_STATE
*);
920 void (*soft_response
) (SINK_STATE
*);
926 #define FLAG_ENABLE (1<<0) /* command is enabled */
927 #define FLAG_SYSLOG (1<<1) /* log the command */
928 #define FLAG_HARD_ERR (1<<2) /* report hard error */
929 #define FLAG_SOFT_ERR (1<<3) /* report soft error */
930 #define FLAG_DISCONNECT (1<<4) /* disconnect */
931 #define FLAG_CLOSE (1<<5) /* say goodbye and disconnect */
933 static SINK_COMMAND command_table
[] = {
934 "connect", conn_response
, hard_err_resp
, soft_err_resp
, 0, 0, 0,
935 "helo", helo_response
, hard_err_resp
, soft_err_resp
, 0, 0, 0,
936 "ehlo", ehlo_response
, hard_err_resp
, soft_err_resp
, 0, 0, 0,
937 "lhlo", ehlo_response
, hard_err_resp
, soft_err_resp
, 0, 0, 0,
938 "xclient", ok_response
, hard_err_resp
, soft_err_resp
, FLAG_ENABLE
, 0, 0,
939 "xforward", ok_response
, hard_err_resp
, soft_err_resp
, FLAG_ENABLE
, 0, 0,
940 "auth", ok_response
, hard_err_resp
, soft_err_resp
, FLAG_ENABLE
, 0, 0,
941 "mail", mail_response
, hard_err_resp
, soft_err_resp
, FLAG_ENABLE
, 0, 0,
942 "rcpt", rcpt_response
, hard_err_resp
, soft_err_resp
, FLAG_ENABLE
, 0, 0,
943 "data", data_response
, hard_err_resp
, soft_err_resp
, FLAG_ENABLE
, 0, 0,
944 ".", dot_response
, dot_resp_hard
, dot_resp_soft
, FLAG_ENABLE
, 0, 0,
945 "rset", rset_response
, hard_err_resp
, soft_err_resp
, FLAG_ENABLE
, 0, 0,
946 "noop", ok_response
, hard_err_resp
, soft_err_resp
, FLAG_ENABLE
, 0, 0,
947 "vrfy", ok_response
, hard_err_resp
, soft_err_resp
, FLAG_ENABLE
, 0, 0,
948 "quit", quit_response
, hard_err_resp
, soft_err_resp
, FLAG_ENABLE
, 0, 0,
952 /* reset_cmd_flags - reset per-command command flags */
954 static void reset_cmd_flags(const char *cmd
, int flags
)
958 for (cmdp
= command_table
; cmdp
->name
!= 0; cmdp
++)
959 if (strcasecmp(cmd
, cmdp
->name
) == 0)
962 msg_fatal("unknown command: %s", cmd
);
963 cmdp
->flags
&= ~flags
;
966 /* set_cmd_flags - set per-command command flags */
968 static void set_cmd_flags(const char *cmd
, int flags
)
972 for (cmdp
= command_table
; cmdp
->name
!= 0; cmdp
++)
973 if (strcasecmp(cmd
, cmdp
->name
) == 0)
976 msg_fatal("unknown command: %s", cmd
);
977 cmdp
->flags
|= flags
;
980 /* set_cmds_flags - set per-command flags for multiple commands */
982 static void set_cmds_flags(const char *cmds
, int flags
)
988 saved_cmds
= cp
= mystrdup(cmds
);
989 while ((cmd
= mystrtok(&cp
, " \t\r\n,")) != 0)
990 set_cmd_flags(cmd
, flags
);
994 /* set_cmd_delay - set per-command delay */
996 static void set_cmd_delay(const char *cmd
, int delay
, int odds
)
1000 for (cmdp
= command_table
; cmdp
->name
!= 0; cmdp
++)
1001 if (strcasecmp(cmd
, cmdp
->name
) == 0)
1003 if (cmdp
->name
== 0)
1004 msg_fatal("unknown command: %s", cmd
);
1007 msg_fatal("non-positive '%s' delay", cmd
);
1008 if (odds
< 0 || odds
> 99)
1009 msg_fatal("delay odds for '%s' out of range", cmd
);
1011 cmdp
->delay
= delay
;
1012 cmdp
->delay_odds
= odds
;
1015 /* set_cmd_delay_arg - set per-command delay from option argument */
1017 static void set_cmd_delay_arg(char *arg
)
1025 saved_arg
= cp
= mystrdup(arg
);
1026 cmd
= mystrtok(&cp
, ":");
1027 delay
= mystrtok(&cp
, ":");
1028 if (cmd
== 0 || delay
== 0)
1029 msg_fatal("invalid command delay argument: %s", arg
);
1030 odds
= mystrtok(&cp
, "");
1031 set_cmd_delay(cmd
, atoi(delay
), odds
? atoi(odds
) : 0);
1035 /* command_resp - respond to command */
1037 static int command_resp(SINK_STATE
*state
, SINK_COMMAND
*cmdp
,
1038 const char *command
, const char *args
)
1040 /* We use raw syslog. Sanitize data content and length. */
1041 if (cmdp
->flags
& FLAG_SYSLOG
)
1042 syslog(LOG_INFO
, "%s %.100s", command
, args
);
1043 if (cmdp
->flags
& FLAG_DISCONNECT
)
1045 if (cmdp
->flags
& FLAG_CLOSE
) {
1046 smtp_printf(state
->stream
, "421 4.0.0 Server closing connection");
1049 if (cmdp
->flags
& FLAG_HARD_ERR
) {
1050 cmdp
->hard_response(state
);
1053 if (cmdp
->flags
& FLAG_SOFT_ERR
) {
1054 cmdp
->soft_response(state
);
1057 if (cmdp
->delay
> 0) {
1058 int delay
= cmdp
->delay
;
1060 if (cmdp
->delay_odds
> 0)
1062 ((int) (100.0 * rand() / (RAND_MAX
+ 1.0))) < cmdp
->delay_odds
;
1063 delay
+= cmdp
->delay
)
1065 /* Suspend input event handling while delaying the command response. */
1066 event_disable_readwrite(vstream_fileno(state
->stream
));
1067 event_cancel_timer(read_timeout
, (char *) state
);
1068 event_request_timer(delay_event
, (char *) state
, delay
);
1069 state
->delayed_response
= cmdp
->response
;
1070 state
->delayed_args
= mystrdup(args
);
1072 cmdp
->response(state
, args
);
1073 if (cmdp
->response
== quit_response
)
1079 /* command_read - talk the SMTP protocol, server side */
1081 static int command_read(SINK_STATE
*state
)
1091 static struct cmd_trans cmd_trans
[] = {
1092 ST_ANY
, '\r', ST_CR
,
1093 ST_CR
, '\n', ST_CR_LF
,
1096 struct cmd_trans
*cp
;
1100 * A read may result in EOF, but is never supposed to time out - a time
1101 * out means that we were trying to read when no data was available.
1103 #define NEXT_CHAR(state) \
1104 (PUSH_BACK_PEEK(state) ? PUSH_BACK_GET(state) : VSTREAM_GETC(state->stream))
1106 if (state
->data_state
== ST_CR_LF
)
1107 state
->data_state
= ST_ANY
; /* XXX */
1109 if ((ch
= NEXT_CHAR(state
)) == VSTREAM_EOF
)
1113 * Sanity check. We don't want to store infinitely long commands.
1115 if (VSTRING_LEN(state
->buffer
) >= var_max_line_length
) {
1116 msg_warn("command line too long");
1119 VSTRING_ADDCH(state
->buffer
, ch
);
1122 * Try to match the current character desired by the state machine.
1123 * If that fails, try to restart the machine with a match for its
1126 for (cp
= cmd_trans
; cp
->state
!= state
->data_state
; cp
++)
1128 msg_panic("command_read: unknown state: %d", state
->data_state
);
1130 state
->data_state
= cp
->next_state
;
1131 else if (ch
== cmd_trans
[0].want
)
1132 state
->data_state
= cmd_trans
[0].next_state
;
1134 state
->data_state
= ST_ANY
;
1135 if (state
->data_state
== ST_CR_LF
)
1139 * We must avoid blocking I/O, so get out of here as soon as both the
1140 * VSTREAM and kernel read buffers dry up.
1142 * XXX Solaris non-blocking read() may fail on a socket when ioctl
1143 * FIONREAD reports there is unread data. Diagnosis by Max Pashkov.
1144 * As a workaround we use readable() (which uses poll or select())
1145 * instead of peek_fd() (which uses ioctl FIONREAD). Workaround added
1148 if (PUSH_BACK_PEEK(state
) == 0 && vstream_peek(state
->stream
) <= 0
1149 && readable(vstream_fileno(state
->stream
)) <= 0)
1154 * Properly terminate the result, and reset the buffer write pointer for
1155 * reading the next command. This is ugly, but not as ugly as trying to
1156 * deal with all the early returns below.
1158 vstring_truncate(state
->buffer
, VSTRING_LEN(state
->buffer
) - 2);
1159 VSTRING_TERMINATE(state
->buffer
);
1160 state
->data_state
= ST_CR_LF
;
1161 VSTRING_RESET(state
->buffer
);
1164 * Got a complete command line. Parse it.
1166 ptr
= vstring_str(state
->buffer
);
1168 msg_info("%s", ptr
);
1169 if ((command
= mystrtok(&ptr
, " \t")) == 0) {
1170 smtp_printf(state
->stream
, "500 5.5.2 Error: unknown command");
1171 smtp_flush(state
->stream
);
1174 for (cmdp
= command_table
; cmdp
->name
!= 0; cmdp
++)
1175 if (strcasecmp(command
, cmdp
->name
) == 0)
1177 if (cmdp
->name
== 0 || (cmdp
->flags
& FLAG_ENABLE
) == 0) {
1178 smtp_printf(state
->stream
, "500 5.5.1 Error: unknown command");
1179 smtp_flush(state
->stream
);
1182 return (command_resp(state
, cmdp
, command
, printable(ptr
, '?')));
1185 /* read_timeout - handle timer event */
1187 static void read_timeout(int unused_event
, char *context
)
1189 SINK_STATE
*state
= (SINK_STATE
*) context
;
1192 * We don't send anything to the client, because we would have to set up
1193 * an smtp_stream exception handler first. And that is just too much
1196 msg_warn("read timeout");
1200 /* read_event - handle command or data read events */
1202 static void read_event(int unused_event
, char *context
)
1204 SINK_STATE
*state
= (SINK_STATE
*) context
;
1207 * The input reading routine not only reads input (with vstream calls)
1208 * but also produces output (with smtp_stream calls). Because the output
1209 * routines can raise timeout or EOF exceptions with vstream_longjmp(),
1210 * the input reading routine needs to set up corresponding exception
1211 * handlers with vstream_setjmp(). Guarding the input operations in the
1212 * same manner is not useful: we must read input in non-blocking mode, so
1213 * we never get called when the socket stays unreadable too long. And EOF
1214 * is already trivial to detect with the vstream calls.
1217 switch (vstream_setjmp(state
->stream
)) {
1220 msg_panic("unknown read/write error");
1224 msg_warn("write timeout");
1229 msg_warn("lost connection");
1234 if (state
->read_fn(state
) < 0) {
1236 msg_info("disconnect");
1241 } while (PUSH_BACK_PEEK(state
) != 0 || vstream_peek(state
->stream
) > 0);
1244 * Reset the idle timer. Wait until the next input event, or until the
1245 * idle timer goes off.
1247 event_request_timer(read_timeout
, (char *) state
, var_tmout
);
1250 static void connect_event(int, char *);
1252 /* disconnect - handle disconnection events */
1254 static void disconnect(SINK_STATE
*state
)
1256 event_disable_readwrite(vstream_fileno(state
->stream
));
1257 event_cancel_timer(read_timeout
, (char *) state
);
1262 vstream_fclose(state
->stream
);
1263 vstring_free(state
->buffer
);
1264 /* Clean up file capture attributes. */
1265 if (state
->helo_args
)
1266 myfree(state
->helo_args
);
1267 /* Delete incomplete mail transaction. */
1268 mail_cmd_reset(state
);
1269 if (state
->delayed_args
)
1270 myfree(state
->delayed_args
);
1271 myfree((char *) state
);
1272 if (max_quit_count
> 0 && quit_count
>= max_quit_count
)
1274 if (client_count
-- == max_client_count
)
1275 event_enable_read(sock
, connect_event
, (char *) 0);
1278 /* connect_event - handle connection events */
1280 static void connect_event(int unused_event
, char *unused_context
)
1283 SOCKADDR_SIZE len
= sizeof(sa
);
1287 if ((fd
= sane_accept(sock
, &sa
, &len
)) >= 0) {
1288 /* Safety: limit the number of open sockets and capture files. */
1289 if (++client_count
== max_client_count
)
1290 event_disable_readwrite(sock
);
1291 state
= (SINK_STATE
*) mymalloc(sizeof(*state
));
1292 if (strchr((char *) proto_info
->sa_family_list
, sa
.sa_family
))
1293 SOCKADDR_TO_HOSTADDR(&sa
, len
, &state
->client_addr
,
1294 (MAI_SERVPORT_STR
*) 0, sa
.sa_family
);
1296 strncpy(state
->client_addr
.buf
, "local", sizeof("local"));
1298 msg_info("connect (%s %s)",
1300 sa
.sa_family
== AF_LOCAL
? "AF_LOCAL" :
1302 sa
.sa_family
== AF_UNIX
? "AF_UNIX" :
1304 sa
.sa_family
== AF_INET
? "AF_INET" :
1306 sa
.sa_family
== AF_INET6
? "AF_INET6" :
1308 "unknown protocol family",
1309 state
->client_addr
.buf
);
1310 non_blocking(fd
, NON_BLOCKING
);
1311 state
->stream
= vstream_fdopen(fd
, O_RDWR
);
1312 state
->buffer
= vstring_alloc(1024);
1313 state
->read_fn
= command_read
;
1314 state
->data_state
= ST_ANY
;
1315 PUSH_BACK_SET(state
, "");
1316 smtp_timeout_setup(state
->stream
, var_tmout
);
1319 state
->delayed_response
= 0;
1320 state
->delayed_args
= 0;
1321 /* Initialize file capture attributes. */
1323 if (sa
.sa_family
== AF_INET6
)
1324 state
->addr_prefix
= "ipv6:";
1327 state
->addr_prefix
= "";
1329 state
->helo_args
= 0;
1330 state
->client_proto
= enable_lmtp
? "LMTP" : "SMTP";
1331 state
->start_time
= 0;
1333 state
->dump_file
= 0;
1336 * We use the smtp_stream module to produce output. That module
1337 * throws an exception via vstream_longjmp() in case of a timeout or
1338 * lost connection error. Therefore we must prepare to handle these
1339 * exceptions with vstream_setjmp().
1341 switch (vstream_setjmp(state
->stream
)) {
1344 msg_panic("unknown read/write error");
1348 msg_warn("write timeout");
1353 msg_warn("lost connection");
1358 if (command_resp(state
, command_table
, "connect", "") < 0)
1360 else if (command_table
->delay
== 0) {
1361 event_enable_read(fd
, read_event
, (char *) state
);
1362 event_request_timer(read_timeout
, (char *) state
, var_tmout
);
1368 /* usage - explain */
1370 static void usage(char *myname
)
1372 msg_fatal("usage: %s [-468acCeEFLpPv] [-A abort_delay] [-d dump-template] [-D dump-template] [-f commands] [-h hostname] [-m max_concurrency] [M message_quit_count] [-n quit_count] [-q commands] [-r commands] [-R root-dir] [-s commands] [-S start-string] [-u user_privs] [-w delay] [host]:port backlog", myname
);
1375 MAIL_VERSION_STAMP_DECLARE
;
1377 int main(int argc
, char **argv
)
1382 const char *protocols
= INET_PROTO_NAME_ALL
;
1383 const char *root_dir
= 0;
1384 const char *user_privs
= 0;
1387 * Fingerprint executables and core dumps.
1389 MAIL_VERSION_STAMP_ALLOCATE
;
1394 signal(SIGPIPE
, SIG_IGN
);
1397 * Initialize diagnostics.
1399 msg_vstream_init(argv
[0], VSTREAM_ERR
);
1404 while ((ch
= GETOPT(argc
, argv
, "468aA:cCd:D:eEf:Fh:Ln:m:M:pPq:Q:r:R:s:S:t:T:u:vw:W:")) > 0) {
1407 protocols
= INET_PROTO_NAME_IPV4
;
1410 protocols
= INET_PROTO_NAME_IPV6
;
1413 disable_8bitmime
= 1;
1416 disable_saslauth
= 1;
1419 if (!alldig(optarg
) || (abort_delay
= atoi(optarg
)) < 0)
1426 disable_xclient
= 1;
1427 reset_cmd_flags("xclient", FLAG_ENABLE
);
1430 single_template
= optarg
;
1433 shared_template
= optarg
;
1439 disable_enh_status
= 1;
1442 set_cmds_flags(optarg
, FLAG_HARD_ERR
);
1443 disable_pipelining
= 1;
1446 disable_xforward
= 1;
1447 reset_cmd_flags("xforward", FLAG_ENABLE
);
1450 var_myhostname
= optarg
;
1456 if ((max_client_count
= atoi(optarg
)) <= 0)
1457 msg_fatal("bad concurrency limit: %s", optarg
);
1460 if ((max_msg_quit_count
= atoi(optarg
)) <= 0)
1461 msg_fatal("bad message quit count: %s", optarg
);
1464 if ((max_quit_count
= atoi(optarg
)) <= 0)
1465 msg_fatal("bad quit count: %s", optarg
);
1468 disable_pipelining
= 1;
1475 set_cmds_flags(optarg
, FLAG_DISCONNECT
);
1478 set_cmds_flags(optarg
, FLAG_CLOSE
);
1481 set_cmds_flags(optarg
, FLAG_SOFT_ERR
);
1482 disable_pipelining
= 1;
1488 openlog(basename(argv
[0]), LOG_PID
, LOG_MAIL
);
1489 set_cmds_flags(optarg
, FLAG_SYSLOG
);
1492 start_string
= vstring_alloc(10);
1493 unescape(start_string
, optarg
);
1496 if ((var_tmout
= atoi(optarg
)) <= 0)
1497 msg_fatal("bad timeout: %s", optarg
);
1500 if ((inet_windowsize
= atoi(optarg
)) <= 0)
1501 msg_fatal("bad TCP window size: %s", optarg
);
1504 user_privs
= optarg
;
1510 if ((delay
= atoi(optarg
)) <= 0)
1512 set_cmd_delay("data", delay
, 0);
1515 set_cmd_delay_arg(optarg
);
1521 if (argc
- optind
!= 2)
1523 if ((backlog
= atoi(argv
[optind
+ 1])) <= 0)
1525 if (single_template
&& shared_template
)
1526 msg_fatal("use only one of -d or -D, but not both");
1527 if (geteuid() == 0 && user_privs
== 0)
1528 msg_fatal("-u option is required if running as root");
1533 if (var_myhostname
== 0)
1534 var_myhostname
= "smtp-sink";
1535 set_cmds_flags(enable_lmtp
? "lhlo" :
1536 disable_esmtp
? "helo" :
1537 "helo, ehlo", FLAG_ENABLE
);
1538 proto_info
= inet_proto_init("protocols", protocols
);
1539 if (strncmp(argv
[optind
], "unix:", 5) == 0) {
1540 sock
= unix_listen(argv
[optind
] + 5, backlog
, BLOCKING
);
1542 if (strncmp(argv
[optind
], "inet:", 5) == 0)
1544 sock
= inet_listen(argv
[optind
], backlog
, BLOCKING
);
1547 chroot_uid(root_dir
, user_privs
);
1549 if (single_template
)
1550 mysrand((int) time((time_t *) 0));
1551 else if (shared_template
)
1552 single_template
= shared_template
;
1555 * Start the event handler.
1557 event_enable_read(sock
, connect_event
, (char *) 0);