No empty .Rs/.Re
[netbsd-mini2440.git] / external / ibm-public / postfix / dist / src / smtpstone / smtp-source.c
blobbdccd488f0a7f13fd03f777d3ebc4785664ca6b4
1 /* $NetBSD$ */
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]
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.
22 /* Note: this is an unsupported test program. No attempt is made
23 /* to maintain compatibility between successive versions.
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 /*--*/
113 /* System library. */
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>
129 /* Utility library. */
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>
149 /* Global library. */
151 #include <smtp_stream.h>
152 #include <mail_date.h>
153 #include <mail_version.h>
155 /* Application-specific. */
158 * Per-session data structure with state.
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.
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;
176 static SESSION *last_session; /* connect() queue tail */
179 * Structure with broken-up SMTP server response.
181 typedef struct { /* server response */
182 int code; /* status */
183 char *str; /* text */
184 VSTRING *buf; /* origin of text */
185 } RESPONSE;
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;
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;
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 *);
236 /* random_interval - generate a random value in 0 .. (small) interval */
238 static int random_interval(int interval)
240 return (rand() % (interval + 1));
243 /* command - send an SMTP command */
245 static void command(VSTREAM *stream, char *fmt,...)
247 VSTRING *buf;
248 va_list ap;
251 * Optionally, log the command before actually sending, so we can see
252 * what the program is trying to do.
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);
262 va_start(ap, fmt);
263 smtp_vprintf(stream, fmt, ap);
264 va_end(ap);
265 smtp_flush(stream);
268 /* socket_error - look up and reset the last socket error */
270 static int socket_error(int sock)
272 int error;
273 SOCKOPT_SIZE error_len;
276 * Some Solaris 2 versions have getsockopt() itself return the error,
277 * instead of returning it via the parameter list.
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);
289 * No problems.
291 return (0);
294 /* response - read and process SMTP server response */
296 static RESPONSE *response(VSTREAM *stream, VSTRING *buf)
298 static RESPONSE rdata;
299 int more;
300 char *cp;
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.
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);
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.
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');
339 VSTRING_TERMINATE(rdata.buf);
340 rdata.str = vstring_str(rdata.buf);
341 return (&rdata);
344 /* exception_text - translate exceptions from the smtp_stream module */
346 static char *exception_text(int except)
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);
356 /* NOTREACHED */
359 /* startup - connect to server but do not wait */
361 static void startup(SESSION *session)
363 if (message_count-- <= 0) {
364 myfree((char *) session);
365 session_count--;
366 return;
368 if (session->stream == 0) {
369 enqueue_connect(session);
370 } else {
371 send_mail(session);
375 /* start_event - invoke startup from timer context */
377 static void start_event(int unused_event, char *context)
379 SESSION *session = (SESSION *) context;
381 startup(session);
384 /* start_another - start another session */
386 static void start_another(SESSION *session)
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);
398 /* enqueue_connect - queue a connection request */
400 static void enqueue_connect(SESSION *session)
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;
412 /* dequeue_connect - connection request completed */
414 static void dequeue_connect(SESSION *session)
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);
427 /* fail_connect - handle failed startup */
429 static void fail_connect(SESSION *session)
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);
445 /* start_connect - start TCP handshake */
447 static void start_connect(SESSION *session)
449 int fd;
450 struct linger linger;
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.
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);
475 /* connect_done - send message sender info */
477 static void connect_done(int unused_event, char *context)
479 SESSION *session = (SESSION *) context;
480 int fd = vstream_fileno(session->stream);
483 * Try again after some delay when the connection failed, in case they
484 * run a Mickey Mouse protocol stack.
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
500 vstream_tweak_tcp(session->stream);
504 /* read_banner - receive SMTP server greeting */
506 static void read_banner(int unused_event, char *context)
508 SESSION *session = (SESSION *) context;
509 RESPONSE *resp;
510 int except;
513 * Prepare for disaster.
515 if ((except = vstream_setjmp(session->stream)) != 0)
516 msg_fatal("%s while reading server greeting", exception_text(except));
519 * Read and parse the server's SMTP greeting banner.
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);
530 * Send helo or send the envelope sender address.
532 if (send_helo_first)
533 send_helo(session);
534 else
535 send_mail(session);
538 /* send_helo - send hostname */
540 static void send_helo(SESSION *session)
542 int except;
543 const char *NOCLOBBER protocol = (talk_lmtp ? "LHLO" : "HELO");
546 * Send the standard greeting with our hostname
548 if ((except = vstream_setjmp(session->stream)) != 0)
549 msg_fatal("%s while sending %s", exception_text(except), protocol);
551 command(session->stream, "%s %s", protocol, var_myhostname);
554 * Prepare for the next event.
556 event_enable_read(vstream_fileno(session->stream), helo_done, (char *) session);
559 /* helo_done - handle HELO response */
561 static void helo_done(int unused_event, char *context)
563 SESSION *session = (SESSION *) context;
564 RESPONSE *resp;
565 int except;
566 const char *protocol = (talk_lmtp ? "LHLO" : "HELO");
569 * Get response to HELO command.
571 if ((except = vstream_setjmp(session->stream)) != 0)
572 msg_fatal("%s while sending %s", exception_text(except), protocol);
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);
582 send_mail(session);
585 /* send_mail - send envelope sender */
587 static void send_mail(SESSION *session)
589 int except;
592 * Send the envelope sender address.
594 if ((except = vstream_setjmp(session->stream)) != 0)
595 msg_fatal("%s while sending sender", exception_text(except));
597 command(session->stream, "MAIL FROM:<%s>", sender);
600 * Prepare for the next event.
602 event_enable_read(vstream_fileno(session->stream), mail_done, (char *) session);
605 /* mail_done - handle MAIL response */
607 static void mail_done(int unused, char *context)
609 SESSION *session = (SESSION *) context;
610 RESPONSE *resp;
611 int except;
614 * Get response to MAIL command.
616 if ((except = vstream_setjmp(session->stream)) != 0)
617 msg_fatal("%s while sending sender", exception_text(except));
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);
632 /* send_rcpt - send recipient address */
634 static void send_rcpt(int unused_event, char *context)
636 SESSION *session = (SESSION *) context;
637 int except;
640 * Send envelope recipient address.
642 if ((except = vstream_setjmp(session->stream)) != 0)
643 msg_fatal("%s while sending recipient", exception_text(except));
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++;
655 * Prepare for the next event.
657 event_enable_read(vstream_fileno(session->stream), rcpt_done, (char *) session);
660 /* rcpt_done - handle RCPT completion */
662 static void rcpt_done(int unused, char *context)
664 SESSION *session = (SESSION *) context;
665 RESPONSE *resp;
666 int except;
669 * Get response to RCPT command.
671 if ((except = vstream_setjmp(session->stream)) != 0)
672 msg_fatal("%s while sending recipient", exception_text(except));
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);
683 * Send another RCPT command or send DATA.
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);
693 /* send_data - send DATA command */
695 static void send_data(int unused_event, char *context)
697 SESSION *session = (SESSION *) context;
698 int except;
701 * Request data transmission.
703 if ((except = vstream_setjmp(session->stream)) != 0)
704 msg_fatal("%s while sending DATA command", exception_text(except));
705 command(session->stream, "DATA");
708 * Prepare for the next event.
710 event_enable_read(vstream_fileno(session->stream), data_done, (char *) session);
713 /* data_done - send message content */
715 static void data_done(int unused, char *context)
717 SESSION *session = (SESSION *) context;
718 RESPONSE *resp;
719 int except;
720 static const char *mydate;
721 static int mypid;
724 * Get response to DATA command.
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);
739 * Send basic header to keep mailers that bother to examine them happy.
741 if (send_headers) {
742 if (mydate == 0) {
743 mydate = mail_date(time((time_t *) 0));
744 mypid = getpid();
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);
757 * Send some garbage.
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 {
769 * XXX This may cause the process to block with message content
770 * larger than VSTREAM_BUFIZ bytes.
772 smtp_fputs(message_data, message_length, session->stream);
776 * Send end of message and process the server response.
778 command(session->stream, ".");
781 * Update the running counter.
783 if (count) {
784 counter++;
785 vstream_printf("%d\r", counter);
786 vstream_fflush(VSTREAM_OUT);
790 * Prepare for the next event.
792 event_enable_read(vstream_fileno(session->stream), dot_done, (char *) session);
795 /* dot_done - send QUIT or start another transaction */
797 static void dot_done(int unused_event, char *context)
799 SESSION *session = (SESSION *) context;
800 RESPONSE *resp;
801 int except;
804 * Get response to "." command.
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);
816 } while (talk_lmtp && --session->rcpt_done > 0);
817 session->xfer_count++;
820 * Say goodbye or send the next message.
822 if (disconnect || message_count < 1) {
823 send_quit(session);
824 } else {
825 event_disable_readwrite(vstream_fileno(session->stream));
826 start_another(session);
830 /* send_rset - send RSET command */
832 static void send_rset(int unused_event, char *context)
834 SESSION *session = (SESSION *) context;
836 command(session->stream, "RSET");
837 event_enable_read(vstream_fileno(session->stream), rset_done, (char *) session);
840 /* rset_done - handle RSET reply */
842 static void rset_done(int unused_event, char *context)
844 SESSION *session = (SESSION *) context;
845 RESPONSE *resp;
846 int except;
849 * Get response to RSET command.
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);
862 * Say goodbye or send the next message.
864 if (disconnect || message_count < 1) {
865 send_quit(session);
866 } else {
867 event_disable_readwrite(vstream_fileno(session->stream));
868 start_another(session);
872 /* send_quit - send QUIT command */
874 static void send_quit(SESSION *session)
876 command(session->stream, "QUIT");
877 event_enable_read(vstream_fileno(session->stream), quit_done, (char *) session);
880 /* quit_done - disconnect */
882 static void quit_done(int unused_event, char *context)
884 SESSION *session = (SESSION *) context;
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);
893 /* usage - explain */
895 static void usage(char *myname)
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);
900 MAIL_VERSION_STAMP_DECLARE;
902 /* main - parse JCL and start the machine */
904 int main(int argc, char **argv)
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;
923 * Fingerprint executables and core dumps.
925 MAIL_VERSION_STAMP_ALLOCATE;
927 signal(SIGPIPE, SIG_IGN);
928 msg_vstream_init(argv[0], VSTREAM_ERR);
931 * Parse JCL.
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);
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]);
1029 if (argc - optind != 1)
1030 usage(argv[0]);
1032 if (random_delay > 0)
1033 srand(getpid());
1036 * Initialize the message content, SMTP encoded. smtp_fputs() will append
1037 * another \r\n but we don't care.
1039 if (message_file != 0) {
1040 VSTREAM *fp;
1041 VSTRING *buf = vstring_alloc(100);
1042 VSTRING *msg = vstring_alloc(100);
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);
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';
1070 * Translate endpoint address to internal form.
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);
1108 * Make sure the SMTP server cannot run us out of memory by sending
1109 * never-ending lines of text.
1111 if (buffer == 0) {
1112 buffer = vstring_alloc(100);
1113 vstring_ctl(buffer, VSTRING_CTL_MAXLEN, (ssize_t) var_line_limit, 0);
1117 * Make sure we have sender and recipient addresses.
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;
1131 * Start sessions.
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);
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);
1149 exit(0);