1/*	$OpenBSD: parser.c,v 1.43 2021/06/14 17:58:15 eric Exp $	*/
2
3/*
4 * Copyright (c) 2013 Eric Faurot	<eric@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <sys/queue.h>
20#include <sys/socket.h>
21
22#include <netinet/in.h>
23#include <net/if.h>
24
25#include <arpa/inet.h>
26#include <err.h>
27#include <inttypes.h>
28#include <limits.h>
29#include <netdb.h>
30#include <stdio.h>
31#include <stdlib.h>
32#include <string.h>
33
34#include "parser.h"
35
36uint64_t text_to_evpid(const char *);
37uint32_t text_to_msgid(const char *);
38
39struct node {
40	int			 type;
41	const char		*token;
42	struct node		*parent;
43	TAILQ_ENTRY(node)	 entry;
44	TAILQ_HEAD(, node)	 children;
45	int			(*cmd)(int, struct parameter*);
46};
47
48static struct node	*root;
49
50static int text_to_sockaddr(struct sockaddr *, int, const char *);
51
52#define ARGVMAX	64
53
54int
55cmd_install(const char *pattern, int (*cmd)(int, struct parameter*))
56{
57	struct node	*node, *tmp;
58	char		*s, *str, *argv[ARGVMAX], **ap;
59	int		 i, n;
60
61	/* Tokenize */
62	str = s = strdup(pattern);
63	if (str == NULL)
64		err(1, "strdup");
65	n = 0;
66	for (ap = argv; n < ARGVMAX && (*ap = strsep(&str, " \t")) != NULL;) {
67		if (**ap != '\0') {
68			ap++;
69			n++;
70		}
71	}
72	*ap = NULL;
73
74	if (root == NULL) {
75		root = calloc(1, sizeof (*root));
76		TAILQ_INIT(&root->children);
77	}
78	node = root;
79
80	for (i = 0; i < n; i++) {
81		TAILQ_FOREACH(tmp, &node->children, entry) {
82			if (!strcmp(tmp->token, argv[i])) {
83				node = tmp;
84				break;
85			}
86		}
87		if (tmp == NULL) {
88			tmp = calloc(1, sizeof (*tmp));
89			TAILQ_INIT(&tmp->children);
90			if (!strcmp(argv[i], "<str>"))
91				tmp->type = P_STR;
92			else if (!strcmp(argv[i], "<int>"))
93				tmp->type = P_INT;
94			else if (!strcmp(argv[i], "<msgid>"))
95				tmp->type = P_MSGID;
96			else if (!strcmp(argv[i], "<evpid>"))
97				tmp->type = P_EVPID;
98			else if (!strcmp(argv[i], "<routeid>"))
99				tmp->type = P_ROUTEID;
100			else if (!strcmp(argv[i], "<addr>"))
101				tmp->type = P_ADDR;
102			else
103				tmp->type = P_TOKEN;
104			tmp->token = strdup(argv[i]);
105			tmp->parent = node;
106			TAILQ_INSERT_TAIL(&node->children, tmp, entry);
107			node = tmp;
108		}
109	}
110
111	if (node->cmd)
112		errx(1, "duplicate pattern: %s", pattern);
113	node->cmd = cmd;
114
115	free(s);
116	return (n);
117}
118
119static int
120cmd_check(const char *str, struct node *node, struct parameter *res)
121{
122	const char *e;
123
124	switch (node->type) {
125	case P_TOKEN:
126		if (!strcmp(str, node->token))
127			return (1);
128		return (0);
129
130	case P_STR:
131		res->u.u_str = str;
132		return (1);
133
134	case P_INT:
135		res->u.u_int = strtonum(str, INT_MIN, INT_MAX, &e);
136		if (e)
137			return (0);
138		return (1);
139
140	case P_MSGID:
141		if (strlen(str) != 8)
142			return (0);
143		res->u.u_msgid = text_to_msgid(str);
144		if (res->u.u_msgid == 0)
145			return (0);
146		return (1);
147
148	case P_EVPID:
149		if (strlen(str) != 16)
150			return (0);
151		res->u.u_evpid = text_to_evpid(str);
152		if (res->u.u_evpid == 0)
153			return (0);
154		return (1);
155
156	case P_ROUTEID:
157		res->u.u_routeid = strtonum(str, 1, LLONG_MAX, &e);
158		if (e)
159			return (0);
160		return (1);
161
162	case P_ADDR:
163		if (text_to_sockaddr((struct sockaddr *)&res->u.u_ss, PF_UNSPEC, str) == 0)
164			return (1);
165		return (0);
166
167	default:
168		errx(1, "bad token type: %d", node->type);
169		return (0);
170	}
171}
172
173int
174cmd_run(int argc, char **argv)
175{
176	struct parameter param[ARGVMAX];
177	struct node	*node, *tmp, *stack[ARGVMAX], *best;
178	int		 i, j, np;
179
180	node = root;
181	np = 0;
182
183	for (i = 0; i < argc; i++) {
184		TAILQ_FOREACH(tmp, &node->children, entry) {
185			if (cmd_check(argv[i], tmp, &param[np])) {
186				stack[i] = tmp;
187				node = tmp;
188				param[np].type = node->type;
189				if (node->type != P_TOKEN)
190					np++;
191				break;
192			}
193		}
194		if (tmp == NULL) {
195			best = NULL;
196			TAILQ_FOREACH(tmp, &node->children, entry) {
197				if (tmp->type != P_TOKEN)
198					continue;
199				if (strstr(tmp->token, argv[i]) != tmp->token)
200					continue;
201				if (best)
202					goto fail;
203				best = tmp;
204			}
205			if (best == NULL)
206				goto fail;
207			stack[i] = best;
208			node = best;
209			param[np].type = node->type;
210			if (node->type != P_TOKEN)
211				np++;
212		}
213	}
214
215	if (node->cmd == NULL)
216		goto fail;
217
218	return (node->cmd(np, np ? param : NULL));
219
220fail:
221	if (TAILQ_FIRST(&node->children) == NULL) {
222		fprintf(stderr, "invalid command\n");
223		return (-1);
224	}
225
226	fprintf(stderr, "possibilities are:\n");
227	TAILQ_FOREACH(tmp, &node->children, entry) {
228		for (j = 0; j < i; j++)
229			fprintf(stderr, "%s%s", j?" ":"", stack[j]->token);
230		fprintf(stderr, "%s%s\n", i?" ":"", tmp->token);
231	}
232
233	return (-1);
234}
235
236int
237cmd_show_params(int argc, struct parameter *argv)
238{
239	int	i;
240
241	for (i = 0; i < argc; i++) {
242		switch(argv[i].type) {
243		case P_STR:
244			printf(" str:\"%s\"", argv[i].u.u_str);
245			break;
246		case P_INT:
247			printf(" int:%d", argv[i].u.u_int);
248			break;
249		case P_MSGID:
250			printf(" msgid:%08"PRIx32, argv[i].u.u_msgid);
251			break;
252		case P_EVPID:
253			printf(" evpid:%016"PRIx64, argv[i].u.u_evpid);
254			break;
255		case P_ROUTEID:
256			printf(" routeid:%016"PRIx64, argv[i].u.u_routeid);
257			break;
258		default:
259			printf(" ???:%d", argv[i].type);
260		}
261	}
262	printf ("\n");
263	return (1);
264}
265
266static int
267text_to_sockaddr(struct sockaddr *sa, int family, const char *str)
268{
269	struct in_addr		 ina;
270	struct in6_addr		 in6a;
271	struct sockaddr_in	*in;
272	struct sockaddr_in6	*in6;
273	char			*cp, *str2;
274	const char		*errstr;
275
276	switch (family) {
277	case PF_UNSPEC:
278		if (text_to_sockaddr(sa, PF_INET, str) == 0)
279			return (0);
280		return text_to_sockaddr(sa, PF_INET6, str);
281
282	case PF_INET:
283		if (inet_pton(PF_INET, str, &ina) != 1)
284			return (-1);
285
286		in = (struct sockaddr_in *)sa;
287		memset(in, 0, sizeof *in);
288		in->sin_len = sizeof(struct sockaddr_in);
289		in->sin_family = PF_INET;
290		in->sin_addr.s_addr = ina.s_addr;
291		return (0);
292
293	case PF_INET6:
294		cp = strchr(str, SCOPE_DELIMITER);
295		if (cp) {
296			str2 = strdup(str);
297			if (str2 == NULL)
298				return (-1);
299			str2[cp - str] = '\0';
300			if (inet_pton(PF_INET6, str2, &in6a) != 1) {
301				free(str2);
302				return (-1);
303			}
304			cp++;
305			free(str2);
306		} else if (inet_pton(PF_INET6, str, &in6a) != 1)
307			return (-1);
308
309		in6 = (struct sockaddr_in6 *)sa;
310		memset(in6, 0, sizeof *in6);
311		in6->sin6_len = sizeof(struct sockaddr_in6);
312		in6->sin6_family = PF_INET6;
313		in6->sin6_addr = in6a;
314
315		if (cp == NULL)
316			return (0);
317
318		if (IN6_IS_ADDR_LINKLOCAL(&in6a) ||
319		    IN6_IS_ADDR_MC_LINKLOCAL(&in6a) ||
320		    IN6_IS_ADDR_MC_INTFACELOCAL(&in6a))
321			if ((in6->sin6_scope_id = if_nametoindex(cp)))
322				return (0);
323
324		in6->sin6_scope_id = strtonum(cp, 0, UINT32_MAX, &errstr);
325		if (errstr)
326			return (-1);
327		return (0);
328
329	default:
330		break;
331	}
332
333	return (-1);
334}
335