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
#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"
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 */
{
.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 '<address>[#<port/service>]'.",
+ .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",
.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",
.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 */
* 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;
free(rpki_config.local_repository);
revert_port:
free(rpki_config.server.port);
+revert_address:
+ string_array_cleanup(&rpki_config.server.address);
return error;
}
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 *
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);
#include <string.h>
#include "log.h"
+#include "str_token.h"
#include "config/str.h"
int
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)) {
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)
{
.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>",
};
#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"
#include "config.h"
#include "log.h"
#include "reqs_errors.h"
-#include "str.h"
+#include "str_token.h"
#include "thread_var.h"
struct uri {
#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;
/* 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;
* 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;
*result = 0; /* Shuts up gcc */
reuse = 1;
- error = init_addrinfo(&addrs);
+ error = init_addrinfo(hostname, service, &addrs);
if (error)
return error;
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) {
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,
/* 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);
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);
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
*/
rtr_listen(void)
{
bool changed;
- int server_fd; /* "file descriptor" */
+ struct server_fds fds; /* "file descriptors" */
int error;
error = clients_db_init();
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;
-#include "str.h"
+#include "str_token.h"
#include <errno.h>
#include <string.h>
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)
? (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;
+}
-#ifndef SRC_STR_H_
-#define SRC_STR_H_
+#ifndef SRC_STR_TOKEN_H_
+#define SRC_STR_TOKEN_H_
#include <stdbool.h>
#include <stddef.h>
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.
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_ */
#include "common.h"
#include "config.h"
#include "log.h"
-#include "str.h"
+#include "str_token.h"
/* Expected URI types */
enum rpki_uri_type {
#include "log.c"
#include "impersonator.c"
-#include "str.c"
+#include "str_token.c"
#include "uri.c"
#include "rsync/rsync.c"
#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"