Patrick Welche <prlw1@cam.ac.uk>
[netbsd-mini2440.git] / external / ibm-public / postfix / dist / src / smtpstone / smtp-sink.c
blobc4f448273da3cfb8cebc749aac5a4cd6954f4962
1 /* $NetBSD$ */
3 /*++
4 /* NAME
5 /* smtp-sink 1
6 /* SUMMARY
7 /* multi-threaded SMTP/LMTP test server
8 /* SYNOPSIS
9 /* .fi
10 /* \fBsmtp-sink\fR [\fIoptions\fR] [\fBinet:\fR][\fIhost\fR]:\fIport\fR
11 /* \fIbacklog\fR
13 /* \fBsmtp-sink\fR [\fIoptions\fR] \fBunix:\fR\fIpathname\fR \fIbacklog\fR
14 /* DESCRIPTION
15 /* \fBsmtp-sink\fR listens on the named host (or address) and port.
16 /* It takes SMTP messages from the network and throws them away.
17 /* The purpose is to measure client performance, not protocol
18 /* compliance.
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.
33 /* Arguments:
34 /* .IP \fB-4\fR
35 /* Support IPv4 only. This option has no effect when
36 /* Postfix is built without IPv6 support.
37 /* .IP \fB-6\fR
38 /* Support IPv6 only. This option is not available when
39 /* Postfix is built without IPv6 support.
40 /* .IP \fB-8\fR
41 /* Do not announce 8BITMIME support.
42 /* .IP \fB-a\fR
43 /* Do not announce SASL authentication support.
44 /* .IP "\fB-A \fIdelay\fR"
45 /* Wait \fIdelay\fR seconds after responding to DATA, then
46 /* abort prematurely with a 550 reply status. Do not read
47 /* further input from the client; this is an attempt to block
48 /* the client before it sends ".". Specify a zero delay value
49 /* to abort immediately.
50 /* .IP \fB-c\fR
51 /* Display running counters that are updated whenever an SMTP
52 /* session ends, a QUIT command is executed, or when "." is
53 /* received.
54 /* .IP \fB-C\fR
55 /* Disable XCLIENT support.
56 /* .IP "\fB-d \fIdump-template\fR"
57 /* Dump each mail transaction to a single-message file whose
58 /* name is created by expanding the \fIdump-template\fR via
59 /* strftime(3) and appending a pseudo-random hexadecimal number
60 /* (example: "%Y%m%d%H/%M." expands into "2006081203/05.809a62e3").
61 /* If the template contains "/" characters, missing directories
62 /* are created automatically. The message dump format is
63 /* described below.
64 /* .sp
65 /* Note: this option keeps one capture file open for every
66 /* mail transaction in progress.
67 /* .IP "\fB-D \fIdump-template\fR"
68 /* Append mail transactions to a multi-message dump file whose
69 /* name is created by expanding the \fIdump-template\fR via
70 /* strftime(3).
71 /* If the template contains "/" characters, missing directories
72 /* are created automatically. The message dump format is
73 /* described below.
74 /* .sp
75 /* Note: this option keeps one capture file open for every
76 /* mail transaction in progress.
77 /* .IP \fB-e\fR
78 /* Do not announce ESMTP support.
79 /* .IP \fB-E\fR
80 /* Do not announce ENHANCEDSTATUSCODES support.
81 /* .IP "\fB-f \fIcommand,command,...\fR"
82 /* Reject the specified commands with a hard (5xx) error code.
83 /* This option implies \fB-p\fR.
84 /* .sp
85 /* Examples of commands are CONNECT, HELO, EHLO, LHLO, MAIL, RCPT, VRFY,
86 /* DATA, ., RSET, NOOP, and QUIT. Separate command names by
87 /* white space or commas, and use quotes to protect white space
88 /* from the shell. Command names are case-insensitive.
89 /* .IP \fB-F\fR
90 /* Disable XFORWARD support.
91 /* .IP "\fB-h\fI hostname\fR"
92 /* Use \fIhostname\fR in the SMTP greeting, in the HELO response,
93 /* and in the EHLO response. The default hostname is "smtp-sink".
94 /* .IP \fB-L\fR
95 /* Enable LMTP instead of SMTP.
96 /* .IP "\fB-m \fIcount\fR (default: 256)"
97 /* An upper bound on the maximal number of simultaneous
98 /* connections that \fBsmtp-sink\fR will handle. This prevents
99 /* the process from running out of file descriptors. Excess
100 /* connections will stay queued in the TCP/IP stack.
101 /* .IP "\fB-M \fIcount\fR"
102 /* Terminate after receiving \fIcount\fR messages.
103 /* .IP "\fB-n \fIcount\fR"
104 /* Terminate after \fIcount\fR sessions.
105 /* .IP \fB-p\fR
106 /* Do not announce support for ESMTP command pipelining.
107 /* .IP \fB-P\fR
108 /* Change the server greeting so that it appears to come through
109 /* a CISCO PIX system. Implies \fB-e\fR.
110 /* .IP "\fB-q \fIcommand,command,...\fR"
111 /* Disconnect (without replying) after receiving one of the
112 /* specified commands.
113 /* .sp
114 /* Examples of commands are CONNECT, HELO, EHLO, LHLO, MAIL, RCPT, VRFY,
115 /* DATA, ., RSET, NOOP, and QUIT. Separate command names by
116 /* white space or commas, and use quotes to protect white space
117 /* from the shell. Command names are case-insensitive.
118 /* .IP "\fB-Q \fIcommand,command,...\fR"
119 /* Send a 421 reply and disconnect after receiving one
120 /* of the specified commands.
121 /* .sp
122 /* Examples of commands are CONNECT, HELO, EHLO, LHLO, MAIL, RCPT, VRFY,
123 /* DATA, ., RSET, NOOP, and QUIT. Separate command names by
124 /* white space or commas, and use quotes to protect white space
125 /* from the shell. Command names are case-insensitive.
126 /* .IP "\fB-r \fIcommand,command,...\fR"
127 /* Reject the specified commands with a soft (4xx) error code.
128 /* This option implies \fB-p\fR.
129 /* .sp
130 /* Examples of commands are CONNECT, HELO, EHLO, LHLO, MAIL, RCPT, VRFY,
131 /* DATA, ., RSET, NOOP, and QUIT. Separate command names by
132 /* white space or commas, and use quotes to protect white space
133 /* from the shell. Command names are case-insensitive.
134 /* .IP "\fB-R \fIroot-directory\fR"
135 /* Change the process root directory to the specified location.
136 /* This option requires super-user privileges. See also the
137 /* \fB-u\fR option.
138 /* .IP "\fB-s \fIcommand,command,...\fR"
139 /* Log the named commands to syslogd.
140 /* .sp
141 /* Examples of commands are CONNECT, HELO, EHLO, LHLO, MAIL, RCPT, VRFY,
142 /* DATA, ., RSET, NOOP, and QUIT. Separate command names by
143 /* white space or commas, and use quotes to protect white space
144 /* from the shell. Command names are case-insensitive.
145 /* .IP "\fB-S start-string\fR"
146 /* An optional string that is prepended to each message that is
147 /* written to a dump file (see the dump file format description
148 /* below). The following C escape sequences are supported: \ea
149 /* (bell), \eb (backslace), \ef (formfeed), \en (newline), \er
150 /* (carriage return), \et (horizontal tab), \ev (vertical tab),
151 /* \e\fIddd\fR (up to three octal digits) and \e\e (the backslash
152 /* character).
153 /* .IP "\fB-t \fItimeout\fR (default: 100)"
154 /* Limit the time for receiving a command or sending a response.
155 /* The time limit is specified in seconds.
156 /* .IP "\fB-T \fIwindowsize\fR"
157 /* Override the default TCP window size. To work around
158 /* broken TCP window scaling implementations, specify a
159 /* value > 0 and < 65536.
160 /* .IP "\fB-u \fIusername\fR"
161 /* Switch to the specified user privileges after opening the
162 /* network socket and optionally changing the process root
163 /* directory. This option is required when the process runs
164 /* with super-user privileges. See also the \fB-R\fR option.
165 /* .IP \fB-v\fR
166 /* Show the SMTP conversations.
167 /* .IP "\fB-w \fIdelay\fR"
168 /* Wait \fIdelay\fR seconds before responding to a DATA command.
169 /* .IP "\fB-W \fIcommand:delay[:odds]\fR"
170 /* Wait \fIdelay\fR seconds before responding to \fIcommand\fR.
171 /* If \fIodds\fR is also specified (a number between 1-99
172 /* inclusive), wait for a random multiple of \fIdelay\fR. The
173 /* random multiplier is equal to the number of times the program
174 /* needs to roll a dice with a range of 0..99 inclusive, before
175 /* the dice produces a result greater than or equal to \fIodds\fR.
176 /* .IP [\fBinet:\fR][\fIhost\fR]:\fIport\fR
177 /* Listen on network interface \fIhost\fR (default: any interface)
178 /* TCP port \fIport\fR. Both \fIhost\fR and \fIport\fR may be
179 /* specified in numeric or symbolic form.
180 /* .IP \fBunix:\fR\fIpathname\fR
181 /* Listen on the UNIX-domain socket at \fIpathname\fR.
182 /* .IP \fIbacklog\fR
183 /* The maximum length the queue of pending connections,
184 /* as defined by the \fBlisten\fR(2) system call.
185 /* DUMP FILE FORMAT
186 /* .ad
187 /* .fi
188 /* Each dumped message contains a sequence of text lines,
189 /* terminated with the newline character. The sequence of
190 /* information is as follows:
191 /* .IP \(bu
192 /* The optional string specified with the \fB-S\fR option.
193 /* .IP \(bu
194 /* The \fBsmtp-sink\fR generated headers as documented below.
195 /* .IP \(bu
196 /* The message header and body as received from the SMTP client.
197 /* .IP \(bu
198 /* An empty line.
199 /* .PP
200 /* The format of the \fBsmtp-sink\fR generated headers is as
201 /* follows:
202 /* .IP "\fBX-Client-Addr: \fItext\fR"
203 /* The client IP address without enclosing []. An IPv6 address
204 /* is prefixed with "ipv6:". This record is always present.
205 /* .IP "\fBX-Client-Proto: \fItext\fR"
206 /* The client protocol: SMTP, ESMTP or LMTP. This record is
207 /* always present.
208 /* .IP "\fBX-Helo-Args: \fItext\fR"
209 /* The arguments of the last HELO or EHLO command before this
210 /* mail delivery transaction. This record is present only if
211 /* the client sent a recognizable HELO or EHLO command before
212 /* the DATA command.
213 /* .IP "\fBX-Mail-Args: \fItext\fR"
214 /* The arguments of the MAIL command that started this mail
215 /* delivery transaction. This record is present exactly once.
216 /* .IP "\fBX-Rcpt-Args: \fItext\fR"
217 /* The arguments of an RCPT command within this mail delivery
218 /* transaction. There is one record for each RCPT command, and
219 /* they are in the order as sent by the client.
220 /* .IP "\fBReceived: \fItext\fR"
221 /* A message header for compatibility with mail processing
222 /* software. This three-line header marks the end of the headers
223 /* provided by \fBsmtp-sink\fR, and is formatted as follows:
224 /* .RS
225 /* .IP "\fBfrom \fIhelo\fB ([\fIaddr\fB])\fR"
226 /* The HELO or EHLO command argument and client IP address.
227 /* If the client did not send HELO or EHLO, the client IP
228 /* address is used instead.
229 /* .IP "\fBby \fIhost\fB (smtp-sink) with \fIproto\fB id \fIrandom\fB;\fR"
230 /* The hostname specified with the \fB-h\fR option, the client
231 /* protocol (see \fBX-Client-Proto\fR above), and the pseudo-random
232 /* portion of the per-message capture file name.
233 /* .IP \fItime-stamp\fR
234 /* A time stamp as defined in RFC 2822.
235 /* .RE
236 /* SEE ALSO
237 /* smtp-source(1), SMTP/LMTP message generator
238 /* LICENSE
239 /* .ad
240 /* .fi
241 /* The Secure Mailer license must be distributed with this software.
242 /* AUTHOR(S)
243 /* Wietse Venema
244 /* IBM T.J. Watson Research
245 /* P.O. Box 704
246 /* Yorktown Heights, NY 10598, USA
247 /*--*/
249 /* System library. */
251 #include <sys_defs.h>
252 #include <sys/socket.h>
253 #include <sys/wait.h>
254 #include <sys/stat.h>
255 #include <unistd.h>
256 #include <string.h>
257 #include <stdlib.h>
258 #include <fcntl.h>
259 #include <syslog.h>
260 #include <signal.h>
261 #include <time.h>
262 #include <ctype.h>
264 #ifdef STRCASECMP_IN_STRINGS_H
265 #include <strings.h>
266 #endif
268 /* Utility library. */
270 #include <msg.h>
271 #include <vstring.h>
272 #include <vstream.h>
273 #include <vstring_vstream.h>
274 #include <get_hostname.h>
275 #include <listen.h>
276 #include <events.h>
277 #include <mymalloc.h>
278 #include <iostuff.h>
279 #include <msg_vstream.h>
280 #include <stringops.h>
281 #include <sane_accept.h>
282 #include <inet_proto.h>
283 #include <myaddrinfo.h>
284 #include <make_dirs.h>
285 #include <myrand.h>
286 #include <chroot_uid.h>
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 {
297 VSTREAM *stream;
298 VSTRING *buffer;
299 int data_state;
300 int (*read_fn) (struct SINK_STATE *);
301 int in_mail;
302 int rcpts;
303 char *push_back_ptr;
304 /* Capture file information for fake Received: header */
305 MAI_HOSTADDR_STR client_addr; /* IP address */
306 char *addr_prefix; /* ipv6: or empty */
307 char *helo_args; /* text after HELO or EHLO */
308 const char *client_proto; /* SMTP, ESMTP, LMTP */
309 time_t start_time; /* MAIL command time */
310 int id; /* pseudo-random */
311 VSTREAM *dump_file; /* dump file or null */
312 void (*delayed_response) (struct SINK_STATE *state, const char *);
313 char *delayed_args;
314 } SINK_STATE;
316 #define ST_ANY 0
317 #define ST_CR 1
318 #define ST_CR_LF 2
319 #define ST_CR_LF_DOT 3
320 #define ST_CR_LF_DOT_CR 4
321 #define ST_CR_LF_DOT_CR_LF 5
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
329 #endif
331 static int var_tmout = 100;
332 static int var_max_line_length = 2048;
333 static char *var_myhostname;
334 static int command_read(SINK_STATE *);
335 static int data_read(SINK_STATE *);
336 static void disconnect(SINK_STATE *);
337 static void read_timeout(int, char *);
338 static void read_event(int, char *);
339 static int count;
340 static int sess_count;
341 static int quit_count;
342 static int mesg_count;
343 static int max_quit_count;
344 static int max_msg_quit_count;
345 static int disable_pipelining;
346 static int disable_8bitmime;
347 static int disable_esmtp;
348 static int enable_lmtp;
349 static int pretend_pix;
350 static int disable_saslauth;
351 static int disable_xclient;
352 static int disable_xforward;
353 static int disable_enh_status;
354 static int max_client_count = DEF_MAX_CLIENT_COUNT;
355 static int client_count;
356 static int sock;
357 static int abort_delay = -1;
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;
400 struct tm *lt;
402 if (path_buf == 0)
403 path_buf = vstring_alloc(100);
404 else
405 VSTRING_RESET(path_buf);
406 lt = localtime(&start_time);
407 while (strftime(STR(path_buf), vstring_avail(path_buf), template, lt) == 0)
408 VSTRING_SPACE(path_buf, vstring_avail(path_buf) + 100);
409 VSTRING_SKIP(path_buf);
410 return (path_buf);
413 /* make_parent_dir - create parent directory or bust */
415 static void make_parent_dir(const char *path, mode_t mode)
417 const char *parent;
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";
429 VSTRING *path_buf;
430 ssize_t len;
431 int tries = 0;
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)) {
452 if (++tries > 100)
453 msg_fatal("%s: something is looping", myname);
454 state->id = myrand();
455 vstring_sprintf_append(path_buf, ID_FORMAT, state->id);
456 if ((state->dump_file = vstream_fopen(STR(path_buf),
457 O_RDWR | O_CREAT | O_EXCL,
458 0644)) != 0) {
459 break;
460 } else if (errno == EEXIST) {
461 continue;
462 } else if (errno == ENOENT) {
463 make_parent_dir(STR(path_buf), 0755);
464 continue;
465 } else {
466 msg_fatal("open %s: %m", STR(path_buf));
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.
479 if (start_string)
480 vstream_fprintf(state->dump_file, "%s", STR(start_string));
481 vstream_fprintf(state->dump_file, "X-Client-Addr: %s%s\n",
482 state->addr_prefix, state->client_addr.buf);
483 vstream_fprintf(state->dump_file, "X-Client-Proto: %s\n", state->client_proto);
484 if (state->helo_args)
485 vstream_fprintf(state->dump_file, "X-Helo-Args: %s\n", state->helo_args);
486 /* Note: there may be more than one recipient. */
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);
497 else
498 vstream_fprintf(state->dump_file, "Received: from [%s%s] ([%s%s])\n",
499 state->addr_prefix, state->client_addr.buf,
500 state->addr_prefix, state->client_addr.buf);
501 vstream_fprintf(state->dump_file, "\tby %s (smtp-sink)"
502 " with %s id " ID_FORMAT ";\n",
503 var_myhostname, state->client_proto, state->id);
504 vstream_fprintf(state->dump_file, "\t%s\n", mail_date(state->start_time));
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;
525 VSTREAM *out_fp;
526 ssize_t count;
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);
544 if (out_fp == 0)
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);
553 for (;;) {
554 count = vstream_fread(state->dump_file, STR(state->buffer),
555 vstring_avail(state->buffer));
556 if (count <= 0)
557 break;
558 if (vstream_fwrite(out_fp, STR(state->buffer), count) != count)
559 msg_fatal("append file %s: %m", out_path);
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
575 && errno != ENOENT)
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)
584 state->in_mail = 0;
585 /* Not: state->rcpts = 0. This breaks the DOT reply with LMTP. */
586 if (state->dump_file)
587 mail_file_reset(state);
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);
664 return;
666 state->in_mail++;
667 state->rcpts = 0;
668 smtp_printf(state->stream, "250 2.1.0 Ok");
669 smtp_flush(state->stream);
670 if (single_template) {
671 mail_file_open(state);
672 SKIP(args, *args != ':');
673 SKIP(args, *args == ':');
674 SKIP(args, ISSPACE(*args));
675 vstream_fprintf(state->dump_file, "X-Mail-Args: %s\n", args);
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);
686 return;
688 state->rcpts++;
689 smtp_printf(state->stream, "250 2.1.5 Ok");
690 smtp_flush(state->stream);
691 /* Note: there may be more than one recipient per mail transaction. */
692 if (state->dump_file) {
693 SKIP(args, *args != ':');
694 SKIP(args, *args == ':');
695 SKIP(args, ISSPACE(*args));
696 vstream_fprintf(state->dump_file, "X-Rcpt-Args: %s\n", args);
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);
708 disconnect(state);
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);
718 return;
720 /* Not: ST_ANY. */
721 state->data_state = ST_CR_LF;
722 smtp_printf(state->stream, "354 End data with <CR><LF>.<CR><LF>");
723 smtp_flush(state->stream);
724 if (abort_delay < 0) {
725 state->read_fn = data_read;
726 } else {
727 /* Stop reading, send premature 550, and disconnect. */
728 event_disable_readwrite(vstream_fileno(state->stream));
729 event_cancel_timer(read_event, (char *) state);
730 event_request_timer(abort_event, (char *) state, abort_delay);
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)
740 if (enable_lmtp) {
741 while (state->rcpts-- > 0) /* XXX this could block */
742 smtp_printf(state->stream, HARD_ERROR_RESP);
743 } else {
744 smtp_printf(state->stream, HARD_ERROR_RESP);
746 smtp_flush(state->stream);
749 /* dot_resp_soft - soft error response to . command */
751 static void dot_resp_soft(SINK_STATE *state)
753 if (enable_lmtp) {
754 while (state->rcpts-- > 0) /* XXX this could block */
755 smtp_printf(state->stream, SOFT_ERROR_RESP);
756 } else {
757 smtp_printf(state->stream, SOFT_ERROR_RESP);
759 smtp_flush(state->stream);
762 /* dot_response - response to . command */
764 static void dot_response(SINK_STATE *state, const char *unused_args)
766 if (enable_lmtp) {
767 while (state->rcpts-- > 0) /* XXX this could block */
768 smtp_printf(state->stream, "250 2.2.0 Ok");
769 } else {
770 smtp_printf(state->stream, "250 2.0.0 Ok");
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);
781 if (count)
782 quit_count++;
785 /* conn_response - respond to connect command */
787 static void conn_response(SINK_STATE *state, const char *unused_args)
789 if (pretend_pix)
790 smtp_printf(state->stream, "220 ********");
791 else if (disable_esmtp)
792 smtp_printf(state->stream, "220 %s", var_myhostname);
793 else
794 smtp_printf(state->stream, "220 %s ESMTP", var_myhostname);
795 smtp_flush(state->stream);
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)) {
806 default:
807 msg_panic("unknown read/write error");
808 /* NOTREACHED */
810 case SMTP_ERR_TIME:
811 msg_warn("write timeout");
812 disconnect(state);
813 return;
815 case SMTP_ERR_EOF:
816 msg_warn("lost connection");
817 disconnect(state);
818 return;
820 case 0:
821 state->delayed_response(state, state->delayed_args);
822 myfree(state->delayed_args);
823 state->delayed_args = 0;
824 break;
827 if (state->delayed_response == quit_response) {
828 disconnect(state);
829 return;
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)
842 int ch;
843 struct data_trans {
844 int state;
845 int want;
846 int next_state;
848 static struct data_trans data_trans[] = {
849 ST_ANY, '\r', ST_CR,
850 ST_CR, '\n', ST_CR_LF,
851 ST_CR_LF, '.', ST_CR_LF_DOT,
852 ST_CR_LF_DOT, '\r', ST_CR_LF_DOT_CR,
853 ST_CR_LF_DOT_CR, '\n', ST_CR_LF_DOT_CR_LF,
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.
861 for (;;) {
862 if ((ch = VSTREAM_GETC(state->stream)) == VSTREAM_EOF)
863 return (-1);
864 for (dp = data_trans; dp->state != state->data_state; dp++)
865 /* void */ ;
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.
873 if (ch == dp->want)
874 state->data_state = dp->next_state;
875 else if (ch == data_trans[0].want)
876 state->data_state = data_trans[0].next_state;
877 else
878 state->data_state = ST_ANY;
879 if (state->dump_file) {
880 if (ch != '\r' && state->data_state != ST_CR_LF_DOT)
881 VSTREAM_PUTC(ch, state->dump_file);
882 if (vstream_ferror(state->dump_file))
883 msg_fatal("append file %s: %m", VSTREAM_PATH(state->dump_file));
885 if (state->data_state == ST_CR_LF_DOT_CR_LF) {
886 PUSH_BACK_SET(state, ".\r\n");
887 state->read_fn = command_read;
888 state->data_state = ST_ANY;
889 if (state->dump_file)
890 mail_file_finish(state);
891 mail_cmd_reset(state);
892 if (count || max_msg_quit_count > 0) {
893 mesg_count++;
894 if (count)
895 do_stats();
896 if (max_msg_quit_count > 0 && mesg_count >= max_msg_quit_count)
897 exit(0);
899 break;
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)
908 return (0);
910 return (0);
914 * The table of all SMTP commands that we can handle.
916 typedef struct SINK_COMMAND {
917 const char *name;
918 void (*response) (SINK_STATE *, const char *);
919 void (*hard_response) (SINK_STATE *);
920 void (*soft_response) (SINK_STATE *);
921 int flags;
922 int delay;
923 int delay_odds;
924 } SINK_COMMAND;
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)
956 SINK_COMMAND *cmdp;
958 for (cmdp = command_table; cmdp->name != 0; cmdp++)
959 if (strcasecmp(cmd, cmdp->name) == 0)
960 break;
961 if (cmdp->name == 0)
962 msg_fatal("unknown command: %s", cmd);
963 cmdp->flags &= ~flags;
966 /* set_cmd_flags - set per-command command flags */
968 static void set_cmd_flags(const char *cmd, int flags)
970 SINK_COMMAND *cmdp;
972 for (cmdp = command_table; cmdp->name != 0; cmdp++)
973 if (strcasecmp(cmd, cmdp->name) == 0)
974 break;
975 if (cmdp->name == 0)
976 msg_fatal("unknown command: %s", cmd);
977 cmdp->flags |= flags;
980 /* set_cmds_flags - set per-command flags for multiple commands */
982 static void set_cmds_flags(const char *cmds, int flags)
984 char *saved_cmds;
985 char *cp;
986 char *cmd;
988 saved_cmds = cp = mystrdup(cmds);
989 while ((cmd = mystrtok(&cp, " \t\r\n,")) != 0)
990 set_cmd_flags(cmd, flags);
991 myfree(saved_cmds);
994 /* set_cmd_delay - set per-command delay */
996 static void set_cmd_delay(const char *cmd, int delay, int odds)
998 SINK_COMMAND *cmdp;
1000 for (cmdp = command_table; cmdp->name != 0; cmdp++)
1001 if (strcasecmp(cmd, cmdp->name) == 0)
1002 break;
1003 if (cmdp->name == 0)
1004 msg_fatal("unknown command: %s", cmd);
1006 if (delay <= 0)
1007 msg_fatal("non-positive '%s' delay", cmd);
1008 if (odds < 0 || odds > 99)
1009 msg_fatal("delay odds for '%s' out of range", cmd);
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)
1019 char *cp;
1020 char *saved_arg;
1021 char *cmd;
1022 char *delay;
1023 char *odds;
1025 saved_arg = cp = mystrdup(arg);
1026 cmd = mystrtok(&cp, ":");
1027 delay = mystrtok(&cp, ":");
1028 if (cmd == 0 || delay == 0)
1029 msg_fatal("invalid command delay argument: %s", arg);
1030 odds = mystrtok(&cp, "");
1031 set_cmd_delay(cmd, atoi(delay), odds ? atoi(odds) : 0);
1032 myfree(saved_arg);
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)
1044 return (-1);
1045 if (cmdp->flags & FLAG_CLOSE) {
1046 smtp_printf(state->stream, "421 4.0.0 Server closing connection");
1047 return (-1);
1049 if (cmdp->flags & FLAG_HARD_ERR) {
1050 cmdp->hard_response(state);
1051 return (0);
1053 if (cmdp->flags & FLAG_SOFT_ERR) {
1054 cmdp->soft_response(state);
1055 return (0);
1057 if (cmdp->delay > 0) {
1058 int delay = cmdp->delay;
1060 if (cmdp->delay_odds > 0)
1061 for (delay = 0;
1062 ((int) (100.0 * rand() / (RAND_MAX + 1.0))) < cmdp->delay_odds;
1063 delay += cmdp->delay)
1064 /* NOP */ ;
1065 /* Suspend input event handling while delaying the command response. */
1066 event_disable_readwrite(vstream_fileno(state->stream));
1067 event_cancel_timer(read_timeout, (char *) state);
1068 event_request_timer(delay_event, (char *) state, delay);
1069 state->delayed_response = cmdp->response;
1070 state->delayed_args = mystrdup(args);
1071 } else {
1072 cmdp->response(state, args);
1073 if (cmdp->response == quit_response)
1074 return (-1);
1076 return (0);
1079 /* command_read - talk the SMTP protocol, server side */
1081 static int command_read(SINK_STATE *state)
1083 char *command;
1084 SINK_COMMAND *cmdp;
1085 int ch;
1086 struct cmd_trans {
1087 int state;
1088 int want;
1089 int next_state;
1091 static struct cmd_trans cmd_trans[] = {
1092 ST_ANY, '\r', ST_CR,
1093 ST_CR, '\n', ST_CR_LF,
1094 0, 0, 0,
1096 struct cmd_trans *cp;
1097 char *ptr;
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 */
1108 for (;;) {
1109 if ((ch = NEXT_CHAR(state)) == VSTREAM_EOF)
1110 return (-1);
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");
1117 return (-1);
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
1124 * first state.
1126 for (cp = cmd_trans; cp->state != state->data_state; cp++)
1127 if (cp->want == 0)
1128 msg_panic("command_read: unknown state: %d", state->data_state);
1129 if (ch == cp->want)
1130 state->data_state = cp->next_state;
1131 else if (ch == cmd_trans[0].want)
1132 state->data_state = cmd_trans[0].next_state;
1133 else
1134 state->data_state = ST_ANY;
1135 if (state->data_state == ST_CR_LF)
1136 break;
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
1146 * 20020604.
1148 if (PUSH_BACK_PEEK(state) == 0 && vstream_peek(state->stream) <= 0
1149 && readable(vstream_fileno(state->stream)) <= 0)
1150 return (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);
1167 if (msg_verbose)
1168 msg_info("%s", ptr);
1169 if ((command = mystrtok(&ptr, " \t")) == 0) {
1170 smtp_printf(state->stream, "500 5.5.2 Error: unknown command");
1171 smtp_flush(state->stream);
1172 return (0);
1174 for (cmdp = command_table; cmdp->name != 0; cmdp++)
1175 if (strcasecmp(command, cmdp->name) == 0)
1176 break;
1177 if (cmdp->name == 0 || (cmdp->flags & FLAG_ENABLE) == 0) {
1178 smtp_printf(state->stream, "500 5.5.1 Error: unknown command");
1179 smtp_flush(state->stream);
1180 return (0);
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
1194 * trouble.
1196 msg_warn("read timeout");
1197 disconnect(state);
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.
1216 do {
1217 switch (vstream_setjmp(state->stream)) {
1219 default:
1220 msg_panic("unknown read/write error");
1221 /* NOTREACHED */
1223 case SMTP_ERR_TIME:
1224 msg_warn("write timeout");
1225 disconnect(state);
1226 return;
1228 case SMTP_ERR_EOF:
1229 msg_warn("lost connection");
1230 disconnect(state);
1231 return;
1233 case 0:
1234 if (state->read_fn(state) < 0) {
1235 if (msg_verbose)
1236 msg_info("disconnect");
1237 disconnect(state);
1238 return;
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);
1258 if (count) {
1259 sess_count++;
1260 do_stats();
1262 vstream_fclose(state->stream);
1263 vstring_free(state->buffer);
1264 /* Clean up file capture attributes. */
1265 if (state->helo_args)
1266 myfree(state->helo_args);
1267 /* Delete incomplete mail transaction. */
1268 mail_cmd_reset(state);
1269 if (state->delayed_args)
1270 myfree(state->delayed_args);
1271 myfree((char *) state);
1272 if (max_quit_count > 0 && quit_count >= max_quit_count)
1273 exit(0);
1274 if (client_count-- == max_client_count)
1275 event_enable_read(sock, connect_event, (char *) 0);
1278 /* connect_event - handle connection events */
1280 static void connect_event(int unused_event, char *unused_context)
1282 struct sockaddr sa;
1283 SOCKADDR_SIZE len = sizeof(sa);
1284 SINK_STATE *state;
1285 int fd;
1287 if ((fd = sane_accept(sock, &sa, &len)) >= 0) {
1288 /* Safety: limit the number of open sockets and capture files. */
1289 if (++client_count == max_client_count)
1290 event_disable_readwrite(sock);
1291 state = (SINK_STATE *) mymalloc(sizeof(*state));
1292 if (strchr((char *) proto_info->sa_family_list, sa.sa_family))
1293 SOCKADDR_TO_HOSTADDR(&sa, len, &state->client_addr,
1294 (MAI_SERVPORT_STR *) 0, sa.sa_family);
1295 else
1296 strncpy(state->client_addr.buf, "local", sizeof("local"));
1297 if (msg_verbose)
1298 msg_info("connect (%s %s)",
1299 #ifdef AF_LOCAL
1300 sa.sa_family == AF_LOCAL ? "AF_LOCAL" :
1301 #else
1302 sa.sa_family == AF_UNIX ? "AF_UNIX" :
1303 #endif
1304 sa.sa_family == AF_INET ? "AF_INET" :
1305 #ifdef AF_INET6
1306 sa.sa_family == AF_INET6 ? "AF_INET6" :
1307 #endif
1308 "unknown protocol family",
1309 state->client_addr.buf);
1310 non_blocking(fd, NON_BLOCKING);
1311 state->stream = vstream_fdopen(fd, O_RDWR);
1312 state->buffer = vstring_alloc(1024);
1313 state->read_fn = command_read;
1314 state->data_state = ST_ANY;
1315 PUSH_BACK_SET(state, "");
1316 smtp_timeout_setup(state->stream, var_tmout);
1317 state->in_mail = 0;
1318 state->rcpts = 0;
1319 state->delayed_response = 0;
1320 state->delayed_args = 0;
1321 /* Initialize file capture attributes. */
1322 #ifdef AF_INET6
1323 if (sa.sa_family == AF_INET6)
1324 state->addr_prefix = "ipv6:";
1325 else
1326 #endif
1327 state->addr_prefix = "";
1329 state->helo_args = 0;
1330 state->client_proto = enable_lmtp ? "LMTP" : "SMTP";
1331 state->start_time = 0;
1332 state->id = 0;
1333 state->dump_file = 0;
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)) {
1343 default:
1344 msg_panic("unknown read/write error");
1345 /* NOTREACHED */
1347 case SMTP_ERR_TIME:
1348 msg_warn("write timeout");
1349 disconnect(state);
1350 return;
1352 case SMTP_ERR_EOF:
1353 msg_warn("lost connection");
1354 disconnect(state);
1355 return;
1357 case 0:
1358 if (command_resp(state, command_table, "connect", "") < 0)
1359 disconnect(state);
1360 else if (command_table->delay == 0) {
1361 event_enable_read(fd, read_event, (char *) state);
1362 event_request_timer(read_timeout, (char *) state, var_tmout);
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)
1379 int backlog;
1380 int ch;
1381 int delay;
1382 const char *protocols = INET_PROTO_NAME_ALL;
1383 const char *root_dir = 0;
1384 const char *user_privs = 0;
1387 * Fingerprint executables and core dumps.
1389 MAIL_VERSION_STAMP_ALLOCATE;
1392 * Fix 20051207.
1394 signal(SIGPIPE, SIG_IGN);
1397 * Initialize diagnostics.
1399 msg_vstream_init(argv[0], VSTREAM_ERR);
1402 * Parse JCL.
1404 while ((ch = GETOPT(argc, argv, "468aA:cCd:D:eEf:Fh:Ln:m:M:pPq:Q:r:R:s:S:t:T:u:vw:W:")) > 0) {
1405 switch (ch) {
1406 case '4':
1407 protocols = INET_PROTO_NAME_IPV4;
1408 break;
1409 case '6':
1410 protocols = INET_PROTO_NAME_IPV6;
1411 break;
1412 case '8':
1413 disable_8bitmime = 1;
1414 break;
1415 case 'a':
1416 disable_saslauth = 1;
1417 break;
1418 case 'A':
1419 if (!alldig(optarg) || (abort_delay = atoi(optarg)) < 0)
1420 usage(argv[0]);
1421 break;
1422 case 'c':
1423 count++;
1424 break;
1425 case 'C':
1426 disable_xclient = 1;
1427 reset_cmd_flags("xclient", FLAG_ENABLE);
1428 break;
1429 case 'd':
1430 single_template = optarg;
1431 break;
1432 case 'D':
1433 shared_template = optarg;
1434 break;
1435 case 'e':
1436 disable_esmtp = 1;
1437 break;
1438 case 'E':
1439 disable_enh_status = 1;
1440 break;
1441 case 'f':
1442 set_cmds_flags(optarg, FLAG_HARD_ERR);
1443 disable_pipelining = 1;
1444 break;
1445 case 'F':
1446 disable_xforward = 1;
1447 reset_cmd_flags("xforward", FLAG_ENABLE);
1448 break;
1449 case 'h':
1450 var_myhostname = optarg;
1451 break;
1452 case 'L':
1453 enable_lmtp = 1;
1454 break;
1455 case 'm':
1456 if ((max_client_count = atoi(optarg)) <= 0)
1457 msg_fatal("bad concurrency limit: %s", optarg);
1458 break;
1459 case 'M':
1460 if ((max_msg_quit_count = atoi(optarg)) <= 0)
1461 msg_fatal("bad message quit count: %s", optarg);
1462 break;
1463 case 'n':
1464 if ((max_quit_count = atoi(optarg)) <= 0)
1465 msg_fatal("bad quit count: %s", optarg);
1466 break;
1467 case 'p':
1468 disable_pipelining = 1;
1469 break;
1470 case 'P':
1471 pretend_pix = 1;
1472 disable_esmtp = 1;
1473 break;
1474 case 'q':
1475 set_cmds_flags(optarg, FLAG_DISCONNECT);
1476 break;
1477 case 'Q':
1478 set_cmds_flags(optarg, FLAG_CLOSE);
1479 break;
1480 case 'r':
1481 set_cmds_flags(optarg, FLAG_SOFT_ERR);
1482 disable_pipelining = 1;
1483 break;
1484 case 'R':
1485 root_dir = optarg;
1486 break;
1487 case 's':
1488 openlog(basename(argv[0]), LOG_PID, LOG_MAIL);
1489 set_cmds_flags(optarg, FLAG_SYSLOG);
1490 break;
1491 case 'S':
1492 start_string = vstring_alloc(10);
1493 unescape(start_string, optarg);
1494 break;
1495 case 't':
1496 if ((var_tmout = atoi(optarg)) <= 0)
1497 msg_fatal("bad timeout: %s", optarg);
1498 break;
1499 case 'T':
1500 if ((inet_windowsize = atoi(optarg)) <= 0)
1501 msg_fatal("bad TCP window size: %s", optarg);
1502 break;
1503 case 'u':
1504 user_privs = optarg;
1505 break;
1506 case 'v':
1507 msg_verbose++;
1508 break;
1509 case 'w':
1510 if ((delay = atoi(optarg)) <= 0)
1511 usage(argv[0]);
1512 set_cmd_delay("data", delay, 0);
1513 break;
1514 case 'W':
1515 set_cmd_delay_arg(optarg);
1516 break;
1517 default:
1518 usage(argv[0]);
1521 if (argc - optind != 2)
1522 usage(argv[0]);
1523 if ((backlog = atoi(argv[optind + 1])) <= 0)
1524 usage(argv[0]);
1525 if (single_template && shared_template)
1526 msg_fatal("use only one of -d or -D, but not both");
1527 if (geteuid() == 0 && user_privs == 0)
1528 msg_fatal("-u option is required if running as root");
1531 * Initialize.
1533 if (var_myhostname == 0)
1534 var_myhostname = "smtp-sink";
1535 set_cmds_flags(enable_lmtp ? "lhlo" :
1536 disable_esmtp ? "helo" :
1537 "helo, ehlo", FLAG_ENABLE);
1538 proto_info = inet_proto_init("protocols", protocols);
1539 if (strncmp(argv[optind], "unix:", 5) == 0) {
1540 sock = unix_listen(argv[optind] + 5, backlog, BLOCKING);
1541 } else {
1542 if (strncmp(argv[optind], "inet:", 5) == 0)
1543 argv[optind] += 5;
1544 sock = inet_listen(argv[optind], backlog, BLOCKING);
1546 if (user_privs)
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);
1558 for (;;)
1559 event_loop(-1);