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 *
parse(int argc,char * argv[])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 *
match_token(char * word,const struct token table[])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
show_valid_args(const struct token table[])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