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