268 #include <sys/socket.h>
269 #include <sys/wait.h>
270 #include <sys/stat.h>
280 #ifdef STRCASECMP_IN_STRINGS_H
335 #define ST_CR_LF_DOT 3
336 #define ST_CR_LF_DOT_CR 4
337 #define ST_CR_LF_DOT_CR_LF 5
339 #define PUSH_BACK_PEEK(state) (*(state)->push_back_ptr != 0)
340 #define PUSH_BACK_GET(state) (*(state)->push_back_ptr++)
341 #define PUSH_BACK_SET(state, text) ((state)->push_back_ptr = (text))
343 #ifndef DEF_MAX_CLIENT_COUNT
344 #define DEF_MAX_CLIENT_COUNT 256
347 #define SOFT_ERROR_RESP "450 4.3.0 Error: command failed"
348 #define HARD_ERROR_RESP "500 5.3.0 Error: command failed"
354 #define SMTP_FLUSH(fp) do { \
355 if (vstream_peek(fp) <= 0 && readable(vstream_fileno(fp)) <= 0) \
359 static int var_tmout = 100;
360 static int var_max_line_length = 2048;
367 static void read_timeout(
int,
void *);
368 static void read_event(
int,
void *);
369 static int show_count;
370 static int sess_count;
371 static int quit_count;
372 static int mesg_count;
373 static int max_quit_count;
374 static int max_msg_quit_count;
375 static int disable_pipelining;
376 static int disable_8bitmime;
377 static int disable_esmtp;
378 static int enable_lmtp;
379 static int pretend_pix;
380 static int disable_saslauth;
381 static int disable_xclient;
382 static int disable_xforward;
383 static int disable_enh_status;
384 static int disable_dsn;
386 static int client_count;
388 static int abort_delay = -1;
389 static int data_read_delay = 0;
391 static char *single_template;
392 static char *shared_template;
397 #define STR(x) vstring_str(x)
401 static void do_stats(
void)
404 sess_count, quit_count, mesg_count);
435 lt = localtime(&start_time);
444 static void make_parent_dir(
const char *path, mode_t mode)
457 const char *myname =
"mail_file_open";
470 path_buf = exp_path_template(single_template, state->
start_time);
478 #define ID_FORMAT "%08x"
482 msg_fatal(
"%s: something is looping", myname);
486 O_RDWR | O_CREAT | O_EXCL,
489 }
else if (errno == EEXIST) {
491 }
else if (errno == ENOENT) {
492 make_parent_dir(
STR(path_buf), 0755);
502 if (shared_template != 0 && unlink(
STR(path_buf)) < 0)
520 static void mail_file_finish_header(
SINK_STATE *state)
538 static void mail_file_cleanup(
SINK_STATE *state)
546 static void mail_file_finish(
SINK_STATE *state)
552 if (shared_template) {
553 const char *out_path;
560 out_path =
STR(exp_path_template(shared_template, state->
start_time));
565 #define OUT_OPEN_FLAGS (O_WRONLY | O_CREAT | O_APPEND)
566 #define OUT_OPEN_MODE 0644
569 == 0 && errno == ENOENT) {
570 make_parent_dir(out_path, 0755);
588 msg_fatal(
"append file %s: %m", out_path);
593 msg_fatal(
"append file %s: %m", out_path);
595 mail_file_cleanup(state);
600 static void mail_file_reset(
SINK_STATE *state)
602 if (shared_template == 0
606 mail_file_cleanup(state);
616 mail_file_reset(state);
621 static void ehlo_response(
SINK_STATE *state,
const char *args)
623 #define SKIP(cp, cond) do { \
624 for (; *cp && (cond); cp++) \
629 mail_cmd_reset(state);
630 if (enable_lmtp == 0)
633 if (!disable_pipelining)
635 if (!disable_8bitmime)
637 if (!disable_saslauth)
639 if (!disable_xclient)
641 if (!disable_xforward)
643 if (!disable_enh_status)
650 if (single_template) {
660 static void helo_response(
SINK_STATE *state,
const char *args)
663 mail_cmd_reset(state);
667 if (single_template) {
677 static void ok_response(
SINK_STATE *state,
const char *unused_args)
685 static void rset_response(
SINK_STATE *state,
const char *unused_args)
687 mail_cmd_reset(state);
694 static void mail_response(
SINK_STATE *state,
const char *args)
705 if (single_template) {
706 mail_file_open(state);
707 SKIP(args, *args !=
':');
708 SKIP(args, *args ==
':');
716 static void rcpt_response(
SINK_STATE *state,
const char *args)
728 SKIP(args, *args !=
':');
729 SKIP(args, *args ==
':');
737 static void abort_event(
int unused_event,
void *context)
748 static void delay_read_event(
int event,
void *context)
753 msg_panic(
"delay_read_event: non-timer event %d", event);
761 static void delay_read(
SINK_STATE *state,
int delay)
770 static void data_response(
SINK_STATE *state,
const char *unused_args)
781 if (abort_delay < 0) {
784 if (data_read_delay > 0)
785 delay_read(state, data_read_delay);
793 mail_file_finish_header(state);
801 while (state->
rcpts-- > 0)
814 while (state->
rcpts-- > 0)
824 static void dot_response(
SINK_STATE *state,
const char *unused_args)
827 while (state->
rcpts-- > 0)
837 static void quit_response(
SINK_STATE *state,
const char *unused_args)
847 static void conn_response(
SINK_STATE *state,
const char *unused_args)
851 else if (disable_esmtp)
860 static void delay_event(
int unused_event,
void *context)
908 static struct data_trans data_trans[] = {
915 struct data_trans *dp;
924 for (dp = data_trans; dp->state != state->
data_state; dp++)
935 else if (ch == data_trans[0].want)
950 mail_file_finish(state);
951 mail_cmd_reset(state);
952 if (show_count || max_msg_quit_count > 0) {
956 if (max_msg_quit_count > 0 && mesg_count >= max_msg_quit_count)
986 #define FLAG_ENABLE (1<<0)
987 #define FLAG_SYSLOG (1<<1)
988 #define FLAG_HARD_ERR (1<<2)
989 #define FLAG_SOFT_ERR (1<<3)
990 #define FLAG_DISCONNECT (1<<4)
991 #define FLAG_CLOSE (1<<5)
994 "connect", conn_response, hard_err_resp, soft_err_resp, 0, 0, 0,
995 "helo", helo_response, hard_err_resp, soft_err_resp, 0, 0, 0,
996 "ehlo", ehlo_response, hard_err_resp, soft_err_resp, 0, 0, 0,
997 "lhlo", ehlo_response, hard_err_resp, soft_err_resp, 0, 0, 0,
998 "xclient", ok_response, hard_err_resp, soft_err_resp,
FLAG_ENABLE, 0, 0,
999 "xforward", ok_response, hard_err_resp, soft_err_resp,
FLAG_ENABLE, 0, 0,
1000 "auth", ok_response, hard_err_resp, soft_err_resp,
FLAG_ENABLE, 0, 0,
1001 "mail", mail_response, hard_err_resp, soft_err_resp,
FLAG_ENABLE, 0, 0,
1002 "rcpt", rcpt_response, hard_err_resp, soft_err_resp,
FLAG_ENABLE, 0, 0,
1003 "data", data_response, hard_err_resp, soft_err_resp,
FLAG_ENABLE, 0, 0,
1004 ".", dot_response, dot_resp_hard, dot_resp_soft,
FLAG_ENABLE, 0, 0,
1005 "rset", rset_response, hard_err_resp, soft_err_resp,
FLAG_ENABLE, 0, 0,
1006 "noop", ok_response, hard_err_resp, soft_err_resp,
FLAG_ENABLE, 0, 0,
1007 "vrfy", ok_response, hard_err_resp, soft_err_resp,
FLAG_ENABLE, 0, 0,
1008 "quit", quit_response, hard_err_resp, soft_err_resp,
FLAG_ENABLE, 0, 0,
1014 static void reset_cmd_flags(
const char *cmd,
int flags)
1018 for (cmdp = command_table; cmdp->
name != 0; cmdp++)
1021 if (cmdp->
name == 0)
1023 cmdp->
flags &= ~flags;
1028 static void set_cmd_flags(
const char *cmd,
int flags)
1032 for (cmdp = command_table; cmdp->
name != 0; cmdp++)
1035 if (cmdp->
name == 0)
1037 cmdp->
flags |= flags;
1042 static void set_cmds_flags(
const char *cmds,
int flags)
1050 set_cmd_flags(cmd, flags);
1056 static void set_cmd_delay(
const char *cmd,
int delay,
int odds)
1060 for (cmdp = command_table; cmdp->
name != 0; cmdp++)
1063 if (cmdp->
name == 0)
1067 msg_fatal(
"non-positive '%s' delay", cmd);
1068 if (odds < 0 || odds > 99)
1069 msg_fatal(
"delay odds for '%s' out of range", cmd);
1071 cmdp->
delay = delay;
1077 static void set_cmd_delay_arg(
char *arg)
1088 if (cmd == 0 || delay == 0)
1089 msg_fatal(
"invalid command delay argument: %s", arg);
1091 set_cmd_delay(cmd, atoi(delay), odds ? atoi(odds) : 0);
1098 const char *command,
const char *args)
1102 syslog(LOG_INFO,
"%s %.100s", command, args);
1117 if (cmdp->
delay > 0) {
1118 int delay = cmdp->
delay;
1123 delay += cmdp->
delay)
1133 if (cmdp->
response == quit_response)
1151 static struct cmd_trans cmd_trans[] = {
1156 struct cmd_trans *cp;
1163 #define NEXT_CHAR(state) \
1164 (PUSH_BACK_PEEK(state) ? PUSH_BACK_GET(state) : VSTREAM_GETC(state->stream))
1186 for (cp = cmd_trans; cp->state != state->
data_state; cp++)
1191 else if (ch == cmd_trans[0].want)
1229 if ((command =
mystrtok(&ptr,
" \t")) == 0) {
1234 for (cmdp = command_table; cmdp->
name != 0; cmdp++)
1242 return (command_resp(state, cmdp, command,
printable(ptr,
'?')));
1247 static void read_timeout(
int unused_event,
void *context)
1262 static void read_event(
int unused_event,
void *context)
1294 if (state->
read_fn(state) < 0) {
1310 static void connect_event(
int,
void *);
1328 mail_cmd_reset(state);
1332 if (max_quit_count > 0 && quit_count >= max_quit_count)
1334 if (client_count-- == max_client_count)
1340 static void connect_event(
int unused_event,
void *unused_context)
1342 struct sockaddr_storage ss;
1344 struct sockaddr *sa = (
struct sockaddr *) &ss;
1350 if (++client_count == max_client_count)
1361 sa->sa_family == AF_LOCAL ?
"AF_LOCAL" :
1363 sa->sa_family == AF_UNIX ?
"AF_UNIX" :
1365 sa->sa_family == AF_INET ?
"AF_INET" :
1367 sa->sa_family == AF_INET6 ?
"AF_INET6" :
1369 "unknown protocol family",
1375 state->
read_fn = command_read;
1385 if (sa->sa_family == AF_INET6)
1420 if (command_resp(state, command_table,
"connect",
"") < 0)
1422 else if (command_table->
delay == 0) {
1432 static void usage(
char *myname)
1434 msg_fatal(
"usage: %s [-468acCeEFLpPv] [-A abort_delay] [-b soft_bounce_reply] [-B hard_bounce_reply] [-d dump-template] [-D dump-template] [-f commands] [-h hostname] [-m max_concurrency] [-M message_quit_count] [-n quit_count] [-q commands] [-r commands] [-R root-dir] [-s commands] [-S start-string] [-u user_privs] [-w delay] [host]:port backlog", myname);
1445 const char *root_dir = 0;
1446 const char *user_privs = 0;
1456 signal(SIGPIPE, SIG_IGN);
1466 while ((ch =
GETOPT(argc, argv,
"468aA:b:B:cCd:D:eEf:Fh:H:Ln:m:M:NpPq:Q:r:R:s:S:t:T:u:vw:W:")) > 0) {
1475 disable_8bitmime = 1;
1478 disable_saslauth = 1;
1481 if (!
alldig(optarg) || (abort_delay = atoi(optarg)) < 0)
1485 if (optarg[0] !=
'4' || strspn(optarg,
"0123456789") != 3) {
1486 msg_error(
"bad soft error reply: %s", optarg);
1489 soft_error_resp = optarg;
1492 if (optarg[0] !=
'5' || strspn(optarg,
"0123456789") != 3) {
1493 msg_error(
"bad hard error reply: %s", optarg);
1496 hard_error_resp = optarg;
1502 disable_xclient = 1;
1506 single_template = optarg;
1509 shared_template = optarg;
1515 disable_enh_status = 1;
1519 disable_pipelining = 1;
1522 disable_xforward = 1;
1529 if ((data_read_delay = atoi(optarg)) <= 0)
1530 msg_fatal(
"bad data read delay: %s", optarg);
1536 if ((max_client_count = atoi(optarg)) <= 0)
1537 msg_fatal(
"bad concurrency limit: %s", optarg);
1540 if ((max_msg_quit_count = atoi(optarg)) <= 0)
1541 msg_fatal(
"bad message quit count: %s", optarg);
1544 if ((max_quit_count = atoi(optarg)) <= 0)
1545 msg_fatal(
"bad quit count: %s", optarg);
1551 disable_pipelining = 1;
1565 disable_pipelining = 1;
1571 openlog(
basename(argv[0]), LOG_PID, LOG_MAIL);
1579 if ((var_tmout = atoi(optarg)) <= 0)
1584 msg_fatal(
"bad TCP window size: %s", optarg);
1587 user_privs = optarg;
1593 if ((delay = atoi(optarg)) <= 0)
1595 set_cmd_delay(
"data", delay, 0);
1598 set_cmd_delay_arg(optarg);
1604 if (argc - optind != 2)
1606 if ((backlog = atoi(argv[optind + 1])) <= 0)
1608 if (single_template && shared_template)
1609 msg_fatal(
"use only one of -d or -D, but not both");
1610 if (geteuid() == 0 && user_privs == 0)
1611 msg_fatal(
"-u option is required if running as root");
1618 set_cmds_flags(enable_lmtp ?
"lhlo" :
1619 disable_esmtp ?
"helo" :
1622 if (strncmp(argv[optind],
"unix:", 5) == 0) {
1625 if (strncmp(argv[optind],
"inet:", 5) == 0)
1632 if (single_template)
1633 mysrand((
int) time((time_t *) 0));
1634 else if (shared_template)
1635 single_template = shared_template;
void event_enable_read(int fd, EVENT_NOTIFY_RDWR_FN callback, void *context)
void msg_error(const char *fmt,...)
#define PUSH_BACK_SET(state, text)
char * mystrdup(const char *str)
int inet_listen(const char *addr, int backlog, int block_mode)
struct SINK_COMMAND SINK_COMMAND
const char * mail_date(time_t when)
struct SINK_STATE SINK_STATE
MAI_HOSTADDR_STR client_addr
NORETURN msg_panic(const char *fmt,...)
void chroot_uid(const char *root_dir, const char *user_name)
INET_PROTO_INFO * inet_proto_init(const char *context, const char *protocols)
MAIL_VERSION_STAMP_DECLARE
VSTRING * vstring_truncate(VSTRING *vp, ssize_t len)
#define SOCKADDR_TO_HOSTADDR(sa, salen, host, port, sock)
#define INET_PROTO_NAME_ALL
int alldig(const char *string)
#define vstream_setjmp(stream)
char * mystrtok(char **src, const char *sep)
#define DEF_MAX_CLIENT_COUNT
char * sane_dirname(VSTRING *bp, const char *path)
const char * client_proto
VSTREAM * vstream_fopen(const char *path, int flags, mode_t mode)
#define VSTRING_TERMINATE(vp)
#define VSTRING_ADDCH(vp, ch)
char buf[MAI_HOSTADDR_STRSIZE]
VSTRING * vstring_sprintf_append(VSTRING *vp, const char *format,...)
VSTREAM * vstream_fprintf(VSTREAM *stream, const char *fmt,...)
void(* soft_response)(SINK_STATE *)
int main(int argc, char **argv)
void(* hard_response)(SINK_STATE *)
int vstream_fclose(VSTREAM *stream)
void event_loop(int delay)
VSTREAM * vstream_printf(const char *fmt,...)
#define VSTRING_RESET(vp)
void msg_warn(const char *fmt,...)
int(* read_fn)(struct SINK_STATE *)
VSTRING * vstring_alloc(ssize_t len)
#define smtp_timeout_setup(stream, timeout)
unsigned char * sa_family_list
#define MAIL_VERSION_STAMP_ALLOCATE
int sane_accept(int sock, struct sockaddr *sa, SOCKADDR_SIZE *len)
void smtp_printf(VSTREAM *stream, const char *fmt,...)
#define vstring_avail(vp)
#define INET_PROTO_NAME_IPV6
NORETURN msg_fatal(const char *fmt,...)
#define PUSH_BACK_PEEK(state)
off_t vstream_fseek(VSTREAM *stream, off_t offset, int whence)
void(* response)(SINK_STATE *, const char *)
#define vstream_fread(v, b, n)
int vstream_fflush(VSTREAM *stream)
void smtp_flush(VSTREAM *stream)
#define GETOPT(argc, argv, str)
#define vstream_fwrite(v, b, n)
#define VSTRING_SPACE(vp, len)
int strcasecmp(const char *s1, const char *s2)
#define ST_CR_LF_DOT_CR_LF
int non_blocking(int, int)
int vstream_tweak_sock(VSTREAM *)
VSTRING * vstring_free(VSTRING *vp)
time_t event_request_timer(EVENT_NOTIFY_TIME_FN callback, void *context, int delay)
int unix_listen(const char *, int, int)
#define vstream_fileno(vp)
void msg_vstream_init(const char *name, VSTREAM *vp)
int make_dirs(const char *path, int perms)
#define VSTREAM_PUTC(ch, vp)
char * printable(char *string, int replacement)
void event_disable_readwrite(int fd)
void(* delayed_response)(struct SINK_STATE *state, const char *)
#define vstream_ferror(vp)
#define INET_PROTO_NAME_IPV4
int event_cancel_timer(EVENT_NOTIFY_TIME_FN callback, void *context)
VSTREAM * vstream_fdopen(int fd, int flags)
void * mymalloc(ssize_t len)
VSTRING * unescape(VSTRING *, const char *)
void msg_info(const char *fmt,...)