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