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