rtr_server_SOURCES += rtr/primitive_reader.h
rtr_server_SOURCES += rtr/rtr.c
rtr_server_SOURCES += rtr/rtr.h
-rtr_server_SOURCES += log.h
rtr_server_SOURCES += file.c
rtr_server_SOURCES += file.h
-rtr_server_SOURCES += types.h
rtr_server_SOURCES += configuration.c
rtr_server_SOURCES += configuration.h
-rtr_server_SOURCES += str_utils.c
-rtr_server_SOURCES += str_utils.h
rtr_server_LDADD = ${JANSSON_LIBS}
\ No newline at end of file
#ifndef _SRC_COMMON_H_
#define _SRC_COMMON_H_
+#include <stdio.h>
#include <string.h>
/* __BEGIN_DECLS should be used at the beginning of your declarations,
#define pr_debug0(msg) printf("Debug: " msg "\n");
#define pr_debug(msg, ...) printf("Debug: " msg "\n", ##__VA_ARGS__);
+#define log_err(text, ...) fprintf(stderr, text "\n", ##__VA_ARGS__)
+#define log_err0(text) fprintf(stderr, text "\n")
+
#endif /* _SRC_COMMON_H_ */
#include "configuration.h"
-#include <string.h>
+#include <stdio.h>
+#include <stdbool.h>
#include <stdlib.h>
#include <errno.h>
-#include <stdio.h>
#include <regex.h>
+
+#include <arpa/inet.h>
+#include <sys/socket.h>
+
#include <jansson.h>
#include "common.h"
#include "file.h"
-#include "str_utils.h"
#define OPTNAME_LISTEN "listen"
-#define OPTNAME_LISTEN_IPV4 "ipv4_server_addr"
+#define OPTNAME_LISTEN_SERVER "server_name"
+#define OPTNAME_LISTEN_PORT "server_port"
static int json_to_config(json_t *, struct rtr_config *);
-static int handle_listen_config(json_t *, struct ipv4_transport_addr *);
+static int handle_listen_config(json_t *, struct rtr_config *);
static json_t *load_json(const char *);
+void
+free_rtr_config(struct rtr_config *config)
+{
+ if (!config)
+ return;
+
+ if (config->host_address)
+ freeaddrinfo(config->host_address);
+
+ free(config);
+}
+
+static bool
+endsWith(char *string, char *suffix)
+{
+ size_t strilen;
+ size_t suflen;
+ if (!string || !suffix)
+ return false;
+
+ strilen = strlen(string);
+ suflen = strlen(suffix);
+
+ return ((strilen >= suflen) && (0 == strcmp(string + strilen - suflen, suffix)));
+}
int
read_config_from_file(char *json_file_path, struct rtr_config **result)
if (!root_json)
return -ENOENT;
- config = malloc(sizeof(*config));
+ config = malloc(sizeof(struct rtr_config));
if (!config)
return -ENOMEM;
error = json_to_config(root_json, config);
- if (error != 0)
+ if (error)
free(config);
*result = config;
check_duplicates(bool *found, char *section)
{
if (*found)
- log_info("Note: I found multiple '%s' sections.", section);
+ log_err("Note: I found multiple '%s' sections.", section);
*found = true;
}
json_object_foreach(json, key, value) {
if(strcasecmp(OPTNAME_LISTEN, key) == 0) {
check_duplicates(&listen_found, OPTNAME_LISTEN);
- error = handle_listen_config(value, &config->ipv4_server_addr);
+ error = handle_listen_config(value, config);
}
}
return error;
}
+static int
+hostname_to_ip(const char *hostname, struct addrinfo **result)
+{
+ int rv;
+ struct addrinfo hints, *servinfo;
+
+ memset(&hints, 0 , sizeof hints);
+ hints.ai_family = AF_UNSPEC;
+// hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_flags |= AI_CANONNAME;
+
+
+ if ((rv = getaddrinfo(hostname, NULL, &hints, &servinfo)) != 0){
+ printf("getaddrinfo: [%d] - %s\n", rv, gai_strerror(rv)); // TODO change to print error or something like that
+ return -EINVAL;
+ }
+
+ *result = servinfo;
+
+ return 0;
+}
static int
-handle_listen_config(json_t *json, struct ipv4_transport_addr *ipv4_server_addr)
+handle_listen_config(json_t *json, struct rtr_config *config)
{
- bool listen_ipv4_found = false;
+ int error;
+ bool listen_servername_found = false;
+ bool listen_port_found = false;
const char *key;
json_t *value;
}
json_object_foreach(json, key, value) {
- if (strcasecmp(OPTNAME_LISTEN_IPV4, key) == 0) {
- check_duplicates(&listen_ipv4_found, OPTNAME_LISTEN_IPV4);
+ if (strcasecmp(OPTNAME_LISTEN_SERVER, key) == 0) {
+ check_duplicates(&listen_servername_found, OPTNAME_LISTEN_SERVER);
if (json_typeof(value) != JSON_STRING) {
log_err("Invalid value for key '%s'", key);
return -EINVAL;
}
- str_to_addr4_port(json_string_value(value), ipv4_server_addr);
+ error = hostname_to_ip(json_string_value(value), &config->host_address);
+ if (error)
+ return error;
+
+ } else if (strcasecmp(OPTNAME_LISTEN_PORT, key) == 0) {
+ check_duplicates(&listen_port_found, OPTNAME_LISTEN_PORT);
+ if (json_typeof(value) != JSON_INTEGER) {
+ log_err("Invalid value for key '%s'", key);
+ return -EINVAL;
+ }
+
+ config->host_port = (__u16) json_integer_value(value);
}
}
#ifndef _SRC_CONFIGURATION_H_
#define _SRC_CONFIGURATION_H_
-#include "types.h"
+#include "netdb.h"
+#include <asm/types.h>
struct rtr_config {
/** The listener address of the RTR server. */
- struct ipv4_transport_addr ipv4_server_addr;
+ struct addrinfo *host_address;
+ /** The listener port of the RTR server. */
+ __u16 host_port;
};
-
+void free_rtr_config(struct rtr_config *);
int read_config_from_file(char *, struct rtr_config **);
+++ /dev/null
-#ifndef _SRC_LOG_H
-#define _SRC_LOG_H
-
-
-#include <stdio.h>
-#define log_debug(text, ...) printf(text "\n", ##__VA_ARGS__)
-#define log_info(text, ...) log_debug(text, ##__VA_ARGS__)
-#define log_err(text, ...) fprintf(stderr, text "\n", ##__VA_ARGS__)
-#define log_err0(text) fprintf(stderr, text "\n")
-
-
-
-#endif /* _SRC_LOG_H */
if (err)
return err;
- err = rtr_listen(&config->ipv4_server_addr.l3,
- config->ipv4_server_addr.l4);
+ err = rtr_listen(config->host_address, config->host_port);
if (config)
- free(config);
+ free_rtr_config(config);
if (err)
return err;
#include <err.h>
#include <errno.h>
+#include <netdb.h>
#include <pthread.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#include <arpa/inet.h>
#include "../common.h"
-#include "../types.h"
#include "pdu.h"
-/*
- * Creates the socket that will stay put and wait for new connections started
- * from the clients.
- */
static int
-create_server_socket(struct in_addr *server_addr, __u16 port)
+bind_server_socket6(int socket, struct sockaddr_in6 *host_addr, __u16 port)
{
- int fd; /* "file descriptor" */
- struct sockaddr_in address;
int err;
+ struct sockaddr_in6 address;
- fd = socket(AF_INET, SOCK_STREAM, 0);
- if (fd < 0) {
+ memset(&address, 0, sizeof(address));
+ address.sin6_family = AF_INET6;
+ address.sin6_addr.__in6_u = host_addr->sin6_addr.__in6_u;
+ address.sin6_port = htons(port);
+
+ if (bind(socket, (struct sockaddr *)&address, sizeof(address)) < 0) {
err = errno;
- warn("Error opening socket");
+ warn("Could not bind the address. errno : %d", -abs(err));
return -abs(err);
}
+ return 0;
+}
+
+static int
+bind_server_socket4(int socket, struct sockaddr_in *host_addr, __u16 port)
+{
+ int err;
+ struct sockaddr_in address;
+
memset(&address, 0, sizeof(address));
address.sin_family = AF_INET;
- address.sin_addr.s_addr = server_addr->s_addr;
+ address.sin_addr.s_addr = host_addr->sin_addr.s_addr;
address.sin_port = htons(port);
- if (bind(fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
+
+ if (bind(socket, (struct sockaddr *)&address, sizeof(address)) < 0) {
err = errno;
- warn("Could not bind the address");
- close(fd);
+ warn("Could not bind the address. errno : %d", -abs(err));
return -abs(err);
}
+ return 0;
+}
+
+static void
+log_binded_server_socket(struct addrinfo *host, __u16 port)
+{
+ char hostaddr[INET6_ADDRSTRLEN];
+ struct sockaddr_in *h4;
+ struct sockaddr_in6 *h6;
+
+ memset(&hostaddr, 0, INET6_ADDRSTRLEN);
+ switch(host->ai_family) {
+ case AF_INET:
+ h4 = (struct sockaddr_in *) host->ai_addr;
+ inet_ntop(host->ai_family, &h4->sin_addr, (char * restrict) &hostaddr, sizeof(hostaddr));
+ pr_debug("Listening %s#%d", hostaddr, port);
+ break;
+ case AF_INET6:
+ h6 = (struct sockaddr_in6 *) host->ai_addr;
+ inet_ntop(host->ai_family, &h6->sin6_addr, (char * restrict) &hostaddr, sizeof(hostaddr));
+ pr_debug("Listening [%s]#%d", hostaddr, port);
+ break;
+ default:
+ warn("Unknown AI_FAMILY type: %d", host->ai_family);
+ }
+}
+
+/*
+ * Creates the socket that will stay put and wait for new connections started
+ * from the clients.
+ */
+static int
+create_server_socket(struct addrinfo *server_addr, __u16 port)
+{
+ int fd; /* "file descriptor" */
+ struct addrinfo *tmp;
+ int err;
+
+ if (server_addr == NULL){
+ warn("A server address must be present to bind a socket");
+ return -EINVAL;
+ }
+
+ for (tmp = server_addr; tmp != NULL; tmp = tmp->ai_next) {
+ err = 0;
+ fd = socket(tmp->ai_family, SOCK_STREAM, 0);
+ if (fd < 0) {
+ err = errno;
+ err = -abs(err);
+ warn("Error opening socket. errno : %d", err);
+ continue;
+ }
+
+ switch(tmp->ai_family) {
+ case AF_INET:
+ err = bind_server_socket4(fd, (struct sockaddr_in *) tmp->ai_addr, port);
+ if (err)
+ close(fd);
+ break;
+ case AF_INET6:
+ err = bind_server_socket6(fd, (struct sockaddr_in6 *) tmp->ai_addr, port);
+ if (err)
+ close(fd);
+ break;
+ default:
+ close(fd);
+ warn("Can't handle ai_family type: %d", tmp->ai_family);
+ err = -EINVAL;
+ }
+
+ if (!err) {
+ log_binded_server_socket(tmp, port);
+ break;
+ }
+ }
+
+ if (err)
+ return err;
+
return fd;
}
* This function blocks.
*/
int
-rtr_listen(struct in_addr *server_addr, __u16 port)
+rtr_listen(struct addrinfo *server_addr, __u16 port)
{
int server_fd; /* "file descriptor" */
#ifndef RTR_RTR_H_
#define RTR_RTR_H_
+#include <netdb.h>
+#include <asm/types.h>
+
#include "../common.h"
-#include "../types.h"
__BEGIN_DECLS
-int rtr_listen(struct in_addr *, __u16);
+int rtr_listen(struct addrinfo *, __u16);
__END_DECLS
#endif /* RTR_RTR_H_ */
+++ /dev/null
-#include "str_utils.h"
-
-#include <string.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <stdio.h>
-#include <arpa/inet.h>
-#include <regex.h>
-#include "types.h"
-
-
-#define MAX_PORT 0xFFFF
-
-int
-validate_int(const char *str)
-{
- regex_t integer_regex;
- int error;
-
- if (!str) {
- log_err0("Programming error: 'str' is NULL.");
- return -EINVAL;
- }
-
- /* It seems this RE implementation doesn't understand '+'. */
- if (regcomp(&integer_regex, "^[0-9][0-9]*", 0)) {
- log_err0("Warning: Integer regex didn't compile.");
- log_err0("(I will be unable to validate integer inputs.)");
- regfree(&integer_regex);
- /*
- * Don't punish the user over our incompetence.
- * If the number is valid, this will not bother the user.
- * Otherwise strtoull() will just read a random value, but then
- * the user is at fault.
- */
- return 0;
- }
-
- error = regexec(&integer_regex, str, 0, NULL, 0);
- if (error) {
- log_err("'%s' is not a number. (error code %d)", str, error);
- regfree(&integer_regex);
- return error;
- }
-
- regfree(&integer_regex);
- return 0;
-}
-
-static int
-str_to_ull(const char *str, char **endptr,
- const unsigned long long int min,
- const unsigned long long int max,
- unsigned long long int *result)
-{
- unsigned long long int parsed;
- int error;
-
- error = validate_int(str);
- if (error)
- return error;
-
- errno = 0;
- parsed = strtoull(str, endptr, 10);
- if (errno) {
- log_err("Parsing of '%s' threw error code %d.", str, errno);
- return errno;
- }
-
- if (parsed < min || max < parsed) {
- log_err("'%s' is out of bounds (%llu-%llu).", str, min, max);
- return -EINVAL;
- }
-
- *result = parsed;
- return 0;
-}
-
-int
-str_to_bool(const char *str, __u8 *bool_out)
-{
- if (strcasecmp(str, "true") == 0
- || strcasecmp(str, "1") == 0
- || strcasecmp(str, "yes") == 0
- || strcasecmp(str, "on") == 0) {
- *bool_out = true;
- return 0;
- }
-
- if (strcasecmp(str, "false") == 0
- || strcasecmp(str, "0") == 0
- || strcasecmp(str, "no") == 0
- || strcasecmp(str, "off") == 0) {
- *bool_out = false;
- return 0;
- }
-
- log_err("Cannot parse '%s' as a bool (true|false|1|0|yes|no|on|off).",
- str);
- return -EINVAL;
-}
-
-int
-str_to_u8(const char *str, __u8 *u8_out, __u8 min, __u8 max)
-{
- unsigned long long int result;
- int error;
-
- error = str_to_ull(str, NULL, min, max, &result);
-
- *u8_out = result;
- return error;
-}
-
-int
-str_to_u16(const char *str, __u16 *u16_out, __u16 min, __u16 max)
-{
- unsigned long long int result;
- int error;
-
- error = str_to_ull(str, NULL, min, max, &result);
-
- *u16_out = result;
- return error;
-}
-
-int
-str_to_u32(const char *str, __u32 *u32_out, __u32 min, __u32 max)
-{
- unsigned long long int result;
- int error;
-
- error = str_to_ull(str, NULL, min, max, &result);
-
- *u32_out = result;
- return error;
-}
-
-int
-str_to_u64(const char *str, __u64 *u64_out, __u64 min, __u64 max)
-{
- unsigned long long int result;
- int error;
-
- error = str_to_ull(str, NULL, min, max, &result);
-
- *u64_out = result;
- return error;
-}
-
-#define STR_MAX_LEN 2048
-int
-str_to_u16_array(const char *str, __u16 **array_out, size_t *array_len_out)
-{
- /* strtok corrupts the string, so we'll be using this copy instead. */
- char str_copy[STR_MAX_LEN];
- char *token;
- __u16 *array;
- size_t array_len;
-
- /* Validate str and copy it to the temp buffer. */
- if (strlen(str) + 1 > STR_MAX_LEN) {
- log_err("'%s' is too long for this poor, limited parser...", str);
- return -EINVAL;
- }
- strcpy(str_copy, str);
-
- /* Count the number of ints in the string. */
- array_len = 0;
- token = strtok(str_copy, ",");
- while (token) {
- array_len++;
- token = strtok(NULL, ",");
- }
-
- if (array_len == 0) {
- log_err("'%s' seems to be an empty list, which is not supported.", str);
- return -EINVAL;
- }
-
- /* Build the result. */
- array = malloc(array_len * sizeof(*array));
- if (!array) {
- log_err0("Memory allocation failed. Cannot parse the input...");
- return -ENOMEM;
- }
-
- strcpy(str_copy, str);
-
- array_len = 0;
- token = strtok(str_copy, ",");
- while (token) {
- int error;
-
- error = str_to_u16(token, &array[array_len], 0, 0xFFFF);
- if (error) {
- free(array);
- return error; /* Error msg already printed. */
- }
-
- array_len++;
- token = strtok(NULL, ",");
- }
-
- /* Finish. */
- *array_out = array;
- *array_len_out = array_len;
- return 0;
-}
-
-int
-str_to_addr4(const char *str, struct in_addr *result)
-{
- if (!inet_pton(AF_INET, str, result)) {
- log_err("Cannot parse '%s' as an IPv4 address.", str);
- return -EINVAL;
- }
- return 0;
-}
-
-int
-str_to_addr6(const char *str, struct in6_addr *result)
-{
- if (!inet_pton(AF_INET6, str, result)) {
- log_err("Cannot parse '%s' as an IPv6 address.", str);
- return -EINVAL;
- }
- return 0;
-}
-
-#undef STR_MAX_LEN
-#define STR_MAX_LEN (INET_ADDRSTRLEN + 1 + 5) /* [addr + null chara] + # + port */
-int
-str_to_addr4_port(const char *str, struct ipv4_transport_addr *addr)
-{
- const char *FORMAT = "<IPv4 address>#<port> (eg. 203.0.113.8#80)";
- /* strtok corrupts the string, so we'll be using this copy instead. */
- char str_copy[STR_MAX_LEN];
- char *token;
- int error;
-
- if (strlen(str) + 1 > STR_MAX_LEN) {
- log_err("'%s' is too long for this poor, limited parser...", str);
- return -EINVAL;
- }
- strcpy(str_copy, str);
-
- token = strtok(str_copy, "#");
- if (!token) {
- log_err("Cannot parse '%s' as a %s.", str, FORMAT);
- return -EINVAL;
- }
-
- error = str_to_addr4(token, &addr->l3);
- if (error)
- return error;
-
- token = strtok(NULL, "#");
- if (!token) {
- log_err("'%s' does not seem to contain a port (format: %s).", str, FORMAT);
- return -EINVAL;
- }
- return str_to_u16(token, &addr->l4, 0, MAX_PORT); /* Error msg already printed. */
-}
-
-#undef STR_MAX_LEN
-#define STR_MAX_LEN (INET6_ADDRSTRLEN + 1 + 5) /* [addr + null chara] + # + port */
-int
-str_to_addr6_port(const char *str, struct ipv6_transport_addr *addr)
-{
- const char *FORMAT = "<IPv6 address>#<port> (eg. 2001:db8::1#96)";
- /* strtok corrupts the string, so we'll be using this copy instead. */
- char str_copy[STR_MAX_LEN];
- char *token;
- int error;
-
- if (strlen(str) + 1 > STR_MAX_LEN) {
- log_err("'%s' is too long for this poor, limited parser...", str);
- return -EINVAL;
- }
- strcpy(str_copy, str);
-
- token = strtok(str_copy, "#");
- if (!token) {
- log_err("Cannot parse '%s' as a %s.", str, FORMAT);
- return -EINVAL;
- }
-
- error = str_to_addr6(token, &addr->l3);
- if (error)
- return error;
-
- token = strtok(NULL, "#");
- if (!token) {
- log_err("'%s' does not seem to contain a port (format: %s).", str, FORMAT);
- return -EINVAL;
- }
- return str_to_u16(token, &addr->l4, 0, MAX_PORT); /* Error msg already printed. */
-}
-
-bool
-endsWith(char *string, char *suffix)
-{
- size_t strilen;
- size_t suflen;
- if (!string || !suffix)
- return false;
-
- strilen = strlen(string);
- suflen = strlen(suffix);
-
- return ((strilen >= suflen) && (0 == strcmp(string + strilen - suflen, suffix)));
-}
+++ /dev/null
-#ifndef _SRC_STR_UTILS_H
-#define _SRC_STR_UTILS_H
-
-/**
- * @file
- * Two-liners (since you need to check the return value) for string-to-something
- * else conversions.
- * This is very noisy on the console on purpose because it is only used by the
- * parser of the userspace app's arguments.
- */
-
-#include "types.h"
-
-/** Maximum storable value on a __u8. */
-#define MAX_U8 0xFFU
-/** Maximum storable value on a __u16. */
-#define MAX_U16 0xFFFFU
-/** Maximum storable value on a __u32. */
-#define MAX_U32 0xFFFFFFFFU
-/** Maximum storable value on a __u64. */
-#define MAX_U64 0xFFFFFFFFFFFFFFFFU
-
-
-/**
- * Converts "str" to a IPv4 address. Stores the result in "result".
- *
- * Useful mainly in code common to kernelspace and userspace, since their conversion functions
- * differ, but meant to be used everywhere to strip the parameters from in4_pton() we don't want.
- */
-int str_to_addr4(const char *, struct in_addr *);
-/**
- * Converts "str" to a IPv6 address. Stores the result in "result".
- *
- * Useful mainly in code common to kernelspace and userspace, since their conversion functions
- * differ, but meant to be used everywhere to strip the parameters from in6_pton() we don't want.
- */
-int str_to_addr6(const char *, struct in6_addr *);
-
-/**
- * Parses @str as a boolean value, which it then copies to @out.
- */
-int str_to_bool(const char *, __u8 *);
-
-int validate_int(const char *);
-
-/**
- * Parses @str" as a number, which it then copies to @out.
- * Refuses to succeed if @out is less than @min or higher than @max.
- */
-int str_to_u8(const char *, __u8 *, __u8, __u8);
-int str_to_u16(const char *, __u16 *, __u16, __u16);
-int str_to_u32(const char *, __u32 *, __u32, __u32);
-int str_to_u64(const char *, __u64 *, __u64, __u64);
-
-/**
- * Parses @str as a comma-separated array of __u16s, which it then copies to
- * @out.
- * It sets @out_len as @out's length in elements (not bytes).
- */
-int str_to_u16_array(const char *, __u16 **, size_t *);
-
-/**
- * Parses @str as a '#' separated l3-address and l4-identifier, which it then
- * copies to @out".
- */
-int str_to_addr4_port(const char *, struct ipv4_transport_addr *);
-int str_to_addr6_port(const char *, struct ipv6_transport_addr *);
-
-bool endsWith(char *, char *);
-
-#endif /* _JOOL_COMM_STR_UTILS_H */
+++ /dev/null
-#ifndef _SRC_COMMON_TYPES_H
-#define _SRC_COMMON_TYPES_H
-
-/**
- * @file
- * The NAT64's core data types. Structures used all over the code.
- *
- * Both the kernel module and the userspace application can see this file.
- */
-
-#include <asm/types.h>
-#include <linux/types.h>
-#include <stdbool.h>
-#include <arpa/inet.h>
-#include "log.h"
-
-
-
-/**
- * A layer-3 (IPv4) identifier attached to a layer-4 identifier.
- * Because they're paired all the time in this project.
- */
-struct ipv4_transport_addr {
- /** The layer-3 identifier. */
- struct in_addr l3;
- /** The layer-4 identifier (Either the TCP/UDP port or the ICMP id). */
- __u16 l4;
-};
-
-/**
- * A layer-3 (IPv6) identifier attached to a layer-4 identifier.
- * Because they're paired all the time in this project.
- */
-struct ipv6_transport_addr {
- /** The layer-3 identifier. */
- struct in6_addr l3;
- /** The layer-4 identifier (Either the TCP/UDP port or the ICMP id). */
- __u16 l4;
-};
-
-#endif /* _COMMON_TYPES_H */