xref: /openbsd/usr.sbin/smtpd/parser.c (revision 8529ddd3)
1 /*	$OpenBSD: parser.c,v 1.40 2015/01/09 08:28:02 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 void
121 cmd_dump(struct node *node, int depth)
122 {
123 	struct node	*n;
124 	int		 i;
125 
126 	for(i = 0; i < depth; i++)
127 		printf("  ");
128 	printf("%s\n", node->token ? node->token : "");
129 
130 	TAILQ_FOREACH(n, &node->children, entry)
131 		cmd_dump(n, depth + 1);
132 }
133 
134 static int
135 cmd_check(const char *str, struct node *node, struct parameter *res)
136 {
137 	const char *e;
138 
139 	switch (node->type) {
140 	case P_TOKEN:
141 		if (!strcmp(str, node->token))
142 			return (1);
143 		return (0);
144 
145 	case P_STR:
146 		res->u.u_str = str;
147 		return (1);
148 
149 	case P_INT:
150 		res->u.u_int = strtonum(str, INT_MIN, INT_MAX, &e);
151 		if (e)
152 			return (0);
153 		return (1);
154 
155 	case P_MSGID:
156 		if (strlen(str) != 8)
157 			return (0);
158 		res->u.u_msgid = text_to_msgid(str);
159 		if (res->u.u_msgid == 0)
160 			return (0);
161 		return (1);
162 
163 	case P_EVPID:
164 		if (strlen(str) != 16)
165 			return (0);
166 		res->u.u_evpid = text_to_evpid(str);
167 		if (res->u.u_evpid == 0)
168 			return (0);
169 		return (1);
170 
171 	case P_ROUTEID:
172 		res->u.u_routeid = strtonum(str, 1, LLONG_MAX, &e);
173 		if (e)
174 			return (0);
175 		return (1);
176 
177 	case P_ADDR:
178 		if (text_to_sockaddr((struct sockaddr *)&res->u.u_ss, PF_UNSPEC, str) == 0)
179 			return (1);
180 		return (0);
181 
182 	default:
183 		errx(1, "bad token type: %d", node->type);
184 		return (0);
185 	}
186 }
187 
188 int
189 cmd_run(int argc, char **argv)
190 {
191 	struct parameter param[ARGVMAX];
192 	struct node	*node, *tmp, *stack[ARGVMAX], *best;
193 	int		 i, j, np;
194 
195 	node = root;
196 	np = 0;
197 
198 	for (i = 0; i < argc; i++) {
199 		TAILQ_FOREACH(tmp, &node->children, entry) {
200 			if (cmd_check(argv[i], tmp, &param[np])) {
201 				stack[i] = tmp;
202 				node = tmp;
203 				param[np].type = node->type;
204 				if (node->type != P_TOKEN)
205 					np++;
206 				break;
207 			}
208 		}
209 		if (tmp == NULL) {
210 			best = NULL;
211 			TAILQ_FOREACH(tmp, &node->children, entry) {
212 				if (tmp->type != P_TOKEN)
213 					continue;
214 				if (strstr(tmp->token, argv[i]) != tmp->token)
215 					continue;
216 				if (best)
217 					goto fail;
218 				best = tmp;
219 			}
220 			if (best == NULL)
221 				goto fail;
222 			stack[i] = best;
223 			node = best;
224 			param[np].type = node->type;
225 			if (node->type != P_TOKEN)
226 				np++;
227 		}
228 	}
229 
230 	if (node->cmd == NULL)
231 		goto fail;
232 
233 	return (node->cmd(np, np ? param : NULL));
234 
235 fail:
236 	fprintf(stderr, "possibilities are:\n");
237 	TAILQ_FOREACH(tmp, &node->children, entry) {
238 		for (j = 0; j < i; j++)
239 			fprintf(stderr, "%s%s", j?" ":"", stack[j]->token);
240 		fprintf(stderr, "%s%s\n", i?" ":"", tmp->token);
241 	}
242 
243 	return (-1);
244 }
245 
246 int
247 cmd_show_params(int argc, struct parameter *argv)
248 {
249 	int	i;
250 
251 	for (i = 0; i < argc; i++) {
252 		switch(argv[i].type) {
253 		case P_STR:
254 			printf(" str:\"%s\"", argv[i].u.u_str);
255 			break;
256 		case P_INT:
257 			printf(" int:%d", argv[i].u.u_int);
258 			break;
259 		case P_MSGID:
260 			printf(" msgid:%08"PRIx32, argv[i].u.u_msgid);
261 			break;
262 		case P_EVPID:
263 			printf(" evpid:%016"PRIx64, argv[i].u.u_evpid);
264 			break;
265 		case P_ROUTEID:
266 			printf(" routeid:%016"PRIx64, argv[i].u.u_routeid);
267 			break;
268 		default:
269 			printf(" ???:%d", argv[i].type);
270 		}
271 	}
272 	printf ("\n");
273 	return (1);
274 }
275 
276 static int
277 text_to_sockaddr(struct sockaddr *sa, int family, const char *str)
278 {
279 	struct in_addr		 ina;
280 	struct in6_addr		 in6a;
281 	struct sockaddr_in	*in;
282 	struct sockaddr_in6	*in6;
283 	char			*cp, *str2;
284 	const char		*errstr;
285 
286 	switch (family) {
287 	case PF_UNSPEC:
288 		if (text_to_sockaddr(sa, PF_INET, str) == 0)
289 			return (0);
290 		return text_to_sockaddr(sa, PF_INET6, str);
291 
292 	case PF_INET:
293 		if (inet_pton(PF_INET, str, &ina) != 1)
294 			return (-1);
295 
296 		in = (struct sockaddr_in *)sa;
297 		memset(in, 0, sizeof *in);
298 		in->sin_len = sizeof(struct sockaddr_in);
299 		in->sin_family = PF_INET;
300 		in->sin_addr.s_addr = ina.s_addr;
301 		return (0);
302 
303 	case PF_INET6:
304 		cp = strchr(str, SCOPE_DELIMITER);
305 		if (cp) {
306 			str2 = strdup(str);
307 			if (str2 == NULL)
308 				return (-1);
309 			str2[cp - str] = '\0';
310 			if (inet_pton(PF_INET6, str2, &in6a) != 1) {
311 				free(str2);
312 				return (-1);
313 			}
314 			cp++;
315 			free(str2);
316 		} else if (inet_pton(PF_INET6, str, &in6a) != 1)
317 			return (-1);
318 
319 		in6 = (struct sockaddr_in6 *)sa;
320 		memset(in6, 0, sizeof *in6);
321 		in6->sin6_len = sizeof(struct sockaddr_in6);
322 		in6->sin6_family = PF_INET6;
323 		in6->sin6_addr = in6a;
324 
325 		if (cp == NULL)
326 			return (0);
327 
328 		if (IN6_IS_ADDR_LINKLOCAL(&in6a) ||
329 		    IN6_IS_ADDR_MC_LINKLOCAL(&in6a) ||
330 		    IN6_IS_ADDR_MC_INTFACELOCAL(&in6a))
331 			if ((in6->sin6_scope_id = if_nametoindex(cp)))
332 				return (0);
333 
334 		in6->sin6_scope_id = strtonum(cp, 0, UINT32_MAX, &errstr);
335 		if (errstr)
336 			return (-1);
337 		return (0);
338 
339 	default:
340 		break;
341 	}
342 
343 	return (-1);
344 }
345