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