bin_PROGRAMS = rtr_server
rtr_server_SOURCES = main.c
-rtr_server_SOURCES += common.h
-rtr_server_SOURCES += rtr/pdu.c
-rtr_server_SOURCES += rtr/pdu.h
-rtr_server_SOURCES += rtr/pdu_handler.c
-rtr_server_SOURCES += rtr/pdu_handler.h
-rtr_server_SOURCES += rtr/primitive_reader.c
-rtr_server_SOURCES += rtr/primitive_reader.h
-rtr_server_SOURCES += rtr/rtr.c
-rtr_server_SOURCES += rtr/rtr.h
-rtr_server_SOURCES += file.c
-rtr_server_SOURCES += file.h
-rtr_server_SOURCES += configuration.c
-rtr_server_SOURCES += configuration.h
+rtr_server_SOURCES += common.c common.h
+rtr_server_SOURCES += configuration.c configuration.h
+
+rtr_server_SOURCES += rtr/pdu_handler.c rtr/pdu_handler.h
+rtr_server_SOURCES += rtr/pdu.c rtr/pdu.h
+rtr_server_SOURCES += rtr/primitive_reader.c rtr/primitive_reader.h
+rtr_server_SOURCES += rtr/rtr.c rtr/rtr.h
rtr_server_LDADD = ${JANSSON_LIBS}
\ No newline at end of file
--- /dev/null
+#include "common.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+char *
+str_clone(char const *original)
+{
+ char *result;
+ result = malloc(strlen(original) + 1);
+ if (result != NULL)
+ strcpy(result, original);
+ return result;
+}
#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,
so that C++ compilers don't mangle their names. Use __END_DECLS at
the end of C declarations. */
#define EUNIMPLEMENTED 566456
-#define warnxerror0(error, msg) \
- warnx(msg ": %s", strerror(error))
-#define warnxerrno0(msg) \
- warnxerror0(errno, msg)
-#define warnxerror(error, msg, ...) \
- warnx(msg ": %s", ##__VA_ARGS__, strerror(error))
-#define warnxerrno(msg, ...) \
- warnxerror(errno, msg, ##__VA_ARGS__)
-
-#define pr_debug0(msg) printf("Debug: " msg "\n");
-#define pr_debug(msg, ...) printf("Debug: " msg "\n", ##__VA_ARGS__);
+/*
+ * FYI: The error functions are warn() and warnx().
+ * warn() automatically appends the errno string message (strerror()), warnx()
+ * does not.
+ */
-#define log_err(text, ...) fprintf(stderr, text "\n", ##__VA_ARGS__)
-#define log_err0(text) fprintf(stderr, text "\n")
+char *str_clone(char const *);
#endif /* _SRC_COMMON_H_ */
#include "configuration.h"
-#include <stdio.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <regex.h>
-
-#include <arpa/inet.h>
#include <sys/socket.h>
-
+#include <err.h>
+#include <errno.h>
#include <jansson.h>
+#include <string.h>
#include "common.h"
-#include "file.h"
-
-#define OPTNAME_LISTEN "listen"
-#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 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);
-}
+#define OPTNAME_LISTEN "listen"
+#define OPTNAME_LISTEN_ADDRESS "address"
+#define OPTNAME_LISTEN_PORT "port"
-static bool
-endsWith(char *string, char *suffix)
-{
- size_t strilen;
- size_t suflen;
- if (!string || !suffix)
- return false;
+#define DEFAULT_ADDR NULL
+#define DEFAULT_PORT "323"
- strilen = strlen(string);
- suflen = strlen(suffix);
+struct rtr_config {
+ /** The listener address of the RTR server. */
+ struct addrinfo *address;
+ /** Stored aside only for printing purposes. */
+ char *port;
+} config;
- return ((strilen >= suflen) && (0 == strcmp(string + strilen - suflen, suffix)));
-}
+static int handle_json(json_t *);
+static int json_get_string(json_t *, char const *, char *, char const **);
+static int init_addrinfo(char const *, char const *);
int
-read_config_from_file(char *json_file_path, struct rtr_config **result)
+config_init(char const *json_file_path)
{
+ json_t *json_root;
+ json_error_t json_error;
int error;
- int is_json_file;
- json_t *root_json;
- struct rtr_config *config;
- struct file_contents fc;
-
- is_json_file = endsWith(json_file_path, ".json");
- if (!is_json_file) {
- log_err("Invalid Json file extension for file '%s'", json_file_path);
- return -EINVAL;
- }
- *result = NULL;
- error = file_load(json_file_path, &fc);
- if (error)
- return error;
+ if (json_file_path == NULL)
+ return init_addrinfo(DEFAULT_ADDR, DEFAULT_PORT);
- root_json = load_json(fc.buffer);
- file_free(&fc);
- if (!root_json)
+ json_root = json_load_file(json_file_path, JSON_REJECT_DUPLICATES,
+ &json_error);
+ if (json_root == NULL) {
+ warnx("JSON error on line %d, column %d: %s",
+ json_error.line, json_error.column, json_error.text);
return -ENOENT;
+ }
- config = malloc(sizeof(struct rtr_config));
- if (!config)
- return -ENOMEM;
-
- error = json_to_config(root_json, config);
- if (error)
- free(config);
+ error = handle_json(json_root);
- *result = config;
- json_decref(root_json);
+ json_decref(json_root);
return error;
}
-static void
-check_duplicates(bool *found, char *section)
+void
+config_cleanup(void)
{
- if (*found)
- log_err("Note: I found multiple '%s' sections.", section);
- *found = true;
+ if (config.address != NULL)
+ freeaddrinfo(config.address);
+ if (config.port != NULL)
+ free(config.port);
}
static int
-json_to_config(json_t *json, struct rtr_config *config)
+handle_json(json_t *root)
{
- bool listen_found = false;
- int error = 0;
- const char *key;
- json_t *value;
+ json_t *listen;
+ char const *address;
+ char const *port;
+ int error;
- if (!json || json->type != JSON_OBJECT) {
- log_err0("Invalid JSON config.");
+ if (!json_is_object(root)) {
+ warnx("The root of the JSON file is not a JSON object.");
return -EINVAL;
}
- json_object_foreach(json, key, value) {
- if(strcasecmp(OPTNAME_LISTEN, key) == 0) {
- check_duplicates(&listen_found, OPTNAME_LISTEN);
- error = handle_listen_config(value, config);
+ listen = json_object_get(root, OPTNAME_LISTEN);
+ if (listen != NULL) {
+ if (!json_is_object(listen)) {
+ warnx("The '%s' element is not a JSON object.",
+ OPTNAME_LISTEN);
+ return -EINVAL;
}
+
+ error = json_get_string(listen, OPTNAME_LISTEN_ADDRESS,
+ DEFAULT_ADDR, &address);
+ if (error)
+ return error;
+
+ error = json_get_string(listen, OPTNAME_LISTEN_PORT,
+ DEFAULT_PORT, &port);
+ if (error)
+ return error;
+
+ } else {
+ address = DEFAULT_ADDR;
+ port = DEFAULT_PORT;
}
- return error;
+ return init_addrinfo(address, port);
}
static int
-hostname_to_ip(const char *hostname, struct addrinfo **result)
+json_get_string(json_t *parent, char const *name, char *default_value,
+ char const **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;
+ json_t *child;
+ child = json_object_get(parent, name);
+ if (child == NULL) {
+ *result = default_value;
+ return 0;
+ }
- 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
+ if (!json_is_string(child)) {
+ warnx("The '%s' element is not a JSON string.", name);
return -EINVAL;
}
- *result = servinfo;
-
+ *result = json_string_value(child);
return 0;
}
static int
-handle_listen_config(json_t *json, struct rtr_config *config)
+init_addrinfo(char const *hostname, char const *service)
{
int error;
- bool listen_servername_found = false;
- bool listen_port_found = false;
- const char *key;
- json_t *value;
-
- if (!json || json->type != JSON_OBJECT) {
- log_err0("Invalid JSON config.");
- return -EINVAL;
- }
+ struct addrinfo hints;
- json_object_foreach(json, key, value) {
- 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;
- }
-
- 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);
- }
+ memset(&hints, 0 , sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ /* hints.ai_socktype = SOCK_DGRAM; */
+ hints.ai_flags |= AI_PASSIVE;
+
+ error = getaddrinfo(hostname, service, &hints, &config.address);
+ if (error) {
+ warnx("Could not infer a bindable address out of address '%s' and port '%s': %s",
+ (hostname != NULL) ? hostname : "any", service,
+ gai_strerror(error));
+ return error;
}
+ config.port = str_clone(service);
return 0;
}
-
-/*
- * Parse text into a JSON object. If text is valid JSON, returns a
- * json_t structure, otherwise prints and error and returns null.
- */
-static json_t *load_json(const char *text) {
- json_t *root;
- json_error_t error;
-
- root = json_loads(text, 0, &error);
-
- if (root)
- return root;
- else {
- log_err("json error on line %d column %d: %s\n", error.line, error.column, error.text);
- return (json_t *)0;
- }
+struct addrinfo const *
+config_get_server_addrinfo(void)
+{
+ return config.address;
}
+char const *
+config_get_server_port(void)
+{
+ return config.port;
+}
#ifndef _SRC_CONFIGURATION_H_
#define _SRC_CONFIGURATION_H_
-#include "netdb.h"
-#include <asm/types.h>
+#include <netdb.h>
-struct rtr_config {
- /** The listener address of the RTR server. */
- 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 **);
+int config_init(char const *);
+void config_cleanup(void);
+struct addrinfo const *config_get_server_addrinfo(void);
+char const *config_get_server_port(void);
#endif /* _SRC_CONFIGURATION_H_ */
+++ /dev/null
-#include "file.h"
-
-#include <err.h>
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "common.h"
-
-/*
- * Will also rewind the file as a side effect.
- * This is currently perfect for calling users.
- */
-static int
-get_file_size(FILE *file, long int *size)
-{
- if (fseek(file, 0L, SEEK_END) == -1)
- return errno ? errno : -EINVAL;
- *size = ftell(file);
- rewind(file);
- return 0;
-}
-
-int
-file_load(const char *file_name, struct file_contents *fc)
-{
- FILE *file;
- long int file_size;
- size_t fread_result;
- int error;
-
- file = fopen(file_name, "rb");
- if (file == NULL) {
- warnxerrno("Could not open file '%s'", file_name);
- return errno;
- }
-
- /* TODO if @file is a directory, this returns a very large integer. */
- error = get_file_size(file, &file_size);
- if (error) {
- warnxerror0(error, "Could not compute file size");
- fclose(file);
- return error;
- }
-
- fc->buffer_size = file_size;
- fc->buffer = malloc(fc->buffer_size);
- if (fc->buffer == NULL) {
- warnx("Out of memory.");
- fclose(file);
- return -ENOMEM;
- }
-
- fread_result = fread(fc->buffer, 1, fc->buffer_size, file);
- if (fread_result < fc->buffer_size) {
- error = ferror(file);
- if (error) {
- /*
- * The manpage doesn't say that the result is an error
- * code. It literally doesn't say how to obtain the
- * error code.
- */
- warnx("File read error. The errcode is presumably %d. (%s)",
- error, strerror(error));
- free(fc->buffer);
- fclose(file);
- return error;
- }
-
- /*
- * As far as I can tell from the man page, feof() cannot return
- * less bytes that requested like read() does.
- */
- warnx("Likely programming error: fread() < file size");
- warnx("fr:%zu bs:%zu EOF:%d", fread_result, fc->buffer_size,
- feof(file));
- free(fc->buffer);
- fclose(file);
- return -EINVAL;
- }
-
- fclose(file);
- return 0;
-}
-
-void
-file_free(struct file_contents *fc)
-{
- free(fc->buffer);
-}
+++ /dev/null
-#ifndef SRC_FILE_H_
-#define SRC_FILE_H_
-
-#include <stddef.h>
-
-/*
- * The entire contents of the file, loaded into a buffer.
- *
- * Instances of this struct are expected to live on the stack.
- */
-struct file_contents {
- char *buffer;
- size_t buffer_size;
-};
-
-int file_load(const char *, struct file_contents *);
-void file_free(struct file_contents *);
-
-#endif /* SRC_FILE_H_ */
+#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "rtr/rtr.h"
#include "configuration.h"
+
/*
* This program is an RTR server.
*
int
main(int argc, char *argv[])
{
- int err = 0;
+ int err;
char *json_file = NULL;
- struct rtr_config *config;
int c;
- int fflag=0;
- static char usage[] = "usage: %s -f fname \n";
-
- puts("!!!Hello World!!!");
- while ((c = getopt(argc, argv, "f:")) != -1)
+ while ((c = getopt(argc, argv, "f:")) != -1) {
switch (c) {
case 'f':
- fflag = 1;
json_file = optarg;
break;
case '?':
- err = 1;
- break;
+ fprintf(stdout, "usage: %s -f <file name>\n", argv[0]);
+ return 0;
}
-
- if (fflag == 0) { /* -f was mandatory */
- fprintf(stderr, "%s: missing -f option\n", argv[0]);
- fprintf(stderr, usage, argv[0]);
- exit(1);
- } else if (err) {
- fprintf(stderr, usage, argv[0]);
- exit(1);
}
- err = read_config_from_file(json_file, &config);
+ err = config_init(json_file);
if (err)
return err;
- err = rtr_listen(config->host_address, config->host_port);
- if (config)
- free_rtr_config(config);
-
- if (err)
- return err;
+ err = rtr_listen();
- return EXIT_SUCCESS;
+ config_cleanup();
+ return err;
}
#include <unistd.h>
#include <arpa/inet.h>
-#include "../common.h"
+#include "configuration.h"
#include "pdu.h"
-static int
-bind_server_socket6(int socket, struct sockaddr_in6 *host_addr, __u16 port)
-{
- int err;
- struct sockaddr_in6 address;
-
- 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("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 = host_addr->sin_addr.s_addr;
- address.sin_port = htons(port);
-
- if (bind(socket, (struct sockaddr *)&address, sizeof(address)) < 0) {
- err = errno;
- 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)
+create_server_socket(void)
{
+ struct addrinfo const *addr;
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;
- }
+ addr = config_get_server_addrinfo();
+ for (; addr != NULL; addr = addr->ai_next) {
+ printf("Attempting to bind socket to address '%s', port '%s'.\n",
+ (addr->ai_canonname != NULL) ? addr->ai_canonname : "any",
+ config_get_server_port());
- for (tmp = server_addr; tmp != NULL; tmp = tmp->ai_next) {
- err = 0;
- fd = socket(tmp->ai_family, SOCK_STREAM, 0);
+ fd = socket(addr->ai_family, SOCK_STREAM, 0);
if (fd < 0) {
- err = errno;
- err = -abs(err);
- warn("Error opening socket. errno : %d", err);
+ warn("socket() failed");
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 (bind(fd, addr->ai_addr, addr->ai_addrlen) < 0) {
+ warn("bind() failed");
+ continue;
}
- if (!err) {
- log_binded_server_socket(tmp, port);
- break;
- }
+ printf("Success.\n");
+ return fd; /* Happy path */
}
- if (err)
- return err;
-
- return fd;
+ warnx("None of the addrinfo candidates could be bound.");
+ return -EINVAL;
}
/*
int err;
memcpy(¶m, param_void, sizeof(param));
- free(param_void);
+ free(param_void); /* Ha. */
while (true) { /* For each PDU... */
err = pdu_load(param.client_fd, &pdu, &meta);
* This function blocks.
*/
int
-rtr_listen(struct addrinfo *server_addr, __u16 port)
+rtr_listen(void)
{
int server_fd; /* "file descriptor" */
- server_fd = create_server_socket(server_addr, port);
+ server_fd = create_server_socket();
if (server_fd < 0)
return server_fd;
#ifndef RTR_RTR_H_
#define RTR_RTR_H_
-#include <netdb.h>
-#include <asm/types.h>
-
-#include "../common.h"
+#include "common.h"
__BEGIN_DECLS
-int rtr_listen(struct addrinfo *, __u16);
+int rtr_listen(void);
__END_DECLS
#endif /* RTR_RTR_H_ */
#include <stdlib.h>
#include <unistd.h>
+#include "common.h"
+
/*
* Writes exactly @length bytes from @buffer to the file descriptor @fd.
* All or nothing.
#include <stddef.h>
-#include "common.h"
-
__BEGIN_DECLS
int write_exact(int, unsigned char *, size_t);
int buffer2fd(unsigned char *, size_t);