]> git.ipfire.org Git - thirdparty/FORT-validator.git/commitdiff
Allow to bind to multiple addresses using a CSV at server.address
authorpcarana <pc.moreno2099@gmail.com>
Mon, 13 Jul 2020 19:08:59 +0000 (14:08 -0500)
committerpcarana <pc.moreno2099@gmail.com>
Mon, 13 Jul 2020 19:08:59 +0000 (14:08 -0500)
13 files changed:
src/Makefile.am
src/cert_stack.c
src/config.c
src/config.h
src/config/string_array.c
src/object/certificate.c
src/rsync/rsync.c
src/rtr/rtr.c
src/str_token.c [moved from src/str.c with 76% similarity]
src/str_token.h [moved from src/str.h with 76% similarity]
src/uri.c
test/rsync_test.c
test/tal_test.c

index 103f279043cbc7b6c15cda14f9eb6bad8480e19a..b03cb514ec1a69435de933aff2d0c536c5b48865 100644 (file)
@@ -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
index b659dc005098a31c5568c49d0f0af0c19d133b2b..e2b09f03f267ad5dd91282680f493ccb50fb9405 100644 (file)
@@ -3,7 +3,7 @@
 #include <sys/queue.h>
 
 #include "resource.h"
-#include "str.h"
+#include "str_token.h"
 #include "thread_var.h"
 #include "data_structure/array_list.h"
 #include "object/name.h"
index 809450cb02749a83c06d0076da16480f38e71fb6..c8b358e8a30cddb9130b5b8b5a8f82b9abce9254 100644 (file)
@@ -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 = &gt_string,
+               .type = &gt_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 '<address>[#<port/service>]'.",
+               .min = 0,
+               .max = 50,
        }, {
                .id = 5001,
                .name = "server.port",
                .type = &gt_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 *
index 0a2230dd0b02d242a86d62c12e38daa6ae015b8c..0e7858624ed23c2bc4ba4c1e8e8b9db777f428d5 100644 (file)
@@ -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);
index cc4bfac01cfcbebe740e969e1e7b9fef8124fc8e..0817d45980aa9970dc1a722c877a4dd3ee4e3221 100644 (file)
@@ -6,6 +6,7 @@
 #include <string.h>
 
 #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 = "<sequence of strings>",
 };
index 4175cfb59e9003d37ea632af986554be6201891f..1e8ea519ffb2ebe067b620a5e21b1a293c516127 100644 (file)
@@ -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"
index 24e14c17b725721ed376bcccec83a01cee9d2c4c..6c7f9c55ba3a05583bfbd7b620248a2cdea2f369 100644 (file)
@@ -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 {
index fe050b14cf7b9b0b8160a42d49178850685130db..1f2f69ae1060eea35dd947f9cf9f258b2ea28da7 100644 (file)
@@ -10,6 +10,7 @@
 #include <string.h>
 #include <unistd.h>
 #include <arpa/inet.h>
+#include <sys/queue.h>
 
 #include "config.h"
 #include "clients.h"
 #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(&param->tid, NULL,
-                   client_thread_cb, param);
+               error = pthread_create(&param->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;
similarity index 76%
rename from src/str.c
rename to src/str_token.c
index 587716670445fad9933a3d82510182842a16c565..b3bd6df33ca2cbacd516af88b45c851f83366db4 100644 (file)
--- a/src/str.c
@@ -1,4 +1,4 @@
-#include "str.h"
+#include "str_token.h"
 
 #include <errno.h>
 #include <string.h>
@@ -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(&copy, tokenizer->str, tokenizer->str_len,
+           tokenizer->separator);
+
+       count = 0;
+       while (string_tokenizer_next(&copy))
+               count++;
+
+       return count;
+}
similarity index 76%
rename from src/str.h
rename to src/str_token.h
index b8d00a384e96138462a99888d2abe37738f24a9c..12da0fe422ba059d7089deba9bcd71f622ddb121 100644 (file)
--- a/src/str.h
@@ -1,5 +1,5 @@
-#ifndef SRC_STR_H_
-#define SRC_STR_H_
+#ifndef SRC_STR_TOKEN_H_
+#define SRC_STR_TOKEN_H_
 
 #include <stdbool.h>
 #include <stddef.h>
@@ -9,7 +9,7 @@
 int ia5s2string(ASN1_IA5STRING *, char **);
 int BN2string(BIGNUM *, char **);
 
-/* This file is named "str.h" because "string.h" collides with <string.h>. */
+/* This file is named "str_token.h" because "string.h" collides with <string.h>. */
 
 /**
  * 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_ */
index dad25e603a0adb431c9ce53526adbc1435736f2e..1b9e05d0f75c517a17a21c282e56b63a2483bd1f 100644 (file)
--- 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 {
index 943c9069dd54c4104defa92264f89906e46e6a59..ff358428f01bdcba0eac7dea066c0ba927a40165 100644 (file)
@@ -4,7 +4,7 @@
 
 #include "log.c"
 #include "impersonator.c"
-#include "str.c"
+#include "str_token.c"
 #include "uri.c"
 #include "rsync/rsync.c"
 
index 7f8a9fe8517c6d584835a3b466b40cf4e86ba2d9..797b15f6ceb6bd9cc9210d988d55b1509a73367b 100644 (file)
@@ -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"