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 */ 172 typedef 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 182 static SESSION *last_session; /* connect() queue tail */ 183 184 /* 185 * Structure with broken-up SMTP server response. 186 */ 187 typedef struct { /* server response */ 188 int code; /* status */ 189 char *str; /* text */ 190 VSTRING *buf; /* origin of text */ 191 } RESPONSE; 192 193 static VSTRING *buffer; 194 static int var_line_limit = 10240; 195 static int var_timeout = 300; 196 static const char *var_myhostname; 197 static int session_count; 198 static int message_count = 1; 199 static struct sockaddr_storage ss; 200 201 #undef sun 202 static struct sockaddr_un sun; 203 static struct sockaddr *sa; 204 static int sa_length; 205 static int recipients = 1; 206 static char *defaddr; 207 static char *recipient; 208 static char *sender; 209 static char *message_data; 210 static int message_length; 211 static int disconnect = 1; 212 static int count = 0; 213 static int counter = 0; 214 static int send_helo_first = 1; 215 static int send_headers = 1; 216 static int connect_count = 1; 217 static int random_delay = 0; 218 static int fixed_delay = 0; 219 static int talk_lmtp = 0; 220 static char *subject = 0; 221 static int number_rcpts = 0; 222 static int allow_reject = 0; 223 224 static void enqueue_connect(SESSION *); 225 static void start_connect(SESSION *); 226 static void connect_done(int, void *); 227 static void read_banner(int, void *); 228 static void send_helo(SESSION *); 229 static void helo_done(int, void *); 230 static void send_mail(SESSION *); 231 static void mail_done(int, void *); 232 static void send_rcpt(int, void *); 233 static void rcpt_done(int, void *); 234 static void send_data(int, void *); 235 static void data_done(int, void *); 236 static void dot_done(int, void *); 237 static void send_rset(int, void *); 238 static void rset_done(int, void *); 239 static void send_quit(SESSION *); 240 static void quit_done(int, void *); 241 static void close_session(SESSION *); 242 243 /* random_interval - generate a random value in 0 .. (small) interval */ 244 245 static int random_interval(int interval) 246 { 247 return (rand() % (interval + 1)); 248 } 249 250 /* command - send an SMTP command */ 251 252 static 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 276 static 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 302 static 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 353 static 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 368 static 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 384 static 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 393 static 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 407 static 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 421 static 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 436 static 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 454 static 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 484 static 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 513 static 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 547 static 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 568 static 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 598 static 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 618 static 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 649 static 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 677 static 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 714 static 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 734 static 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 820 static 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 859 static 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 869 static 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 905 static 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 913 static 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 926 static 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 936 static 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 941 MAIL_VERSION_STAMP_DECLARE; 942 943 /* main - parse JCL and start the machine */ 944 945 int 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