diff options
author | Eric Faurot <eric@faurot.net> | 2013-07-08 11:18:32 +0200 |
---|---|---|
committer | Eric Faurot <eric@faurot.net> | 2013-07-08 11:18:32 +0200 |
commit | a64ef17bb2685177b9645e7578f79653c396deb4 (patch) | |
tree | c55ff0df39434803c685409e74c6e5b9908f433e /smtpd/parser.c | |
parent | Merge branch 'master' into portable (diff) | |
parent | New implementation for the command line parser and dispatching in smtpctl. (diff) | |
download | OpenSMTPD-a64ef17bb2685177b9645e7578f79653c396deb4.tar.xz OpenSMTPD-a64ef17bb2685177b9645e7578f79653c396deb4.zip |
Merge branch 'master' into portable
Diffstat (limited to 'smtpd/parser.c')
-rw-r--r-- | smtpd/parser.c | 445 |
1 files changed, 183 insertions, 262 deletions
diff --git a/smtpd/parser.c b/smtpd/parser.c index c07a99eb..72502b01 100644 --- a/smtpd/parser.c +++ b/smtpd/parser.c @@ -1,9 +1,7 @@ /* $OpenBSD$ */ /* - * 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 @@ -21,303 +19,226 @@ #include "includes.h" #include <sys/types.h> -#include <sys/socket.h> #include <sys/queue.h> -#include <sys/tree.h> +#include <sys/limits.h> -#include <event.h> -#include <imsg.h> - -#include <openssl/ssl.h> - -#include "smtpd.h" +#include <err.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> #include "parser.h" -enum token_type { - NOTOKEN, - ENDTOKEN, - KEYWORD, - VARIABLE -}; +uint64_t text_to_evpid(const char *); +uint32_t text_to_msgid(const char *); -struct token { - enum token_type type; - const char *keyword; - int value; - const struct token *next; +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_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_resume_route[]; -static const struct token t_schedule[]; -static const struct token t_pause_evp[]; -static const struct token t_resume_evp[]; -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 struct node *root; -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}, - {KEYWORD, "routes", SHOW_ROUTES, NULL}, - {KEYWORD, "hoststats", SHOW_HOSTSTATS, NULL}, - {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}, - {KEYWORD, "envelope", PAUSE_EVP, t_pause_evp}, - {ENDTOKEN, "", NONE, NULL} -}; - -static const struct token t_resume[] = { - {KEYWORD, "mda", RESUME_MDA, NULL}, - {KEYWORD, "mta", RESUME_MTA, NULL}, - {KEYWORD, "smtp", RESUME_SMTP, NULL}, - {KEYWORD, "envelope", RESUME_EVP, t_resume_evp}, - {KEYWORD, "route", RESUME_ROUTE, t_resume_route}, - {ENDTOKEN, "", NONE, NULL} -}; - -static const struct token t_pause_evp[] = { - {VARIABLE, "id", PAUSE_EVP, NULL}, - {ENDTOKEN, "", NONE, NULL} -}; - -static const struct token t_resume_evp[] = { - {VARIABLE, "id", RESUME_EVP, NULL}, - {ENDTOKEN, "", NONE, NULL} -}; - -static const struct token t_resume_route[] = { - {VARIABLE, "id", RESUME_ROUTE, NULL}, - {ENDTOKEN, "", NONE, NULL} -}; - -static const struct token t_log[] = { - {KEYWORD, "verbose", LOG_VERBOSE, NULL}, - {KEYWORD, "brief", LOG_BRIEF, NULL}, - {ENDTOKEN, "", NONE, NULL} -}; - -static const struct token t_update[] = { - {KEYWORD, "table", NONE, t_update_table}, - {ENDTOKEN, "", NONE, NULL} -}; - -static const struct token t_update_table[] = { - {VARIABLE, "name", UPDATE_TABLE, NULL}, - {ENDTOKEN, "", NONE, NULL} -}; - -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} -}; +#define ARGVMAX 64 -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; - } - break; - case VARIABLE: - if (word != NULL && strlen(word) != 0) { - match++; - t = &table[i]; - if (t->value) { - res->action = t->value; - res->data = word; - } +int +cmd_run(int argc, char **argv) +{ + struct parameter param[ARGVMAX]; + struct node *node, *tmp, *stack[ARGVMAX]; + 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 ENDTOKEN: - break; } + if (tmp == NULL) + goto fail; } - 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); } |