diff options
-rw-r--r-- | usr.sbin/smtpd/parser.c | 439 | ||||
-rw-r--r-- | usr.sbin/smtpd/parser.h | 83 | ||||
-rw-r--r-- | usr.sbin/smtpd/smtpctl.c | 1272 | ||||
-rw-r--r-- | usr.sbin/smtpd/smtpctl/Makefile | 8 | ||||
-rw-r--r-- | usr.sbin/smtpd/smtpd.h | 10 |
5 files changed, 921 insertions, 891 deletions
diff --git a/usr.sbin/smtpd/parser.c b/usr.sbin/smtpd/parser.c index a0e75473210..a8d174a9956 100644 --- a/usr.sbin/smtpd/parser.c +++ b/usr.sbin/smtpd/parser.c @@ -1,9 +1,7 @@ -/* $OpenBSD: parser.c,v 1.34 2013/05/24 17:03:14 eric Exp $ */ +/* $OpenBSD: parser.c,v 1.35 2013/07/19 13:41:23 eric Exp $ */ /* - * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org> - * Copyright (c) 2004 Esben Norby <norby@openbsd.org> - * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> + * Copyright (c) 2013 Eric Faurot <eric@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -19,280 +17,243 @@ */ #include <sys/types.h> -#include <sys/socket.h> #include <sys/queue.h> -#include <sys/tree.h> -#include <event.h> -#include <imsg.h> - -#include <openssl/ssl.h> - -#include "smtpd.h" +#include <err.h> +#include <inttypes.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> #include "parser.h" -enum token_type { - NOTOKEN, - ENDTOKEN, - KEYWORD, - VARIABLE -}; - -struct token { - enum token_type type; - const char *keyword; - int value; - const struct token *next; -}; - -static const struct token t_log[]; -static const struct token t_main[]; -static const struct token t_pause[]; -static const struct token t_remove[]; -static const struct token t_resume[]; -static const struct token t_schedule[]; -static const struct token t_show[]; -static const struct token t_show_envelope[]; -static const struct token t_show_message[]; -static const struct token t_update[]; -static const struct token t_update_table[]; -static const struct token t_trace[]; -static const struct token t_untrace[]; -static const struct token t_profile[]; -static const struct token t_unprofile[]; - -static const struct token t_main[] = { - {KEYWORD, "schedule", NONE, t_schedule}, - {KEYWORD, "show", NONE, t_show}, - {KEYWORD, "monitor", MONITOR, NULL}, - {KEYWORD, "pause", NONE, t_pause}, - {KEYWORD, "remove", NONE, t_remove}, - {KEYWORD, "resume", NONE, t_resume}, - {KEYWORD, "stop", SHUTDOWN, NULL}, - {KEYWORD, "log", NONE, t_log}, - {KEYWORD, "profile", NONE, t_profile}, - {KEYWORD, "trace", NONE, t_trace}, - {KEYWORD, "unprofile", NONE, t_unprofile}, - {KEYWORD, "untrace", NONE, t_untrace}, - {KEYWORD, "update", NONE, t_update}, - {ENDTOKEN, "", NONE, NULL} -}; - -static const struct token t_remove[] = { - {VARIABLE, "evpid", REMOVE, NULL}, - {ENDTOKEN, "", NONE, NULL} -}; - -static const struct token t_schedule[] = { - {VARIABLE, "msgid/evpid/all", SCHEDULE, NULL}, - {ENDTOKEN, "", NONE, NULL} -}; - -static const struct token t_show[] = { - {KEYWORD, "queue", SHOW_QUEUE, NULL}, - {KEYWORD, "stats", SHOW_STATS, NULL}, - {KEYWORD, "envelope", NONE, t_show_envelope}, - {KEYWORD, "message", SHOW_MESSAGE, t_show_message}, - {ENDTOKEN, "", NONE, NULL} -}; - -static const struct token t_show_envelope[] = { - {VARIABLE, "evpid", SHOW_ENVELOPE, NULL}, - {ENDTOKEN, "", NONE, NULL} -}; - -static const struct token t_show_message[] = { - {VARIABLE, "evpid", SHOW_MESSAGE, NULL}, - {ENDTOKEN, "", NONE, NULL} -}; - -static const struct token t_pause[] = { - {KEYWORD, "mda", PAUSE_MDA, NULL}, - {KEYWORD, "mta", PAUSE_MTA, NULL}, - {KEYWORD, "smtp", PAUSE_SMTP, NULL}, - {ENDTOKEN, "", NONE, NULL} -}; - -static const struct token t_resume[] = { - {KEYWORD, "mda", RESUME_MDA, NULL}, - {KEYWORD, "mta", RESUME_MTA, NULL}, - {KEYWORD, "smtp", RESUME_SMTP, NULL}, - {ENDTOKEN, "", NONE, NULL} -}; +uint64_t text_to_evpid(const char *); +uint32_t text_to_msgid(const char *); -static const struct token t_log[] = { - {KEYWORD, "verbose", LOG_VERBOSE, NULL}, - {KEYWORD, "brief", LOG_BRIEF, NULL}, - {ENDTOKEN, "", NONE, NULL} +struct node { + int type; + const char *token; + struct node *parent; + TAILQ_ENTRY(node) entry; + TAILQ_HEAD(, node) children; + int (*cmd)(int, struct parameter*); }; -static const struct token t_update[] = { - {KEYWORD, "table", NONE, t_update_table}, - {ENDTOKEN, "", NONE, NULL} -}; +static struct node *root; -static const struct token t_update_table[] = { - {VARIABLE, "name", UPDATE_TABLE, NULL}, - {ENDTOKEN, "", NONE, NULL} -}; +#define ARGVMAX 64 -static const struct token t_trace[] = { - {KEYWORD, "imsg", LOG_TRACE_IMSG, NULL}, - {KEYWORD, "io", LOG_TRACE_IO, NULL}, - {KEYWORD, "smtp", LOG_TRACE_SMTP, NULL}, - {KEYWORD, "filter", LOG_TRACE_MFA, NULL}, - {KEYWORD, "transfer", LOG_TRACE_MTA, NULL}, - {KEYWORD, "bounce", LOG_TRACE_BOUNCE, NULL}, - {KEYWORD, "scheduler", LOG_TRACE_SCHEDULER, NULL}, - {KEYWORD, "lookup", LOG_TRACE_LOOKUP, NULL}, - {KEYWORD, "stat", LOG_TRACE_STAT, NULL}, - {KEYWORD, "rules", LOG_TRACE_RULES, NULL}, - {KEYWORD, "mproc", LOG_TRACE_MPROC, NULL}, - {KEYWORD, "expand", LOG_TRACE_EXPAND, NULL}, - {KEYWORD, "all", LOG_TRACE_ALL, NULL}, - {ENDTOKEN, "", NONE, NULL} -}; - -static const struct token t_untrace[] = { - {KEYWORD, "imsg", LOG_UNTRACE_IMSG, NULL}, - {KEYWORD, "io", LOG_UNTRACE_IO, NULL}, - {KEYWORD, "smtp", LOG_UNTRACE_SMTP, NULL}, - {KEYWORD, "filter", LOG_UNTRACE_MFA, NULL}, - {KEYWORD, "transfer", LOG_UNTRACE_MTA, NULL}, - {KEYWORD, "bounce", LOG_UNTRACE_BOUNCE, NULL}, - {KEYWORD, "scheduler", LOG_UNTRACE_SCHEDULER, NULL}, - {KEYWORD, "lookup", LOG_UNTRACE_LOOKUP, NULL}, - {KEYWORD, "stat", LOG_UNTRACE_STAT, NULL}, - {KEYWORD, "rules", LOG_UNTRACE_RULES, NULL}, - {KEYWORD, "mproc", LOG_UNTRACE_MPROC, NULL}, - {KEYWORD, "expand", LOG_UNTRACE_EXPAND, NULL}, - {KEYWORD, "all", LOG_UNTRACE_ALL, NULL}, - {ENDTOKEN, "", NONE, NULL} -}; - -static const struct token t_profile[] = { - {KEYWORD, "imsg", LOG_PROFILE_IMSG, NULL}, - {KEYWORD, "queue", LOG_PROFILE_QUEUE, NULL}, - {ENDTOKEN, "", NONE, NULL} -}; - -static const struct token t_unprofile[] = { - {KEYWORD, "imsg", LOG_UNPROFILE_IMSG, NULL}, - {KEYWORD, "queue", LOG_UNPROFILE_QUEUE, NULL}, - {ENDTOKEN, "", NONE, NULL} -}; - - -static const struct token *match_token(const char *, const struct token [], - struct parse_result *); -static void show_valid_args(const struct token []); - -struct parse_result * -parse(int argc, char *argv[]) +int +cmd_install(const char *pattern, int (*cmd)(int, struct parameter*)) { - static struct parse_result res; - const struct token *table = t_main; - const struct token *match; + struct node *node, *tmp; + char *s, *str, *argv[ARGVMAX], **ap; + int i, n; + + /* Tokenize */ + str = s = strdup(pattern); + if (str == NULL) + err(1, "strdup"); + n = 0; + for (ap = argv; n < ARGVMAX && (*ap = strsep(&str, " \t")) != NULL;) { + if (**ap != '\0') { + ap++; + n++; + } + } + *ap = NULL; - bzero(&res, sizeof(res)); + if (root == NULL) { + root = calloc(1, sizeof (*root)); + TAILQ_INIT(&root->children); + } + node = root; - while (argc >= 0) { - if ((match = match_token(argv[0], table, &res)) == NULL) { - fprintf(stderr, "valid commands/args:\n"); - show_valid_args(table); - return (NULL); + for (i = 0; i < n; i++) { + TAILQ_FOREACH(tmp, &node->children, entry) { + if (!strcmp(tmp->token, argv[i])) { + node = tmp; + break; + } + } + if (tmp == NULL) { + tmp = calloc(1, sizeof (*tmp)); + TAILQ_INIT(&tmp->children); + if (!strcmp(argv[i], "<str>")) + tmp->type = P_STR; + else if (!strcmp(argv[i], "<int>")) + tmp->type = P_INT; + else if (!strcmp(argv[i], "<msgid>")) + tmp->type = P_MSGID; + else if (!strcmp(argv[i], "<evpid>")) + tmp->type = P_EVPID; + else if (!strcmp(argv[i], "<routeid>")) + tmp->type = P_ROUTEID; + else + tmp->type = P_TOKEN; + tmp->token = strdup(argv[i]); + tmp->parent = node; + TAILQ_INSERT_TAIL(&node->children, tmp, entry); + node = tmp; } + } - argc--; - argv++; + if (node->cmd) + errx(1, "duplicate pattern: %s", pattern); + node->cmd = cmd; - if (match->type == NOTOKEN || match->next == NULL) - break; + free(s); + return (n); +} - table = match->next; - } +static void +cmd_dump(struct node *node, int depth) +{ + struct node *n; + int i; - if (argc > 0) { - fprintf(stderr, "superfluous argument: %s\n", argv[0]); - return (NULL); - } + for(i = 0; i < depth; i++) + printf(" "); + printf("%s\n", node->token ? node->token : ""); - return (&res); + TAILQ_FOREACH(n, &node->children, entry) + cmd_dump(n, depth + 1); } -const struct token * -match_token(const char *word, const struct token table[], - struct parse_result *res) +static int +cmd_check(const char *str, struct node *node, struct parameter *res) { - uint i, match; - const struct token *t = NULL; - - match = 0; + const char *e; + + switch (node->type) { + case P_TOKEN: + if (!strcmp(str, node->token)) + return (1); + return (0); + + case P_STR: + res->u.u_str = str; + return (1); + + case P_INT: + res->u.u_int = strtonum(str, INT_MIN, INT_MAX, &e); + if (e) + return (0); + return (1); + + case P_MSGID: + if (strlen(str) != 8) + return (0); + res->u.u_msgid = text_to_msgid(str); + if (res->u.u_msgid == 0) + return (0); + return (1); + + case P_EVPID: + if (strlen(str) != 16) + return (0); + res->u.u_evpid = text_to_evpid(str); + if (res->u.u_evpid == 0) + return (0); + return (1); + + case P_ROUTEID: + res->u.u_int = strtonum(str, 1, LLONG_MAX, &e); + if (e) + return (0); + return (1); + + default: + errx(1, "bad token type: %i", node->type); + return (0); + } +} - for (i = 0; table[i].type != ENDTOKEN; i++) { - switch (table[i].type) { - case NOTOKEN: - if (word == NULL || strlen(word) == 0) { - match++; - t = &table[i]; - } - break; - case KEYWORD: - if (word != NULL && strncmp(word, table[i].keyword, - strlen(word)) == 0) { - match++; - t = &table[i]; - if (t->value) - res->action = t->value; +int +cmd_run(int argc, char **argv) +{ + struct parameter param[ARGVMAX]; + struct node *node, *tmp, *stack[ARGVMAX], *best; + int i, j, np; + + node = root; + np = 0; + + for (i = 0; i < argc; i++) { + TAILQ_FOREACH(tmp, &node->children, entry) { + if (cmd_check(argv[i], tmp, ¶m[np])) { + stack[i] = tmp; + node = tmp; + param[np].type = node->type; + if (node->type != P_TOKEN) + np++; + break; } - break; - case VARIABLE: - if (word != NULL && strlen(word) != 0) { - match++; - t = &table[i]; - if (t->value) { - res->action = t->value; - res->data = word; - } + } + if (tmp == NULL) { + best = NULL; + TAILQ_FOREACH(tmp, &node->children, entry) { + if (tmp->type != P_TOKEN) + continue; + if (strstr(tmp->token, argv[i]) != tmp->token) + continue; + if (best) + goto fail; + best = tmp; } - break; - case ENDTOKEN: - break; + if (best == NULL) + goto fail; + stack[i] = best; + node = best; + param[np].type = node->type; + if (node->type != P_TOKEN) + np++; } } - if (match != 1) { - if (word == NULL) - fprintf(stderr, "missing argument:\n"); - else if (match > 1) - fprintf(stderr, "ambiguous argument: %s\n", word); - else if (match < 1) - fprintf(stderr, "unknown argument: %s\n", word); - return (NULL); + if (node->cmd == NULL) + goto fail; + + return (node->cmd(np, param)); + +fail: + fprintf(stderr, "possibilities are:\n"); + TAILQ_FOREACH(tmp, &node->children, entry) { + for (j = 0; j < i; j++) + fprintf(stderr, "%s%s", j?" ":"", stack[j]->token); + fprintf(stderr, "%s%s\n", i?" ":"", tmp->token); } - return (t); + return (-1); } -static void -show_valid_args(const struct token table[]) +int +cmd_show_params(int argc, struct parameter *argv) { int i; - for (i = 0; table[i].type != ENDTOKEN; i++) { - switch (table[i].type) { - case NOTOKEN: - fprintf(stderr, " <cr>\n"); + for (i = 0; i < argc; i++) { + switch(argv[i].type) { + case P_STR: + printf(" str:\"%s\"", argv[i].u.u_str); + break; + case P_INT: + printf(" int:%i", argv[i].u.u_int); break; - case KEYWORD: - fprintf(stderr, " %s\n", table[i].keyword); + case P_MSGID: + printf(" msgid:%08"PRIx32, argv[i].u.u_msgid); break; - case VARIABLE: - fprintf(stderr, " %s\n", table[i].keyword); + case P_EVPID: + printf(" evpid:%016"PRIx64, argv[i].u.u_evpid); break; - case ENDTOKEN: + case P_ROUTEID: + printf(" routeid:%016"PRIx64, argv[i].u.u_routeid); break; + default: + printf(" ???:%i", argv[i].type); } } + printf ("\n"); + return (1); } diff --git a/usr.sbin/smtpd/parser.h b/usr.sbin/smtpd/parser.h index ffdcc49c08a..905b1e78114 100644 --- a/usr.sbin/smtpd/parser.h +++ b/usr.sbin/smtpd/parser.h @@ -1,7 +1,7 @@ -/* $OpenBSD: parser.h,v 1.27 2013/05/24 17:03:14 eric Exp $ */ +/* $OpenBSD: parser.h,v 1.28 2013/07/19 13:41:23 eric Exp $ */ /* - * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org> + * Copyright (c) 2013 Eric Faurot <eric@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -16,67 +16,26 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -enum actions { - NONE, - SHUTDOWN, - MONITOR, - LOG_VERBOSE, - LOG_BRIEF, - SCHEDULE, - SHOW_QUEUE, - SHOW_STATS, - SHOW_SIZES, - SHOW_ENVELOPE, - SHOW_MESSAGE, - PAUSE_MDA, - PAUSE_MTA, - PAUSE_SMTP, - REMOVE, - RESUME_MDA, - RESUME_MTA, - RESUME_SMTP, - UPDATE_TABLE, - LOG_TRACE_IMSG, - LOG_TRACE_IO, - LOG_TRACE_SMTP, - LOG_TRACE_MFA, - LOG_TRACE_MTA, - LOG_TRACE_BOUNCE, - LOG_TRACE_SCHEDULER, - LOG_TRACE_LOOKUP, - LOG_TRACE_STAT, - LOG_TRACE_RULES, - LOG_TRACE_MPROC, - LOG_TRACE_EXPAND, - LOG_TRACE_ALL, - LOG_UNTRACE_IMSG, - LOG_UNTRACE_IO, - LOG_UNTRACE_SMTP, - LOG_UNTRACE_MFA, - LOG_UNTRACE_MTA, - LOG_UNTRACE_BOUNCE, - LOG_UNTRACE_SCHEDULER, - LOG_UNTRACE_LOOKUP, - LOG_UNTRACE_STAT, - LOG_UNTRACE_RULES, - LOG_UNTRACE_MPROC, - LOG_UNTRACE_EXPAND, - LOG_UNTRACE_ALL, - LOG_PROFILE_IMSG, - LOG_PROFILE_QUEUE, - LOG_UNPROFILE_IMSG, - LOG_UNPROFILE_QUEUE, +enum { + P_TOKEN, + P_STR, + P_INT, + P_MSGID, + P_EVPID, + P_ROUTEID, }; -struct ctl_id { - uint32_t id; - char name[MAX_NAME_SIZE]; +struct parameter { + int type; + union { + const char *u_str; + int u_int; + uint32_t u_msgid; + uint64_t u_evpid; + uint64_t u_routeid; + } u; }; -struct parse_result { - struct ctl_id id; - enum actions action; - const char *data; -}; - -struct parse_result *parse(int, char *[]); +int cmd_install(const char *, int (*)(int, struct parameter *)); +int cmd_run(int, char **); +int cmd_show_params(int argc, struct parameter *argv); diff --git a/usr.sbin/smtpd/smtpctl.c b/usr.sbin/smtpd/smtpctl.c index ea1926fc129..c19bb7d012f 100644 --- a/usr.sbin/smtpd/smtpctl.c +++ b/usr.sbin/smtpd/smtpctl.c @@ -1,6 +1,7 @@ -/* $OpenBSD: smtpctl.c,v 1.105 2013/07/19 11:14:08 eric Exp $ */ +/* $OpenBSD: smtpctl.c,v 1.106 2013/07/19 13:41:23 eric Exp $ */ /* + * Copyright (c) 2013 Eric Faurot <eric@openbsd.org> * Copyright (c) 2006 Gilles Chehade <gilles@poolp.org> * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org> * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org> @@ -30,6 +31,7 @@ #include <err.h> #include <errno.h> #include <event.h> +#include <fts.h> #include <imsg.h> #include <inttypes.h> #include <pwd.h> @@ -43,43 +45,33 @@ #include "parser.h" #include "log.h" -#define PATH_CAT "/bin/cat" #define PATH_GZCAT "/usr/bin/gzcat" +#define PATH_CAT "/bin/cat" #define PATH_QUEUE "/queue" void usage(void); -static void setup_env(struct smtpd *); -static int show_command_output(struct imsg *); -static void show_stats_output(void); -static void show_queue(int); static void show_queue_envelope(struct envelope *, int); static void getflag(uint *, int, char *, char *, size_t); static void display(const char *); -static void show_envelope(const char *); -static void show_message(const char *); -static void show_monitor(struct stat_digest *); - -static int try_connect(void); -static void flush(void); -static void next_message(struct imsg *); -static int action_schedule_all(void); - -static int action_show_queue(void); -static int action_show_queue_message(uint32_t); -static uint32_t trace_convert(uint32_t); -static uint32_t profile_convert(uint32_t); - -int proctype; +static int str_to_trace(const char *); +static int str_to_profile(const char *); +static void show_offline_envelope(uint64_t); +static int is_gzip_fp(FILE *); +static int is_encrypted_fp(FILE *); +static int is_encrypted_buffer(const char *); +static int is_gzip_buffer(const char *); + +extern char *__progname; +int sendmail; +struct smtpd *env; struct imsgbuf *ibuf; - -int sendmail = 0; -extern char *__progname; - -struct smtpd *env = NULL; - -time_t now; +struct imsg imsg; +char *rdata; +size_t rlen; +time_t now; struct queue_backend queue_backend_null; +struct queue_backend queue_backend_proc; struct queue_backend queue_backend_ram; __dead void @@ -96,18 +88,16 @@ usage(void) exit(1); } -static void -setup_env(struct smtpd *smtpd) +void stat_increment(const char *k, size_t v) { - bzero(smtpd, sizeof (*smtpd)); - env = smtpd; +} - if (!queue_init("fs", 0)) - errx(1, "invalid directory permissions"); +void stat_decrement(const char *k, size_t v) +{ } static int -try_connect(void) +srv_connect(void) { struct sockaddr_un sun; int ctl_sock, saved_errno; @@ -134,22 +124,41 @@ try_connect(void) } static void -flush(void) +srv_flush(void) { if (imsg_flush(ibuf) == -1) err(1, "write error"); } static void -next_message(struct imsg *imsg) +srv_send(int msg, const void *data, size_t len) +{ + if (ibuf == NULL && !srv_connect()) + errx(1, "smtpd doesn't seem to be running"); + imsg_compose(ibuf, msg, IMSG_VERSION, 0, -1, data, len); +} + +static void +srv_recv(int type) { ssize_t n; + srv_flush(); + while (1) { - if ((n = imsg_get(ibuf, imsg)) == -1) + if ((n = imsg_get(ibuf, &imsg)) == -1) errx(1, "imsg_get error"); - if (n) - return; + if (n) { + if (imsg.hdr.type == IMSG_CTL_FAIL && + imsg.hdr.peerid != 0 && + imsg.hdr.peerid != IMSG_VERSION) + errx(1, "incompatible smtpctl and smtpd"); + if (type != -1 && type != (int)imsg.hdr.type) + errx(1, "bad message type"); + rdata = imsg.data; + rlen = imsg.hdr.len - sizeof(imsg.hdr); + break; + } if ((n = imsg_read(ibuf)) == -1) errx(1, "imsg_read error"); @@ -158,475 +167,565 @@ next_message(struct imsg *imsg) } } -int -main(int argc, char *argv[]) -{ - struct parse_result *res = NULL; - struct imsg imsg; - struct smtpd smtpd; - uint64_t ulval; - char name[SMTPD_MAXLINESIZE]; - int done = 0; - int verb = 0; - int profile = 0; - int action = -1; - - /* parse options */ - if (strcmp(__progname, "sendmail") == 0 || - strcmp(__progname, "send-mail") == 0) { - sendmail = 1; - if (try_connect()) - return (enqueue(argc, argv)); - return (enqueue_offline(argc, argv)); - } +static void +srv_read(void *dst, size_t sz) +{ + if (sz == 0) + return; + if (rlen < sz) + errx(1, "message too short"); + if (dst) + memmove(dst, rdata, sz); + rlen -= sz; + rdata += sz; +} - if (geteuid()) - errx(1, "need root privileges"); +static void +srv_end(void) +{ + if (rlen) + errx(1, "bogus data"); + imsg_free(&imsg); +} - if (strcmp(__progname, "mailq") == 0) - action = SHOW_QUEUE; - else if (strcmp(__progname, "smtpctl") == 0) { - if ((res = parse(argc - 1, argv + 1)) == NULL) - exit(1); - action = res->action; - } else - errx(1, "unsupported mode"); - - if (action == SHOW_ENVELOPE || - action == SHOW_MESSAGE || - !try_connect()) { - setup_env(&smtpd); - switch (action) { - case SHOW_QUEUE: - show_queue(0); - break; - case SHOW_ENVELOPE: - show_envelope(res->data); - break; - case SHOW_MESSAGE: - show_message(res->data); - break; - default: - errx(1, "smtpd doesn't seem to be running"); - } +static int +srv_check_result(void) +{ + srv_recv(-1); + srv_end(); + + switch (imsg.hdr.type) { + case IMSG_CTL_OK: + printf("command succeeded\n"); return (0); + case IMSG_CTL_FAIL: + if (rlen) + printf("command failed: %s\n", rdata); + else + printf("command failed\n"); + return (1); + default: + errx(1, "wrong message in response: %u", imsg.hdr.type); } + return (0); +} + +static int +srv_iter_messages(uint32_t *res) +{ + static uint32_t *msgids = NULL, from = 0; + static size_t n, curr; + static int done = 0; + + if (done) + return (0); - /* process user request */ - switch (action) { - case NONE: - usage(); - /* not reached */ - - case SCHEDULE: - if (! strcmp(res->data, "all")) - return action_schedule_all(); - - if ((ulval = text_to_evpid(res->data)) == 0) - errx(1, "invalid msgid/evpid"); - imsg_compose(ibuf, IMSG_CTL_SCHEDULE, IMSG_VERSION, 0, -1, &ulval, - sizeof(ulval)); - break; - case REMOVE: - if ((ulval = text_to_evpid(res->data)) == 0) - errx(1, "invalid msgid/evpid"); - imsg_compose(ibuf, IMSG_CTL_REMOVE, IMSG_VERSION, 0, -1, &ulval, - sizeof(ulval)); - break; - case SHOW_QUEUE: - return action_show_queue(); - case SHUTDOWN: - imsg_compose(ibuf, IMSG_CTL_SHUTDOWN, IMSG_VERSION, 0, -1, NULL, 0); - break; - case PAUSE_MDA: - imsg_compose(ibuf, IMSG_CTL_PAUSE_MDA, IMSG_VERSION, 0, -1, NULL, 0); - break; - case PAUSE_MTA: - imsg_compose(ibuf, IMSG_CTL_PAUSE_MTA, IMSG_VERSION, 0, -1, NULL, 0); - break; - case PAUSE_SMTP: - imsg_compose(ibuf, IMSG_CTL_PAUSE_SMTP, IMSG_VERSION, 0, -1, NULL, 0); - break; - case RESUME_MDA: - imsg_compose(ibuf, IMSG_CTL_RESUME_MDA, IMSG_VERSION, 0, -1, NULL, 0); - break; - case RESUME_MTA: - imsg_compose(ibuf, IMSG_CTL_RESUME_MTA, IMSG_VERSION, 0, -1, NULL, 0); - break; - case RESUME_SMTP: - imsg_compose(ibuf, IMSG_CTL_RESUME_SMTP, IMSG_VERSION, 0, -1, NULL, 0); - break; - case SHOW_STATS: - imsg_compose(ibuf, IMSG_STATS, IMSG_VERSION, 0, -1, NULL, 0); - break; - case UPDATE_TABLE: - if (strlcpy(name, res->data, sizeof name) >= sizeof name) - errx(1, "table name too long."); - imsg_compose(ibuf, IMSG_LKA_UPDATE_TABLE, IMSG_VERSION, 0, -1, - name, strlen(name) + 1); - done = 1; - break; - case MONITOR: - while (1) { - imsg_compose(ibuf, IMSG_DIGEST, IMSG_VERSION, 0, -1, NULL, 0); - flush(); - next_message(&imsg); - show_monitor(imsg.data); - imsg_free(&imsg); - sleep(1); + if (msgids == NULL) { + srv_send(IMSG_CTL_LIST_MESSAGES, &from, sizeof(from)); + srv_recv(IMSG_CTL_LIST_MESSAGES); + if (rlen == 0) { + srv_end(); + done = 1; + return (0); } - break; - case LOG_VERBOSE: - verb = TRACE_DEBUG; - /* FALLTHROUGH */ - case LOG_BRIEF: - imsg_compose(ibuf, IMSG_CTL_VERBOSE, IMSG_VERSION, 0, -1, &verb, - sizeof(verb)); - printf("logging request sent.\n"); - done = 1; - break; - - case LOG_TRACE_IMSG: - case LOG_TRACE_IO: - case LOG_TRACE_SMTP: - case LOG_TRACE_MFA: - case LOG_TRACE_MTA: - case LOG_TRACE_BOUNCE: - case LOG_TRACE_SCHEDULER: - case LOG_TRACE_LOOKUP: - case LOG_TRACE_STAT: - case LOG_TRACE_RULES: - case LOG_TRACE_MPROC: - case LOG_TRACE_EXPAND: - case LOG_TRACE_ALL: - verb = trace_convert(action); - imsg_compose(ibuf, IMSG_CTL_TRACE, IMSG_VERSION, 0, -1, &verb, - sizeof(verb)); - done = 1; - break; - - case LOG_UNTRACE_IMSG: - case LOG_UNTRACE_IO: - case LOG_UNTRACE_SMTP: - case LOG_UNTRACE_MFA: - case LOG_UNTRACE_MTA: - case LOG_UNTRACE_BOUNCE: - case LOG_UNTRACE_SCHEDULER: - case LOG_UNTRACE_LOOKUP: - case LOG_UNTRACE_STAT: - case LOG_UNTRACE_RULES: - case LOG_UNTRACE_MPROC: - case LOG_UNTRACE_EXPAND: - case LOG_UNTRACE_ALL: - verb = trace_convert(action); - imsg_compose(ibuf, IMSG_CTL_UNTRACE, IMSG_VERSION, 0, -1, &verb, - sizeof(verb)); - done = 1; - break; - - case LOG_PROFILE_IMSG: - case LOG_PROFILE_QUEUE: - profile = profile_convert(action); - imsg_compose(ibuf, IMSG_CTL_PROFILE, IMSG_VERSION, 0, -1, &profile, - sizeof(profile)); - done = 1; - break; - - case LOG_UNPROFILE_IMSG: - case LOG_UNPROFILE_QUEUE: - profile = profile_convert(action); - imsg_compose(ibuf, IMSG_CTL_UNPROFILE, IMSG_VERSION, 0, -1, &profile, - sizeof(profile)); - done = 1; - break; + msgids = malloc(rlen); + n = rlen / sizeof(*msgids); + srv_read(msgids, rlen); + srv_end(); - default: - errx(1, "unknown request (%d)", action); + curr = 0; + from = msgids[n - 1] + 1; + if (from == 0) + done = 1; } - do { - flush(); - next_message(&imsg); + *res = msgids[curr++]; + if (curr == n) { + free(msgids); + msgids = NULL; + } - /* ANY command can return IMSG_CTL_FAIL if version mismatch */ - if (imsg.hdr.type == IMSG_CTL_FAIL) { - show_command_output(&imsg); - break; - } + return (1); +} - switch (action) { - case REMOVE: - case SCHEDULE: - case SHUTDOWN: - case PAUSE_MDA: - case PAUSE_MTA: - case PAUSE_SMTP: - case RESUME_MDA: - case RESUME_MTA: - case RESUME_SMTP: - case LOG_VERBOSE: - case LOG_BRIEF: - case LOG_TRACE_IMSG: - case LOG_TRACE_IO: - case LOG_TRACE_SMTP: - case LOG_TRACE_MFA: - case LOG_TRACE_MTA: - case LOG_TRACE_BOUNCE: - case LOG_TRACE_SCHEDULER: - case LOG_TRACE_LOOKUP: - case LOG_TRACE_STAT: - case LOG_TRACE_RULES: - case LOG_TRACE_MPROC: - case LOG_TRACE_EXPAND: - case LOG_TRACE_ALL: - case LOG_UNTRACE_IMSG: - case LOG_UNTRACE_IO: - case LOG_UNTRACE_SMTP: - case LOG_UNTRACE_MFA: - case LOG_UNTRACE_MTA: - case LOG_UNTRACE_BOUNCE: - case LOG_UNTRACE_SCHEDULER: - case LOG_UNTRACE_LOOKUP: - case LOG_UNTRACE_STAT: - case LOG_UNTRACE_RULES: - case LOG_UNTRACE_MPROC: - case LOG_UNTRACE_EXPAND: - case LOG_UNTRACE_ALL: - case LOG_PROFILE_IMSG: - case LOG_PROFILE_QUEUE: - case LOG_UNPROFILE_IMSG: - case LOG_UNPROFILE_QUEUE: - done = show_command_output(&imsg); - break; - case SHOW_STATS: - show_stats_output(); - done = 1; - break; - case NONE: - break; - case UPDATE_TABLE: - break; - case MONITOR: +static int +srv_iter_envelopes(uint32_t msgid, struct envelope *evp) +{ + static uint32_t currmsgid = 0; + static uint64_t from = 0; + static int done = 0, need_send = 1, found; + + if (currmsgid != msgid) { + if (currmsgid != 0 && !done) + errx(1, "must finish current iteration first"); + currmsgid = msgid; + from = msgid_to_evpid(msgid); + done = 0; + found = 0; + need_send = 1; + } - default: - err(1, "unexpected reply (%d)", action); - } + if (done) + return (0); - imsg_free(&imsg); - } while (!done); - free(ibuf); + again: + if (need_send) { + found = 0; + srv_send(IMSG_CTL_LIST_ENVELOPES, &from, sizeof(from)); + } + need_send = 0; - return (0); + srv_recv(IMSG_CTL_LIST_ENVELOPES); + if (rlen == 0) { + srv_end(); + if (!found || evpid_to_msgid(from) != msgid) { + done = 1; + return (0); + } + need_send = 1; + goto again; + } + + srv_read(evp, sizeof(*evp)); + srv_end(); + from = evp->id + 1; + found++; + return (1); } +static int +do_log_brief(int argc, struct parameter *argv) +{ + int v = 0; + + srv_send(IMSG_CTL_VERBOSE, &v, sizeof(v)); + return srv_check_result(); +} static int -action_show_queue_message(uint32_t msgid) +do_log_verbose(int argc, struct parameter *argv) { - struct imsg imsg; - struct envelope *evp; - uint64_t evpid; - size_t found; + int v = TRACE_DEBUG; - evpid = msgid_to_evpid(msgid); + srv_send(IMSG_CTL_VERBOSE, &v, sizeof(v)); + return srv_check_result(); +} - nextbatch: +static int +do_monitor(int argc, struct parameter *argv) +{ + struct stat_digest last, digest; + size_t count; - found = 0; - imsg_compose(ibuf, IMSG_CTL_LIST_ENVELOPES, IMSG_VERSION, 0, -1, - &evpid, sizeof evpid); - flush(); + bzero(&last, sizeof(last)); + count = 0; while (1) { - next_message(&imsg); - if (imsg.hdr.type != IMSG_CTL_LIST_ENVELOPES) - errx(1, "unexpected message %i", imsg.hdr.type); - - if (imsg.hdr.len == sizeof imsg.hdr) { - imsg_free(&imsg); - if (!found || evpid_to_msgid(++evpid) != msgid) - return (0); - goto nextbatch; + srv_send(IMSG_DIGEST, NULL, 0); + srv_recv(IMSG_DIGEST); + srv_read(&digest, sizeof(digest)); + srv_end(); + + if (count % 25 == 0) { + if (count != 0) + printf("\n"); + printf("--- client --- " + "-- envelope -- " + "---- relay/delivery --- " + "------- misc -------\n" + "curr conn disc " + "curr enq deq " + "ok tmpfail prmfail loop " + "expire remove bounce\n"); } - found++; - evp = imsg.data; - evpid = evp->id; - show_queue_envelope(evp, 1); - imsg_free(&imsg); + printf("%4zu %4zu %4zu " + "%4zu %4zu %4zu " + "%4zu %4zu %4zu %4zu " + "%4zu %4zu %4zu\n", + digest.clt_connect - digest.clt_disconnect, + digest.clt_connect - last.clt_connect, + digest.clt_disconnect - last.clt_disconnect, + + digest.evp_enqueued - digest.evp_dequeued, + digest.evp_enqueued - last.evp_enqueued, + digest.evp_dequeued - last.evp_dequeued, + + digest.dlv_ok - last.dlv_ok, + digest.dlv_tempfail - last.dlv_tempfail, + digest.dlv_permfail - last.dlv_permfail, + digest.dlv_loop - last.dlv_loop, + + digest.evp_expired - last.evp_expired, + digest.evp_removed - last.evp_removed, + digest.evp_bounce - last.evp_bounce); + + last = digest; + count++; + sleep(1); } + return (0); +} + +static int +do_pause_mda(int argc, struct parameter *argv) +{ + srv_send(IMSG_CTL_PAUSE_MDA, NULL, 0); + return srv_check_result(); } static int -action_show_queue(void) +do_pause_mta(int argc, struct parameter *argv) { - struct imsg imsg; - uint32_t *msgids, msgid; - size_t i, n; + srv_send(IMSG_CTL_PAUSE_MTA, NULL, 0); + return srv_check_result(); +} - msgid = 0; - now = time(NULL); +static int +do_pause_smtp(int argc, struct parameter *argv) +{ + srv_send(IMSG_CTL_PAUSE_SMTP, NULL, 0); + return srv_check_result(); +} - do { - imsg_compose(ibuf, IMSG_CTL_LIST_MESSAGES, IMSG_VERSION, 0, -1, - &msgid, sizeof msgid); - flush(); - next_message(&imsg); - if (imsg.hdr.type != IMSG_CTL_LIST_MESSAGES) - errx(1, "unexpected message type %i", imsg.hdr.type); - msgids = imsg.data; - n = (imsg.hdr.len - sizeof imsg.hdr) / sizeof (*msgids); - if (n == 0) { - imsg_free(&imsg); - break; +static int +do_profile(int argc, struct parameter *argv) +{ + int v; + + v = str_to_profile(argv[0].u.u_str); + + srv_send(IMSG_CTL_PROFILE, &v, sizeof(v)); + return srv_check_result(); +} + +static int +do_remove(int argc, struct parameter *argv) +{ + struct envelope evp; + uint32_t msgid; + + if (argc == 0) { + while (srv_iter_messages(&msgid)) { + while (srv_iter_envelopes(msgid, &evp)) { + srv_send(IMSG_CTL_REMOVE, &evp.id, + sizeof(evp.id)); + srv_check_result(); + } } - for (i = 0; i < n; i++) { - msgid = msgids[i]; - action_show_queue_message(msgid); + } else if (argv[0].type == P_MSGID) { + while (srv_iter_envelopes(argv[0].u.u_msgid, &evp)) { + srv_send(IMSG_CTL_REMOVE, &evp.id, sizeof(evp.id)); + srv_check_result(); } - imsg_free(&imsg); - - } while (++msgid); + } else { + srv_send(IMSG_CTL_REMOVE, &argv[0].u.u_evpid, sizeof(evp.id)); + srv_check_result(); + } return (0); } static int -action_schedule_all(void) +do_resume_mda(int argc, struct parameter *argv) { - struct imsg imsg; - uint32_t *msgids, from; - uint64_t evpid; - size_t i, n; - - from = 0; - while (1) { - imsg_compose(ibuf, IMSG_CTL_LIST_MESSAGES, IMSG_VERSION, 0, -1, - &from, sizeof from); - flush(); - next_message(&imsg); - if (imsg.hdr.type != IMSG_CTL_LIST_MESSAGES) - errx(1, "unexpected message type %i", imsg.hdr.type); - msgids = imsg.data; - n = (imsg.hdr.len - sizeof imsg.hdr) / sizeof (*msgids); - if (n == 0) - break; + srv_send(IMSG_CTL_RESUME_MDA, NULL, 0); + return srv_check_result(); +} - for (i = 0; i < n; i++) { - evpid = msgids[i]; - imsg_compose(ibuf, IMSG_CTL_SCHEDULE, IMSG_VERSION, - 0, -1, &evpid, sizeof(evpid)); - } - from = msgids[n - 1] + 1; +static int +do_resume_mta(int argc, struct parameter *argv) +{ + srv_send(IMSG_CTL_RESUME_MTA, NULL, 0); + return srv_check_result(); +} - imsg_free(&imsg); - flush(); +static int +do_resume_smtp(int argc, struct parameter *argv) +{ + srv_send(IMSG_CTL_RESUME_SMTP, NULL, 0); + return srv_check_result(); +} - for (i = 0; i < n; i++) { - next_message(&imsg); - if (imsg.hdr.type != IMSG_CTL_OK) - errx(1, "unexpected message type %i", - imsg.hdr.type); +static int +do_schedule(int argc, struct parameter *argv) +{ + struct envelope evp; + uint32_t msgid; + + if (argc == 0) { + while (srv_iter_messages(&msgid)) { + while (srv_iter_envelopes(msgid, &evp)) { + srv_send(IMSG_CTL_SCHEDULE, &evp.id, + sizeof(evp.id)); + srv_check_result(); + } } - if (from == 0) - break; + } else if (argv[0].type == P_MSGID) { + while (srv_iter_envelopes(argv[0].u.u_msgid, &evp)) { + srv_send(IMSG_CTL_SCHEDULE, &evp.id, sizeof(evp.id)); + srv_check_result(); + } + } else { + srv_send(IMSG_CTL_SCHEDULE, &argv[0].u.u_evpid, sizeof(evp.id)); + srv_check_result(); } return (0); } static int -show_command_output(struct imsg *imsg) +do_show_envelope(int argc, struct parameter *argv) { - switch (imsg->hdr.type) { - case IMSG_CTL_OK: - printf("command succeeded\n"); - break; - case IMSG_CTL_FAIL: - if (imsg->hdr.peerid != IMSG_VERSION) - printf("command failed: incompatible smtpctl and smtpd\n"); - else - printf("command failed\n"); - break; - default: - errx(1, "wrong message in summary: %u", imsg->hdr.type); + char buf[SMTPD_MAXPATHLEN]; + + if (! bsnprintf(buf, sizeof(buf), "%s%s/%02x/%08x/%016" PRIx64, + PATH_SPOOL, + PATH_QUEUE, + (evpid_to_msgid(argv[0].u.u_evpid) & 0xff000000) >> 24, + evpid_to_msgid(argv[0].u.u_evpid), + argv[0].u.u_evpid)) + errx(1, "unable to retrieve envelope"); + + display(buf); + + return (0); +} + +static int +do_show_message(int argc, struct parameter *argv) +{ + char buf[SMTPD_MAXPATHLEN]; + uint32_t msgid; + + if (argv[0].type == P_EVPID) + msgid = evpid_to_msgid(argv[0].u.u_evpid); + else + msgid = argv[0].u.u_msgid; + + if (! bsnprintf(buf, sizeof(buf), "%s%s/%02x/%08x/message", + PATH_SPOOL, + PATH_QUEUE, + (msgid & 0xff000000) >> 24, + msgid)) + errx(1, "unable to retrieve message"); + + display(buf); + + return (0); +} + +static int +do_show_queue(int argc, struct parameter *argv) +{ + struct envelope evp; + uint32_t msgid; + FTS *fts; + FTSENT *ftse; + char *qpath[] = {"/queue", NULL}; + char *tmp; + uint64_t evpid; + + now = time(NULL); + + if (!srv_connect()) { + log_init(1); + queue_init("fs", 0); + if (chroot(PATH_SPOOL) == -1 || chdir(".") == -1) + err(1, "%s", PATH_SPOOL); + fts = fts_open(qpath, FTS_PHYSICAL|FTS_NOCHDIR, NULL); + if (fts == NULL) + err(1, "%s/queue", PATH_SPOOL); + + while ((ftse = fts_read(fts)) != NULL) { + switch (ftse->fts_info) { + case FTS_DP: + case FTS_DNR: + break; + case FTS_F: + tmp = NULL; + evpid = strtoull(ftse->fts_name, &tmp, 16); + if (tmp && *tmp != '\0') + break; + show_offline_envelope(evpid); + } + } + + fts_close(fts); + /* + while ((r = queue_envelope_walk(&evp)) != -1) + if (r) + show_queue_envelope(&evp, 0); + */ + return (0); } - return (1); + + if (argc == 0) { + msgid = 0; + while (srv_iter_messages(&msgid)) + while (srv_iter_envelopes(msgid, &evp)) + show_queue_envelope(&evp, 1); + } else if (argv[0].type == P_MSGID) { + while (srv_iter_envelopes(argv[0].u.u_msgid, &evp)) + show_queue_envelope(&evp, 1); + } + + return (0); } -static void -show_stats_output(void) +static int +do_show_stats(int argc, struct parameter *argv) { - struct stat_kv kv, *kvp; - struct imsg imsg; + struct stat_kv kv; time_t duration; bzero(&kv, sizeof kv); while (1) { - imsg_compose(ibuf, IMSG_STATS_GET, IMSG_VERSION, 0, -1, &kv, sizeof kv); - flush(); - next_message(&imsg); - if (imsg.hdr.type != IMSG_STATS_GET) - errx(1, "invalid imsg type"); - - kvp = imsg.data; - if (kvp->iter == NULL) { - imsg_free(&imsg); - return; - } + srv_send(IMSG_STATS_GET, &kv, sizeof kv); + srv_recv(IMSG_STATS_GET); + srv_read(&kv, sizeof(kv)); + srv_end(); - if (strcmp(kvp->key, "uptime") == 0) { - duration = time(NULL) - kvp->val.u.counter; - printf("uptime=%zd\n", (size_t)duration); + if (kv.iter == NULL) + break; + + if (strcmp(kv.key, "uptime") == 0) { + duration = time(NULL) - kv.val.u.counter; + printf("uptime=%lld\n", (long long)duration); printf("uptime.human=%s\n", duration_to_text(duration)); } else { - switch (kvp->val.type) { + switch (kv.val.type) { case STAT_COUNTER: printf("%s=%zd\n", - kvp->key, kvp->val.u.counter); + kv.key, kv.val.u.counter); break; case STAT_TIMESTAMP: printf("%s=%" PRId64 "\n", - kvp->key, (int64_t)kvp->val.u.timestamp); + kv.key, (int64_t)kv.val.u.timestamp); break; case STAT_TIMEVAL: - printf("%s=%zd.%zd\n", - kvp->key, kvp->val.u.tv.tv_sec, - kvp->val.u.tv.tv_usec); + printf("%s=%lld.%lld\n", + kv.key, (long long)kv.val.u.tv.tv_sec, + (long long)kv.val.u.tv.tv_usec); break; case STAT_TIMESPEC: - printf("%s=%li.%06li\n", - kvp->key, - kvp->val.u.ts.tv_sec * 1000000 + - kvp->val.u.ts.tv_nsec / 1000000, - kvp->val.u.ts.tv_nsec % 1000000); + printf("%s=%lli.%06li\n", + kv.key, + (long long)kv.val.u.ts.tv_sec * 1000000 + + kv.val.u.ts.tv_nsec / 1000000, + kv.val.u.ts.tv_nsec % 1000000); break; } } - - kv = *kvp; - imsg_free(&imsg); } + + return (0); } -static void -show_queue(flags) +static int +do_stop(int argc, struct parameter *argv) { - struct envelope envelope; - int r; + srv_send(IMSG_CTL_SHUTDOWN, NULL, 0); + return srv_check_result(); +} - log_init(1); +static int +do_trace(int argc, struct parameter *argv) +{ + int v; - if (chroot(PATH_SPOOL) == -1 || chdir(".") == -1) - err(1, "%s", PATH_SPOOL); + v = str_to_trace(argv[0].u.u_str); + + srv_send(IMSG_CTL_TRACE, &v, sizeof(v)); + return srv_check_result(); +} + +static int +do_unprofile(int argc, struct parameter *argv) +{ + int v; + + v = str_to_profile(argv[0].u.u_str); + + srv_send(IMSG_CTL_UNPROFILE, &v, sizeof(v)); + return srv_check_result(); +} + +static int +do_untrace(int argc, struct parameter *argv) +{ + int v; + + v = str_to_trace(argv[0].u.u_str); + + srv_send(IMSG_CTL_UNTRACE, &v, sizeof(v)); + return srv_check_result(); +} + +static int +do_update_table(int argc, struct parameter *argv) +{ + const char *name = argv[0].u.u_str; + + srv_send(IMSG_LKA_UPDATE_TABLE, name, strlen(name) + 1); + return srv_check_result(); +} + +int +main(int argc, char **argv) +{ + char *argv_mailq[] = { "show", "queue", NULL }; + + if (strcmp(__progname, "sendmail") == 0 || + strcmp(__progname, "send-mail") == 0) { + sendmail = 1; + if (srv_connect()) + return (enqueue(argc, argv)); + return (enqueue_offline(argc, argv)); + } + + if (geteuid()) + errx(1, "need root privileges"); + + cmd_install("log brief", do_log_brief); + cmd_install("log verbose", do_log_verbose); + cmd_install("monitor", do_monitor); + cmd_install("pause mda", do_pause_mda); + cmd_install("pause mta", do_pause_mta); + cmd_install("pause smtp", do_pause_smtp); + cmd_install("profile <str>", do_profile); + cmd_install("remove <evpid>", do_remove); + cmd_install("remove <msgid>", do_remove); + cmd_install("resume mda", do_resume_mda); + cmd_install("resume mta", do_resume_mta); + cmd_install("resume smtp", do_resume_smtp); + cmd_install("schedule <msgid>", do_schedule); + cmd_install("schedule <evpid>", do_schedule); + cmd_install("schedule all", do_schedule); + cmd_install("show envelope <evpid>", do_show_envelope); + cmd_install("show message <msgid>", do_show_message); + cmd_install("show message <evpid>", do_show_message); + cmd_install("show queue", do_show_queue); + cmd_install("show queue <msgid>", do_show_queue); + cmd_install("show stats", do_show_stats); + cmd_install("stop", do_stop); + cmd_install("trace <str>", do_trace); + cmd_install("unprofile <str>", do_unprofile); + cmd_install("untrace <str>", do_untrace); + cmd_install("update table <str>", do_update_table); + + if (strcmp(__progname, "mailq") == 0) + return cmd_run(2, argv_mailq); + if (strcmp(__progname, "smtpctl") == 0) + return cmd_run(argc - 1, argv + 1); + + errx(1, "unsupported mode"); + return (0); - while ((r = queue_envelope_walk(&envelope)) != -1) - if (r) - show_queue_envelope(&envelope, flags); } static void @@ -637,12 +736,9 @@ show_queue_envelope(struct envelope *e, int online) status[0] = '\0'; - getflag(&e->flags, EF_BOUNCE, "bounce", - status, sizeof(status)); - getflag(&e->flags, EF_AUTHENTICATED, "auth", - status, sizeof(status)); - getflag(&e->flags, EF_INTERNAL, "internal", - status, sizeof(status)); + getflag(&e->flags, EF_BOUNCE, "bounce", status, sizeof(status)); + getflag(&e->flags, EF_AUTHENTICATED, "auth", status, sizeof(status)); + getflag(&e->flags, EF_INTERNAL, "internal", status, sizeof(status)); if (online) { if (e->flags & EF_PENDING) @@ -711,197 +807,203 @@ getflag(uint *bitmap, int bit, char *bitstr, char *buf, size_t len) } static void -display(const char *s) +show_offline_envelope(uint64_t evpid) { - pid_t pid; - arglist args; - char *cmd; - int status; - - pid = fork(); - if (pid < 0) - err(1, "fork"); - if (pid == 0) { - cmd = PATH_GZCAT; - bzero(&args, sizeof(args)); - addargs(&args, "%s", cmd); - addargs(&args, "%s", s); - execvp(cmd, args.list); - err(1, "execvp"); - } - wait(&status); - if (WIFEXITED(status) && WEXITSTATUS(status) == 0) - exit(0); - cmd = PATH_CAT; - bzero(&args, sizeof(args)); - addargs(&args, "%s", cmd); - addargs(&args, "%s", s); - execvp(cmd, args.list); - err(1, "execvp"); -} - -static void -show_envelope(const char *s) -{ - char buf[SMTPD_MAXPATHLEN]; - uint64_t evpid; + FILE *fp = NULL; + char pathname[SMTPD_MAXPATHLEN]; + size_t plen; + char *p; + size_t buflen; + char buffer[sizeof(struct envelope)]; - if ((evpid = text_to_evpid(s)) == 0) - errx(1, "invalid msgid/evpid"); + struct envelope evp; - if (! bsnprintf(buf, sizeof(buf), "%s%s/%02x/%08x/%016" PRIx64, - PATH_SPOOL, - PATH_QUEUE, + if (! bsnprintf(pathname, sizeof pathname, + "/queue/%02x/%08x/%016"PRIx64, (evpid_to_msgid(evpid) & 0xff000000) >> 24, - evpid_to_msgid(evpid), - evpid)) - errx(1, "unable to retrieve envelope"); - - display(buf); -} - -static void -show_message(const char *s) -{ - char buf[SMTPD_MAXPATHLEN]; - uint32_t msgid; - uint64_t evpid; + evpid_to_msgid(evpid), evpid)) + goto end; + fp = fopen(pathname, "r"); + if (fp == NULL) + goto end; + + buflen = fread(buffer, 1, sizeof buffer, fp); + p = buffer; + plen = buflen; + + if (is_encrypted_buffer(p)) { + warnx("offline encrypted queue is not supported yet"); + goto end; + } - if ((evpid = text_to_evpid(s)) == 0) - errx(1, "invalid msgid/evpid"); + if (is_gzip_buffer(p)) { + warnx("offline compressed queue is not supported yet"); + goto end; + } - msgid = evpid_to_msgid(evpid); - if (! bsnprintf(buf, sizeof(buf), "%s%s/%02x/%08x/message", - PATH_SPOOL, - PATH_QUEUE, - (evpid_to_msgid(evpid) & 0xff000000) >> 24, - msgid)) - errx(1, "unable to retrieve message"); + if (! envelope_load_buffer(&evp, p, plen)) + goto end; + evp.id = evpid; + show_queue_envelope(&evp, 0); - display(buf); +end: + if (fp) + fclose(fp); } static void -show_monitor(struct stat_digest *d) +display(const char *s) { - static int init = 0; - static size_t count = 0; - static struct stat_digest last; - - if (init == 0) { - init = 1; - bzero(&last, sizeof last); - } - - if (count % 25 == 0) { - if (count != 0) - printf("\n"); - printf("--- client --- " - "-- envelope -- " - "---- relay/delivery --- " - "------- misc -------\n" - "curr conn disc " - "curr enq deq " - "ok tmpfail prmfail loop " - "expire remove bounce\n"); - } - printf("%4zu %4zu %4zu " - "%4zu %4zu %4zu " - "%4zu %4zu %4zu %4zu " - "%4zu %4zu %4zu\n", - d->clt_connect - d->clt_disconnect, - d->clt_connect - last.clt_connect, - d->clt_disconnect - last.clt_disconnect, + FILE *fp; + char *key; + int gzipped; + char *gzcat_argv0 = strrchr(PATH_GZCAT, '/') + 1; + + if ((fp = fopen(s, "r")) == NULL) + err(1, "fopen"); + + if (is_encrypted_fp(fp)) { + int i; + int fd; + FILE *ofp; + char sfn[] = "/tmp/smtpd.XXXXXXXXXX"; + + if ((fd = mkstemp(sfn)) == -1 || + (ofp = fdopen(fd, "w+")) == NULL) { + if (fd != -1) { + unlink(sfn); + close(fd); + } + err(1, "mkstemp"); + } + unlink(sfn); - d->evp_enqueued - d->evp_dequeued, - d->evp_enqueued - last.evp_enqueued, - d->evp_dequeued - last.evp_dequeued, + for (i = 0; i < 3; i++) { + key = getpass("key> "); + if (crypto_setup(key, strlen(key))) + break; + } + if (i == 3) + errx(1, "crypto-setup: invalid key"); - d->dlv_ok - last.dlv_ok, - d->dlv_tempfail - last.dlv_tempfail, - d->dlv_permfail - last.dlv_permfail, - d->dlv_loop - last.dlv_loop, + if (! crypto_decrypt_file(fp, ofp)) { + printf("object is encrypted: %s\n", key); + exit(1); + } - d->evp_expired - last.evp_expired, - d->evp_removed - last.evp_removed, - d->evp_bounce - last.evp_bounce); + fclose(fp); + fp = ofp; + fseek(fp, SEEK_SET, 0); + } + gzipped = is_gzip_fp(fp); - last = *d; - count++; + (void)dup2(fileno(fp), STDIN_FILENO); + if (gzipped) + execl(PATH_GZCAT, gzcat_argv0, NULL); + else + execl(PATH_CAT, "cat", NULL); + err(1, "execl"); } -static uint32_t -trace_convert(uint32_t trace) +static int +str_to_trace(const char *str) { - switch (trace) { - case LOG_TRACE_IMSG: - case LOG_UNTRACE_IMSG: + if (!strcmp(str, "imsg")) return TRACE_IMSG; - - case LOG_TRACE_IO: - case LOG_UNTRACE_IO: + if (!strcmp(str, "io")) return TRACE_IO; - - case LOG_TRACE_SMTP: - case LOG_UNTRACE_SMTP: + if (!strcmp(str, "smtp")) return TRACE_SMTP; - - case LOG_TRACE_MFA: - case LOG_UNTRACE_MFA: + if (!strcmp(str, "mfa")) return TRACE_MFA; - - case LOG_TRACE_MTA: - case LOG_UNTRACE_MTA: + if (!strcmp(str, "mta")) return TRACE_MTA; - - case LOG_TRACE_BOUNCE: - case LOG_UNTRACE_BOUNCE: + if (!strcmp(str, "bounce")) return TRACE_BOUNCE; - - case LOG_TRACE_SCHEDULER: - case LOG_UNTRACE_SCHEDULER: + if (!strcmp(str, "scheduler")) return TRACE_SCHEDULER; - - case LOG_TRACE_LOOKUP: - case LOG_UNTRACE_LOOKUP: + if (!strcmp(str, "lookup")) return TRACE_LOOKUP; - - case LOG_TRACE_STAT: - case LOG_UNTRACE_STAT: + if (!strcmp(str, "stat")) return TRACE_STAT; - - case LOG_TRACE_RULES: - case LOG_UNTRACE_RULES: + if (!strcmp(str, "rules")) return TRACE_RULES; - - case LOG_TRACE_MPROC: - case LOG_UNTRACE_MPROC: + if (!strcmp(str, "mproc")) return TRACE_MPROC; - - case LOG_TRACE_EXPAND: - case LOG_UNTRACE_EXPAND: + if (!strcmp(str, "expand")) return TRACE_EXPAND; - - case LOG_TRACE_ALL: - case LOG_UNTRACE_ALL: + if (!strcmp(str, "all")) return ~TRACE_DEBUG; - } - - return 0; + errx(1, "invalid trace keyword: %s", str); + return (0); } -static uint32_t -profile_convert(uint32_t prof) +static int +str_to_profile(const char *str) { - switch (prof) { - case LOG_PROFILE_IMSG: - case LOG_UNPROFILE_IMSG: + if (!strcmp(str, "imsg")) return PROFILE_IMSG; - - case LOG_PROFILE_QUEUE: - case LOG_UNPROFILE_QUEUE: + if (!strcmp(str, "queue")) return PROFILE_QUEUE; - } + errx(1, "invalid profile keyword: %s", str); + return (0); +} + +static int +is_gzip_buffer(const char *buffer) +{ + uint16_t magic; + + memcpy(&magic, buffer, sizeof magic); +#define GZIP_MAGIC 0x8b1f + return (magic == GZIP_MAGIC); +} + +static int +is_gzip_fp(FILE *fp) +{ + uint8_t magic[2]; + int ret = 0; + + if (fread(&magic, 1, sizeof magic, fp) != sizeof magic) + goto end; + + ret = is_gzip_buffer((const char *)&magic); +end: + fseek(fp, SEEK_SET, 0); + return ret; +} + + +/* XXX */ +/* + * queue supports transparent encryption. + * encrypted chunks are prefixed with an API version byte + * which we ensure is unambiguous with gzipped / plain + * objects. + */ + +static int +is_encrypted_buffer(const char *buffer) +{ + uint8_t magic; + + magic = *buffer; +#define ENCRYPTION_MAGIC 0x1 + return (magic == ENCRYPTION_MAGIC); +} + +static int +is_encrypted_fp(FILE *fp) +{ + uint8_t magic; + int ret = 0; + + if (fread(&magic, 1, sizeof magic, fp) != sizeof magic) + goto end; - return 0; + ret = is_encrypted_buffer((const char *)&magic); +end: + fseek(fp, SEEK_SET, 0); + return ret; } diff --git a/usr.sbin/smtpd/smtpctl/Makefile b/usr.sbin/smtpd/smtpctl/Makefile index e61249f252a..511c783dae9 100644 --- a/usr.sbin/smtpd/smtpctl/Makefile +++ b/usr.sbin/smtpd/smtpctl/Makefile @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile,v 1.31 2013/05/24 17:03:14 eric Exp $ +# $OpenBSD: Makefile,v 1.32 2013/07/19 13:41:23 eric Exp $ .PATH: ${.CURDIR}/.. @@ -17,12 +17,12 @@ CFLAGS+= -Wshadow -Wpointer-arith -Wcast-qual CFLAGS+= -Wsign-compare -Wbounded CFLAGS+= -DNO_IO -SRCS= enqueue.c parser.c log.c envelope.c +SRCS= enqueue.c parser.c log.c envelope.c crypto.c SRCS+= queue_backend.c queue_fsqueue.c SRCS+= smtpctl.c util.c SRCS+= compress_backend.c compress_gzip.c SRCS+= to.c expand.c tree.c -LDADD+= -lutil -lz -DPADD+= ${LIBUTIL} ${LIBZ} +LDADD+= -lutil -lz -lcrypto +DPADD+= ${LIBUTIL} ${LIBZ} ${LIBCRYPTO} .include <bsd.prog.mk> diff --git a/usr.sbin/smtpd/smtpd.h b/usr.sbin/smtpd/smtpd.h index cec8ebfd036..752cba6188e 100644 --- a/usr.sbin/smtpd/smtpd.h +++ b/usr.sbin/smtpd/smtpd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: smtpd.h,v 1.415 2013/07/19 11:14:08 eric Exp $ */ +/* $OpenBSD: smtpd.h,v 1.416 2013/07/19 13:41:23 eric Exp $ */ /* * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org> @@ -1073,6 +1073,14 @@ void config_done(void); pid_t control(void); +/* crypto.c */ +int crypto_setup(const char *, size_t); +int crypto_encrypt_file(FILE *, FILE *); +int crypto_decrypt_file(FILE *, FILE *); +size_t crypto_encrypt_buffer(const char *, size_t, char *, size_t); +size_t crypto_decrypt_buffer(const char *, size_t, char *, size_t); + + /* delivery.c */ struct delivery_backend *delivery_backend_lookup(enum action_type); |