diff --git a/src/server/server.c b/src/server/server.c index 904d80a..e3a0a35 100644 --- a/src/server/server.c +++ b/src/server/server.c @@ -226,11 +226,6 @@ static void release_authorization(void) { close(root_fd); } -static bool path_is_within(const char* root, const char* path) { - size_t n = strlen(root); - return strncmp(root, path, n) == 0 && (path[n] == '\0' || path[n] == '/'); -} - /* --mkpath contract: when the client's destination root directory does not exist yet on the server side, --mkpath tells the server to create it (and any missing leading components) below the authorized root at connection @@ -790,7 +785,7 @@ void handler(int file_descriptor) { if (joined_destination) destination = joined_destination; if (!destination || has_path_traversal(destination) || - !path_is_within(authorized_root, destination)) { + !path_is_within_root(authorized_root, destination)) { log_message(LOG_LEVEL_ERROR, "Rejected destination outside authorized root"); free(joined_destination); joined_destination = NULL; diff --git a/src/server/server_cli.c b/src/server/server_cli.c index de505ab..c49fa01 100644 --- a/src/server/server_cli.c +++ b/src/server/server_cli.c @@ -3,20 +3,12 @@ #include "credentials.h" #include "utils.h" #include -#include #include #include #include #include -static void set_error(char* err, size_t err_size, const char* fmt, ...) { - if (!err || err_size == 0) - return; - va_list args; - va_start(args, fmt); - vsnprintf(err, err_size, fmt, args); - va_end(args); -} +#define set_error utils_set_error void server_cli_options_default(ServerCliOptions* opts) { if (!opts) diff --git a/src/shared/credentials.c b/src/shared/credentials.c index 998bae9..e241271 100644 --- a/src/shared/credentials.c +++ b/src/shared/credentials.c @@ -8,7 +8,6 @@ #include #include #include -#include #include #include #include @@ -58,14 +57,7 @@ struct CredentialStore { static const uint8_t k_dummy_stored_key[CREDENTIAL_KEY_LEN] = {0}; static const uint8_t k_dummy_server_key[CREDENTIAL_KEY_LEN] = {0}; -static void set_error(char* err, size_t err_size, const char* fmt, ...) { - if (!err || err_size == 0) - return; - va_list args; - va_start(args, fmt); - vsnprintf(err, err_size, fmt, args); - va_end(args); -} +#define set_error utils_set_error static bool is_comment_char(char c) { return c == '#' || c == ';'; diff --git a/src/shared/daemon_conf.c b/src/shared/daemon_conf.c index 88b836f..2297a48 100644 --- a/src/shared/daemon_conf.c +++ b/src/shared/daemon_conf.c @@ -6,7 +6,6 @@ #include #include #include -#include #include #include #include @@ -17,14 +16,7 @@ /* helpers */ /* ------------------------------------------------------------------ */ -static void set_error(char* err, size_t err_size, const char* fmt, ...) { - if (!err || err_size == 0) - return; - va_list args; - va_start(args, fmt); - vsnprintf(err, err_size, fmt, args); - va_end(args); -} +#define set_error utils_set_error /* Trim leading and trailing ASCII space/tab in place; returns the new start. */ static char* trim_ws(char* s) { diff --git a/src/shared/filter.c b/src/shared/filter.c index f478286..4bfd462 100644 --- a/src/shared/filter.c +++ b/src/shared/filter.c @@ -4,22 +4,12 @@ #include #include #include -#include #include #include #include -/* Write a diagnostic message into the caller's optional buffer. A NULL `err` - * (or a zero size) is a no-op, so a caller that only needs the boolean status - * may pass NULL without the snprintf-on-NULL undefined behaviour. */ -static void filter_set_error(char* err, size_t err_size, const char* fmt, ...) { - if (!err || err_size == 0) - return; - va_list ap; - va_start(ap, fmt); - vsnprintf(err, err_size, fmt, ap); - va_end(ap); -} +/* Write a diagnostic message into the caller's optional buffer. */ +#define filter_set_error utils_set_error /* ---- Ordered rule lists ---- */ @@ -870,8 +860,3 @@ FilterAction filter_rules_apply_side(const FilterRuleList* list, const char* rel } return FILTER_ACTION_NONE; } - -FilterAction filter_rules_apply(const FilterRuleList* list, const char* rel_path, const char* leaf, - bool is_dir) { - return filter_rules_apply_side(list, rel_path, leaf, is_dir, FILTER_SIDE_SENDER); -} diff --git a/src/shared/filter.h b/src/shared/filter.h index bd9877b..bc9e9ca 100644 --- a/src/shared/filter.h +++ b/src/shared/filter.h @@ -131,9 +131,4 @@ FilterRuleList* filter_file_read(const char* dir_path, const char* owner_rel, bo FilterAction filter_rules_apply_side(const FilterRuleList* list, const char* rel_path, const char* leaf, bool is_dir, unsigned side); -/* Sender-side convenience wrapper (kept for callers/tests that only need the - * transfer decision). */ -FilterAction filter_rules_apply(const FilterRuleList* list, const char* rel_path, const char* leaf, - bool is_dir); - #endif diff --git a/src/shared/utils.c b/src/shared/utils.c index d028e36..947a4d9 100644 --- a/src/shared/utils.c +++ b/src/shared/utils.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -48,6 +49,15 @@ const char* utils_get_authorized_root_path(void) { return authorized_root_path; } +void utils_set_error(char* err, size_t err_size, const char* fmt, ...) { + if (!err || err_size == 0) + return; + va_list args; + va_start(args, fmt); + vsnprintf(err, err_size, fmt, args); + va_end(args); +} + bool path_is_within_root(const char* root, const char* path) { size_t root_len = strlen(root); return strncmp(root, path, root_len) == 0 && (path[root_len] == '\0' || path[root_len] == '/'); diff --git a/src/shared/utils.h b/src/shared/utils.h index 7bf89d8..aedb9ce 100644 --- a/src/shared/utils.h +++ b/src/shared/utils.h @@ -225,6 +225,11 @@ void utils_set_authorized_root_fd(int fd); * threads spawn; see utils.c). */ int utils_get_authorized_root_fd(void); const char* utils_get_authorized_root_path(void); +/* Write a diagnostic message into a caller-supplied buffer, mirroring + * vsnprintf. A NULL `err` or a zero `err_size` is a no-op, so a caller that + * only needs the boolean status may safely pass NULL. Returns nothing; the + * buffer is always NUL-terminated by vsnprintf when err_size > 0. */ +void utils_set_error(char* err, size_t err_size, const char* fmt, ...); /* True when `path` is `root` itself or lies directly beneath it: a lexical * prefix test requiring the byte after `root` to be '\0' or '/'. Both `root` * and `path` must be absolute canonical paths free of "."/".." components (the diff --git a/tests/test_filter.c b/tests/test_filter.c index 76e2729..4036cbd 100644 --- a/tests/test_filter.c +++ b/tests/test_filter.c @@ -179,8 +179,10 @@ static void test_filter_rules_apply_supported_modifiers() { const char* texts[] = {"- *.tmp"}; FilterRuleList* list = filter_base_build(texts, 1, false, false, NULL, 0); EXPECT_NOT_NULL(list); - EXPECT_EQ_INT(filter_rules_apply(list, "b.tmp", "b.tmp", false), FILTER_ACTION_EXCLUDE); - EXPECT_EQ_INT(filter_rules_apply(list, "a.txt", "a.txt", false), FILTER_ACTION_NONE); + EXPECT_EQ_INT(filter_rules_apply_side(list, "b.tmp", "b.tmp", false, FILTER_SIDE_SENDER), + FILTER_ACTION_EXCLUDE); + EXPECT_EQ_INT(filter_rules_apply_side(list, "a.txt", "a.txt", false, FILTER_SIDE_SENDER), + FILTER_ACTION_NONE); filter_rule_list_free(list); } /* anchored include then exclude-all */ @@ -188,8 +190,10 @@ static void test_filter_rules_apply_supported_modifiers() { const char* texts[] = {"+ /a.txt", "- *"}; FilterRuleList* list = filter_base_build(texts, 2, false, false, NULL, 0); EXPECT_NOT_NULL(list); - EXPECT_EQ_INT(filter_rules_apply(list, "a.txt", "a.txt", false), FILTER_ACTION_INCLUDE); - EXPECT_EQ_INT(filter_rules_apply(list, "b.txt", "b.txt", false), FILTER_ACTION_EXCLUDE); + EXPECT_EQ_INT(filter_rules_apply_side(list, "a.txt", "a.txt", false, FILTER_SIDE_SENDER), + FILTER_ACTION_INCLUDE); + EXPECT_EQ_INT(filter_rules_apply_side(list, "b.txt", "b.txt", false, FILTER_SIDE_SENDER), + FILTER_ACTION_EXCLUDE); filter_rule_list_free(list); } /* negate */ @@ -197,8 +201,10 @@ static void test_filter_rules_apply_supported_modifiers() { const char* texts[] = {"-! *.o"}; FilterRuleList* list = filter_base_build(texts, 1, false, false, NULL, 0); EXPECT_NOT_NULL(list); - EXPECT_EQ_INT(filter_rules_apply(list, "foo.c", "foo.c", false), FILTER_ACTION_EXCLUDE); - EXPECT_EQ_INT(filter_rules_apply(list, "foo.o", "foo.o", false), FILTER_ACTION_NONE); + EXPECT_EQ_INT(filter_rules_apply_side(list, "foo.c", "foo.c", false, FILTER_SIDE_SENDER), + FILTER_ACTION_EXCLUDE); + EXPECT_EQ_INT(filter_rules_apply_side(list, "foo.o", "foo.o", false, FILTER_SIDE_SENDER), + FILTER_ACTION_NONE); filter_rule_list_free(list); } /* dir-only trailing slash */ @@ -206,8 +212,10 @@ static void test_filter_rules_apply_supported_modifiers() { const char* texts[] = {"+ dir/", "- *"}; FilterRuleList* list = filter_base_build(texts, 2, false, false, NULL, 0); EXPECT_NOT_NULL(list); - EXPECT_EQ_INT(filter_rules_apply(list, "dir", "dir", true), FILTER_ACTION_INCLUDE); - EXPECT_EQ_INT(filter_rules_apply(list, "dir", "dir", false), FILTER_ACTION_EXCLUDE); + EXPECT_EQ_INT(filter_rules_apply_side(list, "dir", "dir", true, FILTER_SIDE_SENDER), + FILTER_ACTION_INCLUDE); + EXPECT_EQ_INT(filter_rules_apply_side(list, "dir", "dir", false, FILTER_SIDE_SENDER), + FILTER_ACTION_EXCLUDE); filter_rule_list_free(list); } }