xref: /netbsd-src/external/ibm-public/postfix/dist/src/smtpstone/smtp-source.c (revision bdc22b2e01993381dcefeff2bc9b56ca75a4235c)
1 /*	$NetBSD: smtp-source.c,v 1.2 2017/02/14 01:16:48 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   */
172 typedef 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 
182 static SESSION *last_session;		/* connect() queue tail */
183 
184  /*
185   * Structure with broken-up SMTP server response.
186   */
187 typedef struct {			/* server response */
188     int     code;			/* status */
189     char   *str;			/* text */
190     VSTRING *buf;			/* origin of text */
191 } RESPONSE;
192 
193 static VSTRING *buffer;
194 static int var_line_limit = 10240;
195 static int var_timeout = 300;
196 static const char *var_myhostname;
197 static int session_count;
198 static int message_count = 1;
199 static struct sockaddr_storage ss;
200 
201 #undef sun
202 static struct sockaddr_un sun;
203 static struct sockaddr *sa;
204 static int sa_length;
205 static int recipients = 1;
206 static char *defaddr;
207 static char *recipient;
208 static char *sender;
209 static char *message_data;
210 static int message_length;
211 static int disconnect = 1;
212 static int count = 0;
213 static int counter = 0;
214 static int send_helo_first = 1;
215 static int send_headers = 1;
216 static int connect_count = 1;
217 static int random_delay = 0;
218 static int fixed_delay = 0;
219 static int talk_lmtp = 0;
220 static char *subject = 0;
221 static int number_rcpts = 0;
222 static int allow_reject = 0;
223 
224 static void enqueue_connect(SESSION *);
225 static void start_connect(SESSION *);
226 static void connect_done(int, void *);
227 static void read_banner(int, void *);
228 static void send_helo(SESSION *);
229 static void helo_done(int, void *);
230 static void send_mail(SESSION *);
231 static void mail_done(int, void *);
232 static void send_rcpt(int, void *);
233 static void rcpt_done(int, void *);
234 static void send_data(int, void *);
235 static void data_done(int, void *);
236 static void dot_done(int, void *);
237 static void send_rset(int, void *);
238 static void rset_done(int, void *);
239 static void send_quit(SESSION *);
240 static void quit_done(int, void *);
241 static void close_session(SESSION *);
242 
243 /* random_interval - generate a random value in 0 .. (small) interval */
244 
245 static int random_interval(int interval)
246 {
247     return (rand() % (interval + 1));
248 }
249 
250 /* command - send an SMTP command */
251 
252 static 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 
276 static 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 
302 static 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. Defend against a denial of
310      * service attack by limiting the amount of multi-line text that we are
311      * willing to store.
312      */
313     if (rdata.buf == 0) {
314 	rdata.buf = vstring_alloc(100);
315 	vstring_ctl(rdata.buf, CA_VSTRING_CTL_MAXLEN(var_line_limit), 0);
316     }
317 
318     /*
319      * Censor out non-printable characters in server responses. Concatenate
320      * multi-line server responses. Separate the status code from the text.
321      * Leave further parsing up to the application.
322      */
323 #define BUF ((char *) vstring_str(buf))
324     VSTRING_RESET(rdata.buf);
325     for (;;) {
326 	smtp_get(buf, stream, var_line_limit, SMTP_GET_FLAG_SKIP);
327 	for (cp = BUF; *cp != 0; cp++)
328 	    if (!ISPRINT(*cp) && !ISSPACE(*cp))
329 		*cp = '?';
330 	cp = BUF;
331 	if (msg_verbose)
332 	    msg_info("<<< %s", cp);
333 	while (ISDIGIT(*cp))
334 	    cp++;
335 	rdata.code = (cp - BUF == 3 ? atoi(BUF) : 0);
336 	if ((more = (*cp == '-')) != 0)
337 	    cp++;
338 	while (ISSPACE(*cp))
339 	    cp++;
340 	vstring_strcat(rdata.buf, cp);
341 	if (more == 0)
342 	    break;
343 	VSTRING_ADDCH(rdata.buf, '\n');
344     }
345     VSTRING_TERMINATE(rdata.buf);
346     rdata.str = vstring_str(rdata.buf);
347     return (&rdata);
348 }
349 
350 /* exception_text - translate exceptions from the smtp_stream module */
351 
352 static char *exception_text(int except)
353 {
354     switch (except) {
355 	case SMTP_ERR_EOF:
356 	return ("lost connection");
357     case SMTP_ERR_TIME:
358 	return ("timeout");
359     default:
360 	msg_panic("exception_text: unknown exception %d", except);
361     }
362     /* NOTREACHED */
363 }
364 
365 /* startup - connect to server but do not wait */
366 
367 static void startup(SESSION *session)
368 {
369     if (message_count-- <= 0) {
370 	myfree((void *) session);
371 	session_count--;
372 	return;
373     }
374     if (session->stream == 0) {
375 	enqueue_connect(session);
376     } else {
377 	send_mail(session);
378     }
379 }
380 
381 /* start_event - invoke startup from timer context */
382 
383 static void start_event(int unused_event, void *context)
384 {
385     SESSION *session = (SESSION *) context;
386 
387     startup(session);
388 }
389 
390 /* start_another - start another session */
391 
392 static void start_another(SESSION *session)
393 {
394     if (random_delay > 0) {
395 	event_request_timer(start_event, (void *) session,
396 			    random_interval(random_delay));
397     } else if (fixed_delay > 0) {
398 	event_request_timer(start_event, (void *) session, fixed_delay);
399     } else {
400 	startup(session);
401     }
402 }
403 
404 /* enqueue_connect - queue a connection request */
405 
406 static void enqueue_connect(SESSION *session)
407 {
408     session->next = 0;
409     if (last_session == 0) {
410 	last_session = session;
411 	start_connect(session);
412     } else {
413 	last_session->next = session;
414 	last_session = session;
415     }
416 }
417 
418 /* dequeue_connect - connection request completed */
419 
420 static void dequeue_connect(SESSION *session)
421 {
422     if (session == last_session) {
423 	if (session->next != 0)
424 	    msg_panic("dequeue_connect: queue ends after last");
425 	last_session = 0;
426     } else {
427 	if (session->next == 0)
428 	    msg_panic("dequeue_connect: queue ends before last");
429 	start_connect(session->next);
430     }
431 }
432 
433 /* fail_connect - handle failed startup */
434 
435 static void fail_connect(SESSION *session)
436 {
437     if (session->connect_count-- == 1)
438 	msg_fatal("connect: %m");
439     msg_warn("connect: %m");
440     event_disable_readwrite(vstream_fileno(session->stream));
441     vstream_fclose(session->stream);
442     session->stream = 0;
443 #ifdef MISSING_USLEEP
444     doze(10);
445 #else
446     usleep(10);
447 #endif
448     start_connect(session);
449 }
450 
451 /* start_connect - start TCP handshake */
452 
453 static void start_connect(SESSION *session)
454 {
455     int     fd;
456     struct linger linger;
457 
458     /*
459      * Some systems don't set the socket error when connect() fails early
460      * (loopback) so we must deal with the error immediately, rather than
461      * retrieving it later with getsockopt(). We can't use MSG_PEEK to
462      * distinguish between server disconnect and connection refused.
463      */
464     if ((fd = socket(sa->sa_family, SOCK_STREAM, 0)) < 0)
465 	msg_fatal("socket: %m");
466     (void) non_blocking(fd, NON_BLOCKING);
467     linger.l_onoff = 1;
468     linger.l_linger = 0;
469     if (setsockopt(fd, SOL_SOCKET, SO_LINGER, (void *) &linger,
470 		   sizeof(linger)) < 0)
471 	msg_warn("setsockopt SO_LINGER %d: %m", linger.l_linger);
472     session->stream = vstream_fdopen(fd, O_RDWR);
473     event_enable_write(fd, connect_done, (void *) session);
474     smtp_timeout_setup(session->stream, var_timeout);
475     if (inet_windowsize > 0)
476 	set_inet_windowsize(fd, inet_windowsize);
477     if (sane_connect(fd, sa, sa_length) < 0 && errno != EINPROGRESS)
478 	fail_connect(session);
479 }
480 
481 /* connect_done - send message sender info */
482 
483 static void connect_done(int unused_event, void *context)
484 {
485     SESSION *session = (SESSION *) context;
486     int     fd = vstream_fileno(session->stream);
487 
488     /*
489      * Try again after some delay when the connection failed, in case they
490      * run a Mickey Mouse protocol stack.
491      */
492     if (socket_error(fd) < 0) {
493 	fail_connect(session);
494     } else {
495 	non_blocking(fd, BLOCKING);
496 	/* Disable write events. */
497 	event_disable_readwrite(fd);
498 	event_enable_read(fd, read_banner, (void *) session);
499 	dequeue_connect(session);
500 	/* Avoid poor performance when TCP MSS > VSTREAM_BUFSIZE. */
501 	if (sa->sa_family == AF_INET
502 #ifdef AF_INET6
503 	    || sa->sa_family == AF_INET6
504 #endif
505 	    )
506 	    vstream_tweak_tcp(session->stream);
507     }
508 }
509 
510 /* read_banner - receive SMTP server greeting */
511 
512 static void read_banner(int unused_event, void *context)
513 {
514     SESSION *session = (SESSION *) context;
515     RESPONSE *resp;
516     int     except;
517 
518     /*
519      * Prepare for disaster.
520      */
521     if ((except = vstream_setjmp(session->stream)) != 0)
522 	msg_fatal("%s while reading server greeting", exception_text(except));
523 
524     /*
525      * Read and parse the server's SMTP greeting banner.
526      */
527     if (((resp = response(session->stream, buffer))->code / 100) == 2) {
528 	 /* void */ ;
529     } else if (allow_reject) {
530 	msg_warn("rejected at server banner: %d %s", resp->code, resp->str);
531     } else {
532 	msg_fatal("rejected at server banner: %d %s", resp->code, resp->str);
533     }
534 
535     /*
536      * Send helo or send the envelope sender address.
537      */
538     if (send_helo_first)
539 	send_helo(session);
540     else
541 	send_mail(session);
542 }
543 
544 /* send_helo - send hostname */
545 
546 static void send_helo(SESSION *session)
547 {
548     int     except;
549     const char *NOCLOBBER protocol = (talk_lmtp ? "LHLO" : "HELO");
550 
551     /*
552      * Send the standard greeting with our hostname
553      */
554     if ((except = vstream_setjmp(session->stream)) != 0)
555 	msg_fatal("%s while sending %s", exception_text(except), protocol);
556 
557     command(session->stream, "%s %s", protocol, var_myhostname);
558 
559     /*
560      * Prepare for the next event.
561      */
562     event_enable_read(vstream_fileno(session->stream), helo_done, (void *) session);
563 }
564 
565 /* helo_done - handle HELO response */
566 
567 static void helo_done(int unused_event, void *context)
568 {
569     SESSION *session = (SESSION *) context;
570     RESPONSE *resp;
571     int     except;
572     const char *protocol = (talk_lmtp ? "LHLO" : "HELO");
573 
574     /*
575      * Get response to HELO command.
576      */
577     if ((except = vstream_setjmp(session->stream)) != 0)
578 	msg_fatal("%s while sending %s", exception_text(except), protocol);
579 
580     if ((resp = response(session->stream, buffer))->code / 100 == 2) {
581 	 /* void */ ;
582     } else if (allow_reject) {
583 	msg_warn("%s rejected: %d %s", protocol, resp->code, resp->str);
584 	if (resp->code == 421 || resp->code == 521) {
585 	    close_session(session);
586 	    return;
587 	}
588     } else {
589 	msg_fatal("%s rejected: %d %s", protocol, resp->code, resp->str);
590     }
591 
592     send_mail(session);
593 }
594 
595 /* send_mail - send envelope sender */
596 
597 static void send_mail(SESSION *session)
598 {
599     int     except;
600 
601     /*
602      * Send the envelope sender address.
603      */
604     if ((except = vstream_setjmp(session->stream)) != 0)
605 	msg_fatal("%s while sending sender", exception_text(except));
606 
607     command(session->stream, "MAIL FROM:<%s>", sender);
608 
609     /*
610      * Prepare for the next event.
611      */
612     event_enable_read(vstream_fileno(session->stream), mail_done, (void *) session);
613 }
614 
615 /* mail_done - handle MAIL response */
616 
617 static void mail_done(int unused, void *context)
618 {
619     SESSION *session = (SESSION *) context;
620     RESPONSE *resp;
621     int     except;
622 
623     /*
624      * Get response to MAIL command.
625      */
626     if ((except = vstream_setjmp(session->stream)) != 0)
627 	msg_fatal("%s while sending sender", exception_text(except));
628 
629     if ((resp = response(session->stream, buffer))->code / 100 == 2) {
630 	session->rcpt_count = recipients;
631 	session->rcpt_done = 0;
632 	session->rcpt_accepted = 0;
633 	send_rcpt(unused, context);
634     } else if (allow_reject) {
635 	msg_warn("sender rejected: %d %s", resp->code, resp->str);
636 	if (resp->code == 421 || resp->code == 521) {
637 	    close_session(session);
638 	    return;
639 	}
640 	send_rset(unused, context);
641     } else {
642 	msg_fatal("sender rejected: %d %s", resp->code, resp->str);
643     }
644 }
645 
646 /* send_rcpt - send recipient address */
647 
648 static void send_rcpt(int unused_event, void *context)
649 {
650     SESSION *session = (SESSION *) context;
651     int     except;
652 
653     /*
654      * Send envelope recipient address.
655      */
656     if ((except = vstream_setjmp(session->stream)) != 0)
657 	msg_fatal("%s while sending recipient", exception_text(except));
658 
659     if (session->rcpt_count > 1 || number_rcpts > 0)
660 	command(session->stream, "RCPT TO:<%d%s>",
661 		number_rcpts ? number_rcpts++ : session->rcpt_count,
662 		recipient);
663     else
664 	command(session->stream, "RCPT TO:<%s>", recipient);
665     session->rcpt_count--;
666     session->rcpt_done++;
667 
668     /*
669      * Prepare for the next event.
670      */
671     event_enable_read(vstream_fileno(session->stream), rcpt_done, (void *) session);
672 }
673 
674 /* rcpt_done - handle RCPT completion */
675 
676 static void rcpt_done(int unused, void *context)
677 {
678     SESSION *session = (SESSION *) context;
679     RESPONSE *resp;
680     int     except;
681 
682     /*
683      * Get response to RCPT command.
684      */
685     if ((except = vstream_setjmp(session->stream)) != 0)
686 	msg_fatal("%s while sending recipient", exception_text(except));
687 
688     if ((resp = response(session->stream, buffer))->code / 100 == 2) {
689 	session->rcpt_accepted++;
690     } else if (allow_reject) {
691 	msg_warn("recipient rejected: %d %s", resp->code, resp->str);
692 	if (resp->code == 421 || resp->code == 521) {
693 	    close_session(session);
694 	    return;
695 	}
696     } else {
697 	msg_fatal("recipient rejected: %d %s", resp->code, resp->str);
698     }
699 
700     /*
701      * Send another RCPT command or send DATA.
702      */
703     if (session->rcpt_count > 0)
704 	send_rcpt(unused, context);
705     else if (session->rcpt_accepted > 0)
706 	send_data(unused, context);
707     else
708 	send_rset(unused, context);
709 }
710 
711 /* send_data - send DATA command */
712 
713 static void send_data(int unused_event, void *context)
714 {
715     SESSION *session = (SESSION *) context;
716     int     except;
717 
718     /*
719      * Request data transmission.
720      */
721     if ((except = vstream_setjmp(session->stream)) != 0)
722 	msg_fatal("%s while sending DATA command", exception_text(except));
723     command(session->stream, "DATA");
724 
725     /*
726      * Prepare for the next event.
727      */
728     event_enable_read(vstream_fileno(session->stream), data_done, (void *) session);
729 }
730 
731 /* data_done - send message content */
732 
733 static void data_done(int unused, void *context)
734 {
735     SESSION *session = (SESSION *) context;
736     RESPONSE *resp;
737     int     except;
738     static const char *mydate;
739     static int mypid;
740 
741     /*
742      * Get response to DATA command.
743      */
744     if ((except = vstream_setjmp(session->stream)) != 0)
745 	msg_fatal("%s while sending DATA command", exception_text(except));
746     if ((resp = response(session->stream, buffer))->code == 354) {
747 	 /* see below */ ;
748     } else if (allow_reject) {
749 	msg_warn("data rejected: %d %s", resp->code, resp->str);
750 	if (resp->code == 421 || resp->code == 521) {
751 	    close_session(session);
752 	    return;
753 	}
754 	send_rset(unused, context);
755 	return;
756     } else {
757 	msg_fatal("data rejected: %d %s", resp->code, resp->str);
758     }
759 
760     /*
761      * Send basic header to keep mailers that bother to examine them happy.
762      */
763     if (send_headers) {
764 	if (mydate == 0) {
765 	    mydate = mail_date(time((time_t *) 0));
766 	    mypid = getpid();
767 	}
768 	smtp_printf(session->stream, "From: <%s>", sender);
769 	smtp_printf(session->stream, "To: <%s>", recipient);
770 	smtp_printf(session->stream, "Date: %s", mydate);
771 	smtp_printf(session->stream, "Message-Id: <%04x.%04x.%04x@%s>",
772 		    mypid, vstream_fileno(session->stream), message_count, var_myhostname);
773 	if (subject)
774 	    smtp_printf(session->stream, "Subject: %s", subject);
775 	smtp_fputs("", 0, session->stream);
776     }
777 
778     /*
779      * Send some garbage.
780      */
781     if ((except = vstream_setjmp(session->stream)) != 0)
782 	msg_fatal("%s while sending message", exception_text(except));
783     if (message_length == 0) {
784 	smtp_fputs("La de da de da 1.", 17, session->stream);
785 	smtp_fputs("La de da de da 2.", 17, session->stream);
786 	smtp_fputs("La de da de da 3.", 17, session->stream);
787 	smtp_fputs("La de da de da 4.", 17, session->stream);
788     } else {
789 
790 	/*
791 	 * XXX This may cause the process to block with message content
792 	 * larger than VSTREAM_BUFIZ bytes.
793 	 */
794 	smtp_fputs(message_data, message_length, session->stream);
795     }
796 
797     /*
798      * Send end of message and process the server response.
799      */
800     command(session->stream, ".");
801 
802     /*
803      * Update the running counter.
804      */
805     if (count) {
806 	counter++;
807 	vstream_printf("%d\r", counter);
808 	vstream_fflush(VSTREAM_OUT);
809     }
810 
811     /*
812      * Prepare for the next event.
813      */
814     event_enable_read(vstream_fileno(session->stream), dot_done, (void *) session);
815 }
816 
817 /* dot_done - send QUIT or start another transaction */
818 
819 static void dot_done(int unused_event, void *context)
820 {
821     SESSION *session = (SESSION *) context;
822     RESPONSE *resp;
823     int     except;
824 
825     /*
826      * Get response to "." command.
827      */
828     if ((except = vstream_setjmp(session->stream)) != 0)
829 	msg_fatal("%s while sending message", exception_text(except));
830     do {					/* XXX this could block */
831 	if ((resp = response(session->stream, buffer))->code / 100 == 2) {
832 	     /* void */ ;
833 	} else if (allow_reject) {
834 	    msg_warn("end of data rejected: %d %s", resp->code, resp->str);
835 	    if (resp->code == 421 || resp->code == 521) {
836 		close_session(session);
837 		return;
838 	    }
839 	} else {
840 	    msg_fatal("end of data rejected: %d %s", resp->code, resp->str);
841 	}
842     } while (talk_lmtp && --session->rcpt_done > 0);
843     session->xfer_count++;
844 
845     /*
846      * Say goodbye or send the next message.
847      */
848     if (disconnect || message_count < 1) {
849 	send_quit(session);
850     } else {
851 	event_disable_readwrite(vstream_fileno(session->stream));
852 	start_another(session);
853     }
854 }
855 
856 /* send_rset - send RSET command */
857 
858 static void send_rset(int unused_event, void *context)
859 {
860     SESSION *session = (SESSION *) context;
861 
862     command(session->stream, "RSET");
863     event_enable_read(vstream_fileno(session->stream), rset_done, (void *) session);
864 }
865 
866 /* rset_done - handle RSET reply */
867 
868 static void rset_done(int unused_event, void *context)
869 {
870     SESSION *session = (SESSION *) context;
871     RESPONSE *resp;
872     int     except;
873 
874     /*
875      * Get response to RSET command.
876      */
877     if ((except = vstream_setjmp(session->stream)) != 0)
878 	msg_fatal("%s while sending message", exception_text(except));
879     if ((resp = response(session->stream, buffer))->code / 100 == 2) {
880 	/* void */
881     } else if (allow_reject) {
882 	msg_warn("rset rejected: %d %s", resp->code, resp->str);
883 	if (resp->code == 421 || resp->code == 521) {
884 	    close_session(session);
885 	    return;
886 	}
887     } else {
888 	msg_fatal("rset rejected: %d %s", resp->code, resp->str);
889     }
890 
891     /*
892      * Say goodbye or send the next message.
893      */
894     if (disconnect || message_count < 1) {
895 	send_quit(session);
896     } else {
897 	event_disable_readwrite(vstream_fileno(session->stream));
898 	start_another(session);
899     }
900 }
901 
902 /* send_quit - send QUIT command */
903 
904 static void send_quit(SESSION *session)
905 {
906     command(session->stream, "QUIT");
907     event_enable_read(vstream_fileno(session->stream), quit_done, (void *) session);
908 }
909 
910 /* quit_done - disconnect */
911 
912 static void quit_done(int unused_event, void *context)
913 {
914     SESSION *session = (SESSION *) context;
915 
916     (void) response(session->stream, buffer);
917     event_disable_readwrite(vstream_fileno(session->stream));
918     vstream_fclose(session->stream);
919     session->stream = 0;
920     start_another(session);
921 }
922 
923 /* close_session - disconnect, for example after 421 or 521 reply */
924 
925 static void close_session(SESSION *session)
926 {
927     event_disable_readwrite(vstream_fileno(session->stream));
928     vstream_fclose(session->stream);
929     session->stream = 0;
930     start_another(session);
931 }
932 
933 /* usage - explain */
934 
935 static void usage(char *myname)
936 {
937     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);
938 }
939 
940 MAIL_VERSION_STAMP_DECLARE;
941 
942 /* main - parse JCL and start the machine */
943 
944 int     main(int argc, char **argv)
945 {
946     SESSION *session;
947     char   *host;
948     char   *port;
949     char   *path;
950     int     path_len;
951     int     sessions = 1;
952     int     ch;
953     int     i;
954     char   *buf;
955     const char *parse_err;
956     struct addrinfo *res;
957     int     aierr;
958     const char *protocols = INET_PROTO_NAME_ALL;
959     char   *message_file = 0;
960 
961     /*
962      * Fingerprint executables and core dumps.
963      */
964     MAIL_VERSION_STAMP_ALLOCATE;
965 
966     signal(SIGPIPE, SIG_IGN);
967     msg_vstream_init(argv[0], VSTREAM_ERR);
968 
969     /*
970      * Parse JCL.
971      */
972     while ((ch = GETOPT(argc, argv, "46AcC:df:F:l:Lm:M:Nor:R:s:S:t:T:vw:")) > 0) {
973 	switch (ch) {
974 	case '4':
975 	    protocols = INET_PROTO_NAME_IPV4;
976 	    break;
977 	case '6':
978 	    protocols = INET_PROTO_NAME_IPV6;
979 	    break;
980 	case 'A':
981 	    allow_reject = 1;
982 	    break;
983 	case 'c':
984 	    count++;
985 	    break;
986 	case 'C':
987 	    if ((connect_count = atoi(optarg)) <= 0)
988 		msg_fatal("bad connection count: %s", optarg);
989 	    break;
990 	case 'd':
991 	    disconnect = 0;
992 	    break;
993 	case 'f':
994 	    sender = optarg;
995 	    break;
996 	case 'F':
997 	    if (message_file == 0 && message_length > 0)
998 		msg_fatal("-l option cannot be used with -F");
999 	    message_file = optarg;
1000 	    break;
1001 	case 'l':
1002 	    if (message_file != 0)
1003 		msg_fatal("-l option cannot be used with -F");
1004 	    if ((message_length = atoi(optarg)) <= 0)
1005 		msg_fatal("bad message length: %s", optarg);
1006 	    break;
1007 	case 'L':
1008 	    talk_lmtp = 1;
1009 	    break;
1010 	case 'm':
1011 	    if ((message_count = atoi(optarg)) <= 0)
1012 		msg_fatal("bad message count: %s", optarg);
1013 	    break;
1014 	case 'M':
1015 	    if (*optarg == '[') {
1016 		if (!valid_mailhost_literal(optarg, DO_GRIPE))
1017 		    msg_fatal("bad address literal: %s", optarg);
1018 	    } else {
1019 		if (!valid_hostname(optarg, DO_GRIPE))
1020 		    msg_fatal("bad hostname: %s", optarg);
1021 	    }
1022 	    var_myhostname = optarg;
1023 	    break;
1024 	case 'N':
1025 	    number_rcpts = 1;
1026 	    break;
1027 	case 'o':
1028 	    send_helo_first = 0;
1029 	    send_headers = 0;
1030 	    break;
1031 	case 'r':
1032 	    if ((recipients = atoi(optarg)) <= 0)
1033 		msg_fatal("bad recipient count: %s", optarg);
1034 	    break;
1035 	case 'R':
1036 	    if (fixed_delay > 0)
1037 		msg_fatal("do not use -w and -R options at the same time");
1038 	    if ((random_delay = atoi(optarg)) <= 0)
1039 		msg_fatal("bad random delay: %s", optarg);
1040 	    break;
1041 	case 's':
1042 	    if ((sessions = atoi(optarg)) <= 0)
1043 		msg_fatal("bad session count: %s", optarg);
1044 	    break;
1045 	case 'S':
1046 	    subject = optarg;
1047 	    break;
1048 	case 't':
1049 	    recipient = optarg;
1050 	    break;
1051 	case 'T':
1052 	    if ((inet_windowsize = atoi(optarg)) <= 0)
1053 		msg_fatal("bad TCP window size: %s", optarg);
1054 	    break;
1055 	case 'v':
1056 	    msg_verbose++;
1057 	    break;
1058 	case 'w':
1059 	    if (random_delay > 0)
1060 		msg_fatal("do not use -w and -R options at the same time");
1061 	    if ((fixed_delay = atoi(optarg)) <= 0)
1062 		msg_fatal("bad fixed delay: %s", optarg);
1063 	    break;
1064 	default:
1065 	    usage(argv[0]);
1066 	}
1067     }
1068     if (argc - optind != 1)
1069 	usage(argv[0]);
1070 
1071     if (random_delay > 0)
1072 	srand(getpid());
1073 
1074     /*
1075      * Initialize the message content, SMTP encoded. smtp_fputs() will append
1076      * another \r\n but we don't care.
1077      */
1078     if (message_file != 0) {
1079 	VSTREAM *fp;
1080 	VSTRING *buf = vstring_alloc(100);
1081 	VSTRING *msg = vstring_alloc(100);
1082 
1083 	if ((fp = vstream_fopen(message_file, O_RDONLY, 0)) == 0)
1084 	    msg_fatal("open %s: %m", message_file);
1085 	while (vstring_get_nonl(buf, fp) != VSTREAM_EOF) {
1086 	    if (*vstring_str(buf) == '.')
1087 		VSTRING_ADDCH(msg, '.');
1088 	    vstring_memcat(msg, vstring_str(buf), VSTRING_LEN(buf));
1089 	    vstring_memcat(msg, "\r\n", 2);
1090 	}
1091 	if (vstream_ferror(fp))
1092 	    msg_fatal("read %s: %m", message_file);
1093 	vstream_fclose(fp);
1094 	vstring_free(buf);
1095 	message_length = VSTRING_LEN(msg);
1096 	message_data = vstring_export(msg);
1097 	send_headers = 0;
1098     } else if (message_length > 0) {
1099 	message_data = mymalloc(message_length);
1100 	memset(message_data, 'X', message_length);
1101 	for (i = 80; i < message_length; i += 80) {
1102 	    message_data[i - 80] = "0123456789"[(i / 80) % 10];
1103 	    message_data[i - 2] = '\r';
1104 	    message_data[i - 1] = '\n';
1105 	}
1106     }
1107 
1108     /*
1109      * Translate endpoint address to internal form.
1110      */
1111     (void) inet_proto_init("protocols", protocols);
1112     if (strncmp(argv[optind], "unix:", 5) == 0) {
1113 	path = argv[optind] + 5;
1114 	path_len = strlen(path);
1115 	if (path_len >= (int) sizeof(sun.sun_path))
1116 	    msg_fatal("unix-domain name too long: %s", path);
1117 	memset((void *) &sun, 0, sizeof(sun));
1118 	sun.sun_family = AF_UNIX;
1119 #ifdef HAS_SUN_LEN
1120 	sun.sun_len = path_len + 1;
1121 #endif
1122 	memcpy(sun.sun_path, path, path_len);
1123 	sa = (struct sockaddr *) &sun;
1124 	sa_length = sizeof(sun);
1125     } else {
1126 	if (strncmp(argv[optind], "inet:", 5) == 0)
1127 	    argv[optind] += 5;
1128 	buf = mystrdup(argv[optind]);
1129 	if ((parse_err = host_port(buf, &host, (char *) 0, &port, "smtp")) != 0)
1130 	    msg_fatal("%s: %s", argv[optind], parse_err);
1131 	if ((aierr = hostname_to_sockaddr(host, port, SOCK_STREAM, &res)) != 0)
1132 	    msg_fatal("%s: %s", argv[optind], MAI_STRERROR(aierr));
1133 	myfree(buf);
1134 	sa = (struct sockaddr *) &ss;
1135 	if (res->ai_addrlen > sizeof(ss))
1136 	    msg_fatal("address length %d > buffer length %d",
1137 		      (int) res->ai_addrlen, (int) sizeof(ss));
1138 	memcpy((void *) sa, res->ai_addr, res->ai_addrlen);
1139 	sa_length = res->ai_addrlen;
1140 #ifdef HAS_SA_LEN
1141 	sa->sa_len = sa_length;
1142 #endif
1143 	freeaddrinfo(res);
1144     }
1145 
1146     /*
1147      * Make sure the SMTP server cannot run us out of memory by sending
1148      * never-ending lines of text.
1149      */
1150     if (buffer == 0) {
1151 	buffer = vstring_alloc(100);
1152 	vstring_ctl(buffer, CA_VSTRING_CTL_MAXLEN(var_line_limit), 0);
1153     }
1154 
1155     /*
1156      * Make sure we have sender and recipient addresses.
1157      */
1158     if (var_myhostname == 0)
1159 	var_myhostname = get_hostname();
1160     if (sender == 0 || recipient == 0) {
1161 	vstring_sprintf(buffer, "foo@%s", var_myhostname);
1162 	defaddr = mystrdup(vstring_str(buffer));
1163 	if (sender == 0)
1164 	    sender = defaddr;
1165 	if (recipient == 0)
1166 	    recipient = defaddr;
1167     }
1168 
1169     /*
1170      * Start sessions.
1171      */
1172     while (sessions-- > 0) {
1173 	session = (SESSION *) mymalloc(sizeof(*session));
1174 	session->stream = 0;
1175 	session->xfer_count = 0;
1176 	session->connect_count = connect_count;
1177 	session->next = 0;
1178 	session_count++;
1179 	startup(session);
1180     }
1181     for (;;) {
1182 	event_loop(-1);
1183 	if (session_count <= 0 && message_count <= 0) {
1184 	    if (count) {
1185 		VSTREAM_PUTC('\n', VSTREAM_OUT);
1186 		vstream_fflush(VSTREAM_OUT);
1187 	    }
1188 	    exit(0);
1189 	}
1190     }
1191 }
1192