xref: /openbsd/usr.sbin/smtpd/parser.c (revision 5dea098c)
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 
36 uint64_t text_to_evpid(const char *);
37 uint32_t text_to_msgid(const char *);
38 
39 struct 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 
48 static struct node	*root;
49 
50 static int text_to_sockaddr(struct sockaddr *, int, const char *);
51 
52 #define ARGVMAX	64
53 
54 int
55 cmd_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 
119 static int
120 cmd_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 
173 int
174 cmd_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 
220 fail:
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 
236 int
237 cmd_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 
266 static int
267 text_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