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, ¶m[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