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 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 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 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 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 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