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