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