1/* $NetBSD$ */ 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 302typedef 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 340static int var_tmout = 100; 341static int var_max_line_length = 2048; 342static char *var_myhostname; 343static char *soft_error_resp = SOFT_ERROR_RESP; 344static char *hard_error_resp = HARD_ERROR_RESP; 345static int command_read(SINK_STATE *); 346static int data_read(SINK_STATE *); 347static void disconnect(SINK_STATE *); 348static void read_timeout(int, char *); 349static void read_event(int, char *); 350static int count; 351static int sess_count; 352static int quit_count; 353static int mesg_count; 354static int max_quit_count; 355static int max_msg_quit_count; 356static int disable_pipelining; 357static int disable_8bitmime; 358static int disable_esmtp; 359static int enable_lmtp; 360static int pretend_pix; 361static int disable_saslauth; 362static int disable_xclient; 363static int disable_xforward; 364static int disable_enh_status; 365static int max_client_count = DEF_MAX_CLIENT_COUNT; 366static int client_count; 367static int sock; 368static int abort_delay = -1; 369 370static char *single_template; /* individual template */ 371static char *shared_template; /* shared template */ 372static VSTRING *start_string; /* dump content prefix */ 373 374static INET_PROTO_INFO *proto_info; 375 376#define STR(x) vstring_str(x) 377 378/* do_stats - show counters */ 379 380static void do_stats(void) 381{ 382 vstream_printf("sess=%d quit=%d mesg=%d\r", 383 sess_count, quit_count, mesg_count); 384 vstream_fflush(VSTREAM_OUT); 385} 386 387/* hard_err_resp - generic hard error response */ 388 389static void hard_err_resp(SINK_STATE *state) 390{ 391 smtp_printf(state->stream, hard_error_resp); 392 smtp_flush(state->stream); 393} 394 395/* soft_err_resp - generic soft error response */ 396 397static void soft_err_resp(SINK_STATE *state) 398{ 399 smtp_printf(state->stream, soft_error_resp); 400 smtp_flush(state->stream); 401} 402 403/* exp_path_template - expand template pathname, static result */ 404 405static VSTRING *exp_path_template(const char *template, time_t start_time) 406{ 407 static VSTRING *path_buf = 0; 408 struct tm *lt; 409 410 if (path_buf == 0) 411 path_buf = vstring_alloc(100); 412 else 413 VSTRING_RESET(path_buf); 414 lt = localtime(&start_time); 415 while (strftime(STR(path_buf), vstring_avail(path_buf), template, lt) == 0) 416 VSTRING_SPACE(path_buf, vstring_avail(path_buf) + 100); 417 VSTRING_SKIP(path_buf); 418 return (path_buf); 419} 420 421/* make_parent_dir - create parent directory or bust */ 422 423static void make_parent_dir(const char *path, mode_t mode) 424{ 425 const char *parent; 426 427 parent = sane_dirname((VSTRING *) 0, path); 428 if (make_dirs(parent, mode) < 0) 429 msg_fatal("mkdir %s: %m", parent); 430} 431 432/* mail_file_open - open mail capture file */ 433 434static void mail_file_open(SINK_STATE *state) 435{ 436 const char *myname = "mail_file_open"; 437 VSTRING *path_buf; 438 ssize_t len; 439 int tries = 0; 440 441 /* 442 * Save the start time for later. 443 */ 444 time(&(state->start_time)); 445 446 /* 447 * Expand the per-message dumpfile pathname template. 448 */ 449 path_buf = exp_path_template(single_template, state->start_time); 450 451 /* 452 * Append a random hexadecimal string to the pathname and create a new 453 * file. Retry with a different path if the file already exists. Create 454 * intermediate directories on the fly when the template specifies 455 * multiple pathname segments. 456 */ 457#define ID_FORMAT "%08x" 458 459 for (len = VSTRING_LEN(path_buf); /* void */ ; vstring_truncate(path_buf, len)) { 460 if (++tries > 100) 461 msg_fatal("%s: something is looping", myname); 462 state->id = myrand(); 463 vstring_sprintf_append(path_buf, ID_FORMAT, state->id); 464 if ((state->dump_file = vstream_fopen(STR(path_buf), 465 O_RDWR | O_CREAT | O_EXCL, 466 0644)) != 0) { 467 break; 468 } else if (errno == EEXIST) { 469 continue; 470 } else if (errno == ENOENT) { 471 make_parent_dir(STR(path_buf), 0755); 472 continue; 473 } else { 474 msg_fatal("open %s: %m", STR(path_buf)); 475 } 476 } 477 478 /* 479 * Don't leave temporary files behind. 480 */ 481 if (shared_template != 0 && unlink(STR(path_buf)) < 0) 482 msg_fatal("unlink %s: %m", STR(path_buf)); 483 484 /* 485 * Do initial header records. 486 */ 487 if (start_string) 488 vstream_fprintf(state->dump_file, "%s", STR(start_string)); 489 vstream_fprintf(state->dump_file, "X-Client-Addr: %s%s\n", 490 state->addr_prefix, state->client_addr.buf); 491 vstream_fprintf(state->dump_file, "X-Client-Proto: %s\n", state->client_proto); 492 if (state->helo_args) 493 vstream_fprintf(state->dump_file, "X-Helo-Args: %s\n", state->helo_args); 494 /* Note: there may be more than one recipient. */ 495} 496 497/* mail_file_finish_header - do final smtp-sink generated header records */ 498 499static void mail_file_finish_header(SINK_STATE *state) 500{ 501 if (state->helo_args) 502 vstream_fprintf(state->dump_file, "Received: from %s ([%s%s])\n", 503 state->helo_args, state->addr_prefix, 504 state->client_addr.buf); 505 else 506 vstream_fprintf(state->dump_file, "Received: from [%s%s] ([%s%s])\n", 507 state->addr_prefix, state->client_addr.buf, 508 state->addr_prefix, state->client_addr.buf); 509 vstream_fprintf(state->dump_file, "\tby %s (smtp-sink)" 510 " with %s id " ID_FORMAT ";\n", 511 var_myhostname, state->client_proto, state->id); 512 vstream_fprintf(state->dump_file, "\t%s\n", mail_date(state->start_time)); 513} 514 515/* mail_file_cleanup - common cleanup for capture file */ 516 517static void mail_file_cleanup(SINK_STATE *state) 518{ 519 (void) vstream_fclose(state->dump_file); 520 state->dump_file = 0; 521} 522 523/* mail_file_finish - handle message completion for capture file */ 524 525static void mail_file_finish(SINK_STATE *state) 526{ 527 528 /* 529 * Optionally append the captured message to a shared dumpfile. 530 */ 531 if (shared_template) { 532 const char *out_path; 533 VSTREAM *out_fp; 534 ssize_t count; 535 536 /* 537 * Expand the shared dumpfile pathname template. 538 */ 539 out_path = STR(exp_path_template(shared_template, state->start_time)); 540 541 /* 542 * Open the shared dump file. 543 */ 544#define OUT_OPEN_FLAGS (O_WRONLY | O_CREAT | O_APPEND) 545#define OUT_OPEN_MODE 0644 546 547 if ((out_fp = vstream_fopen(out_path, OUT_OPEN_FLAGS, OUT_OPEN_MODE)) 548 == 0 && errno == ENOENT) { 549 make_parent_dir(out_path, 0755); 550 out_fp = vstream_fopen(out_path, OUT_OPEN_FLAGS, OUT_OPEN_MODE); 551 } 552 if (out_fp == 0) 553 msg_fatal("open %s: %m", out_path); 554 555 /* 556 * Append message content from single-message dump file. 557 */ 558 if (vstream_fseek(state->dump_file, 0L, SEEK_SET) < 0) 559 msg_fatal("seek file %s: %m", VSTREAM_PATH(state->dump_file)); 560 VSTRING_RESET(state->buffer); 561 for (;;) { 562 count = vstream_fread(state->dump_file, STR(state->buffer), 563 vstring_avail(state->buffer)); 564 if (count <= 0) 565 break; 566 if (vstream_fwrite(out_fp, STR(state->buffer), count) != count) 567 msg_fatal("append file %s: %m", out_path); 568 } 569 if (vstream_ferror(state->dump_file)) 570 msg_fatal("read file %s: %m", VSTREAM_PATH(state->dump_file)); 571 if (vstream_fclose(out_fp)) 572 msg_fatal("append file %s: %m", out_path); 573 } 574 mail_file_cleanup(state); 575} 576 577/* mail_file_reset - abort mail to capture file */ 578 579static void mail_file_reset(SINK_STATE *state) 580{ 581 if (shared_template == 0 582 && unlink(VSTREAM_PATH(state->dump_file)) < 0 583 && errno != ENOENT) 584 msg_fatal("unlink %s: %m", VSTREAM_PATH(state->dump_file)); 585 mail_file_cleanup(state); 586} 587 588/* mail_cmd_reset - reset mail transaction information */ 589 590static void mail_cmd_reset(SINK_STATE *state) 591{ 592 state->in_mail = 0; 593 /* Not: state->rcpts = 0. This breaks the DOT reply with LMTP. */ 594 if (state->dump_file) 595 mail_file_reset(state); 596} 597 598/* ehlo_response - respond to EHLO command */ 599 600static void ehlo_response(SINK_STATE *state, const char *args) 601{ 602#define SKIP(cp, cond) for (/* void */; *cp && (cond); cp++) 603 604 /* EHLO aborts a mail transaction in progress. */ 605 mail_cmd_reset(state); 606 if (enable_lmtp == 0) 607 state->client_proto = "ESMTP"; 608 smtp_printf(state->stream, "250-%s", var_myhostname); 609 if (!disable_pipelining) 610 smtp_printf(state->stream, "250-PIPELINING"); 611 if (!disable_8bitmime) 612 smtp_printf(state->stream, "250-8BITMIME"); 613 if (!disable_saslauth) 614 smtp_printf(state->stream, "250-AUTH PLAIN LOGIN"); 615 if (!disable_xclient) 616 smtp_printf(state->stream, "250-XCLIENT NAME HELO"); 617 if (!disable_xforward) 618 smtp_printf(state->stream, "250-XFORWARD NAME ADDR PROTO HELO"); 619 if (!disable_enh_status) 620 smtp_printf(state->stream, "250-ENHANCEDSTATUSCODES"); 621 smtp_printf(state->stream, "250 "); 622 smtp_flush(state->stream); 623 if (single_template) { 624 if (state->helo_args) 625 myfree(state->helo_args); 626 SKIP(args, ISSPACE(*args)); 627 state->helo_args = mystrdup(args); 628 } 629} 630 631/* helo_response - respond to HELO command */ 632 633static void helo_response(SINK_STATE *state, const char *args) 634{ 635 /* HELO aborts a mail transaction in progress. */ 636 mail_cmd_reset(state); 637 state->client_proto = "SMTP"; 638 smtp_printf(state->stream, "250 %s", var_myhostname); 639 smtp_flush(state->stream); 640 if (single_template) { 641 if (state->helo_args) 642 myfree(state->helo_args); 643 SKIP(args, ISSPACE(*args)); 644 state->helo_args = mystrdup(args); 645 } 646} 647 648/* ok_response - send 250 OK */ 649 650static void ok_response(SINK_STATE *state, const char *unused_args) 651{ 652 smtp_printf(state->stream, "250 2.0.0 Ok"); 653 smtp_flush(state->stream); 654} 655 656/* rset_response - reset, send 250 OK */ 657 658static void rset_response(SINK_STATE *state, const char *unused_args) 659{ 660 mail_cmd_reset(state); 661 smtp_printf(state->stream, "250 2.1.0 Ok"); 662 smtp_flush(state->stream); 663} 664 665/* mail_response - reset recipient count, send 250 OK */ 666 667static void mail_response(SINK_STATE *state, const char *args) 668{ 669 if (state->in_mail) { 670 smtp_printf(state->stream, "503 5.5.1 Error: nested MAIL command"); 671 smtp_flush(state->stream); 672 return; 673 } 674 state->in_mail++; 675 state->rcpts = 0; 676 smtp_printf(state->stream, "250 2.1.0 Ok"); 677 smtp_flush(state->stream); 678 if (single_template) { 679 mail_file_open(state); 680 SKIP(args, *args != ':'); 681 SKIP(args, *args == ':'); 682 SKIP(args, ISSPACE(*args)); 683 vstream_fprintf(state->dump_file, "X-Mail-Args: %s\n", args); 684 } 685} 686 687/* rcpt_response - bump recipient count, send 250 OK */ 688 689static void rcpt_response(SINK_STATE *state, const char *args) 690{ 691 if (state->in_mail == 0) { 692 smtp_printf(state->stream, "503 5.5.1 Error: need MAIL command"); 693 smtp_flush(state->stream); 694 return; 695 } 696 state->rcpts++; 697 smtp_printf(state->stream, "250 2.1.5 Ok"); 698 smtp_flush(state->stream); 699 /* Note: there may be more than one recipient per mail transaction. */ 700 if (state->dump_file) { 701 SKIP(args, *args != ':'); 702 SKIP(args, *args == ':'); 703 SKIP(args, ISSPACE(*args)); 704 vstream_fprintf(state->dump_file, "X-Rcpt-Args: %s\n", args); 705 } 706} 707 708/* abort_event - delayed abort after DATA command */ 709 710static void abort_event(int unused_event, char *context) 711{ 712 SINK_STATE *state = (SINK_STATE *) context; 713 714 smtp_printf(state->stream, "550 This violates SMTP"); 715 smtp_flush(state->stream); 716 disconnect(state); 717} 718 719/* data_response - respond to DATA command */ 720 721static void data_response(SINK_STATE *state, const char *unused_args) 722{ 723 if (state->in_mail == 0 || state->rcpts == 0) { 724 smtp_printf(state->stream, "503 5.5.1 Error: need RCPT command"); 725 smtp_flush(state->stream); 726 return; 727 } 728 /* Not: ST_ANY. */ 729 state->data_state = ST_CR_LF; 730 smtp_printf(state->stream, "354 End data with <CR><LF>.<CR><LF>"); 731 smtp_flush(state->stream); 732 if (abort_delay < 0) { 733 state->read_fn = data_read; 734 } else { 735 /* Stop reading, send premature 550, and disconnect. */ 736 event_disable_readwrite(vstream_fileno(state->stream)); 737 event_cancel_timer(read_event, (char *) state); 738 event_request_timer(abort_event, (char *) state, abort_delay); 739 } 740 if (state->dump_file) 741 mail_file_finish_header(state); 742} 743 744/* dot_resp_hard - hard error response to . command */ 745 746static void dot_resp_hard(SINK_STATE *state) 747{ 748 if (enable_lmtp) { 749 while (state->rcpts-- > 0) /* XXX this could block */ 750 smtp_printf(state->stream, hard_error_resp); 751 } else { 752 smtp_printf(state->stream, hard_error_resp); 753 } 754 smtp_flush(state->stream); 755} 756 757/* dot_resp_soft - soft error response to . command */ 758 759static void dot_resp_soft(SINK_STATE *state) 760{ 761 if (enable_lmtp) { 762 while (state->rcpts-- > 0) /* XXX this could block */ 763 smtp_printf(state->stream, soft_error_resp); 764 } else { 765 smtp_printf(state->stream, soft_error_resp); 766 } 767 smtp_flush(state->stream); 768} 769 770/* dot_response - response to . command */ 771 772static void dot_response(SINK_STATE *state, const char *unused_args) 773{ 774 if (enable_lmtp) { 775 while (state->rcpts-- > 0) /* XXX this could block */ 776 smtp_printf(state->stream, "250 2.2.0 Ok"); 777 } else { 778 smtp_printf(state->stream, "250 2.0.0 Ok"); 779 } 780 smtp_flush(state->stream); 781} 782 783/* quit_response - respond to QUIT command */ 784 785static void quit_response(SINK_STATE *state, const char *unused_args) 786{ 787 smtp_printf(state->stream, "221 Bye"); 788 smtp_flush(state->stream); 789 if (count) 790 quit_count++; 791} 792 793/* conn_response - respond to connect command */ 794 795static void conn_response(SINK_STATE *state, const char *unused_args) 796{ 797 if (pretend_pix) 798 smtp_printf(state->stream, "220 ********"); 799 else if (disable_esmtp) 800 smtp_printf(state->stream, "220 %s", var_myhostname); 801 else 802 smtp_printf(state->stream, "220 %s ESMTP", var_myhostname); 803 smtp_flush(state->stream); 804} 805 806/* delay_event - delayed command response */ 807 808static void delay_event(int unused_event, char *context) 809{ 810 SINK_STATE *state = (SINK_STATE *) context; 811 812 switch (vstream_setjmp(state->stream)) { 813 814 default: 815 msg_panic("unknown read/write error"); 816 /* NOTREACHED */ 817 818 case SMTP_ERR_TIME: 819 msg_warn("write timeout"); 820 disconnect(state); 821 return; 822 823 case SMTP_ERR_EOF: 824 msg_warn("lost connection"); 825 disconnect(state); 826 return; 827 828 case 0: 829 state->delayed_response(state, state->delayed_args); 830 myfree(state->delayed_args); 831 state->delayed_args = 0; 832 break; 833 } 834 835 if (state->delayed_response == quit_response) { 836 disconnect(state); 837 return; 838 } 839 state->delayed_response = 0; 840 841 /* Resume input event handling after the delayed response. */ 842 event_enable_read(vstream_fileno(state->stream), read_event, (char *) state); 843 event_request_timer(read_timeout, (char *) state, var_tmout); 844} 845 846/* data_read - read data from socket */ 847 848static int data_read(SINK_STATE *state) 849{ 850 int ch; 851 struct data_trans { 852 int state; 853 int want; 854 int next_state; 855 }; 856 static struct data_trans data_trans[] = { 857 ST_ANY, '\r', ST_CR, 858 ST_CR, '\n', ST_CR_LF, 859 ST_CR_LF, '.', ST_CR_LF_DOT, 860 ST_CR_LF_DOT, '\r', ST_CR_LF_DOT_CR, 861 ST_CR_LF_DOT_CR, '\n', ST_CR_LF_DOT_CR_LF, 862 }; 863 struct data_trans *dp; 864 865 /* 866 * A read may result in EOF, but is never supposed to time out - a time 867 * out means that we were trying to read when no data was available. 868 */ 869 for (;;) { 870 if ((ch = VSTREAM_GETC(state->stream)) == VSTREAM_EOF) 871 return (-1); 872 for (dp = data_trans; dp->state != state->data_state; dp++) 873 /* void */ ; 874 875 /* 876 * Try to match the current character desired by the state machine. 877 * If that fails, try to restart the machine with a match for its 878 * first state. This covers the case of a CR/LF/CR/LF sequence 879 * (empty line) right before the end of the message data. 880 */ 881 if (ch == dp->want) 882 state->data_state = dp->next_state; 883 else if (ch == data_trans[0].want) 884 state->data_state = data_trans[0].next_state; 885 else 886 state->data_state = ST_ANY; 887 if (state->dump_file) { 888 if (ch != '\r' && state->data_state != ST_CR_LF_DOT) 889 VSTREAM_PUTC(ch, state->dump_file); 890 if (vstream_ferror(state->dump_file)) 891 msg_fatal("append file %s: %m", VSTREAM_PATH(state->dump_file)); 892 } 893 if (state->data_state == ST_CR_LF_DOT_CR_LF) { 894 PUSH_BACK_SET(state, ".\r\n"); 895 state->read_fn = command_read; 896 state->data_state = ST_ANY; 897 if (state->dump_file) 898 mail_file_finish(state); 899 mail_cmd_reset(state); 900 if (count || max_msg_quit_count > 0) { 901 mesg_count++; 902 if (count) 903 do_stats(); 904 if (max_msg_quit_count > 0 && mesg_count >= max_msg_quit_count) 905 exit(0); 906 } 907 break; 908 } 909 910 /* 911 * We must avoid blocking I/O, so get out of here as soon as both the 912 * VSTREAM and kernel read buffers dry up. 913 */ 914 if (vstream_peek(state->stream) <= 0 915 && readable(vstream_fileno(state->stream)) <= 0) 916 return (0); 917 } 918 return (0); 919} 920 921 /* 922 * The table of all SMTP commands that we can handle. 923 */ 924typedef struct SINK_COMMAND { 925 const char *name; 926 void (*response) (SINK_STATE *, const char *); 927 void (*hard_response) (SINK_STATE *); 928 void (*soft_response) (SINK_STATE *); 929 int flags; 930 int delay; 931 int delay_odds; 932} SINK_COMMAND; 933 934#define FLAG_ENABLE (1<<0) /* command is enabled */ 935#define FLAG_SYSLOG (1<<1) /* log the command */ 936#define FLAG_HARD_ERR (1<<2) /* report hard error */ 937#define FLAG_SOFT_ERR (1<<3) /* report soft error */ 938#define FLAG_DISCONNECT (1<<4) /* disconnect */ 939#define FLAG_CLOSE (1<<5) /* say goodbye and disconnect */ 940 941static SINK_COMMAND command_table[] = { 942 "connect", conn_response, hard_err_resp, soft_err_resp, 0, 0, 0, 943 "helo", helo_response, hard_err_resp, soft_err_resp, 0, 0, 0, 944 "ehlo", ehlo_response, hard_err_resp, soft_err_resp, 0, 0, 0, 945 "lhlo", ehlo_response, hard_err_resp, soft_err_resp, 0, 0, 0, 946 "xclient", ok_response, hard_err_resp, soft_err_resp, FLAG_ENABLE, 0, 0, 947 "xforward", ok_response, hard_err_resp, soft_err_resp, FLAG_ENABLE, 0, 0, 948 "auth", ok_response, hard_err_resp, soft_err_resp, FLAG_ENABLE, 0, 0, 949 "mail", mail_response, hard_err_resp, soft_err_resp, FLAG_ENABLE, 0, 0, 950 "rcpt", rcpt_response, hard_err_resp, soft_err_resp, FLAG_ENABLE, 0, 0, 951 "data", data_response, hard_err_resp, soft_err_resp, FLAG_ENABLE, 0, 0, 952 ".", dot_response, dot_resp_hard, dot_resp_soft, FLAG_ENABLE, 0, 0, 953 "rset", rset_response, hard_err_resp, soft_err_resp, FLAG_ENABLE, 0, 0, 954 "noop", ok_response, hard_err_resp, soft_err_resp, FLAG_ENABLE, 0, 0, 955 "vrfy", ok_response, hard_err_resp, soft_err_resp, FLAG_ENABLE, 0, 0, 956 "quit", quit_response, hard_err_resp, soft_err_resp, FLAG_ENABLE, 0, 0, 957 0, 958}; 959 960/* reset_cmd_flags - reset per-command command flags */ 961 962static void reset_cmd_flags(const char *cmd, int flags) 963{ 964 SINK_COMMAND *cmdp; 965 966 for (cmdp = command_table; cmdp->name != 0; cmdp++) 967 if (strcasecmp(cmd, cmdp->name) == 0) 968 break; 969 if (cmdp->name == 0) 970 msg_fatal("unknown command: %s", cmd); 971 cmdp->flags &= ~flags; 972} 973 974/* set_cmd_flags - set per-command command flags */ 975 976static void set_cmd_flags(const char *cmd, int flags) 977{ 978 SINK_COMMAND *cmdp; 979 980 for (cmdp = command_table; cmdp->name != 0; cmdp++) 981 if (strcasecmp(cmd, cmdp->name) == 0) 982 break; 983 if (cmdp->name == 0) 984 msg_fatal("unknown command: %s", cmd); 985 cmdp->flags |= flags; 986} 987 988/* set_cmds_flags - set per-command flags for multiple commands */ 989 990static void set_cmds_flags(const char *cmds, int flags) 991{ 992 char *saved_cmds; 993 char *cp; 994 char *cmd; 995 996 saved_cmds = cp = mystrdup(cmds); 997 while ((cmd = mystrtok(&cp, " \t\r\n,")) != 0) 998 set_cmd_flags(cmd, flags); 999 myfree(saved_cmds); 1000} 1001 1002/* set_cmd_delay - set per-command delay */ 1003 1004static void set_cmd_delay(const char *cmd, int delay, int odds) 1005{ 1006 SINK_COMMAND *cmdp; 1007 1008 for (cmdp = command_table; cmdp->name != 0; cmdp++) 1009 if (strcasecmp(cmd, cmdp->name) == 0) 1010 break; 1011 if (cmdp->name == 0) 1012 msg_fatal("unknown command: %s", cmd); 1013 1014 if (delay <= 0) 1015 msg_fatal("non-positive '%s' delay", cmd); 1016 if (odds < 0 || odds > 99) 1017 msg_fatal("delay odds for '%s' out of range", cmd); 1018 1019 cmdp->delay = delay; 1020 cmdp->delay_odds = odds; 1021} 1022 1023/* set_cmd_delay_arg - set per-command delay from option argument */ 1024 1025static void set_cmd_delay_arg(char *arg) 1026{ 1027 char *cp; 1028 char *saved_arg; 1029 char *cmd; 1030 char *delay; 1031 char *odds; 1032 1033 saved_arg = cp = mystrdup(arg); 1034 cmd = mystrtok(&cp, ":"); 1035 delay = mystrtok(&cp, ":"); 1036 if (cmd == 0 || delay == 0) 1037 msg_fatal("invalid command delay argument: %s", arg); 1038 odds = mystrtok(&cp, ""); 1039 set_cmd_delay(cmd, atoi(delay), odds ? atoi(odds) : 0); 1040 myfree(saved_arg); 1041} 1042 1043/* command_resp - respond to command */ 1044 1045static int command_resp(SINK_STATE *state, SINK_COMMAND *cmdp, 1046 const char *command, const char *args) 1047{ 1048 /* We use raw syslog. Sanitize data content and length. */ 1049 if (cmdp->flags & FLAG_SYSLOG) 1050 syslog(LOG_INFO, "%s %.100s", command, args); 1051 if (cmdp->flags & FLAG_DISCONNECT) 1052 return (-1); 1053 if (cmdp->flags & FLAG_CLOSE) { 1054 smtp_printf(state->stream, "421 4.0.0 Server closing connection"); 1055 return (-1); 1056 } 1057 if (cmdp->flags & FLAG_HARD_ERR) { 1058 cmdp->hard_response(state); 1059 return (0); 1060 } 1061 if (cmdp->flags & FLAG_SOFT_ERR) { 1062 cmdp->soft_response(state); 1063 return (0); 1064 } 1065 if (cmdp->delay > 0) { 1066 int delay = cmdp->delay; 1067 1068 if (cmdp->delay_odds > 0) 1069 for (delay = 0; 1070 ((int) (100.0 * rand() / (RAND_MAX + 1.0))) < cmdp->delay_odds; 1071 delay += cmdp->delay) 1072 /* NOP */ ; 1073 /* Suspend input event handling while delaying the command response. */ 1074 event_disable_readwrite(vstream_fileno(state->stream)); 1075 event_cancel_timer(read_timeout, (char *) state); 1076 event_request_timer(delay_event, (char *) state, delay); 1077 state->delayed_response = cmdp->response; 1078 state->delayed_args = mystrdup(args); 1079 } else { 1080 cmdp->response(state, args); 1081 if (cmdp->response == quit_response) 1082 return (-1); 1083 } 1084 return (0); 1085} 1086 1087/* command_read - talk the SMTP protocol, server side */ 1088 1089static int command_read(SINK_STATE *state) 1090{ 1091 char *command; 1092 SINK_COMMAND *cmdp; 1093 int ch; 1094 struct cmd_trans { 1095 int state; 1096 int want; 1097 int next_state; 1098 }; 1099 static struct cmd_trans cmd_trans[] = { 1100 ST_ANY, '\r', ST_CR, 1101 ST_CR, '\n', ST_CR_LF, 1102 0, 0, 0, 1103 }; 1104 struct cmd_trans *cp; 1105 char *ptr; 1106 1107 /* 1108 * A read may result in EOF, but is never supposed to time out - a time 1109 * out means that we were trying to read when no data was available. 1110 */ 1111#define NEXT_CHAR(state) \ 1112 (PUSH_BACK_PEEK(state) ? PUSH_BACK_GET(state) : VSTREAM_GETC(state->stream)) 1113 1114 if (state->data_state == ST_CR_LF) 1115 state->data_state = ST_ANY; /* XXX */ 1116 for (;;) { 1117 if ((ch = NEXT_CHAR(state)) == VSTREAM_EOF) 1118 return (-1); 1119 1120 /* 1121 * Sanity check. We don't want to store infinitely long commands. 1122 */ 1123 if (VSTRING_LEN(state->buffer) >= var_max_line_length) { 1124 msg_warn("command line too long"); 1125 return (-1); 1126 } 1127 VSTRING_ADDCH(state->buffer, ch); 1128 1129 /* 1130 * Try to match the current character desired by the state machine. 1131 * If that fails, try to restart the machine with a match for its 1132 * first state. 1133 */ 1134 for (cp = cmd_trans; cp->state != state->data_state; cp++) 1135 if (cp->want == 0) 1136 msg_panic("command_read: unknown state: %d", state->data_state); 1137 if (ch == cp->want) 1138 state->data_state = cp->next_state; 1139 else if (ch == cmd_trans[0].want) 1140 state->data_state = cmd_trans[0].next_state; 1141 else 1142 state->data_state = ST_ANY; 1143 if (state->data_state == ST_CR_LF) 1144 break; 1145 1146 /* 1147 * We must avoid blocking I/O, so get out of here as soon as both the 1148 * VSTREAM and kernel read buffers dry up. 1149 * 1150 * XXX Solaris non-blocking read() may fail on a socket when ioctl 1151 * FIONREAD reports there is unread data. Diagnosis by Max Pashkov. 1152 * As a workaround we use readable() (which uses poll or select()) 1153 * instead of peek_fd() (which uses ioctl FIONREAD). Workaround added 1154 * 20020604. 1155 */ 1156 if (PUSH_BACK_PEEK(state) == 0 && vstream_peek(state->stream) <= 0 1157 && readable(vstream_fileno(state->stream)) <= 0) 1158 return (0); 1159 } 1160 1161 /* 1162 * Properly terminate the result, and reset the buffer write pointer for 1163 * reading the next command. This is ugly, but not as ugly as trying to 1164 * deal with all the early returns below. 1165 */ 1166 vstring_truncate(state->buffer, VSTRING_LEN(state->buffer) - 2); 1167 VSTRING_TERMINATE(state->buffer); 1168 state->data_state = ST_CR_LF; 1169 VSTRING_RESET(state->buffer); 1170 1171 /* 1172 * Got a complete command line. Parse it. 1173 */ 1174 ptr = vstring_str(state->buffer); 1175 if (msg_verbose) 1176 msg_info("%s", ptr); 1177 if ((command = mystrtok(&ptr, " \t")) == 0) { 1178 smtp_printf(state->stream, "500 5.5.2 Error: unknown command"); 1179 smtp_flush(state->stream); 1180 return (0); 1181 } 1182 for (cmdp = command_table; cmdp->name != 0; cmdp++) 1183 if (strcasecmp(command, cmdp->name) == 0) 1184 break; 1185 if (cmdp->name == 0 || (cmdp->flags & FLAG_ENABLE) == 0) { 1186 smtp_printf(state->stream, "500 5.5.1 Error: unknown command"); 1187 smtp_flush(state->stream); 1188 return (0); 1189 } 1190 return (command_resp(state, cmdp, command, printable(ptr, '?'))); 1191} 1192 1193/* read_timeout - handle timer event */ 1194 1195static void read_timeout(int unused_event, char *context) 1196{ 1197 SINK_STATE *state = (SINK_STATE *) context; 1198 1199 /* 1200 * We don't send anything to the client, because we would have to set up 1201 * an smtp_stream exception handler first. And that is just too much 1202 * trouble. 1203 */ 1204 msg_warn("read timeout"); 1205 disconnect(state); 1206} 1207 1208/* read_event - handle command or data read events */ 1209 1210static void read_event(int unused_event, char *context) 1211{ 1212 SINK_STATE *state = (SINK_STATE *) context; 1213 1214 /* 1215 * The input reading routine not only reads input (with vstream calls) 1216 * but also produces output (with smtp_stream calls). Because the output 1217 * routines can raise timeout or EOF exceptions with vstream_longjmp(), 1218 * the input reading routine needs to set up corresponding exception 1219 * handlers with vstream_setjmp(). Guarding the input operations in the 1220 * same manner is not useful: we must read input in non-blocking mode, so 1221 * we never get called when the socket stays unreadable too long. And EOF 1222 * is already trivial to detect with the vstream calls. 1223 */ 1224 do { 1225 switch (vstream_setjmp(state->stream)) { 1226 1227 default: 1228 msg_panic("unknown read/write error"); 1229 /* NOTREACHED */ 1230 1231 case SMTP_ERR_TIME: 1232 msg_warn("write timeout"); 1233 disconnect(state); 1234 return; 1235 1236 case SMTP_ERR_EOF: 1237 msg_warn("lost connection"); 1238 disconnect(state); 1239 return; 1240 1241 case 0: 1242 if (state->read_fn(state) < 0) { 1243 if (msg_verbose) 1244 msg_info("disconnect"); 1245 disconnect(state); 1246 return; 1247 } 1248 } 1249 } while (PUSH_BACK_PEEK(state) != 0 || vstream_peek(state->stream) > 0); 1250 1251 /* 1252 * Reset the idle timer. Wait until the next input event, or until the 1253 * idle timer goes off. 1254 */ 1255 event_request_timer(read_timeout, (char *) state, var_tmout); 1256} 1257 1258static void connect_event(int, char *); 1259 1260/* disconnect - handle disconnection events */ 1261 1262static void disconnect(SINK_STATE *state) 1263{ 1264 event_disable_readwrite(vstream_fileno(state->stream)); 1265 event_cancel_timer(read_timeout, (char *) state); 1266 if (count) { 1267 sess_count++; 1268 do_stats(); 1269 } 1270 vstream_fclose(state->stream); 1271 vstring_free(state->buffer); 1272 /* Clean up file capture attributes. */ 1273 if (state->helo_args) 1274 myfree(state->helo_args); 1275 /* Delete incomplete mail transaction. */ 1276 mail_cmd_reset(state); 1277 if (state->delayed_args) 1278 myfree(state->delayed_args); 1279 myfree((char *) state); 1280 if (max_quit_count > 0 && quit_count >= max_quit_count) 1281 exit(0); 1282 if (client_count-- == max_client_count) 1283 event_enable_read(sock, connect_event, (char *) 0); 1284} 1285 1286/* connect_event - handle connection events */ 1287 1288static void connect_event(int unused_event, char *unused_context) 1289{ 1290 struct sockaddr sa; 1291 SOCKADDR_SIZE len = sizeof(sa); 1292 SINK_STATE *state; 1293 int fd; 1294 1295 if ((fd = sane_accept(sock, &sa, &len)) >= 0) { 1296 /* Safety: limit the number of open sockets and capture files. */ 1297 if (++client_count == max_client_count) 1298 event_disable_readwrite(sock); 1299 state = (SINK_STATE *) mymalloc(sizeof(*state)); 1300 if (strchr((char *) proto_info->sa_family_list, sa.sa_family)) 1301 SOCKADDR_TO_HOSTADDR(&sa, len, &state->client_addr, 1302 (MAI_SERVPORT_STR *) 0, sa.sa_family); 1303 else 1304 strncpy(state->client_addr.buf, "local", sizeof("local")); 1305 if (msg_verbose) 1306 msg_info("connect (%s %s)", 1307#ifdef AF_LOCAL 1308 sa.sa_family == AF_LOCAL ? "AF_LOCAL" : 1309#else 1310 sa.sa_family == AF_UNIX ? "AF_UNIX" : 1311#endif 1312 sa.sa_family == AF_INET ? "AF_INET" : 1313#ifdef AF_INET6 1314 sa.sa_family == AF_INET6 ? "AF_INET6" : 1315#endif 1316 "unknown protocol family", 1317 state->client_addr.buf); 1318 non_blocking(fd, NON_BLOCKING); 1319 state->stream = vstream_fdopen(fd, O_RDWR); 1320 state->buffer = vstring_alloc(1024); 1321 state->read_fn = command_read; 1322 state->data_state = ST_ANY; 1323 PUSH_BACK_SET(state, ""); 1324 smtp_timeout_setup(state->stream, var_tmout); 1325 state->in_mail = 0; 1326 state->rcpts = 0; 1327 state->delayed_response = 0; 1328 state->delayed_args = 0; 1329 /* Initialize file capture attributes. */ 1330#ifdef AF_INET6 1331 if (sa.sa_family == AF_INET6) 1332 state->addr_prefix = "ipv6:"; 1333 else 1334#endif 1335 state->addr_prefix = ""; 1336 1337 state->helo_args = 0; 1338 state->client_proto = enable_lmtp ? "LMTP" : "SMTP"; 1339 state->start_time = 0; 1340 state->id = 0; 1341 state->dump_file = 0; 1342 1343 /* 1344 * We use the smtp_stream module to produce output. That module 1345 * throws an exception via vstream_longjmp() in case of a timeout or 1346 * lost connection error. Therefore we must prepare to handle these 1347 * exceptions with vstream_setjmp(). 1348 */ 1349 switch (vstream_setjmp(state->stream)) { 1350 1351 default: 1352 msg_panic("unknown read/write error"); 1353 /* NOTREACHED */ 1354 1355 case SMTP_ERR_TIME: 1356 msg_warn("write timeout"); 1357 disconnect(state); 1358 return; 1359 1360 case SMTP_ERR_EOF: 1361 msg_warn("lost connection"); 1362 disconnect(state); 1363 return; 1364 1365 case 0: 1366 if (command_resp(state, command_table, "connect", "") < 0) 1367 disconnect(state); 1368 else if (command_table->delay == 0) { 1369 event_enable_read(fd, read_event, (char *) state); 1370 event_request_timer(read_timeout, (char *) state, var_tmout); 1371 } 1372 } 1373 } 1374} 1375 1376/* usage - explain */ 1377 1378static void usage(char *myname) 1379{ 1380 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); 1381} 1382 1383MAIL_VERSION_STAMP_DECLARE; 1384 1385int main(int argc, char **argv) 1386{ 1387 int backlog; 1388 int ch; 1389 int delay; 1390 const char *protocols = INET_PROTO_NAME_ALL; 1391 const char *root_dir = 0; 1392 const char *user_privs = 0; 1393 1394 /* 1395 * Fingerprint executables and core dumps. 1396 */ 1397 MAIL_VERSION_STAMP_ALLOCATE; 1398 1399 /* 1400 * Fix 20051207. 1401 */ 1402 signal(SIGPIPE, SIG_IGN); 1403 1404 /* 1405 * Initialize diagnostics. 1406 */ 1407 msg_vstream_init(argv[0], VSTREAM_ERR); 1408 1409 /* 1410 * Parse JCL. 1411 */ 1412 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) { 1413 switch (ch) { 1414 case '4': 1415 protocols = INET_PROTO_NAME_IPV4; 1416 break; 1417 case '6': 1418 protocols = INET_PROTO_NAME_IPV6; 1419 break; 1420 case '8': 1421 disable_8bitmime = 1; 1422 break; 1423 case 'a': 1424 disable_saslauth = 1; 1425 break; 1426 case 'A': 1427 if (!alldig(optarg) || (abort_delay = atoi(optarg)) < 0) 1428 usage(argv[0]); 1429 break; 1430 case 'b': 1431 if (optarg[0] != '4' || strspn(optarg, "0123456789") != 3) { 1432 msg_error("bad soft error reply: %s", optarg); 1433 usage(argv[0]); 1434 } else 1435 soft_error_resp = optarg; 1436 break; 1437 case 'B': 1438 if (optarg[0] != '5' || strspn(optarg, "0123456789") != 3) { 1439 msg_error("bad hard error reply: %s", optarg); 1440 usage(argv[0]); 1441 } else 1442 hard_error_resp = optarg; 1443 break; 1444 case 'c': 1445 count++; 1446 break; 1447 case 'C': 1448 disable_xclient = 1; 1449 reset_cmd_flags("xclient", FLAG_ENABLE); 1450 break; 1451 case 'd': 1452 single_template = optarg; 1453 break; 1454 case 'D': 1455 shared_template = optarg; 1456 break; 1457 case 'e': 1458 disable_esmtp = 1; 1459 break; 1460 case 'E': 1461 disable_enh_status = 1; 1462 break; 1463 case 'f': 1464 set_cmds_flags(optarg, FLAG_HARD_ERR); 1465 disable_pipelining = 1; 1466 break; 1467 case 'F': 1468 disable_xforward = 1; 1469 reset_cmd_flags("xforward", FLAG_ENABLE); 1470 break; 1471 case 'h': 1472 var_myhostname = optarg; 1473 break; 1474 case 'L': 1475 enable_lmtp = 1; 1476 break; 1477 case 'm': 1478 if ((max_client_count = atoi(optarg)) <= 0) 1479 msg_fatal("bad concurrency limit: %s", optarg); 1480 break; 1481 case 'M': 1482 if ((max_msg_quit_count = atoi(optarg)) <= 0) 1483 msg_fatal("bad message quit count: %s", optarg); 1484 break; 1485 case 'n': 1486 if ((max_quit_count = atoi(optarg)) <= 0) 1487 msg_fatal("bad quit count: %s", optarg); 1488 break; 1489 case 'p': 1490 disable_pipelining = 1; 1491 break; 1492 case 'P': 1493 pretend_pix = 1; 1494 disable_esmtp = 1; 1495 break; 1496 case 'q': 1497 set_cmds_flags(optarg, FLAG_DISCONNECT); 1498 break; 1499 case 'Q': 1500 set_cmds_flags(optarg, FLAG_CLOSE); 1501 break; 1502 case 'r': 1503 set_cmds_flags(optarg, FLAG_SOFT_ERR); 1504 disable_pipelining = 1; 1505 break; 1506 case 'R': 1507 root_dir = optarg; 1508 break; 1509 case 's': 1510 openlog(basename(argv[0]), LOG_PID, LOG_MAIL); 1511 set_cmds_flags(optarg, FLAG_SYSLOG); 1512 break; 1513 case 'S': 1514 start_string = vstring_alloc(10); 1515 unescape(start_string, optarg); 1516 break; 1517 case 't': 1518 if ((var_tmout = atoi(optarg)) <= 0) 1519 msg_fatal("bad timeout: %s", optarg); 1520 break; 1521 case 'T': 1522 if ((inet_windowsize = atoi(optarg)) <= 0) 1523 msg_fatal("bad TCP window size: %s", optarg); 1524 break; 1525 case 'u': 1526 user_privs = optarg; 1527 break; 1528 case 'v': 1529 msg_verbose++; 1530 break; 1531 case 'w': 1532 if ((delay = atoi(optarg)) <= 0) 1533 usage(argv[0]); 1534 set_cmd_delay("data", delay, 0); 1535 break; 1536 case 'W': 1537 set_cmd_delay_arg(optarg); 1538 break; 1539 default: 1540 usage(argv[0]); 1541 } 1542 } 1543 if (argc - optind != 2) 1544 usage(argv[0]); 1545 if ((backlog = atoi(argv[optind + 1])) <= 0) 1546 usage(argv[0]); 1547 if (single_template && shared_template) 1548 msg_fatal("use only one of -d or -D, but not both"); 1549 if (geteuid() == 0 && user_privs == 0) 1550 msg_fatal("-u option is required if running as root"); 1551 1552 /* 1553 * Initialize. 1554 */ 1555 if (var_myhostname == 0) 1556 var_myhostname = "smtp-sink"; 1557 set_cmds_flags(enable_lmtp ? "lhlo" : 1558 disable_esmtp ? "helo" : 1559 "helo, ehlo", FLAG_ENABLE); 1560 proto_info = inet_proto_init("protocols", protocols); 1561 if (strncmp(argv[optind], "unix:", 5) == 0) { 1562 sock = unix_listen(argv[optind] + 5, backlog, BLOCKING); 1563 } else { 1564 if (strncmp(argv[optind], "inet:", 5) == 0) 1565 argv[optind] += 5; 1566 sock = inet_listen(argv[optind], backlog, BLOCKING); 1567 } 1568 if (user_privs) 1569 chroot_uid(root_dir, user_privs); 1570 1571 if (single_template) 1572 mysrand((int) time((time_t *) 0)); 1573 else if (shared_template) 1574 single_template = shared_template; 1575 1576 /* 1577 * Start the event handler. 1578 */ 1579 event_enable_read(sock, connect_event, (char *) 0); 1580 for (;;) 1581 event_loop(-1); 1582} 1583