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