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