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