Read and parse configuration files. Fixes #41. (#42)

Read and parse configuration files. Fixes #41.

 * supports nDPId / nDPIsrvd via command line parameter `-f`
 * nDPId: read general/tuning and libnDPI settings
 * support for settings risk domains libnDPI option via config file or via `-R` (Fixes #45, thanks to @UnveilTech)
 * added some documentation in the config file
 * adjusted Systemd and Debian packaging to make use of config files

Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
This commit is contained in:
Toni
2024-10-16 14:13:55 +02:00
committed by GitHub
parent 3e2ce661f0
commit efed6f196e
18 changed files with 1500 additions and 639 deletions

View File

@@ -22,6 +22,11 @@ find_package(PkgConfig REQUIRED)
set(CPACK_PACKAGE_CONTACT "toni@impl.cc")
set(CPACK_DEBIAN_PACKAGE_NAME "nDPId")
set(CPACK_DEBIAN_PACKAGE_SECTION "network")
set(CPACK_DEBIAN_PACKAGE_DESCRIPTION "nDPId is a set of daemons and tools to capture, process and classify network traffic.")
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Toni Uhlig")
set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${CMAKE_SOURCE_DIR}/packages/debian/preinst;${CMAKE_SOURCE_DIR}/packages/debian/prerm;${CMAKE_SOURCE_DIR}/packages/debian/postrm")
set(CPACK_DEBIAN_PACKAGE_CONTROL_STRICT_PERMISSION TRUE)
set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
set(CPACK_DEBIAN_DEBUGINFO_PACKAGE ON)
set(CPACK_STRIP_FILES ON)
@@ -516,7 +521,9 @@ if(ENABLE_SYSTEMD)
configure_file(packages/systemd/ndpisrvd.service.in ndpisrvd.service @ONLY)
configure_file(packages/systemd/ndpid@.service.in ndpid@.service @ONLY)
install(FILES packages/systemd/default.cfg DESTINATION etc/default RENAME ndpid)
install(DIRECTORY DESTINATION etc/nDPId)
install(FILES "ndpid.conf.example" DESTINATION share/nDPId)
install(FILES "ndpisrvd.conf.example" DESTINATION share/nDPId)
install(FILES "${CMAKE_BINARY_DIR}/ndpisrvd.service" DESTINATION lib/systemd/system)
install(FILES "${CMAKE_BINARY_DIR}/ndpid@.service" DESTINATION lib/systemd/system)
endif()

View File

@@ -33,8 +33,8 @@
#define nDPIsrvd_JSON_KEY_STRLEN (32)
#define nDPIsrvd_HASHKEY_SEED (0x995fd871u)
#define nDPIsrvd_ARRAY_LENGTH(s) (sizeof(s) / sizeof(s[0]))
#define nDPIsrvd_STRLEN_SZ(s) (sizeof(s) / sizeof(s[0]) - sizeof(s[0]))
#define nDPIsrvd_ARRAY_LENGTH(s) ((size_t)(sizeof(s) / sizeof(s[0])))
#define nDPIsrvd_STRLEN_SZ(s) ((size_t)((sizeof(s) / sizeof(s[0])) - sizeof(s[0])))
#define TOKEN_GET_SZ(sock, ...) nDPIsrvd_get_token(sock, __VA_ARGS__, NULL)
#define TOKEN_VALUE_EQUALS(sock, token, string_to_check, string_to_check_length) \
nDPIsrvd_token_value_equals(sock, token, string_to_check, string_to_check_length)

View File

@@ -2059,7 +2059,7 @@ int main(int argc, char ** argv)
}
errno = 0;
if (user != NULL && change_user_group(user, group, pidfile, NULL, NULL) != 0)
if (user != NULL && change_user_group(user, group, pidfile) != 0)
{
if (errno != 0)
{

View File

@@ -24,7 +24,7 @@
#include "utarray.h"
#include "utils.h"
//#define VERBOSE
// #define VERBOSE
#define DEFAULT_DATADIR "/tmp/nDPId-captured"
struct packet_data
@@ -1355,8 +1355,14 @@ int main(int argc, char ** argv)
return 1;
}
if (capture_mode != 0 && chmod_chown(datadir, S_IRWXU | S_IRGRP | S_IXGRP, user, group) != 0)
{
logger(1, "Could not chmod/chown `%s': %s", datadir, strerror(errno));
return 1;
}
errno = 0;
if (user != NULL && change_user_group(user, group, pidfile, datadir /* :D */, NULL) != 0)
if (user != NULL && change_user_group(user, group, pidfile) != 0)
{
if (errno != 0)
{
@@ -1368,10 +1374,6 @@ int main(int argc, char ** argv)
}
return 1;
}
if (datadir != NULL)
{
chmod(datadir, S_IRWXU);
}
if (nDPIsrvd_set_read_timeout(ndpisrvd_socket, 180, 0) != 0)
{

View File

@@ -1671,7 +1671,7 @@ int main(int argc, char ** argv)
}
errno = 0;
if (user != NULL && change_user_group(user, group, pidfile, NULL, NULL) != 0)
if (user != NULL && change_user_group(user, group, pidfile) != 0)
{
if (errno != 0)
{

View File

@@ -7,7 +7,7 @@ extern void nDPIsrvd_memprof_log(char const * const format, ...);
extern void nDPIsrvd_memprof_log_alloc(size_t alloc_size);
extern void nDPIsrvd_memprof_log_free(size_t free_size);
//#define VERBOSE_MEMORY_PROFILING 1
// #define VERBOSE_MEMORY_PROFILING 1
#define NO_MAIN 1
#include "utils.c"
#include "nio.c"
@@ -1315,7 +1315,7 @@ static void * nDPId_mainloop_thread(void * const arg)
}
run_capture_loop(&reader_threads[0]);
process_remaining_flows();
for (size_t i = 0; i < nDPId_options.reader_thread_count; ++i)
for (size_t i = 0; i < GET_CMDARG_ULL(nDPId_options.reader_thread_count); ++i)
{
nrv->packets_captured += reader_threads[i].workflow->packets_captured;
nrv->packets_processed += reader_threads[i].workflow->packets_processed;
@@ -1667,27 +1667,30 @@ int main(int argc, char ** argv)
return retval;
}
nDPIsrvd_options.max_write_buffers = 32;
nDPId_options.enable_data_analysis = 1;
nDPId_options.max_packets_per_flow_to_send = 5;
set_cmdarg_ull(&nDPIsrvd_options.max_write_buffers, 32);
set_cmdarg_boolean(&nDPId_options.enable_data_analysis, 1);
set_cmdarg_ull(&nDPId_options.max_packets_per_flow_to_send, 5);
#ifdef ENABLE_ZLIB
/*
* zLib compression is forced enabled for testing.
* Remember to compile nDPId with zlib enabled.
* There will be diff's while running `test/run_tests.sh' otherwise.
*/
nDPId_options.enable_zlib_compression = 1;
set_cmdarg_boolean(&nDPId_options.enable_zlib_compression, 1);
#endif
nDPId_options.memory_profiling_log_interval = (unsigned long long int)-1;
nDPId_options.reader_thread_count = 1; /* Please do not change this! Generating meaningful pcap diff's relies on a
single reader thread! */
set_cmdarg(&nDPId_options.instance_alias, "nDPId-test");
set_cmdarg_ull(&nDPId_options.memory_profiling_log_interval, (unsigned long long int)-1);
set_cmdarg_ull(&nDPId_options.reader_thread_count, 1); /* Please do not change this! Generating meaningful pcap
diff's relies on a single reader thread! */
set_cmdarg_string(&nDPId_options.instance_alias, "nDPId-test");
if (access(argv[1], R_OK) != 0)
{
logger(1, "%s: pcap file `%s' does not exist or is not readable", argv[0], argv[1]);
return 1;
}
set_cmdarg(&nDPId_options.pcap_file_or_interface, argv[1]);
set_cmdarg_string(&nDPId_options.pcap_file_or_interface, argv[1]);
set_config_defaults(&config_map[0], nDPIsrvd_ARRAY_LENGTH(config_map));
set_config_defaults(&general_config_map[0], nDPIsrvd_ARRAY_LENGTH(general_config_map));
set_config_defaults(&tuning_config_map[0], nDPIsrvd_ARRAY_LENGTH(tuning_config_map));
if (validate_options() != 0)
{
return 1;
@@ -1845,10 +1848,12 @@ int main(int argc, char ** argv)
"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n",
total_alloc_bytes -
sizeof(struct nDPId_workflow) *
nDPId_options.reader_thread_count /* We do not want to take the workflow into account. */,
GET_CMDARG_ULL(
nDPId_options.reader_thread_count) /* We do not want to take the workflow into account. */,
total_free_bytes -
sizeof(struct nDPId_workflow) *
nDPId_options.reader_thread_count /* We do not want to take the workflow into account. */,
GET_CMDARG_ULL(
nDPId_options.reader_thread_count) /* We do not want to take the workflow into account. */,
total_alloc_count,
total_free_count);

1085
nDPId.c

File diff suppressed because it is too large Load Diff

View File

@@ -90,27 +90,54 @@ static struct nDPIsrvd_address distributor_in_address = {
static struct
{
struct cmdarg config_file;
struct cmdarg pidfile;
struct cmdarg collector_un_sockpath;
struct cmdarg distributor_un_sockpath;
struct cmdarg distributor_in_address;
struct cmdarg user;
struct cmdarg group;
nDPIsrvd_ull max_remote_descriptors;
nDPIsrvd_ull max_write_buffers;
uint8_t bufferbloat_fallback_to_blocking;
struct cmdarg collector_group;
struct cmdarg distributor_group;
struct cmdarg max_remote_descriptors;
struct cmdarg max_write_buffers;
struct cmdarg bufferbloat_fallback_to_blocking;
#ifdef ENABLE_EPOLL
uint8_t use_poll;
struct cmdarg use_poll;
#endif
} nDPIsrvd_options = {.pidfile = CMDARG(nDPIsrvd_PIDFILE),
.collector_un_sockpath = CMDARG(COLLECTOR_UNIX_SOCKET),
.distributor_un_sockpath = CMDARG(DISTRIBUTOR_UNIX_SOCKET),
.distributor_in_address = CMDARG(NULL),
.user = CMDARG(DEFAULT_CHUSER),
.group = CMDARG(NULL),
.max_remote_descriptors = nDPIsrvd_MAX_REMOTE_DESCRIPTORS,
.max_write_buffers = nDPIsrvd_MAX_WRITE_BUFFERS,
.bufferbloat_fallback_to_blocking = 1};
} nDPIsrvd_options = {.config_file = CMDARG_STR(NULL),
.pidfile = CMDARG_STR(nDPIsrvd_PIDFILE),
.collector_un_sockpath = CMDARG_STR(COLLECTOR_UNIX_SOCKET),
.distributor_un_sockpath = CMDARG_STR(DISTRIBUTOR_UNIX_SOCKET),
.distributor_in_address = CMDARG_STR(NULL),
.user = CMDARG_STR(DEFAULT_CHUSER),
.group = CMDARG_STR(NULL),
.collector_group = CMDARG_STR(NULL),
.distributor_group = CMDARG_STR(NULL),
.max_remote_descriptors = CMDARG_ULL(nDPIsrvd_MAX_REMOTE_DESCRIPTORS),
.max_write_buffers = CMDARG_ULL(nDPIsrvd_MAX_WRITE_BUFFERS),
.bufferbloat_fallback_to_blocking = CMDARG_BOOL(1)
#ifdef ENABLE_EPOLL
,
.use_poll = CMDARG_BOOL(0)
#endif
};
struct confopt config_map[] = {CONFOPT("pidfile", &nDPIsrvd_options.pidfile),
CONFOPT("collector", &nDPIsrvd_options.collector_un_sockpath),
CONFOPT("distributor-unix", &nDPIsrvd_options.distributor_un_sockpath),
CONFOPT("distributor-in", &nDPIsrvd_options.distributor_in_address),
CONFOPT("user", &nDPIsrvd_options.user),
CONFOPT("group", &nDPIsrvd_options.group),
CONFOPT("collector-group", &nDPIsrvd_options.collector_group),
CONFOPT("distributor-group", &nDPIsrvd_options.distributor_group),
CONFOPT("max-remote-descriptors", &nDPIsrvd_options.max_remote_descriptors),
CONFOPT("max-write-buffers", &nDPIsrvd_options.max_write_buffers),
CONFOPT("blocking-io-fallback", &nDPIsrvd_options.bufferbloat_fallback_to_blocking)
#ifdef ENABLE_EPOLL
,
CONFOPT("poll", &nDPIsrvd_options.use_poll)
#endif
};
static void logger_nDPIsrvd(struct remote_desc const * const remote,
char const * const prefix,
@@ -239,9 +266,9 @@ static int add_to_additional_write_buffers(struct remote_desc * const remote,
return -1;
}
if (utarray_len(additional_write_buffers) >= nDPIsrvd_options.max_write_buffers)
if (utarray_len(additional_write_buffers) >= GET_CMDARG_ULL(nDPIsrvd_options.max_write_buffers))
{
if (nDPIsrvd_options.bufferbloat_fallback_to_blocking == 0)
if (GET_CMDARG_BOOL(nDPIsrvd_options.bufferbloat_fallback_to_blocking) == 0)
{
logger_nDPIsrvd(remote,
"Buffer limit for",
@@ -498,7 +525,7 @@ static int create_listen_sockets(void)
return 1;
}
if (is_cmdarg_set(&nDPIsrvd_options.distributor_in_address) != 0)
if (IS_CMDARG_SET(nDPIsrvd_options.distributor_in_address) != 0)
{
distributor_in_sockfd = socket(distributor_in_address.raw.sa_family, SOCK_STREAM, 0);
if (distributor_in_sockfd < 0 || set_fd_cloexec(distributor_in_sockfd) < 0)
@@ -528,7 +555,7 @@ static int create_listen_sockets(void)
int written = snprintf(collector_addr.sun_path,
sizeof(collector_addr.sun_path),
"%s",
get_cmdarg(&nDPIsrvd_options.collector_un_sockpath));
GET_CMDARG_STR(nDPIsrvd_options.collector_un_sockpath));
if (written < 0)
{
logger(1, "snprintf failed: %s", strerror(errno));
@@ -536,10 +563,7 @@ static int create_listen_sockets(void)
}
else if (written == sizeof(collector_addr.sun_path))
{
logger(1,
"Collector UNIX socket path too long, current/max: %zu/%zu",
strlen(get_cmdarg(&nDPIsrvd_options.collector_un_sockpath)),
sizeof(collector_addr.sun_path) - 1);
logger(1, "Collector UNIX socket path too long, max: %zu characters", sizeof(collector_addr.sun_path) - 1);
return 1;
}
@@ -547,7 +571,7 @@ static int create_listen_sockets(void)
{
logger(1,
"Error binding Collector UNIX socket to `%s': %s",
get_cmdarg(&nDPIsrvd_options.collector_un_sockpath),
GET_CMDARG_STR(nDPIsrvd_options.collector_un_sockpath),
strerror(errno));
return 1;
}
@@ -559,7 +583,7 @@ static int create_listen_sockets(void)
int written = snprintf(distributor_addr.sun_path,
sizeof(distributor_addr.sun_path),
"%s",
get_cmdarg(&nDPIsrvd_options.distributor_un_sockpath));
GET_CMDARG_STR(nDPIsrvd_options.distributor_un_sockpath));
if (written < 0)
{
logger(1, "snprintf failed: %s", strerror(errno));
@@ -568,8 +592,7 @@ static int create_listen_sockets(void)
else if (written == sizeof(distributor_addr.sun_path))
{
logger(1,
"Distributor UNIX socket path too long, current/max: %zu/%zu",
strlen(get_cmdarg(&nDPIsrvd_options.distributor_un_sockpath)),
"Distributor UNIX socket path too long, max: %zu characters",
sizeof(distributor_addr.sun_path) - 1);
return 2;
}
@@ -578,19 +601,19 @@ static int create_listen_sockets(void)
{
logger(1,
"Error binding Distributor socket to `%s': %s",
get_cmdarg(&nDPIsrvd_options.distributor_un_sockpath),
GET_CMDARG_STR(nDPIsrvd_options.distributor_un_sockpath),
strerror(errno));
return 2;
}
}
if (is_cmdarg_set(&nDPIsrvd_options.distributor_in_address) != 0)
if (IS_CMDARG_SET(nDPIsrvd_options.distributor_in_address) != 0)
{
if (bind(distributor_in_sockfd, &distributor_in_address.raw, distributor_in_address.size) < 0)
{
logger(1,
"Error binding Distributor TCP/IP socket to %s: %s",
get_cmdarg(&nDPIsrvd_options.distributor_in_address),
GET_CMDARG_STR(nDPIsrvd_options.distributor_in_address),
strerror(errno));
return 3;
}
@@ -598,7 +621,7 @@ static int create_listen_sockets(void)
{
logger(1,
"Error listening Distributor TCP/IP socket to %s: %s",
get_cmdarg(&nDPIsrvd_options.distributor_in_address),
GET_CMDARG_STR(nDPIsrvd_options.distributor_in_address),
strerror(errno));
return 3;
}
@@ -606,7 +629,7 @@ static int create_listen_sockets(void)
{
logger(1,
"Error setting Distributor TCP/IP socket %s to non-blocking mode: %s",
get_cmdarg(&nDPIsrvd_options.distributor_in_address),
GET_CMDARG_STR(nDPIsrvd_options.distributor_in_address),
strerror(errno));
return 3;
}
@@ -622,7 +645,7 @@ static int create_listen_sockets(void)
{
logger(1,
"Error setting Collector UNIX socket `%s' to non-blocking mode: %s",
get_cmdarg(&nDPIsrvd_options.collector_un_sockpath),
GET_CMDARG_STR(nDPIsrvd_options.collector_un_sockpath),
strerror(errno));
return 3;
}
@@ -631,7 +654,7 @@ static int create_listen_sockets(void)
{
logger(1,
"Error setting Distributor UNIX socket `%s' to non-blocking mode: %s",
get_cmdarg(&nDPIsrvd_options.distributor_un_sockpath),
GET_CMDARG_STR(nDPIsrvd_options.distributor_un_sockpath),
strerror(errno));
return 3;
}
@@ -804,10 +827,13 @@ static int nDPIsrvd_parse_options(int argc, char ** argv)
{
int opt;
while ((opt = getopt(argc, argv, "lL:c:dp:s:S:m:u:g:C:Dvh")) != -1)
while ((opt = getopt(argc, argv, "f:lL:c:dp:s:S:G:m:u:g:C:Dvh")) != -1)
{
switch (opt)
{
case 'f':
set_cmdarg_string(&nDPIsrvd_options.config_file, optarg);
break;
case 'l':
enable_console_logger();
break;
@@ -818,11 +844,11 @@ static int nDPIsrvd_parse_options(int argc, char ** argv)
}
break;
case 'c':
set_cmdarg(&nDPIsrvd_options.collector_un_sockpath, optarg);
set_cmdarg_string(&nDPIsrvd_options.collector_un_sockpath, optarg);
break;
case 'e':
#ifdef ENABLE_EPOLL
nDPIsrvd_options.use_poll = 1;
set_cmdarg_boolean(&nDPIsrvd_options.use_poll, 1);
#else
logger_early(1, "%s", "nDPIsrvd was built w/o epoll() support, poll() is already the default");
#endif
@@ -831,36 +857,67 @@ static int nDPIsrvd_parse_options(int argc, char ** argv)
daemonize_enable();
break;
case 'p':
set_cmdarg(&nDPIsrvd_options.pidfile, optarg);
set_cmdarg_string(&nDPIsrvd_options.pidfile, optarg);
break;
case 's':
set_cmdarg(&nDPIsrvd_options.distributor_un_sockpath, optarg);
set_cmdarg_string(&nDPIsrvd_options.distributor_un_sockpath, optarg);
break;
case 'S':
set_cmdarg(&nDPIsrvd_options.distributor_in_address, optarg);
set_cmdarg_string(&nDPIsrvd_options.distributor_in_address, optarg);
break;
case 'G':
{
char const * const sep = strchr(optarg, ':');
char group[256];
if (sep == NULL)
{
fprintf(stderr, "%s: Argument for `-G' is not in the format group:group\n", argv[0]);
return 1;
}
if (snprintf(group, sizeof(group), "%.*s", (int)(sep - optarg), optarg) > 0)
{
set_cmdarg_string(&nDPIsrvd_options.collector_group, group);
}
if (snprintf(group, sizeof(group), "%s", sep + 1) > 0)
{
set_cmdarg_string(&nDPIsrvd_options.distributor_group, group);
}
break;
}
case 'm':
if (str_value_to_ull(optarg, &nDPIsrvd_options.max_remote_descriptors) != CONVERSION_OK)
{
nDPIsrvd_ull tmp;
if (str_value_to_ull(optarg, &tmp) != CONVERSION_OK)
{
fprintf(stderr, "%s: Argument for `-C' is not a number: %s\n", argv[0], optarg);
return 1;
}
set_cmdarg_ull(&nDPIsrvd_options.max_remote_descriptors, tmp);
break;
}
case 'u':
set_cmdarg(&nDPIsrvd_options.user, optarg);
set_cmdarg_string(&nDPIsrvd_options.user, optarg);
break;
case 'g':
set_cmdarg(&nDPIsrvd_options.group, optarg);
set_cmdarg_string(&nDPIsrvd_options.group, optarg);
break;
case 'C':
if (str_value_to_ull(optarg, &nDPIsrvd_options.max_write_buffers) != CONVERSION_OK)
{
nDPIsrvd_ull tmp;
if (str_value_to_ull(optarg, &tmp) != CONVERSION_OK)
{
fprintf(stderr, "%s: Argument for `-C' is not a number: %s\n", argv[0], optarg);
return 1;
}
set_cmdarg_ull(&nDPIsrvd_options.max_write_buffers, tmp);
break;
}
case 'D':
nDPIsrvd_options.bufferbloat_fallback_to_blocking = 0;
set_cmdarg_boolean(&nDPIsrvd_options.bufferbloat_fallback_to_blocking, 0);
break;
case 'v':
fprintf(stderr, "%s", get_nDPId_version());
@@ -869,11 +926,14 @@ static int nDPIsrvd_parse_options(int argc, char ** argv)
default:
fprintf(stderr, "%s\n", get_nDPId_version());
fprintf(stderr,
"Usage: %s [-l] [-L logfile] [-c path-to-unix-sock] [-e] [-d] [-p pidfile]\n"
"Usage: %s [-f config-file] [-l] [-L logfile]\n"
"\t[-c path-to-unix-sock] [-e] [-d] [-p pidfile]\n"
"\t[-s path-to-distributor-unix-socket] [-S distributor-host:port]\n"
"\t[-G collector-unix-socket-group:distributor-unix-socket-group]\n"
"\t[-m max-remote-descriptors] [-u user] [-g group]\n"
"\t[-C max-buffered-json-lines] [-D]\n"
"\t[-v] [-h]\n\n"
"\t-f\tLoad nDPIsrvd options from a configuration file.\n"
"\t-l\tLog all messages to stderr.\n"
"\t-L\tLog all messages to a log file.\n"
"\t-c\tPath to a listening UNIX socket (nDPIsrvd Collector).\n"
@@ -892,40 +952,45 @@ static int nDPIsrvd_parse_options(int argc, char ** argv)
"\t-s\tPath to a listening UNIX socket (nDPIsrvd Distributor).\n"
"\t \tDefault: %s\n"
"\t-S\tAddress:Port of the listening TCP/IP socket (nDPIsrvd Distributor).\n"
"\t-G\tGroup owner of the UNIX collector/distributor socket.\n"
"\t \tDefault: Either the group set via `-g', otherwise the primary group of `-u'\n"
"\t-v\tversion\n"
"\t-h\tthis\n\n",
argv[0],
get_cmdarg(&nDPIsrvd_options.collector_un_sockpath),
get_cmdarg(&nDPIsrvd_options.pidfile),
get_cmdarg(&nDPIsrvd_options.user),
get_cmdarg(&nDPIsrvd_options.distributor_un_sockpath));
nDPIsrvd_options.collector_un_sockpath.string.default_value,
nDPIsrvd_options.pidfile.string.default_value,
nDPIsrvd_options.user.string.default_value,
nDPIsrvd_options.distributor_un_sockpath.string.default_value);
return 1;
}
}
if (is_path_absolute("Pidfile", get_cmdarg(&nDPIsrvd_options.pidfile)) != 0)
set_config_defaults(&config_map[0], nDPIsrvd_ARRAY_LENGTH(config_map));
if (is_path_absolute("Pidfile", GET_CMDARG_STR(nDPIsrvd_options.pidfile)) != 0)
{
return 1;
}
if (is_path_absolute("Collector UNIX socket", get_cmdarg(&nDPIsrvd_options.collector_un_sockpath)) != 0)
if (is_path_absolute("Collector UNIX socket", GET_CMDARG_STR(nDPIsrvd_options.collector_un_sockpath)) != 0)
{
return 1;
}
if (is_path_absolute("Distributor UNIX socket", get_cmdarg(&nDPIsrvd_options.distributor_un_sockpath)) != 0)
if (is_path_absolute("Distributor UNIX socket", GET_CMDARG_STR(nDPIsrvd_options.distributor_un_sockpath)) != 0)
{
return 1;
}
if (is_cmdarg_set(&nDPIsrvd_options.distributor_in_address) != 0)
if (IS_CMDARG_SET(nDPIsrvd_options.distributor_in_address) != 0)
{
if (nDPIsrvd_setup_address(&distributor_in_address, get_cmdarg(&nDPIsrvd_options.distributor_in_address)) != 0)
if (nDPIsrvd_setup_address(&distributor_in_address, GET_CMDARG_STR(nDPIsrvd_options.distributor_in_address)) !=
0)
{
logger_early(1,
"%s: Could not parse address %s",
argv[0],
get_cmdarg(&nDPIsrvd_options.distributor_in_address));
GET_CMDARG_STR(nDPIsrvd_options.distributor_in_address));
return 1;
}
if (distributor_in_address.raw.sa_family == AF_UNIX)
@@ -933,8 +998,8 @@ static int nDPIsrvd_parse_options(int argc, char ** argv)
logger_early(1,
"%s: You've requested to setup another UNIX socket `%s', but there is already one at `%s'",
argv[0],
get_cmdarg(&nDPIsrvd_options.distributor_in_address),
get_cmdarg(&nDPIsrvd_options.distributor_un_sockpath));
GET_CMDARG_STR(nDPIsrvd_options.distributor_in_address),
GET_CMDARG_STR(nDPIsrvd_options.distributor_un_sockpath));
return 1;
}
}
@@ -1516,8 +1581,9 @@ static int mainloop(struct nio * const io)
static int setup_event_queue(struct nio * const io)
{
#ifdef ENABLE_EPOLL
if ((nDPIsrvd_options.use_poll == 0 && nio_use_epoll(io, 32) != NIO_SUCCESS) ||
(nDPIsrvd_options.use_poll != 0 && nio_use_poll(io, nDPIsrvd_MAX_REMOTE_DESCRIPTORS) != NIO_SUCCESS))
if ((GET_CMDARG_BOOL(nDPIsrvd_options.use_poll) == 0 && nio_use_epoll(io, 32) != NIO_SUCCESS) ||
(GET_CMDARG_BOOL(nDPIsrvd_options.use_poll) != 0 &&
nio_use_poll(io, nDPIsrvd_MAX_REMOTE_DESCRIPTORS) != NIO_SUCCESS))
#else
if (nio_use_poll(io, nDPIsrvd_MAX_REMOTE_DESCRIPTORS) != NIO_SUCCESS)
#endif
@@ -1576,6 +1642,49 @@ static int setup_remote_descriptors(nDPIsrvd_ull max_remote_descriptors)
return 0;
}
static int nDPIsrvd_parsed_config_line(
int lineno, char const * const section, char const * const name, char const * const value, void * const user_data)
{
(void)user_data;
if (strnlen(section, INI_MAX_SECTION) == nDPIsrvd_STRLEN_SZ("general") &&
strncmp(section, "general", INI_MAX_SECTION) == 0)
{
size_t i;
for (i = 0; i < nDPIsrvd_ARRAY_LENGTH(config_map); ++i)
{
if (strnlen(name, INI_MAX_NAME) == strnlen(config_map[i].key, INI_MAX_NAME) &&
strncmp(name, config_map[i].key, INI_MAX_NAME) == 0)
{
if (IS_CMDARG_SET(*config_map[i].opt) != 0)
{
logger_early(1, "General config key `%s' already set, ignoring value `%s'", name, value);
}
else
{
if (set_config_from(&config_map[i], value) != 0)
{
return 0;
}
}
break;
}
}
if (i == nDPIsrvd_ARRAY_LENGTH(config_map))
{
logger_early(1, "Invalid general config key `%s' at line %d", name, lineno);
}
}
else
{
logger_early(
1, "Invalid config section `%s' at line %d with key `%s' and value `%s'", section, lineno, name, value);
}
return 1;
}
#ifndef NO_MAIN
int main(int argc, char ** argv)
{
@@ -1594,6 +1703,32 @@ int main(int argc, char ** argv)
{
return 1;
}
{
int ret;
if (IS_CMDARG_SET(nDPIsrvd_options.config_file) != 0 &&
(ret =
parse_config_file(GET_CMDARG_STR(nDPIsrvd_options.config_file), nDPIsrvd_parsed_config_line, NULL)) !=
0)
{
if (ret > 0)
{
logger_early(1, "Config file `%s' is malformed", GET_CMDARG_STR(nDPIsrvd_options.config_file));
}
else if (ret == -ENOENT)
{
logger_early(1, "Path `%s' is not a regular file", GET_CMDARG_STR(nDPIsrvd_options.config_file));
}
else
{
logger_early(1,
"Could not open file `%s' for reading: %s",
GET_CMDARG_STR(nDPIsrvd_options.config_file),
strerror(errno));
}
return 1;
}
}
if (is_daemonize_enabled() != 0 && is_console_logger_enabled() != 0)
{
@@ -1604,32 +1739,32 @@ int main(int argc, char ** argv)
return 1;
}
if (access(get_cmdarg(&nDPIsrvd_options.collector_un_sockpath), F_OK) == 0)
if (access(GET_CMDARG_STR(nDPIsrvd_options.collector_un_sockpath), F_OK) == 0)
{
logger_early(1,
"UNIX socket `%s' exists; nDPIsrvd already running? "
"Please remove the socket manually or change socket path.",
get_cmdarg(&nDPIsrvd_options.collector_un_sockpath));
GET_CMDARG_STR(nDPIsrvd_options.collector_un_sockpath));
return 1;
}
if (access(get_cmdarg(&nDPIsrvd_options.distributor_un_sockpath), F_OK) == 0)
if (access(GET_CMDARG_STR(nDPIsrvd_options.distributor_un_sockpath), F_OK) == 0)
{
logger_early(1,
"UNIX socket `%s' exists; nDPIsrvd already running? "
"Please remove the socket manually or change socket path.",
get_cmdarg(&nDPIsrvd_options.distributor_un_sockpath));
GET_CMDARG_STR(nDPIsrvd_options.distributor_un_sockpath));
return 1;
}
log_app_info();
if (daemonize_with_pidfile(get_cmdarg(&nDPIsrvd_options.pidfile)) != 0)
if (daemonize_with_pidfile(GET_CMDARG_STR(nDPIsrvd_options.pidfile)) != 0)
{
goto error;
}
if (setup_remote_descriptors(nDPIsrvd_options.max_remote_descriptors) != 0)
if (setup_remote_descriptors(GET_CMDARG_ULL(nDPIsrvd_options.max_remote_descriptors)) != 0)
{
goto error;
}
@@ -1641,11 +1776,11 @@ int main(int argc, char ** argv)
case 1:
goto error;
case 2:
if (unlink(get_cmdarg(&nDPIsrvd_options.collector_un_sockpath)) != 0)
if (unlink(GET_CMDARG_STR(nDPIsrvd_options.collector_un_sockpath)) != 0)
{
logger(1,
"Could not unlink `%s': %s",
get_cmdarg(&nDPIsrvd_options.collector_un_sockpath),
GET_CMDARG_STR(nDPIsrvd_options.collector_un_sockpath),
strerror(errno));
}
goto error;
@@ -1655,8 +1790,8 @@ int main(int argc, char ** argv)
goto error;
}
logger(0, "collector UNIX socket listen on `%s'", get_cmdarg(&nDPIsrvd_options.collector_un_sockpath));
logger(0, "distributor UNIX listen on `%s'", get_cmdarg(&nDPIsrvd_options.distributor_un_sockpath));
logger(0, "collector UNIX socket listen on `%s'", GET_CMDARG_STR(nDPIsrvd_options.collector_un_sockpath));
logger(0, "distributor UNIX listen on `%s'", GET_CMDARG_STR(nDPIsrvd_options.distributor_un_sockpath));
switch (distributor_in_address.raw.sa_family)
{
default:
@@ -1672,28 +1807,88 @@ int main(int argc, char ** argv)
break;
}
errno = 0;
if (change_user_group(get_cmdarg(&nDPIsrvd_options.user),
get_cmdarg(&nDPIsrvd_options.group),
get_cmdarg(&nDPIsrvd_options.pidfile),
get_cmdarg(&nDPIsrvd_options.collector_un_sockpath),
get_cmdarg(&nDPIsrvd_options.distributor_un_sockpath)) != 0 &&
errno != EPERM)
int ret = chmod_chown(GET_CMDARG_STR(nDPIsrvd_options.collector_un_sockpath),
S_IRUSR | S_IWUSR | S_IWGRP,
GET_CMDARG_STR(nDPIsrvd_options.user),
IS_CMDARG_SET(nDPIsrvd_options.collector_group) != 0
? GET_CMDARG_STR(nDPIsrvd_options.collector_group)
: GET_CMDARG_STR(nDPIsrvd_options.group));
if (ret != 0)
{
if (errno != 0)
if (IS_CMDARG_SET(nDPIsrvd_options.collector_group) != 0 || IS_CMDARG_SET(nDPIsrvd_options.group) != 0)
{
logger(1,
"Change user/group to %s/%s failed: %s",
get_cmdarg(&nDPIsrvd_options.user),
(is_cmdarg_set(&nDPIsrvd_options.group) != 0 ? get_cmdarg(&nDPIsrvd_options.group) : "-"),
"Could not chmod/chown `%s' to user `%s' and group `%s': %s",
GET_CMDARG_STR(nDPIsrvd_options.collector_un_sockpath),
GET_CMDARG_STR(nDPIsrvd_options.user),
IS_CMDARG_SET(nDPIsrvd_options.collector_group) != 0
? GET_CMDARG_STR(nDPIsrvd_options.collector_group)
: GET_CMDARG_STR(nDPIsrvd_options.group),
strerror(errno));
}
else
{
logger(1,
"Change user/group to %s/%s failed.",
get_cmdarg(&nDPIsrvd_options.user),
(is_cmdarg_set(&nDPIsrvd_options.group) != 0 ? get_cmdarg(&nDPIsrvd_options.group) : "-"));
"Could not chmod/chown `%s' to user `%s': %s",
GET_CMDARG_STR(nDPIsrvd_options.collector_un_sockpath),
GET_CMDARG_STR(nDPIsrvd_options.user),
strerror(errno));
}
if (ret != -EPERM)
{
goto error_unlink_sockets;
}
}
ret = chmod_chown(GET_CMDARG_STR(nDPIsrvd_options.distributor_un_sockpath),
S_IRUSR | S_IWUSR | S_IWGRP,
GET_CMDARG_STR(nDPIsrvd_options.user),
IS_CMDARG_SET(nDPIsrvd_options.distributor_group) != 0
? GET_CMDARG_STR(nDPIsrvd_options.distributor_group)
: GET_CMDARG_STR(nDPIsrvd_options.group));
if (ret != 0)
{
if (IS_CMDARG_SET(nDPIsrvd_options.distributor_group) != 0 || IS_CMDARG_SET(nDPIsrvd_options.group) != 0)
{
logger(1,
"Could not chmod/chown `%s' to user `%s' and group `%s': %s",
GET_CMDARG_STR(nDPIsrvd_options.distributor_un_sockpath),
GET_CMDARG_STR(nDPIsrvd_options.user),
IS_CMDARG_SET(nDPIsrvd_options.distributor_group) != 0
? GET_CMDARG_STR(nDPIsrvd_options.distributor_group)
: GET_CMDARG_STR(nDPIsrvd_options.group),
strerror(errno));
}
else
{
logger(1,
"Could not chmod/chown `%s' to user `%s': %s",
GET_CMDARG_STR(nDPIsrvd_options.distributor_un_sockpath),
GET_CMDARG_STR(nDPIsrvd_options.user),
strerror(errno));
}
if (ret != -EPERM)
{
goto error_unlink_sockets;
}
}
ret = change_user_group(GET_CMDARG_STR(nDPIsrvd_options.user),
GET_CMDARG_STR(nDPIsrvd_options.group),
GET_CMDARG_STR(nDPIsrvd_options.pidfile));
if (ret != 0 && ret != -EPERM)
{
if (GET_CMDARG_STR(nDPIsrvd_options.group) != NULL)
{
logger(1,
"Change user/group to %s/%s failed: %s",
GET_CMDARG_STR(nDPIsrvd_options.user),
GET_CMDARG_STR(nDPIsrvd_options.group),
strerror(-ret));
}
else
{
logger(1, "Change user to %s failed: %s", GET_CMDARG_STR(nDPIsrvd_options.user), strerror(-ret));
}
goto error_unlink_sockets;
}
@@ -1713,20 +1908,23 @@ int main(int argc, char ** argv)
retval = mainloop(&io);
error_unlink_sockets:
if (unlink(get_cmdarg(&nDPIsrvd_options.collector_un_sockpath)) != 0)
if (unlink(GET_CMDARG_STR(nDPIsrvd_options.collector_un_sockpath)) != 0)
{
logger(1, "Could not unlink `%s': %s", get_cmdarg(&nDPIsrvd_options.collector_un_sockpath), strerror(errno));
logger(1, "Could not unlink `%s': %s", GET_CMDARG_STR(nDPIsrvd_options.collector_un_sockpath), strerror(errno));
}
if (unlink(get_cmdarg(&nDPIsrvd_options.distributor_un_sockpath)) != 0)
if (unlink(GET_CMDARG_STR(nDPIsrvd_options.distributor_un_sockpath)) != 0)
{
logger(1, "Could not unlink `%s': %s", get_cmdarg(&nDPIsrvd_options.distributor_un_sockpath), strerror(errno));
logger(1,
"Could not unlink `%s': %s",
GET_CMDARG_STR(nDPIsrvd_options.distributor_un_sockpath),
strerror(errno));
}
error:
close(collector_un_sockfd);
close(distributor_un_sockfd);
close(distributor_in_sockfd);
daemonize_shutdown(get_cmdarg(&nDPIsrvd_options.pidfile));
daemonize_shutdown(GET_CMDARG_STR(nDPIsrvd_options.pidfile));
logger(0, "Bye.");
shutdown_logging();

86
ndpid.conf.example Normal file
View File

@@ -0,0 +1,86 @@
[general]
# Set the network interface from which packets are captured and processed.
# Leave it empty to let nDPId choose the default network interface.
#netif = eth0
# Set a Berkeley Packet Filter.
# This will work for libpcap as well as with PF_RING.
#bpf = udp or tcp
#pidfile = /tmp/ndpid.pid
#user = nobody
#group = daemon
#riskdomains = /path/to/libnDPI/example/risky_domains.txt
#protocols = /path/to/libnDPI/example/protos.txt
#categories = /path/to/libnDPI/example/categories.txt
#ja3 = /path/to/libnDPI/example/ja3_fingerprints.csv
#sha1 = /path/to/libnDPI/example/sha1_fingerprints.csv
# Collector endpoint as UNIX socket (usually nDPIsrvd)
#collector = /run/nDPIsrvd/collector
# Collector endpoint as UDP socket (usually a custom application)
#collector = 127.0.0.1:7777
# Set a name for this nDPId instance
#alias = myhostname
# Process only internal initial connections
#internal = true
# Process only external initial connections
#external = true
# Enable zLib compression of flow memory for long lasting flows
compression = true
# Enable "analyse" events, which can be used for machine learning
analysis = true
# Force poll() on systems that support epoll() as well
#poll = false
# Enable PF_RING packet capture instead of libpcap
#pfring = false
[tuning]
max-flows-per-thread = 2048
max-idle-flows-per-thread = 64
max-reader-threads = 10
daemon-status-interval = 600000000
#memory-profiling-log-interval = 5
compression-scan-interval = 20000000
compression-flow-inactivity = 30000000
flow-scan-interval = 10000000
generic-max-idle-time = 600000000
icmp-max-idle-time = 120000000
tcp-max-idle-time = 180000000
udp-max-idle-time = 7440000000
tcp-max-post-end-flow-time = 120000000
max-packets-per-flow-to-send = 15
max-packets-per-flow-to-process = 32
max-packets-per-flow-to-analyse = 32
error-event-threshold-n = 16
error-event-threshold-time = 10000000
# Please note that the following options are libnDPI related and can only be set via config file,
# not as commnand line parameter.
# See libnDPI/doc/configuration_parameters.md for detailed information.
[ndpi]
packets_limit_per_flow = 32
flow.direction_detection = enable
flow.track_payload = disable
tcp_ack_payload_heuristic = disable
fully_encrypted_heuristic = enable
libgcrypt.init = 1
dpi.compute_entropy = 1
fpc = disable
dpi.guess_on_giveup = 0x03
flow_risk_lists.load = 1
flow_risk.crawler_bot.list.load = 1
log.level = 0
[protos]
tls.certificate_expiration_threshold = 7
tls.application_blocks_tracking = enable
stun.max_packets_extra_dissection = 8

31
ndpisrvd.conf.example Normal file
View File

@@ -0,0 +1,31 @@
[general]
#pidfile = /tmp/ndpisrvd.pid
#user = nobody
#group = nogroup
# Collector listener as UNIX socket
#collector = /run/nDPIsrvd/collector
# Distributor listener as UNIX socket
#distributor-unix = /run/nDPIsrvd/distributor
# Distributor listener as IP socket
#distributor-in = 127.0.0.1:7000
# Change group of the collector socket
#collector-group = daemon
# Change group of the distirbutor socket
#distirbutor-group = staff
# Max (distributor) clients allowed to connect to nDPIsrvd
max-remote-descriptors = 128
# Additional output buffers useful if a distributor sink speed unstable
max-write-buffers = 1024
# Fallback to blocking I/O if output buffers full
blocking-io-fallback = true
# Force poll() on systems that support epoll() as well
#poll = false

6
packages/debian/postrm Executable file
View File

@@ -0,0 +1,6 @@
#!/bin/sh
rm -rf /run/nDPId /run/nDPIsrvd
deluser ndpid || true
deluser ndpisrvd || true
delgroup ndpisrvd-distributor || true

16
packages/debian/preinst Executable file
View File

@@ -0,0 +1,16 @@
#!/bin/sh
addgroup --system ndpisrvd-distributor
adduser --system --no-create-home --shell=/bin/false --group ndpisrvd
adduser --system --no-create-home --shell=/bin/false --group ndpid
cat <<EOF
**********************************************************************************
* The that may want to access DPI data needs access to /run/nDPIsrvd/distributor *
* *
* To make it accessible to a user, type: *
* sudo usermod --append --groups ndpisrvd-distributor [USER] *
* *
* Please not that you might need to re-login to make changes take effect. *
**********************************************************************************
EOF

3
packages/debian/prerm Executable file
View File

@@ -0,0 +1,3 @@
#!/bin/sh
systemctl stop ndpisrvd.service

View File

@@ -1,2 +0,0 @@
COLLECTOR_PATH=/var/run/ndpisrvd-collector
NDPID_ARGS="-A -z"

View File

@@ -5,10 +5,9 @@ Requires=ndpisrvd.service
[Service]
Type=simple
ExecStart=@CMAKE_INSTALL_PREFIX@/sbin/nDPId $NDPID_ARGS -i %i -c ${COLLECTOR_PATH}
ExecStartPre=/bin/sh -c 'test -r "@CMAKE_INSTALL_PREFIX@/etc/nDPId/%i.conf" || cp -v "@CMAKE_INSTALL_PREFIX@/share/nDPId/ndpid.conf.example" "@CMAKE_INSTALL_PREFIX@/etc/nDPId/%i.conf"'
ExecStart=@CMAKE_INSTALL_PREFIX@/sbin/nDPId -f @CMAKE_INSTALL_PREFIX@/etc/nDPId/%i.conf -i %i -u ndpid -c /run/nDPIsrvd/collector
Restart=on-failure
Environment=COLLECTOR_PATH=/var/run/ndpisrvd-collector NDPID_ARGS="-A -z"
EnvironmentFile=@CMAKE_INSTALL_PREFIX@/etc/default/ndpid
[Install]
WantedBy=multi-user.target

View File

@@ -4,11 +4,10 @@ After=network.target
[Service]
Type=simple
ExecStart=@CMAKE_INSTALL_PREFIX@/bin/nDPIsrvd -c ${COLLECTOR_PATH}
ExecStopPost=/bin/rm -f /var/run/ndpisrvd-collector
ExecStartPre=/bin/sh -c 'test -r "@CMAKE_INSTALL_PREFIX@/etc/nDPId/nDPIsrvd.conf" || cp -v "@CMAKE_INSTALL_PREFIX@/share/nDPId/ndpisrvd.conf.example" "@CMAKE_INSTALL_PREFIX@/etc/nDPId/nDPIsrvd.conf"'
ExecStartPre=/bin/sh -c 'mkdir -p /run/nDPIsrvd && chown root:root /run/nDPIsrvd && chmod 0775 /run/nDPIsrvd'
ExecStart=@CMAKE_INSTALL_PREFIX@/bin/nDPIsrvd -f @CMAKE_INSTALL_PREFIX@/etc/nDPId/nDPIsrvd.conf -u ndpisrvd -c /run/nDPIsrvd/collector -s /run/nDPIsrvd/distributor -G ndpid:ndpisrvd-distributor
Restart=on-failure
Environment=COLLECTOR_PATH=/var/run/ndpisrvd-collector
EnvironmentFile=@CMAKE_INSTALL_PREFIX@/etc/default/ndpid
[Install]
WantedBy=multi-user.target

377
utils.c
View File

@@ -1,3 +1,4 @@
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <grp.h>
@@ -10,11 +11,19 @@
#include <syslog.h>
#endif
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "utils.h"
#define UTILS_STRLEN_SZ(s) ((size_t)((sizeof(s) / sizeof(s[0])) - sizeof(s[0])))
#ifndef INI_MAX_LINE
#define INI_MAX_LINE BUFSIZ
#endif
#define INI_INLINE_COMMENT_PREFIXES ";"
#define INI_START_COMMENT_PREFIXES ";#"
typedef char pid_str[16];
static char const * app_name = NULL;
@@ -22,40 +31,151 @@ static int daemonize = 0;
static int log_to_console = 0;
static int log_to_file_fd = -1;
void set_cmdarg(struct cmdarg * const ca, char const * const val)
void set_config_defaults(struct confopt * const co_array, size_t array_length)
{
for (size_t i = 0; i < array_length; ++i)
{
if (co_array[i].opt == NULL)
{
logger_early(1, "%s", "BUG: Config option is NULL");
continue;
}
if (IS_CMDARG_SET(*co_array[i].opt) == 0)
{
switch (co_array[i].opt->type)
{
case CMDTYPE_INVALID:
logger_early(1, "BUG: Config option `%s' has CMDTYPE_INVALID!", co_array[i].key);
break;
case CMDTYPE_STRING:
if (co_array[i].opt->string.default_value == NULL)
{
break;
}
co_array[i].opt->string.value = strdup(co_array[i].opt->string.default_value);
break;
case CMDTYPE_BOOLEAN:
co_array[i].opt->boolean.value = co_array[i].opt->boolean.default_value;
break;
case CMDTYPE_ULL:
co_array[i].opt->ull.value = co_array[i].opt->ull.default_value;
break;
}
}
}
}
int set_config_from(struct confopt * const co, char const * const from)
{
if (co == NULL || co->opt == NULL || from == NULL)
{
return -1;
}
switch (co->opt->type)
{
case CMDTYPE_INVALID:
break;
case CMDTYPE_STRING:
set_cmdarg_string(co->opt, from);
break;
case CMDTYPE_BOOLEAN:
{
uint8_t enabled;
if ((strnlen(from, INI_MAX_LINE) == UTILS_STRLEN_SZ("true") &&
strncasecmp(from, "true", INI_MAX_LINE) == 0) ||
(strnlen(from, INI_MAX_LINE) == UTILS_STRLEN_SZ("1") && strncasecmp(from, "1", INI_MAX_LINE) == 0))
{
enabled = 1;
}
else if ((strnlen(from, INI_MAX_LINE) == UTILS_STRLEN_SZ("false") &&
strncasecmp(from, "false", INI_MAX_LINE) == 0) ||
(strnlen(from, INI_MAX_LINE) == UTILS_STRLEN_SZ("0") && strncasecmp(from, "0", INI_MAX_LINE) == 0))
{
enabled = 0;
}
else
{
logger_early(1, "Config key `%s' has a value not of type bool: `%s'", co->key, from);
return 1;
}
set_cmdarg_boolean(co->opt, enabled);
}
break;
case CMDTYPE_ULL:
{
char * endptr;
long int value_llu = strtoull(from, &endptr, 10);
if (from == endptr)
{
logger_early(1, "Subopt `%s': Value `%s' is not a valid number.", co->key, from);
return 1;
}
if (errno == ERANGE)
{
logger_early(1, "Subopt `%s': Number too large.", co->key);
return 1;
}
set_cmdarg_ull(co->opt, value_llu);
}
break;
}
return 0;
}
void set_cmdarg_string(struct cmdarg * const ca, char const * const val)
{
if (ca == NULL || val == NULL)
{
return;
}
free(ca->value);
ca->value = strdup(val);
if (ca->type != CMDTYPE_STRING)
{
logger_early(1, "%s", "BUG: Type is not CMDTYPE_STRING!");
return;
}
ca->is_set = 1;
free(ca->string.value);
ca->string.value = strdup(val);
}
char const * get_cmdarg(struct cmdarg const * const ca)
void set_cmdarg_boolean(struct cmdarg * const ca, uint8_t val)
{
if (ca == NULL)
{
return NULL;
return;
}
if (ca->value != NULL)
if (ca->type != CMDTYPE_BOOLEAN)
{
return ca->value;
logger_early(1, "%s", "BUG: Type is not CMDTYPE_BOOLEAN!");
return;
}
return ca->default_value;
ca->is_set = 1;
ca->boolean.value = (val != 0);
}
int is_cmdarg_set(struct cmdarg const * const ca)
void set_cmdarg_ull(struct cmdarg * const ca, unsigned long long int val)
{
if (ca == NULL)
{
return 0;
return;
}
return ca->value != NULL;
if (ca->type != CMDTYPE_ULL)
{
logger_early(1, "%s", "BUG: Type is not CMDTYPE_ULL!");
return;
}
ca->is_set = 1;
ca->ull.value = val;
}
void daemonize_enable(void)
@@ -218,11 +338,7 @@ int daemonize_shutdown(char const * const pidfile)
return 0;
}
int change_user_group(char const * const user,
char const * const group,
char const * const pidfile,
char const * const uds_collector_path,
char const * const uds_distributor_path)
int change_user_group(char const * const user, char const * const group, char const * const pidfile)
{
struct passwd * pwd;
struct group * grp;
@@ -237,7 +353,7 @@ int change_user_group(char const * const user,
pwd = getpwnam(user);
if (pwd == NULL)
{
return -errno;
return (errno != 0 ? -errno : -ENOENT);
}
if (group != NULL)
@@ -246,7 +362,7 @@ int change_user_group(char const * const user,
grp = getgrnam(group);
if (grp == NULL)
{
return -errno;
return (errno != 0 ? -errno : -ENOENT);
}
gid = grp->gr_gid;
}
@@ -255,23 +371,6 @@ int change_user_group(char const * const user,
gid = pwd->pw_gid;
}
if (uds_collector_path != NULL)
{
errno = 0;
if (chmod(uds_collector_path, S_IRUSR | S_IWUSR) != 0 || chown(uds_collector_path, pwd->pw_uid, gid) != 0)
{
return -errno;
}
}
if (uds_distributor_path != NULL)
{
errno = 0;
if (chmod(uds_distributor_path, S_IRUSR | S_IWUSR | S_IRGRP) != 0 ||
chown(uds_distributor_path, pwd->pw_uid, gid) != 0)
{
return -errno;
}
}
if (daemonize != 0 && pidfile != NULL)
{
errno = 0;
@@ -283,6 +382,56 @@ int change_user_group(char const * const user,
return setregid(gid, gid) != 0 || setreuid(pwd->pw_uid, pwd->pw_uid);
}
WARN_UNUSED
int chmod_chown(char const * const path, mode_t mode, char const * const user, char const * const group)
{
uid_t path_uid = (uid_t)-1;
gid_t path_gid = (gid_t)-1;
if (mode != 0)
{
if (chmod(path, mode) != 0)
{
return -errno;
}
}
if (user != NULL)
{
errno = 0;
struct passwd * const pwd = getpwnam(user);
if (pwd == NULL)
{
return (errno != 0 ? -errno : -ENOENT);
}
path_uid = pwd->pw_uid;
path_gid = pwd->pw_gid;
}
if (group != NULL)
{
errno = 0;
struct group * const grp = getgrnam(group);
if (grp == NULL)
{
return (errno != 0 ? -errno : -ENOENT);
}
path_gid = grp->gr_gid;
}
if (path_uid != (uid_t)-1 || path_gid != (gid_t)-1)
{
if (chown(path, path_uid, path_gid) != 0)
{
return -errno;
}
}
return 0;
}
void init_logging(char const * const name)
{
app_name = name;
@@ -454,6 +603,160 @@ char const * get_nDPId_version(void)
"unknown"
#endif
"\n"
"(C) 2020-2023 Toni Uhlig\n"
"(C) 2020-2024 Toni Uhlig\n"
"Please report any BUG to toni@impl.cc\n";
}
/* Strip whitespace chars off end of given string, in place. Return s. */
static char * ini_rstrip(char * s)
{
char * p = s + strlen(s);
while (p > s && isspace((unsigned char)(*--p)))
*p = '\0';
return s;
}
/* Return pointer to first non-whitespace char in given string. */
static char * ini_lskip(const char * s)
{
while (*s && isspace((unsigned char)(*s)))
s++;
return (char *)s;
}
/* Return pointer to first char (of chars) or inline comment in given string,
or pointer to NUL at end of string if neither found. Inline comment must
be prefixed by a whitespace character to register as a comment. */
static char * ini_find_chars_or_comment(const char * s, const char * chars)
{
int was_space = 0;
while (*s && (!chars || !strchr(chars, *s)) && !(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s)))
{
was_space = isspace((unsigned char)(*s));
s++;
}
return (char *)s;
}
/* See: https://github.com/benhoyt/inih/blob/master/ini.c#L97C67-L97C74 */
static int parse_config_lines(FILE * const file, config_line_callback cb, void * const user_data)
{
char line[INI_MAX_LINE];
int max_line = INI_MAX_LINE;
char section[INI_MAX_SECTION] = "";
char prev_name[INI_MAX_NAME] = "";
char * start;
char * end;
char * name;
char * value;
int lineno = 0;
int error = 0;
while (fgets(line, max_line, file) != NULL)
{
lineno++;
start = line;
start = ini_lskip(ini_rstrip(start));
if (strchr(INI_START_COMMENT_PREFIXES, *start))
{
/* Start-of-line comment */
}
else if (*prev_name && *start && start > line)
{
end = ini_find_chars_or_comment(start, NULL);
if (*end)
{
*end = '\0';
}
ini_rstrip(start);
/* Non-blank line with leading whitespace, treat as continuation
of previous name's value (as per Python configparser). */
if (!cb(lineno, section, prev_name, start, user_data) && !error)
{
error = lineno;
}
}
else if (*start == '[')
{
/* A "[section]" line */
end = ini_find_chars_or_comment(start + 1, "]");
if (*end == ']')
{
*end = '\0';
snprintf(section, sizeof(section), "%s", start + 1);
*prev_name = '\0';
}
else if (!error)
{
/* No ']' found on section line */
error = lineno;
}
}
else if (*start)
{
/* Not a comment, must be a name[=:]value pair */
end = ini_find_chars_or_comment(start, "=:");
if (*end == '=' || *end == ':')
{
*end = '\0';
name = ini_rstrip(start);
value = end + 1;
end = ini_find_chars_or_comment(value, NULL);
if (*end)
{
*end = '\0';
}
value = ini_lskip(value);
ini_rstrip(value);
/* Valid name[=:]value pair found, call handler */
snprintf(prev_name, sizeof(prev_name), "%s", name);
if (!cb(lineno, section, prev_name, value, user_data) && !error)
{
error = lineno;
}
}
else if (!error)
{
/* No '=' or ':' found on name[=:]value line */
error = lineno;
}
}
}
return error;
}
int parse_config_file(char const * const config_file, config_line_callback cb, void * const user_data)
{
int file_fd;
FILE * file;
int error;
struct stat sbuf;
file_fd = open(config_file, O_RDONLY);
if (file_fd < 0)
{
return -1;
}
if (fstat(file_fd, &sbuf) != 0)
{
return -1;
}
if ((sbuf.st_mode & S_IFMT) != S_IFREG)
{
return -ENOENT;
}
file = fdopen(file_fd, "r");
if (file == NULL)
{
return -1;
}
error = parse_config_lines(file, cb, user_data);
fclose(file);
return error;
}

87
utils.h
View File

@@ -2,27 +2,86 @@
#define UTILS_H 1
#include <stdarg.h>
#include <stdint.h>
#include <sys/types.h>
#define WARN_UNUSED __attribute__((__warn_unused_result__))
#define CMDARG(_default_value) \
#define INI_MAX_SECTION 50
#define INI_MAX_NAME 50
#define CMDARG_STR(_default_value) \
{ \
.value = NULL, .default_value = (_default_value) \
.is_set = 0, .type = CMDTYPE_STRING, .string.value = NULL, .string.default_value = (_default_value) \
}
#define CMDARG_BOOL(_default_value) \
{ \
.is_set = 0, .type = CMDTYPE_BOOLEAN, .boolean.value = 0, .boolean.default_value = (_default_value) \
}
#define CMDARG_ULL(_default_value) \
{ \
.is_set = 0, .type = CMDTYPE_ULL, .ull.value = 0ull, .ull.default_value = (_default_value) \
}
#define CONFOPT(_key, _opt) \
{ \
.key = _key, .opt = _opt \
}
#define GET_CMDARG_STR(cmdarg) ((cmdarg).string.value)
#define GET_CMDARG_BOOL(cmdarg) ((cmdarg).boolean.value)
#define GET_CMDARG_ULL(cmdarg) ((cmdarg).ull.value)
#define IS_CMDARG_SET(cmdarg) ((cmdarg).is_set)
enum cmdtype
{
CMDTYPE_INVALID = 0,
CMDTYPE_STRING,
CMDTYPE_BOOLEAN,
CMDTYPE_ULL
};
struct cmdarg
{
char * value;
char const * const default_value;
enum cmdtype type;
int is_set;
union
{
struct
{
char * value;
char const * const default_value;
} string;
struct
{
uint8_t value;
uint8_t const default_value;
} boolean;
struct
{
unsigned long long int value;
unsigned long long int const default_value;
} ull;
};
};
void set_cmdarg(struct cmdarg * const ca, char const * const val);
struct confopt
{
char const * const key;
struct cmdarg * const opt;
};
typedef int (*config_line_callback)(
int lineno, char const * const section, char const * const key, char const * const value, void * const user_data);
void set_config_defaults(struct confopt * const co_array, size_t array_length);
WARN_UNUSED
char const * get_cmdarg(struct cmdarg const * const ca);
int set_config_from(struct confopt * const co, char const * const from);
WARN_UNUSED
int is_cmdarg_set(struct cmdarg const * const ca);
void set_cmdarg_string(struct cmdarg * const ca, char const * const val);
void set_cmdarg_boolean(struct cmdarg * const ca, uint8_t val);
void set_cmdarg_ull(struct cmdarg * const ca, unsigned long long int val);
WARN_UNUSED
int is_path_absolute(char const * const prefix, char const * const path);
@@ -38,11 +97,10 @@ int daemonize_with_pidfile(char const * const pidfile);
int daemonize_shutdown(char const * const pidfile);
WARN_UNUSED
int change_user_group(char const * const user,
char const * const group,
char const * const pidfile,
char const * const uds_collector_path,
char const * const uds_distributor_path);
int change_user_group(char const * const user, char const * const group, char const * const pidfile);
WARN_UNUSED
int chmod_chown(char const * const path, mode_t mode, char const * const user, char const * const group);
void init_logging(char const * const daemon_name);
@@ -73,4 +131,7 @@ int set_fd_cloexec(int fd);
WARN_UNUSED
char const * get_nDPId_version(void);
WARN_UNUSED
int parse_config_file(char const * const config_file, config_line_callback cb, void * const user_data);
#endif