diff options
author | Gilles Chehade <gilles@poolp.org> | 2020-04-30 02:06:46 +0200 |
---|---|---|
committer | Gilles Chehade <gilles@poolp.org> | 2020-04-30 02:06:46 +0200 |
commit | d4180f85d53f7381da9fcc7257cf61d0b29497a0 (patch) | |
tree | 928f5541f702f22aec334cd7262d611821ff44cf /smtpd/parser.c | |
parent | Merge remote-tracking branch 'portable/native' (diff) | |
download | OpenSMTPD-d4180f85d53f7381da9fcc7257cf61d0b29497a0.tar.xz OpenSMTPD-d4180f85d53f7381da9fcc7257cf61d0b29497a0.zip |
merge histories
Diffstat (limited to 'smtpd/parser.c')
-rw-r--r-- | smtpd/parser.c | 341 |
1 files changed, 341 insertions, 0 deletions
diff --git a/smtpd/parser.c b/smtpd/parser.c new file mode 100644 index 00000000..4c2321ec --- /dev/null +++ b/smtpd/parser.c @@ -0,0 +1,341 @@ +/* $OpenBSD: parser.c,v 1.42 2020/01/06 11:02:38 gilles Exp $ */ + +/* + * 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 + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include <sys/types.h> +#include <sys/queue.h> +#include <sys/socket.h> + +#include <netinet/in.h> +#include <net/if.h> +#include <arpa/inet.h> + +#include <err.h> +#include <inttypes.h> +#include <limits.h> +#include <netdb.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "parser.h" + +uint64_t text_to_evpid(const char *); +uint32_t text_to_msgid(const char *); + +struct node { + int type; + const char *token; + struct node *parent; + TAILQ_ENTRY(node) entry; + TAILQ_HEAD(, node) children; + int (*cmd)(int, struct parameter*); +}; + +static struct node *root; + +static int text_to_sockaddr(struct sockaddr *, int, const char *); + +#define ARGVMAX 64 + +int +cmd_install(const char *pattern, int (*cmd)(int, struct parameter*)) +{ + 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; + + if (root == NULL) { + root = calloc(1, sizeof (*root)); + TAILQ_INIT(&root->children); + } + node = root; + + 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 if (!strcmp(argv[i], "<addr>")) + tmp->type = P_ADDR; + else + tmp->type = P_TOKEN; + tmp->token = strdup(argv[i]); + tmp->parent = node; + TAILQ_INSERT_TAIL(&node->children, tmp, entry); + node = tmp; + } + } + + if (node->cmd) + errx(1, "duplicate pattern: %s", pattern); + node->cmd = cmd; + + free(s); + return (n); +} + +static int +cmd_check(const char *str, struct node *node, struct parameter *res) +{ + 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_routeid = strtonum(str, 1, LLONG_MAX, &e); + if (e) + return (0); + return (1); + + case P_ADDR: + if (text_to_sockaddr((struct sockaddr *)&res->u.u_ss, PF_UNSPEC, str) == 0) + return (1); + return (0); + + default: + errx(1, "bad token type: %d", node->type); + return (0); + } +} + +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; + } + } + 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; + } + if (best == NULL) + goto fail; + stack[i] = best; + node = best; + param[np].type = node->type; + if (node->type != P_TOKEN) + np++; + } + } + + if (node->cmd == NULL) + goto fail; + + return (node->cmd(np, np ? param : NULL)); + +fail: + if (TAILQ_FIRST(&node->children) == NULL) { + fprintf(stderr, "invalid command\n"); + return (-1); + } + + 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 (-1); +} + +int +cmd_show_params(int argc, struct parameter *argv) +{ + int i; + + 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:%d", argv[i].u.u_int); + break; + case P_MSGID: + printf(" msgid:%08"PRIx32, argv[i].u.u_msgid); + break; + case P_EVPID: + printf(" evpid:%016"PRIx64, argv[i].u.u_evpid); + break; + case P_ROUTEID: + printf(" routeid:%016"PRIx64, argv[i].u.u_routeid); + break; + default: + printf(" ???:%d", argv[i].type); + } + } + printf ("\n"); + return (1); +} + +static int +text_to_sockaddr(struct sockaddr *sa, int family, const char *str) +{ + struct in_addr ina; + struct in6_addr in6a; + struct sockaddr_in *in; + struct sockaddr_in6 *in6; + char *cp, *str2; + const char *errstr; + + switch (family) { + case PF_UNSPEC: + if (text_to_sockaddr(sa, PF_INET, str) == 0) + return (0); + return text_to_sockaddr(sa, PF_INET6, str); + + case PF_INET: + if (inet_pton(PF_INET, str, &ina) != 1) + return (-1); + + in = (struct sockaddr_in *)sa; + memset(in, 0, sizeof *in); +#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN + in->sin_len = sizeof(struct sockaddr_in); +#endif + in->sin_family = PF_INET; + in->sin_addr.s_addr = ina.s_addr; + return (0); + + case PF_INET6: + cp = strchr(str, SCOPE_DELIMITER); + if (cp) { + str2 = strdup(str); + if (str2 == NULL) + return (-1); + str2[cp - str] = '\0'; + if (inet_pton(PF_INET6, str2, &in6a) != 1) { + free(str2); + return (-1); + } + cp++; + free(str2); + } else if (inet_pton(PF_INET6, str, &in6a) != 1) + return (-1); + + in6 = (struct sockaddr_in6 *)sa; + memset(in6, 0, sizeof *in6); +#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN + in6->sin6_len = sizeof(struct sockaddr_in6); +#endif + in6->sin6_family = PF_INET6; + in6->sin6_addr = in6a; + + if (cp == NULL) + return (0); + + if (IN6_IS_ADDR_LINKLOCAL(&in6a) || + IN6_IS_ADDR_MC_LINKLOCAL(&in6a) || + IN6_IS_ADDR_MC_NODELOCAL(&in6a)) + if ((in6->sin6_scope_id = if_nametoindex(cp))) + return (0); + + in6->sin6_scope_id = strtonum(cp, 0, UINT32_MAX, &errstr); + if (errstr) + return (-1); + return (0); + + default: + break; + } + + return (-1); +} |