1/*++ 2/* NAME 3/* smtp-source 1 4/* SUMMARY 5/* multi-threaded SMTP/LMTP test generator 6/* SYNOPSIS 7/* .fi 8/* \fBsmtp-source\fR [\fIoptions\fR] [\fBinet:\fR]\fIhost\fR[:\fIport\fR] 9/* 10/* \fBsmtp-source\fR [\fIoptions\fR] \fBunix:\fIpathname\fR 11/* DESCRIPTION 12/* \fBsmtp-source\fR connects to the named \fIhost\fR and TCP \fIport\fR 13/* (default: port 25) 14/* and sends one or more messages to it, either sequentially 15/* or in parallel. The program speaks either SMTP (default) or 16/* LMTP. 17/* Connections can be made to UNIX-domain and IPv4 or IPv6 servers. 18/* IPv4 and IPv6 are the default. 19/* 20/* Note: this is an unsupported test program. No attempt is made 21/* to maintain compatibility between successive versions. 22/* 23/* Arguments: 24/* .IP \fB-4\fR 25/* Connect to the server with IPv4. This option has no effect when 26/* Postfix is built without IPv6 support. 27/* .IP \fB-6\fR 28/* Connect to the server with IPv6. This option is not available when 29/* Postfix is built without IPv6 support. 30/* .IP "\fB-A\fR" 31/* Don't abort when the server sends something other than the 32/* expected positive reply code. 33/* .IP \fB-c\fR 34/* Display a running counter that is incremented each time 35/* an SMTP DATA command completes. 36/* .IP "\fB-C \fIcount\fR" 37/* When a host sends RESET instead of SYN|ACK, try \fIcount\fR times 38/* before giving up. The default count is 1. Specify a larger count in 39/* order to work around a problem with TCP/IP stacks that send RESET 40/* when the listen queue is full. 41/* .IP \fB-d\fR 42/* Don't disconnect after sending a message; send the next 43/* message over the same connection. 44/* .IP "\fB-f \fIfrom\fR" 45/* Use the specified sender address (default: <foo@myhostname>). 46/* .IP "\fB-F \fIfile\fR" 47/* Send the pre-formatted message header and body in the 48/* specified \fIfile\fR, while prepending '.' before lines that 49/* begin with '.', and while appending CRLF after each line. 50/* .IP "\fB-l \fIlength\fR" 51/* Send \fIlength\fR bytes as message payload. The length does not 52/* include message headers. 53/* .IP \fB-L\fR 54/* Speak LMTP rather than SMTP. 55/* .IP "\fB-m \fImessage_count\fR" 56/* Send the specified number of messages (default: 1). 57/* .IP "\fB-M \fImyhostname\fR" 58/* Use the specified hostname or [address] in the HELO command 59/* and in the default sender and recipient addresses, instead 60/* of the machine hostname. 61/* .IP "\fB-N\fR" 62/* Prepend a non-repeating sequence number to each recipient 63/* address. This avoids the artificial 100% hit rate in the 64/* resolve and rewrite client caches and exercises the 65/* trivial-rewrite daemon, better approximating Postfix 66/* performance under real-life work-loads. 67/* .IP \fB-o\fR 68/* Old mode: don't send HELO, and don't send message headers. 69/* .IP "\fB-r \fIrecipient_count\fR" 70/* Send the specified number of recipients per transaction (default: 1). 71/* Recipient names are generated by prepending a number to the 72/* recipient address. 73/* .IP "\fB-R \fIinterval\fR" 74/* Wait for a random period of time 0 <= n <= interval between messages. 75/* Suspending one thread does not affect other delivery threads. 76/* .IP "\fB-s \fIsession_count\fR" 77/* Run the specified number of SMTP sessions in parallel (default: 1). 78/* .IP "\fB-S \fIsubject\fR" 79/* Send mail with the named subject line (default: none). 80/* .IP "\fB-t \fIto\fR" 81/* Use the specified recipient address (default: <foo@myhostname>). 82/* .IP "\fB-T \fIwindowsize\fR" 83/* Override the default TCP window size. To work around 84/* broken TCP window scaling implementations, specify a 85/* value > 0 and < 65536. 86/* .IP \fB-v\fR 87/* Make the program more verbose, for debugging purposes. 88/* .IP "\fB-w \fIinterval\fR" 89/* Wait a fixed time between messages. 90/* Suspending one thread does not affect other delivery threads. 91/* .IP [\fBinet:\fR]\fIhost\fR[:\fIport\fR] 92/* Connect via TCP to host \fIhost\fR, port \fIport\fR. The default 93/* port is \fBsmtp\fR. 94/* .IP \fBunix:\fIpathname\fR 95/* Connect to the UNIX-domain socket at \fIpathname\fR. 96/* BUGS 97/* No SMTP command pipelining support. 98/* SEE ALSO 99/* smtp-sink(1), SMTP/LMTP message dump 100/* LICENSE 101/* .ad 102/* .fi 103/* The Secure Mailer license must be distributed with this software. 104/* AUTHOR(S) 105/* Wietse Venema 106/* IBM T.J. Watson Research 107/* P.O. Box 704 108/* Yorktown Heights, NY 10598, USA 109/*--*/ 110 111/* System library. */ 112 113#include <sys_defs.h> 114#include <sys/socket.h> 115#include <sys/wait.h> 116#include <netinet/in.h> 117#include <sys/un.h> 118#include <stdarg.h> 119#include <string.h> 120#include <ctype.h> 121#include <stdlib.h> 122#include <unistd.h> 123#include <signal.h> 124#include <fcntl.h> 125#include <errno.h> 126 127/* Utility library. */ 128 129#include <msg.h> 130#include <msg_vstream.h> 131#include <vstring.h> 132#include <vstream.h> 133#include <vstring_vstream.h> 134#include <get_hostname.h> 135#include <split_at.h> 136#include <connect.h> 137#include <mymalloc.h> 138#include <events.h> 139#include <iostuff.h> 140#include <sane_connect.h> 141#include <host_port.h> 142#include <myaddrinfo.h> 143#include <inet_proto.h> 144#include <valid_hostname.h> 145#include <valid_mailhost_addr.h> 146#include <compat_va_copy.h> 147 148/* Global library. */ 149 150#include <smtp_stream.h> 151#include <mail_date.h> 152#include <mail_version.h> 153 154/* Application-specific. */ 155 156 /* 157 * Per-session data structure with state. 158 * 159 * This software can maintain multiple parallel connections to the same SMTP 160 * server. However, it makes no more than one connection request at a time 161 * to avoid overwhelming the server with SYN packets and having to back off. 162 * Back-off would screw up the benchmark. Pending connection requests are 163 * kept in a linear list. 164 */ 165typedef struct SESSION { 166 int xfer_count; /* # of xfers in session */ 167 int rcpt_done; /* # of recipients done */ 168 int rcpt_count; /* # of recipients to go */ 169 int rcpt_accepted; /* # of recipients accepted */ 170 VSTREAM *stream; /* open connection */ 171 int connect_count; /* # of connect()s to retry */ 172 struct SESSION *next; /* connect() queue linkage */ 173} SESSION; 174 175static SESSION *last_session; /* connect() queue tail */ 176 177 /* 178 * Structure with broken-up SMTP server response. 179 */ 180typedef struct { /* server response */ 181 int code; /* status */ 182 char *str; /* text */ 183 VSTRING *buf; /* origin of text */ 184} RESPONSE; 185 186static VSTRING *buffer; 187static int var_line_limit = 10240; 188static int var_timeout = 300; 189static const char *var_myhostname; 190static int session_count; 191static int message_count = 1; 192static struct sockaddr_storage ss; 193 194#undef sun 195static struct sockaddr_un sun; 196static struct sockaddr *sa; 197static int sa_length; 198static int recipients = 1; 199static char *defaddr; 200static char *recipient; 201static char *sender; 202static char *message_data; 203static int message_length; 204static int disconnect = 1; 205static int count = 0; 206static int counter = 0; 207static int send_helo_first = 1; 208static int send_headers = 1; 209static int connect_count = 1; 210static int random_delay = 0; 211static int fixed_delay = 0; 212static int talk_lmtp = 0; 213static char *subject = 0; 214static int number_rcpts = 0; 215static int allow_reject = 0; 216 217static void enqueue_connect(SESSION *); 218static void start_connect(SESSION *); 219static void connect_done(int, char *); 220static void read_banner(int, char *); 221static void send_helo(SESSION *); 222static void helo_done(int, char *); 223static void send_mail(SESSION *); 224static void mail_done(int, char *); 225static void send_rcpt(int, char *); 226static void rcpt_done(int, char *); 227static void send_data(int, char *); 228static void data_done(int, char *); 229static void dot_done(int, char *); 230static void send_rset(int, char *); 231static void rset_done(int, char *); 232static void send_quit(SESSION *); 233static void quit_done(int, char *); 234static void close_session(SESSION *); 235 236/* random_interval - generate a random value in 0 .. (small) interval */ 237 238static int random_interval(int interval) 239{ 240 return (rand() % (interval + 1)); 241} 242 243/* command - send an SMTP command */ 244 245static void command(VSTREAM *stream, char *fmt,...) 246{ 247 va_list ap; 248 249 va_start(ap, fmt); 250 251 /* 252 * Optionally, log the command before actually sending, so we can see 253 * what the program is trying to do. 254 */ 255 if (msg_verbose) { 256 va_list ap2; 257 258 VA_COPY(ap2, ap); 259 vmsg_info(fmt, ap2); 260 va_end(ap2); 261 } 262 smtp_vprintf(stream, fmt, ap); 263 va_end(ap); 264 smtp_flush(stream); 265} 266 267/* socket_error - look up and reset the last socket error */ 268 269static int socket_error(int sock) 270{ 271 int error; 272 SOCKOPT_SIZE error_len; 273 274 /* 275 * Some Solaris 2 versions have getsockopt() itself return the error, 276 * instead of returning it via the parameter list. 277 */ 278 error = 0; 279 error_len = sizeof(error); 280 if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (char *) &error, &error_len) < 0) 281 return (-1); 282 if (error) { 283 errno = error; 284 return (-1); 285 } 286 287 /* 288 * No problems. 289 */ 290 return (0); 291} 292 293/* response - read and process SMTP server response */ 294 295static RESPONSE *response(VSTREAM *stream, VSTRING *buf) 296{ 297 static RESPONSE rdata; 298 int more; 299 char *cp; 300 301 /* 302 * Initialize the response data buffer. Defend against a denial of 303 * service attack by limiting the amount of multi-line text that we are 304 * willing to store. 305 */ 306 if (rdata.buf == 0) { 307 rdata.buf = vstring_alloc(100); 308 vstring_ctl(rdata.buf, VSTRING_CTL_MAXLEN, (ssize_t) var_line_limit, 0); 309 } 310 311 /* 312 * Censor out non-printable characters in server responses. Concatenate 313 * multi-line server responses. Separate the status code from the text. 314 * Leave further parsing up to the application. 315 */ 316#define BUF ((char *) vstring_str(buf)) 317 VSTRING_RESET(rdata.buf); 318 for (;;) { 319 smtp_get(buf, stream, var_line_limit, SMTP_GET_FLAG_SKIP); 320 for (cp = BUF; *cp != 0; cp++) 321 if (!ISPRINT(*cp) && !ISSPACE(*cp)) 322 *cp = '?'; 323 cp = BUF; 324 if (msg_verbose) 325 msg_info("<<< %s", cp); 326 while (ISDIGIT(*cp)) 327 cp++; 328 rdata.code = (cp - BUF == 3 ? atoi(BUF) : 0); 329 if ((more = (*cp == '-')) != 0) 330 cp++; 331 while (ISSPACE(*cp)) 332 cp++; 333 vstring_strcat(rdata.buf, cp); 334 if (more == 0) 335 break; 336 VSTRING_ADDCH(rdata.buf, '\n'); 337 } 338 VSTRING_TERMINATE(rdata.buf); 339 rdata.str = vstring_str(rdata.buf); 340 return (&rdata); 341} 342 343/* exception_text - translate exceptions from the smtp_stream module */ 344 345static char *exception_text(int except) 346{ 347 switch (except) { 348 case SMTP_ERR_EOF: 349 return ("lost connection"); 350 case SMTP_ERR_TIME: 351 return ("timeout"); 352 default: 353 msg_panic("exception_text: unknown exception %d", except); 354 } 355 /* NOTREACHED */ 356} 357 358/* startup - connect to server but do not wait */ 359 360static void startup(SESSION *session) 361{ 362 if (message_count-- <= 0) { 363 myfree((char *) session); 364 session_count--; 365 return; 366 } 367 if (session->stream == 0) { 368 enqueue_connect(session); 369 } else { 370 send_mail(session); 371 } 372} 373 374/* start_event - invoke startup from timer context */ 375 376static void start_event(int unused_event, char *context) 377{ 378 SESSION *session = (SESSION *) context; 379 380 startup(session); 381} 382 383/* start_another - start another session */ 384 385static void start_another(SESSION *session) 386{ 387 if (random_delay > 0) { 388 event_request_timer(start_event, (char *) session, 389 random_interval(random_delay)); 390 } else if (fixed_delay > 0) { 391 event_request_timer(start_event, (char *) session, fixed_delay); 392 } else { 393 startup(session); 394 } 395} 396 397/* enqueue_connect - queue a connection request */ 398 399static void enqueue_connect(SESSION *session) 400{ 401 session->next = 0; 402 if (last_session == 0) { 403 last_session = session; 404 start_connect(session); 405 } else { 406 last_session->next = session; 407 last_session = session; 408 } 409} 410 411/* dequeue_connect - connection request completed */ 412 413static void dequeue_connect(SESSION *session) 414{ 415 if (session == last_session) { 416 if (session->next != 0) 417 msg_panic("dequeue_connect: queue ends after last"); 418 last_session = 0; 419 } else { 420 if (session->next == 0) 421 msg_panic("dequeue_connect: queue ends before last"); 422 start_connect(session->next); 423 } 424} 425 426/* fail_connect - handle failed startup */ 427 428static void fail_connect(SESSION *session) 429{ 430 if (session->connect_count-- == 1) 431 msg_fatal("connect: %m"); 432 msg_warn("connect: %m"); 433 event_disable_readwrite(vstream_fileno(session->stream)); 434 vstream_fclose(session->stream); 435 session->stream = 0; 436#ifdef MISSING_USLEEP 437 doze(10); 438#else 439 usleep(10); 440#endif 441 start_connect(session); 442} 443 444/* start_connect - start TCP handshake */ 445 446static void start_connect(SESSION *session) 447{ 448 int fd; 449 struct linger linger; 450 451 /* 452 * Some systems don't set the socket error when connect() fails early 453 * (loopback) so we must deal with the error immediately, rather than 454 * retrieving it later with getsockopt(). We can't use MSG_PEEK to 455 * distinguish between server disconnect and connection refused. 456 */ 457 if ((fd = socket(sa->sa_family, SOCK_STREAM, 0)) < 0) 458 msg_fatal("socket: %m"); 459 (void) non_blocking(fd, NON_BLOCKING); 460 linger.l_onoff = 1; 461 linger.l_linger = 0; 462 if (setsockopt(fd, SOL_SOCKET, SO_LINGER, (char *) &linger, 463 sizeof(linger)) < 0) 464 msg_warn("setsockopt SO_LINGER %d: %m", linger.l_linger); 465 session->stream = vstream_fdopen(fd, O_RDWR); 466 event_enable_write(fd, connect_done, (char *) session); 467 smtp_timeout_setup(session->stream, var_timeout); 468 if (inet_windowsize > 0) 469 set_inet_windowsize(fd, inet_windowsize); 470 if (sane_connect(fd, sa, sa_length) < 0 && errno != EINPROGRESS) 471 fail_connect(session); 472} 473 474/* connect_done - send message sender info */ 475 476static void connect_done(int unused_event, char *context) 477{ 478 SESSION *session = (SESSION *) context; 479 int fd = vstream_fileno(session->stream); 480 481 /* 482 * Try again after some delay when the connection failed, in case they 483 * run a Mickey Mouse protocol stack. 484 */ 485 if (socket_error(fd) < 0) { 486 fail_connect(session); 487 } else { 488 non_blocking(fd, BLOCKING); 489 /* Disable write events. */ 490 event_disable_readwrite(fd); 491 event_enable_read(fd, read_banner, (char *) session); 492 dequeue_connect(session); 493 /* Avoid poor performance when TCP MSS > VSTREAM_BUFSIZE. */ 494 if (sa->sa_family == AF_INET 495#ifdef AF_INET6 496 || sa->sa_family == AF_INET6 497#endif 498 ) 499 vstream_tweak_tcp(session->stream); 500 } 501} 502 503/* read_banner - receive SMTP server greeting */ 504 505static void read_banner(int unused_event, char *context) 506{ 507 SESSION *session = (SESSION *) context; 508 RESPONSE *resp; 509 int except; 510 511 /* 512 * Prepare for disaster. 513 */ 514 if ((except = vstream_setjmp(session->stream)) != 0) 515 msg_fatal("%s while reading server greeting", exception_text(except)); 516 517 /* 518 * Read and parse the server's SMTP greeting banner. 519 */ 520 if (((resp = response(session->stream, buffer))->code / 100) == 2) { 521 /* void */ ; 522 } else if (allow_reject) { 523 msg_warn("rejected at server banner: %d %s", resp->code, resp->str); 524 } else { 525 msg_fatal("rejected at server banner: %d %s", resp->code, resp->str); 526 } 527 528 /* 529 * Send helo or send the envelope sender address. 530 */ 531 if (send_helo_first) 532 send_helo(session); 533 else 534 send_mail(session); 535} 536 537/* send_helo - send hostname */ 538 539static void send_helo(SESSION *session) 540{ 541 int except; 542 const char *NOCLOBBER protocol = (talk_lmtp ? "LHLO" : "HELO"); 543 544 /* 545 * Send the standard greeting with our hostname 546 */ 547 if ((except = vstream_setjmp(session->stream)) != 0) 548 msg_fatal("%s while sending %s", exception_text(except), protocol); 549 550 command(session->stream, "%s %s", protocol, var_myhostname); 551 552 /* 553 * Prepare for the next event. 554 */ 555 event_enable_read(vstream_fileno(session->stream), helo_done, (char *) session); 556} 557 558/* helo_done - handle HELO response */ 559 560static void helo_done(int unused_event, char *context) 561{ 562 SESSION *session = (SESSION *) context; 563 RESPONSE *resp; 564 int except; 565 const char *protocol = (talk_lmtp ? "LHLO" : "HELO"); 566 567 /* 568 * Get response to HELO command. 569 */ 570 if ((except = vstream_setjmp(session->stream)) != 0) 571 msg_fatal("%s while sending %s", exception_text(except), protocol); 572 573 if ((resp = response(session->stream, buffer))->code / 100 == 2) { 574 /* void */ ; 575 } else if (allow_reject) { 576 msg_warn("%s rejected: %d %s", protocol, resp->code, resp->str); 577 if (resp->code == 421 || resp->code == 521) { 578 close_session(session); 579 return; 580 } 581 } else { 582 msg_fatal("%s rejected: %d %s", protocol, resp->code, resp->str); 583 } 584 585 send_mail(session); 586} 587 588/* send_mail - send envelope sender */ 589 590static void send_mail(SESSION *session) 591{ 592 int except; 593 594 /* 595 * Send the envelope sender address. 596 */ 597 if ((except = vstream_setjmp(session->stream)) != 0) 598 msg_fatal("%s while sending sender", exception_text(except)); 599 600 command(session->stream, "MAIL FROM:<%s>", sender); 601 602 /* 603 * Prepare for the next event. 604 */ 605 event_enable_read(vstream_fileno(session->stream), mail_done, (char *) session); 606} 607 608/* mail_done - handle MAIL response */ 609 610static void mail_done(int unused, char *context) 611{ 612 SESSION *session = (SESSION *) context; 613 RESPONSE *resp; 614 int except; 615 616 /* 617 * Get response to MAIL command. 618 */ 619 if ((except = vstream_setjmp(session->stream)) != 0) 620 msg_fatal("%s while sending sender", exception_text(except)); 621 622 if ((resp = response(session->stream, buffer))->code / 100 == 2) { 623 session->rcpt_count = recipients; 624 session->rcpt_done = 0; 625 session->rcpt_accepted = 0; 626 send_rcpt(unused, context); 627 } else if (allow_reject) { 628 msg_warn("sender rejected: %d %s", resp->code, resp->str); 629 if (resp->code == 421 || resp->code == 521) { 630 close_session(session); 631 return; 632 } 633 send_rset(unused, context); 634 } else { 635 msg_fatal("sender rejected: %d %s", resp->code, resp->str); 636 } 637} 638 639/* send_rcpt - send recipient address */ 640 641static void send_rcpt(int unused_event, char *context) 642{ 643 SESSION *session = (SESSION *) context; 644 int except; 645 646 /* 647 * Send envelope recipient address. 648 */ 649 if ((except = vstream_setjmp(session->stream)) != 0) 650 msg_fatal("%s while sending recipient", exception_text(except)); 651 652 if (session->rcpt_count > 1 || number_rcpts > 0) 653 command(session->stream, "RCPT TO:<%d%s>", 654 number_rcpts ? number_rcpts++ : session->rcpt_count, 655 recipient); 656 else 657 command(session->stream, "RCPT TO:<%s>", recipient); 658 session->rcpt_count--; 659 session->rcpt_done++; 660 661 /* 662 * Prepare for the next event. 663 */ 664 event_enable_read(vstream_fileno(session->stream), rcpt_done, (char *) session); 665} 666 667/* rcpt_done - handle RCPT completion */ 668 669static void rcpt_done(int unused, char *context) 670{ 671 SESSION *session = (SESSION *) context; 672 RESPONSE *resp; 673 int except; 674 675 /* 676 * Get response to RCPT command. 677 */ 678 if ((except = vstream_setjmp(session->stream)) != 0) 679 msg_fatal("%s while sending recipient", exception_text(except)); 680 681 if ((resp = response(session->stream, buffer))->code / 100 == 2) { 682 session->rcpt_accepted++; 683 } else if (allow_reject) { 684 msg_warn("recipient rejected: %d %s", resp->code, resp->str); 685 if (resp->code == 421 || resp->code == 521) { 686 close_session(session); 687 return; 688 } 689 } else { 690 msg_fatal("recipient rejected: %d %s", resp->code, resp->str); 691 } 692 693 /* 694 * Send another RCPT command or send DATA. 695 */ 696 if (session->rcpt_count > 0) 697 send_rcpt(unused, context); 698 else if (session->rcpt_accepted > 0) 699 send_data(unused, context); 700 else 701 send_rset(unused, context); 702} 703 704/* send_data - send DATA command */ 705 706static void send_data(int unused_event, char *context) 707{ 708 SESSION *session = (SESSION *) context; 709 int except; 710 711 /* 712 * Request data transmission. 713 */ 714 if ((except = vstream_setjmp(session->stream)) != 0) 715 msg_fatal("%s while sending DATA command", exception_text(except)); 716 command(session->stream, "DATA"); 717 718 /* 719 * Prepare for the next event. 720 */ 721 event_enable_read(vstream_fileno(session->stream), data_done, (char *) session); 722} 723 724/* data_done - send message content */ 725 726static void data_done(int unused, char *context) 727{ 728 SESSION *session = (SESSION *) context; 729 RESPONSE *resp; 730 int except; 731 static const char *mydate; 732 static int mypid; 733 734 /* 735 * Get response to DATA command. 736 */ 737 if ((except = vstream_setjmp(session->stream)) != 0) 738 msg_fatal("%s while sending DATA command", exception_text(except)); 739 if ((resp = response(session->stream, buffer))->code == 354) { 740 /* see below */ ; 741 } else if (allow_reject) { 742 msg_warn("data rejected: %d %s", resp->code, resp->str); 743 if (resp->code == 421 || resp->code == 521) { 744 close_session(session); 745 return; 746 } 747 send_rset(unused, context); 748 return; 749 } else { 750 msg_fatal("data rejected: %d %s", resp->code, resp->str); 751 } 752 753 /* 754 * Send basic header to keep mailers that bother to examine them happy. 755 */ 756 if (send_headers) { 757 if (mydate == 0) { 758 mydate = mail_date(time((time_t *) 0)); 759 mypid = getpid(); 760 } 761 smtp_printf(session->stream, "From: <%s>", sender); 762 smtp_printf(session->stream, "To: <%s>", recipient); 763 smtp_printf(session->stream, "Date: %s", mydate); 764 smtp_printf(session->stream, "Message-Id: <%04x.%04x.%04x@%s>", 765 mypid, vstream_fileno(session->stream), message_count, var_myhostname); 766 if (subject) 767 smtp_printf(session->stream, "Subject: %s", subject); 768 smtp_fputs("", 0, session->stream); 769 } 770 771 /* 772 * Send some garbage. 773 */ 774 if ((except = vstream_setjmp(session->stream)) != 0) 775 msg_fatal("%s while sending message", exception_text(except)); 776 if (message_length == 0) { 777 smtp_fputs("La de da de da 1.", 17, session->stream); 778 smtp_fputs("La de da de da 2.", 17, session->stream); 779 smtp_fputs("La de da de da 3.", 17, session->stream); 780 smtp_fputs("La de da de da 4.", 17, session->stream); 781 } else { 782 783 /* 784 * XXX This may cause the process to block with message content 785 * larger than VSTREAM_BUFIZ bytes. 786 */ 787 smtp_fputs(message_data, message_length, session->stream); 788 } 789 790 /* 791 * Send end of message and process the server response. 792 */ 793 command(session->stream, "."); 794 795 /* 796 * Update the running counter. 797 */ 798 if (count) { 799 counter++; 800 vstream_printf("%d\r", counter); 801 vstream_fflush(VSTREAM_OUT); 802 } 803 804 /* 805 * Prepare for the next event. 806 */ 807 event_enable_read(vstream_fileno(session->stream), dot_done, (char *) session); 808} 809 810/* dot_done - send QUIT or start another transaction */ 811 812static void dot_done(int unused_event, char *context) 813{ 814 SESSION *session = (SESSION *) context; 815 RESPONSE *resp; 816 int except; 817 818 /* 819 * Get response to "." command. 820 */ 821 if ((except = vstream_setjmp(session->stream)) != 0) 822 msg_fatal("%s while sending message", exception_text(except)); 823 do { /* XXX this could block */ 824 if ((resp = response(session->stream, buffer))->code / 100 == 2) { 825 /* void */ ; 826 } else if (allow_reject) { 827 msg_warn("end of data rejected: %d %s", resp->code, resp->str); 828 if (resp->code == 421 || resp->code == 521) { 829 close_session(session); 830 return; 831 } 832 } else { 833 msg_fatal("end of data rejected: %d %s", resp->code, resp->str); 834 } 835 } while (talk_lmtp && --session->rcpt_done > 0); 836 session->xfer_count++; 837 838 /* 839 * Say goodbye or send the next message. 840 */ 841 if (disconnect || message_count < 1) { 842 send_quit(session); 843 } else { 844 event_disable_readwrite(vstream_fileno(session->stream)); 845 start_another(session); 846 } 847} 848 849/* send_rset - send RSET command */ 850 851static void send_rset(int unused_event, char *context) 852{ 853 SESSION *session = (SESSION *) context; 854 855 command(session->stream, "RSET"); 856 event_enable_read(vstream_fileno(session->stream), rset_done, (char *) session); 857} 858 859/* rset_done - handle RSET reply */ 860 861static void rset_done(int unused_event, char *context) 862{ 863 SESSION *session = (SESSION *) context; 864 RESPONSE *resp; 865 int except; 866 867 /* 868 * Get response to RSET command. 869 */ 870 if ((except = vstream_setjmp(session->stream)) != 0) 871 msg_fatal("%s while sending message", exception_text(except)); 872 if ((resp = response(session->stream, buffer))->code / 100 == 2) { 873 /* void */ 874 } else if (allow_reject) { 875 msg_warn("rset rejected: %d %s", resp->code, resp->str); 876 if (resp->code == 421 || resp->code == 521) { 877 close_session(session); 878 return; 879 } 880 } else { 881 msg_fatal("rset rejected: %d %s", resp->code, resp->str); 882 } 883 884 /* 885 * Say goodbye or send the next message. 886 */ 887 if (disconnect || message_count < 1) { 888 send_quit(session); 889 } else { 890 event_disable_readwrite(vstream_fileno(session->stream)); 891 start_another(session); 892 } 893} 894 895/* send_quit - send QUIT command */ 896 897static void send_quit(SESSION *session) 898{ 899 command(session->stream, "QUIT"); 900 event_enable_read(vstream_fileno(session->stream), quit_done, (char *) session); 901} 902 903/* quit_done - disconnect */ 904 905static void quit_done(int unused_event, char *context) 906{ 907 SESSION *session = (SESSION *) context; 908 909 (void) response(session->stream, buffer); 910 event_disable_readwrite(vstream_fileno(session->stream)); 911 vstream_fclose(session->stream); 912 session->stream = 0; 913 start_another(session); 914} 915 916/* close_session - disconnect, for example after 421 or 521 reply */ 917 918static void close_session(SESSION *session) 919{ 920 event_disable_readwrite(vstream_fileno(session->stream)); 921 vstream_fclose(session->stream); 922 session->stream = 0; 923 start_another(session); 924} 925 926/* usage - explain */ 927 928static void usage(char *myname) 929{ 930 msg_fatal("usage: %s -cdLNov -s sess -l msglen -m msgs -C count -M myhostname -f from -t to -r rcptcount -R delay -w delay host[:port]", myname); 931} 932 933MAIL_VERSION_STAMP_DECLARE; 934 935/* main - parse JCL and start the machine */ 936 937int main(int argc, char **argv) 938{ 939 SESSION *session; 940 char *host; 941 char *port; 942 char *path; 943 int path_len; 944 int sessions = 1; 945 int ch; 946 int i; 947 char *buf; 948 const char *parse_err; 949 struct addrinfo *res; 950 int aierr; 951 const char *protocols = INET_PROTO_NAME_ALL; 952 INET_PROTO_INFO *proto_info; 953 char *message_file = 0; 954 955 /* 956 * Fingerprint executables and core dumps. 957 */ 958 MAIL_VERSION_STAMP_ALLOCATE; 959 960 signal(SIGPIPE, SIG_IGN); 961 msg_vstream_init(argv[0], VSTREAM_ERR); 962 963 /* 964 * Parse JCL. 965 */ 966 while ((ch = GETOPT(argc, argv, "46AcC:df:F:l:Lm:M:Nor:R:s:S:t:T:vw:")) > 0) { 967 switch (ch) { 968 case '4': 969 protocols = INET_PROTO_NAME_IPV4; 970 break; 971 case '6': 972 protocols = INET_PROTO_NAME_IPV6; 973 break; 974 case 'A': 975 allow_reject = 1; 976 break; 977 case 'c': 978 count++; 979 break; 980 case 'C': 981 if ((connect_count = atoi(optarg)) <= 0) 982 msg_fatal("bad connection count: %s", optarg); 983 break; 984 case 'd': 985 disconnect = 0; 986 break; 987 case 'f': 988 sender = optarg; 989 break; 990 case 'F': 991 if (message_file == 0 && message_length > 0) 992 msg_fatal("-l option cannot be used with -F"); 993 message_file = optarg; 994 break; 995 case 'l': 996 if (message_file != 0) 997 msg_fatal("-l option cannot be used with -F"); 998 if ((message_length = atoi(optarg)) <= 0) 999 msg_fatal("bad message length: %s", optarg); 1000 break; 1001 case 'L': 1002 talk_lmtp = 1; 1003 break; 1004 case 'm': 1005 if ((message_count = atoi(optarg)) <= 0) 1006 msg_fatal("bad message count: %s", optarg); 1007 break; 1008 case 'M': 1009 if (*optarg == '[') { 1010 if (!valid_mailhost_literal(optarg, DO_GRIPE)) 1011 msg_fatal("bad address literal: %s", optarg); 1012 } else { 1013 if (!valid_hostname(optarg, DO_GRIPE)) 1014 msg_fatal("bad hostname: %s", optarg); 1015 } 1016 var_myhostname = optarg; 1017 break; 1018 case 'N': 1019 number_rcpts = 1; 1020 break; 1021 case 'o': 1022 send_helo_first = 0; 1023 send_headers = 0; 1024 break; 1025 case 'r': 1026 if ((recipients = atoi(optarg)) <= 0) 1027 msg_fatal("bad recipient count: %s", optarg); 1028 break; 1029 case 'R': 1030 if (fixed_delay > 0) 1031 msg_fatal("do not use -w and -R options at the same time"); 1032 if ((random_delay = atoi(optarg)) <= 0) 1033 msg_fatal("bad random delay: %s", optarg); 1034 break; 1035 case 's': 1036 if ((sessions = atoi(optarg)) <= 0) 1037 msg_fatal("bad session count: %s", optarg); 1038 break; 1039 case 'S': 1040 subject = optarg; 1041 break; 1042 case 't': 1043 recipient = optarg; 1044 break; 1045 case 'T': 1046 if ((inet_windowsize = atoi(optarg)) <= 0) 1047 msg_fatal("bad TCP window size: %s", optarg); 1048 break; 1049 case 'v': 1050 msg_verbose++; 1051 break; 1052 case 'w': 1053 if (random_delay > 0) 1054 msg_fatal("do not use -w and -R options at the same time"); 1055 if ((fixed_delay = atoi(optarg)) <= 0) 1056 msg_fatal("bad fixed delay: %s", optarg); 1057 break; 1058 default: 1059 usage(argv[0]); 1060 } 1061 } 1062 if (argc - optind != 1) 1063 usage(argv[0]); 1064 1065 if (random_delay > 0) 1066 srand(getpid()); 1067 1068 /* 1069 * Initialize the message content, SMTP encoded. smtp_fputs() will append 1070 * another \r\n but we don't care. 1071 */ 1072 if (message_file != 0) { 1073 VSTREAM *fp; 1074 VSTRING *buf = vstring_alloc(100); 1075 VSTRING *msg = vstring_alloc(100); 1076 1077 if ((fp = vstream_fopen(message_file, O_RDONLY, 0)) == 0) 1078 msg_fatal("open %s: %m", message_file); 1079 while (vstring_get_nonl(buf, fp) != VSTREAM_EOF) { 1080 if (*vstring_str(buf) == '.') 1081 VSTRING_ADDCH(msg, '.'); 1082 vstring_memcat(msg, vstring_str(buf), VSTRING_LEN(buf)); 1083 vstring_memcat(msg, "\r\n", 2); 1084 } 1085 if (vstream_ferror(fp)) 1086 msg_fatal("read %s: %m", message_file); 1087 vstream_fclose(fp); 1088 vstring_free(buf); 1089 message_length = VSTRING_LEN(msg); 1090 message_data = vstring_export(msg); 1091 send_headers = 0; 1092 } else if (message_length > 0) { 1093 message_data = mymalloc(message_length); 1094 memset(message_data, 'X', message_length); 1095 for (i = 80; i < message_length; i += 80) { 1096 message_data[i - 80] = "0123456789"[(i / 80) % 10]; 1097 message_data[i - 2] = '\r'; 1098 message_data[i - 1] = '\n'; 1099 } 1100 } 1101 1102 /* 1103 * Translate endpoint address to internal form. 1104 */ 1105 proto_info = inet_proto_init("protocols", protocols); 1106 if (strncmp(argv[optind], "unix:", 5) == 0) { 1107 path = argv[optind] + 5; 1108 path_len = strlen(path); 1109 if (path_len >= (int) sizeof(sun.sun_path)) 1110 msg_fatal("unix-domain name too long: %s", path); 1111 memset((char *) &sun, 0, sizeof(sun)); 1112 sun.sun_family = AF_UNIX; 1113#ifdef HAS_SUN_LEN 1114 sun.sun_len = path_len + 1; 1115#endif 1116 memcpy(sun.sun_path, path, path_len); 1117 sa = (struct sockaddr *) & sun; 1118 sa_length = sizeof(sun); 1119 } else { 1120 if (strncmp(argv[optind], "inet:", 5) == 0) 1121 argv[optind] += 5; 1122 buf = mystrdup(argv[optind]); 1123 if ((parse_err = host_port(buf, &host, (char *) 0, &port, "smtp")) != 0) 1124 msg_fatal("%s: %s", argv[optind], parse_err); 1125 if ((aierr = hostname_to_sockaddr(host, port, SOCK_STREAM, &res)) != 0) 1126 msg_fatal("%s: %s", argv[optind], MAI_STRERROR(aierr)); 1127 myfree(buf); 1128 sa = (struct sockaddr *) & ss; 1129 if (res->ai_addrlen > sizeof(ss)) 1130 msg_fatal("address length %d > buffer length %d", 1131 (int) res->ai_addrlen, (int) sizeof(ss)); 1132 memcpy((char *) sa, res->ai_addr, res->ai_addrlen); 1133 sa_length = res->ai_addrlen; 1134#ifdef HAS_SA_LEN 1135 sa->sa_len = sa_length; 1136#endif 1137 freeaddrinfo(res); 1138 } 1139 1140 /* 1141 * Make sure the SMTP server cannot run us out of memory by sending 1142 * never-ending lines of text. 1143 */ 1144 if (buffer == 0) { 1145 buffer = vstring_alloc(100); 1146 vstring_ctl(buffer, VSTRING_CTL_MAXLEN, (ssize_t) var_line_limit, 0); 1147 } 1148 1149 /* 1150 * Make sure we have sender and recipient addresses. 1151 */ 1152 if (var_myhostname == 0) 1153 var_myhostname = get_hostname(); 1154 if (sender == 0 || recipient == 0) { 1155 vstring_sprintf(buffer, "foo@%s", var_myhostname); 1156 defaddr = mystrdup(vstring_str(buffer)); 1157 if (sender == 0) 1158 sender = defaddr; 1159 if (recipient == 0) 1160 recipient = defaddr; 1161 } 1162 1163 /* 1164 * Start sessions. 1165 */ 1166 while (sessions-- > 0) { 1167 session = (SESSION *) mymalloc(sizeof(*session)); 1168 session->stream = 0; 1169 session->xfer_count = 0; 1170 session->connect_count = connect_count; 1171 session->next = 0; 1172 session_count++; 1173 startup(session); 1174 } 1175 for (;;) { 1176 event_loop(-1); 1177 if (session_count <= 0 && message_count <= 0) { 1178 if (count) { 1179 VSTREAM_PUTC('\n', VSTREAM_OUT); 1180 vstream_fflush(VSTREAM_OUT); 1181 } 1182 exit(0); 1183 } 1184 } 1185} 1186