1 /* $OpenBSD: parser.c,v 1.2 2020/02/24 07:07:11 dlg 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 28 #include "parser.h" 29 30 enum token_type { 31 NOTOKEN, 32 KEYWORD, 33 HOSTNAME, 34 SECRET, 35 USERNAME, 36 PASSWORD, 37 PORT, 38 METHOD, 39 NAS_PORT, 40 TRIES, 41 INTERVAL, 42 MAXWAIT, 43 ENDTOKEN 44 }; 45 46 struct token { 47 enum token_type type; 48 const char *keyword; 49 int value; 50 const struct token *next; 51 }; 52 53 static struct parse_result res = { 54 .tries = TEST_TRIES_DEFAULT, 55 .interval = { TEST_INTERVAL_DEFAULT, 0 }, 56 .maxwait = { TEST_MAXWAIT_DEFAULT, 0 }, 57 }; 58 59 static const struct token t_test[]; 60 static const struct token t_secret[]; 61 static const struct token t_username[]; 62 static const struct token t_test_opts[]; 63 static const struct token t_password[]; 64 static const struct token t_port[]; 65 static const struct token t_method[]; 66 static const struct token t_nas_port[]; 67 static const struct token t_tries[]; 68 static const struct token t_interval[]; 69 static const struct token t_maxwait[]; 70 71 static const struct token t_main[] = { 72 { KEYWORD, "test", TEST, t_test }, 73 { ENDTOKEN, "", NONE, NULL } 74 }; 75 76 static const struct token t_test[] = { 77 { HOSTNAME, "", NONE, t_secret }, 78 { ENDTOKEN, "", NONE, NULL } 79 }; 80 81 static const struct token t_secret[] = { 82 { SECRET, "", NONE, t_username }, 83 { ENDTOKEN, "", NONE, NULL } 84 }; 85 86 static const struct token t_username[] = { 87 { USERNAME, "", NONE, t_test_opts }, 88 { ENDTOKEN, "", NONE, NULL } 89 }; 90 91 static const struct token t_test_opts[] = { 92 { NOTOKEN, "", NONE, NULL }, 93 { KEYWORD, "password", NONE, t_password }, 94 { KEYWORD, "port", NONE, t_port }, 95 { KEYWORD, "method", NONE, t_method }, 96 { KEYWORD, "nas-port", NONE, t_nas_port }, 97 { KEYWORD, "interval", NONE, t_interval }, 98 { KEYWORD, "tries", NONE, t_tries }, 99 { KEYWORD, "maxwait", NONE, t_maxwait }, 100 { ENDTOKEN, "", NONE, NULL } 101 }; 102 103 static const struct token t_password[] = { 104 { PASSWORD, "", NONE, t_test_opts }, 105 { ENDTOKEN, "", NONE, NULL } 106 }; 107 108 static const struct token t_port[] = { 109 { PORT, "", NONE, t_test_opts }, 110 { ENDTOKEN, "", NONE, NULL } 111 }; 112 113 static const struct token t_method[] = { 114 { METHOD, "", NONE, t_test_opts }, 115 { ENDTOKEN, "", NONE, NULL } 116 }; 117 118 static const struct token t_nas_port[] = { 119 { NAS_PORT, "", NONE, t_test_opts }, 120 { ENDTOKEN, "", NONE, NULL } 121 }; 122 123 static const struct token t_tries[] = { 124 { TRIES, "", NONE, t_test_opts }, 125 { ENDTOKEN, "", NONE, NULL } 126 }; 127 128 static const struct token t_interval[] = { 129 { INTERVAL, "", NONE, t_test_opts }, 130 { ENDTOKEN, "", NONE, NULL } 131 }; 132 133 static const struct token t_maxwait[] = { 134 { MAXWAIT, "", NONE, t_test_opts }, 135 { ENDTOKEN, "", NONE, NULL } 136 }; 137 138 139 static const struct token *match_token(char *, const struct token []); 140 static void show_valid_args(const struct token []); 141 142 struct parse_result * 143 parse(int argc, char *argv[]) 144 { 145 const struct token *table = t_main; 146 const struct token *match; 147 148 while (argc >= 0) { 149 if ((match = match_token(argv[0], table)) == NULL) { 150 fprintf(stderr, "valid commands/args:\n"); 151 show_valid_args(table); 152 return (NULL); 153 } 154 155 argc--; 156 argv++; 157 158 if (match->type == NOTOKEN || match->next == NULL) 159 break; 160 161 table = match->next; 162 } 163 164 if (argc > 0) { 165 fprintf(stderr, "superfluous argument: %s\n", argv[0]); 166 return (NULL); 167 } 168 169 if (res.tries * res.interval.tv_sec > res.maxwait.tv_sec) { 170 fprintf(stderr, "tries %u by interval %lld > maxwait %lld", 171 res.tries, res.interval.tv_sec, res.maxwait.tv_sec); 172 return (NULL); 173 } 174 175 return (&res); 176 } 177 178 static const struct token * 179 match_token(char *word, const struct token table[]) 180 { 181 u_int i, match = 0; 182 const struct token *t = NULL; 183 long long num; 184 const char *errstr; 185 186 for (i = 0; table[i].type != ENDTOKEN; i++) { 187 switch (table[i].type) { 188 case NOTOKEN: 189 if (word == NULL || strlen(word) == 0) { 190 match++; 191 t = &table[i]; 192 } 193 break; 194 case KEYWORD: 195 if (word != NULL && strncmp(word, table[i].keyword, 196 strlen(word)) == 0) { 197 match++; 198 t = &table[i]; 199 if (t->value) 200 res.action = t->value; 201 } 202 break; 203 case HOSTNAME: 204 if (word == NULL) 205 break; 206 match++; 207 res.hostname = word; 208 t = &table[i]; 209 break; 210 case SECRET: 211 if (word == NULL) 212 break; 213 match++; 214 res.secret = word; 215 t = &table[i]; 216 break; 217 case USERNAME: 218 if (word == NULL) 219 break; 220 match++; 221 res.username = word; 222 t = &table[i]; 223 break; 224 case PASSWORD: 225 if (word == NULL) 226 break; 227 match++; 228 res.password = word; 229 t = &table[i]; 230 break; 231 case PORT: 232 if (word == NULL) 233 break; 234 num = strtonum(word, 1, UINT16_MAX, &errstr); 235 if (errstr != NULL) { 236 fprintf(stderr, 237 "invalid argument: %s is %s for \"port\"\n", 238 word, errstr); 239 return (NULL); 240 } 241 match++; 242 res.port = num; 243 t = &table[i]; 244 break; 245 case METHOD: 246 if (word == NULL) 247 break; 248 if (strcasecmp(word, "pap") == 0) 249 res.auth_method = PAP; 250 else if (strcasecmp(word, "chap") == 0) 251 res.auth_method = CHAP; 252 else if (strcasecmp(word, "mschapv2") == 0) 253 res.auth_method = MSCHAPV2; 254 else { 255 fprintf(stderr, 256 "invalid argument: %s for \"method\"\n", 257 word); 258 return (NULL); 259 } 260 match++; 261 t = &table[i]; 262 break; 263 case NAS_PORT: 264 if (word == NULL) 265 break; 266 num = strtonum(word, 0, 65535, &errstr); 267 if (errstr != NULL) { 268 fprintf(stderr, 269 "invalid argument: %s is %s for " 270 "\"nas-port\"\n", word, errstr); 271 return (NULL); 272 } 273 match++; 274 res.nas_port = num; 275 t = &table[i]; 276 break; 277 278 case TRIES: 279 if (word == NULL) 280 break; 281 num = strtonum(word, 282 TEST_TRIES_MIN, TEST_TRIES_MAX, &errstr); 283 if (errstr != NULL) { 284 printf("invalid argument: %s is %s" 285 " for \"tries\"\n", word, errstr); 286 return (NULL); 287 } 288 match++; 289 res.tries = num; 290 t = &table[i]; 291 break; 292 case INTERVAL: 293 if (word == NULL) 294 break; 295 num = strtonum(word, 296 TEST_INTERVAL_MIN, TEST_INTERVAL_MAX, &errstr); 297 if (errstr != NULL) { 298 printf("invalid argument: %s is %s" 299 " for \"interval\"\n", word, errstr); 300 return (NULL); 301 } 302 match++; 303 res.interval.tv_sec = num; 304 t = &table[i]; 305 break; 306 case MAXWAIT: 307 if (word == NULL) 308 break; 309 num = strtonum(word, 310 TEST_MAXWAIT_MIN, TEST_MAXWAIT_MAX, &errstr); 311 if (errstr != NULL) { 312 printf("invalid argument: %s is %s" 313 " for \"maxwait\"\n", word, errstr); 314 return (NULL); 315 } 316 match++; 317 res.maxwait.tv_sec = num; 318 t = &table[i]; 319 break; 320 321 case ENDTOKEN: 322 break; 323 } 324 } 325 326 if (match != 1) { 327 if (word == NULL) 328 fprintf(stderr, "missing argument:\n"); 329 else if (match > 1) 330 fprintf(stderr, "ambiguous argument: %s\n", word); 331 else if (match < 1) 332 fprintf(stderr, "unknown argument: %s\n", word); 333 return (NULL); 334 } 335 336 return (t); 337 } 338 339 static void 340 show_valid_args(const struct token table[]) 341 { 342 int i; 343 344 for (i = 0; table[i].type != ENDTOKEN; i++) { 345 switch (table[i].type) { 346 case NOTOKEN: 347 fprintf(stderr, " <cr>\n"); 348 break; 349 case KEYWORD: 350 fprintf(stderr, " %s\n", table[i].keyword); 351 break; 352 case HOSTNAME: 353 fprintf(stderr, " <hostname>\n"); 354 break; 355 case SECRET: 356 fprintf(stderr, " <radius secret>\n"); 357 break; 358 case USERNAME: 359 fprintf(stderr, " <username>\n"); 360 break; 361 case PASSWORD: 362 fprintf(stderr, " <password>\n"); 363 break; 364 case PORT: 365 fprintf(stderr, " <port number>\n"); 366 break; 367 case METHOD: 368 fprintf(stderr, " <auth method (pap, chap, " 369 "mschapv2)>\n"); 370 break; 371 case NAS_PORT: 372 fprintf(stderr, " <nas-port (0-65535)>\n"); 373 break; 374 case TRIES: 375 fprintf(stderr, " <tries (%u-%u)>\n", 376 TEST_TRIES_MIN, TEST_TRIES_MAX); 377 break; 378 case INTERVAL: 379 fprintf(stderr, " <interval (%u-%u)>\n", 380 TEST_INTERVAL_MIN, TEST_INTERVAL_MAX); 381 break; 382 case MAXWAIT: 383 fprintf(stderr, " <maxwait (%u-%u)>\n", 384 TEST_MAXWAIT_MIN, TEST_MAXWAIT_MAX); 385 break; 386 case ENDTOKEN: 387 break; 388 } 389 } 390 } 391