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