1 /* $OpenBSD: parser.c,v 1.4 2024/07/24 08:27:20 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 { ENDTOKEN, "", NONE, NULL } 162 }; 163 164 static const struct token t_ipcp_flags[] = { 165 { NOTOKEN, "", NONE, NULL }, 166 { FLAGS, "-json", FLAGS_JSON, NULL }, 167 { ENDTOKEN, "", NONE, NULL } 168 }; 169 170 static const struct token t_ipcp_session_seq[] = { 171 { SESSION_SEQ, "", NONE, NULL }, 172 { ENDTOKEN, "", NONE, NULL } 173 }; 174 175 static const struct token *match_token(char *, const struct token []); 176 static void show_valid_args(const struct token []); 177 178 struct parse_result * 179 parse(int argc, char *argv[]) 180 { 181 const struct token *table = t_main; 182 const struct token *match; 183 184 while (argc >= 0) { 185 if ((match = match_token(argv[0], table)) == NULL) { 186 fprintf(stderr, "valid commands/args:\n"); 187 show_valid_args(table); 188 return (NULL); 189 } 190 191 argc--; 192 argv++; 193 194 if (match->type == NOTOKEN || match->next == NULL) 195 break; 196 197 table = match->next; 198 } 199 200 if (argc > 0) { 201 fprintf(stderr, "superfluous argument: %s\n", argv[0]); 202 return (NULL); 203 } 204 205 if (res.tries * res.interval.tv_sec > res.maxwait.tv_sec) { 206 fprintf(stderr, "tries %u by interval %lld > maxwait %lld", 207 res.tries, res.interval.tv_sec, res.maxwait.tv_sec); 208 return (NULL); 209 } 210 211 return (&res); 212 } 213 214 static const struct token * 215 match_token(char *word, const struct token table[]) 216 { 217 u_int i, match = 0; 218 const struct token *t = NULL; 219 long long num; 220 const char *errstr; 221 size_t wordlen = 0; 222 223 if (word != NULL) 224 wordlen = strlen(word); 225 226 for (i = 0; table[i].type != ENDTOKEN; i++) { 227 switch (table[i].type) { 228 case NOTOKEN: 229 if (word == NULL || strlen(word) == 0) { 230 match++; 231 t = &table[i]; 232 } 233 break; 234 case KEYWORD: 235 if (word != NULL && strncmp(word, table[i].keyword, 236 wordlen) == 0) { 237 match++; 238 t = &table[i]; 239 if (t->value) 240 res.action = t->value; 241 } 242 break; 243 case HOSTNAME: 244 if (word == NULL) 245 break; 246 match++; 247 res.hostname = word; 248 t = &table[i]; 249 break; 250 case SECRET: 251 if (word == NULL) 252 break; 253 match++; 254 res.secret = word; 255 t = &table[i]; 256 break; 257 case USERNAME: 258 if (word == NULL) 259 break; 260 match++; 261 res.username = word; 262 t = &table[i]; 263 break; 264 case PASSWORD: 265 if (word == NULL) 266 break; 267 match++; 268 res.password = word; 269 t = &table[i]; 270 break; 271 case PORT: 272 if (word == NULL) 273 break; 274 num = strtonum(word, 1, UINT16_MAX, &errstr); 275 if (errstr != NULL) { 276 fprintf(stderr, 277 "invalid argument: %s is %s for \"port\"\n", 278 word, errstr); 279 return (NULL); 280 } 281 match++; 282 res.port = num; 283 t = &table[i]; 284 break; 285 case METHOD: 286 if (word == NULL) 287 break; 288 if (strcasecmp(word, "pap") == 0) 289 res.auth_method = PAP; 290 else if (strcasecmp(word, "chap") == 0) 291 res.auth_method = CHAP; 292 else if (strcasecmp(word, "mschapv2") == 0) 293 res.auth_method = MSCHAPV2; 294 else { 295 fprintf(stderr, 296 "invalid argument: %s for \"method\"\n", 297 word); 298 return (NULL); 299 } 300 match++; 301 t = &table[i]; 302 break; 303 case NAS_PORT: 304 if (word == NULL) 305 break; 306 num = strtonum(word, 0, 65535, &errstr); 307 if (errstr != NULL) { 308 fprintf(stderr, 309 "invalid argument: %s is %s for " 310 "\"nas-port\"\n", word, errstr); 311 return (NULL); 312 } 313 match++; 314 res.nas_port = num; 315 t = &table[i]; 316 break; 317 318 case TRIES: 319 if (word == NULL) 320 break; 321 num = strtonum(word, 322 TEST_TRIES_MIN, TEST_TRIES_MAX, &errstr); 323 if (errstr != NULL) { 324 printf("invalid argument: %s is %s" 325 " for \"tries\"\n", word, errstr); 326 return (NULL); 327 } 328 match++; 329 res.tries = num; 330 t = &table[i]; 331 break; 332 case INTERVAL: 333 if (word == NULL) 334 break; 335 num = strtonum(word, 336 TEST_INTERVAL_MIN, TEST_INTERVAL_MAX, &errstr); 337 if (errstr != NULL) { 338 printf("invalid argument: %s is %s" 339 " for \"interval\"\n", word, errstr); 340 return (NULL); 341 } 342 match++; 343 res.interval.tv_sec = num; 344 t = &table[i]; 345 break; 346 case MAXWAIT: 347 if (word == NULL) 348 break; 349 num = strtonum(word, 350 TEST_MAXWAIT_MIN, TEST_MAXWAIT_MAX, &errstr); 351 if (errstr != NULL) { 352 printf("invalid argument: %s is %s" 353 " for \"maxwait\"\n", word, errstr); 354 return (NULL); 355 } 356 match++; 357 res.maxwait.tv_sec = num; 358 t = &table[i]; 359 break; 360 case FLAGS: 361 if (word != NULL && wordlen >= 2 && 362 strncmp(word, table[i].keyword, wordlen) == 0) { 363 match++; 364 t = &table[i]; 365 if (t->value) 366 res.flags |= t->value; 367 } 368 break; 369 case SESSION_SEQ: 370 if (word == NULL) 371 break; 372 match++; 373 res.session_seq = strtonum(word, 1, UINT_MAX, &errstr); 374 if (errstr != NULL) 375 printf("invalid argument: %s is %s for " 376 "\"session-id\"", word, errstr); 377 t = &table[i]; 378 case MSGAUTH: 379 if (word != NULL && 380 strcmp(word, table[i].keyword) == 0) { 381 match++; 382 res.msgauth = table[i].value; 383 t = &table[i]; 384 } 385 break; 386 case ENDTOKEN: 387 break; 388 } 389 } 390 391 if (match != 1) { 392 if (word == NULL) 393 fprintf(stderr, "missing argument:\n"); 394 else if (match > 1) 395 fprintf(stderr, "ambiguous argument: %s\n", word); 396 else if (match < 1) 397 fprintf(stderr, "unknown argument: %s\n", word); 398 return (NULL); 399 } 400 401 return (t); 402 } 403 404 static void 405 show_valid_args(const struct token table[]) 406 { 407 int i; 408 409 for (i = 0; table[i].type != ENDTOKEN; i++) { 410 switch (table[i].type) { 411 case NOTOKEN: 412 fprintf(stderr, " <cr>\n"); 413 break; 414 case KEYWORD: 415 fprintf(stderr, " %s\n", table[i].keyword); 416 break; 417 case HOSTNAME: 418 fprintf(stderr, " <hostname>\n"); 419 break; 420 case SECRET: 421 fprintf(stderr, " <radius secret>\n"); 422 break; 423 case USERNAME: 424 fprintf(stderr, " <username>\n"); 425 break; 426 case PASSWORD: 427 fprintf(stderr, " <password>\n"); 428 break; 429 case PORT: 430 fprintf(stderr, " <port number>\n"); 431 break; 432 case METHOD: 433 fprintf(stderr, " <auth method (pap, chap, " 434 "mschapv2)>\n"); 435 break; 436 case NAS_PORT: 437 fprintf(stderr, " <nas-port (0-65535)>\n"); 438 break; 439 case TRIES: 440 fprintf(stderr, " <tries (%u-%u)>\n", 441 TEST_TRIES_MIN, TEST_TRIES_MAX); 442 break; 443 case INTERVAL: 444 fprintf(stderr, " <interval (%u-%u)>\n", 445 TEST_INTERVAL_MIN, TEST_INTERVAL_MAX); 446 break; 447 case MAXWAIT: 448 fprintf(stderr, " <maxwait (%u-%u)>\n", 449 TEST_MAXWAIT_MIN, TEST_MAXWAIT_MAX); 450 break; 451 case FLAGS: 452 fprintf(stderr, " %s\n", table[i].keyword); 453 break; 454 case SESSION_SEQ: 455 fprintf(stderr, " <sequence number>\n"); 456 break; 457 case MSGAUTH: 458 fprintf(stderr, " %s\n", table[i].keyword); 459 break; 460 case ENDTOKEN: 461 break; 462 } 463 } 464 } 465