From 5b4141471a767c2e2f29a19185eefaa3b74eaa34 Mon Sep 17 00:00:00 2001 From: Alex 'AdUser' Z Date: Thu, 14 Jan 2021 00:12:34 +1000 Subject: [PATCH 01/20] * fix minor compile warning --- src/log.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/log.c b/src/log.c index 16007ec..f72e908 100644 --- a/src/log.c +++ b/src/log.c @@ -56,6 +56,7 @@ void f2b_log_msg(log_msgtype_t l, const char *fmt, ...) { default: case log_stderr: logfile = stderr; + /* fallthru */ case log_file: strftime(when, sizeof(when), "%F %H:%M:%S", localtime(&now)); fprintf(logfile, "%s [%s] %s\n", when, loglevels[l], msg); From 11aa09abebe98b2db277a2d80698d8c3d63019df Mon Sep 17 00:00:00 2001 From: Alex 'AdUser' Z Date: Thu, 14 Jan 2021 00:15:40 +1000 Subject: [PATCH 02/20] + f2b_buf_t --- src/buf.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++++ src/buf.h | 15 ++++++++++ t/CMakeLists.txt | 2 ++ t/t_buf.c | 48 +++++++++++++++++++++++++++++++ 4 files changed, 139 insertions(+) create mode 100644 src/buf.c create mode 100644 src/buf.h create mode 100644 t/t_buf.c diff --git a/src/buf.c b/src/buf.c new file mode 100644 index 0000000..ed08886 --- /dev/null +++ b/src/buf.c @@ -0,0 +1,74 @@ +#include "common.h" +#include "buf.h" + +bool +f2b_buf_alloc(f2b_buf_t *buf, size_t size) { + assert(buf != NULL); + assert(size > 0); + + memset(buf, 0x0, sizeof(f2b_buf_t)); + if ((buf->data = malloc(size)) == NULL) + return false; /* can't allocate memory */ + + buf->size = size; + return true; +} + +void +f2b_buf_free(f2b_buf_t *buf) { + assert(buf != NULL); + + free(buf->data); + memset(buf, 0x0, sizeof(f2b_buf_t)); +} + +bool +f2b_buf_append(f2b_buf_t *buf, const char *str, size_t len) { + + assert(buf != NULL); + assert(str != NULL); + + if (len == 0) + len = strlen(str); + if (buf->size < (buf->used + len)) + return false; /* not enough space */ + + memcpy(&buf->data[buf->used], str, len); + buf->used += len; + buf->data[buf->used] = '\0'; + return true; +} + +/** + * @brief Extracts line terminated by '\r', '\n' + * @return Pointer to extracted string on success or NULL otherwise + * @note Use only with 'read' buffer type + */ +char * +f2b_buf_extract(f2b_buf_t *buf, const char *end) { + char *s = NULL; + size_t len = 0; + + assert(buf != NULL); + assert(end != NULL); + + if (buf->data == NULL || buf->used == 0) + return NULL; /* no data */ + + if ((s = strstr(buf->data, end)) == NULL) + return NULL; /* not found */ + + /* copy the data before modifying buffer */ + len = s - buf->data; + if ((s = strndup(buf->data, len)) == NULL) + return NULL; /* malloc error */ + + /* shift data inside buffer */ + len += strlen(end); + assert(buf->used >= len); + buf->used -= len, + memmove(buf->data, &buf->data[len], buf->used); + buf->data[buf->used] = '\0'; + + return s; +} diff --git a/src/buf.h b/src/buf.h new file mode 100644 index 0000000..31d5472 --- /dev/null +++ b/src/buf.h @@ -0,0 +1,15 @@ +#ifndef F2B_BUF_H_ +#define F2B_BUF_H_ + +typedef struct f2b_buf_t { + size_t used; /**< bytes used in buffer */ + size_t size; /**< available size in data */ + char *data; /**< allocated buffer */ +} f2b_buf_t; + +bool f2b_buf_alloc(f2b_buf_t *buf, size_t max); +void f2b_buf_free(f2b_buf_t *buf); +bool f2b_buf_append(f2b_buf_t *buf, const char *str, size_t size); +char * f2b_buf_extract(f2b_buf_t *buf, const char *end); + +#endif /* F2B_BUF_H_ */ diff --git a/t/CMakeLists.txt b/t/CMakeLists.txt index 7ec6c88..ad6c2a7 100644 --- a/t/CMakeLists.txt +++ b/t/CMakeLists.txt @@ -2,6 +2,7 @@ enable_testing() set(SRC_DIR "../src") +add_executable("t_buf" "t_buf.c" "${SRC_DIR}/strlcpy.c" "${SRC_DIR}/buf.c") add_executable("t_cmd" "t_cmd.c" "${SRC_DIR}/strlcpy.c" "${SRC_DIR}/commands.c") add_executable("t_cmsg" "t_cmsg.c" "${SRC_DIR}/strlcpy.c" "${SRC_DIR}/cmsg.c") add_executable("t_matches" "t_matches.c" "${SRC_DIR}/strlcpy.c" "${SRC_DIR}/matches.c") @@ -10,6 +11,7 @@ add_executable("t_statefile" "t_statefile.c" "${SRC_DIR}/strlcpy.c" "${SRC_D add_executable("t_config_param" "t_config_param.c" "${SRC_DIR}/strlcpy.c" "${SRC_DIR}/config.c" "${SRC_DIR}/log.c") add_executable("t_backend_usage" "t_backend_usage.c" "${SRC_DIR}/strlcpy.c") +add_test("tests/f2b_buf_*" "t_buf") add_test("tests/f2b_cmd_*" "t_cmd") add_test("tests/f2b_cmsg_*" "t_cmsg") add_test("tests/f2b_matches_*" "t_matches") diff --git a/t/t_buf.c b/t/t_buf.c new file mode 100644 index 0000000..129b487 --- /dev/null +++ b/t/t_buf.c @@ -0,0 +1,48 @@ +#include "../src/common.h" +#include "../src/buf.h" + +int main() { + f2b_buf_t buf; + char *line; + bool ret; + + memset(&buf, 0x0, sizeof(buf)); + + ret = f2b_buf_alloc(&buf, 512); + assert(ret = true); + assert(buf.used == 0); + assert(buf.size == 512); + + ret = f2b_buf_append(&buf, "test ", 0); + assert(ret == true); + assert(buf.used == 5); + + line = f2b_buf_extract(&buf, "\n"); + assert(line == NULL); + + ret = f2b_buf_append(&buf, "test2\n", 0); + assert(ret == true); + assert(buf.used == 11); + + ret = f2b_buf_append(&buf, "test3\n", 0); + assert(ret == true); + assert(buf.used == 17); + + line = f2b_buf_extract(&buf, "\n"); + assert(line != NULL); + assert(strcmp(line, "test test2") == 0); + assert(buf.used == 6); + free(line); + + line = f2b_buf_extract(&buf, "\n"); + assert(line != NULL); + assert(buf.used == 0); + free(line); + + f2b_buf_free(&buf); + assert(buf.used == 0); + assert(buf.size == 0); + assert(buf.data == NULL); + + return 0; +} From 830ed213750e924094e98290801be554c5684604 Mon Sep 17 00:00:00 2001 From: Alex 'AdUser' Z Date: Thu, 14 Jan 2021 00:43:29 +1000 Subject: [PATCH 03/20] * refactor f2b_cmd_t --- src/commands.c | 235 +++++++++++++++++++++-------------------------- src/commands.h | 71 ++++++-------- t/CMakeLists.txt | 2 +- t/t_cmd.c | 61 ++++++------ 4 files changed, 166 insertions(+), 203 deletions(-) diff --git a/src/commands.c b/src/commands.c index 62eb6f5..765b70f 100644 --- a/src/commands.c +++ b/src/commands.c @@ -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", "", NULL }, .help = "Change maximum level of logged messages", - }, - [CMD_JAIL_STATUS] = { + }, { + .type = CMD_JAIL_STATUS, .argc = 1, .tokenc = 3, .tokens = { "jail", "", "status", NULL }, .help = "Show status and stats of given jail", - }, - [CMD_JAIL_SET] = { + }, { + .type = CMD_JAIL_SET, .argc = 3, .tokenc = 5, .tokens = { "jail", "", "set", "", "", NULL }, .help = "Set parameter of given jail", - }, - [CMD_JAIL_IP_STATUS] = { + }, { + .type = CMD_JAIL_IP_STATUS, .argc = 2, .tokenc = 5, .tokens = { "jail", "", "ip", "status", "", NULL }, .help = "Show ip status in given jail", - }, - [CMD_JAIL_IP_BAN] = { + }, { + .type = CMD_JAIL_IP_BAN, .argc = 2, .tokenc = 5, .tokens = { "jail", "", "ip", "ban", "", NULL }, .help = "Forcefully ban some ip in given jail", - }, - [CMD_JAIL_IP_RELEASE] = { + }, { + .type = CMD_JAIL_IP_RELEASE, .argc = 2, .tokenc = 5, .tokens = { "jail", "", "ip", "release", "", NULL }, .help = "Forcefully release some ip in given jail", - }, - [CMD_JAIL_FILTER_STATS] = { + }, { + .type = CMD_JAIL_FILTER_STATS, .argc = 1, .tokenc = 4, .tokens = { "jail", "", "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", "", "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; + + 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); } - return; + help = strndup(buf.data, buf.used); + f2b_buf_free(&buf); + return help; } -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); -} +bool +f2b_cmd_parse(f2b_cmd_t *cmd, const char *src) { + char *p = NULL; -/** - * @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; + 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; } diff --git a/src/commands.h b/src/commands.h index cc00bd7..194ea39 100644 --- a/src/commands.h +++ b/src/commands.h @@ -10,30 +10,23 @@ /** * @file * This header contains definition of control commands and routines - * for work with data buffer of control message + * for parsing user input */ -/** - * Maximum length of input line in client - * @note yes, i know about LINE_MAX - */ -#define INPUT_LINE_MAX 256 -/** Maximum count of data pieces in control message data buf */ -#define CMD_TOKENS_MAX 6 +#define CMD_TOKENS_MAXCOUNT 6 /**< Maximum count of data pieces in control message data buf */ +#define CMD_TOKENS_MAXSIZE 260 /**< parsed tokens */ -/** control command type */ -enum f2b_cmd_type { - CMD_NONE = 0, /**< unset */ - CMD_RESP, /**< response of command */ - CMD_HELP, /**< show help for commands (used internally by client) */ - CMD_PING = 8, /**< check connection */ - CMD_STATUS, /**< show general status of f2b daemon */ - CMD_LOG_ROTATE,/**< reopen logfile. works only if set `logdest = file` */ - CMD_RELOAD, /**< reload all jails */ - CMD_SHUTDOWN, /**< gracefull shutdown */ - CMD_LOG_LEVEL, /**< change maximum level of logged messages */ +enum f2b_command_type { + CMD_UNKNOWN = 0, /**< unset */ + CMD_HELP, /**< show help for commands */ + CMD_STATUS, /**< show general status of f2b daemon */ + CMD_RELOAD, /**< reload all jails */ + CMD_SHUTDOWN, /**< gracefull shutdown daemon */ + /* logging */ + CMD_LOG_ROTATE, /**< reopen logfile. (only for `logdest = file`) */ + CMD_LOG_LEVEL, /**< change maximum level of logged messages */ /* jail commands */ - CMD_JAIL_STATUS = 16, /**< show status of given jail */ + CMD_JAIL_STATUS, /**< show status of given jail */ CMD_JAIL_SET, /**< set parameter of given jail */ CMD_JAIL_IP_STATUS, /**< show status of given ip */ CMD_JAIL_IP_BAN, /**< force ban given ip */ @@ -43,36 +36,28 @@ enum f2b_cmd_type { CMD_MAX_NUMBER, /**< placeholder */ }; +/** control command type */ +typedef struct f2b_cmd_t { + enum f2b_command_type type; + int argc; + char *args[CMD_TOKENS_MAXCOUNT+1]; + f2b_buf_t data; +} f2b_cmd_t; + /** - * @brief Print to stdout help for defined commands + * @brief Returns multiline string with commands list and it's brief descriptions + * @returns constant multiline string */ -void f2b_cmd_help(); +const char * +f2b_cmd_help(); /** * @brief Try to parse user input - * @param buf Buffer of control message for storing parsed args - * @param bufsize Size of buffer size above + * @param cmd pointer to preallocated f2b_cmd_t struct * @param src Line with user input - * @returns @a CMD_NONE if parsing fails, or cmd type less than @a CMD_MAX_NUMBER on success - */ -enum f2b_cmd_type -f2b_cmd_parse(char *buf, size_t bufsize, const char *src); - -/** - * @brief Append data piece to data buffer of control message - * @param buf Buffer of control message for storing parsed args - * @param bufsize Size of buffer size above - * @param arg Piece to append - */ -void -f2b_cmd_append_arg(char *buf, size_t bufsize, const char *arg); -/** - * @brief Checks is args count match given command type - * @param type Command type - * @param argc Args count - * @returns true if matches, false if not + * @returns true on success, false otherwise */ bool -f2b_cmd_check_argc(enum f2b_cmd_type type, int argc); +f2b_cmd_parse(f2b_cmd_t *cmd, const char *src); #endif /* F2B_COMMANDS_H_ */ diff --git a/t/CMakeLists.txt b/t/CMakeLists.txt index ad6c2a7..e7c1093 100644 --- a/t/CMakeLists.txt +++ b/t/CMakeLists.txt @@ -3,7 +3,7 @@ enable_testing() set(SRC_DIR "../src") add_executable("t_buf" "t_buf.c" "${SRC_DIR}/strlcpy.c" "${SRC_DIR}/buf.c") -add_executable("t_cmd" "t_cmd.c" "${SRC_DIR}/strlcpy.c" "${SRC_DIR}/commands.c") +add_executable("t_cmd" "t_cmd.c" "${SRC_DIR}/strlcpy.c" "${SRC_DIR}/buf.c" "${SRC_DIR}/commands.c") add_executable("t_cmsg" "t_cmsg.c" "${SRC_DIR}/strlcpy.c" "${SRC_DIR}/cmsg.c") add_executable("t_matches" "t_matches.c" "${SRC_DIR}/strlcpy.c" "${SRC_DIR}/matches.c") add_executable("t_ipaddr" "t_ipaddr.c" "${SRC_DIR}/strlcpy.c" "${SRC_DIR}/matches.c" "${SRC_DIR}/ipaddr.c") diff --git a/t/t_cmd.c b/t/t_cmd.c index 708ff53..7194a89 100644 --- a/t/t_cmd.c +++ b/t/t_cmd.c @@ -1,34 +1,41 @@ #include "../src/common.h" +#include "../src/buf.h" #include "../src/commands.h" int main() { - char buf[1024]; - const char *line; - - UNUSED(line); - - buf[0] = '\0'; - f2b_cmd_append_arg(buf, sizeof(buf), "42"); - assert(strcmp(buf, "42\n") == 0); - - line = "status"; - assert(f2b_cmd_parse(buf, sizeof(buf), line) == CMD_STATUS); - - line = "statu"; /* no such command */ - assert(f2b_cmd_parse(buf, sizeof(buf), line) == CMD_NONE); - - line = "jail test"; /* incomplete command */ - assert(f2b_cmd_parse(buf, sizeof(buf), line) == CMD_NONE); - - buf[0] = '\0'; - line = "jail test status"; - assert(f2b_cmd_parse(buf, sizeof(buf), line) == CMD_JAIL_STATUS); - assert(strcmp(buf, "test\n") == 0); - - buf[0] = '\0'; - line = "jail test set bantime 7200"; - assert(f2b_cmd_parse(buf, sizeof(buf), line) == CMD_JAIL_SET); - assert(strcmp(buf, "test\nbantime\n7200\n") == 0); + f2b_cmd_t cmd; + + assert(f2b_cmd_parse(&cmd, "status") == true); + assert(cmd.type = CMD_STATUS); + assert(cmd.argc == 1); + assert(strcmp(cmd.args[0], "status") == 0); + assert(cmd.data.used == 6); /* "status" */ + f2b_buf_free(&cmd.data); + + assert(f2b_cmd_parse(&cmd, "stat") == false); /* no such command */ + assert(cmd.type == CMD_UNKNOWN); + + assert(f2b_cmd_parse(&cmd, "jail test") == false); /* incomplete command */ + assert(cmd.type == CMD_UNKNOWN); + + assert(f2b_cmd_parse(&cmd, "jail test status") == true); + assert(cmd.type == CMD_JAIL_STATUS); + assert(cmd.argc == 3); + assert(strcmp(cmd.args[0], "jail") == 0); + assert(strcmp(cmd.args[1], "test") == 0); + assert(strcmp(cmd.args[2], "status") == 0); + assert(cmd.data.used == 16); /* "jail\0test\0status" */ + f2b_buf_free(&cmd.data); + + assert(f2b_cmd_parse(&cmd, "jail test set bantime 7200") == true); + assert(cmd.type == CMD_JAIL_SET); + assert(cmd.argc == 5); + assert(strcmp(cmd.args[0], "jail") == 0); + assert(strcmp(cmd.args[1], "test") == 0); + assert(strcmp(cmd.args[2], "set") == 0); + assert(strcmp(cmd.args[3], "bantime") == 0); + assert(strcmp(cmd.args[4], "7200") == 0); + f2b_buf_free(&cmd.data); return EXIT_SUCCESS; } From 8988d5f8bc62549539ffa027f66750e916de13d6 Mon Sep 17 00:00:00 2001 From: Alex 'AdUser' Z Date: Fri, 15 Jan 2021 00:57:15 +1000 Subject: [PATCH 04/20] * move SA_REGISTER() macro to daemon.c, cleanup headers --- src/common.h | 20 ++++---------------- src/daemon.c | 12 ++++++++++++ 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/common.h b/src/common.h index 9537462..fb883c2 100644 --- a/src/common.h +++ b/src/common.h @@ -39,17 +39,13 @@ */ #define DEFAULT_PIDFILE_PATH "/var/run/f2b.pid" /** - * Default path of unix control socket (server endpoint) + * Default path of unix control socket */ #define DEFAULT_CSOCKET_PATH "/var/run/f2b.sock" /** * Default path of directory to store ip states for jails */ #define DEFAULT_STATEDIR_PATH "/var/db/f2b" -/** - * Template for making path for client side of connection to control socket - */ -#define DEFAULT_CSOCKET_CPATH "/tmp/f2bc-sock-XXXXXX" /** * @def UNUSED @@ -57,16 +53,8 @@ */ #define UNUSED(x) (void)(x) -/** - * @def SA_REGISTER - * Register signal handler - */ -#define SA_REGISTER(SIGNUM, HANDLER) \ - memset(&act, 0x0, sizeof(act)); \ - act.sa_handler = HANDLER; \ - if (sigaction(SIGNUM, &act, NULL) != 0) { \ - f2b_log_msg(log_fatal, "can't register handler for " #SIGNUM); \ - return EXIT_FAILURE; \ - } +/* default size of buffers */ +#define RBUF_SIZE 256 +#define WBUF_SIZE 32768 /* 32Kb */ #endif /* F2B_COMMON_H_ */ diff --git a/src/daemon.c b/src/daemon.c index 6597658..3c7a201 100644 --- a/src/daemon.c +++ b/src/daemon.c @@ -17,6 +17,18 @@ #include #include +/** + * @def SA_REGISTER + * Register signal handler + */ +#define SA_REGISTER(SIGNUM, HANDLER) \ + memset(&act, 0x0, sizeof(act)); \ + act.sa_handler = HANDLER; \ + if (sigaction(SIGNUM, &act, NULL) != 0) { \ + f2b_log_msg(log_fatal, "can't register handler for " #SIGNUM); \ + return EXIT_FAILURE; \ + } + enum { stop = 0, run, reconfig, logrotate, test } state = run; void signal_handler(int signum) { From 94676fea56c0ed8192bdb28caaef220168ea06bb Mon Sep 17 00:00:00 2001 From: Alex 'AdUser' Z Date: Fri, 15 Jan 2021 00:58:51 +1000 Subject: [PATCH 05/20] * fully refactor client (f2bc) --- src/CMakeLists.txt | 2 +- src/client.c | 168 +++++++++++++++++++++++++-------------------- 2 files changed, 96 insertions(+), 74 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index dd79925..217a297 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -12,7 +12,7 @@ add_executable("f2b" ${SOURCES}) install(TARGETS "f2b" RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR}) if (WITH_CLIENT) - set(SOURCES "strlcpy.c" "log.c" "client.c" "cmsg.c" "commands.c" "csocket.c") + set(SOURCES "strlcpy.c" "client.c") add_executable("f2bc" ${SOURCES}) if (WITH_READLINE) add_definitions("-DWITH_READLINE") diff --git a/src/client.c b/src/client.c index b73290f..acd73ee 100644 --- a/src/client.c +++ b/src/client.c @@ -4,27 +4,23 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ +#include +#include +#include + #include "common.h" -#include "cmsg.h" -#include "commands.h" -#include "csocket.h" #include "client.h" -#include "log.h" - -#include struct { enum { interactive = 0, oneshot } mode; - int csocket; - float timeout; - char csocket_spath[PATH_MAX]; - char csocket_cpath[PATH_MAX]; + char spath[PATH_MAX]; } opts = { - interactive, -1, 5.0, + interactive, DEFAULT_CSOCKET_PATH, - DEFAULT_CSOCKET_CPATH, /* template */ }; +static int csock = -1; + void usage(int exitcode) { fputs("Usage: f2bc [-h] [-s ] [-c ]\n", stdout); fputs("\t-h Show this message\n", stdout); @@ -35,59 +31,55 @@ void usage(int exitcode) { int handle_cmd(const char *line) { - f2b_cmsg_t cmsg; - struct sockaddr_storage addr; - socklen_t addrlen = 0; - int ret; - - memset(&addr, 0x0, sizeof(addr)); - memset(&cmsg, 0x0, sizeof(cmsg)); - - cmsg.type = f2b_cmd_parse(&cmsg.data[0], sizeof(cmsg.data), line); - if (cmsg.type == CMD_HELP) { - f2b_cmd_help(); - return EXIT_SUCCESS; - } else if (cmsg.type == CMD_NONE) { - printf("! unable to parse command line\n"); - return EXIT_FAILURE; - } - /* fill other fields */ - strncpy(cmsg.magic, "F2B", sizeof(cmsg.magic)); - cmsg.version = F2B_PROTO_VER; - cmsg.size = strlen(cmsg.data); - cmsg.data[cmsg.size] = '\0'; - f2b_cmsg_convert_args(&cmsg); - cmsg.flags |= CMSG_FLAG_NEED_REPLY; - if ((ret = f2b_csocket_send(opts.csocket, &cmsg, NULL, &addrlen)) < 0) - return EXIT_FAILURE; - - memset(&cmsg, 0x0, sizeof(cmsg)); - addrlen = sizeof(addr); - if ((ret = f2b_csocket_recv(opts.csocket, &cmsg, &addr, &addrlen)) < 0) { - printf("! control sicket: %s\n", f2b_csocket_error(ret)); - return EXIT_FAILURE; + const char *p = NULL; + char buf[WBUF_SIZE] = ""; /* our "read" is server "write" */ + int ret; int len; + + assert(line != NULL); + + snprintf(buf, sizeof(buf), "%s\n", line); + p = buf; + while (p && *p != '\0') { + len = strlen(p); + ret = send(csock, p, strlen(p), 0); + /* blocks only for a second, see timeouts */ + if (ret < 0 && errno == EAGAIN) { + continue; /* try again */ + } else if (ret < 0) { + perror("send()"); + exit(EXIT_FAILURE); + } else if (ret == len){ + break; /* all data sent */ + } else /* ret > 0 */ { + p += ret; + } } - if (cmsg.type != CMD_RESP) { - printf("! recieved message not a 'response' type\n"); - return EXIT_FAILURE; + while (1) { + ret = recv(csock, &buf, sizeof(buf), 0); + /* blocks only for a second, see timeouts */ + if (ret > 0) { + write(fileno(stdout), buf, ret); + continue; + } else if (ret == 0) { + puts("connection closed"); + exit(EXIT_SUCCESS); /* received EOF */ + } else if (ret < 0 && errno == EAGAIN) { + break; + } else /* ret < 0 */ { + perror("recv()"); + exit(EXIT_FAILURE); + } } - fputs(cmsg.data, stdout); - fputc('\n', stdout); - return EXIT_SUCCESS; -} -void cleanup() { - f2b_csocket_disconnect(opts.csocket, opts.csocket_cpath); - unlink(opts.csocket_cpath); + return 0; } void -signal_handler(int signum) { +handle_signal(int signum) { switch (signum) { case SIGINT: case SIGTERM: - cleanup(); exit(EXIT_SUCCESS); break; default: @@ -95,6 +87,48 @@ signal_handler(int signum) { } } +void +setup_sigaction(int signum) { + struct sigaction act; + memset(&act, 0x0, sizeof(act)); + act.sa_handler = &handle_signal; + if (sigaction(signum, &act, NULL) != 0) { + perror("sigaction()"); + exit(EXIT_FAILURE); + } +} + +void +setup_socket() { + struct sockaddr_un saddr; + struct timeval tv; + + if ((csock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { + perror("socket()"); + exit(EXIT_FAILURE); + } + + tv.tv_sec = 1, tv.tv_usec = 0; + if (setsockopt(csock, SOL_SOCKET, SO_RCVTIMEO, (void *) &tv, sizeof(struct timeval)) < 0) { + perror("setsockopt() - recv"); + exit(EXIT_FAILURE); + } + + tv.tv_sec = 1, tv.tv_usec = 0; + if (setsockopt(csock, SOL_SOCKET, SO_SNDTIMEO, (void *) &tv, sizeof(struct timeval)) < 0) { + perror("setsockopt() - send"); + exit(EXIT_FAILURE); + } + + memset(&saddr, 0x0, sizeof(saddr)); + saddr.sun_family = AF_UNIX; + strlcpy(saddr.sun_path, opts.spath, sizeof(saddr.sun_path) - 1); + if (connect(csock, (struct sockaddr *) &saddr, sizeof(struct sockaddr_un)) < 0) { + perror("connect()"); + exit(EXIT_FAILURE); + } +} + #ifdef WITH_READLINE #include #include @@ -129,7 +163,6 @@ readline(const char *prompt) { #endif int main(int argc, char *argv[]) { - struct sigaction act; char *line = NULL; char opt = '\0'; int ret; @@ -138,10 +171,10 @@ int main(int argc, char *argv[]) { switch (opt) { case 'c': opts.mode = oneshot; - line = strndup(optarg, INPUT_LINE_MAX); + line = strndup(optarg, RBUF_SIZE); break; case 's': - strlcpy(opts.csocket_spath, optarg, sizeof(opts.csocket_spath)); + strlcpy(opts.spath, optarg, sizeof(opts.spath)); break; case 'h': usage(EXIT_SUCCESS); @@ -152,23 +185,14 @@ int main(int argc, char *argv[]) { } } - SA_REGISTER(SIGTERM, &signal_handler); - SA_REGISTER(SIGINT, &signal_handler); - - /* prepare client side of socket */ - ret = mkstemp(opts.csocket_cpath); - if (ret >= 0) - close(ret); /* suppress compiler warning */ - unlink(opts.csocket_cpath); /* remove regular file created by mkstemp() */ + setup_sigaction(SIGTERM); + setup_sigaction(SIGINT); - if ((opts.csocket = f2b_csocket_connect(opts.csocket_spath, opts.csocket_cpath)) <= 0) - exit(EXIT_FAILURE); - - f2b_csocket_rtimeout(opts.csocket, opts.timeout); + setup_socket(); if (opts.mode == oneshot) { ret = handle_cmd(line); - f2b_csocket_disconnect(opts.csocket, opts.csocket_cpath); + shutdown(csock, SHUT_RDWR); exit(ret); } @@ -183,7 +207,5 @@ int main(int argc, char *argv[]) { } putc('\n', stdout); - cleanup(); - return EXIT_SUCCESS; } From 342c075e00df43d2b5a72e0757d44c93aca834c9 Mon Sep 17 00:00:00 2001 From: Alex 'AdUser' Z Date: Fri, 15 Jan 2021 01:03:13 +1000 Subject: [PATCH 06/20] * drop multicast source/backend (so, it was funny, but it's time to die) --- CMakeLists.txt | 2 - src/backends/CMakeLists.txt | 6 - src/backends/mcast.c | 217 ----------------------------- src/sources/CMakeLists.txt | 6 - src/sources/mcast.c | 271 ------------------------------------ 5 files changed, 502 deletions(-) delete mode 100644 src/backends/mcast.c delete mode 100644 src/sources/mcast.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 5d50f61..e85176a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,7 +12,6 @@ option(WITH_HARDENING "Enable hardening options" ON) option(WITH_READLINE "Use readline library for client" ON) option(WITH_PCRE "Build pcre-compatible filter" ON) option(WITH_REDIS "Build redis source/backend" OFF) -option(WITH_MCAST "Build mcast source/backend" OFF) option(WITH_IPSET "Build native ipset backend" OFF) if (NOT DEFINED CMAKE_INSTALL_PREFIX) @@ -56,7 +55,6 @@ message(STATUS "- WITH_CSOCKET : ${WITH_CSOCKET}") message(STATUS "- WITH_HARDENING : ${WITH_HARDENING}") message(STATUS "- WITH_PCRE : ${WITH_PCRE}") message(STATUS "- WITH_REDIS : ${WITH_REDIS}") -message(STATUS "- WITH_MCAST : ${WITH_MCAST}") message(STATUS "- WITH_IPSET : ${WITH_IPSET}") message(STATUS "- WITH_READLINE : ${WITH_READLINE}") message(STATUS "Components:") diff --git a/src/backends/CMakeLists.txt b/src/backends/CMakeLists.txt index 2f9e87f..606570f 100644 --- a/src/backends/CMakeLists.txt +++ b/src/backends/CMakeLists.txt @@ -5,12 +5,6 @@ set(BACKENDS "") add_library("b_exec" MODULE "exec.c" "../strlcpy.c") list(APPEND BACKENDS "exec") -if (WITH_MCAST) - add_library("b_mcast" MODULE "mcast.c" "../strlcpy.c" - "../commands.c" "../cmsg.c" "../csocket.c") - list(APPEND BACKENDS "mcast") -endif () - find_library(REDIS_FOUND "hiredis") if (WITH_REDIS AND REDIS_FOUND) add_library("b_redis" MODULE "redis.c" "../strlcpy.c") diff --git a/src/backends/mcast.c b/src/backends/mcast.c deleted file mode 100644 index 23a90ef..0000000 --- a/src/backends/mcast.c +++ /dev/null @@ -1,217 +0,0 @@ -/* Copyright 2016 Alex 'AdUser' Z (ad_user@runbox.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../strlcpy.h" -#include "../commands.h" -#include "../cmsg.h" -#include "../csocket.h" - -#include "backend.h" -#include "shared.c" - -#define DEFAULT_MCAST_ADDR "239.255.186.1" -#define DEFAULT_MCAST_PORT "3370" - -struct _config { - char name[ID_MAX + 1]; - char error[256]; - bool shared; - char maddr[INET_ADDRSTRLEN]; /**< multicast address */ - char mport[6]; /**< multicast port */ - char iface[IF_NAMESIZE]; /**< bind interface */ - int sock; - struct sockaddr_storage sa; - socklen_t sa_len; -}; - -cfg_t * -create(const char *id) { - cfg_t *cfg = NULL; - - assert(id != NULL); - - if ((cfg = calloc(1, sizeof(cfg_t))) == NULL) - return NULL; - strlcpy(cfg->name, id, sizeof(cfg->name)); - strlcpy(cfg->maddr, DEFAULT_MCAST_ADDR, sizeof(cfg->maddr)); - strlcpy(cfg->mport, DEFAULT_MCAST_PORT, sizeof(cfg->mport)); - - return cfg; -} - -bool -config(cfg_t *cfg, const char *key, const char *value) { - assert(cfg != NULL); - assert(key != NULL); - assert(value != NULL); - - if (strcmp(key, "group") == 0) { - if (strncmp(value, "239.255.", 8) != 0) { - strlcpy(cfg->error, "mcast group address should be inside 239.255.0.0/16 block", sizeof(cfg->error)); - return false; - } - strlcpy(cfg->maddr, value, sizeof(cfg->maddr)); - return true; - } - if (strcmp(key, "port") == 0) { - strlcpy(cfg->mport, value, sizeof(cfg->mport)); - return true; - } - if (strcmp(key, "iface") == 0) { - strlcpy(cfg->iface, value, sizeof(cfg->iface)); - return true; - } - - return false; -} - -bool -ready(cfg_t *cfg) { - assert(cfg != NULL); - - if (cfg->maddr[0] && cfg->mport[0]) - return true; - - return false; -} - -char * -error(cfg_t *cfg) { - assert(cfg != NULL); - - return cfg->error; -} - -bool -start(cfg_t *cfg) { - struct addrinfo hints; - struct addrinfo *result; - assert(cfg != NULL); - int ret, sock = -1; - - if (cfg->shared && usage_inc(cfg->name) > 1) - return true; - - memset(&hints, 0, sizeof(struct addrinfo)); - hints.ai_family = AF_INET; - hints.ai_socktype = SOCK_DGRAM; - hints.ai_protocol = 0; - - if ((ret = getaddrinfo(cfg->maddr, cfg->mport, &hints, &result)) < 0) { - snprintf(cfg->error, sizeof(cfg->error), "can't create socket: %s", gai_strerror(ret)); - return false; - } - - for (struct addrinfo *rp = result; rp != NULL; rp = rp->ai_next) { - if (sock >= 0) { - close(sock); /* from prev iteration */ - sock = -1; - } - if ((sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol)) < 0) { - snprintf(cfg->error, sizeof(cfg->error), "can't create socket: %s", strerror(errno)); - continue; - } - memcpy(&cfg->sa, rp->ai_addr, rp->ai_addrlen); - cfg->sa_len = rp->ai_addrlen; - break; - } - freeaddrinfo(result); - - if (sock < 0) - return false; - - cfg->sock = sock; - return true; - - return true; -} - -bool -stop(cfg_t *cfg) { - assert(cfg != NULL); - - if (cfg->shared && usage_dec(cfg->name) > 0) - return true; - - close(cfg->sock); - cfg->sock = -1; - - return true; -} - -bool -ban(cfg_t *cfg, const char *ip) { - f2b_cmsg_t cmsg; - int ret; - - assert(cfg != NULL); - - memset(&cmsg, 0x0, sizeof(cmsg)); - - strncpy(cmsg.magic, "F2B", sizeof(cmsg.magic)); - cmsg.version = F2B_PROTO_VER; - cmsg.type = CMD_JAIL_IP_BAN; - f2b_cmd_append_arg(cmsg.data, sizeof(cmsg.data), cfg->name); - f2b_cmd_append_arg(cmsg.data, sizeof(cmsg.data), ip); - cmsg.size = strlen(cmsg.data); - cmsg.data[cmsg.size] = '\0'; - f2b_cmsg_convert_args(&cmsg); - - ret = f2b_csocket_send(cfg->sock, &cmsg, &cfg->sa, &cfg->sa_len); - if (ret <= 0) { - strlcpy(cfg->error, f2b_csocket_error(ret), sizeof(cfg->error)); - return false; - } - - return true; -} - -bool -unban(cfg_t *cfg, const char *ip) { - assert(cfg != NULL); - - (void)(cfg); /* suppress warning for unused variable 'cfg' */ - (void)(ip); /* suppress warning for unused variable 'ip' */ - return true; -} - -bool -check(cfg_t *cfg, const char *ip) { - assert(cfg != NULL); - - (void)(cfg); /* suppress warning for unused variable 'cfg' */ - (void)(ip); /* suppress warning for unused variable 'ip' */ - return false; -} - -bool -ping(cfg_t *cfg) { - assert(cfg != NULL); - - (void)(cfg); /* silence warning about unused variable */ - return true; -} - -void -destroy(cfg_t *cfg) { - assert(cfg != NULL); - - free(cfg); -} diff --git a/src/sources/CMakeLists.txt b/src/sources/CMakeLists.txt index a6003d6..ae32ed1 100644 --- a/src/sources/CMakeLists.txt +++ b/src/sources/CMakeLists.txt @@ -8,12 +8,6 @@ list(APPEND SOURCES "files") add_library("s_portknock" MODULE "portknock.c" "../strlcpy.c") list(APPEND SOURCES "portknock") -if (WITH_MCAST) - add_library("s_mcast" MODULE "mcast.c" "../strlcpy.c" - "../commands.c" "../cmsg.c" "../csocket.c") - list(APPEND SOURCES "mcast") -endif () - find_library(REDIS_FOUND "hiredis") if (WITH_REDIS AND REDIS_FOUND) add_library("s_redis" MODULE "redis.c" "../strlcpy.c") diff --git a/src/sources/mcast.c b/src/sources/mcast.c deleted file mode 100644 index 68b3761..0000000 --- a/src/sources/mcast.c +++ /dev/null @@ -1,271 +0,0 @@ -/* Copyright 2016 Alex 'AdUser' Z (ad_user@runbox.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#include "source.h" - -#include -#include -#include -#include -#include -#include -#include - -#include "../commands.h" -#include "../cmsg.h" -#include "../csocket.h" - -#define DEFAULT_BIND_ADDR "0.0.0.0" -#define DEFAULT_MCAST_ADDR "239.255.186.1" -#define DEFAULT_MCAST_PORT "3370" - -#if defined(IPV6_JOIN_GROUP) && !defined(IPV6_ADD_MEMBERSHIP) - /* bsd-derivatives */ - #define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP -#endif - -struct _config { - char name[32]; - char error[256]; - void (*errcb)(const char *errstr); - char baddr[INET6_ADDRSTRLEN]; /**< bind address */ - char maddr[INET6_ADDRSTRLEN]; /**< multicast address */ - char mport[6]; /**< multicast port */ - char iface[IF_NAMESIZE]; /**< bind interface */ - int sock; -}; - -static void -errcb_stub(const char *str) { - assert(str != NULL); - (void)(str); -} - -cfg_t * -create(const char *init) { - cfg_t *cfg = NULL; - assert(init != NULL); - if ((cfg = calloc(1, sizeof(cfg_t))) == NULL) - return NULL; - strlcpy(cfg->name, init, sizeof(cfg->name)); - strlcpy(cfg->baddr, DEFAULT_BIND_ADDR, sizeof(cfg->baddr)); - strlcpy(cfg->maddr, DEFAULT_MCAST_ADDR, sizeof(cfg->maddr)); - strlcpy(cfg->mport, DEFAULT_MCAST_PORT, sizeof(cfg->mport)); - cfg->errcb = &errcb_stub; - return cfg; -} - -bool -config(cfg_t *cfg, const char *key, const char *value) { - assert(cfg != NULL); - assert(key != NULL); - assert(value != NULL); - - if (strcmp(key, "group") == 0) { - if (strncmp(value, "239.255.", 8) != 0) { - strlcpy(cfg->error, "mcast group address should be inside 239.255.0.0/16 block", sizeof(cfg->error)); - return false; - } - strlcpy(cfg->maddr, value, sizeof(cfg->maddr)); - return true; - } - if (strcmp(key, "address") == 0) { - strlcpy(cfg->baddr, value, sizeof(cfg->baddr)); - return true; - } - if (strcmp(key, "port") == 0) { - strlcpy(cfg->mport, value, sizeof(cfg->mport)); - return true; - } - if (strcmp(key, "iface") == 0) { - strlcpy(cfg->iface, value, sizeof(cfg->iface)); - return true; - } - - return false; -} - -bool -ready(cfg_t *cfg) { - assert(cfg != NULL); - - if (cfg->maddr[0] && (cfg->iface[0] || cfg->baddr[0])) - return true; - - return false; -} - -char * -error(cfg_t *cfg) { - assert(cfg != NULL); - - return cfg->error; -} - -void -errcb(cfg_t *cfg, void (*cb)(const char *errstr)) { - assert(cfg != NULL); - assert(cb != NULL); - - cfg->errcb = cb; -} - -bool -start(cfg_t *cfg) { - struct addrinfo hints; - struct addrinfo *maddr, *laddr; - int opt, ret, sock = -1; - - assert(cfg != NULL); - - memset(&hints, 0, sizeof(struct addrinfo)); - hints.ai_family = AF_UNSPEC; - hints.ai_flags = AI_NUMERICHOST; - hints.ai_socktype = SOCK_DGRAM; - hints.ai_protocol = 0; - - if ((ret = getaddrinfo(cfg->maddr, cfg->mport, &hints, &maddr)) < 0) { - snprintf(cfg->error, sizeof(cfg->error), "can't resolve mcast addr: %s", gai_strerror(ret)); - return false; - } - - hints.ai_family = maddr->ai_family; - hints.ai_flags = AI_PASSIVE; - hints.ai_socktype = SOCK_DGRAM; - - if ((ret = getaddrinfo(cfg->baddr[0] != '\0' ? cfg->baddr : NULL, cfg->mport, &hints, &laddr)) < 0) { - snprintf(cfg->error, sizeof(cfg->error), "can't resolve local addr: %s", gai_strerror(ret)); - freeaddrinfo(maddr); - return false; - } - - do { - cfg->sock = -1; - /* create socket */ - if ((sock = socket(laddr->ai_family, laddr->ai_socktype, laddr->ai_protocol)) < 0) { - snprintf(cfg->error, sizeof(cfg->error), "can't create socket: %s", strerror(errno)); - continue; - } - /* set non-blocking mode */ - if ((opt = fcntl(sock, F_GETFL, 0)) < 0) { - continue; - } - fcntl(sock, F_SETFL, opt | O_NONBLOCK); - /* reuse address */ - opt = 1; - setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); - /* bind to given address */ - if (bind(sock, laddr->ai_addr, laddr->ai_addrlen) < 0) { - snprintf(cfg->error, sizeof(cfg->error), "can't bind socket to addr %s: %s", - cfg->baddr, strerror(errno)); - continue; - } - /* IP_MULTICAST_LOOP -- default: yes */ - /* IP_MULTICAST_TTL -- default: 1 */ - /* join mcast group */ - if (maddr->ai_family == AF_INET) { - struct ip_mreq mreq; - struct in_addr *addr = NULL; - memset(&mreq, 0x0, sizeof(mreq)); - addr = &((struct sockaddr_in *)(maddr->ai_addr))->sin_addr; - memcpy(&mreq.imr_multiaddr, addr, sizeof(mreq.imr_multiaddr)); - addr = &((struct sockaddr_in *)(laddr->ai_addr))->sin_addr; - memcpy(&mreq.imr_interface, addr, sizeof(mreq.imr_interface)); - if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) { - snprintf(cfg->error, sizeof(cfg->error), "can't join mcast group: %s", - strerror(errno)); - continue; - } - } else if (maddr->ai_family == AF_INET6) { - struct ipv6_mreq mreq; - struct in6_addr *addr = NULL; - memset(&mreq, 0x0, sizeof(mreq)); - addr = &((struct sockaddr_in6 *)(maddr->ai_addr))->sin6_addr; - memcpy(&mreq.ipv6mr_multiaddr, addr, sizeof(mreq.ipv6mr_multiaddr)); - if (cfg->iface[0] != '\0') { - mreq.ipv6mr_interface = if_nametoindex(cfg->iface); - } - if (setsockopt(sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) { - snprintf(cfg->error, sizeof(cfg->error), "can't join mcast group: %s", - strerror(errno)); - continue; - } - } else { - snprintf(cfg->error, sizeof(cfg->error), "unknown family for mcast addr: %d", laddr->ai_family); - continue; - } - /* all ok */ - cfg->sock = sock; - sock = -1; - } while (0); - - freeaddrinfo(maddr); - freeaddrinfo(laddr); - - if (cfg->sock < 0) - return false; - - return true; -} - -bool -stop(cfg_t *cfg) { - assert(cfg != NULL); - - /* mcast group will be leaved automatically */ - close(cfg->sock); - cfg->sock = -1; - - return true; -} - -bool -next(cfg_t *cfg, char *buf, size_t bufsize, bool reset) { - const char *args[DATA_ARGS_MAX]; - struct sockaddr_storage addr; - socklen_t socklen; - f2b_cmsg_t cmsg; - int ret; - - (void)(reset); - assert(cfg != NULL); - assert(buf != NULL); - assert(bufsize > 0); - - memset(&cmsg, 0x0, sizeof(cmsg)); - memset(&addr, 0x0, sizeof(addr)); - - while (1) { - ret = f2b_csocket_recv(cfg->sock, &cmsg, &addr, &socklen); - if (ret == 0) - break; /* no messages */ - if (ret < 0) { - cfg->errcb(f2b_csocket_error(ret)); - continue; - } - /* ret > 0 */ - if (cmsg.type != CMD_JAIL_IP_BAN) - continue; - ret = f2b_cmsg_extract_args(&cmsg, args); - if (!f2b_cmd_check_argc(cmsg.type, ret)) { - cfg->errcb("received cmsg with wrong args count"); - continue; - } - if (strcmp(cfg->name, args[0]) != 0) - continue; /* wrong group */ - strlcpy(buf, args[1], bufsize); - return true; - } - - return false; -} - -void -destroy(cfg_t *cfg) { - assert(cfg != NULL); - - free(cfg); -} From 62e9e2d2d19f908cf7c5652a5521ee777f2248ae Mon Sep 17 00:00:00 2001 From: Alex 'AdUser' Z Date: Fri, 15 Jan 2021 01:04:35 +1000 Subject: [PATCH 07/20] * drop tests for cmsg (prepare for delete f2b_cmsg_t) --- t/CMakeLists.txt | 2 -- t/t_cmsg.c | 32 -------------------------------- 2 files changed, 34 deletions(-) delete mode 100644 t/t_cmsg.c diff --git a/t/CMakeLists.txt b/t/CMakeLists.txt index e7c1093..f4a19ca 100644 --- a/t/CMakeLists.txt +++ b/t/CMakeLists.txt @@ -4,7 +4,6 @@ set(SRC_DIR "../src") add_executable("t_buf" "t_buf.c" "${SRC_DIR}/strlcpy.c" "${SRC_DIR}/buf.c") add_executable("t_cmd" "t_cmd.c" "${SRC_DIR}/strlcpy.c" "${SRC_DIR}/buf.c" "${SRC_DIR}/commands.c") -add_executable("t_cmsg" "t_cmsg.c" "${SRC_DIR}/strlcpy.c" "${SRC_DIR}/cmsg.c") add_executable("t_matches" "t_matches.c" "${SRC_DIR}/strlcpy.c" "${SRC_DIR}/matches.c") add_executable("t_ipaddr" "t_ipaddr.c" "${SRC_DIR}/strlcpy.c" "${SRC_DIR}/matches.c" "${SRC_DIR}/ipaddr.c") add_executable("t_statefile" "t_statefile.c" "${SRC_DIR}/strlcpy.c" "${SRC_DIR}/matches.c" "${SRC_DIR}/ipaddr.c" "${SRC_DIR}/statefile.c" "${SRC_DIR}/log.c") @@ -13,7 +12,6 @@ add_executable("t_backend_usage" "t_backend_usage.c" "${SRC_DIR}/strlcpy.c") add_test("tests/f2b_buf_*" "t_buf") add_test("tests/f2b_cmd_*" "t_cmd") -add_test("tests/f2b_cmsg_*" "t_cmsg") add_test("tests/f2b_matches_*" "t_matches") add_test("tests/f2b_ipaddr_*" "t_ipaddr") add_test("tests/f2b_statefile_*" "t_statefile") diff --git a/t/t_cmsg.c b/t/t_cmsg.c deleted file mode 100644 index fb87aed..0000000 --- a/t/t_cmsg.c +++ /dev/null @@ -1,32 +0,0 @@ -#include "../src/common.h" -#include "../src/cmsg.h" - -int main() { - const char *argv[DATA_ARGS_MAX]; - f2b_cmsg_t msg; - - memset(&msg, 0x0, sizeof(msg)); - memset(argv, 0x0, sizeof(argv)); - - snprintf(msg.data, sizeof(msg.data), "test1\ntest2\n"); - msg.size = strlen(msg.data); - - f2b_cmsg_convert_args(&msg); - assert(memcmp(msg.data, "test1\0test2\0", 12) == 0); - - f2b_cmsg_extract_args(&msg, argv); - assert(argv[0] == msg.data + 0); - assert(argv[1] == msg.data + 6); - assert(memcmp(argv[0], "test1\0", 6) == 0); - assert(memcmp(argv[1], "test2\0", 6) == 0); - - /* data not null-terminated */ - msg.size = 10; - memcpy(msg.data, "test1\0test2\n", 10); - assert(f2b_cmsg_extract_args(&msg, argv) == -1); - - msg.size = 0; - assert(f2b_cmsg_extract_args(&msg, argv) == 0); - - return EXIT_SUCCESS; -} From 0c4958b2b252147738127aa634347dc4726e68ae Mon Sep 17 00:00:00 2001 From: Alex 'AdUser' Z Date: Mon, 18 Jan 2021 12:38:40 +1000 Subject: [PATCH 08/20] - cmsg.[ch] --- src/CMakeLists.txt | 2 +- src/cmsg.c | 42 ---------------------------- src/cmsg.h | 68 ---------------------------------------------- src/csocket.c | 3 +- src/csocket.h | 4 ++- src/daemon.c | 1 - src/filter.h | 2 +- src/jail.h | 2 +- 8 files changed, 7 insertions(+), 117 deletions(-) delete mode 100644 src/cmsg.c delete mode 100644 src/cmsg.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 217a297..4252e69 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -4,7 +4,7 @@ set(SOURCES "daemon.c" "strlcpy.c" "config.c" "log.c" "matches.c" "ipaddr.c" "appconfig.c" "statefile.c" "source.c" "filter.c" "backend.c" "jail.c") if (WITH_CSOCKET) - list(APPEND SOURCES "commands.c" "csocket.c" "cmsg.c") + list(APPEND SOURCES "commands.c" "csocket.c") add_definitions("-DWITH_CSOCKET") endif () diff --git a/src/cmsg.c b/src/cmsg.c deleted file mode 100644 index 86ffda5..0000000 --- a/src/cmsg.c +++ /dev/null @@ -1,42 +0,0 @@ -#include "common.h" -#include "commands.h" -#include "cmsg.h" - -#include - -void -f2b_cmsg_convert_args(f2b_cmsg_t *msg) { - assert(msg != NULL); - - for (size_t i = 0; i < msg->size && i < sizeof(msg->data); i++) { - if (msg->data[i] == '\n') - msg->data[i] = '\0'; - } -} - -int -f2b_cmsg_extract_args(const f2b_cmsg_t *msg, const char **argv) { - char prev = '\0'; - size_t argc = 0; - - assert(msg != NULL); - assert(argv != NULL); - - if (msg->size == 0) - return 0; /* no args */ - - if (msg->data[msg->size - 1] != '\0') - return -1; /* message data not null-terminated */ - - for (size_t i = 0; i < msg->size; i++) { - if (prev == '\0' && msg->data[i] != '\0') - argv[argc] = &msg->data[i], argc++; - if (argc >= DATA_ARGS_MAX) { - argc = -1; - break; - } - prev = msg->data[i]; - } - - return argc; -} diff --git a/src/cmsg.h b/src/cmsg.h deleted file mode 100644 index e56bdd5..0000000 --- a/src/cmsg.h +++ /dev/null @@ -1,68 +0,0 @@ -#ifndef F2B_CMSG_H_ -#define F2B_CMSG_H_ - -/** - * @file - * This header contains definitions of control messages format and routines - */ - -/** - * @def DATA_LEN_MAX - * Maximum length of data in packet - */ -#define DATA_LEN_MAX 1476 /* 1500 - (16 bytes of cmsg header + 8 bytes of udp) */ -/** - * @def DATA_ARGS_MAX - * Maximum count of data pieces in packet - */ -#define DATA_ARGS_MAX 6 /* number of args in data */ -/** - * @def F2B_PROTO_VER - * Protocol version of control message - */ -#define F2B_PROTO_VER 1 - -/** - * @def CMSG_FLAG_NEED_REPLY - * Server should reply to this control message - */ -#define CMSG_FLAG_NEED_REPLY 0x01 -/** - * @def CMSG_FLAG_AUTH_PASS - * This control message contains password - */ -#define CMSG_FLAG_AUTH_PASS 0x02 - -/** - * f2b control message - * @note Use sendmsg/recvmsg and iovec structs to pack/unpack - */ -typedef struct f2b_cmsg_t { - char magic[3]; /**< magic string "F2B" */ - uint8_t version; /**< protocol version */ - /* 4 bytes */ - uint8_t type; /**< command type, cast from enum f2b_cmd_type */ - uint8_t flags; /**< CMSG_FLAG_* */ - uint16_t size; /**< payload length */ - /* 8 bytes */ - char pass[8]; /**< auth data */ - /* 16 bytes */ - /* end of header */ - char data[DATA_LEN_MAX]; /**< set of "\n"-terminated strings */ - /* end of data */ -} f2b_cmsg_t; - -/** - * @brief Convert every '\n' in data to '\0' - * @param msg Pointer to control message - */ -void f2b_cmsg_convert_args(f2b_cmsg_t *msg); -/** - * @brief Fill @a argv array with pointers to found data pieces - * @param msg Pointer to control message - * @param argv Array of pointers - * @returns Number of found args - */ -int f2b_cmsg_extract_args(const f2b_cmsg_t *msg, const char **argv); - -#endif /* F2B_CMSG_H_ */ diff --git a/src/csocket.c b/src/csocket.c index e6ac5d7..fffc1a4 100644 --- a/src/csocket.c +++ b/src/csocket.c @@ -6,7 +6,6 @@ */ #include "common.h" #include "commands.h" -#include "cmsg.h" #include "csocket.h" #include "log.h" @@ -202,7 +201,7 @@ f2b_csocket_send(int csock, f2b_cmsg_t *cmsg, struct sockaddr_storage *addr, soc } int -f2b_csocket_poll(int csock, void (*cb)(const f2b_cmsg_t *cmsg, char *res, size_t ressize)) { +f2b_csocket_poll(int csock, void (*cb)(const f2b_cmd_t *cmd, f2b_buf_t *res)) { char res[DATA_LEN_MAX + 1]; f2b_cmsg_t cmsg; struct sockaddr_storage addr; diff --git a/src/csocket.h b/src/csocket.h index 42662bb..725b594 100644 --- a/src/csocket.h +++ b/src/csocket.h @@ -64,7 +64,8 @@ f2b_csocket_error(int retcode); * @param cb Callback for handling message * @returns -1 on error, 0 on no messages, and > 0 on some messages processed */ -int f2b_csocket_poll(int csock, void (*cb)(const f2b_cmsg_t *cmsg, char *res, size_t ressize)); +int f2b_csocket_poll(int csock, void (*cb)(const f2b_cmd_t *cmd, f2b_buf_t *res)); + /** * @brief Pack and send control message * @param csock Opened socket fd @@ -74,6 +75,7 @@ int f2b_csocket_poll(int csock, void (*cb)(const f2b_cmsg_t *cmsg, char *res, si * @returns >0 on success */ int f2b_csocket_send(int csock, f2b_cmsg_t *cmsg, struct sockaddr_storage *addr, socklen_t *addrlen); + /** * @brief Recieve and unpack control message * @param csock Opened socket fd diff --git a/src/daemon.c b/src/daemon.c index 3c7a201..e8c2bbc 100644 --- a/src/daemon.c +++ b/src/daemon.c @@ -11,7 +11,6 @@ #include "backend.h" #include "appconfig.h" #include "commands.h" -#include "cmsg.h" #include "csocket.h" #include diff --git a/src/filter.h b/src/filter.h index 8198b45..81d932f 100644 --- a/src/filter.h +++ b/src/filter.h @@ -77,7 +77,7 @@ bool f2b_filter_append(f2b_filter_t *f, const char *pattern); */ bool f2b_filter_match (f2b_filter_t *f, const char *line, char *buf, size_t bufsize); -/* handlers for cmsg processing */ +/* handlers for csocket commands processing */ /** handler of 'jail $JAIL filter reload' cmd */ void f2b_filter_cmd_reload(char *buf, size_t bufsize, f2b_filter_t *f); /** handler of 'jail $JAIL filter stats' cmd */ diff --git a/src/jail.h b/src/jail.h index 13ad2bc..3c7a662 100644 --- a/src/jail.h +++ b/src/jail.h @@ -110,7 +110,7 @@ size_t f2b_jail_process (f2b_jail_t *jail); */ bool f2b_jail_stop (f2b_jail_t *jail); -/* handlers for cmsg */ +/* handlers for csocket commands processing */ /** * @brief Get jail status From 179be41f1835cf3b58cfc1cb6fb7e66d75fb73ad Mon Sep 17 00:00:00 2001 From: Alex 'AdUser' Z Date: Mon, 18 Jan 2021 12:38:43 +1000 Subject: [PATCH 09/20] * general cleanup : drop remaining f2b_cmsg_t --- src/CMakeLists.txt | 2 +- src/appconfig.c | 1 - src/appconfig.h | 1 - src/csocket.c | 252 ++++++++++----------------------------------- src/csocket.h | 63 ++---------- src/daemon.c | 122 ++++++++++++---------- 6 files changed, 130 insertions(+), 311 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4252e69..4d80b81 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,6 +1,6 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON) -set(SOURCES "daemon.c" "strlcpy.c" "config.c" "log.c" "matches.c" "ipaddr.c" +set(SOURCES "daemon.c" "strlcpy.c" "config.c" "buf.c" "log.c" "matches.c" "ipaddr.c" "appconfig.c" "statefile.c" "source.c" "filter.c" "backend.c" "jail.c") if (WITH_CSOCKET) diff --git a/src/appconfig.c b/src/appconfig.c index 7969dfe..d20d721 100644 --- a/src/appconfig.c +++ b/src/appconfig.c @@ -11,7 +11,6 @@ f2b_appconfig_t appconfig = { .daemon = false, - .csock = -1, .uid = 0, .gid = 0, .logdest = "file", diff --git a/src/appconfig.h b/src/appconfig.h index 54cd89b..9967bb1 100644 --- a/src/appconfig.h +++ b/src/appconfig.h @@ -14,7 +14,6 @@ typedef struct f2b_appconfig_t { bool daemon; uid_t uid; gid_t gid; - int csock; char logdest[CONFIG_KEY_MAX]; char config_path[PATH_MAX]; char logfile_path[PATH_MAX]; diff --git a/src/csocket.c b/src/csocket.c index fffc1a4..c484b5f 100644 --- a/src/csocket.c +++ b/src/csocket.c @@ -5,236 +5,98 @@ * published by the Free Software Foundation. */ #include "common.h" +#include "buf.h" +#include "log.h" #include "commands.h" #include "csocket.h" -#include "log.h" -int +#include +#include +#include + +typedef struct f2b_conn_t { + int sock; + const char *path; + f2b_buf_t recv; + f2b_buf_t send; +} f2b_conn_t; + +struct f2b_csock_t { + f2b_conn_t *clients[MAXCONNS]; + const char *path; + int sock; + bool shutdown; +}; + +f2b_csock_t * f2b_csocket_create(const char *path) { + f2b_csock_t *csock; struct sockaddr_un addr; - int csock = -1; - int flags = -1; + int sock = -1; assert(path != NULL); - if ((csock = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) { + if ((sock = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0)) < 0) { f2b_log_msg(log_error, "can't create control socket: %s", strerror(errno)); - return -1; + return NULL; } memset(&addr, 0x0, sizeof(addr)); addr.sun_family = AF_UNIX; strlcpy(addr.sun_path, path, sizeof(addr.sun_path) - 1); - if ((flags = fcntl(csock, F_GETFL, 0)) < 0) - return -1; - if (fcntl(csock, F_SETFL, flags | O_NONBLOCK) < 0) { - f2b_log_msg(log_error, "can't set non-blocking mode on socket: %s", strerror(errno)); - return -1; - } - unlink(path); - if (bind(csock, (struct sockaddr *) &addr, sizeof(struct sockaddr_un)) != 0) { + if (bind(sock, (struct sockaddr *) &addr, sizeof(struct sockaddr_un)) != 0) { f2b_log_msg(log_error, "bind() on socket failed: %s", strerror(errno)); - return -1; + return NULL; } - return csock; -} - -void -f2b_csocket_destroy(int sock, const char *path) { - assert(path != NULL); - - if (sock >= 0) - close(sock); - unlink(path); - - return; -} - -int -f2b_csocket_connect(const char *spath, const char *cpath) { - struct sockaddr_un caddr, saddr; - int csock = -1; - - assert(spath != NULL); - assert(cpath != NULL); - - memset(&saddr, 0x0, sizeof(caddr)); - memset(&caddr, 0x0, sizeof(saddr)); - - caddr.sun_family = AF_LOCAL; - strlcpy(caddr.sun_path, cpath, sizeof(caddr.sun_path)); - saddr.sun_family = AF_LOCAL; - strlcpy(saddr.sun_path, spath, sizeof(saddr.sun_path)); - - if ((csock = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) { - f2b_log_msg(log_error, "can't create control socket"); - return -1; + if (listen(sock, 5) < 0) { + f2b_log_msg(log_error, "listen() on socket failed: %s", strerror(errno)); + return NULL; } - if (bind(csock, (struct sockaddr *) &caddr, sizeof(struct sockaddr_un)) != 0) { - f2b_log_msg(log_error, "bind() to local side of socket failed: %s", strerror(errno)); - return -1; - } - - if (connect(csock, (struct sockaddr *) &saddr, sizeof(struct sockaddr_un)) != 0) { - f2b_log_msg(log_error, "connect() to socket failed: %s", strerror(errno)); - return -1; + if ((csock = calloc(1, sizeof(f2b_csock_t))) == NULL) { + f2b_log_msg(log_error, "can't allocate memory for csocket struct"); + shutdown(sock, SHUT_RDWR); + unlink(path); + return NULL; } + csock->sock = sock; + csock->path = path; return csock; } void -f2b_csocket_disconnect(int sock, const char *cpath) { - unlink(cpath); - if (sock >= 0) - close(sock); - return; -} - -void -f2b_csocket_rtimeout(int sock, float timeout) { - int ret = 0; - struct timeval tv; - tv.tv_sec = (int) timeout; - tv.tv_usec = (int) ((timeout - tv.tv_sec) * 1000000); - ret = setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (void *) &tv, sizeof(struct timeval)); - if (ret == 0) - return; - f2b_log_msg(log_warn, "can't set recv timeout for csocket: %s", strerror(errno)); -} - -const char * -f2b_csocket_error(int retcode) { - const char *err = "no error"; - switch (retcode) { - case -1 : err = strerror(errno); break; - case -2 : err = "damaged cmsg on socket: truncated"; break; - case -3 : err = "damaged cmsg on socket: no magic"; break; - case -4 : err = "damaged cmsg on socket: version mismatch"; break; - case -5 : err = "damaged cmsg on socket: unknown command type"; break; - case -6 : err = "damaged cmsg on socket: size mismatch"; break; - default : err = "unknown cmsg error"; break; +f2b_csocket_destroy(f2b_csock_t *csock) { + f2b_conn_t *conn = NULL; + assert(csock != NULL); + + if (csock->sock >= 0) + shutdown(csock->sock, SHUT_RDWR); + if (csock->path != NULL) + unlink(csock->path); + for (int i = 0; i < MAXCONNS; i++) { + if ((conn = csock->clients[i]) == NULL) + continue; + shutdown(conn->sock, SHUT_RDWR); + f2b_buf_free(&conn->recv); + f2b_buf_free(&conn->send); + free(conn); } - return err; -} - -int -f2b_csocket_recv(int csock, f2b_cmsg_t *cmsg, struct sockaddr_storage *addr, socklen_t *addrlen) { - struct msghdr msg; - uint16_t size; - int ret; - - assert(csock >= 0); - assert(cmsg != NULL); - - struct iovec iov[] = { - { .iov_len = sizeof(cmsg->magic), .iov_base = &cmsg->magic[0] }, - { .iov_len = sizeof(cmsg->version), .iov_base = &cmsg->version }, - { .iov_len = sizeof(cmsg->type), .iov_base = &cmsg->type }, - { .iov_len = sizeof(cmsg->flags), .iov_base = &cmsg->flags }, - { .iov_len = sizeof(cmsg->size), .iov_base = &size /* need ntohs */ }, - { .iov_len = sizeof(cmsg->pass), .iov_base = &cmsg->pass[0] }, - { .iov_len = sizeof(cmsg->data), .iov_base = &cmsg->data[0] }, - }; - - memset(&msg, 0x0, sizeof(msg)); - msg.msg_name = addr; - msg.msg_namelen = *addrlen; - msg.msg_iov = iov; - msg.msg_iovlen = 7; - - ret = recvmsg(csock, &msg, 0); - if (ret < 0 && errno == EAGAIN) - return 0; /* non-blocking mode & no messages */ - if (ret < 0) - return -1; /* recvmsg() error, see errno */ - if (msg.msg_flags & MSG_TRUNC) - return -2; /* truncated */ - if (memcmp(cmsg->magic, "F2B", 3) != 0) - return -3; /* no magic */ - if (cmsg->version != F2B_PROTO_VER) - return -4; /* version mismatch */ - if (cmsg->type >= CMD_MAX_NUMBER) - return -5; /* unknown command */ - cmsg->size = ntohs(size); - if (ret != (cmsg->size + 16)) - return -6; /* size mismatch */ - *addrlen = msg.msg_namelen; - - return ret; -} + free(csock); -int -f2b_csocket_send(int csock, f2b_cmsg_t *cmsg, struct sockaddr_storage *addr, socklen_t *addrlen) { - struct msghdr msg; - uint16_t size; - int ret; - - assert(csock >= 0); - assert(cmsg != NULL); - - struct iovec iov[] = { - { .iov_len = sizeof(cmsg->magic), .iov_base = &cmsg->magic[0] }, - { .iov_len = sizeof(cmsg->version), .iov_base = &cmsg->version }, - { .iov_len = sizeof(cmsg->type), .iov_base = &cmsg->type }, - { .iov_len = sizeof(cmsg->flags), .iov_base = &cmsg->flags }, - { .iov_len = sizeof(cmsg->size), .iov_base = &size /* need htons */ }, - { .iov_len = sizeof(cmsg->pass), .iov_base = &cmsg->pass[0] }, - { .iov_len = cmsg->size, .iov_base = &cmsg->data[0] }, - }; - size = htons(cmsg->size); - - memset(&msg, 0x0, sizeof(msg)); - msg.msg_name = addr; - msg.msg_namelen = *addrlen; - msg.msg_iov = iov; - msg.msg_iovlen = 7; - - if ((ret = sendmsg(csock, &msg, 0)) <= 0) - return -1; /* see errno */ - - return ret; + return; } int -f2b_csocket_poll(int csock, void (*cb)(const f2b_cmd_t *cmd, f2b_buf_t *res)) { - char res[DATA_LEN_MAX + 1]; - f2b_cmsg_t cmsg; - struct sockaddr_storage addr; - socklen_t addrlen; +f2b_csocket_poll(f2b_csock_t *csock, void (*cb)(const f2b_cmd_t *cmd, f2b_buf_t *res)) { + f2b_cmd_t *cmd = NULL; int ret, processed = 0; - assert(csock >= 0); + assert(csock != NULL); assert(cb != NULL); - while (1) { - memset(&cmsg, 0x0, sizeof(cmsg)); - memset(&addr, 0x0, sizeof(addr)); - addrlen = sizeof(addr); - ret = f2b_csocket_recv(csock, &cmsg, &addr, &addrlen); - if (ret == 0) - break; /* no messages */ - if (ret < 0) { - f2b_log_msg(log_error, "%s", f2b_csocket_error(ret)); - } - /* TODO: check auth */ - cb(&cmsg, res, sizeof(res)); - if (cmsg.flags & CMSG_FLAG_NEED_REPLY) { - memset(&cmsg, 0x0, sizeof(cmsg)); - strncpy(cmsg.magic, "F2B", sizeof(cmsg.magic)); - strlcpy(cmsg.data, res, sizeof(cmsg.data)); - cmsg.version = F2B_PROTO_VER; - cmsg.type = CMD_RESP; - cmsg.size = strlen(res); - cmsg.data[cmsg.size] = '\0'; - ret = f2b_csocket_send(csock, &cmsg, &addr, &addrlen); - } - processed++; - } - return processed; } diff --git a/src/csocket.h b/src/csocket.h index 725b594..f2559c9 100644 --- a/src/csocket.h +++ b/src/csocket.h @@ -7,56 +7,27 @@ #ifndef F2B_CSOCKET_H_ #define F2B_CSOCKET_H_ +#define MAXCONNS 5 + +typedef struct f2b_csock_t f2b_csock_t; + /** * @file * This file contains control socket manage routines */ -#include -#include -#include - /** * @brief Create UNIX socket with given path * @param path Path to socket endpoint * @returns Socket fd */ -int f2b_csocket_create (const char *path); +f2b_csock_t * f2b_csocket_create (const char *path); /** * @brief Close UNIX socket and unlink endpoint * @param csock Socket fd * @param path Path to socket endpoint */ -void f2b_csocket_destroy(int csock, const char *path); - -/** - * @brief Connect to given socket - * @param spath path to control socket endpoint - * @param cpath Path to client socket's endpoint - * @returns Connected fd or -1 on error - */ -int f2b_csocket_connect(const char *spath, const char *cpath); -/** - * @brief Close client connection and unlink client's endpoint - * @param csock Socket fd - * @param cpath Path to client socket's endpoint - */ -void f2b_csocket_disconnect(int csock, const char *cpath); - -/** - * @brief Set recieve rimeout on socket - * @param csock Socket fd - * @param timeout Timeout in seconds - */ -void f2b_csocket_rtimeout(int csock, float timeout); - -/** - * @brief Get error description for f2b_csocket_recv() - * @param retcode Return code fromf2b_csocket_recv() - * @returns Pointer to errro description - */ -const char * -f2b_csocket_error(int retcode); +void f2b_csocket_destroy(f2b_csock_t *csock); /** * @brief Poll control socket for new messages @@ -64,26 +35,6 @@ f2b_csocket_error(int retcode); * @param cb Callback for handling message * @returns -1 on error, 0 on no messages, and > 0 on some messages processed */ -int f2b_csocket_poll(int csock, void (*cb)(const f2b_cmd_t *cmd, f2b_buf_t *res)); - -/** - * @brief Pack and send control message - * @param csock Opened socket fd - * @param cmsg Control message pointer - * @param addr Pointer for destination address store - * @param addrlen Size of address storage - * @returns >0 on success - */ -int f2b_csocket_send(int csock, f2b_cmsg_t *cmsg, struct sockaddr_storage *addr, socklen_t *addrlen); - -/** - * @brief Recieve and unpack control message - * @param csock Opened socket fd - * @param cmsg Control message pointer - * @param addr Pointer for sender address store - * @param addrlen Size of address storage - * @returns >0 on success, 0 on no avalilable messages, <0 on error - */ -int f2b_csocket_recv(int csock, f2b_cmsg_t *cmsg, struct sockaddr_storage *addr, socklen_t *addrlen); +int f2b_csocket_poll(f2b_csock_t *csock, void (*cb)(const f2b_cmd_t *cmd, f2b_buf_t *res)); #endif /* F2B_CSOCKET_H_ */ diff --git a/src/daemon.c b/src/daemon.c index e8c2bbc..e11fce0 100644 --- a/src/daemon.c +++ b/src/daemon.c @@ -10,6 +10,7 @@ #include "jail.h" #include "backend.h" #include "appconfig.h" +#include "buf.h" #include "commands.h" #include "csocket.h" @@ -55,89 +56,95 @@ void usage(int exitcode) { exit(exitcode); } +static f2b_csock_t *csock = NULL; #ifndef WITH_CSOCKET /* add stubs to reduce #ifdef count */ -int f2b_csocket_create (const char *path) { +f2b_csock_t * +f2b_csocket_create (const char *path) { UNUSED(path); f2b_log_msg(log_warn, "control socket support was disabled at compile-time"); - return -1; + return NULL; } -void f2b_csocket_destroy(int csock, const char *path) { - UNUSED(csock); UNUSED(path); return; +void +f2b_csocket_destroy(f2b_csock_t *csock) { + UNUSED(csock); return; } -int f2b_csocket_poll(int csock, void (*cb)(const f2b_cmsg_t *msg, char *res, size_t ressize)) { +int f2b_csocket_poll(f2b_csock_t *csock, void (*cb)(const f2b_cmd_t *cmd, f2b_buf_t *res)) { UNUSED(csock); UNUSED(cb); return 0; } void -f2b_cmsg_process(const f2b_cmsg_t *msg, char *res, size_t ressize) { - UNUSED(msg); UNUSED(res); UNUSED(ressize); return; +f2b_csocket_cmd_process(const f2b_cmd_t *cmd, f2b_buf_t *res) { + UNUSED(cmd); UNUSED(res); return; } #else /* WITH_CSOCKET */ void -f2b_cmsg_process(const f2b_cmsg_t *msg, char *res, size_t ressize) { - const char *args[DATA_ARGS_MAX]; +f2b_csocket_cmd_process(const f2b_cmd_t *cmd, f2b_buf_t *res) { f2b_jail_t *jail = NULL; - char line[LINE_MAX]; + char buf[4096] = ""; + size_t len; - assert(msg != NULL); + assert(cmd != NULL); assert(res != NULL); - assert(msg->type < CMD_MAX_NUMBER); - if (msg->type == CMD_NONE) + if (cmd->type == CMD_UNKNOWN) return; - memset(args, 0x0, sizeof(args)); - int argc = f2b_cmsg_extract_args(msg, args); - - if (f2b_cmd_check_argc(msg->type, argc) == false) { - strlcpy(res, "cmd args number mismatch", ressize); - return; - } - - if (msg->type >= CMD_JAIL_STATUS && msg->type <= CMD_MAX_NUMBER) { - if ((jail = f2b_jail_find(jails, args[0])) == NULL) { - snprintf(res, ressize, "can't find jail '%s'", args[0]); + if (cmd->type >= CMD_JAIL_STATUS && cmd->type <= CMD_JAIL_FILTER_RELOAD) { + if ((jail = f2b_jail_find(jails, cmd->args[1])) == NULL) { + len = snprintf(buf, sizeof(buf), "can't find jail '%s'\n", cmd->args[1]); + f2b_buf_append(res, buf, len); return; } } - strlcpy(res, "ok", ressize); /* default reply */ - if (msg->type == CMD_PING) { - /* nothing to do */ - } else if (msg->type == CMD_RELOAD) { + if (cmd->type == CMD_RELOAD) { state = reconfig; - } else if (msg->type == CMD_LOG_ROTATE) { + } else if (cmd->type == CMD_LOG_ROTATE) { state = logrotate; - } else if (msg->type == CMD_LOG_LEVEL) { - f2b_log_set_level(args[0]); - } else if (msg->type == CMD_SHUTDOWN) { + } else if (cmd->type == CMD_LOG_LEVEL) { + f2b_log_set_level(cmd->args[2]); + } else if (cmd->type == CMD_SHUTDOWN) { state = stop; - } else if (msg->type == CMD_STATUS) { - snprintf(line, sizeof(line), "pid: %u\npidfile: %s\ncsocket: %s\nstatedir: %s\njails:\n", - getpid(), appconfig.pidfile_path, appconfig.csocket_path, appconfig.statedir_path); - strlcpy(res, line, ressize); + } else if (cmd->type == CMD_STATUS) { + len = snprintf(buf, sizeof(buf), "pid: %u\npidfile: %s\n", getpid(), appconfig.pidfile_path); + f2b_buf_append(res, buf, len); + len = snprintf(buf, sizeof(buf), "csocket: %s\n", appconfig.csocket_path); + f2b_buf_append(res, buf, len); + len = snprintf(buf, sizeof(buf), "statedir: %s\n", appconfig.statedir_path); + f2b_buf_append(res, buf, len); + f2b_buf_append(res, "jails:\n", 0); for (jail = jails; jail != NULL; jail = jail->next) { - snprintf(line, sizeof(line), "- %s\n", jail->name); - strlcat(res, line, ressize); + len = snprintf(buf, sizeof(buf), "- %s\n", jail->name); + f2b_buf_append(res, buf, len); } - } else if (msg->type == CMD_JAIL_STATUS) { - f2b_jail_cmd_status(res, ressize, jail); - } else if (msg->type == CMD_JAIL_SET) { - f2b_jail_cmd_set(res, ressize, jail, args[1], args[2]); - } else if (msg->type == CMD_JAIL_IP_STATUS) { - f2b_jail_cmd_ip_xxx(res, ressize, jail, 0, args[1]); - } else if (msg->type == CMD_JAIL_IP_BAN) { - f2b_jail_cmd_ip_xxx(res, ressize, jail, 1, args[1]); - } else if (msg->type == CMD_JAIL_IP_RELEASE) { - f2b_jail_cmd_ip_xxx(res, ressize, jail, -1, args[1]); - } else if (msg->type == CMD_JAIL_FILTER_STATS) { - f2b_filter_cmd_stats(res, ressize, jail->filter); - } else if (msg->type == CMD_JAIL_FILTER_RELOAD) { - f2b_filter_cmd_reload(res, ressize, jail->filter); + } else if (cmd->type == CMD_JAIL_STATUS) { + f2b_jail_cmd_status(buf, sizeof(buf), jail); + f2b_buf_append(res, buf, 0); + } else if (cmd->type == CMD_JAIL_SET) { + f2b_jail_cmd_set(buf, sizeof(buf), jail, cmd->args[3], cmd->args[4]); + f2b_buf_append(res, buf, 0); + } else if (cmd->type == CMD_JAIL_IP_STATUS) { + f2b_jail_cmd_ip_xxx(buf, sizeof(buf), jail, 0, cmd->args[4]); + f2b_buf_append(res, buf, 0); + } else if (cmd->type == CMD_JAIL_IP_BAN) { + f2b_jail_cmd_ip_xxx(buf, sizeof(buf), jail, 1, cmd->args[4]); + f2b_buf_append(res, buf, 0); + } else if (cmd->type == CMD_JAIL_IP_RELEASE) { + f2b_jail_cmd_ip_xxx(buf, sizeof(buf), jail, -1, cmd->args[4]); + f2b_buf_append(res, buf, 0); + } else if (cmd->type == CMD_JAIL_FILTER_STATS) { + f2b_filter_cmd_stats(buf, sizeof(buf), jail->filter); + f2b_buf_append(res, buf, 0); + } else if (cmd->type == CMD_JAIL_FILTER_RELOAD) { + f2b_filter_cmd_reload(buf, sizeof(buf), jail->filter); + f2b_buf_append(res, buf, 0); } else { - strlcpy(res, "error: unsupported command type", ressize); + f2b_buf_append(res, "error: unknown command\n", 0); } + if (res->used == 0) + f2b_buf_append(res, "ok\n", 3); /* default reply if not set above */ + return; } #endif /* WITH_CSOCKET */ @@ -288,8 +295,9 @@ int main(int argc, char *argv[]) { } } - if (appconfig.csocket_path[0] != '\0') - appconfig.csock = f2b_csocket_create(appconfig.csocket_path); + if (appconfig.csocket_path[0] != '\0') { + csock = f2b_csocket_create(appconfig.csocket_path); + } if (config.defaults) f2b_jail_set_defaults(config.defaults); @@ -306,7 +314,7 @@ int main(int argc, char *argv[]) { for (f2b_jail_t *jail = jails; jail != NULL; jail = jail->next) { f2b_jail_process(jail); } - f2b_csocket_poll(appconfig.csock, f2b_cmsg_process); + f2b_csocket_poll(csock, f2b_csocket_cmd_process); sleep(1); if (state == logrotate && strcmp(appconfig.logdest, "file") == 0) { state = run; @@ -328,7 +336,7 @@ int main(int argc, char *argv[]) { } } - f2b_csocket_destroy(appconfig.csock, appconfig.csocket_path); + f2b_csocket_destroy(csock); jails_stop(jails); jails = NULL; From d44aee855f7b4a6838ff9149c2fb84b2e2b720ca Mon Sep 17 00:00:00 2001 From: Alex 'AdUser' Z Date: Mon, 18 Jan 2021 12:38:46 +1000 Subject: [PATCH 10/20] * f2b_cmd_{create,destroy}() --- src/commands.c | 26 ++++++++++++++++++++++++++ src/commands.h | 14 ++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/src/commands.c b/src/commands.c index 765b70f..f42add1 100644 --- a/src/commands.c +++ b/src/commands.c @@ -116,6 +116,32 @@ f2b_cmd_help() { return help; } +f2b_cmd_t * +f2b_cmd_create(const char *line) { + f2b_cmd_t *cmd = NULL; + + assert(line != NULL); + + if ((cmd = calloc(1, sizeof(f2b_cmd_t))) == NULL) + return NULL; + + if (f2b_buf_alloc(&cmd->data, strlen(line))) { + if (f2b_cmd_parse(cmd, line)) + return cmd; + free(cmd); + cmd = NULL; + } + + + return cmd; +} + +void +f2b_cmd_destroy(f2b_cmd_t *cmd) { + f2b_buf_free(&cmd->data); + free(cmd); +} + bool f2b_cmd_parse(f2b_cmd_t *cmd, const char *src) { char *p = NULL; diff --git a/src/commands.h b/src/commands.h index 194ea39..2327ca1 100644 --- a/src/commands.h +++ b/src/commands.h @@ -51,6 +51,20 @@ typedef struct f2b_cmd_t { const char * f2b_cmd_help(); +/** + * @brief Creates new struct from user input + * @param line User input string + * @returns pointer to newly allocated struct or NULL on malloc()/parse error + */ +f2b_cmd_t * +f2b_cmd_create(const char *line); + +/** + * @brief Frees memory + */ +void +f2b_cmd_destroy(f2b_cmd_t *cmd); + /** * @brief Try to parse user input * @param cmd pointer to preallocated f2b_cmd_t struct From 8233e28b07ca1f102afb48e601de64c7409c05df Mon Sep 17 00:00:00 2001 From: Alex 'AdUser' Z Date: Mon, 18 Jan 2021 12:38:51 +1000 Subject: [PATCH 11/20] * csocket.c : big refactoring --- src/csocket.c | 155 +++++++++++++++++++++++++++++++++++++++++++++++--- src/csocket.h | 2 +- 2 files changed, 149 insertions(+), 8 deletions(-) diff --git a/src/csocket.c b/src/csocket.c index c484b5f..e88ee5d 100644 --- a/src/csocket.c +++ b/src/csocket.c @@ -13,6 +13,7 @@ #include #include #include +#include typedef struct f2b_conn_t { int sock; @@ -28,6 +29,39 @@ struct f2b_csock_t { bool shutdown; }; +/* helpers */ + +static inline int +max(int a, int b) { + return a > b ? a : b; +} + +/* connection-related functions */ + +f2b_conn_t * +f2b_conn_create(size_t rbuf, size_t wbuf) { + f2b_conn_t *conn = NULL; + if ((conn = calloc(1, sizeof(f2b_conn_t))) != NULL) { + if (f2b_buf_alloc(&conn->recv, rbuf)) { + if (f2b_buf_alloc(&conn->send, wbuf)) { + return conn; + } + f2b_buf_free(&conn->recv); + } + free(conn); + } + return NULL; +} + +void +f2b_conn_destroy(f2b_conn_t *conn) { + f2b_buf_free(&conn->recv); + f2b_buf_free(&conn->send); + free(conn); +} + +/* control socket-related functions */ + f2b_csock_t * f2b_csocket_create(const char *path) { f2b_csock_t *csock; @@ -80,23 +114,130 @@ f2b_csocket_destroy(f2b_csock_t *csock) { for (int i = 0; i < MAXCONNS; i++) { if ((conn = csock->clients[i]) == NULL) continue; - shutdown(conn->sock, SHUT_RDWR); - f2b_buf_free(&conn->recv); - f2b_buf_free(&conn->send); - free(conn); + f2b_conn_destroy(conn); + csock->clients[i] = NULL; } free(csock); return; } -int +void f2b_csocket_poll(f2b_csock_t *csock, void (*cb)(const f2b_cmd_t *cmd, f2b_buf_t *res)) { + struct timeval tv = { .tv_sec = 0, .tv_usec = 0 }; + fd_set rfds, wfds; + f2b_conn_t *conn = NULL; f2b_cmd_t *cmd = NULL; - int ret, processed = 0; + int retval, nfds; assert(csock != NULL); assert(cb != NULL); - return processed; + /* loop / init */ + FD_ZERO(&rfds); + FD_ZERO(&wfds); + + FD_SET(csock->sock, &rfds); /* watch for new connections */ + + /* watch for new data on established connections */ + for (int cnum = 0; cnum < MAXCONNS; cnum++) { + if ((conn = csock->clients[cnum]) == NULL) + continue; + if (!csock->shutdown) + FD_SET(conn->sock, &rfds); + if (conn->send.used) + FD_SET(conn->sock, &wfds); + nfds = max(csock->sock, conn->sock); + } + + /* check for new data */ + retval = select(nfds + 1, &rfds, &wfds, NULL, &tv); + if (retval < 0) { + if (errno == EINTR) { + /* interrupted by signal */ + } else if (errno == EAGAIN) { + /* no data */ + } else { + f2b_log_msg(log_error, "select() error: %s", strerror(errno)); + } + return; + } + if (retval == 0) + return; /* no new data */ + + /* new connection on listening socket? */ + if (FD_ISSET(csock->sock, &rfds)) { + /* find free connection slot */ + int cnum = 0; + for (int cnum = 0; cnum < MAXCONNS; cnum++) { + if (csock->clients[cnum] == NULL) break; + } + int sock = -1; + /* accept() new connection */ + if ((sock = accept(csock->sock, NULL, NULL)) < 0) { + perror("accept()"); + } else if (cnum < MAXCONNS) { + if ((conn = f2b_conn_create(RBUF_SIZE, WBUF_SIZE)) != NULL) { + f2b_log_msg(log_debug, "new connection accept()ed, socket %d, conn %d\n", sock, cnum); + conn->sock = sock; + csock->clients[cnum] = conn; + } else { + f2b_log_msg(log_error, "can;t create new connection"); + } + } else { + f2b_log_msg(log_error, "max number of clients reached, drop connection on socket %d\n", sock); + shutdown(sock, SHUT_RDWR); + } + } + + for (int cnum = 0; cnum < MAXCONNS; cnum++) { + if ((conn = csock->clients[cnum]) == NULL) + continue; + /* handle incoming data */ + if (FD_ISSET(conn->sock, &rfds)) { + f2b_log_msg(log_debug, "some incoming data on socket %d", conn->sock); + char tmp[RBUF_SIZE] = ""; + char *line = NULL; + ssize_t read = 0; + read = recv(conn->sock, tmp, RBUF_SIZE, MSG_DONTWAIT); + if (read > 0) { + tmp[read] = '\0'; + f2b_log_msg(log_debug, "conn %d received %zd bytes, and recv buf is now %zd bytes\n", conn->sock, read, conn->recv.used); + f2b_buf_append(&conn->recv, tmp, read); + /* TODO: properly handle empty lines */ + while (*conn->recv.data == '\n') { + /* TODO f2b_buf_splice(conn->send, retval); */ + break; + } + /* extract message(s) */ + while ((line = f2b_buf_extract(&conn->recv, "\n")) != NULL) { + if ((cmd = f2b_cmd_create(line)) != NULL) { + cb(cmd, &conn->send); /* handle command */ + f2b_cmd_destroy(cmd); + } else { + f2b_buf_append(&conn->send, "can't parse input\n", 0); + } + free(line); + } + } else if (read == 0) { + f2b_log_msg(log_debug, "received connection close on socket %d", conn->sock); + shutdown(conn->sock, SHUT_RDWR); + f2b_conn_destroy(conn); + csock->clients[cnum] = NULL; + } else { + perror("recv()"); + } + } + /* handle outgoing data */ + if (conn->send.used > 0) { + f2b_log_msg(log_debug, "sending %zu bytes to socket %d\n", conn->send.used, conn->sock); + retval = send(conn->sock, conn->send.data, conn->send.used, MSG_DONTWAIT); + if (retval > 0) { + /* TODO f2b_buf_splice(conn->send, retval); */ + } else if (retval < 0) { + f2b_log_msg(log_error, "can't send %zu bytes to socket %d", conn->send.used, conn->sock); + } + } + } /* foreach connection(s) */ + return; } diff --git a/src/csocket.h b/src/csocket.h index f2559c9..498a698 100644 --- a/src/csocket.h +++ b/src/csocket.h @@ -35,6 +35,6 @@ void f2b_csocket_destroy(f2b_csock_t *csock); * @param cb Callback for handling message * @returns -1 on error, 0 on no messages, and > 0 on some messages processed */ -int f2b_csocket_poll(f2b_csock_t *csock, void (*cb)(const f2b_cmd_t *cmd, f2b_buf_t *res)); +void f2b_csocket_poll(f2b_csock_t *csock, void (*cb)(const f2b_cmd_t *cmd, f2b_buf_t *res)); #endif /* F2B_CSOCKET_H_ */ From b7b624f133056333190aec06e632b52142112bf4 Mon Sep 17 00:00:00 2001 From: Alex 'AdUser' Z Date: Mon, 18 Jan 2021 12:38:55 +1000 Subject: [PATCH 12/20] + csocket-test --- src/CMakeLists.txt | 3 +++ src/csocket-test.c | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 src/csocket-test.c diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4d80b81..25f8d57 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -30,6 +30,9 @@ add_executable("f2b-filter-test" ${SOURCES}) set(SOURCES "strlcpy.c" "backend-test.c" "log.c" "config.c" "backend.c") add_executable("f2b-backend-test" ${SOURCES}) +set(SOURCES "strlcpy.c" "csocket-test.c" "log.c" "buf.c" "commands.c" "csocket.c") +add_executable("f2b-csocket-test" ${SOURCES}) + if (${CMAKE_SYSTEM_NAME} MATCHES "Linux") target_link_libraries("f2b" "dl") target_link_libraries("f2b-source-test" "dl") diff --git a/src/csocket-test.c b/src/csocket-test.c new file mode 100644 index 0000000..933ae53 --- /dev/null +++ b/src/csocket-test.c @@ -0,0 +1,33 @@ +#include "common.h" +#include "buf.h" +#include "log.h" +#include "commands.h" +#include "csocket.h" + +static int run = 1; + +void +cmd_handler(const f2b_cmd_t *cmd, f2b_buf_t *res) { + fprintf(stdout, "[handler] received cmd with type %d and %d args:\n", cmd->type, cmd->argc); + for (int i = 0; i < cmd->argc; i++) { + fprintf(stdout, "[handler] arg %d : %s\n", i + 1, cmd->args[i]); + } + UNUSED(res); + return; +} + +int main(void) { + f2b_csock_t *csock = NULL; + + if ((csock = f2b_csocket_create(DEFAULT_CSOCKET_PATH)) == NULL) { + perror("f2b_csocket_create()"); + } + + while (run) { + f2b_csocket_poll(csock, cmd_handler); + /* TODO: sleep 0.1s */ + } + f2b_csocket_destroy(csock); + + return 0; +} From 9cff1842886aee87599fed30cd9b30bfb2c0bd8c Mon Sep 17 00:00:00 2001 From: Alex 'AdUser' Z Date: Fri, 15 Jan 2021 20:22:02 +1000 Subject: [PATCH 13/20] + f2b_buf_splice() --- src/buf.c | 21 +++++++++++++++++---- src/buf.h | 1 + t/t_buf.c | 20 ++++++++++++++++++++ 3 files changed, 38 insertions(+), 4 deletions(-) diff --git a/src/buf.c b/src/buf.c index ed08886..08cc01f 100644 --- a/src/buf.c +++ b/src/buf.c @@ -40,7 +40,7 @@ f2b_buf_append(f2b_buf_t *buf, const char *str, size_t len) { } /** - * @brief Extracts line terminated by '\r', '\n' + * @brief Extracts line terminated by delimiter * @return Pointer to extracted string on success or NULL otherwise * @note Use only with 'read' buffer type */ @@ -65,10 +65,23 @@ f2b_buf_extract(f2b_buf_t *buf, const char *end) { /* shift data inside buffer */ len += strlen(end); - assert(buf->used >= len); + f2b_buf_splice(buf, len); + + return s; +} + +size_t +f2b_buf_splice(f2b_buf_t *buf, size_t len) { + assert(buf != NULL); + + if (len == 0) + return len; + + if (buf->used <= len) + len = buf->used; + buf->used -= len, memmove(buf->data, &buf->data[len], buf->used); buf->data[buf->used] = '\0'; - - return s; + return len; } diff --git a/src/buf.h b/src/buf.h index 31d5472..0779112 100644 --- a/src/buf.h +++ b/src/buf.h @@ -11,5 +11,6 @@ bool f2b_buf_alloc(f2b_buf_t *buf, size_t max); void f2b_buf_free(f2b_buf_t *buf); bool f2b_buf_append(f2b_buf_t *buf, const char *str, size_t size); char * f2b_buf_extract(f2b_buf_t *buf, const char *end); +size_t f2b_buf_splice(f2b_buf_t *buf, size_t len); #endif /* F2B_BUF_H_ */ diff --git a/t/t_buf.c b/t/t_buf.c index 129b487..5055941 100644 --- a/t/t_buf.c +++ b/t/t_buf.c @@ -5,6 +5,7 @@ int main() { f2b_buf_t buf; char *line; bool ret; + int len; memset(&buf, 0x0, sizeof(buf)); @@ -39,6 +40,25 @@ int main() { assert(buf.used == 0); free(line); + f2b_buf_append(&buf, "test4\n\n", 6); + assert(buf.used == 6); + assert(strcmp(buf.data, "test4\n") == 0); + + len = f2b_buf_splice(&buf, 0); + assert(len == 0); + assert(buf.used == 6); + assert(strcmp(buf.data, "test4\n") == 0); + + len = f2b_buf_splice(&buf, 2); + assert(len == 2); + assert(buf.used == 4); + assert(strcmp(buf.data, "st4\n") == 0); + + len = f2b_buf_splice(&buf, 6); + assert(len == 4); + assert(buf.used == 0); + assert(buf.data[0] == '\0'); + f2b_buf_free(&buf); assert(buf.used == 0); assert(buf.size == 0); From 39fe0fcce21565cb31b33fbc9f1c8e608def99ab Mon Sep 17 00:00:00 2001 From: Alex 'AdUser' Z Date: Fri, 15 Jan 2021 20:31:12 +1000 Subject: [PATCH 14/20] + extract f2b_conn_process() as separate function --- src/csocket.c | 127 +++++++++++++++++++++++++++++--------------------- 1 file changed, 73 insertions(+), 54 deletions(-) diff --git a/src/csocket.c b/src/csocket.c index e88ee5d..fca14cc 100644 --- a/src/csocket.c +++ b/src/csocket.c @@ -16,17 +16,15 @@ #include typedef struct f2b_conn_t { - int sock; - const char *path; f2b_buf_t recv; f2b_buf_t send; + int sock; } f2b_conn_t; struct f2b_csock_t { f2b_conn_t *clients[MAXCONNS]; const char *path; int sock; - bool shutdown; }; /* helpers */ @@ -60,6 +58,67 @@ f2b_conn_destroy(f2b_conn_t *conn) { free(conn); } +int +f2b_conn_process(f2b_conn_t *conn, bool in, void (*cb)(const f2b_cmd_t *cmd, f2b_buf_t *res)) { + f2b_cmd_t *cmd = NULL; + int retval; + + /* handle incoming data */ + if (in) { + f2b_log_msg(log_debug, "some incoming data on socket %d", conn->sock); + char tmp[RBUF_SIZE] = ""; + char *line = NULL; + ssize_t read = 0; + read = recv(conn->sock, tmp, RBUF_SIZE, MSG_DONTWAIT); + if (read == 0) { + f2b_log_msg(log_debug, "received connection close on socket %d", conn->sock); + return -1; + } + if (read < 0) { + f2b_log_msg(log_error, "received error on sock %d: %s", conn->sock, strerror(errno)); + return -1; + } + if (read > 0) { + tmp[read] = '\0'; + f2b_buf_append(&conn->recv, tmp, read); + f2b_log_msg(log_debug, "received %zd bytes from socket %d", read, conn->sock); + /* TODO: properly handle empty lines */ + while (conn->recv.data[0] == '\n') { + f2b_buf_splice(&conn->recv, 1); + break; + } + /* extract message(s) */ + while ((line = f2b_buf_extract(&conn->recv, "\n")) != NULL) { + f2b_log_msg(log_debug, "extracted line: %s", line); + if ((cmd = f2b_cmd_create(line)) != NULL) { + cb(cmd, &conn->send); /* handle command */ + f2b_cmd_destroy(cmd); + } else { + f2b_buf_append(&conn->send, "can't parse input\n", 0); + } + free(line); + } + if (conn->recv.used >= conn->recv.size) { + f2b_log_msg(log_error, "drop connection on socket %d, recv buffer overflow", conn->sock); + return -1; + } + } + } + /* handle outgoing data */ + if (conn->send.used > 0) { + f2b_log_msg(log_debug, "sending %zu bytes to socket %d", conn->send.used, conn->sock); + retval = send(conn->sock, conn->send.data, conn->send.used, MSG_DONTWAIT); + if (retval > 0) { + f2b_buf_splice(&conn->send, retval); + f2b_log_msg(log_debug, "sent %d bytes to socket %d (%zu remains)", retval, conn->sock, conn->send.used); + } else if (retval < 0 && errno != EAGAIN) { + f2b_log_msg(log_error, "can't send() to socket %d: %s", conn->sock, strerror(errno)); + return -1; /* remote side closed connection */ + } + } + return 0; +} + /* control socket-related functions */ f2b_csock_t * @@ -127,7 +186,6 @@ f2b_csocket_poll(f2b_csock_t *csock, void (*cb)(const f2b_cmd_t *cmd, f2b_buf_t struct timeval tv = { .tv_sec = 0, .tv_usec = 0 }; fd_set rfds, wfds; f2b_conn_t *conn = NULL; - f2b_cmd_t *cmd = NULL; int retval, nfds; assert(csock != NULL); @@ -140,11 +198,11 @@ f2b_csocket_poll(f2b_csock_t *csock, void (*cb)(const f2b_cmd_t *cmd, f2b_buf_t FD_SET(csock->sock, &rfds); /* watch for new connections */ /* watch for new data on established connections */ + nfds = csock->sock; for (int cnum = 0; cnum < MAXCONNS; cnum++) { if ((conn = csock->clients[cnum]) == NULL) continue; - if (!csock->shutdown) - FD_SET(conn->sock, &rfds); + FD_SET(conn->sock, &rfds); if (conn->send.used) FD_SET(conn->sock, &wfds); nfds = max(csock->sock, conn->sock); @@ -175,17 +233,17 @@ f2b_csocket_poll(f2b_csock_t *csock, void (*cb)(const f2b_cmd_t *cmd, f2b_buf_t int sock = -1; /* accept() new connection */ if ((sock = accept(csock->sock, NULL, NULL)) < 0) { - perror("accept()"); + f2b_log_msg(log_error, "can't accept() new connection: %s", strerror(errno)); } else if (cnum < MAXCONNS) { if ((conn = f2b_conn_create(RBUF_SIZE, WBUF_SIZE)) != NULL) { - f2b_log_msg(log_debug, "new connection accept()ed, socket %d, conn %d\n", sock, cnum); + f2b_log_msg(log_debug, "new connection accept()ed, socket %d", sock); conn->sock = sock; csock->clients[cnum] = conn; } else { - f2b_log_msg(log_error, "can;t create new connection"); + f2b_log_msg(log_error, "can't create new connection"); } } else { - f2b_log_msg(log_error, "max number of clients reached, drop connection on socket %d\n", sock); + f2b_log_msg(log_error, "max number of clients reached, drop connection on socket %d", sock); shutdown(sock, SHUT_RDWR); } } @@ -193,50 +251,11 @@ f2b_csocket_poll(f2b_csock_t *csock, void (*cb)(const f2b_cmd_t *cmd, f2b_buf_t for (int cnum = 0; cnum < MAXCONNS; cnum++) { if ((conn = csock->clients[cnum]) == NULL) continue; - /* handle incoming data */ - if (FD_ISSET(conn->sock, &rfds)) { - f2b_log_msg(log_debug, "some incoming data on socket %d", conn->sock); - char tmp[RBUF_SIZE] = ""; - char *line = NULL; - ssize_t read = 0; - read = recv(conn->sock, tmp, RBUF_SIZE, MSG_DONTWAIT); - if (read > 0) { - tmp[read] = '\0'; - f2b_log_msg(log_debug, "conn %d received %zd bytes, and recv buf is now %zd bytes\n", conn->sock, read, conn->recv.used); - f2b_buf_append(&conn->recv, tmp, read); - /* TODO: properly handle empty lines */ - while (*conn->recv.data == '\n') { - /* TODO f2b_buf_splice(conn->send, retval); */ - break; - } - /* extract message(s) */ - while ((line = f2b_buf_extract(&conn->recv, "\n")) != NULL) { - if ((cmd = f2b_cmd_create(line)) != NULL) { - cb(cmd, &conn->send); /* handle command */ - f2b_cmd_destroy(cmd); - } else { - f2b_buf_append(&conn->send, "can't parse input\n", 0); - } - free(line); - } - } else if (read == 0) { - f2b_log_msg(log_debug, "received connection close on socket %d", conn->sock); - shutdown(conn->sock, SHUT_RDWR); - f2b_conn_destroy(conn); - csock->clients[cnum] = NULL; - } else { - perror("recv()"); - } - } - /* handle outgoing data */ - if (conn->send.used > 0) { - f2b_log_msg(log_debug, "sending %zu bytes to socket %d\n", conn->send.used, conn->sock); - retval = send(conn->sock, conn->send.data, conn->send.used, MSG_DONTWAIT); - if (retval > 0) { - /* TODO f2b_buf_splice(conn->send, retval); */ - } else if (retval < 0) { - f2b_log_msg(log_error, "can't send %zu bytes to socket %d", conn->send.used, conn->sock); - } + retval = f2b_conn_process(conn, FD_ISSET(conn->sock, &rfds), cb); + if (retval < 0) { + shutdown(conn->sock, SHUT_RDWR); + f2b_conn_destroy(conn); + csock->clients[cnum] = NULL; } } /* foreach connection(s) */ return; From 0e3737e47f3a749bbc6e2f02805a88524ba70a68 Mon Sep 17 00:00:00 2001 From: Alex 'AdUser' Z Date: Fri, 15 Jan 2021 21:45:43 +1000 Subject: [PATCH 15/20] * csocket-test : fixes --- src/csocket-test.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/csocket-test.c b/src/csocket-test.c index 933ae53..15dfaa8 100644 --- a/src/csocket-test.c +++ b/src/csocket-test.c @@ -16,16 +16,25 @@ cmd_handler(const f2b_cmd_t *cmd, f2b_buf_t *res) { return; } -int main(void) { +int main(int argc, const char **argv) { f2b_csock_t *csock = NULL; - if ((csock = f2b_csocket_create(DEFAULT_CSOCKET_PATH)) == NULL) { + if (argc < 2) { + puts("Usage: csocket-test "); + exit(EXIT_FAILURE); + } + + f2b_log_set_level("debug"); + f2b_log_to_stderr(); + + if ((csock = f2b_csocket_create(argv[1])) == NULL) { perror("f2b_csocket_create()"); + exit(EXIT_FAILURE); } while (run) { f2b_csocket_poll(csock, cmd_handler); - /* TODO: sleep 0.1s */ + sleep(1); } f2b_csocket_destroy(csock); From 0ece77ef78773e1e3ecd02e261293354400339ee Mon Sep 17 00:00:00 2001 From: Alex 'AdUser' Z Date: Fri, 15 Jan 2021 21:46:29 +1000 Subject: [PATCH 16/20] * f2b_conn_process() : fix handling empty lines && connection reset --- src/csocket.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/csocket.c b/src/csocket.c index fca14cc..fa94e75 100644 --- a/src/csocket.c +++ b/src/csocket.c @@ -70,7 +70,7 @@ f2b_conn_process(f2b_conn_t *conn, bool in, void (*cb)(const f2b_cmd_t *cmd, f2b char *line = NULL; ssize_t read = 0; read = recv(conn->sock, tmp, RBUF_SIZE, MSG_DONTWAIT); - if (read == 0) { + if (read == 0 || (read < 0 && errno == ECONNRESET)) { f2b_log_msg(log_debug, "received connection close on socket %d", conn->sock); return -1; } @@ -82,13 +82,12 @@ f2b_conn_process(f2b_conn_t *conn, bool in, void (*cb)(const f2b_cmd_t *cmd, f2b tmp[read] = '\0'; f2b_buf_append(&conn->recv, tmp, read); f2b_log_msg(log_debug, "received %zd bytes from socket %d", read, conn->sock); - /* TODO: properly handle empty lines */ - while (conn->recv.data[0] == '\n') { - f2b_buf_splice(&conn->recv, 1); - break; - } /* extract message(s) */ while ((line = f2b_buf_extract(&conn->recv, "\n")) != NULL) { + if (strlen(line) == 0) { + free(line); + continue; + } f2b_log_msg(log_debug, "extracted line: %s", line); if ((cmd = f2b_cmd_create(line)) != NULL) { cb(cmd, &conn->send); /* handle command */ From 9ae9a974b8a45471ce8c1d183a2d4e6e8a7efe6b Mon Sep 17 00:00:00 2001 From: Alex 'AdUser' Z Date: Fri, 15 Jan 2021 21:48:43 +1000 Subject: [PATCH 17/20] * f2bc : fix build without libreadline & socket poll with them --- src/client.c | 63 ++++++++++++++++++++++------------------------------ 1 file changed, 27 insertions(+), 36 deletions(-) diff --git a/src/client.c b/src/client.c index acd73ee..fc148f9 100644 --- a/src/client.c +++ b/src/client.c @@ -29,6 +29,29 @@ void usage(int exitcode) { exit(exitcode); } +int +handle_recv() { + char buf[WBUF_SIZE] = ""; /* our "read" is server "write" */ + int ret; + + if (csock < 0) + return 0; /* not connected */ + + ret = recv(csock, &buf, sizeof(buf), MSG_DONTWAIT); + if (ret > 0) { + write(fileno(stdout), buf, ret); + } else if (ret == 0) { + puts("connection closed"); + exit(EXIT_SUCCESS); /* received EOF */ + } else if (ret < 0 && errno == EAGAIN) { + return 0; + } else /* ret < 0 */ { + perror("recv()"); + exit(EXIT_FAILURE); + } + return 0; +} + int handle_cmd(const char *line) { const char *p = NULL; @@ -42,7 +65,6 @@ handle_cmd(const char *line) { while (p && *p != '\0') { len = strlen(p); ret = send(csock, p, strlen(p), 0); - /* blocks only for a second, see timeouts */ if (ret < 0 && errno == EAGAIN) { continue; /* try again */ } else if (ret < 0) { @@ -55,23 +77,6 @@ handle_cmd(const char *line) { } } - while (1) { - ret = recv(csock, &buf, sizeof(buf), 0); - /* blocks only for a second, see timeouts */ - if (ret > 0) { - write(fileno(stdout), buf, ret); - continue; - } else if (ret == 0) { - puts("connection closed"); - exit(EXIT_SUCCESS); /* received EOF */ - } else if (ret < 0 && errno == EAGAIN) { - break; - } else /* ret < 0 */ { - perror("recv()"); - exit(EXIT_FAILURE); - } - } - return 0; } @@ -101,25 +106,12 @@ setup_sigaction(int signum) { void setup_socket() { struct sockaddr_un saddr; - struct timeval tv; if ((csock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { perror("socket()"); exit(EXIT_FAILURE); } - tv.tv_sec = 1, tv.tv_usec = 0; - if (setsockopt(csock, SOL_SOCKET, SO_RCVTIMEO, (void *) &tv, sizeof(struct timeval)) < 0) { - perror("setsockopt() - recv"); - exit(EXIT_FAILURE); - } - - tv.tv_sec = 1, tv.tv_usec = 0; - if (setsockopt(csock, SOL_SOCKET, SO_SNDTIMEO, (void *) &tv, sizeof(struct timeval)) < 0) { - perror("setsockopt() - send"); - exit(EXIT_FAILURE); - } - memset(&saddr, 0x0, sizeof(saddr)); saddr.sun_family = AF_UNIX; strlcpy(saddr.sun_path, opts.spath, sizeof(saddr.sun_path) - 1); @@ -132,22 +124,21 @@ setup_socket() { #ifdef WITH_READLINE #include #include + rl_event_hook = &handle_recv; #else char * readline(const char *prompt) { - char line[INPUT_LINE_MAX]; + char line[RBUF_SIZE+1]; char *p; while (1) { line[0] = '\0'; p = &line[0]; + handle_recv(); fputs(prompt, stdout); if (!fgets(line, sizeof(line) - 1, stdin)) { - if (feof(stdin)) { - fputc('\n', stdout); - } else { + if (!feof(stdin)) fputs("read error\n", stdout); - } return NULL; } while (isspace(*p)) p++; From 739ea48520fe75404ba2254d9e2b2b0cea17fb7b Mon Sep 17 00:00:00 2001 From: Alex 'AdUser' Z Date: Fri, 15 Jan 2021 21:48:43 +1000 Subject: [PATCH 18/20] * f2bc : fix build without libreadline & socket poll with them --- src/client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client.c b/src/client.c index fc148f9..4ecaf69 100644 --- a/src/client.c +++ b/src/client.c @@ -124,7 +124,7 @@ setup_socket() { #ifdef WITH_READLINE #include #include - rl_event_hook = &handle_recv; + rl_hook_func_t *rl_event_hook = &handle_recv; #else char * readline(const char *prompt) { From 555c781237468a4227918b6e5d3ee14e8192fae4 Mon Sep 17 00:00:00 2001 From: Alex 'AdUser' Z Date: Mon, 18 Jan 2021 14:25:31 +1000 Subject: [PATCH 19/20] * f2b_buf_append() : return size of actually appended data instead true/false --- src/buf.c | 11 ++++++----- src/buf.h | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/buf.c b/src/buf.c index 08cc01f..2d25eae 100644 --- a/src/buf.c +++ b/src/buf.c @@ -22,21 +22,22 @@ f2b_buf_free(f2b_buf_t *buf) { memset(buf, 0x0, sizeof(f2b_buf_t)); } -bool +size_t f2b_buf_append(f2b_buf_t *buf, const char *str, size_t len) { - assert(buf != NULL); assert(str != NULL); if (len == 0) len = strlen(str); - if (buf->size < (buf->used + len)) - return false; /* not enough space */ + if ((buf->used + len) > buf->size) { + /* not enough space, append as much as possible */ + len = buf->size - buf->used; + } memcpy(&buf->data[buf->used], str, len); buf->used += len; buf->data[buf->used] = '\0'; - return true; + return len; } /** diff --git a/src/buf.h b/src/buf.h index 0779112..8745c57 100644 --- a/src/buf.h +++ b/src/buf.h @@ -9,7 +9,7 @@ typedef struct f2b_buf_t { bool f2b_buf_alloc(f2b_buf_t *buf, size_t max); void f2b_buf_free(f2b_buf_t *buf); -bool f2b_buf_append(f2b_buf_t *buf, const char *str, size_t size); +size_t f2b_buf_append(f2b_buf_t *buf, const char *str, size_t size); char * f2b_buf_extract(f2b_buf_t *buf, const char *end); size_t f2b_buf_splice(f2b_buf_t *buf, size_t len); From 19529fe3a574e124586c52ab042c3ed934f807e3 Mon Sep 17 00:00:00 2001 From: Alex 'AdUser' Z Date: Mon, 18 Jan 2021 14:41:54 +1000 Subject: [PATCH 20/20] * security tunes for csocket --- src/csocket.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/csocket.c b/src/csocket.c index fa94e75..8851d19 100644 --- a/src/csocket.c +++ b/src/csocket.c @@ -65,11 +65,11 @@ f2b_conn_process(f2b_conn_t *conn, bool in, void (*cb)(const f2b_cmd_t *cmd, f2b /* handle incoming data */ if (in) { - f2b_log_msg(log_debug, "some incoming data on socket %d", conn->sock); char tmp[RBUF_SIZE] = ""; char *line = NULL; ssize_t read = 0; - read = recv(conn->sock, tmp, RBUF_SIZE, MSG_DONTWAIT); + size_t avail = conn->recv.size - conn->recv.used; + read = recv(conn->sock, tmp, avail, MSG_DONTWAIT); if (read == 0 || (read < 0 && errno == ECONNRESET)) { f2b_log_msg(log_debug, "received connection close on socket %d", conn->sock); return -1; @@ -80,8 +80,14 @@ f2b_conn_process(f2b_conn_t *conn, bool in, void (*cb)(const f2b_cmd_t *cmd, f2b } if (read > 0) { tmp[read] = '\0'; - f2b_buf_append(&conn->recv, tmp, read); - f2b_log_msg(log_debug, "received %zd bytes from socket %d", read, conn->sock); + for (const char *p = tmp; *p != '\0'; p++) { + if (isgraph(*p) || isspace(*p)) + continue; + f2b_log_msg(log_error, "non-printable character in data on sock %d", conn->sock); + return -1; + } + retval = f2b_buf_append(&conn->recv, tmp, read); + f2b_log_msg(log_debug, "received %zd bytes from socket %d, append %d to buf", read, conn->sock, retval); /* extract message(s) */ while ((line = f2b_buf_extract(&conn->recv, "\n")) != NULL) { if (strlen(line) == 0) { @@ -106,7 +112,7 @@ f2b_conn_process(f2b_conn_t *conn, bool in, void (*cb)(const f2b_cmd_t *cmd, f2b /* handle outgoing data */ if (conn->send.used > 0) { f2b_log_msg(log_debug, "sending %zu bytes to socket %d", conn->send.used, conn->sock); - retval = send(conn->sock, conn->send.data, conn->send.used, MSG_DONTWAIT); + retval = send(conn->sock, conn->send.data, conn->send.used, MSG_DONTWAIT | MSG_NOSIGNAL); if (retval > 0) { f2b_buf_splice(&conn->send, retval); f2b_log_msg(log_debug, "sent %d bytes to socket %d (%zu remains)", retval, conn->sock, conn->send.used); @@ -183,6 +189,8 @@ f2b_csocket_destroy(f2b_csock_t *csock) { void f2b_csocket_poll(f2b_csock_t *csock, void (*cb)(const f2b_cmd_t *cmd, f2b_buf_t *res)) { struct timeval tv = { .tv_sec = 0, .tv_usec = 0 }; + struct ucred peer; + socklen_t peerlen = 0; fd_set rfds, wfds; f2b_conn_t *conn = NULL; int retval, nfds; @@ -234,8 +242,11 @@ f2b_csocket_poll(f2b_csock_t *csock, void (*cb)(const f2b_cmd_t *cmd, f2b_buf_t if ((sock = accept(csock->sock, NULL, NULL)) < 0) { f2b_log_msg(log_error, "can't accept() new connection: %s", strerror(errno)); } else if (cnum < MAXCONNS) { + peerlen = sizeof(peer); + if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &peer, &peerlen) < 0) + f2b_log_msg(log_error, "can't get remote peer credentials: %s", strerror(errno)); if ((conn = f2b_conn_create(RBUF_SIZE, WBUF_SIZE)) != NULL) { - f2b_log_msg(log_debug, "new connection accept()ed, socket %d", sock); + f2b_log_msg(log_debug, "new connection accept()ed, socket %d from uid %d", sock, peer.uid); conn->sock = sock; csock->clients[cnum] = conn; } else { @@ -252,6 +263,7 @@ f2b_csocket_poll(f2b_csock_t *csock, void (*cb)(const f2b_cmd_t *cmd, f2b_buf_t continue; retval = f2b_conn_process(conn, FD_ISSET(conn->sock, &rfds), cb); if (retval < 0) { + f2b_log_msg(log_debug, "closing connection on socket %d", conn->sock); shutdown(conn->sock, SHUT_RDWR); f2b_conn_destroy(conn); csock->clients[cnum] = NULL;