7 /* multi-threaded SMTP/LMTP test generator
10 /* \fBsmtp-source\fR [\fIoptions\fR] [\fBinet:\fR]\fIhost\fR[:\fIport\fR]
12 /* \fBsmtp-source\fR [\fIoptions\fR] \fBunix:\fIpathname\fR
14 /* \fBsmtp-source\fR connects to the named \fIhost\fR and TCP \fIport\fR
16 /* and sends one or more messages to it, either sequentially
17 /* or in parallel. The program speaks either SMTP (default) or
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.
27 /* Connect to the server with IPv4. This option has no effect when
28 /* Postfix is built without IPv6 support.
30 /* Connect to the server with IPv6. This option is not available when
31 /* Postfix is built without IPv6 support.
33 /* Don't abort when the server sends something other than the
34 /* expected positive reply code.
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.
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.
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.
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.
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
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.
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.
99 /* No SMTP command pipelining support.
101 /* smtp-sink(1), SMTP/LMTP message dump
105 /* The Secure Mailer license must be distributed with this software.
108 /* IBM T.J. Watson Research
110 /* Yorktown Heights, NY 10598, USA
113 /* System library. */
115 #include <sys_defs.h>
116 #include <sys/socket.h>
117 #include <sys/wait.h>
118 #include <netinet/in.h>
129 /* Utility library. */
132 #include <msg_vstream.h>
135 #include <vstring_vstream.h>
136 #include <get_hostname.h>
137 #include <split_at.h>
139 #include <mymalloc.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 */
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 */
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
;
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
;
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
,...)
251 * Optionally, log the command before actually sending, so we can see
252 * what the program is trying to do.
255 buf
= vstring_alloc(100);
257 vstring_vsprintf(buf
, fmt
, ap
);
259 msg_info("%s", vstring_str(buf
));
263 smtp_vprintf(stream
, fmt
, ap
);
268 /* socket_error - look up and reset the last socket error */
270 static int socket_error(int sock
)
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.
280 error_len
= sizeof(error
);
281 if (getsockopt(sock
, SOL_SOCKET
, SO_ERROR
, (char *) &error
, &error_len
) < 0)
294 /* response - read and process SMTP server response */
296 static RESPONSE
*response(VSTREAM
*stream
, VSTRING
*buf
)
298 static RESPONSE rdata
;
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
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
);
320 smtp_get(buf
, stream
, var_line_limit
);
321 for (cp
= BUF
; *cp
!= 0; cp
++)
322 if (!ISPRINT(*cp
) && !ISSPACE(*cp
))
326 msg_info("<<< %s", cp
);
329 rdata
.code
= (cp
- BUF
== 3 ? atoi(BUF
) : 0);
330 if ((more
= (*cp
== '-')) != 0)
334 vstring_strcat(rdata
.buf
, cp
);
337 VSTRING_ADDCH(rdata
.buf
, '\n');
339 VSTRING_TERMINATE(rdata
.buf
);
340 rdata
.str
= vstring_str(rdata
.buf
);
344 /* exception_text - translate exceptions from the smtp_stream module */
346 static char *exception_text(int except
)
350 return ("lost connection");
354 msg_panic("exception_text: unknown exception %d", except
);
359 /* startup - connect to server but do not wait */
361 static void startup(SESSION
*session
)
363 if (message_count
-- <= 0) {
364 myfree((char *) session
);
368 if (session
->stream
== 0) {
369 enqueue_connect(session
);
375 /* start_event - invoke startup from timer context */
377 static void start_event(int unused_event
, char *context
)
379 SESSION
*session
= (SESSION
*) context
;
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
);
398 /* enqueue_connect - queue a connection request */
400 static void enqueue_connect(SESSION
*session
)
403 if (last_session
== 0) {
404 last_session
= session
;
405 start_connect(session
);
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");
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
);
437 #ifdef MISSING_USLEEP
442 start_connect(session
);
445 /* start_connect - start TCP handshake */
447 static void start_connect(SESSION
*session
)
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
);
463 if (setsockopt(fd
, SOL_SOCKET
, SO_LINGER
, (char *) &linger
,
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
);
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
497 || sa
->sa_family
== AF_INET6
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
;
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) {
523 } else if (allow_reject
) {
524 msg_warn("rejected at server banner: %d %s", resp
->code
, resp
->str
);
526 msg_fatal("rejected at server banner: %d %s", resp
->code
, resp
->str
);
530 * Send helo or send the envelope sender address.
538 /* send_helo - send hostname */
540 static void send_helo(SESSION
*session
)
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
;
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) {
576 } else if (allow_reject
) {
577 msg_warn("%s rejected: %d %s", protocol
, resp
->code
, resp
->str
);
579 msg_fatal("%s rejected: %d %s", protocol
, resp
->code
, resp
->str
);
585 /* send_mail - send envelope sender */
587 static void send_mail(SESSION
*session
)
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
;
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
);
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
;
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
,
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
;
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
);
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
);
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
;
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
;
720 static const char *mydate
;
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) {
730 } else if (allow_reject
) {
731 msg_warn("data rejected: %d %s", resp
->code
, resp
->str
);
732 send_rset(unused
, context
);
735 msg_fatal("data rejected: %d %s", resp
->code
, resp
->str
);
739 * Send basic header to keep mailers that bother to examine them happy.
743 mydate
= mail_date(time((time_t *) 0));
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
);
752 smtp_printf(session
->stream
, "Subject: %s", subject
);
753 smtp_fputs("", 0, session
->stream
);
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
);
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.
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
;
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) {
811 } else if (allow_reject
) {
812 msg_warn("end of data rejected: %d %s", resp
->code
, resp
->str
);
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) {
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
;
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) {
855 } else if (allow_reject
) {
856 msg_warn("rset rejected: %d %s", resp
->code
, resp
->str
);
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) {
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
);
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
)
915 const char *parse_err
;
916 struct addrinfo
*res
;
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
);
933 while ((ch
= GETOPT(argc
, argv
, "46AcC:df:F:l:Lm:M:Nor:R:s:S:t:T:vw:")) > 0) {
936 protocols
= INET_PROTO_NAME_IPV4
;
939 protocols
= INET_PROTO_NAME_IPV6
;
948 if ((connect_count
= atoi(optarg
)) <= 0)
949 msg_fatal("bad connection count: %s", optarg
);
958 if (message_file
== 0 && message_length
> 0)
959 msg_fatal("-l option cannot be used with -F");
960 message_file
= optarg
;
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
);
972 if ((message_count
= atoi(optarg
)) <= 0)
973 msg_fatal("bad message count: %s", optarg
);
976 if (*optarg
== '[') {
977 if (!valid_mailhost_literal(optarg
, DO_GRIPE
))
978 msg_fatal("bad address literal: %s", optarg
);
980 if (!valid_hostname(optarg
, DO_GRIPE
))
981 msg_fatal("bad hostname: %s", optarg
);
983 var_myhostname
= optarg
;
993 if ((recipients
= atoi(optarg
)) <= 0)
994 msg_fatal("bad recipient count: %s", optarg
);
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
);
1003 if ((sessions
= atoi(optarg
)) <= 0)
1004 msg_fatal("bad session count: %s", optarg
);
1013 if ((inet_windowsize
= atoi(optarg
)) <= 0)
1014 msg_fatal("bad TCP window size: %s", optarg
);
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
);
1029 if (argc
- optind
!= 1)
1032 if (random_delay
> 0)
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) {
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
);
1056 message_length
= VSTRING_LEN(msg
);
1057 message_data
= vstring_export(msg
);
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
;
1081 sun
.sun_len
= path_len
+ 1;
1083 memcpy(sun
.sun_path
, path
, path_len
);
1084 sa
= (struct sockaddr
*) & sun
;
1085 sa_length
= sizeof(sun
);
1087 if (strncmp(argv
[optind
], "inet:", 5) == 0)
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
));
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
;
1102 sa
->sa_len
= sa_length
;
1108 * Make sure the SMTP server cannot run us out of memory by sending
1109 * never-ending lines of text.
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
));
1127 recipient
= defaddr
;
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
;
1144 if (session_count
<= 0 && message_count
<= 0) {
1146 VSTREAM_PUTC('\n', VSTREAM_OUT
);
1147 vstream_fflush(VSTREAM_OUT
);