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