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
cmd_install(const char * pattern,int (* cmd)(int,struct parameter *))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
cmd_check(const char * str,struct node * node,struct parameter * res)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
cmd_run(int argc,char ** argv)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, ¶m[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
cmd_show_params(int argc,struct parameter * argv)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
text_to_sockaddr(struct sockaddr * sa,int family,const char * str)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