From: pcarana Date: Mon, 13 Jul 2020 19:08:59 +0000 (-0500) Subject: Allow to bind to multiple addresses using a CSV at server.address X-Git-Tag: v1.4.0~24 X-Git-Url: http://git.ipfire.org/gitweb/gitweb.cgi?a=commitdiff_plain;h=f5a899f2a7d940735c9d28f93e3faf1eb385729f;p=thirdparty%2FFORT-validator.git Allow to bind to multiple addresses using a CSV at server.address --- diff --git a/src/Makefile.am b/src/Makefile.am index 103f2790..b03cb514 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -29,7 +29,7 @@ fort_SOURCES += resource.h resource.c fort_SOURCES += rpp.h rpp.c fort_SOURCES += sorted_array.h sorted_array.c fort_SOURCES += state.h state.c -fort_SOURCES += str.h str.c +fort_SOURCES += str_token.h str_token.c fort_SOURCES += thread_var.h thread_var.c fort_SOURCES += updates_daemon.c updates_daemon.h fort_SOURCES += uri.h uri.c diff --git a/src/cert_stack.c b/src/cert_stack.c index b659dc00..e2b09f03 100644 --- a/src/cert_stack.c +++ b/src/cert_stack.c @@ -3,7 +3,7 @@ #include #include "resource.h" -#include "str.h" +#include "str_token.h" #include "thread_var.h" #include "data_structure/array_list.h" #include "object/name.h" diff --git a/src/config.c b/src/config.c index 809450cb..c8b358e8 100644 --- a/src/config.c +++ b/src/config.c @@ -61,7 +61,7 @@ struct rpki_config { struct { /** The bound listening address of the RTR server. */ - char *address; + struct string_array address; /** The bound listening port of the RTR server. */ char *port; /** Outstanding connections in the socket's listen queue */ @@ -300,15 +300,17 @@ static const struct option_field options[] = { { .id = 5000, .name = "server.address", - .type = >_string, + .type = >_string_array, .offset = offsetof(struct rpki_config, server.address), - .doc = "Address to which RTR server will bind itself to. Can be a name, in which case an address will be resolved.", + .doc = "List of addresses (comma separated) to which RTR server will bind itself to. Can be a name, in which case an address will be resolved. The format for each address is '
[#]'.", + .min = 0, + .max = 50, }, { .id = 5001, .name = "server.port", .type = >_string, .offset = offsetof(struct rpki_config, server.port), - .doc = "Port to which RTR server will bind itself to. Can be a string, in which case a number will be resolved.", + .doc = "Default port to which RTR server addresses will bind itself to. Can be a string, in which case a number will be resolved. If all of the addresses have a port, this value isn't utilized.", }, { .id = 5002, .name = "server.backlog", @@ -438,6 +440,8 @@ static const struct option_field options[] = { .offset = offsetof(struct rpki_config, rsync.args.recursive), .doc = "RSYNC program arguments that will trigger a recursive RSYNC", .availability = AVAILABILITY_JSON, + /* Unlimited */ + .max = 0, }, { .id = 3007, .name = "rsync.arguments-flat", @@ -445,6 +449,8 @@ static const struct option_field options[] = { .offset = offsetof(struct rpki_config, rsync.args.flat), .doc = "RSYNC program arguments that will trigger a non-recursive RSYNC", .availability = AVAILABILITY_JSON, + /* Unlimited */ + .max = 0, }, /* RRDP fields */ @@ -817,11 +823,16 @@ set_default_values(void) * duplicates. */ - rpki_config.server.address = NULL; - rpki_config.server.port = strdup("323"); - if (rpki_config.server.port == NULL) + error = string_array_init(&rpki_config.server.address, NULL, 0); + if (error) return pr_enomem(); + rpki_config.server.port = strdup("323"); + if (rpki_config.server.port == NULL) { + error = pr_enomem(); + goto revert_address; + } + rpki_config.server.backlog = SOMAXCONN; rpki_config.server.interval.validation = 3600; rpki_config.server.interval.refresh = 3600; @@ -923,6 +934,8 @@ revert_repository: free(rpki_config.local_repository); revert_port: free(rpki_config.server.port); +revert_address: + string_array_cleanup(&rpki_config.server.address); return error; } @@ -1086,10 +1099,10 @@ config_get_mode(void) return rpki_config.mode; } -char const * +struct string_array const * config_get_server_address(void) { - return rpki_config.server.address; + return &rpki_config.server.address; } char const * diff --git a/src/config.h b/src/config.h index 0a2230dd..0e785862 100644 --- a/src/config.h +++ b/src/config.h @@ -16,7 +16,7 @@ int handle_flags_config(int , char **); void free_rpki_config(void); /* Getters */ -char const *config_get_server_address(void); +struct string_array const *config_get_server_address(void); char const *config_get_server_port(void); int config_get_server_queue(void); unsigned int config_get_validation_interval(void); diff --git a/src/config/string_array.c b/src/config/string_array.c index cc4bfac0..0817d459 100644 --- a/src/config/string_array.c +++ b/src/config/string_array.c @@ -6,6 +6,7 @@ #include #include "log.h" +#include "str_token.h" #include "config/str.h" int @@ -88,6 +89,10 @@ string_array_parse_json(struct option_field const *opt, json_t *json, return 0; } + if (opt->max > 0 && len > opt->max) + return pr_op_err("'%s' can have %u elements max; currently it has %lu elements.", + opt->name, opt->max, len); + for (i = 0; i < len; i++) { child = json_array_get(json, i); if (!json_is_string(child)) { @@ -126,6 +131,45 @@ fail: return error; } +static int +string_array_parse_argv(struct option_field const *opt, char const *str, + void *_result) +{ + struct string_tokenizer tokenizer; + struct string_array *result; + size_t i, len; + int error; + + result = _result; + + /* Remove the previous value (usually the default). */ + __string_array_free(result); + + string_tokenizer_init(&tokenizer, str, strlen(str), ','); + len = token_count(&tokenizer); + + if (opt->max > 0 && len > opt->max) + return pr_op_err("'%s' can have %u elements max; currently it has %lu elements.", + opt->name, opt->max, len); + + result->array = calloc(len, sizeof(char *)); + if (result->array == NULL) + return pr_enomem(); + result->length = len; + + for (i = 0; string_tokenizer_next(&tokenizer); i++) { + error = token_read(&tokenizer, &result->array[i]); + if (error) + goto fail; + } + + return 0; + +fail: + __string_array_free(result); + return error; +} + static void string_array_free(void *array) { @@ -137,6 +181,7 @@ const struct global_type gt_string_array = { .size = sizeof(char *const *), .print = string_array_print, .parse.json = string_array_parse_json, + .parse.argv = string_array_parse_argv, .free = string_array_free, .arg_doc = "", }; diff --git a/src/object/certificate.c b/src/object/certificate.c index 4175cfb5..1e8ea519 100644 --- a/src/object/certificate.c +++ b/src/object/certificate.c @@ -10,7 +10,7 @@ #include "log.h" #include "nid.h" #include "reqs_errors.h" -#include "str.h" +#include "str_token.h" #include "thread_var.h" #include "asn1/decode.h" #include "asn1/oid.h" diff --git a/src/rsync/rsync.c b/src/rsync/rsync.c index 24e14c17..6c7f9c55 100644 --- a/src/rsync/rsync.c +++ b/src/rsync/rsync.c @@ -12,7 +12,7 @@ #include "config.h" #include "log.h" #include "reqs_errors.h" -#include "str.h" +#include "str_token.h" #include "thread_var.h" struct uri { diff --git a/src/rtr/rtr.c b/src/rtr/rtr.c index fe050b14..1f2f69ae 100644 --- a/src/rtr/rtr.c +++ b/src/rtr/rtr.c @@ -10,6 +10,7 @@ #include #include #include +#include #include "config.h" #include "clients.h" @@ -19,17 +20,27 @@ #include "rtr/pdu.h" #include "rtr/db/vrps.h" +/* Parameters for each thread that handles client connections */ struct thread_param { int fd; pthread_t tid; struct sockaddr_storage addr; }; +/* Parameters for each thread that binds to a server address/socket */ +struct fd_node { + int id; + pthread_t tid; + SLIST_ENTRY(fd_node) next; +}; + +/* List of server sockets */ +SLIST_HEAD(server_fds, fd_node); + static int -init_addrinfo(struct addrinfo **result) +init_addrinfo(char const *hostname, char const *service, + struct addrinfo **result) { - char const *hostname; - char const *service; char *tmp; struct addrinfo hints; unsigned long parsed, port; @@ -40,9 +51,6 @@ init_addrinfo(struct addrinfo **result) /* hints.ai_socktype = SOCK_DGRAM; */ hints.ai_flags |= AI_PASSIVE; - hostname = config_get_server_address(); - service = config_get_server_port(); - if (hostname != NULL) hints.ai_flags |= AI_CANONNAME; @@ -82,7 +90,7 @@ init_addrinfo(struct addrinfo **result) * from the clients. */ static int -create_server_socket(int *result) +create_server_socket(char const *hostname, char const *service, int *result) { struct addrinfo *addrs; struct addrinfo *addr; @@ -94,7 +102,7 @@ create_server_socket(int *result) *result = 0; /* Shuts up gcc */ reuse = 1; - error = init_addrinfo(&addrs); + error = init_addrinfo(hostname, service, &addrs); if (error) return error; @@ -102,7 +110,7 @@ create_server_socket(int *result) pr_op_info( "Attempting to bind socket to address '%s', port '%s'.", (addr->ai_canonname != NULL) ? addr->ai_canonname : "any", - config_get_server_port()); + service); fd = socket(addr->ai_family, SOCK_STREAM, 0); if (fd < 0) { @@ -150,6 +158,142 @@ create_server_socket(int *result) return pr_op_err("None of the addrinfo candidates could be bound."); } +static int +fd_node_create(struct fd_node **result) +{ + struct fd_node *node; + + node = malloc(sizeof(struct fd_node)); + if (node == NULL) + return pr_enomem(); + + node->id = -1; + node->tid = -1; + + *result = node; + return 0; +} + +static int +server_fd_add(struct server_fds *fds, char const *address, char const *service) +{ + struct fd_node *node; + int error; + + node = NULL; + error = fd_node_create(&node); + if (error) + return error; + + error = create_server_socket(address, service, &node->id); + if (error) { + free(node); + return error; + } + + SLIST_INSERT_HEAD(fds, node, next); + pr_op_debug("Created server socket with FD %d.", node->id); + return 0; +} + +static void +server_fd_cleanup(struct server_fds *fds) +{ + struct fd_node *fd; + + while (!SLIST_EMPTY(fds)) { + fd = fds->slh_first; + SLIST_REMOVE_HEAD(fds, next); + close(fd->id); + free(fd); + } +} + +static int +parse_address(char const *full_address, char const *default_service, + char **address, char **service) +{ + char *ptr; + char *tmp_addr; + char *tmp_serv; + size_t tmp_addr_len; + + ptr = strrchr(full_address, '#'); + if (ptr == NULL) { + tmp_addr = strdup(full_address); + if (tmp_addr == NULL) + return pr_enomem(); + + tmp_serv = strdup(default_service); + if (tmp_serv == NULL) { + free(tmp_addr); + return pr_enomem(); + } + *address = tmp_addr; + *service = tmp_serv; + return 0; + } + + if (*(ptr + 1) == '\0') + return pr_op_err("Invalid server address '%s', can't end with '#'", + full_address); + + tmp_addr_len = strlen(full_address) - strlen(ptr); + tmp_addr = malloc(tmp_addr_len + 1); + if (tmp_addr == NULL) + return pr_enomem(); + + strncpy(tmp_addr, full_address, tmp_addr_len); + tmp_addr[tmp_addr_len] = '\0'; + + tmp_serv = strdup(ptr + 1); + if (tmp_serv == NULL) { + free(tmp_addr); + return pr_enomem(); + } + + *address = tmp_addr; + *service = tmp_serv; + return 0; +} + +static int +create_server_sockets(struct server_fds *fds) +{ + struct string_array const *addresses; + char const *default_service; + char *address; + char *service; + unsigned int i; + int error; + + default_service = config_get_server_port(); + addresses = config_get_server_address(); + if (addresses->length == 0) + return server_fd_add(fds, NULL, default_service); + + for (i = 0; i < addresses->length; i++) { + address = NULL; + service = NULL; + error = parse_address(addresses->array[i], default_service, + &address, &service); + if (error) + goto cleanup_fds; + + error = server_fd_add(fds, address, service); + /* Always release them */ + free(address); + free(service); + if (error) + goto cleanup_fds; + } + + return 0; +cleanup_fds: + server_fd_cleanup(fds); + return error; +} + enum verdict { /* No errors; continue happily. */ VERDICT_SUCCESS, @@ -300,13 +444,16 @@ handle_client_connections(int server_fd) /* Ignore SIGPIPES, they're handled apart */ ign.sa_handler = SIG_IGN; + ign.sa_flags = 0; + sigemptyset(&ign.sa_mask); sigaction(SIGPIPE, &ign, NULL); listen(server_fd, config_get_server_queue()); sizeof_client_addr = sizeof(client_addr); - pr_op_debug("Waiting for client connections..."); + pr_op_debug("Waiting for client connections at server FD %d...", + server_fd); do { client_fd = accept(server_fd, (struct sockaddr *) &client_addr, &sizeof_client_addr); @@ -338,8 +485,8 @@ handle_client_connections(int server_fd) param->fd = client_fd; param->addr = client_addr; - error = pthread_create(¶m->tid, NULL, - client_thread_cb, param); + error = pthread_create(¶m->tid, NULL, client_thread_cb, + param); if (error && error != EAGAIN) /* Error with min RTR version */ err_pdu_send_internal_error(client_fd, RTR_V0); @@ -354,6 +501,52 @@ handle_client_connections(int server_fd) return 0; /* Unreachable. */ } +static void * +server_thread_cb(void *arg) +{ + int *server_fd = (int *)(arg); + handle_client_connections(*server_fd); + return NULL; +} + +static void +server_fd_stop_threads(struct server_fds *fds) +{ + struct fd_node *node; + + SLIST_FOREACH(node, fds, next) { + if (node ->tid < 0) + continue; + close_thread(node->tid, "RTR server"); + node->tid = -1; + } +} + +static int +__handle_client_connections(struct server_fds *fds) +{ + struct fd_node *node; + int error; + + SLIST_FOREACH(node, fds, next) { + error = pthread_create(&node->tid, NULL, server_thread_cb, + &node->id); + if (error) { + server_fd_stop_threads(fds); + return error; + } + } + + SLIST_FOREACH(node, fds, next) { + error = pthread_join(node->tid, NULL); + if (error) + pr_crit("pthread_join() threw %d on an RTR server thread.", + error); + } + + return 0; +} + /* * Receive @arg to be called as a clients_foreach_cb */ @@ -392,7 +585,7 @@ int rtr_listen(void) { bool changed; - int server_fd; /* "file descriptor" */ + struct server_fds fds; /* "file descriptors" */ int error; error = clients_db_init(); @@ -407,20 +600,21 @@ rtr_listen(void) goto revert_clients_db; /* Error 0 it's ok */ } - error = create_server_socket(&server_fd); + SLIST_INIT(&fds); + error = create_server_sockets(&fds); if (error) goto revert_clients_db; error = updates_daemon_start(); if (error) - goto revert_server_socket; + goto revert_server_sockets; - error = handle_client_connections(server_fd); + error = __handle_client_connections(&fds); end_clients(); updates_daemon_destroy(); -revert_server_socket: - close(server_fd); +revert_server_sockets: + server_fd_cleanup(&fds); revert_clients_db: clients_db_destroy(join_thread, NULL); return error; diff --git a/src/str.c b/src/str_token.c similarity index 76% rename from src/str.c rename to src/str_token.c index 58771667..b3bd6df3 100644 --- a/src/str.c +++ b/src/str_token.c @@ -1,4 +1,4 @@ -#include "str.h" +#include "str_token.h" #include #include @@ -87,13 +87,17 @@ string_tokenizer_next(struct string_tokenizer *tokenizer) if (end == tokenizer->str_len) return false; - if (end != 0) { /* end is pointing to a slash. */ + /* Ignore "empty" tokens */ + while (tokenizer->str[end] == tokenizer->separator) { end++; - if (end == tokenizer->str_len) + if (end == tokenizer->str_len) { + tokenizer->start = end; + tokenizer->end = end; return false; + } + } - tokenizer->start = end; - } /* otherwise it's pointing to the beginning of the string. */ + tokenizer->start = end; for (; end < tokenizer->str_len; end++) if (tokenizer->str[end] == tokenizer->separator) @@ -115,3 +119,26 @@ token_equals(struct string_tokenizer *t1, struct string_tokenizer *t2) ? (memcmp(t1->str + t1->start, t2->str + t2->start, t1len) == 0) : false; } + +int +token_read(struct string_tokenizer *tokenizer, char **token) +{ + return string_clone(tokenizer->str + tokenizer->start, + tokenizer->end - tokenizer->start, token); +} + +size_t +token_count(struct string_tokenizer *tokenizer) +{ + struct string_tokenizer copy; + size_t count; + + string_tokenizer_init(©, tokenizer->str, tokenizer->str_len, + tokenizer->separator); + + count = 0; + while (string_tokenizer_next(©)) + count++; + + return count; +} diff --git a/src/str.h b/src/str_token.h similarity index 76% rename from src/str.h rename to src/str_token.h index b8d00a38..12da0fe4 100644 --- a/src/str.h +++ b/src/str_token.h @@ -1,5 +1,5 @@ -#ifndef SRC_STR_H_ -#define SRC_STR_H_ +#ifndef SRC_STR_TOKEN_H_ +#define SRC_STR_TOKEN_H_ #include #include @@ -9,7 +9,7 @@ int ia5s2string(ASN1_IA5STRING *, char **); int BN2string(BIGNUM *, char **); -/* This file is named "str.h" because "string.h" collides with . */ +/* This file is named "str_token.h" because "string.h" collides with . */ /** * Do not modify fields directly; this should be private. @@ -33,5 +33,7 @@ void string_tokenizer_init(struct string_tokenizer *, char const *, size_t, unsigned char); bool string_tokenizer_next(struct string_tokenizer *); bool token_equals(struct string_tokenizer *, struct string_tokenizer *); +int token_read(struct string_tokenizer *, char **); +size_t token_count(struct string_tokenizer *); -#endif /* SRC_STR_H_ */ +#endif /* SRC_STR_TOKEN_H_ */ diff --git a/src/uri.c b/src/uri.c index dad25e60..1b9e05d0 100644 --- a/src/uri.c +++ b/src/uri.c @@ -4,7 +4,7 @@ #include "common.h" #include "config.h" #include "log.h" -#include "str.h" +#include "str_token.h" /* Expected URI types */ enum rpki_uri_type { diff --git a/test/rsync_test.c b/test/rsync_test.c index 943c9069..ff358428 100644 --- a/test/rsync_test.c +++ b/test/rsync_test.c @@ -4,7 +4,7 @@ #include "log.c" #include "impersonator.c" -#include "str.c" +#include "str_token.c" #include "uri.c" #include "rsync/rsync.c" diff --git a/test/tal_test.c b/test/tal_test.c index 7f8a9fe8..797b15f6 100644 --- a/test/tal_test.c +++ b/test/tal_test.c @@ -10,7 +10,7 @@ #include "line_file.c" #include "log.c" #include "state.h" -#include "str.c" +#include "str_token.c" #include "uri.c" #include "random.c" #include "crypto/base64.c"