1 /* $OpenBSD: parser.c,v 1.6 2024/09/15 05:26:05 yasuoka Exp $ */ 2 3 /* 4 * Copyright (c) 2010 Reyk Floeter <reyk@vantronix.net> 5 * Copyright (c) 2004 Esben Norby <norby@openbsd.org> 6 * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> 7 * 8 * Permission to use, copy, modify, and distribute this software for any 9 * purpose with or without fee is hereby granted, provided that the above 10 * copyright notice and this permission notice appear in all copies. 11 * 12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 13 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 14 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 15 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 16 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 17 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 18 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 19 */ 20 21 #include <sys/time.h> 22 23 #include <stdint.h> 24 #include <stdio.h> 25 #include <stdlib.h> 26 #include <string.h> 27 #include <strings.h> 28 #include <limits.h> 29 30 #include "parser.h" 31 32 enum token_type { 33 NOTOKEN, 34 KEYWORD, 35 HOSTNAME, 36 SECRET, 37 USERNAME, 38 PASSWORD, 39 PORT, 40 METHOD, 41 NAS_PORT, 42 TRIES, 43 INTERVAL, 44 MAXWAIT, 45 FLAGS, 46 SESSION_SEQ, 47 MSGAUTH, 48 ENDTOKEN 49 }; 50 51 struct token { 52 enum token_type type; 53 const char *keyword; 54 int value; 55 const struct token *next; 56 }; 57 58 static struct parse_result res = { 59 .tries = TEST_TRIES_DEFAULT, 60 .interval = { TEST_INTERVAL_DEFAULT, 0 }, 61 .maxwait = { TEST_MAXWAIT_DEFAULT, 0 }, 62 .msgauth = 1 63 }; 64 65 static const struct token t_test[]; 66 static const struct token t_secret[]; 67 static const struct token t_username[]; 68 static const struct token t_test_opts[]; 69 static const struct token t_password[]; 70 static const struct token t_port[]; 71 static const struct token t_method[]; 72 static const struct token t_nas_port[]; 73 static const struct token t_tries[]; 74 static const struct token t_interval[]; 75 static const struct token t_maxwait[]; 76 static const struct token t_yesno[]; 77 static const struct token t_ipcp[]; 78 static const struct token t_ipcp_flags[]; 79 static const struct token t_ipcp_session_seq[]; 80 81 static const struct token t_main[] = { 82 { KEYWORD, "test", TEST, t_test }, 83 { KEYWORD, "ipcp", NONE, t_ipcp }, 84 { ENDTOKEN, "", NONE, NULL } 85 }; 86 87 static const struct token t_test[] = { 88 { HOSTNAME, "", NONE, t_secret }, 89 { ENDTOKEN, "", NONE, NULL } 90 }; 91 92 static const struct token t_secret[] = { 93 { SECRET, "", NONE, t_username }, 94 { ENDTOKEN, "", NONE, NULL } 95 }; 96 97 static const struct token t_username[] = { 98 { USERNAME, "", NONE, t_test_opts }, 99 { ENDTOKEN, "", NONE, NULL } 100 }; 101 102 static const struct token t_test_opts[] = { 103 { NOTOKEN, "", NONE, NULL }, 104 { KEYWORD, "password", NONE, t_password }, 105 { KEYWORD, "port", NONE, t_port }, 106 { KEYWORD, "method", NONE, t_method }, 107 { KEYWORD, "nas-port", NONE, t_nas_port }, 108 { KEYWORD, "interval", NONE, t_interval }, 109 { KEYWORD, "tries", NONE, t_tries }, 110 { KEYWORD, "maxwait", NONE, t_maxwait }, 111 { KEYWORD, "msgauth", NONE, t_yesno }, 112 { ENDTOKEN, "", NONE, NULL } 113 }; 114 115 static const struct token t_password[] = { 116 { PASSWORD, "", NONE, t_test_opts }, 117 { ENDTOKEN, "", NONE, NULL } 118 }; 119 120 static const struct token t_port[] = { 121 { PORT, "", NONE, t_test_opts }, 122 { ENDTOKEN, "", NONE, NULL } 123 }; 124 125 static const struct token t_method[] = { 126 { METHOD, "", NONE, t_test_opts }, 127 { ENDTOKEN, "", NONE, NULL } 128 }; 129 130 static const struct token t_nas_port[] = { 131 { NAS_PORT, "", NONE, t_test_opts }, 132 { ENDTOKEN, "", NONE, NULL } 133 }; 134 135 static const struct token t_tries[] = { 136 { TRIES, "", NONE, t_test_opts }, 137 { ENDTOKEN, "", NONE, NULL } 138 }; 139 140 static const struct token t_interval[] = { 141 { INTERVAL, "", NONE, t_test_opts }, 142 { ENDTOKEN, "", NONE, NULL } 143 }; 144 145 static const struct token t_maxwait[] = { 146 { MAXWAIT, "", NONE, t_test_opts }, 147 { ENDTOKEN, "", NONE, NULL } 148 }; 149 150 static const struct token t_yesno[] = { 151 { MSGAUTH, "yes", 1, t_test_opts }, 152 { MSGAUTH, "no", 0, t_test_opts }, 153 { ENDTOKEN, "", NONE, NULL } 154 }; 155 156 static const struct token t_ipcp[] = { 157 { KEYWORD, "show", IPCP_SHOW, NULL }, 158 { KEYWORD, "dump", IPCP_DUMP, t_ipcp_flags }, 159 { KEYWORD, "monitor", IPCP_MONITOR, t_ipcp_flags }, 160 { KEYWORD, "disconnect", IPCP_DISCONNECT,t_ipcp_session_seq }, 161 { KEYWORD, "delete", IPCP_DELETE, t_ipcp_session_seq }, 162 { ENDTOKEN, "", NONE, NULL } 163 }; 164 165 static const struct token t_ipcp_flags[] = { 166 { NOTOKEN, "", NONE, NULL }, 167 { FLAGS, "-json", FLAGS_JSON, NULL }, 168 { ENDTOKEN, "", NONE, NULL } 169 }; 170 171 static const struct token t_ipcp_session_seq[] = { 172 { SESSION_SEQ, "", NONE, NULL }, 173 { ENDTOKEN, "", NONE, NULL } 174 }; 175 176 static const struct token *match_token(char *, const struct token []); 177 static void show_valid_args(const struct token []); 178 179 struct parse_result * 180 parse(int argc, char *argv[]) 181 { 182 const struct token *table = t_main; 183 const struct token *match; 184 185 while (argc >= 0) { 186 if ((match = match_token(argv[0], table)) == NULL) { 187 fprintf(stderr, "valid commands/args:\n"); 188 show_valid_args(table); 189 return (NULL); 190 } 191 192 argc--; 193 argv++; 194 195 if (match->type == NOTOKEN || match->next == NULL) 196 break; 197 198 table = match->next; 199 } 200 201 if (argc > 0) { 202 fprintf(stderr, "superfluous argument: %s\n", argv[0]); 203 return (NULL); 204 } 205 206 if (res.tries * res.interval.tv_sec > res.maxwait.tv_sec) { 207 fprintf(stderr, "tries %u by interval %lld > maxwait %lld", 208 res.tries, res.interval.tv_sec, res.maxwait.tv_sec); 209 return (NULL); 210 } 211 212 return (&res); 213 } 214 215 static const struct token * 216 match_token(char *word, const struct token table[]) 217 { 218 u_int i, match = 0; 219 const struct token *t = NULL; 220 long long num; 221 const char *errstr; 222 size_t wordlen = 0; 223 224 if (word != NULL) 225 wordlen = strlen(word); 226 227 for (i = 0; table[i].type != ENDTOKEN; i++) { 228 switch (table[i].type) { 229 case NOTOKEN: 230 if (word == NULL || strlen(word) == 0) { 231 match++; 232 t = &table[i]; 233 } 234 break; 235 case KEYWORD: 236 if (word != NULL && strncmp(word, table[i].keyword, 237 wordlen) == 0) { 238 match++; 239 t = &table[i]; 240 if (t->value) 241 res.action = t->value; 242 } 243 break; 244 case HOSTNAME: 245 if (word == NULL) 246 break; 247 match++; 248 res.hostname = word; 249 t = &table[i]; 250 break; 251 case SECRET: 252 if (word == NULL) 253 break; 254 match++; 255 res.secret = word; 256 t = &table[i]; 257 break; 258 case USERNAME: 259 if (word == NULL) 260 break; 261 match++; 262 res.username = word; 263 t = &table[i]; 264 break; 265 case PASSWORD: 266 if (word == NULL) 267 break; 268 match++; 269 res.password = word; 270 t = &table[i]; 271 break; 272 case PORT: 273 if (word == NULL) 274 break; 275 num = strtonum(word, 1, UINT16_MAX, &errstr); 276 if (errstr != NULL) { 277 fprintf(stderr, 278 "invalid argument: %s is %s for \"port\"\n", 279 word, errstr); 280 return (NULL); 281 } 282 match++; 283 res.port = num; 284 t = &table[i]; 285 break; 286 case METHOD: 287 if (word == NULL) 288 break; 289 if (strcasecmp(word, "pap") == 0) 290 res.auth_method = PAP; 291 else if (strcasecmp(word, "chap") == 0) 292 res.auth_method = CHAP; 293 else if (strcasecmp(word, "mschapv2") == 0) 294 res.auth_method = MSCHAPV2; 295 else { 296 fprintf(stderr, 297 "invalid argument: %s for \"method\"\n", 298 word); 299 return (NULL); 300 } 301 match++; 302 t = &table[i]; 303 break; 304 case NAS_PORT: 305 if (word == NULL) 306 break; 307 num = strtonum(word, 0, 65535, &errstr); 308 if (errstr != NULL) { 309 fprintf(stderr, 310 "invalid argument: %s is %s for " 311 "\"nas-port\"\n", word, errstr); 312 return (NULL); 313 } 314 match++; 315 res.nas_port = num; 316 t = &table[i]; 317 break; 318 319 case TRIES: 320 if (word == NULL) 321 break; 322 num = strtonum(word, 323 TEST_TRIES_MIN, TEST_TRIES_MAX, &errstr); 324 if (errstr != NULL) { 325 printf("invalid argument: %s is %s" 326 " for \"tries\"\n", word, errstr); 327 return (NULL); 328 } 329 match++; 330 res.tries = num; 331 t = &table[i]; 332 break; 333 case INTERVAL: 334 if (word == NULL) 335 break; 336 num = strtonum(word, 337 TEST_INTERVAL_MIN, TEST_INTERVAL_MAX, &errstr); 338 if (errstr != NULL) { 339 printf("invalid argument: %s is %s" 340 " for \"interval\"\n", word, errstr); 341 return (NULL); 342 } 343 match++; 344 res.interval.tv_sec = num; 345 t = &table[i]; 346 break; 347 case MAXWAIT: 348 if (word == NULL) 349 break; 350 num = strtonum(word, 351 TEST_MAXWAIT_MIN, TEST_MAXWAIT_MAX, &errstr); 352 if (errstr != NULL) { 353 printf("invalid argument: %s is %s" 354 " for \"maxwait\"\n", word, errstr); 355 return (NULL); 356 } 357 match++; 358 res.maxwait.tv_sec = num; 359 t = &table[i]; 360 break; 361 case FLAGS: 362 if (word != NULL && wordlen >= 2 && 363 strncmp(word, table[i].keyword, wordlen) == 0) { 364 match++; 365 t = &table[i]; 366 if (t->value) 367 res.flags |= t->value; 368 } 369 break; 370 case SESSION_SEQ: 371 if (word == NULL) 372 break; 373 match++; 374 res.session_seq = strtonum(word, 1, UINT_MAX, &errstr); 375 if (errstr != NULL) { 376 printf("invalid argument: %s is %s for " 377 "\"session-id\"\n", word, errstr); 378 return (NULL); 379 } 380 t = &table[i]; 381 case MSGAUTH: 382 if (word != NULL && 383 strcmp(word, table[i].keyword) == 0) { 384 match++; 385 res.msgauth = table[i].value; 386 t = &table[i]; 387 } 388 break; 389 case ENDTOKEN: 390 break; 391 } 392 } 393 394 if (match != 1) { 395 if (word == NULL) 396 fprintf(stderr, "missing argument:\n"); 397 else if (match > 1) 398 fprintf(stderr, "ambiguous argument: %s\n", word); 399 else if (match < 1) 400 fprintf(stderr, "unknown argument: %s\n", word); 401 return (NULL); 402 } 403 404 return (t); 405 } 406 407 static void 408 show_valid_args(const struct token table[]) 409 { 410 int i; 411 412 for (i = 0; table[i].type != ENDTOKEN; i++) { 413 switch (table[i].type) { 414 case NOTOKEN: 415 fprintf(stderr, " <cr>\n"); 416 break; 417 case KEYWORD: 418 fprintf(stderr, " %s\n", table[i].keyword); 419 break; 420 case HOSTNAME: 421 fprintf(stderr, " <hostname>\n"); 422 break; 423 case SECRET: 424 fprintf(stderr, " <radius secret>\n"); 425 break; 426 case USERNAME: 427 fprintf(stderr, " <username>\n"); 428 break; 429 case PASSWORD: 430 fprintf(stderr, " <password>\n"); 431 break; 432 case PORT: 433 fprintf(stderr, " <port number>\n"); 434 break; 435 case METHOD: 436 fprintf(stderr, " <auth method (pap, chap, " 437 "mschapv2)>\n"); 438 break; 439 case NAS_PORT: 440 fprintf(stderr, " <nas-port (0-65535)>\n"); 441 break; 442 case TRIES: 443 fprintf(stderr, " <tries (%u-%u)>\n", 444 TEST_TRIES_MIN, TEST_TRIES_MAX); 445 break; 446 case INTERVAL: 447 fprintf(stderr, " <interval (%u-%u)>\n", 448 TEST_INTERVAL_MIN, TEST_INTERVAL_MAX); 449 break; 450 case MAXWAIT: 451 fprintf(stderr, " <maxwait (%u-%u)>\n", 452 TEST_MAXWAIT_MIN, TEST_MAXWAIT_MAX); 453 break; 454 case FLAGS: 455 fprintf(stderr, " %s\n", table[i].keyword); 456 break; 457 case SESSION_SEQ: 458 fprintf(stderr, " <sequence number>\n"); 459 break; 460 case MSGAUTH: 461 fprintf(stderr, " %s\n", table[i].keyword); 462 break; 463 case ENDTOKEN: 464 break; 465 } 466 } 467 } 468