#include <sys/time.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <limits.h>
#include "parser.h"
enum token_type {
NOTOKEN,
KEYWORD,
HOSTNAME,
SECRET,
USERNAME,
PASSWORD,
PORT,
METHOD,
NAS_PORT,
TRIES,
INTERVAL,
MAXWAIT,
FLAGS,
SESSION_SEQ,
MSGAUTH,
ENDTOKEN
};
struct token {
enum token_type type;
const char *keyword;
int value;
const struct token *next;
};
static struct parse_result res = {
.tries = TEST_TRIES_DEFAULT,
.interval = { TEST_INTERVAL_DEFAULT, 0 },
.maxwait = { TEST_MAXWAIT_DEFAULT, 0 },
.msgauth = 1
};
static const struct token t_test[];
static const struct token t_secret[];
static const struct token t_username[];
static const struct token t_test_opts[];
static const struct token t_password[];
static const struct token t_port[];
static const struct token t_method[];
static const struct token t_nas_port[];
static const struct token t_tries[];
static const struct token t_interval[];
static const struct token t_maxwait[];
static const struct token t_yesno[];
static const struct token t_ipcp[];
static const struct token t_ipcp_flags[];
static const struct token t_ipcp_session_seq[];
static const struct token t_main[] = {
{ KEYWORD, "test", TEST, t_test },
{ KEYWORD, "ipcp", NONE, t_ipcp },
{ ENDTOKEN, "", NONE, NULL }
};
static const struct token t_test[] = {
{ HOSTNAME, "", NONE, t_secret },
{ ENDTOKEN, "", NONE, NULL }
};
static const struct token t_secret[] = {
{ SECRET, "", NONE, t_username },
{ ENDTOKEN, "", NONE, NULL }
};
static const struct token t_username[] = {
{ USERNAME, "", NONE, t_test_opts },
{ ENDTOKEN, "", NONE, NULL }
};
static const struct token t_test_opts[] = {
{ NOTOKEN, "", NONE, NULL },
{ KEYWORD, "password", NONE, t_password },
{ KEYWORD, "port", NONE, t_port },
{ KEYWORD, "method", NONE, t_method },
{ KEYWORD, "nas-port", NONE, t_nas_port },
{ KEYWORD, "interval", NONE, t_interval },
{ KEYWORD, "tries", NONE, t_tries },
{ KEYWORD, "maxwait", NONE, t_maxwait },
{ KEYWORD, "msgauth", NONE, t_yesno },
{ ENDTOKEN, "", NONE, NULL }
};
static const struct token t_password[] = {
{ PASSWORD, "", NONE, t_test_opts },
{ ENDTOKEN, "", NONE, NULL }
};
static const struct token t_port[] = {
{ PORT, "", NONE, t_test_opts },
{ ENDTOKEN, "", NONE, NULL }
};
static const struct token t_method[] = {
{ METHOD, "", NONE, t_test_opts },
{ ENDTOKEN, "", NONE, NULL }
};
static const struct token t_nas_port[] = {
{ NAS_PORT, "", NONE, t_test_opts },
{ ENDTOKEN, "", NONE, NULL }
};
static const struct token t_tries[] = {
{ TRIES, "", NONE, t_test_opts },
{ ENDTOKEN, "", NONE, NULL }
};
static const struct token t_interval[] = {
{ INTERVAL, "", NONE, t_test_opts },
{ ENDTOKEN, "", NONE, NULL }
};
static const struct token t_maxwait[] = {
{ MAXWAIT, "", NONE, t_test_opts },
{ ENDTOKEN, "", NONE, NULL }
};
static const struct token t_yesno[] = {
{ MSGAUTH, "yes", 1, t_test_opts },
{ MSGAUTH, "no", 0, t_test_opts },
{ ENDTOKEN, "", NONE, NULL }
};
static const struct token t_ipcp[] = {
{ KEYWORD, "show", IPCP_SHOW, NULL },
{ KEYWORD, "dump", IPCP_DUMP, t_ipcp_flags },
{ KEYWORD, "monitor", IPCP_MONITOR, t_ipcp_flags },
{ KEYWORD, "disconnect", IPCP_DISCONNECT,t_ipcp_session_seq },
{ KEYWORD, "delete", IPCP_DELETE, t_ipcp_session_seq },
{ ENDTOKEN, "", NONE, NULL }
};
static const struct token t_ipcp_flags[] = {
{ NOTOKEN, "", NONE, NULL },
{ FLAGS, "-json", FLAGS_JSON, NULL },
{ ENDTOKEN, "", NONE, NULL }
};
static const struct token t_ipcp_session_seq[] = {
{ SESSION_SEQ, "", NONE, NULL },
{ ENDTOKEN, "", NONE, NULL }
};
static const struct token *match_token(char *, const struct token []);
static void show_valid_args(const struct token []);
struct parse_result *
parse(int argc, char *argv[])
{
const struct token *table = t_main;
const struct token *match;
while (argc >= 0) {
if ((match = match_token(argv[0], table)) == NULL) {
fprintf(stderr, "valid commands/args:\n");
show_valid_args(table);
return (NULL);
}
argc--;
argv++;
if (match->type == NOTOKEN || match->next == NULL)
break;
table = match->next;
}
if (argc > 0) {
fprintf(stderr, "superfluous argument: %s\n", argv[0]);
return (NULL);
}
if (res.tries * res.interval.tv_sec > res.maxwait.tv_sec) {
fprintf(stderr, "tries %u by interval %lld > maxwait %lld",
res.tries, res.interval.tv_sec, res.maxwait.tv_sec);
return (NULL);
}
return (&res);
}
static const struct token *
match_token(char *word, const struct token table[])
{
u_int i, match = 0;
const struct token *t = NULL;
long long num;
const char *errstr;
size_t wordlen = 0;
if (word != NULL)
wordlen = strlen(word);
for (i = 0; table[i].type != ENDTOKEN; i++) {
switch (table[i].type) {
case NOTOKEN:
if (word == NULL || strlen(word) == 0) {
match++;
t = &table[i];
}
break;
case KEYWORD:
if (word != NULL && strncmp(word, table[i].keyword,
wordlen) == 0) {
match++;
t = &table[i];
if (t->value)
res.action = t->value;
}
break;
case HOSTNAME:
if (word == NULL)
break;
match++;
res.hostname = word;
t = &table[i];
break;
case SECRET:
if (word == NULL)
break;
match++;
res.secret = word;
t = &table[i];
break;
case USERNAME:
if (word == NULL)
break;
match++;
res.username = word;
t = &table[i];
break;
case PASSWORD:
if (word == NULL)
break;
match++;
res.password = word;
t = &table[i];
break;
case PORT:
if (word == NULL)
break;
num = strtonum(word, 1, UINT16_MAX, &errstr);
if (errstr != NULL) {
fprintf(stderr,
"invalid argument: %s is %s for \"port\"\n",
word, errstr);
return (NULL);
}
match++;
res.port = num;
t = &table[i];
break;
case METHOD:
if (word == NULL)
break;
if (strcasecmp(word, "pap") == 0)
res.auth_method = PAP;
else if (strcasecmp(word, "chap") == 0)
res.auth_method = CHAP;
else if (strcasecmp(word, "mschapv2") == 0)
res.auth_method = MSCHAPV2;
else {
fprintf(stderr,
"invalid argument: %s for \"method\"\n",
word);
return (NULL);
}
match++;
t = &table[i];
break;
case NAS_PORT:
if (word == NULL)
break;
num = strtonum(word, 0, 65535, &errstr);
if (errstr != NULL) {
fprintf(stderr,
"invalid argument: %s is %s for "
"\"nas-port\"\n", word, errstr);
return (NULL);
}
match++;
res.nas_port = num;
t = &table[i];
break;
case TRIES:
if (word == NULL)
break;
num = strtonum(word,
TEST_TRIES_MIN, TEST_TRIES_MAX, &errstr);
if (errstr != NULL) {
printf("invalid argument: %s is %s"
" for \"tries\"\n", word, errstr);
return (NULL);
}
match++;
res.tries = num;
t = &table[i];
break;
case INTERVAL:
if (word == NULL)
break;
num = strtonum(word,
TEST_INTERVAL_MIN, TEST_INTERVAL_MAX, &errstr);
if (errstr != NULL) {
printf("invalid argument: %s is %s"
" for \"interval\"\n", word, errstr);
return (NULL);
}
match++;
res.interval.tv_sec = num;
t = &table[i];
break;
case MAXWAIT:
if (word == NULL)
break;
num = strtonum(word,
TEST_MAXWAIT_MIN, TEST_MAXWAIT_MAX, &errstr);
if (errstr != NULL) {
printf("invalid argument: %s is %s"
" for \"maxwait\"\n", word, errstr);
return (NULL);
}
match++;
res.maxwait.tv_sec = num;
t = &table[i];
break;
case FLAGS:
if (word != NULL && wordlen >= 2 &&
strncmp(word, table[i].keyword, wordlen) == 0) {
match++;
t = &table[i];
if (t->value)
res.flags |= t->value;
}
break;
case SESSION_SEQ:
if (word == NULL)
break;
match++;
res.session_seq = strtonum(word, 1, UINT_MAX, &errstr);
if (errstr != NULL) {
printf("invalid argument: %s is %s for "
"\"session-id\"\n", word, errstr);
return (NULL);
}
t = &table[i];
case MSGAUTH:
if (word != NULL &&
strcmp(word, table[i].keyword) == 0) {
match++;
res.msgauth = table[i].value;
t = &table[i];
}
break;
case ENDTOKEN:
break;
}
}
if (match != 1) {
if (word == NULL)
fprintf(stderr, "missing argument:\n");
else if (match > 1)
fprintf(stderr, "ambiguous argument: %s\n", word);
else if (match < 1)
fprintf(stderr, "unknown argument: %s\n", word);
return (NULL);
}
return (t);
}
static void
show_valid_args(const struct token table[])
{
int i;
for (i = 0; table[i].type != ENDTOKEN; i++) {
switch (table[i].type) {
case NOTOKEN:
fprintf(stderr, " <cr>\n");
break;
case KEYWORD:
fprintf(stderr, " %s\n", table[i].keyword);
break;
case HOSTNAME:
fprintf(stderr, " <hostname>\n");
break;
case SECRET:
fprintf(stderr, " <radius secret>\n");
break;
case USERNAME:
fprintf(stderr, " <username>\n");
break;
case PASSWORD:
fprintf(stderr, " <password>\n");
break;
case PORT:
fprintf(stderr, " <port number>\n");
break;
case METHOD:
fprintf(stderr, " <auth method (pap, chap, "
"mschapv2)>\n");
break;
case NAS_PORT:
fprintf(stderr, " <nas-port (0-65535)>\n");
break;
case TRIES:
fprintf(stderr, " <tries (%u-%u)>\n",
TEST_TRIES_MIN, TEST_TRIES_MAX);
break;
case INTERVAL:
fprintf(stderr, " <interval (%u-%u)>\n",
TEST_INTERVAL_MIN, TEST_INTERVAL_MAX);
break;
case MAXWAIT:
fprintf(stderr, " <maxwait (%u-%u)>\n",
TEST_MAXWAIT_MIN, TEST_MAXWAIT_MAX);
break;
case FLAGS:
fprintf(stderr, " %s\n", table[i].keyword);
break;
case SESSION_SEQ:
fprintf(stderr, " <sequence number>\n");
break;
case MSGAUTH:
fprintf(stderr, " %s\n", table[i].keyword);
break;
case ENDTOKEN:
break;
}
}
}