aboutsummaryrefslogtreecommitdiffstats
path: root/smtpd/parser.c
diff options
context:
space:
mode:
Diffstat (limited to 'smtpd/parser.c')
-rw-r--r--smtpd/parser.c341
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, &param[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);
+}