]> git.ipfire.org Git - thirdparty/FORT-validator.git/commitdiff
Merge remote-tracking branch 'rtrserver/master'
authorpcarana <pc.moreno2099@gmail.com>
Tue, 23 Apr 2019 23:27:35 +0000 (18:27 -0500)
committerpcarana <pc.moreno2099@gmail.com>
Tue, 23 Apr 2019 23:27:35 +0000 (18:27 -0500)
Include SLURM functions and some adequations to run the validator.

20 files changed:
1  2 
src/Makefile.am
src/address.c
src/address.h
src/config.c
src/config.h
src/crypto/base64.c
src/crypto/base64.h
src/main.c
src/object/tal.c
src/rtr/pdu.c
src/rtr/pdu.h
src/rtr/pdu_handler.c
src/rtr/pdu_sender.c
src/rtr/pdu_serializer.c
src/rtr/pdu_serializer.h
src/rtr/primitive_writer.c
src/rtr/primitive_writer.h
src/slurm_db.c
src/slurm_loader.c
src/slurm_parser.c

diff --cc src/Makefile.am
index fd901afc29c5f0b4d2f6aa9aaaacbb1d211cb1e6,003cf0ff8ca13c6dd769d333de480e7858dd629e..b4a95d4b2ccda5d7af3d046ffd7bfe1bc8f6b464
 -AM_CFLAGS = -pedantic -Wall -std=gnu11 -O3
 -AM_LDFLAGS = 
 -
 -bin_PROGRAMS = rtr_server
 -
 -rtr_server_SOURCES = main.c
 -rtr_server_SOURCES += address.c address.h
 -rtr_server_SOURCES += array_list.h
 -rtr_server_SOURCES += clients.c clients.h
 -rtr_server_SOURCES += common.c common.h
 -rtr_server_SOURCES += configuration.c configuration.h
 -rtr_server_SOURCES += csv.c csv.h
 -rtr_server_SOURCES += json_parser.c json_parser.h
 -rtr_server_SOURCES += line_file.c line_file.h
 -rtr_server_SOURCES += notify.c notify.h
 -rtr_server_SOURCES += slurm_db.c slurm_db.h
 -rtr_server_SOURCES += slurm_loader.c slurm_loader.h
 -rtr_server_SOURCES += slurm_parser.c slurm_parser.h
 -rtr_server_SOURCES += updates_daemon.c updates_daemon.h
 -rtr_server_SOURCES += vrps.c vrps.h
 -
 -rtr_server_SOURCES += rtr/err_pdu.c rtr/err_pdu.h
 -rtr_server_SOURCES += rtr/pdu_handler.c rtr/pdu_handler.h
 -rtr_server_SOURCES += rtr/pdu_sender.c rtr/pdu_sender.h
 -rtr_server_SOURCES += rtr/pdu_serializer.c rtr/pdu_serializer.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/primitive_writer.c rtr/primitive_writer.h
 -rtr_server_SOURCES += rtr/rtr.c rtr/rtr.h
 -
 -rtr_server_SOURCES += crypto/base64.c crypto/base64.h
 -
 -rtr_server_LDADD = ${JANSSON_LIBS}
 +# Comment these out during releases.
 +# Also increase optimization I guess (-O0 -> -On)
 +#CFLAGS_DEBUG  = -DDEBUG
 +LDFLAGS_DEBUG = -rdynamic
 +
 +bin_PROGRAMS = fort
 +
 +fort_SOURCES  = main.c
 +
 +fort_SOURCES += address.h address.c
 +fort_SOURCES += algorithm.h algorithm.c
 +fort_SOURCES += certificate_refs.h certificate_refs.c
 +fort_SOURCES += console_handler.h console_handler.c
 +fort_SOURCES += common.h
 +fort_SOURCES += config.h config.c
 +fort_SOURCES += debug.h debug.c
 +fort_SOURCES += extension.h extension.c
 +fort_SOURCES += file.h file.c
 +fort_SOURCES += line_file.h line_file.c
 +fort_SOURCES += log.h log.c
 +fort_SOURCES += nid.h nid.c
 +fort_SOURCES += random.h random.c
 +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 += thread_var.h thread_var.c
 +fort_SOURCES += uri.h uri.c
 +fort_SOURCES += json_handler.h json_handler.c
 +fort_SOURCES += validation_handler.h validation_handler.c
 +
 +fort_SOURCES += rsync/rsync.h rsync/rsync.c
 +
 +fort_SOURCES += asn1/content_info.h asn1/content_info.c
 +fort_SOURCES += asn1/decode.h asn1/decode.c
 +fort_SOURCES += asn1/oid.h asn1/oid.c
 +fort_SOURCES += asn1/signed_data.h asn1/signed_data.c
 +
 +fort_SOURCES += config/boolean.c config/boolean.h
 +fort_SOURCES += config/filename_format.h config/filename_format.c
 +fort_SOURCES += config/str.c config/str.h
 +fort_SOURCES += config/string_array.h config/string_array.c
 +fort_SOURCES += config/sync_strategy.h config/sync_strategy.c
 +fort_SOURCES += config/types.h
 +fort_SOURCES += config/uint.c config/uint.h
 +fort_SOURCES += config/uint32.c config/uint32.h
 +
 +fort_SOURCES += data_structure/array_list.h
 +
 +fort_SOURCES += crypto/base64.h crypto/base64.c
 +fort_SOURCES += crypto/hash.h crypto/hash.c
 +
 +fort_SOURCES += data_structure/circular_indexer.h
 +fort_SOURCES += data_structure/circular_indexer.c
 +
 +fort_SOURCES += object/certificate.h object/certificate.c
 +fort_SOURCES += object/crl.h object/crl.c
 +fort_SOURCES += object/ghostbusters.h object/ghostbusters.c
 +fort_SOURCES += object/manifest.h object/manifest.c
 +fort_SOURCES += object/name.h object/name.c
 +fort_SOURCES += object/roa.h object/roa.c
 +fort_SOURCES += object/signed_object.h object/signed_object.c
 +fort_SOURCES += object/tal.h object/tal.c
 +fort_SOURCES += object/vcard.h object/vcard.c
 +
 +fort_SOURCES += resource/ip4.h resource/ip4.c
 +fort_SOURCES += resource/ip6.h resource/ip6.c
 +fort_SOURCES += resource/asn.h resource/asn.c
 +
 +fort_SOURCES += array_list.h
 +fort_SOURCES += clients.c clients.h
 +fort_SOURCES += common.c common.h
++fort_SOURCES += json_parser.c json_parser.h
 +fort_SOURCES += notify.c notify.h
++fort_SOURCES += slurm_db.c slurm_db.h
++fort_SOURCES += slurm_loader.c slurm_loader.h
++fort_SOURCES += slurm_parser.c slurm_parser.h
 +fort_SOURCES += updates_daemon.c updates_daemon.h
 +
 +fort_SOURCES += rtr/err_pdu.c rtr/err_pdu.h
 +fort_SOURCES += rtr/pdu_handler.c rtr/pdu_handler.h
 +fort_SOURCES += rtr/pdu_sender.c rtr/pdu_sender.h
 +fort_SOURCES += rtr/pdu_serializer.c rtr/pdu_serializer.h
 +fort_SOURCES += rtr/pdu.c rtr/pdu.h
 +fort_SOURCES += rtr/primitive_reader.c rtr/primitive_reader.h
 +fort_SOURCES += rtr/primitive_writer.c rtr/primitive_writer.h
 +fort_SOURCES += rtr/rtr.c rtr/rtr.h
 +
 +fort_SOURCES += rtr/db/delta.c rtr/db/delta.h
 +fort_SOURCES += rtr/db/roa_tree.c rtr/db/roa_tree.h
 +fort_SOURCES += rtr/db/roa.c rtr/db/roa.h
 +fort_SOURCES += rtr/db/vrps.c rtr/db/vrps.h
 +
 +fort_CFLAGS  = -Wall
 +# Feel free to temporarily remove this one if you're not using gcc 7.3.0.
 +#fort_CFLAGS += $(GCC_WARNS)
 +fort_CFLAGS += -std=gnu99 -O0 -g $(CFLAGS_DEBUG)
 +fort_LDFLAGS = $(LDFLAGS_DEBUG)
 +fort_LDADD   = ${JANSSON_LIBS}
  
  # I'm tired of scrolling up, but feel free to comment this out.
  GCC_WARNS  = -fmax-errors=1
diff --cc src/address.c
index cdde1ec63781d295286cf7dbdd3a85f37a60d617,de8b9fdff8e53cdf72d988f8f27a8e435ea0aa08..6766302136fbef159e7f691d5f0b4509990ddfb1
@@@ -171,196 -98,79 +171,307 @@@ prefix4_decode(IPAddress_t const *str, 
        return 0;
  }
  
 +/**
 + * Translates an `IPAddress_t` to its equivalent `struct ipv6_prefix`.
 + */
  int
 -prefix6_decode(const char *str, struct ipv6_prefix *result)
 +prefix6_decode(IPAddress_t const *str, struct ipv6_prefix *result)
  {
 -      int error;
 +      struct in6_addr suffix;
 +      int len;
  
 -      if (str == NULL) {
 -              warnx("Null string received, can't decode IPv6 prefix");
 -              return -EINVAL;
 +      if (str->size > 16) {
 +              return pr_err("IPv6 address has too many octets. (%zu)",
 +                  str->size);
 +      }
 +      if (str->bits_unused < 0 || 7 < str->bits_unused) {
 +              return pr_err("Bit string IPv6 address's unused bits count (%d) is out of range (0-7).",
 +                  str->bits_unused);
        }
  
 -      error = str2addr6(str, &result->addr);
 -      if (error) {
 -              warnx("Invalid IPv6 prefix '%s'", str);
 +      memset(&result->addr, 0, sizeof(result->addr));
 +      memcpy(&result->addr, str->buf, str->size);
 +      len = 8 * str->size - str->bits_unused;
 +
 +      if (len < 0 || 128 < len) {
 +              return pr_err("IPv6 prefix length (%d) is out of bounds (0-128).",
 +                  len);
 +      }
 +
 +      result->len = len;
 +
 +      memset(&suffix, 0, sizeof(suffix));
 +      ipv6_suffix_mask(result->len, &suffix);
 +      if (addr6_bitwise_and(&result->addr, &suffix)) {
 +              return pr_err("IPv6 prefix '%s/%u' has enabled suffix bits.",
 +                  v6addr2str(&result->addr), result->len);
 +      }
 +
 +      return 0;
 +}
 +
 +static int
 +check_order4(struct ipv4_range *result)
 +{
 +      if (ntohl(result->min.s_addr) > ntohl(result->max.s_addr)) {
 +              return pr_err("The IPv4 range '%s-%s' is inverted.",
 +                  v4addr2str(&result->min), v4addr2str2(&result->max));
 +      }
 +
 +      return 0;
 +}
 +
 +/**
 + * If @range could have been encoded as a prefix, this function errors.
 + *
 + * rfc3779#section-2.2.3.7
 + */
 +static int
 +check_encoding4(struct ipv4_range *range)
 +{
 +      const uint32_t MIN = ntohl(range->min.s_addr);
 +      const uint32_t MAX = ntohl(range->max.s_addr);
 +      uint32_t mask;
 +
 +      for (mask = 0x80000000u; mask != 0; mask >>= 1)
 +              if ((MIN & mask) != (MAX & mask))
 +                      break;
 +
 +      for (; mask != 0; mask >>= 1)
 +              if (((MIN & mask) != 0) || ((MAX & mask) == 0))
 +                      return 0;
 +
 +      return pr_err("IPAddressRange '%s-%s' is a range, but should have been encoded as a prefix.",
 +          v4addr2str(&range->min), v4addr2str2(&range->max));
 +}
 +
 +/**
 + * Translates an `IPAddressRange_t` to its equivalent `struct ipv4_range`.
 + */
 +int
 +range4_decode(IPAddressRange_t const *input, struct ipv4_range *result)
 +{
 +      struct ipv4_prefix prefix;
 +      int error;
 +
 +      error = prefix4_decode(&input->min, &prefix);
 +      if (error)
 +              return error;
 +      result->min = prefix.addr;
 +
 +      error = prefix4_decode(&input->max, &prefix);
 +      if (error)
 +              return error;
 +      result->max.s_addr = prefix.addr.s_addr | be32_suffix_mask(prefix.len);
 +
 +      error = check_order4(result);
 +      if (error)
                return error;
 +
 +      return check_encoding4(result);
 +}
 +
 +static int
 +check_order6(struct ipv6_range *result)
 +{
 +      uint32_t min;
 +      uint32_t max;
 +      unsigned int quadrant;
 +
 +      for (quadrant = 0; quadrant < 4; quadrant++) {
 +              min = addr6_get_quadrant(&result->min, quadrant);
 +              max = addr6_get_quadrant(&result->max, quadrant);
 +              if (min > max) {
 +                      return pr_err("The IPv6 range '%s-%s' is inverted.",
 +                          v6addr2str(&result->min),
 +                          v6addr2str2(&result->max));
 +              } else if (min < max) {
 +                      return 0; /* result->min < result->max */
 +              }
 +      }
 +
 +      return 0; /* result->min == result->max */
 +}
 +
 +static int
 +pr_bad_encoding(struct ipv6_range *range)
 +{
 +      return pr_err("IPAddressRange %s-%s is a range, but should have been encoded as a prefix.",
 +          v6addr2str(&range->min), v6addr2str2(&range->max));
 +}
 +
 +static int
 +__check_encoding6(struct ipv6_range *range, unsigned int quadrant,
 +    uint32_t mask)
 +{
 +      uint32_t min;
 +      uint32_t max;
 +
 +      for (; quadrant < 4; quadrant++) {
 +              min = addr6_get_quadrant(&range->min, quadrant);
 +              max = addr6_get_quadrant(&range->max, quadrant);
 +              for (; mask != 0; mask >>= 1)
 +                      if (((min & mask) != 0) || ((max & mask) == 0))
 +                              return 0;
 +              mask = 0x80000000u;
 +      }
 +
 +      return pr_bad_encoding(range);
 +}
 +
 +static int
 +check_encoding6(struct ipv6_range *range)
 +{
 +      uint32_t min;
 +      uint32_t max;
 +      unsigned int quadrant;
 +      uint32_t mask;
 +
 +      for (quadrant = 0; quadrant < 4; quadrant++) {
 +              min = addr6_get_quadrant(&range->min, quadrant);
 +              max = addr6_get_quadrant(&range->max, quadrant);
 +              for (mask = 0x80000000u; mask != 0; mask >>= 1)
 +                      if ((min & mask) != (max & mask))
 +                              return __check_encoding6(range, quadrant, mask);
        }
  
 - }
 +      return pr_bad_encoding(range);
 +}
 +
 +/**
 + * Translates an `IPAddressRange_t` to its equivalent `struct ipv6_range`.
 + */
 +int
 +range6_decode(IPAddressRange_t const *input, struct ipv6_range *result)
 +{
 +      struct ipv6_prefix prefix;
 +      int error;
 +
 +      error = prefix6_decode(&input->min, &prefix);
 +      if (error)
 +              return error;
 +      result->min = prefix.addr;
 +
 +      error = prefix6_decode(&input->max, &prefix);
 +      if (error)
 +              return error;
 +      result->max = prefix.addr;
 +      ipv6_suffix_mask(prefix.len, &result->max);
 +
 +      error = check_order6(result);
 +      if (error)
 +              return error;
 +
 +      return check_encoding6(result);
 +}
++
++static char const *
++addr2str4(struct in_addr *addr, char *buffer)
++{
++      return inet_ntop(AF_INET, addr, buffer, INET_ADDRSTRLEN);
++}
++
++static char const *
++addr2str6(struct in6_addr *addr, char *buffer)
++{
++      return inet_ntop(AF_INET6, addr, buffer, INET6_ADDRSTRLEN);
++}
++
++static int const
++str2addr4(const char *addr, struct in_addr *dst)
++{
++      if (!inet_pton(AF_INET, addr, dst))
++              return -EINVAL;
+       return 0;
 -prefix_length_decode (const char *text, unsigned int *dst, int max_value)
++}
++
++static int const
++str2addr6(const char *addr, struct in6_addr *dst)
++{
++      if (!inet_pton(AF_INET6, addr, dst))
++              return -EINVAL;
++      return 0;
++}
+ int
 -      if (text == NULL) {
 -              warnx("Null string received, can't decode prefix length");
 -              return -EINVAL;
 -      }
++str_to_prefix4(const char *str, struct ipv4_prefix *result)
++{
++      int error;
++
++      if (str == NULL)
++              return pr_err("Can't parse NULL IPv4 prefix");
++
++      error = str2addr4(str, &result->addr);
++      if (error)
++              return pr_err("Invalid IPv4 prefix '%s'", str);
++
++      return 0;
++}
++
++int
++str_to_prefix6(const char *str, struct ipv6_prefix *result)
++{
++      int error;
++
++      if (str == NULL)
++              return pr_err("Can't parse NULL IPv6 prefix");
++
++      error = str2addr6(str, &result->addr);
++      if (error)
++              return pr_err("Invalid IPv6 prefix '%s'", str);
++
++      return 0;
++}
++
++int
++str_to_prefix_length(const char *text, uint8_t *dst, uint8_t max_value)
+ {
+       unsigned long len;
 -              warn("Invalid prefix length '%s'", text);
++      if (text == NULL)
++              return pr_err("Can't decode NULL prefix length");
+       errno = 0;
+       len = strtoul(text, NULL, 10);
+       if (errno) {
 -      if (len < 0 || max_value < len) {
 -              warnx("Prefix length (%ld) is out of range (0-%d).",
++              pr_errno(errno, "Invalid prefix length '%s'", text);
+               return -EINVAL;
+       }
+       /* An underflow or overflow will be considered here */
 -              return -EINVAL;
 -      }
 -      *dst = (unsigned int) len;
++      if (len < 0 || max_value < len)
++              return pr_err("Prefix length (%ld) is out of range (0-%d).",
+                   len, max_value);
 -prefix4_validate (struct ipv4_prefix *prefix)
++
++      *dst = (uint8_t) len;
+       return 0;
+ }
+ int
 -      if ((prefix->addr.s_addr & be32_suffix_mask(prefix->len)) != 0) {
 -              warnx("IPv4 prefix %s/%u has enabled suffix bits.",
++ipv4_prefix_validate(struct ipv4_prefix *prefix)
+ {
+       char buffer[INET_ADDRSTRLEN];
 -              return -EINVAL;
 -      }
++      if ((prefix->addr.s_addr & be32_suffix_mask(prefix->len)) != 0)
++              return pr_err("IPv4 prefix %s/%u has enabled suffix bits.",
+                   addr2str4(&prefix->addr, buffer), prefix->len);
 -prefix6_validate (struct ipv6_prefix *prefix)
++
+       return 0;
+ }
+ int
 -          || (prefix->addr.s6_addr32[3] & suffix.s6_addr32[3])) {
 -              warnx("IPv6 prefix %s/%u has enabled suffix bits.",
++ipv6_prefix_validate(struct ipv6_prefix *prefix)
+ {
+       struct in6_addr suffix;
+       char buffer[INET6_ADDRSTRLEN];
+       memset(&suffix, 0, sizeof(suffix));
+       ipv6_suffix_mask(prefix->len, &suffix);
+       if ((prefix->addr.s6_addr32[0] & suffix.s6_addr32[0])
+           || (prefix->addr.s6_addr32[1] & suffix.s6_addr32[1])
+           || (prefix->addr.s6_addr32[2] & suffix.s6_addr32[2])
 -              return -EINVAL;
 -      }
++          || (prefix->addr.s6_addr32[3] & suffix.s6_addr32[3]))
++              return pr_err("IPv6 prefix %s/%u has enabled suffix bits.",
+                   addr2str6(&prefix->addr, buffer), prefix->len);
++
+       return 0;
+ }
diff --cc src/address.h
index 74dba9c3f02492ab443c7832cc5212479092afd3,1969f62537bbaf8492a847ee9e62aca094f1597f..f7869937123462029795a3e47bb3bd9d8aa900a3
@@@ -13,29 -10,15 +13,36 @@@ struct ipv4_prefix 
  
  struct ipv6_prefix {
        struct in6_addr addr;
 -      unsigned int len;
 +      uint8_t len;
  };
  
 -int prefix4_decode(const char *, struct ipv4_prefix *);
 -int prefix6_decode(const char *, struct ipv6_prefix *);
 +struct ipv4_range {
 +      struct in_addr min;
 +      struct in_addr max;
 +};
 +
 +struct ipv6_range {
 +      struct in6_addr min;
 +      struct in6_addr max;
 +};
 +
 +uint32_t u32_suffix_mask(unsigned int);
 +uint32_t be32_suffix_mask(unsigned int);
 +void ipv6_suffix_mask(unsigned int, struct in6_addr *);
 +
 +bool prefix4_equals(struct ipv4_prefix const *, struct ipv4_prefix const *);
 +bool prefix6_equals(struct ipv6_prefix const *, struct ipv6_prefix const *);
 +
 +int prefix4_decode(IPAddress_t const *, struct ipv4_prefix *);
 +int prefix6_decode(IPAddress_t const *, struct ipv6_prefix *);
 +int range4_decode(IPAddressRange_t const *, struct ipv4_range *);
 +int range6_decode(IPAddressRange_t const *, struct ipv6_range *);
  
 -int prefix_length_decode(const char *, unsigned int *, int);
++int str_to_prefix4(const char *, struct ipv4_prefix *);
++int str_to_prefix6(const char *, struct ipv6_prefix *);
++int str_to_prefix_length(const char *, uint8_t *, uint8_t);
 -int prefix4_validate (struct ipv4_prefix *);
 -int prefix6_validate (struct ipv6_prefix *);
++int ipv4_prefix_validate(struct ipv4_prefix *);
++int ipv6_prefix_validate(struct ipv6_prefix *);
  #endif /* SRC_ADDRESS_H_ */
diff --cc src/config.c
index bae3f2b22076eee75b3da421d5a27da0106a638f,0000000000000000000000000000000000000000..de9354daae9cbf490d38212ab0a5445cbf8824d5
mode 100644,000000..100644
--- /dev/null
@@@ -1,728 -1,0 +1,743 @@@
-       },
 +#include "config.h"
 +
 +#include <limits.h>
 +#include <stdlib.h>
 +#include <string.h>
 +#include <errno.h>
 +#include <getopt.h>
 +#include <sys/socket.h>
 +
 +#include "common.h"
 +#include "json_handler.h"
 +#include "log.h"
 +#include "config/boolean.h"
 +#include "config/str.h"
 +#include "config/uint.h"
 +#include "config/uint32.h"
 +
 +/**
 + * To add a member to this structure,
 + *
 + * 1. Add it.
 + * 2. Add its metadata somewhere in @groups.
 + * 3. Add default value to set_default_values().
 + * 4. Create the getter.
 + *
 + * Assuming you don't need to create a data type, that should be all.
 + */
 +struct rpki_config {
 +      /** TAL file name or directory. TODO support directories */
 +      char *tal;
 +      /** Path of our local clone of the repository */
 +      char *local_repository;
 +      /** Synchronization (currently only RSYNC) download strategy. */
 +      enum sync_strategy sync_strategy;
 +      /**
 +       * Handle TAL URIs in random order?
 +       * (https://tools.ietf.org/html/rfc7730#section-3, last
 +       * paragraphs)
 +       */
 +      bool shuffle_tal_uris;
 +      /**
 +       * rfc6487#section-7.2, last paragraph.
 +       * Prevents arbitrarily long paths and loops.
 +       */
 +      unsigned int maximum_certificate_depth;
 +
 +      struct {
 +              /** The listener address of the RTR server. */
 +              char *address;
 +              /** TODO document */
 +              char *port;
 +              /** Maximum accepted client connections */
 +              unsigned int queue;
 +
 +              /** Interval used to look for updates at VRPs location */
 +              /* TODO rename */
 +              unsigned int vrps_check_interval;
 +
 +              /** Intervals use at RTR v1 End of data PDU **/
 +              uint32_t refresh_interval;
 +              uint32_t retry_interval;
 +              uint32_t expire_interval;
++
++              /** Directory where the .slurm files are located */
++              char *slurm_location;
 +      } server;
 +
 +      struct {
 +              char *program;
 +              struct {
 +                      struct string_array flat;
 +                      struct string_array recursive;
 +              } args;
 +      } rsync;
 +
 +      struct {
 +              /** Print ANSI color codes? */
 +              bool color;
 +              /** Format in which file names will be printed. */
 +              enum filename_format filename_format;
 +      } log;
 +};
 +
 +static void print_usage(FILE *, bool);
 +
 +#define DECLARE_HANDLE_FN(name)                                               \
 +      static int name(                                                \
 +          struct option_field const *,                                \
 +          char *                                                      \
 +      )
 +DECLARE_HANDLE_FN(handle_help);
 +DECLARE_HANDLE_FN(handle_usage);
 +DECLARE_HANDLE_FN(handle_version);
 +DECLARE_HANDLE_FN(handle_json);
 +
 +static char const *program_name;
 +static struct rpki_config rpki_config;
 +
 +/**
 + * An option that takes no arguments, is not correlated to any rpki_config
 + * fields, and is entirely managed by its handler function.
 + */
 +static const struct global_type gt_callback = {
 +      .has_arg = no_argument,
 +};
 +
 +static const struct option_field options[] = {
 +
 +      /* ARGP-only, non-fields */
 +      {
 +              .id = 'h',
 +              .name = "help",
 +              .type = &gt_callback,
 +              .handler = handle_help,
 +              .doc = "Give this help list",
 +              .availability = AVAILABILITY_GETOPT,
 +      }, {
 +              .id = 1000,
 +              .name = "usage",
 +              .type = &gt_callback,
 +              .handler = handle_usage,
 +              .doc = "Give a short usage message",
 +              .availability = AVAILABILITY_GETOPT,
 +      }, {
 +              .id = 'V',
 +              .name = "version",
 +              .type = &gt_callback,
 +              .handler = handle_version,
 +              .doc = "Print program version",
 +              .availability = AVAILABILITY_GETOPT,
 +      }, {
 +              .id = 'f',
 +              .name = "configuration-file",
 +              .type = &gt_string,
 +              .handler = handle_json,
 +              .doc = "JSON file additional configuration will be read from",
 +              .arg_doc = "<file>",
 +              .availability = AVAILABILITY_GETOPT,
 +      },
 +
 +      /* Root fields */
 +      {
 +              .id = 't',
 +              .name = "tal",
 +              .type = &gt_string,
 +              .offset = offsetof(struct rpki_config, tal),
 +              .doc = "Path to the TAL file",
 +              .arg_doc = "<file>",
 +      }, {
 +              .id = 'r',
 +              .name = "local-repository",
 +              .type = &gt_string,
 +              .offset = offsetof(struct rpki_config, local_repository),
 +              .doc = "Directory where the repository local cache will be stored/read",
 +              .arg_doc = "<directory>",
 +      }, {
 +              .id = 1001,
 +              .name = "sync-strategy",
 +              .type = &gt_sync_strategy,
 +              .offset = offsetof(struct rpki_config, sync_strategy),
 +              .doc = "RSYNC download strategy",
 +      }, {
 +              .id = 2000,
 +              .name = "shuffle-uris",
 +              .type = &gt_bool,
 +              .offset = offsetof(struct rpki_config, shuffle_tal_uris),
 +              .doc = "Shuffle URIs in the TAL before accessing them",
 +      }, {
 +              .id = 1002,
 +              .name = "maximum-certificate-depth",
 +              .type = &gt_uint,
 +              .offset = offsetof(struct rpki_config,
 +                  maximum_certificate_depth),
 +              .doc = "Maximum allowable certificate chain length",
 +              .min = 1,
 +              /**
 +               * It cannot be UINT_MAX, because then the actual number will
 +               * overflow and will never be bigger than this.
 +               */
 +              .max = UINT_MAX - 1,
 +      },
 +
 +      /* Server fields */
 +      {
 +              .id = 5000,
 +              .name = "server.address",
 +              .type = &gt_string,
 +              .offset = offsetof(struct rpki_config, server.address),
 +              .doc = "The listener address of the RTR server.",
 +      }, {
 +              .id = 5001,
 +              .name = "server.port",
 +              .type = &gt_string,
 +              .offset = offsetof(struct rpki_config, server.port),
 +              .doc = "", /* TODO */
 +      }, {
 +              .id = 5003,
 +              .name = "server.queue",
 +              .type = &gt_uint,
 +              .offset = offsetof(struct rpki_config, server.queue),
 +              .doc = "Maximum accepted client connections",
 +              .min = 1,
 +              .max = SOMAXCONN,
 +      }, {
 +              .id = 5004,
 +              .name = "server.vrps-check-interval",
 +              .type = &gt_uint,
 +              .offset = offsetof(struct rpki_config, server.vrps_check_interval),
 +              .doc = "Interval used to look for updates at VRPs location",
 +              /*
 +               * RFC 6810 and 8210:
 +               * The cache MUST rate-limit Serial Notifies to no more frequently than
 +               * one per minute.
 +               */
 +              .min = 60,
 +              .max = 7200,
 +      }, {
 +              .id = 5005,
 +              .name = "server.rtr-interval.refresh",
 +              .type = &gt_uint32,
 +              .offset = offsetof(struct rpki_config, server.refresh_interval),
 +              .doc = "Intervals use at RTR v1 End of data PDU",
 +              .min = 1,
 +              .max = 86400,
 +      }, {
 +              .id = 5006,
 +              .name = "server.rtr-interval.retry",
 +              .type = &gt_uint32,
 +              .offset = offsetof(struct rpki_config, server.retry_interval),
 +              .doc = "",
 +              .min = 1,
 +              .max = 7200,
 +      }, {
 +              .id = 5007,
 +              .name = "server.rtr-interval.expire",
 +              .type = &gt_uint32,
 +              .offset = offsetof(struct rpki_config, server.expire_interval),
 +              .doc = "",
 +              .min = 600,
 +              .max = 172800,
++      }, {
++              .id = 5008,
++              .name = "server.slurm.location",
++              .type = &gt_string,
++              .offset = offsetof(struct rpki_config, server.slurm_location),
++              .doc = "Directory where the .slurm files are located",
++              },
 +
 +      /* RSYNC fields */
 +      {
 +              .id = 3000,
 +              .name = "rsync.program",
 +              .type = &gt_string,
 +              .offset = offsetof(struct rpki_config, rsync.program),
 +              .doc = "Name of the program needed to execute an RSYNC",
 +              .arg_doc = "<path to program>",
 +              .availability = AVAILABILITY_JSON,
 +      }, {
 +              .id = 3001,
 +              .name = "rsync.arguments-recursive",
 +              .type = &gt_string_array,
 +              .offset = offsetof(struct rpki_config, rsync.args.recursive),
 +              .doc = "RSYNC program arguments that will trigger a recursive RSYNC",
 +              .availability = AVAILABILITY_JSON,
 +      }, {
 +              .id = 3002,
 +              .name = "rsync.arguments-flat",
 +              .type = &gt_string_array,
 +              .offset = offsetof(struct rpki_config, rsync.args.flat),
 +              .doc = "RSYNC program arguments that will trigger a non-recursive RSYNC",
 +              .availability = AVAILABILITY_JSON,
 +      },
 +
 +      /* Logging fields */
 +      {
 +              .id = 'c',
 +              .name = "log.color-output",
 +              .type = &gt_bool,
 +              .offset = offsetof(struct rpki_config, log.color),
 +              .doc = "Print ANSI color codes.",
 +      }, {
 +              .id = 4000,
 +              .name = "log.file-name-format",
 +              .type = &gt_filename_format,
 +              .offset = offsetof(struct rpki_config, log.filename_format),
 +              .doc = "File name variant to print during debug/error messages",
 +      },
 +
 +      { 0 },
 +};
 +
 +/**
 + * Returns true if @field is the descriptor of one of the members of the
 + * struct rpki_config structure, false otherwise.
 + * (Alternatively: Returns true if @field->offset is valid, false otherwise.)
 + */
 +static bool
 +is_rpki_config_field(struct option_field const *field)
 +{
 +      return field->handler == NULL;
 +}
 +
 +void *
 +get_rpki_config_field(struct option_field const *field)
 +{
 +      return ((unsigned char *) &rpki_config) + field->offset;
 +}
 +
 +static int
 +handle_help(struct option_field const *field, char *arg)
 +{
 +      print_usage(stdout, true);
 +      exit(0);
 +}
 +
 +static int
 +handle_usage(struct option_field const *field, char *arg)
 +{
 +      print_usage(stdout, false);
 +      exit(0);
 +}
 +
 +static int
 +handle_version(struct option_field const *field, char *arg)
 +{
 +      printf("0.0.1\n");
 +      exit(0);
 +}
 +
 +static int
 +handle_json(struct option_field const *field, char *file_name)
 +{
 +      return set_config_from_file(file_name);
 +}
 +
 +static bool
 +is_alphanumeric(int chara)
 +{
 +      return ('a' <= chara && chara <= 'z')
 +          || ('A' <= chara && chara <= 'Z')
 +          || ('0' <= chara && chara <= '9');
 +}
 +
 +/**
 + * "struct option" is the array that getopt expects.
 + * "struct args_flag" is our option metadata.
 + */
 +static int
 +construct_getopt_options(struct option **_long_opts, char **_short_opts)
 +{
 +      struct option_field const *opt;
 +      struct option *long_opts;
 +      char *short_opts;
 +      unsigned int total_long_options;
 +      unsigned int total_short_options;
 +
 +      total_long_options = 0;
 +      total_short_options = 0;
 +      FOREACH_OPTION(options, opt, AVAILABILITY_GETOPT) {
 +              total_long_options++;
 +              if (is_alphanumeric(opt->id)) {
 +                      total_short_options++;
 +                      if (opt->type->has_arg != no_argument)
 +                              total_short_options++; /* ":" */
 +              }
 +      }
 +
 +      /* +1 NULL end, means end of array. */
 +      long_opts = calloc(total_long_options + 1, sizeof(struct option));
 +      if (long_opts == NULL)
 +              return pr_enomem();
 +      short_opts = malloc(total_short_options + 1);
 +      if (short_opts == NULL) {
 +              free(long_opts);
 +              return pr_enomem();
 +      }
 +
 +      *_long_opts = long_opts;
 +      *_short_opts = short_opts;
 +
 +      FOREACH_OPTION(options, opt, AVAILABILITY_GETOPT) {
 +              long_opts->name = opt->name;
 +              long_opts->has_arg = opt->type->has_arg;
 +              long_opts->flag = NULL;
 +              long_opts->val = opt->id;
 +              long_opts++;
 +
 +              if (is_alphanumeric(opt->id)) {
 +                      *short_opts = opt->id;
 +                      short_opts++;
 +                      if (opt->type->has_arg != no_argument) {
 +                              *short_opts = ':';
 +                              short_opts++;
 +                      }
 +              }
 +      }
 +
 +      *short_opts = '\0';
 +      return 0;
 +}
 +
 +static void
 +print_config(void)
 +{
 +      struct option_field const *opt;
 +
 +      pr_info("Configuration {");
 +      pr_indent_add();
 +
 +      FOREACH_OPTION(options, opt, 0xFFFF)
 +              if (is_rpki_config_field(opt) && opt->type->print != NULL)
 +                      opt->type->print(opt, get_rpki_config_field(opt));
 +
 +      pr_indent_rm();
 +      pr_info("}");
 +}
 +
 +static int
 +set_default_values(void)
 +{
 +      static char const *default_rsync_args[] = {
 +              "--recursive",
 +              "--delete",
 +              "--times",
 +              "--contimeout=20",
 +              "$REMOTE",
 +              "$LOCAL",
 +      };
 +
 +      int error;
 +
 +      /*
 +       * Values that might need to be freed WILL be freed, so use heap
 +       * duplicates.
 +       */
 +
 +      rpki_config.server.address = NULL;
 +      rpki_config.server.port = strdup("323");
 +      if (rpki_config.server.port == NULL)
 +              return pr_enomem();
 +
 +      rpki_config.server.queue = 10;
 +      rpki_config.server.vrps_check_interval = 60;
 +      rpki_config.server.refresh_interval = 3600;
 +      rpki_config.server.retry_interval = 600;
 +      rpki_config.server.expire_interval = 7200;
++      rpki_config.server.slurm_location = NULL;
 +
 +      rpki_config.tal = NULL;
 +
 +      rpki_config.local_repository = strdup("repository/");
 +      if (rpki_config.local_repository == NULL) {
 +              error = pr_enomem();
 +              goto revert_port;
 +      }
 +
 +      rpki_config.sync_strategy = SYNC_ROOT;
 +      rpki_config.shuffle_tal_uris = false;
 +      rpki_config.maximum_certificate_depth = 32;
 +
 +      rpki_config.rsync.program = strdup("rsync");
 +      if (rpki_config.rsync.program == NULL) {
 +              error = pr_enomem();
 +              goto revert_repository;
 +      }
 +
 +      error = string_array_init(&rpki_config.rsync.args.recursive,
 +          default_rsync_args, ARRAY_LEN(default_rsync_args));
 +      if (error)
 +              goto revert_rsync_program;
 +      /* Simply remove --recursive and --delete. */
 +      error = string_array_init(&rpki_config.rsync.args.flat,
 +          default_rsync_args + 2, ARRAY_LEN(default_rsync_args) - 2);
 +      if (error)
 +              goto revert_recursive_array;
 +
 +      rpki_config.log.color = false;
 +      rpki_config.log.filename_format = FNF_GLOBAL;
 +
 +      return 0;
 +
 +revert_recursive_array:
 +      string_array_cleanup(&rpki_config.rsync.args.recursive);
 +revert_rsync_program:
 +      free(rpki_config.rsync.program);
 +revert_repository:
 +      free(rpki_config.local_repository);
 +revert_port:
 +      free(rpki_config.server.port);
 +      return error;
 +}
 +
 +static int
 +validate_config(void)
 +{
 +      return (rpki_config.tal != NULL)
 +          ? 0
 +          : pr_err("The TAL file (--tal) is mandatory.");
 +}
 +
 +static void
 +print_usage(FILE *stream, bool print_doc)
 +{
 +      struct option_field const *option;
 +      char const *arg_doc;
 +
 +      fprintf(stream, "Usage: %s\n", program_name);
 +      FOREACH_OPTION(options, option, AVAILABILITY_GETOPT) {
 +              fprintf(stream, "\t[");
 +              fprintf(stream, "--%s", option->name);
 +
 +              if (option->arg_doc != NULL)
 +                      arg_doc = option->arg_doc;
 +              else if (option->type->arg_doc != NULL)
 +                      arg_doc = option->type->arg_doc;
 +              else
 +                      arg_doc = NULL;
 +
 +              switch (option->type->has_arg) {
 +              case no_argument:
 +                      break;
 +              case optional_argument:
 +              case required_argument:
 +                      if (arg_doc != NULL)
 +                              fprintf(stream, "=%s", arg_doc);
 +                      break;
 +              }
 +
 +              fprintf(stream, "]\n");
 +
 +              if (print_doc)
 +                      fprintf(stream, "\t    (%s)\n", option->doc);
 +      }
 +}
 +
 +
 +static int
 +handle_opt(int opt)
 +{
 +      struct option_field const *option;
 +
 +      FOREACH_OPTION(options, option, AVAILABILITY_GETOPT) {
 +              if (option->id == opt) {
 +                      return is_rpki_config_field(option)
 +                          ? option->type->parse.argv(option, optarg,
 +                                get_rpki_config_field(option))
 +                          : option->handler(option, optarg);
 +              }
 +      }
 +
 +      pr_err("Unrecognized option: %d", opt);
 +      return -ESRCH;
 +}
 +
 +int
 +handle_flags_config(int argc, char **argv)
 +{
 +      struct option *long_opts;
 +      char *short_opts;
 +      int opt;
 +      int error;
 +
 +      program_name = argv[0];
 +      error = set_default_values();
 +      if (error)
 +              return error;
 +
 +      long_opts = NULL;
 +      short_opts = NULL;
 +      error = construct_getopt_options(&long_opts, &short_opts);
 +      if (error)
 +              goto end; /* Error msg already printed. */
 +
 +      while ((opt = getopt_long(argc, argv, short_opts, long_opts, NULL))
 +          != -1) {
 +              error = handle_opt(opt);
 +              if (error)
 +                      goto end;
 +      }
 +
 +      /*
 +       * This triggers when the user runs something like
 +       * `rpki-validator disable-rsync` instead of
 +       * `rpki-validator --disable-rsync`.
 +       * This program does not have unflagged payload.
 +       */
 +      if (optind < argc) {
 +              error = pr_err("I don't know what '%s' is.", argv[optind]);
 +              goto end;
 +      }
 +
 +      error = validate_config();
 +
 +end:
 +      if (error)
 +              free_rpki_config();
 +      else
 +              print_config();
 +
 +      free(long_opts);
 +      free(short_opts);
 +      return error;
 +
 +}
 +
 +struct option_field const *
 +get_option_metadatas(void)
 +{
 +      return options;
 +}
 +
 +char const *
 +config_get_server_address(void)
 +{
 +      return rpki_config.server.address;
 +}
 +
 +char const *
 +config_get_server_port(void)
 +{
 +      return rpki_config.server.port;
 +}
 +
 +int
 +config_get_server_queue(void)
 +{
 +      /*
 +       * The range of this is 1-<small number>, so adding signedness is safe.
 +       */
 +      return rpki_config.server.queue;
 +}
 +
 +unsigned int
 +config_get_vrps_check_interval(void)
 +{
 +      return rpki_config.server.vrps_check_interval;
 +}
 +
 +uint32_t
 +config_get_refresh_interval(void)
 +{
 +      return rpki_config.server.refresh_interval;
 +}
 +
 +uint32_t
 +config_get_retry_interval(void)
 +{
 +      return rpki_config.server.retry_interval;
 +}
 +
 +uint32_t
 +config_get_expire_interval(void)
 +{
 +      return rpki_config.server.expire_interval;
 +}
 +
++char const *
++config_get_slurm_location(void)
++{
++      return rpki_config.server.slurm_location;
++}
 +char const *
 +config_get_tal(void)
 +{
 +      return rpki_config.tal;
 +}
 +
 +char const *
 +config_get_local_repository(void)
 +{
 +      return rpki_config.local_repository;
 +}
 +
 +enum sync_strategy
 +config_get_sync_strategy(void)
 +{
 +      return rpki_config.sync_strategy;
 +}
 +
 +bool
 +config_get_shuffle_tal_uris(void)
 +{
 +      return rpki_config.shuffle_tal_uris;
 +}
 +
 +unsigned int
 +config_get_max_cert_depth(void)
 +{
 +      return rpki_config.maximum_certificate_depth;
 +}
 +
 +bool
 +config_get_color_output(void)
 +{
 +      return rpki_config.log.color;
 +}
 +
 +enum filename_format
 +config_get_filename_format(void)
 +{
 +      return rpki_config.log.filename_format;
 +}
 +
 +char *
 +config_get_rsync_program(void)
 +{
 +      return rpki_config.rsync.program;
 +}
 +
 +struct string_array const *
 +config_get_rsync_args(bool is_ta)
 +{
 +      switch (rpki_config.sync_strategy) {
 +      case SYNC_ROOT:
 +              return &rpki_config.rsync.args.recursive;
 +      case SYNC_ROOT_EXCEPT_TA:
 +              return is_ta
 +                  ? &rpki_config.rsync.args.flat
 +                  : &rpki_config.rsync.args.recursive;
 +      case SYNC_STRICT:
 +              return &rpki_config.rsync.args.flat;
 +      case SYNC_OFF:
 +              break;
 +      }
 +
 +      pr_crit("Invalid sync strategy: '%u'",
 +          rpki_config.sync_strategy);
 +      /*
 +       * Return something usable anyway; don't want to check NULL.
 +       * This is supposed to be unreachable code anyway.
 +       */
 +      return &rpki_config.rsync.args.recursive;
 +}
 +
 +void
 +free_rpki_config(void)
 +{
 +      struct option_field const *option;
 +
 +      FOREACH_OPTION(options, option, 0xFFFF)
 +              if (is_rpki_config_field(option) && option->type->free != NULL)
 +                      option->type->free(get_rpki_config_field(option));
 +}
diff --cc src/config.h
index 1eca1e09b8c9e2e57750df6ece99802c1cb259c6,0000000000000000000000000000000000000000..8730ba15781b77ad932e5b4093cdf3fd62f4fb9e
mode 100644,000000..100644
--- /dev/null
@@@ -1,39 -1,0 +1,40 @@@
 +#ifndef SRC_CONFIG_H_
 +#define SRC_CONFIG_H_
 +
 +#include <stdbool.h>
 +#include <stdint.h>
 +
 +#include "config/filename_format.h"
 +#include "config/sync_strategy.h"
 +#include "config/string_array.h"
 +#include "config/types.h"
 +
 +/* Init/destroy */
 +int handle_flags_config(int , char **);
 +void free_rpki_config(void);
 +
 +/* Getters */
 +char const *config_get_server_address(void);
 +char const *config_get_server_port(void);
 +int config_get_server_queue(void);
 +unsigned int config_get_vrps_check_interval(void);
 +uint32_t config_get_refresh_interval(void);
 +uint32_t config_get_retry_interval(void);
 +uint32_t config_get_expire_interval(void);
++char const *config_get_slurm_location(void);
 +
 +char const *config_get_tal(void);
 +char const *config_get_local_repository(void);
 +enum sync_strategy config_get_sync_strategy(void);
 +bool config_get_shuffle_tal_uris(void);
 +unsigned int config_get_max_cert_depth(void);
 +bool config_get_color_output(void);
 +enum filename_format config_get_filename_format(void);
 +char *config_get_rsync_program(void);
 +struct string_array const *config_get_rsync_args(bool);
 +
 +/* Needed public by the JSON module */
 +void *get_rpki_config_field(struct option_field const *);
 +struct option_field const *get_option_metadatas(void);
 +
 +#endif /* SRC_CONFIG_H_ */
index efb5ecc6189c0636970dd95e4d781e48399781be,0ce85f23f3724d5459e1624714c2148b2fa7d2ae..f5969880d32a33a85c8f61c6dd08d13d6cc47120
@@@ -2,7 -2,18 +2,10 @@@
  
  #include <openssl/err.h>
  #include <openssl/evp.h>
+ #include <err.h>
+ #include <errno.h>
+ #include <string.h>
  
 -/*
 - * TODO This is a copy of fort-validator/src/crypto/base64.c with a few
 - * modifications:
 - * - At 'base64_decode': new parameter 'has_nl' to indicate if the encoded
 - *   string has newlines in it.
 - * - New function 'base64url_decode'.
 - */
 -
  /**
   * Converts error from libcrypto representation to this project's
   * representation.
@@@ -32,7 -44,8 +36,8 @@@ error_ul2i(unsigned long error
   * register a libcrypto stack error.
   */
  int
- base64_decode(BIO *in, unsigned char *out, size_t out_len, size_t *out_written)
 -base64_decode(BIO *in, unsigned char **out, bool has_nl, size_t out_len,
++base64_decode(BIO *in, unsigned char *out, bool has_nl, size_t out_len,
+     size_t *out_written)
  {
        BIO *b64;
        size_t offset = 0;
@@@ -108,3 -124,88 +116,88 @@@ end
  
        return error ? error_ul2i(error) : 0;
  }
 -      error = base64_decode(encoded, result, false, alloc_size, &dec_len);
+ /*
+  * Decode a base64 encoded string (@str_encoded), the decoded value is
+  * allocated at @result with a length of @result_len.
+  *
+  * Return 0 on success, or the error code if something went wrong. Don't forget
+  * to free @result after a successful decoding.
+  */
+ int
+ base64url_decode(char const *str_encoded, unsigned char **result,
+     size_t *result_len)
+ {
+       BIO *encoded; /* base64 encoded. */
+       char *str_copy;
+       size_t encoded_len, alloc_size, dec_len;
+       int error, pad, i;
+       /*
+        * Apparently there isn't a base64url decoder, and there isn't
+        * much difference between base64 codification and base64url, just as
+        * stated in RFC 4648 section 5: "This encoding is technically
+        * identical to the previous one, except for the 62:nd and 63:rd
+        * alphabet character, as indicated in Table 2".
+        *
+        * The existing base64 can be used if the 62:nd and 63:rd base64url
+        * alphabet chars are replaced with the corresponding base64 chars, and
+        * also if we add the optional padding that the member should have.
+        */
+       encoded_len = strlen(str_encoded);
+       pad = (encoded_len % 4) > 0 ? 4 - (encoded_len % 4) : 0;
+       str_copy = malloc(encoded_len + pad + 1);
+       if (str_copy == NULL)
+               return -ENOMEM;
+       /* Set all with pad char, then replace with the original string */
+       memset(str_copy, '=', encoded_len + pad);
+       memcpy(str_copy, str_encoded, encoded_len);
+       str_copy[encoded_len + pad] = '\0';
+       for (i = 0; i < encoded_len; i++) {
+               if (str_copy[i] == '-')
+                       str_copy[i] = '+';
+               else if (str_copy[i] == '_')
+                       str_copy[i] = '/';
+       }
+       /* Now decode as regular base64 */
+       encoded =  BIO_new_mem_buf(str_copy, -1);
+       if (encoded == NULL) {
+               warnx("BIO_new() returned NULL");
+               error = -EINVAL;
+               goto free_copy;
+       }
+       alloc_size = EVP_DECODE_LENGTH(strlen(str_copy));
+       *result = malloc(alloc_size + 1);
+       if (*result == NULL) {
+               error = -ENOMEM;
+               goto free_enc;
+       }
+       memset(*result, 0, alloc_size);
+       (*result)[alloc_size] = '\0';
++      error = base64_decode(encoded, *result, false, alloc_size, &dec_len);
+       if (error)
+               goto free_all;
+       if (dec_len == 0) {
+               warnx("'%s' couldn't be decoded", str_encoded);
+               error = -EINVAL;
+               goto free_all;
+       }
+       *result_len = dec_len;
+       free(str_copy);
+       BIO_free(encoded);
+       return 0;
+ free_all:
+       free(*result);
+ free_enc:
+       BIO_free(encoded);
+ free_copy:
+       free(str_copy);
+       return error;
+ }
index c56b3e6f1c95acbdd17ea1f64b249ee9e1a2318c,fb8210d73b95eed5c09b3c09ab6da62d86eb5bd1..a3d83ddd044c0e6bbf667dd54a7865f32cd6fa59
@@@ -4,6 -5,7 +5,7 @@@
  #include <stddef.h>
  #include <openssl/bio.h>
  
- int base64_decode(BIO *, unsigned char *, size_t, size_t *);
 -int base64_decode(BIO *, unsigned char **, bool, size_t, size_t *);
++int base64_decode(BIO *, unsigned char *, bool, size_t, size_t *);
+ int base64url_decode(char const *, unsigned char **, size_t *);
  
  #endif /* SRC_BASE64_H_ */
diff --cc src/main.c
index 909eead71cb16e39f1c73af33d4fdca0b7c40f2e,85d9d1bf65674963ce5ed05d0ffb5c22cb944be2..cc673259c9d77fb67bf826d17ee888583abfcbf0
@@@ -1,67 -1,86 +1,74 @@@
 -#include <errno.h>
 -#include <stdio.h>
 -#include <stdlib.h>
 -#include <unistd.h>
 -
 -#include "rtr/rtr.h"
 -#include "slurm_loader.h"
  #include "clients.h"
 -#include "configuration.h"
 -#include "csv.h"
 -#include "vrps.h"
 +#include "config.h"
 +#include "console_handler.h"
 +#include "debug.h"
 +#include "extension.h"
 +#include "nid.h"
++#include "slurm_loader.h"
 +#include "thread_var.h"
 +#include "rsync/rsync.h"
 +#include "rtr/rtr.h"
 +#include "rtr/db/vrps.h"
  
 -/*
 - * This program is an RTR server.
 - *
 - * RTR ("RPKI-to-Router") is a protocol (defined in RFCs 6810 and 8210) that
 - * reports the work of an RPKI validator (cryptographcally-verified
 - * attestations that define the ASN that owns a given routing prefix). It is
 - * normally served to routers who wish to verify BGP claims.
 - */
 -int
 -main(int argc, char *argv[])
 +static int
 +start_rtr_server(void)
  {
 -      int err;
 -      char *json_file = NULL;
 -      int c;
 +      int error;
  
 -      while ((c = getopt(argc, argv, "f:")) != -1) {
 -              switch (c) {
 -              case 'f':
 -                      json_file = optarg;
 -                      break;
 -              case '?':
 -                      fprintf(stdout, "usage: %s -f <file name>\n", argv[0]);
 -                      return 0;
 -              }
 -      }
 -
 -      if (json_file == NULL) {
 -              fprintf(stderr, "Missing flag '-f <file name>'\n");
 -              return -EINVAL;
 -      }
 -
 -      err = config_init(json_file);
 -      if (err) {
 -              /*
 -               * TODO Special scenario, if the VRPs location doesn't exists
 -               * just send a warning (logged by the config_init function).
 -               *
 -               * This should be fixed later.
 -               */
 -              err = (err == -ENOENT ? 0 : err);
 -              goto end1;
 -      }
 -
 -      err = deltas_db_init();
 -      if (err)
 +      error = vrps_init();
 +      if (error)
                goto end1;
  
 -      err = clients_db_init();
 -      if (err)
 +      error = clients_db_init();
 +      if (error)
                goto end2;
  
 -      err = csv_parse_vrps_file();
 -      if (err)
++      error = slurm_load();
++      if (error)
+               goto end3;
 -      err = slurm_load();
 -      if (err)
 -              goto end4;
 -
 -      err = rtr_listen();
 -      if (err)
 -              goto end4;
 +      error = rtr_listen();
 +      rtr_cleanup(); /* TODO shouldn't this only happen on !error? */
  
 -      rtr_cleanup();
 -end4:
 -      slurm_cleanup();
+ end3:
        clients_db_destroy();
 -end2:
 -      deltas_db_destroy();
 -end1:
 -      config_cleanup();
 -      return err;
++      slurm_cleanup();
 +end2: vrps_destroy();
 +end1: return error;
 +}
 +
 +int
 +main(int argc, char **argv)
 +{
 +      int error;
 +
 +      print_stack_trace_on_segfault();
 +
 +      error = thvar_init();
 +      if (error)
 +              return error;
 +
 +      error = handle_flags_config(argc, argv);
 +      if (error)
 +              return error;
 +
 +      error = rsync_init();
 +      if (error)
 +              goto revert_config;
 +      error = nid_init();
 +      if (error)
 +              goto revert_rsync;
 +      error = extension_init();
 +      if (error)
 +              goto revert_rsync;
 +
 +      error = (config_get_server_address() != NULL)
 +          ? start_rtr_server()
 +          : validate_into_console();
 +
 +revert_rsync:
 +      rsync_destroy();
 +revert_config:
 +      free_rpki_config();
 +      return error;
  }
index 16a69aa808edd0282fb7e376b4415e9dda3df1bd,0000000000000000000000000000000000000000..d9557e0b33257f3ff61acc98df77ec03a078fbdc
mode 100644,000000..100644
--- /dev/null
@@@ -1,363 -1,0 +1,364 @@@
-       error = base64_decode(encoded, tal->spki, alloc_size, &tal->spki_len);
 +#define _GNU_SOURCE
 +
 +#include "tal.h"
 +
 +#include <errno.h>
 +#include <stdbool.h>
 +#include <stdlib.h>
 +#include <string.h>
 +#include <sys/stat.h>
 +#include <openssl/evp.h>
 +
 +#include "common.h"
 +#include "config.h"
 +#include "line_file.h"
 +#include "log.h"
 +#include "random.h"
 +#include "state.h"
 +#include "thread_var.h"
 +#include "crypto/base64.h"
 +#include "object/certificate.h"
 +#include "rsync/rsync.h"
 +
 +struct uris {
 +      char **array; /* This is an array of string pointers. */
 +      unsigned int count;
 +      unsigned int size;
 +};
 +
 +struct tal {
 +      char const *file_name;
 +      struct uris uris;
 +      unsigned char *spki; /* Decoded; not base64. */
 +      size_t spki_len;
 +};
 +
 +static int
 +uris_init(struct uris *uris)
 +{
 +      uris->count = 0;
 +      uris->size = 4; /* Most TALs only define one. */
 +      uris->array = malloc(uris->size * sizeof(char *));
 +      return (uris->array != NULL) ? 0 : -ENOMEM;
 +}
 +
 +static void
 +uris_destroy(struct uris *uris)
 +{
 +      unsigned int i;
 +      for (i = 0; i < uris->count; i++)
 +              free(uris->array[i]);
 +      free(uris->array);
 +}
 +
 +static int
 +uris_add(struct uris *uris, char *uri)
 +{
 +      char **tmp;
 +
 +      if (uris->count + 1 >= uris->size) {
 +              uris->size *= 2;
 +              tmp = realloc(uris->array, uris->size * sizeof(char *));
 +              if (tmp == NULL)
 +                      return pr_enomem();
 +              uris->array = tmp;
 +      }
 +
 +      uris->array[uris->count++] = uri;
 +      return 0;
 +}
 +
 +static int
 +read_uris(struct line_file *lfile, struct uris *uris)
 +{
 +      char *uri;
 +      int error;
 +
 +      error = lfile_read(lfile, &uri);
 +      if (error)
 +              return error;
 +
 +      if (uri == NULL)
 +              return pr_err("TAL file is empty.");
 +      if (strcmp(uri, "") == 0) {
 +              free(uri);
 +              return pr_err("There's no URI in the first line of the TAL.");
 +      }
 +
 +      error = uris_add(uris, uri);
 +      if (error)
 +              return error;
 +
 +      do {
 +              error = lfile_read(lfile, &uri);
 +              if (error)
 +                      return error;
 +
 +              if (uri == NULL)
 +                      return pr_err("TAL file ended prematurely. (Expected URI list, blank line and public key.)");
 +              if (strcmp(uri, "") == 0) {
 +                      free(uri);
 +                      return 0; /* Happy path */
 +              }
 +
 +              error = uris_add(uris, uri);
 +              if (error)
 +                      return error;
 +      } while (true);
 +}
 +
 +/*
 + * Will usually allocate slightly more because of the newlines, but I'm fine
 + * with it.
 + */
 +static size_t
 +get_spki_alloc_size(struct line_file *lfile)
 +{
 +      struct stat st;
 +      size_t result;
 +
 +      stat(lfile_name(lfile), &st);
 +      result = st.st_size - lfile_offset(lfile);
 +
 +      return EVP_DECODE_LENGTH(result);
 +}
 +
 +static int
 +read_spki(struct line_file *lfile, struct tal *tal)
 +{
 +      BIO *encoded; /* base64 encoded. */
 +      size_t alloc_size;
 +      int error;
 +
 +      alloc_size = get_spki_alloc_size(lfile);
 +      tal->spki = malloc(alloc_size);
 +      if (tal->spki == NULL)
 +              return -ENOMEM;
 +
 +      encoded = BIO_new_fp(lfile_fd(lfile), BIO_NOCLOSE);
 +      if (encoded == NULL) {
 +              free(tal->spki);
 +              return crypto_err("BIO_new_fp() returned NULL");
 +      }
 +
++      error = base64_decode(encoded, tal->spki, true, alloc_size,
++          &tal->spki_len);
 +      if (error)
 +              free(tal->spki);
 +
 +      BIO_free(encoded);
 +      return error;
 +}
 +
 +/**
 + * @file_name is expected to outlive @result.
 + */
 +int
 +tal_load(char const *file_name, struct tal **result)
 +{
 +      struct line_file *lfile;
 +      struct tal *tal;
 +      int error;
 +
 +      error = lfile_open(file_name, &lfile);
 +      if (error) {
 +              pr_errno(error, "Error opening file '%s'", file_name);
 +              goto fail4;
 +      }
 +
 +      tal = malloc(sizeof(struct tal));
 +      if (tal == NULL) {
 +              error = -ENOMEM;
 +              goto fail3;
 +      }
 +
 +      tal->file_name = file_name;
 +
 +      error = uris_init(&tal->uris);
 +      if (error)
 +              goto fail2;
 +
 +      error = read_uris(lfile, &tal->uris);
 +      if (error)
 +              goto fail1;
 +
 +      error = read_spki(lfile, tal);
 +      if (error)
 +              goto fail1;
 +
 +      lfile_close(lfile);
 +      *result = tal;
 +      return 0;
 +
 +fail1:
 +      uris_destroy(&tal->uris);
 +fail2:
 +      free(tal);
 +fail3:
 +      lfile_close(lfile);
 +fail4:
 +      return error;
 +}
 +
 +void tal_destroy(struct tal *tal)
 +{
 +      if (tal == NULL)
 +              return;
 +
 +      uris_destroy(&tal->uris);
 +      free(tal->spki);
 +      free(tal);
 +}
 +
 +int
 +foreach_uri(struct tal *tal, foreach_uri_cb cb, void *arg)
 +{
 +      struct rpki_uri uri;
 +      unsigned int i;
 +      int error;
 +
 +      for (i = 0; i < tal->uris.count; i++) {
 +              error = uri_init_str(&uri, tal->uris.array[i],
 +                  strlen(tal->uris.array[i]));
 +              if (error == ENOTRSYNC) {
 +                      /* Log level should probably be INFO. */
 +                      pr_debug("TAL has non-RSYNC URI; ignoring.");
 +                      continue;
 +              }
 +              if (error)
 +                      return error;
 +
 +              error = cb(tal, &uri, arg);
 +              uri_cleanup(&uri);
 +              if (error)
 +                      return error;
 +      }
 +
 +      return 0;
 +}
 +
 +void
 +tal_shuffle_uris(struct tal *tal)
 +{
 +      char **array = tal->uris.array;
 +      unsigned int count = tal->uris.count;
 +      char *tmp;
 +      long random_index;
 +      unsigned int i;
 +
 +      random_init();
 +
 +      for (i = 0; i < count; i++) {
 +              tmp = array[i];
 +              random_index = random_at_most(count - 1 - i) + i;
 +              array[i] = array[random_index];
 +              array[random_index] = tmp;
 +      }
 +}
 +
 +char const *
 +tal_get_file_name(struct tal *tal)
 +{
 +      return tal->file_name;
 +}
 +
 +void
 +tal_get_spki(struct tal *tal, unsigned char const **buffer, size_t *len)
 +{
 +      *buffer = tal->spki;
 +      *len = tal->spki_len;
 +}
 +
 +/**
 + * Performs the whole validation walkthrough on uri @uri, which is assumed to
 + * have been extracted from a TAL.
 + */
 +static int
 +handle_tal_uri(struct tal *tal, struct rpki_uri const *uri, void *arg)
 +{
 +      /*
 +       * Because of the way the foreach iterates, this function must return
 +       *
 +       * - 0 on soft errors.
 +       * - `> 0` on URI handled successfully.
 +       * - `< 0` on hard errors.
 +       *
 +       * A "soft error" is "the connection to the preferred URI fails, or the
 +       * retrieved CA certificate public key does not match the TAL public
 +       * key." (RFC 7730)
 +       *
 +       * A "hard error" is any other error.
 +       */
 +
 +      struct validation *state;
 +      int error;
 +
 +      error = download_files(uri, true);
 +      if (error) {
 +              return pr_warn("TAL URI '%s' could not be RSYNC'd.",
 +                  uri->global);
 +      }
 +
 +      error = validation_prepare(&state, tal, arg);
 +      if (error)
 +              return ENSURE_NEGATIVE(error);
 +
 +      error = vhandler_reset(arg);
 +      if (error)
 +              return ENSURE_NEGATIVE(error);
 +
 +      pr_debug_add("TAL URI '%s' {", uri_get_printable(uri));
 +
 +      if (!uri_is_certificate(uri)) {
 +              pr_err("TAL file does not point to a certificate. (Expected .cer, got '%s')",
 +                  uri_get_printable(uri));
 +              error = -EINVAL;
 +              goto end;
 +      }
 +
 +      error = certificate_traverse(NULL, uri, NULL, true);
 +      if (error) {
 +              switch (validation_pubkey_state(state)) {
 +              case PKS_INVALID:
 +                      error = 0;
 +                      break;
 +              case PKS_VALID:
 +              case PKS_UNTESTED:
 +                      error = ENSURE_NEGATIVE(error);
 +                      break;
 +              }
 +      } else {
 +              error = 1;
 +      }
 +
 +end:  validation_destroy(state);
 +      pr_debug_rm("}");
 +      return error;
 +}
 +
 +int
 +perform_standalone_validation(struct validation_handler *handler)
 +{
 +      struct tal *tal;
 +      int error;
 +
 +      fnstack_init();
 +      fnstack_push(config_get_tal());
 +
 +      error = tal_load(config_get_tal(), &tal);
 +      if (error)
 +              goto end;
 +
 +      if (config_get_shuffle_tal_uris())
 +              tal_shuffle_uris(tal);
 +      error = foreach_uri(tal, handle_tal_uri, handler);
 +      if (error > 0)
 +              error = 0;
 +      else if (error == 0)
 +              error = pr_err("None of the URIs of the TAL yielded a successful traversal.");
 +
 +end:  tal_destroy(tal);
 +      fnstack_pop();
 +      fnstack_cleanup();
 +      return error;
 +}
diff --cc src/rtr/pdu.c
Simple merge
diff --cc src/rtr/pdu.h
index a0f1252dd8f33c266470d1ac3f110dfa3fd476d0,8e61646308cc9440edf27e56008794fd4d611cf5..c34cc4c4dc1770b4ac2b0a6bf82f395b97bcc39a
@@@ -78,11 -79,20 +79,20 @@@ struct cache_reset_pdu 
        struct  pdu_header header;
  };
  
+ struct router_key_pdu {
+       struct  pdu_header header;
+       unsigned char   *ski;
+       size_t          ski_len;
+       u_int32_t       asn;
+       unsigned char   *spki;
+       size_t          spki_len;
+ };
  struct error_report_pdu {
        struct  pdu_header header;
 -      u_int32_t       error_pdu_length;
 +      uint32_t        error_pdu_length;
        void            *erroneous_pdu;
 -      u_int32_t       error_message_length;
 +      uint32_t        error_message_length;
        rtr_char        *error_message;
  };
  
Simple merge
index 87baf586119ce26aa1ca134572aee46226394163,b700545ea8fff3d49c93e8410e3087593a63bc85..7f8571f160ffd77fcf8f731f369bf1d614525b74
@@@ -68,7 -68,14 +68,17 @@@ length_end_of_data_pdu(struct end_of_da
        return len;
  }
  
 -static u_int32_t
 -length_router_key_pdu(struct router_key_pdu *pdu)
 -{
 -      return HEADER_LENGTH +
 -          pdu->ski_len + sizeof(pdu->asn) + pdu->spki_len;
 -}
++/* TODO Include Router Key PDU serials */
++/**
++ * static uint32_t
++ * length_router_key_pdu(struct router_key_pdu *pdu)
++ * {
++ *    return HEADER_LENGTH +
++ *        pdu->ski_len + sizeof(pdu->asn) + pdu->spki_len;
++ * }
++ */
 -static u_int32_t
 +static uint32_t
  length_error_report_pdu(struct error_report_pdu *pdu)
  {
        return HEADER_LENGTH +
@@@ -247,11 -377,11 +297,11 @@@ send_end_of_data_pdu(struct sender_comm
  }
  
  int
 -send_error_report_pdu(int fd, u_int8_t version, u_int16_t code,
 -struct pdu_header *err_pdu_header, char *message)
 +send_error_report_pdu(int fd, uint8_t version, uint16_t code,
 +struct pdu_header *err_pdu_header, char const *message)
  {
        struct error_report_pdu pdu;
-       char data[BUFFER_SIZE];
+       unsigned char data[BUFFER_SIZE];
        size_t len;
  
        set_header_values(&pdu.header, version, PDU_TYPE_ERROR_REPORT,
index f3ef6ecbc133fb2365e0740f87dfed1d798a4b59,5de4ba8212ee741af2c952a7fd1d24dc4c71f3d5..fbb74b206cd98d9f93445f8b316721420c9149e0
@@@ -18,9 -18,10 +18,10 @@@ free_buffer(struct data_buffer *buffer
  }
  
  static size_t
- serialize_pdu_header(struct pdu_header *header, uint16_t union_value, char *buf)
 -serialize_pdu_header(struct pdu_header *header, u_int16_t union_value,
++serialize_pdu_header(struct pdu_header *header, uint16_t union_value,
+     unsigned char *buf)
  {
-       char *ptr;
+       unsigned char *ptr;
  
        ptr = buf;
        ptr = write_int8(ptr, header->protocol_version);
  }
  
  size_t
- serialize_serial_notify_pdu(struct serial_notify_pdu *pdu, char *buf)
+ serialize_serial_notify_pdu(struct serial_notify_pdu *pdu, unsigned char *buf)
  {
        size_t head_size;
-       char *ptr;
+       unsigned char *ptr;
  
 -      head_size = serialize_pdu_header(&pdu->header, pdu->header.session_id,
 +      head_size = serialize_pdu_header(&pdu->header, pdu->header.m.session_id,
            buf);
  
        ptr = buf + head_size;
  }
  
  size_t
- serialize_cache_response_pdu(struct cache_response_pdu *pdu, char *buf)
+ serialize_cache_response_pdu(struct cache_response_pdu *pdu,
+     unsigned char *buf)
  {
        /* No payload to serialize */
 -      return serialize_pdu_header(&pdu->header, pdu->header.session_id, buf);
 +      return serialize_pdu_header(&pdu->header, pdu->header.m.session_id,
 +          buf);
  }
  
  size_t
- serialize_ipv4_prefix_pdu(struct ipv4_prefix_pdu *pdu, char *buf)
+ serialize_ipv4_prefix_pdu(struct ipv4_prefix_pdu *pdu, unsigned char *buf)
  {
        size_t head_size;
-       char *ptr;
+       unsigned char *ptr;
  
 -      head_size = serialize_pdu_header(&pdu->header, pdu->header.reserved,
 +      head_size = serialize_pdu_header(&pdu->header, pdu->header.m.reserved,
            buf);
  
        ptr = buf + head_size;
  }
  
  size_t
- serialize_ipv6_prefix_pdu(struct ipv6_prefix_pdu *pdu, char *buf)
+ serialize_ipv6_prefix_pdu(struct ipv6_prefix_pdu *pdu, unsigned char *buf)
  {
        size_t head_size;
-       char *ptr;
+       unsigned char *ptr;
  
 -      head_size = serialize_pdu_header(&pdu->header, pdu->header.reserved,
 +      head_size = serialize_pdu_header(&pdu->header, pdu->header.m.reserved,
            buf);
  
        ptr = buf + head_size;
  }
  
  size_t
- serialize_end_of_data_pdu(struct end_of_data_pdu *pdu, char *buf)
+ serialize_end_of_data_pdu(struct end_of_data_pdu *pdu, unsigned char *buf)
  {
        size_t head_size;
-       char *ptr;
+       unsigned char *ptr;
  
 -      head_size = serialize_pdu_header(&pdu->header, pdu->header.session_id,
 +      head_size = serialize_pdu_header(&pdu->header, pdu->header.m.session_id,
            buf);
  
        ptr = buf + head_size;
  }
  
  size_t
- serialize_cache_reset_pdu(struct cache_reset_pdu *pdu, char *buf)
+ serialize_cache_reset_pdu(struct cache_reset_pdu *pdu, unsigned char *buf)
  {
        /* No payload to serialize */
 -      return serialize_pdu_header(&pdu->header, pdu->header.reserved, buf);
 +      return serialize_pdu_header(&pdu->header, pdu->header.m.reserved, buf);
  }
  
+ /*
+  * Don't forget to use 'header->reserved' to set flags
+  */
  size_t
- serialize_error_report_pdu(struct error_report_pdu *pdu, char *buf)
+ serialize_router_key_pdu(struct router_key_pdu *pdu, unsigned char *buf)
+ {
+       size_t head_size;
+       unsigned char *ptr;
+       int i;
+       if (pdu->header.protocol_version == RTR_V0)
+               return 0;
 -      head_size = serialize_pdu_header(&pdu->header, pdu->header.reserved,
++      head_size = serialize_pdu_header(&pdu->header, pdu->header.m.reserved,
+           buf);
+       ptr = buf + head_size;
+       for (i = 0; i < pdu->ski_len; i++)
+               ptr = write_int8(ptr, pdu->ski[i]);
+       ptr = write_int32(ptr, pdu->asn);
+       for (i = 0; i < pdu->spki_len; i++)
+               ptr = write_int8(ptr, pdu->spki[i]);
+       return ptr - buf;
+ }
+ size_t
+ serialize_error_report_pdu(struct error_report_pdu *pdu, unsigned char *buf)
  {
        struct pdu_header *err_pdu_header;
        size_t head_size;
-       char *ptr, *tmp_ptr;
+       unsigned char *ptr;
+       char *tmp_ptr;
        int i;
  
 -      head_size = serialize_pdu_header(&pdu->header, pdu->header.error_code,
 +      head_size = serialize_pdu_header(&pdu->header, pdu->header.m.error_code,
            buf);
  
        ptr = buf + head_size;
index 9c23dce223ce850ebf3c860c4edaca9063bfb1d2,2c490be56526ba84bd27901078934870f9c4e68a..4889f7f2eb3c68e640c6a4dde127c65ed18eb19d
@@@ -8,18 -8,23 +8,21 @@@
  struct data_buffer {
        size_t len;
        size_t capacity;
-       char *data;
+       unsigned char *data;
  };
  
 -__BEGIN_DECLS
  void init_buffer(struct data_buffer *);
  void free_buffer(struct data_buffer *);
  
- size_t serialize_serial_notify_pdu(struct serial_notify_pdu *, char *);
- size_t serialize_cache_response_pdu(struct cache_response_pdu *, char *);
- size_t serialize_ipv4_prefix_pdu(struct ipv4_prefix_pdu *, char *);
- size_t serialize_ipv6_prefix_pdu(struct ipv6_prefix_pdu *, char *);
- size_t serialize_end_of_data_pdu(struct end_of_data_pdu *, char *);
- size_t serialize_cache_reset_pdu(struct cache_reset_pdu *, char *);
- size_t serialize_error_report_pdu(struct error_report_pdu *, char *);
+ size_t serialize_serial_notify_pdu(struct serial_notify_pdu *,
+     unsigned char *);
+ size_t serialize_cache_response_pdu(struct cache_response_pdu *,
+     unsigned char *);
+ size_t serialize_ipv4_prefix_pdu(struct ipv4_prefix_pdu *, unsigned char *);
+ size_t serialize_ipv6_prefix_pdu(struct ipv6_prefix_pdu *, unsigned char *);
+ size_t serialize_end_of_data_pdu(struct end_of_data_pdu *, unsigned char *);
+ size_t serialize_cache_reset_pdu(struct cache_reset_pdu *, unsigned char *);
+ size_t serialize_router_key_pdu(struct router_key_pdu *, unsigned char *);
+ size_t serialize_error_report_pdu(struct error_report_pdu *, unsigned char *);
 -__END_DECLS
  
  #endif /* SRC_RTR_PDU_SERIALIZER_H_ */
index f117b32d148f1cca7b82c951f8c2ac6df493fc7e,bb1a9be4ba8e4384c2a9899fc08dde0d14a73345..599d267df3d804a13c722c4834ebd2acc44b5bcc
@@@ -1,15 -1,15 +1,15 @@@
  #include "primitive_writer.h"
  
- char *
- write_int8(char *buf, uint8_t value)
+ unsigned char *
 -write_int8(unsigned char *buf, u_int8_t value)
++write_int8(unsigned char *buf, uint8_t value)
  {
        buf[0] = value;
        return buf + 1;
  }
  
  /** Big Endian. */
- char *
- write_int16(char *buf, uint16_t value)
+ unsigned char *
 -write_int16(unsigned char *buf, u_int16_t value)
++write_int16(unsigned char *buf, uint16_t value)
  {
        buf[0] = value >> 8;
        buf[1] = value;
@@@ -17,8 -17,8 +17,8 @@@
  }
  
  /** Big Endian. */
- char *
- write_int32(char *buf, uint32_t value)
+ unsigned char *
 -write_int32(unsigned char *buf, u_int32_t value)
++write_int32(unsigned char *buf, uint32_t value)
  {
        buf[0] = value >> 24;
        buf[1] = value >> 16;
index 8a34ce7d939273447996a29554bb7d28a4773fc9,7bed5a71c65bd35ac5b4b2395e699d168276a78a..badd1019c46a8ff82a4ed39195a16ef41e8ba7f0
@@@ -3,10 -3,12 +3,10 @@@
  
  #include <netinet/in.h>
  
- char *write_int8(char *, uint8_t);
- char *write_int16(char *, uint16_t);
- char *write_int32(char *, uint32_t);
- char *write_in_addr(char *, struct in_addr);
- char *write_in6_addr(char *, struct in6_addr);
 -__BEGIN_DECLS
 -unsigned char *write_int8(unsigned char *, u_int8_t);
 -unsigned char *write_int16(unsigned char *, u_int16_t);
 -unsigned char *write_int32(unsigned char *, u_int32_t);
++unsigned char *write_int8(unsigned char *, uint8_t);
++unsigned char *write_int16(unsigned char *, uint16_t);
++unsigned char *write_int32(unsigned char *, uint32_t);
+ unsigned char *write_in_addr(unsigned char *, struct in_addr);
+ unsigned char *write_in6_addr(unsigned char *, struct in6_addr);
 -__END_DECLS
  
  #endif /* RTR_PRIMITIVE_WRITER_H_ */
diff --cc src/slurm_db.c
index 0000000000000000000000000000000000000000,1279732c9392404efc21bf52e6404810afbcbc85..ea7749a2da8f1221778a85a198e1371c6d381205
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,247 +1,248 @@@
 -#include "array_list.h"
+ #include "slurm_db.h"
+ #include <stdbool.h>
+ #include <string.h>
++
++#include "data_structure/array_list.h"
+ ARRAY_LIST(al_filter_prefix, struct slurm_prefix)
+ ARRAY_LIST(al_assertion_prefix, struct slurm_prefix)
+ ARRAY_LIST(al_filter_bgpsec, struct slurm_bgpsec)
+ ARRAY_LIST(al_assertion_bgpsec, struct slurm_bgpsec)
+ struct arraylist_db {
+       struct al_filter_prefix filter_pfx_al;
+       struct al_assertion_prefix assertion_pfx_al;
+       struct al_filter_bgpsec filter_bgps_al;
+       struct al_assertion_bgpsec assertion_bgps_al;
+ } array_lists_db;
+ #define LOCATE_FUNCS(name, type, array_list, equal_cb, filter)                \
+       static type *                                                   \
+       name##_locate(array_list *base, type *obj)                      \
+       {                                                               \
+               type *cursor;                                           \
+                                                                       \
+               ARRAYLIST_FOREACH(base, cursor)                         \
+                       if (equal_cb(cursor, obj, filter))              \
+                               return cursor;                          \
+                                                                       \
+               return NULL;                                            \
+       }                                                               \
+                                                                       \
+       static bool                                                     \
+       name##_exists(array_list *base, type *obj)                      \
+       {                                                               \
+               return name##_locate(base, obj) != NULL;                \
+       }
+ int
+ slurm_db_init(void)
+ {
+       al_filter_prefix_init(&array_lists_db.filter_pfx_al);
+       al_assertion_prefix_init(&array_lists_db.assertion_pfx_al);
+       al_filter_bgpsec_init(&array_lists_db.filter_bgps_al);
+       al_assertion_bgpsec_init(&array_lists_db.assertion_bgps_al);
+       return 0;
+ }
+ static bool
+ prefix_filtered_by(struct slurm_prefix *prefix, struct slurm_prefix *filter)
+ {
+       /* Both have ASN */
+       if ((prefix->data_flag & SLURM_COM_FLAG_ASN) > 0 &&
+           (filter->data_flag & SLURM_COM_FLAG_ASN) > 0)
+               return prefix->asn == filter->asn;
+       /* Both have a prefix of the same type */
+       if ((prefix->data_flag & SLURM_PFX_FLAG_PREFIX) > 0 &&
+           (filter->data_flag & SLURM_PFX_FLAG_PREFIX) > 0 &&
+           prefix->addr_fam == filter->addr_fam &&
+           prefix->prefix_length == filter->prefix_length)
+               return ((prefix->addr_fam == AF_INET &&
+                   prefix->ipv4_prefix.s_addr == filter->ipv4_prefix.s_addr) ||
+                   (prefix->addr_fam == AF_INET6 &&
+                   IN6_ARE_ADDR_EQUAL(prefix->ipv6_prefix.s6_addr32,
+                   filter->ipv6_prefix.s6_addr32)));
+       return false;
+ }
+ static bool
+ prefix_equal(struct slurm_prefix *left, struct slurm_prefix *right,
+     bool filter)
+ {
+       bool equal;
+       /* Ignore the comments */
+       if ((left->data_flag & ~SLURM_COM_FLAG_COMMENT) !=
+           (right->data_flag & ~SLURM_COM_FLAG_COMMENT))
+               return filter && prefix_filtered_by(left, right);
+       /* It has the same data, compare it */
+       equal = true;
+       if ((left->data_flag & SLURM_COM_FLAG_ASN) > 0)
+               equal = equal && left->asn == right->asn;
+       if ((left->data_flag & SLURM_PFX_FLAG_PREFIX) > 0)
+               equal = equal && left->prefix_length == right->prefix_length
+                   && left->addr_fam == right->addr_fam
+                   && ((left->addr_fam == AF_INET
+                   && left->ipv4_prefix.s_addr == right->ipv4_prefix.s_addr)
+                   || (left->addr_fam == AF_INET6
+                   && IN6_ARE_ADDR_EQUAL(left->ipv6_prefix.s6_addr32,
+                   right->ipv6_prefix.s6_addr32)));
+       if ((left->data_flag & SLURM_PFX_FLAG_MAX_LENGTH) > 0)
+               equal = equal &&
+                   ((left->data_flag & SLURM_PFX_FLAG_MAX_LENGTH) > 0) &&
+                   left->max_prefix_length == right->max_prefix_length;
+       return equal;
+ }
+ static bool
+ bgpsec_filtered_by(struct slurm_bgpsec *bgpsec, struct slurm_bgpsec *filter)
+ {
+       /* Both have ASN */
+       if ((bgpsec->data_flag & SLURM_COM_FLAG_ASN) > 0 &&
+           (filter->data_flag & SLURM_COM_FLAG_ASN) > 0)
+               return bgpsec->asn == filter->asn;
+       /* Both have a SKI */
+       if ((bgpsec->data_flag & SLURM_BGPS_FLAG_SKI) > 0 &&
+           (filter->data_flag & SLURM_BGPS_FLAG_SKI) > 0 &&
+           bgpsec->ski_len == filter->ski_len)
+               return memcmp(bgpsec->ski, filter->ski, bgpsec->ski_len) == 0;
+       return false;
+ }
+ static bool
+ bgpsec_equal(struct slurm_bgpsec *left, struct slurm_bgpsec *right,
+     bool filter)
+ {
+       bool equal;
+       /* Ignore the comments */
+       if ((left->data_flag & ~SLURM_COM_FLAG_COMMENT) !=
+           (right->data_flag & ~SLURM_COM_FLAG_COMMENT))
+               return filter && bgpsec_filtered_by(left, right);
+       /* It has the same data, compare it */
+       equal = true;
+       if ((left->data_flag & SLURM_COM_FLAG_ASN) > 0)
+               equal = equal && left->asn == right->asn;
+       if ((left->data_flag & SLURM_BGPS_FLAG_SKI) > 0)
+               equal = equal && left->ski_len == right->ski_len &&
+                   memcmp(left->ski, right->ski, left->ski_len) == 0;
+       if ((left->data_flag & SLURM_BGPS_FLAG_ROUTER_KEY) > 0)
+               equal = equal &&
+                   left->router_public_key_len ==
+                   right->router_public_key_len &&
+                   memcmp(left->router_public_key, right->router_public_key,
+                   left->router_public_key_len) == 0;
+       return equal;
+ }
+ LOCATE_FUNCS(prefix_filter, struct slurm_prefix, struct al_filter_prefix,
+     prefix_equal, true)
+ LOCATE_FUNCS(bgpsec_filter, struct slurm_bgpsec, struct al_filter_bgpsec,
+     bgpsec_equal, true)
+ LOCATE_FUNCS(prefix_assertion, struct slurm_prefix, struct al_assertion_prefix,
+     prefix_equal, false)
+ LOCATE_FUNCS(bgpsec_assertion, struct slurm_bgpsec, struct al_assertion_bgpsec,
+     bgpsec_equal, false)
+ /*
+  * Try to persist the @prefix filter, if it already exists or is covered
+  * by another filter, then the error -EEXIST is returned; otherwise, returns
+  * the result of persisting the @prefix.
+  */
+ int
+ slurm_db_add_prefix_filter(struct slurm_prefix *prefix)
+ {
+       if (prefix_filter_exists(&array_lists_db.filter_pfx_al, prefix))
+               return -EEXIST;
+       return al_filter_prefix_add(&array_lists_db.filter_pfx_al, prefix);
+ }
+ /*
+  * Try to persist the @prefix assertion, if it already exists, then the error
+  * -EEXIST is returned; otherwise, returns the result of persisting the
+  * @prefix.
+  */
+ int
+ slurm_db_add_prefix_assertion(struct slurm_prefix *prefix)
+ {
+       if (prefix_assertion_exists(&array_lists_db.assertion_pfx_al, prefix))
+               return -EEXIST;
+       return al_assertion_prefix_add(&array_lists_db.assertion_pfx_al,
+           prefix);
+ }
+ /*
+  * Try to persist the @bgpsec filter, if it already exists or is covered
+  * by another filter, then the error -EEXIST is returned; otherwise, returns
+  * the result of persisting the @bgpsec.
+  */
+ int
+ slurm_db_add_bgpsec_filter(struct slurm_bgpsec *bgpsec)
+ {
+       if (bgpsec_filter_exists(&array_lists_db.filter_bgps_al, bgpsec))
+               return -EEXIST;
+       return al_filter_bgpsec_add(&array_lists_db.filter_bgps_al, bgpsec);
+ }
+ /*
+  * Try to persist the @bgpsec assertion, if it already exists, then the error
+  * -EEXIST is returned; otherwise, returns the result of persisting the
+  * @bgpsec.
+  */
+ int
+ slurm_db_add_bgpsec_assertion(struct slurm_bgpsec *bgpsec)
+ {
+       if (bgpsec_assertion_exists(&array_lists_db.assertion_bgps_al, bgpsec))
+               return -EEXIST;
+       return al_assertion_bgpsec_add(&array_lists_db.assertion_bgps_al,
+           bgpsec);
+ }
+ static void
+ clean_slurm_prefix(struct slurm_prefix *prefix)
+ {
+       if ((prefix->data_flag & SLURM_COM_FLAG_COMMENT) > 0)
+               free((void *)prefix->comment);
+ }
+ static void
+ clean_slurm_bgpsec(struct slurm_bgpsec *bgpsec)
+ {
+       if ((bgpsec->data_flag & SLURM_BGPS_FLAG_SKI) > 0)
+               free(bgpsec->ski);
+       if ((bgpsec->data_flag & SLURM_BGPS_FLAG_ROUTER_KEY) > 0)
+               free(bgpsec->router_public_key);
+       if ((bgpsec->data_flag & SLURM_COM_FLAG_COMMENT) > 0)
+               free((void *)bgpsec->comment);
+ }
+ void
+ slurm_db_cleanup(void)
+ {
+       al_filter_prefix_cleanup(&array_lists_db.filter_pfx_al,
+           clean_slurm_prefix);
+       al_filter_bgpsec_cleanup(&array_lists_db.filter_bgps_al,
+           clean_slurm_bgpsec);
+       al_assertion_prefix_cleanup(&array_lists_db.assertion_pfx_al,
+           clean_slurm_prefix);
+       al_assertion_bgpsec_cleanup(&array_lists_db.assertion_bgps_al,
+           clean_slurm_bgpsec);
+ }
index 0000000000000000000000000000000000000000,bae0987f357820e094b845290945e7c0297254f4..df249a5cccffe71e2878124c9fb7bd6f885e52a7
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,102 +1,102 @@@
 -#include "configuration.h"
+ #include "slurm_loader.h"
+ #include <err.h>
+ #include <errno.h>
+ #include <dirent.h>
+ #include <stdlib.h>
+ #include <string.h>
++#include "config.h"
+ #include "slurm_db.h"
+ #include "slurm_parser.h"
+ #define SLURM_FILE_EXTENSION  ".slurm"
+ static int
+ single_slurm_load(const char *dir_name, const char *file_name)
+ {
+       char *ext, *fullpath, *tmp;
+       int error;
+       ext = strrchr(file_name, '.');
+       /* Ignore file if extension isn't the expected */
+       if (ext == NULL || strcmp(ext, SLURM_FILE_EXTENSION) != 0)
+               return 0;
+       /* Get the full file path */
+       tmp = strdup(dir_name);
+       if (tmp == NULL) {
+               warn("Couldn't create temporal char for SLURM");
+               return -errno;
+       }
+       tmp = realloc(tmp, strlen(tmp) + 1 + strlen(file_name) + 1);
+       if (tmp == NULL) {
+               warn("Couldn't reallocate temporal char for SLURM");
+               return -errno;
+       }
+       strcat(tmp, "/");
+       strcat(tmp, file_name);
+       fullpath = realpath(tmp, NULL);
+       if (fullpath == NULL) {
+               warn("Error getting real path for file '%s' at dir '%s'",
+                   dir_name, file_name);
+               free(tmp);
+               return -errno;
+       }
+       error = slurm_parse(fullpath);
+       free(tmp);
+       free(fullpath);
+       return error;
+ }
+ int
+ slurm_load(void)
+ {
+       DIR *dir_loc;
+       struct dirent *dir_ent;
+       char const *slurm_dir;
+       int error;
+       /* Optional configuration */
+       slurm_dir = config_get_slurm_location();
+       if (slurm_dir == NULL)
+               return 0;
+       error = slurm_db_init();
+       if (error)
+               return error;
+       dir_loc = opendir(slurm_dir);
+       if (dir_loc == NULL) {
+               warn("Couldn't open dir %s", slurm_dir);
+               return -errno;
+       }
+       errno = 0;
+       while ((dir_ent = readdir(dir_loc)) != NULL) {
+               error = single_slurm_load(slurm_dir, dir_ent->d_name);
+               if (error) {
+                       warnx("The error was at SLURM file %s",
+                           dir_ent->d_name);
+                       goto end;
+               }
+               errno = 0;
+       }
+       if (errno) {
+               warn("Error reading dir %s", slurm_dir);
+               error = -errno;
+       }
+ end:
+       closedir(dir_loc);
+       return error;
+ }
+ void
+ slurm_cleanup(void)
+ {
+       /* Only if the SLURM was configured */
+       if (config_get_slurm_location() != NULL)
+               slurm_db_cleanup();
+ }
index 0000000000000000000000000000000000000000,af285c378424e4c84175f33a745a03e49d784484..fed754632f6ee3fa9de1fa92382b91fa6c51f07c
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,763 +1,763 @@@
 -      return prefix4_decode(text, prefixv4);
+ #include "slurm_parser.h"
+ #include <err.h>
+ #include <errno.h>
+ #include <stdbool.h>
+ #include <stdint.h>
+ #include <string.h>
+ #include <openssl/evp.h>
+ #include "crypto/base64.h"
+ #include "address.h"
+ #include "json_parser.h"
+ #include "slurm_db.h"
+ /* JSON members */
+ #define SLURM_VERSION                 "slurmVersion"
+ #define VALIDATION_OUTPUT_FILTERS     "validationOutputFilters"
+ #define PREFIX_FILTERS                        "prefixFilters"
+ #define BGPSEC_FILTERS                        "bgpsecFilters"
+ #define LOCALLY_ADDED_ASSERTIONS      "locallyAddedAssertions"
+ #define PREFIX_ASSERTIONS             "prefixAssertions"
+ #define BGPSEC_ASSERTIONS             "bgpsecAssertions"
+ /* Prefix and BGPsec properties */
+ #define PREFIX                                "prefix"
+ #define ASN                           "asn"
+ #define MAX_PREFIX_LENGTH             "maxPrefixLength"
+ #define SKI                           "SKI"
+ #define ROUTER_PUBLIC_KEY             "routerPublicKey"
+ #define COMMENT                               "comment"
+ #define CHECK_REQUIRED(element, name)                         \
+       if (element == NULL) {                                  \
+               warnx("SLURM member '%s' is required", name);   \
+               return -EINVAL;                                 \
+       }
+ static int handle_json(json_t *);
+ int
+ slurm_parse(char const *location)
+ {
+       json_t *json_root;
+       json_error_t json_error;
+       int error;
+       json_root = json_load_file(location, JSON_REJECT_DUPLICATES,
+           &json_error);
+       if (json_root == NULL) {
+               warnx("SLURM JSON error on line %d, column %d: %s",
+                   json_error.line, json_error.column, json_error.text);
+               return -ENOENT;
+       }
+       error = handle_json(json_root);
+       json_decref(json_root);
+       return error;
+ }
+ /*
+  * TODO Maybe some of the parsing functions can be on a common place, since
+  * csv.c also does a similar parsing
+  */
+ static int
+ parse_prefix4(char *text, struct ipv4_prefix *prefixv4)
+ {
+       if (text == NULL)
+               return -EINVAL;
 -      return prefix6_decode(text, prefixv6);
++      return str_to_prefix4(text, prefixv4);
+ }
+ static int
+ parse_prefix6(char *text, struct ipv6_prefix *prefixv6)
+ {
+       if (text == NULL)
+               return -EINVAL;
 -parse_prefix_length(char *text, unsigned int *value, int max_value)
++      return str_to_prefix6(text, prefixv6);
+ }
+ static int
 -      return prefix_length_decode(text, value, max_value);
++parse_prefix_length(char *text, uint8_t *value, uint8_t max_value)
+ {
+       if (text == NULL)
+               return -EINVAL;
 -          isv4 ? &prefixv4.len : &prefixv6.len,
 -          isv4 ? 32 : 128);
++      return str_to_prefix_length(text, value, max_value);
+ }
+ /*
+  * Any unknown members should be treated as errors, RFC8416 3.1:
+  * "JSON members that are not defined here MUST NOT be used in SLURM
+  * files. An RP MUST consider any deviations from the specifications to
+  * be errors."
+  */
+ static bool
+ valid_members_count(json_t *object, size_t expected_size)
+ {
+       return json_object_size(object) == expected_size;
+ }
+ static int
+ set_asn(json_t *object, bool is_assertion, u_int32_t *result,
+     u_int8_t *flag, size_t *members_loaded)
+ {
+       json_int_t int_tmp;
+       int error;
+       error = json_get_int(object, ASN, &int_tmp);
+       if (error == -ENOENT) {
+               if (is_assertion) {
+                       warnx("ASN is required");
+                       return -EINVAL;
+               } else
+                       return 0; /* Optional for filters */
+       } else if (error)
+               return error;
+       /* An underflow or overflow will be considered here */
+       if (int_tmp < 0 || UINT32_MAX < int_tmp) {
+               warnx("ASN (%lld) is out of range [0 - %u].", int_tmp,
+                   UINT32_MAX);
+               return -EINVAL;
+       }
+       *flag = *flag | SLURM_COM_FLAG_ASN;
+       *result = (u_int32_t) int_tmp;
+       (*members_loaded)++;
+       return 0;
+ }
+ static int
+ set_comment(json_t *object, char const **comment, u_int8_t *flag,
+     size_t *members_loaded)
+ {
+       char const *tmp;
+       int error;
+       error = json_get_string(object, COMMENT, &tmp);
+       if (error && error == -ENOENT)
+               return 0; /* Optional member */
+       else if (error)
+               return error;
+       *comment = strdup(tmp);
+       *flag = *flag | SLURM_COM_FLAG_COMMENT;
+       (*members_loaded)++;
+       return 0;
+ }
+ static int
+ set_prefix(json_t *object, bool is_assertion, struct slurm_prefix *result,
+     size_t *members_loaded)
+ {
+       struct ipv4_prefix prefixv4;
+       struct ipv6_prefix prefixv6;
+       char const *str_prefix;
+       char *clone, *token;
+       bool isv4;
+       int error;
+       /* First part: Prefix in string format */
+       error = json_get_string(object, PREFIX, &str_prefix);
+       if (error && error == -ENOENT) {
+               if (is_assertion) {
+                       warnx("SLURM assertion prefix is required");
+                       return -EINVAL;
+               } else
+                       return 0; /* Optional for filters */
+       } else if (error)
+               return error;
+       clone = strdup(str_prefix);
+       if (clone == NULL) {
+               warn("Couldn't allocate string to parse prefix");
+               return -errno;
+       }
+       token = strtok(clone, "/");
+       isv4 = strchr(token, ':') == NULL;
+       if (isv4)
+               error = parse_prefix4(token, &prefixv4);
+       else
+               error = parse_prefix6(token, &prefixv6);
+       if (error) {
+               free(clone);
+               return error;
+       }
+       /* Second part: Prefix length in numeric format */
+       token = strtok(NULL, "/");
+       error = parse_prefix_length(token,
 -              error = prefix4_validate(&prefixv4);
++          (isv4 ? &prefixv4.len : &prefixv6.len),
++          (isv4 ? 32 : 128));
+       free(clone);
+       if (error)
+               return error;
+       if (isv4) {
 -              error = prefix6_validate(&prefixv6);
++              error = ipv4_prefix_validate(&prefixv4);
+               if (error)
+                       return error;
+               result->addr_fam = AF_INET;
+               result->ipv4_prefix = prefixv4.addr;
+               result->prefix_length = prefixv4.len;
+       } else {
++              error = ipv6_prefix_validate(&prefixv6);
+               if (error)
+                       return error;
+               result->addr_fam = AF_INET6;
+               result->ipv6_prefix = prefixv6.addr;
+               result->prefix_length = prefixv6.len;
+       }
+       result->data_flag |= SLURM_PFX_FLAG_PREFIX;
+       (*members_loaded)++;
+       return 0;
+ }
+ static int
+ set_max_prefix_length(json_t *object, bool is_assertion, u_int8_t addr_fam,
+     u_int8_t *result, u_int8_t *flag, size_t *members_loaded)
+ {
+       json_int_t int_tmp;
+       int error;
+       error = json_get_int(object, MAX_PREFIX_LENGTH, &int_tmp);
+       if (error == -ENOENT)
+               return 0; /* Optional for assertions, unsupported by filters */
+       if (error && is_assertion)
+               return error;
+       /* Unsupported by filters */
+       if (!is_assertion) {
+               warnx("Prefix filter can't have a max prefix length");
+               return -EINVAL;
+       }
+       /* An underflow or overflow will be considered here */
+       if (int_tmp <= 0 || (addr_fam == AF_INET ? 32 : 128) < int_tmp) {
+               warnx("Max prefix length (%lld) is out of range [1 - %d].",
+                   int_tmp, (addr_fam == AF_INET ? 32 : 128));
+               return -EINVAL;
+       }
+       *flag = *flag | SLURM_PFX_FLAG_MAX_LENGTH;
+       *result = (u_int8_t) int_tmp;
+       (*members_loaded)++;
+       return 0;
+ }
+ int
+ validate_base64url_encoded(const char *encoded)
+ {
+       /*
+        * RFC 8416, sections 3.3.2 (SKI member), and 3.4.2 (SKI and
+        * routerPublicKey members): "{..} whose value is the Base64 encoding
+        * without trailing '=' (Section 5 of [RFC4648])"
+        */
+       if (strrchr(encoded, '=') != NULL) {
+               warnx("The base64 encoded value has trailing '='");
+               return -EINVAL;
+       }
+       /*
+        * IMHO there's an error at RFC 8416 regarding the use of base64
+        * encoding. The RFC cites "RFC 4648 section 5" to justify the
+        * removal of trailing pad char '=', a section that refers to base64url
+        * encoding. So, at the same RFC 4648 section, there's this paragraph:
+        * "This encoding may be referred to as "base64url".  This encoding
+        * should not be regarded as the same as the "base64" encoding and
+        * should not be referred to as only "base64".  Unless clarified
+        * otherwise, "base64" refers to the base 64 in the previous section."
+        *
+        * Well, I believe that the RFC 8416 must say something like:
+        * "{..} whose value is the Base64url encoding without trailing '='
+        * (Section 5 of [RFC4648])"
+        */
+       return 0;
+ }
+ static int
+ set_ski(json_t *object, bool is_assertion, struct slurm_bgpsec *result,
+     size_t *members_loaded)
+ {
+       char const *str_encoded;
+       int error;
+       error = json_get_string(object, SKI, &str_encoded);
+       if (error && error == -ENOENT) {
+               if (is_assertion) {
+                       warnx("SLURM assertion %s is required", SKI);
+                       return -EINVAL;
+               } else
+                       return 0; /* Optional for filters */
+       } else if (error)
+               return error;
+       error = validate_base64url_encoded(str_encoded);
+       if (error)
+               return error;
+       error = base64url_decode(str_encoded, &result->ski, &result->ski_len);
+       if (error)
+               return error;
+       /* Validate that's at least 20 octects long */
+       if (result->ski_len != 20) {
+               warnx("The decoded SKI must be 20 octets long");
+               free(result->ski);
+               return -EINVAL;
+       }
+       result->data_flag = result->data_flag | SLURM_BGPS_FLAG_SKI;
+       (*members_loaded)++;
+       return 0;
+ }
+ static int
+ set_router_pub_key(json_t *object, bool is_assertion,
+     struct slurm_bgpsec *result, size_t *members_loaded)
+ {
+       char const *str_encoded;
+       int error;
+       error = json_get_string(object, ROUTER_PUBLIC_KEY, &str_encoded);
+       if (error == -ENOENT && !is_assertion)
+               return 0; /* OK for filters */
+       /* Required by assertions */
+       if (error && is_assertion) {
+               if (error == -ENOENT) {
+                       warnx("SLURM assertion %s is required", ROUTER_PUBLIC_KEY);
+                       return -EINVAL;
+               }
+               return error;
+       }
+       /* Unsupported by filters */
+       if (!is_assertion) {
+               warnx("BGPsec filter can't have a router public key");
+               return -EINVAL;
+       }
+       error = validate_base64url_encoded(str_encoded);
+       if (error)
+               return error;
+       error = base64url_decode(str_encoded, &result->router_public_key,
+           &result->router_public_key_len);
+       if (error)
+               return error;
+       /*
+        * TODO Validate that 'routerPublicKey' is: "the equivalent to the
+        * subjectPublicKeyInfo value of the router certificate's public key,
+        * as described in [RFC8208].  This is the full ASN.1 DER encoding of
+        * the subjectPublicKeyInfo, including the ASN.1 tag and length values
+        * of the subjectPublicKeyInfo SEQUENCE.
+        */
+       /*
+        * TODO When the merge is done, reuse the functions at fort-validator
+        *
+        * #include <libcmscodec/SubjectPublicKeyInfo.h>
+        * #include "asn1/decode.h"
+        * struct SubjectPublicKeyInfo *router_pki;
+        * error = asn1_decode(result->router_public_key,
+        *     result->router_public_key_len, &asn_DEF_SubjectPublicKeyInfo,
+        *     (void **) &router_pki);
+        */
+       result->data_flag = result->data_flag | SLURM_BGPS_FLAG_ROUTER_KEY;
+       (*members_loaded)++;
+       return 0;
+ }
+ static void
+ init_slurm_prefix(struct slurm_prefix *slurm_prefix)
+ {
+       slurm_prefix->data_flag = SLURM_COM_FLAG_NONE;
+       slurm_prefix->asn = 0;
+       slurm_prefix->ipv6_prefix = in6addr_any;
+       slurm_prefix->prefix_length = 0;
+       slurm_prefix->max_prefix_length = 0;
+       slurm_prefix->addr_fam = 0;
+       slurm_prefix->comment = NULL;
+ }
+ static int
+ load_single_prefix(json_t *object, bool is_assertion)
+ {
+       struct slurm_prefix result;
+       size_t member_count;
+       int error;
+       if (!json_is_object(object)) {
+               warnx("Not a valid JSON object");
+               return -EINVAL;
+       }
+       init_slurm_prefix(&result);
+       member_count = 0;
+       error = set_asn(object, is_assertion, &result.asn, &result.data_flag,
+           &member_count);
+       if (error)
+               return error;
+       error = set_prefix(object, is_assertion, &result, &member_count);
+       if (error)
+               return error;
+       error = set_max_prefix_length(object, is_assertion, result.addr_fam,
+           &result.max_prefix_length, &result.data_flag, &member_count);
+       if (error)
+               return error;
+       error = set_comment(object, &result.comment, &result.data_flag,
+           &member_count);
+       if (error)
+               return error;
+       /* A single comment isn't valid */
+       if (result.data_flag == SLURM_COM_FLAG_COMMENT) {
+               warnx("Single comments aren't valid");
+               error = -EINVAL;
+               goto release_comment;
+       }
+       /* A filter must have ASN and/or prefix */
+       if (!is_assertion) {
+               if ((result.data_flag &
+                   (SLURM_COM_FLAG_ASN | SLURM_PFX_FLAG_PREFIX)) == 0) {
+                       warnx("Prefix filter must have an asn and/or prefix");
+                       error = -EINVAL;
+                       goto release_comment;
+               }
+               /* Validate expected members */
+               if (!valid_members_count(object, member_count)) {
+                       warnx("Prefix filter has unknown members (see RFC 8416 section 3.3.1)");
+                       error = -EINVAL;
+                       goto release_comment;
+               }
+               error = slurm_db_add_prefix_filter(&result);
+               if (error)
+                       goto release_comment;
+               return 0;
+       }
+       /*
+        * An assertion must have ASN and prefix, the validation is done at
+        * set_asn and set_prefix
+        */
+       if ((result.data_flag & SLURM_PFX_FLAG_MAX_LENGTH) > 0)
+               if (result.prefix_length > result.max_prefix_length) {
+                       warnx(
+                           "Prefix length is greater than max prefix length");
+                       error = -EINVAL;
+                       goto release_comment;
+               }
+       /* Validate expected members */
+       if (!valid_members_count(object, member_count)) {
+               warnx("Prefix assertion has unknown members (see RFC 8416 section 3.4.1)");
+               error = -EINVAL;
+               goto release_comment;
+       }
+       error = slurm_db_add_prefix_assertion(&result);
+       if (error)
+               goto release_comment;
+       return 0;
+ release_comment:
+       free((void *)result.comment);
+       return error;
+ }
+ static int
+ load_prefix_array(json_t *array, bool is_assertion)
+ {
+       json_t *element;
+       int index, error;
+       json_array_foreach(array, index, element) {
+               error = load_single_prefix(element, is_assertion);
+               if (error) {
+                       if (error == -EEXIST)
+                               warnx(
+                                   "The prefix %s element #%d, is duplicated or covered by another %s; SLURM loading will be stopped",
+                                   (is_assertion ? "assertion" : "filter"),
+                                   index + 1,
+                                   (is_assertion ? "assertion" : "filter"));
+                       else
+                               warnx(
+                                   "Error at prefix %s, element #%d, SLURM loading will be stopped",
+                                   (is_assertion ? "assertions" : "filters"),
+                                   index + 1);
+                       return error;
+               }
+       }
+       return 0;
+ }
+ static void
+ init_slurm_bgpsec(struct slurm_bgpsec *slurm_bgpsec)
+ {
+       slurm_bgpsec->data_flag = SLURM_COM_FLAG_NONE;
+       slurm_bgpsec->asn = 0;
+       slurm_bgpsec->ski = NULL;
+       slurm_bgpsec->ski_len = 0;
+       slurm_bgpsec->router_public_key = NULL;
+       slurm_bgpsec->router_public_key_len = 0;
+       slurm_bgpsec->comment = NULL;
+ }
+ static int
+ load_single_bgpsec(json_t *object, bool is_assertion)
+ {
+       struct slurm_bgpsec result;
+       size_t member_count;
+       int error;
+       if (!json_is_object(object)) {
+               warnx("Not a valid JSON object");
+               return -EINVAL;
+       }
+       init_slurm_bgpsec(&result);
+       member_count = 0;
+       error = set_asn(object, is_assertion, &result.asn, &result.data_flag,
+           &member_count);
+       if (error)
+               return error;
+       error = set_ski(object, is_assertion, &result, &member_count);
+       if (error)
+               return error;
+       error = set_router_pub_key(object, is_assertion, &result,
+           &member_count);
+       if (error)
+               goto release_ski;
+       error = set_comment(object, &result.comment, &result.data_flag,
+           &member_count);
+       if (error)
+               goto release_router_key;
+       /* A single comment isn't valid */
+       if (result.data_flag == SLURM_COM_FLAG_COMMENT) {
+               warnx("Single comments aren't valid");
+               error = -EINVAL;
+               goto release_comment;
+       }
+       /* A filter must have ASN and/or SKI */
+       if (!is_assertion) {
+               if ((result.data_flag &
+                   (SLURM_COM_FLAG_ASN | SLURM_BGPS_FLAG_SKI)) == 0) {
+                       warnx("BGPsec filter must have an asn and/or SKI");
+                       error = -EINVAL;
+                       goto release_comment;
+               }
+               /* Validate expected members */
+               if (!valid_members_count(object, member_count)) {
+                       warnx("BGPsec filter has unknown members (see RFC 8416 section 3.3.2)");
+                       error = -EINVAL;
+                       goto release_comment;
+               }
+               error = slurm_db_add_bgpsec_filter(&result);
+               if (error)
+                       goto release_comment;
+               return 0;
+       }
+       /* Validate expected members */
+       if (!valid_members_count(object, member_count)) {
+               warnx("BGPsec assertion has unknown members (see RFC 8416 section 3.4.2)");
+               error = -EINVAL;
+               goto release_comment;
+       }
+       error = slurm_db_add_bgpsec_assertion(&result);
+       if (error)
+               goto release_comment;
+       return 0;
+ release_comment:
+       free((void *)result.comment);
+ release_router_key:
+       free(result.router_public_key);
+ release_ski:
+       free(result.ski);
+       return error;
+ }
+ static int
+ load_bgpsec_array(json_t *array, bool is_assertion)
+ {
+       json_t *element;
+       int index, error;
+       json_array_foreach(array, index, element) {
+               error = load_single_bgpsec(element, is_assertion);
+               if (error) {
+                       if (error == -EEXIST)
+                               warnx(
+                                   "The bgpsec %s element #%d, is duplicated or covered by another %s; SLURM loading will be stopped",
+                                   (is_assertion ? "assertion" : "filter"),
+                                   index + 1,
+                                   (is_assertion ? "assertion" : "filter"));
+                       else
+                               warnx(
+                                   "Error at bgpsec %s, element #%d, SLURM loading will be stopped",
+                                   (is_assertion ? "assertions" : "filters"),
+                                   index + 1);
+                       return error;
+               }
+       }
+       return 0;
+ }
+ static int
+ load_version(json_t *root)
+ {
+       json_int_t version;
+       int error;
+       version = -1;
+       error = json_get_int(root, SLURM_VERSION, &version);
+       if (error)
+               return error;
+       /* Validate data */
+       if (version != 1) {
+               warnx("'%s' must be 1", SLURM_VERSION);
+               return -EINVAL;
+       }
+       return 0;
+ }
+ static int
+ load_filters(json_t *root)
+ {
+       json_t *filters, *prefix, *bgpsec;
+       size_t expected_members;
+       int error;
+       filters = json_get_object(root, VALIDATION_OUTPUT_FILTERS);
+       CHECK_REQUIRED(filters, VALIDATION_OUTPUT_FILTERS)
+       prefix = json_get_array(filters, PREFIX_FILTERS);
+       CHECK_REQUIRED(prefix, PREFIX_FILTERS)
+       bgpsec = json_get_array(filters, BGPSEC_FILTERS);
+       CHECK_REQUIRED(bgpsec, BGPSEC_FILTERS)
+       expected_members = 2;
+       if (!valid_members_count(filters, expected_members)) {
+               warnx(
+                   "SLURM '%s' must contain only %lu members (RFC 8416 section 3.2)",
+                   VALIDATION_OUTPUT_FILTERS,
+                   expected_members);
+               return -EINVAL;
+       }
+       /* Arrays loaded, now iterate */
+       error = load_prefix_array(prefix, false);
+       if (error)
+               return error;
+       error = load_bgpsec_array(bgpsec, false);
+       if (error)
+               return error;
+       return 0;
+ }
+ static int
+ load_assertions(json_t *root)
+ {
+       json_t *assertions, *prefix, *bgpsec;
+       size_t expected_members;
+       int error;
+       assertions = json_get_object(root, LOCALLY_ADDED_ASSERTIONS);
+       CHECK_REQUIRED(assertions, LOCALLY_ADDED_ASSERTIONS)
+       prefix = json_get_array(assertions, PREFIX_ASSERTIONS);
+       CHECK_REQUIRED(prefix, PREFIX_ASSERTIONS)
+       bgpsec = json_get_array(assertions, BGPSEC_ASSERTIONS);
+       CHECK_REQUIRED(bgpsec, BGPSEC_ASSERTIONS)
+       expected_members = 2;
+       if (!valid_members_count(assertions, expected_members)) {
+               warnx(
+                   "SLURM '%s' must contain only %lu members (RFC 8416 section 3.2)",
+                   LOCALLY_ADDED_ASSERTIONS,
+                   expected_members);
+               return -EINVAL;
+       }
+       error = load_prefix_array(prefix, true);
+       if (error)
+               return error;
+       error = load_bgpsec_array(bgpsec, true);
+       if (error)
+               return error;
+       return 0;
+ }
+ static int
+ handle_json(json_t *root)
+ {
+       size_t expected_members;
+       int error;
+       if (!json_is_object(root)) {
+               warnx("The root of the SLURM is not a JSON object.");
+               return -EINVAL;
+       }
+       error = load_version(root);
+       if (error)
+               return error;
+       error = load_filters(root);
+       if (error)
+               return error;
+       error = load_assertions(root);
+       if (error)
+               return error;
+       expected_members = 3;
+       if (!valid_members_count(root, expected_members)) {
+               warnx(
+                   "SLURM root must have only %lu members (RFC 8416 section 3.2)",
+                   expected_members);
+               return -EINVAL;
+       }
+       return 0;
+ }