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