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