|
|
|
@ -5,211 +5,182 @@
|
|
|
|
|
* published by the Free Software Foundation. |
|
|
|
|
*/ |
|
|
|
|
#include "common.h" |
|
|
|
|
#include "buf.h" |
|
|
|
|
#include "commands.h" |
|
|
|
|
|
|
|
|
|
struct f2b_cmd_t { |
|
|
|
|
struct cmd_desc { |
|
|
|
|
enum f2b_command_type type; |
|
|
|
|
const short int argc; |
|
|
|
|
const short int tokenc; |
|
|
|
|
const char *help; |
|
|
|
|
const char *tokens[CMD_TOKENS_MAX]; |
|
|
|
|
} commands[CMD_MAX_NUMBER] = { |
|
|
|
|
[CMD_NONE] = { |
|
|
|
|
.argc = 0, .tokenc = 0, |
|
|
|
|
.tokens = { NULL }, |
|
|
|
|
.help = "Unspecified command" |
|
|
|
|
}, |
|
|
|
|
[CMD_RESP] = { |
|
|
|
|
.argc = 1, .tokenc = 0, |
|
|
|
|
.tokens = { NULL }, |
|
|
|
|
.help = "Command response, used internally", |
|
|
|
|
}, |
|
|
|
|
[CMD_HELP] = { |
|
|
|
|
const char *tokens[CMD_TOKENS_MAXCOUNT]; |
|
|
|
|
} commands[] = { |
|
|
|
|
{ |
|
|
|
|
.type = CMD_HELP, |
|
|
|
|
.argc = 0, .tokenc = 1, |
|
|
|
|
.tokens = { "help", NULL }, |
|
|
|
|
.help = "Show available commands", |
|
|
|
|
}, |
|
|
|
|
[CMD_PING] = { |
|
|
|
|
.argc = 0, .tokenc = 1, |
|
|
|
|
.tokens = { "ping", NULL }, |
|
|
|
|
.help = "Check the connection", |
|
|
|
|
}, |
|
|
|
|
[CMD_STATUS] = { |
|
|
|
|
}, { |
|
|
|
|
.type = CMD_STATUS, |
|
|
|
|
.argc = 0, .tokenc = 1, |
|
|
|
|
.tokens = { "status", NULL }, |
|
|
|
|
.help = "Show general stats and jails list", |
|
|
|
|
}, |
|
|
|
|
[CMD_LOG_ROTATE] = { |
|
|
|
|
.argc = 0, .tokenc = 2, |
|
|
|
|
.tokens = { "log", "rotate", NULL }, |
|
|
|
|
.help = "Reopen daemon's own log file", |
|
|
|
|
}, |
|
|
|
|
[CMD_RELOAD] = { |
|
|
|
|
}, { |
|
|
|
|
.type = CMD_RELOAD, |
|
|
|
|
.argc = 0, .tokenc = 1, |
|
|
|
|
.tokens = { "reload", NULL }, |
|
|
|
|
.help = "Reload own config, all jails will be reset.", |
|
|
|
|
}, |
|
|
|
|
[CMD_SHUTDOWN] = { |
|
|
|
|
}, { |
|
|
|
|
.type = CMD_SHUTDOWN, |
|
|
|
|
.argc = 0, .tokenc = 1, |
|
|
|
|
.tokens = { "shutdown", NULL }, |
|
|
|
|
.help = "Gracefully terminate f2b daemon", |
|
|
|
|
}, |
|
|
|
|
[CMD_LOG_LEVEL] = { |
|
|
|
|
}, { |
|
|
|
|
.type = CMD_LOG_ROTATE, |
|
|
|
|
.argc = 0, .tokenc = 2, |
|
|
|
|
.tokens = { "log", "rotate", NULL }, |
|
|
|
|
.help = "Reopen daemon's own log file", |
|
|
|
|
}, { |
|
|
|
|
.type = CMD_LOG_LEVEL, |
|
|
|
|
.argc = 1, .tokenc = 3, |
|
|
|
|
.tokens = { "log", "level", "<level>", NULL }, |
|
|
|
|
.help = "Change maximum level of logged messages", |
|
|
|
|
}, |
|
|
|
|
[CMD_JAIL_STATUS] = { |
|
|
|
|
}, { |
|
|
|
|
.type = CMD_JAIL_STATUS, |
|
|
|
|
.argc = 1, .tokenc = 3, |
|
|
|
|
.tokens = { "jail", "<jailname>", "status", NULL }, |
|
|
|
|
.help = "Show status and stats of given jail", |
|
|
|
|
}, |
|
|
|
|
[CMD_JAIL_SET] = { |
|
|
|
|
}, { |
|
|
|
|
.type = CMD_JAIL_SET, |
|
|
|
|
.argc = 3, .tokenc = 5, |
|
|
|
|
.tokens = { "jail", "<jailname>", "set", "<param>", "<value>", NULL }, |
|
|
|
|
.help = "Set parameter of given jail", |
|
|
|
|
}, |
|
|
|
|
[CMD_JAIL_IP_STATUS] = { |
|
|
|
|
}, { |
|
|
|
|
.type = CMD_JAIL_IP_STATUS, |
|
|
|
|
.argc = 2, .tokenc = 5, |
|
|
|
|
.tokens = { "jail", "<jailname>", "ip", "status", "<ip>", NULL }, |
|
|
|
|
.help = "Show ip status in given jail", |
|
|
|
|
}, |
|
|
|
|
[CMD_JAIL_IP_BAN] = { |
|
|
|
|
}, { |
|
|
|
|
.type = CMD_JAIL_IP_BAN, |
|
|
|
|
.argc = 2, .tokenc = 5, |
|
|
|
|
.tokens = { "jail", "<jailname>", "ip", "ban", "<ip>", NULL }, |
|
|
|
|
.help = "Forcefully ban some ip in given jail", |
|
|
|
|
}, |
|
|
|
|
[CMD_JAIL_IP_RELEASE] = { |
|
|
|
|
}, { |
|
|
|
|
.type = CMD_JAIL_IP_RELEASE, |
|
|
|
|
.argc = 2, .tokenc = 5, |
|
|
|
|
.tokens = { "jail", "<jailname>", "ip", "release", "<ip>", NULL }, |
|
|
|
|
.help = "Forcefully release some ip in given jail", |
|
|
|
|
}, |
|
|
|
|
[CMD_JAIL_FILTER_STATS] = { |
|
|
|
|
}, { |
|
|
|
|
.type = CMD_JAIL_FILTER_STATS, |
|
|
|
|
.argc = 1, .tokenc = 4, |
|
|
|
|
.tokens = { "jail", "<jailname>", "filter", "stats", NULL }, |
|
|
|
|
.help = "Show matches stats for jail regexps", |
|
|
|
|
}, |
|
|
|
|
[CMD_JAIL_FILTER_RELOAD] = { |
|
|
|
|
}, { |
|
|
|
|
.type = CMD_JAIL_FILTER_RELOAD, |
|
|
|
|
.argc = 1, .tokenc = 4, |
|
|
|
|
.tokens = { "jail", "<jailname>", "filter", "reload", NULL }, |
|
|
|
|
.help = "Reload regexps for given jail", |
|
|
|
|
}, |
|
|
|
|
}, { |
|
|
|
|
.type = CMD_UNKNOWN, |
|
|
|
|
.argc = 0, .tokenc = 0, |
|
|
|
|
.tokens = { NULL }, |
|
|
|
|
.help = "", |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
void |
|
|
|
|
static char *help = NULL; |
|
|
|
|
|
|
|
|
|
const char * |
|
|
|
|
f2b_cmd_help() { |
|
|
|
|
struct f2b_cmd_t *cmd = NULL; |
|
|
|
|
f2b_buf_t buf; |
|
|
|
|
const char **p = NULL; |
|
|
|
|
|
|
|
|
|
fputs("Available commands:\n\n", stdout); |
|
|
|
|
for (size_t i = CMD_PING; i < CMD_MAX_NUMBER; i++) { |
|
|
|
|
cmd = &commands[i]; |
|
|
|
|
if (cmd->tokens[0] == NULL) |
|
|
|
|
continue; |
|
|
|
|
for (p = cmd->tokens; *p != NULL; p++) |
|
|
|
|
fprintf(stdout, "%s ", *p); |
|
|
|
|
fprintf(stdout, "\n\t%s\n\n", cmd->help); |
|
|
|
|
} |
|
|
|
|
if (help) |
|
|
|
|
return help; |
|
|
|
|
|
|
|
|
|
return; |
|
|
|
|
if (!f2b_buf_alloc(&buf, 8192)) |
|
|
|
|
return "internal error: can't allocate memory\n"; |
|
|
|
|
f2b_buf_append(&buf, "Available commands:\n\n", 0); |
|
|
|
|
for (struct cmd_desc *cmd = commands; cmd->type != CMD_UNKNOWN; cmd++) { |
|
|
|
|
for (p = cmd->tokens; *p != NULL; p++) { |
|
|
|
|
f2b_buf_append(&buf, *p, 0); |
|
|
|
|
f2b_buf_append(&buf, " ", 1); |
|
|
|
|
} |
|
|
|
|
f2b_buf_append(&buf, "\n\t", 2); |
|
|
|
|
f2b_buf_append(&buf, cmd->help, 0); |
|
|
|
|
f2b_buf_append(&buf, "\n\n", 2); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void |
|
|
|
|
f2b_cmd_append_arg(char *buf, size_t bufsize, const char *arg) { |
|
|
|
|
assert(buf != NULL); |
|
|
|
|
assert(arg != NULL); |
|
|
|
|
strlcat(buf, arg, bufsize); |
|
|
|
|
strlcat(buf, "\n", bufsize); |
|
|
|
|
help = strndup(buf.data, buf.used); |
|
|
|
|
f2b_buf_free(&buf); |
|
|
|
|
return help; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Parse command from line |
|
|
|
|
* @param buf Buffer for command parameters |
|
|
|
|
* @param bufsize SSize of buffer above |
|
|
|
|
* @param src Line taken from user input |
|
|
|
|
* @return Type of parsed command or CMD_NONE if no matches |
|
|
|
|
*/ |
|
|
|
|
enum f2b_cmd_type |
|
|
|
|
f2b_cmd_parse(char *buf, size_t bufsize, const char *src) { |
|
|
|
|
size_t tokenc = 0; /* tokens count */ |
|
|
|
|
char *tokens[CMD_TOKENS_MAX] = { NULL }; |
|
|
|
|
char line[INPUT_LINE_MAX]; |
|
|
|
|
char *p; |
|
|
|
|
bool |
|
|
|
|
f2b_cmd_parse(f2b_cmd_t *cmd, const char *src) { |
|
|
|
|
char *p = NULL; |
|
|
|
|
|
|
|
|
|
assert(cmd != NULL); |
|
|
|
|
assert(src != NULL); |
|
|
|
|
|
|
|
|
|
assert(line != NULL); |
|
|
|
|
/* strip leading spaces */ |
|
|
|
|
while (isblank(*line)) |
|
|
|
|
while (isblank(*src)) |
|
|
|
|
src++; |
|
|
|
|
/* strip trailing spaces, newlines, etc */ |
|
|
|
|
strlcpy(line, src, sizeof(line)); |
|
|
|
|
for (size_t l = strlen(line); l >= 1 && isspace(line[l - 1]); l--) |
|
|
|
|
line[l - 1] = '\0'; |
|
|
|
|
|
|
|
|
|
if (line[0] == '\0') |
|
|
|
|
return CMD_NONE; /* empty string */ |
|
|
|
|
f2b_buf_alloc(&cmd->data, strlen(src) + 1); |
|
|
|
|
f2b_buf_append(&cmd->data, src, 0); |
|
|
|
|
|
|
|
|
|
/* simple commands without args */ |
|
|
|
|
if (strcmp(line, "ping") == 0) { return CMD_PING; } |
|
|
|
|
else if (strcmp(line, "help") == 0) { return CMD_HELP; } |
|
|
|
|
else if (strcmp(line, "status") == 0) { return CMD_STATUS; } |
|
|
|
|
else if (strcmp(line, "reload") == 0) { return CMD_RELOAD; } |
|
|
|
|
else if (strcmp(line, "shutdown") == 0) { return CMD_SHUTDOWN; } |
|
|
|
|
cmd->argc = 1; /* we has at least one arg */ |
|
|
|
|
cmd->args[0] = cmd->data.data; |
|
|
|
|
if (strcmp(src, "help") == 0) { cmd->type = CMD_HELP; return true; } |
|
|
|
|
else if (strcmp(src, "status") == 0) { cmd->type = CMD_STATUS; return true; } |
|
|
|
|
else if (strcmp(src, "reload") == 0) { cmd->type = CMD_RELOAD; return true; } |
|
|
|
|
else if (strcmp(src, "shutdown") == 0) { cmd->type = CMD_SHUTDOWN; return true; } |
|
|
|
|
|
|
|
|
|
/* split string to tokens */ |
|
|
|
|
tokenc = 1; /* we has at least one token */ |
|
|
|
|
tokens[0] = strtok_r(line, " \t", &p); |
|
|
|
|
for (size_t i = 1; i < CMD_TOKENS_MAX; i++) { |
|
|
|
|
tokens[i] = strtok_r(NULL, " \t", &p); |
|
|
|
|
if (tokens[i] == NULL) |
|
|
|
|
p = cmd->data.data; |
|
|
|
|
cmd->args[0] = strtok_r(cmd->data.data, " \t", &p); |
|
|
|
|
for (size_t i = 1; i < CMD_TOKENS_MAXCOUNT; i++) { |
|
|
|
|
cmd->args[i] = strtok_r(NULL, " \t", &p); |
|
|
|
|
if (cmd->args[i] == NULL) |
|
|
|
|
break; |
|
|
|
|
tokenc++; |
|
|
|
|
cmd->argc++; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
buf[0] = '\0'; |
|
|
|
|
if (strcmp(line, "jail") == 0 && tokenc > 1) { |
|
|
|
|
if (strcmp(cmd->args[0], "jail") == 0 && cmd->argc > 1) { |
|
|
|
|
/* commands for jail */ |
|
|
|
|
f2b_cmd_append_arg(buf, bufsize, tokens[1]); |
|
|
|
|
if (tokenc == 3 && strcmp(tokens[2], "status") == 0) { |
|
|
|
|
return CMD_JAIL_STATUS; |
|
|
|
|
if (cmd->argc == 3 && strcmp(cmd->args[2], "status") == 0) { |
|
|
|
|
cmd->type = CMD_JAIL_STATUS; return true; |
|
|
|
|
} |
|
|
|
|
if (tokenc == 5 && strcmp(tokens[2], "set") == 0) { |
|
|
|
|
f2b_cmd_append_arg(buf, bufsize, tokens[3]); |
|
|
|
|
f2b_cmd_append_arg(buf, bufsize, tokens[4]); |
|
|
|
|
return CMD_JAIL_SET; |
|
|
|
|
if (cmd->argc == 5 && strcmp(cmd->args[2], "set") == 0) { |
|
|
|
|
cmd->type = CMD_JAIL_SET; return true; |
|
|
|
|
} |
|
|
|
|
if (tokenc == 5 && strcmp(tokens[2], "ip") == 0 && strcmp(tokens[3], "status") == 0) { |
|
|
|
|
f2b_cmd_append_arg(buf, bufsize, tokens[4]); |
|
|
|
|
return CMD_JAIL_IP_STATUS; |
|
|
|
|
if (cmd->argc == 5 && strcmp(cmd->args[2], "ip") == 0 && strcmp(cmd->args[3], "status") == 0) { |
|
|
|
|
cmd->type = CMD_JAIL_IP_STATUS; return true; |
|
|
|
|
} |
|
|
|
|
if (tokenc == 5 && strcmp(tokens[2], "ip") == 0 && strcmp(tokens[3], "ban") == 0) { |
|
|
|
|
f2b_cmd_append_arg(buf, bufsize, tokens[4]); |
|
|
|
|
return CMD_JAIL_IP_BAN; |
|
|
|
|
if (cmd->argc == 5 && strcmp(cmd->args[2], "ip") == 0 && strcmp(cmd->args[3], "ban") == 0) { |
|
|
|
|
cmd->type = CMD_JAIL_IP_BAN; return true; |
|
|
|
|
} |
|
|
|
|
if (tokenc == 5 && strcmp(tokens[2], "ip") == 0 && strcmp(tokens[3], "release") == 0) { |
|
|
|
|
f2b_cmd_append_arg(buf, bufsize, tokens[4]); |
|
|
|
|
return CMD_JAIL_IP_RELEASE; |
|
|
|
|
if (cmd->argc == 5 && strcmp(cmd->args[2], "ip") == 0 && strcmp(cmd->args[3], "release") == 0) { |
|
|
|
|
cmd->type = CMD_JAIL_IP_RELEASE; return true; |
|
|
|
|
} |
|
|
|
|
if (tokenc == 4 && strcmp(tokens[2], "filter") == 0 && strcmp(tokens[3], "stats") == 0) { |
|
|
|
|
return CMD_JAIL_FILTER_STATS; |
|
|
|
|
if (cmd->argc == 4 && strcmp(cmd->args[2], "filter") == 0 && strcmp(cmd->args[3], "stats") == 0) { |
|
|
|
|
cmd->type = CMD_JAIL_FILTER_STATS; return true; |
|
|
|
|
} |
|
|
|
|
if (tokenc == 4 && strcmp(tokens[2], "filter") == 0 && strcmp(tokens[3], "reload") == 0) { |
|
|
|
|
return CMD_JAIL_FILTER_RELOAD; |
|
|
|
|
if (cmd->argc == 4 && strcmp(cmd->args[2], "filter") == 0 && strcmp(cmd->args[3], "reload") == 0) { |
|
|
|
|
cmd->type = CMD_JAIL_FILTER_RELOAD; return true; |
|
|
|
|
} |
|
|
|
|
} else if (strcmp(line, "log") == 0 && tokenc > 1) { |
|
|
|
|
if (tokenc == 2 && strcmp(tokens[1], "rotate") == 0) { |
|
|
|
|
return CMD_LOG_ROTATE; |
|
|
|
|
} else if (strcmp(cmd->args[0], "log") == 0 && cmd->argc > 1) { |
|
|
|
|
if (cmd->argc == 2 && strcmp(cmd->args[1], "rotate") == 0) { |
|
|
|
|
cmd->type = CMD_LOG_ROTATE; return true; |
|
|
|
|
} |
|
|
|
|
if (tokenc == 3 && strcmp(tokens[1], "level") == 0) { |
|
|
|
|
f2b_cmd_append_arg(buf, bufsize, tokens[2]); |
|
|
|
|
return CMD_LOG_LEVEL; |
|
|
|
|
if (cmd->argc == 3 && strcmp(cmd->args[1], "level") == 0) { |
|
|
|
|
cmd->type = CMD_LOG_LEVEL; return true; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
cmd->type = CMD_UNKNOWN; |
|
|
|
|
f2b_buf_free(&cmd->data); |
|
|
|
|
|
|
|
|
|
return CMD_NONE; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool |
|
|
|
|
f2b_cmd_check_argc(enum f2b_cmd_type type, int argc) { |
|
|
|
|
if (commands[type].argc == argc) |
|
|
|
|
return true; |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|