]> git.ipfire.org Git - thirdparty/FORT-validator.git/commitdiff
Add jansson reference as json parser
authordhfelix <daniel.hdz.felix@hotmail.com>
Fri, 9 Nov 2018 22:12:02 +0000 (16:12 -0600)
committerdhfelix <daniel.hdz.felix@hotmail.com>
Fri, 9 Nov 2018 22:18:01 +0000 (16:18 -0600)
14 files changed:
configure.ac
src/Makefile.am
src/common.h
src/configuration.c [new file with mode: 0644]
src/configuration.h [new file with mode: 0644]
src/file.c [new file with mode: 0644]
src/file.h [new file with mode: 0644]
src/log.h [new file with mode: 0644]
src/main.c
src/rtr/rtr.c
src/rtr/rtr.h
src/str_utils.c [new file with mode: 0644]
src/str_utils.h [new file with mode: 0644]
src/types.h [new file with mode: 0644]

index 6f5e7f9d9135ca3891e4d93b4e41951811854e6a..2a6aa36aad135c0b74af050f3bec15ec94ed0358 100644 (file)
@@ -28,6 +28,7 @@ AC_SEARCH_LIBS([pthread_create], [pthread])
 # Uhhh... this one starts with "PKG_" so it's probably different.
 # No idea.
 PKG_CHECK_MODULES([CHECK], [check])
+PKG_CHECK_MODULES([JANSSON], [jansson])
 
 # Spit out the makefiles.
 AC_OUTPUT(Makefile src/Makefile man/Makefile test/Makefile)
index 605167896a08c9c0c65a0b8d6416c3ac986a20b4..d4972ac6f03411ecec7a2064ac1fe196551ff7fc 100644 (file)
@@ -1,5 +1,5 @@
 AM_CFLAGS = -pedantic -Wall -std=gnu11 -O3
-AM_LDFLAGS =
+AM_LDFLAGS = 
 
 bin_PROGRAMS = rtr_server
 
@@ -13,3 +13,13 @@ 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 += 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
index 1f54d5d6c2b08889fea46c4733104e55c93959d3..1c075332c6dae92ba0467b4288b49cdc52d2a1fd 100644 (file)
@@ -1,5 +1,7 @@
-#ifndef COMMON_H_
-#define COMMON_H_
+#ifndef _SRC_COMMON_H_
+#define _SRC_COMMON_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
 
 #define EUNIMPLEMENTED 566456
 
-#endif /* COMMON_H_ */
+#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__);
+
+#endif /* _SRC_COMMON_H_ */
diff --git a/src/configuration.c b/src/configuration.c
new file mode 100644 (file)
index 0000000..3612ede
--- /dev/null
@@ -0,0 +1,137 @@
+#include "configuration.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdio.h>
+#include <regex.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"
+
+static int json_to_config(json_t *, struct rtr_config *);
+static int handle_listen_config(json_t *, struct ipv4_transport_addr *);
+static json_t *load_json(const char *);
+
+
+int
+read_config_from_file(char *json_file_path, struct rtr_config **result)
+{
+       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;
+
+       root_json = load_json(fc.buffer);
+       file_free(&fc);
+       if (!root_json)
+               return -ENOENT;
+
+       config = malloc(sizeof(*config));
+       if (!config)
+               return -ENOMEM;
+
+       error = json_to_config(root_json, config);
+       if (error != 0)
+               free(config);
+
+       *result = config;
+       json_decref(root_json);
+       return error;
+}
+
+static void
+check_duplicates(bool *found, char *section)
+{
+       if (*found)
+               log_info("Note: I found multiple '%s' sections.", section);
+       *found = true;
+}
+
+static int
+json_to_config(json_t *json, struct rtr_config *config)
+{
+       bool listen_found = false;
+       int error = 0;
+       const char *key;
+       json_t *value;
+
+       if (!json || json->type != JSON_OBJECT) {
+               log_err0("Invalid JSON config.");
+               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->ipv4_server_addr);
+               }
+       }
+
+       return error;
+}
+
+
+static int
+handle_listen_config(json_t *json, struct ipv4_transport_addr *ipv4_server_addr)
+{
+       bool listen_ipv4_found = false;
+       const char *key;
+       json_t *value;
+
+       if (!json || json->type != JSON_OBJECT) {
+               log_err0("Invalid JSON config.");
+               return -EINVAL;
+       }
+
+       json_object_foreach(json, key, value) {
+               if (strcasecmp(OPTNAME_LISTEN_IPV4, key) == 0) {
+                       check_duplicates(&listen_ipv4_found, OPTNAME_LISTEN_IPV4);
+                       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);
+               }
+       }
+
+       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;
+    }
+}
+
diff --git a/src/configuration.h b/src/configuration.h
new file mode 100644 (file)
index 0000000..928ad2c
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef _SRC_CONFIGURATION_H_
+#define _SRC_CONFIGURATION_H_
+
+#include "types.h"
+
+struct rtr_config {
+       /** The listener address of the RTR server. */
+       struct ipv4_transport_addr ipv4_server_addr;
+};
+
+
+int read_config_from_file(char *, struct rtr_config **);
+
+
+#endif /* _SRC_CONFIGURATION_H_ */
diff --git a/src/file.c b/src/file.c
new file mode 100644 (file)
index 0000000..72903a7
--- /dev/null
@@ -0,0 +1,90 @@
+#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);
+}
diff --git a/src/file.h b/src/file.h
new file mode 100644 (file)
index 0000000..5affcf1
--- /dev/null
@@ -0,0 +1,19 @@
+#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_ */
diff --git a/src/log.h b/src/log.h
new file mode 100644 (file)
index 0000000..736dcda
--- /dev/null
+++ b/src/log.h
@@ -0,0 +1,13 @@
+#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 */
index bb1ab32095d5e821aef865330b37579b1dd07ec2..4c17aeedcb50d8f87afc52044dd6e0b72c91dd5b 100644 (file)
@@ -1,8 +1,9 @@
 #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;
+       char *json_file = NULL;
+       struct rtr_config *config;
+       int c;
+       int fflag=0;
+       static char usage[] = "usage: %s -f fname \n";
+
        puts("!!!Hello World!!!");
-       rtr_listen();
+
+       while ((c = getopt(argc, argv, "f:")) != -1)
+               switch (c) {
+               case 'f':
+                       fflag = 1;
+                       json_file = optarg;
+                       break;
+               case '?':
+                       err = 1;
+                       break;
+               }
+
+       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);
+       if (err)
+               return err;
+
+       err = rtr_listen(&config->ipv4_server_addr.l3,
+                       config->ipv4_server_addr.l4);
+       if (config)
+               free(config);
+
+       if (err)
+               return err;
+
        return EXIT_SUCCESS;
 }
index a89240da13f12d5d7ad766da664dee9eba4e9765..664672e55eb1b424c5335bde1e0c95cc2ac59d84 100644 (file)
@@ -9,6 +9,7 @@
 #include <unistd.h>
 
 #include "../common.h"
+#include "../types.h"
 #include "pdu.h"
 
 /*
@@ -16,7 +17,7 @@
  * from the clients.
  */
 static int
-create_server_socket(void)
+create_server_socket(struct in_addr *server_addr, __u16 port)
 {
        int fd; /* "file descriptor" */
        struct sockaddr_in address;
@@ -31,8 +32,8 @@ create_server_socket(void)
 
        memset(&address, 0, sizeof(address));
        address.sin_family = AF_INET;
-       address.sin_addr.s_addr = INADDR_ANY;
-       address.sin_port = htons(5001);
+       address.sin_addr.s_addr = server_addr->s_addr;
+       address.sin_port = htons(port);
        if (bind(fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
                err = errno;
                warn("Could not bind the address");
@@ -189,11 +190,11 @@ handle_client_connections(int server_fd)
  * This function blocks.
  */
 int
-rtr_listen(void)
+rtr_listen(struct in_addr *server_addr, __u16 port)
 {
        int server_fd; /* "file descriptor" */
 
-       server_fd = create_server_socket();
+       server_fd = create_server_socket(server_addr, port);
        if (server_fd < 0)
                return server_fd;
 
index f68668b03bd661b1aa130f65003e8ad8dddaf584..c3307e0c072800656e89c9438c79f80abf8da582 100644 (file)
@@ -2,9 +2,10 @@
 #define RTR_RTR_H_
 
 #include "../common.h"
+#include "../types.h"
 
 __BEGIN_DECLS
-int rtr_listen(void);
+int rtr_listen(struct in_addr *, __u16);
 __END_DECLS
 
 #endif /* RTR_RTR_H_ */
diff --git a/src/str_utils.c b/src/str_utils.c
new file mode 100644 (file)
index 0000000..a6f2747
--- /dev/null
@@ -0,0 +1,313 @@
+#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)));
+}
diff --git a/src/str_utils.h b/src/str_utils.h
new file mode 100644 (file)
index 0000000..79ae3de
--- /dev/null
@@ -0,0 +1,71 @@
+#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 */
diff --git a/src/types.h b/src/types.h
new file mode 100644 (file)
index 0000000..9043f5b
--- /dev/null
@@ -0,0 +1,41 @@
+#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 */