xref: /openbsd-src/usr.sbin/smtpd/parser.c (revision ae3cb403620ab940fbaabb3055fac045a63d56b7)
1 /*	$OpenBSD: parser.c,v 1.41 2017/07/31 16:38:33 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 	fprintf(stderr, "possibilities are:\n");
223 	TAILQ_FOREACH(tmp, &node->children, entry) {
224 		for (j = 0; j < i; j++)
225 			fprintf(stderr, "%s%s", j?" ":"", stack[j]->token);
226 		fprintf(stderr, "%s%s\n", i?" ":"", tmp->token);
227 	}
228 
229 	return (-1);
230 }
231 
232 int
233 cmd_show_params(int argc, struct parameter *argv)
234 {
235 	int	i;
236 
237 	for (i = 0; i < argc; i++) {
238 		switch(argv[i].type) {
239 		case P_STR:
240 			printf(" str:\"%s\"", argv[i].u.u_str);
241 			break;
242 		case P_INT:
243 			printf(" int:%d", argv[i].u.u_int);
244 			break;
245 		case P_MSGID:
246 			printf(" msgid:%08"PRIx32, argv[i].u.u_msgid);
247 			break;
248 		case P_EVPID:
249 			printf(" evpid:%016"PRIx64, argv[i].u.u_evpid);
250 			break;
251 		case P_ROUTEID:
252 			printf(" routeid:%016"PRIx64, argv[i].u.u_routeid);
253 			break;
254 		default:
255 			printf(" ???:%d", argv[i].type);
256 		}
257 	}
258 	printf ("\n");
259 	return (1);
260 }
261 
262 static int
263 text_to_sockaddr(struct sockaddr *sa, int family, const char *str)
264 {
265 	struct in_addr		 ina;
266 	struct in6_addr		 in6a;
267 	struct sockaddr_in	*in;
268 	struct sockaddr_in6	*in6;
269 	char			*cp, *str2;
270 	const char		*errstr;
271 
272 	switch (family) {
273 	case PF_UNSPEC:
274 		if (text_to_sockaddr(sa, PF_INET, str) == 0)
275 			return (0);
276 		return text_to_sockaddr(sa, PF_INET6, str);
277 
278 	case PF_INET:
279 		if (inet_pton(PF_INET, str, &ina) != 1)
280 			return (-1);
281 
282 		in = (struct sockaddr_in *)sa;
283 		memset(in, 0, sizeof *in);
284 		in->sin_len = sizeof(struct sockaddr_in);
285 		in->sin_family = PF_INET;
286 		in->sin_addr.s_addr = ina.s_addr;
287 		return (0);
288 
289 	case PF_INET6:
290 		cp = strchr(str, SCOPE_DELIMITER);
291 		if (cp) {
292 			str2 = strdup(str);
293 			if (str2 == NULL)
294 				return (-1);
295 			str2[cp - str] = '\0';
296 			if (inet_pton(PF_INET6, str2, &in6a) != 1) {
297 				free(str2);
298 				return (-1);
299 			}
300 			cp++;
301 			free(str2);
302 		} else if (inet_pton(PF_INET6, str, &in6a) != 1)
303 			return (-1);
304 
305 		in6 = (struct sockaddr_in6 *)sa;
306 		memset(in6, 0, sizeof *in6);
307 		in6->sin6_len = sizeof(struct sockaddr_in6);
308 		in6->sin6_family = PF_INET6;
309 		in6->sin6_addr = in6a;
310 
311 		if (cp == NULL)
312 			return (0);
313 
314 		if (IN6_IS_ADDR_LINKLOCAL(&in6a) ||
315 		    IN6_IS_ADDR_MC_LINKLOCAL(&in6a) ||
316 		    IN6_IS_ADDR_MC_INTFACELOCAL(&in6a))
317 			if ((in6->sin6_scope_id = if_nametoindex(cp)))
318 				return (0);
319 
320 		in6->sin6_scope_id = strtonum(cp, 0, UINT32_MAX, &errstr);
321 		if (errstr)
322 			return (-1);
323 		return (0);
324 
325 	default:
326 		break;
327 	}
328 
329 	return (-1);
330 }
331