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