diff --git a/.github/workflows/cross-compile.yml b/.github/workflows/cross-compile.yml
index fa60252cbe0269a0f5030f148b473abbbb44046b..80785a5a2d94eccc6966f15ce9f1e03132f12327 100644
--- a/.github/workflows/cross-compile.yml
+++ b/.github/workflows/cross-compile.yml
@@ -12,6 +12,8 @@ jobs:
 
       - name: Checkout repository
         uses: actions/checkout@v3
+        with:
+          submodules: recursive
       
       - name: Install Python packages
         run: pip install -r $GITHUB_WORKSPACE/requirements.txt
diff --git a/.github/workflows/full-test.yml b/.github/workflows/full-test.yml
index a09c010b61e0519355724b52312192fe4d202b59..79bf5dce6a44d019146c27bfba3febcefc37dc32 100644
--- a/.github/workflows/full-test.yml
+++ b/.github/workflows/full-test.yml
@@ -11,6 +11,8 @@ jobs:
     
       - name: Checkout repository
         uses: actions/checkout@v3
+        with:
+          submodules: recursive
 
       - name: Install required packages
         run: sudo $GITHUB_WORKSPACE/.ci_scripts/full-test/install_packages.sh
diff --git a/.github/workflows/publish-docs.yml b/.github/workflows/publish-docs.yml
index a08272dc806816175b5d6673db17009f5ddc39b0..7126e9344cf3c31712379dcea7f72fe4836e7f17 100644
--- a/.github/workflows/publish-docs.yml
+++ b/.github/workflows/publish-docs.yml
@@ -19,7 +19,7 @@ jobs:
       - name: Checkout repository
         uses: actions/checkout@v3
         with:
-          submodules: "true"
+          submodules: recursive
 
       - name: Install required packages
         run: sudo $GITHUB_WORKSPACE/.ci_scripts/publish-docs/install_packages.sh
diff --git a/include/dns_map.h b/include/dns_map.h
deleted file mode 100644
index 77de135f0ae3e5d6490073effb3b0ab349347028..0000000000000000000000000000000000000000
--- a/include/dns_map.h
+++ /dev/null
@@ -1,132 +0,0 @@
-/**
- * @file include/dns_map.h
- * @brief Implementation of a DNS domain name to IP addresses mapping, using Joshua J Baker's hashmap.c (https://github.com/tidwall/hashmap.c)
- * @date 2022-09-06
- * 
- * @copyright Copyright (c) 2022
- * 
- */
-
-#ifndef _IOTFIREWALL_DNS_MAP_
-#define _IOTFIREWALL_DNS_MAP_
-
-#include <stdlib.h>
-#include <stdint.h>
-#include <stdbool.h>
-#include <string.h>
-#include "hashmap.h"
-#include "packet_utils.h"
-
-// Initial size of the DNS table
-// If set to 0, the default size will be 16
-#define DNS_MAP_INIT_SIZE 0
-
-
-////////// TYPE DEFINITIONS //////////
-
-/**
- * List of IP addresses
- */
-typedef struct ip_list {
-    uint8_t ip_count;         // Number of IP addresses
-    ip_addr_t *ip_addresses;  // List of IP addresses
-} ip_list_t;
-
-/**
- * DNS table entry:
- * mapping between domain name and a list of IP addresses.
- */
-typedef struct dns_entry {
-    char *domain_name;  // Domain name
-    ip_list_t ip_list;  // List of IP addresses
-} dns_entry_t;
-
-/**
- * Alias for the hashmap structure.
- */
-typedef struct hashmap dns_map_t;
-
-
-////////// FUNCTIONS //////////
-
-/**
- * @brief Initialize an ip_list_t structure.
- *
- * Creates an empty list of IP addresses.
- * The `ip_count` field is set to 0,
- * and the `ip_addresses` field is set to NULL.
- *
- * @return ip_list_t newly initialized structure
- */
-ip_list_t ip_list_init();
-
-/**
- * @brief Checks if a dns_entry_t structure contains a given IP address.
- *
- * @param dns_entry pointer to the DNS entry to process
- * @param ip_address IP address to check the presence of
- * @return true if the IP address is present in the DNS entry, false otherwise
- */
-bool dns_entry_contains(dns_entry_t *dns_entry, ip_addr_t ip_address);
-
-/**
- * Create a new DNS table.
- * 
- * @return the newly created DNS table 
- */
-dns_map_t* dns_map_create();
-
-/**
- * Destroy (free) a DNS table.
- * 
- * @param table the DNS table to free
- */
-void dns_map_free(dns_map_t *table);
-
-/**
- * Add IP addresses corresponding to a given domain name in the DNS table.
- * If the domain name was already present, its IP addresses will be replaced by the new ones.
- * 
- * @param table the DNS table to add the entry to
- * @param domain_name the domain name of the entry
- * @param ip_list an ip_list_t structure containing the list of IP addresses
- */
-void dns_map_add(dns_map_t *table, char *domain_name, ip_list_t ip_list);
-
-/**
- * Remove a domain name (and its corresponding IP addresses) from the DNS table.
- * 
- * @param table the DNS table to remove the entry from
- * @param domain_name the domain name of the entry to remove
- */
-void dns_map_remove(dns_map_t *table, char *domain_name);
-
-/**
- * Retrieve the IP addresses corresponding to a given domain name in the DNS table.
- * 
- * @param table the DNS table to retrieve the entry from
- * @param domain_name the domain name of the entry to retrieve
- * @return a pointer to a dns_entry structure containing the IP addresses corresponding to the domain name,
- *         or NULL if the domain name was not found in the DNS table
- */
-dns_entry_t* dns_map_get(dns_map_t *table, char *domain_name);
-
-/**
- * Retrieve the IP addresses corresponding to a given domain name,
- * and remove the domain name from the DNS table.
- * 
- * @param table the DNS table to retrieve the entry from
- * @param domain_name the domain name of the entry to retrieve
- * @return a pointer to a dns_entry structure containing the IP addresses corresponding to the domain name,
- *         or NULL if the domain name was not found in the DNS table
- */
-dns_entry_t* dns_map_pop(dns_map_t *table, char *domain_name);
-
-/**
- * @brief Print a DNS table entry.
- *
- * @param dns_entry the DNS table entry to print
- */
-void dns_entry_print(dns_entry_t *dns_entry);
-
-#endif /* _IOTFIREWALL_DNS_MAP_ */
diff --git a/include/packet_utils.h b/include/packet_utils.h
deleted file mode 100644
index 9f383ae9f83c36d4eb9e3983b117960fe9880c0b..0000000000000000000000000000000000000000
--- a/include/packet_utils.h
+++ /dev/null
@@ -1,184 +0,0 @@
-/**
- * @file include/packet_utils.h
- * @brief Utilitaries for payload manipulation and display
- * @date 2022-09-09
- * 
- * @copyright Copyright (c) 2022
- * 
- */
-
-#ifndef _IOTFIREWALL_PACKET_UTILS_
-#define _IOTFIREWALL_PACKET_UTILS_
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdint.h>
-#include <stdbool.h>
-#include <string.h>
-#include <arpa/inet.h>
-#include "sha256.h"
-
-#define MAC_ADDR_LENGTH  6
-#define MAC_ADDR_STRLEN  18
-#define IPV4_ADDR_LENGTH 4
-#define IPV6_ADDR_LENGTH 16
-
-/**
- * @brief IP (v4 or v6) address value
- */
-typedef union {
-    uint32_t ipv4;  // IPv4 address, as a 32-bit unsigned integer in network byte order
-    uint8_t ipv6[IPV6_ADDR_LENGTH];  // IPv6 address, as a 16-byte array
-} ip_val_t;
-
-/**
- * @brief IP (v4 or v6) address
- */
-typedef struct {
-    uint8_t version;  // IP version (4 or 6, 0 if not set)
-    ip_val_t value;   // IP address value (0 if not set)
-} ip_addr_t;
-
-/**
- * Print a packet payload.
- * 
- * @param length length of the payload in bytes
- * @param data pointer to the start of the payload
- */
-void print_payload(int length, uint8_t *data);
-
-/**
- * Converts a hexstring payload to a data buffer.
- * 
- * @param hexstring the hexstring to convert
- * @param payload a double pointer to the payload, which will be set to the start of the payload
- * @return the length of the payload in bytes
- */
-size_t hexstr_to_payload(char *hexstring, uint8_t **payload);
-
-/**
- * Converts a MAC address from its hexadecimal representation
- * to its string representation.
- *
- * @param mac_hex MAC address in hexadecimal representation
- * @return the same MAC address in string representation
- */
-char *mac_hex_to_str(uint8_t mac_hex[]);
-
-/**
- * Converts a MAC address from its string representation
- * to its hexadecimal representation.
- *
- * @param mac_str MAC address in string representation
- * @return the same MAC address in hexadecimal representation
- */
-uint8_t *mac_str_to_hex(char *mac_str);
-
-/**
- * Converts an IPv4 address from its network order numerical representation
- * to its string representation.
- * (Wrapper arount inet_ntoa)
- * 
- * @param ipv4_net IPv4 address in hexadecimal representation
- * @return the same IPv4 address in string representation
- */
-char* ipv4_net_to_str(uint32_t ipv4_net);
-
-/**
- * Converts an IPv4 address from its string representation
- * to its network order numerical representation.
- * (Wrapper arount inet_aton)
- * 
- * @param ipv4_str IPv4 address in string representation
- * @return the same IPv4 address in network order numerical representation
- */
-uint32_t ipv4_str_to_net(char *ipv4_str);
-
-/**
- * Converts an IPv4 addres from its hexadecimal representation
- * to its string representation.
- * 
- * @param ipv4_hex IPv4 address in hexadecimal representation
- * @return the same IPv4 address in string representation
- */
-char* ipv4_hex_to_str(char *ipv4_hex);
-
-/**
- * Converts an IPv4 address from its string representation
- * to its hexadecimal representation.
- * 
- * @param ipv4_str IPv4 address in string representation
- * @return the same IPv4 address in hexadecimal representation
- */
-char* ipv4_str_to_hex(char *ipv4_str);
-
-/**
- * @brief Converts an IPv6 address to its string representation.
- * 
- * @param ipv6 the IPv6 address
- * @return the same IPv6 address in string representation
- */
-char* ipv6_net_to_str(uint8_t ipv6[]);
-
-/**
- * Converts an IPv6 address from its string representation
- * to its network representation (a 16-byte array).
- *
- * @param ipv6_str IPv6 address in string representation
- * @return the same IPv6 address as a 16-byte array
- */
-uint8_t* ipv6_str_to_net(char *ipv6_str);
-
-/**
- * @brief Converts an IP (v4 or v6) address to its string representation.
- * 
- * @param ip_addr the IP address, as an ip_addr_t struct
- * @return the same IP address in string representation
- */
-char* ip_net_to_str(ip_addr_t ip_addr);
-
-/**
- * Converts an IP (v4 or v6) address from its string representation
- * to an ip_addr_t struct.
- *
- * @param ip_str IP (v4 or v6) address in string representation
- * @return the same IP address as a ip_addr_t struct
- */
-ip_addr_t ip_str_to_net(char *ip_str, uint8_t version);
-
-/**
- * @brief Compare two IPv6 addresses.
- *
- * @param ipv6_1 first IPv6 address
- * @param ipv6_2 second IPv6 address
- * @return true if the two addresses are equal, false otherwise
- */
-bool compare_ipv6(uint8_t *ipv6_1, uint8_t *ipv6_2);
-
-/**
- * @brief Compare two IP (v4 or v6) addresses.
- *
- * @param ip_1 first IP address
- * @param ip_2 second IP address
- * @return true if the two addresses are equal, false otherwise
- */
-bool compare_ip(ip_addr_t ip_1, ip_addr_t ip_2);
-
-/**
- * @brief Compute SHA256 hash of a given payload.
- *
- * @param payload Payload to hash
- * @param payload_len Payload length, including padding (in bytes)
- * @return uint8_t* SHA256 hash of the payload
- */
-uint8_t* compute_hash(uint8_t *payload, int payload_len);
-
-/**
- * @brief Print a SHA256 hash.
- *
- * @param hash SHA256 hash to print
- */
-void print_hash(uint8_t *hash);
-
-
-#endif /* _IOTFIREWALL_PACKET_UTILS_ */
diff --git a/include/parsers/coap.h b/include/parsers/coap.h
deleted file mode 100644
index f0fec91667729c12176d6097d6ee0dd568903d23..0000000000000000000000000000000000000000
--- a/include/parsers/coap.h
+++ /dev/null
@@ -1,87 +0,0 @@
-/**
- * @file include/parsers/coap.h
- * @brief CoAP message parser
- * @date 2022-11-30
- * 
- * @copyright Copyright (c) 2022
- * 
- */
-
-#ifndef _IOTFIREWALL_COAP_
-#define _IOTFIREWALL_COAP_
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdint.h>
-#include <string.h>
-#include <arpa/inet.h>
-#include "parsers/http.h"
-
-
-/**
- * @brief CoAP message type
- */
-typedef enum
-{
-    COAP_CON = 0,
-    COAP_NON = 1,
-    COAP_ACK = 2,
-    COAP_RST = 3
-} coap_type_t;
-
-/**
- * @brief CoAP Option number
- */
-typedef enum
-{
-    COAP_URI_PATH = 11,
-    COAP_URI_QUERY = 15
-} coap_option_t;
-
-/**
- * @brief Abstraction of a CoAP message
- */
-typedef struct coap_message
-{
-    coap_type_t type;      // CoAP message type
-    http_method_t method;  // CoAP method, analogous to HTTP
-    char *uri;             // Message URI
-    uint16_t uri_len;      // URI length
-} coap_message_t;
-
-
-////////// FUNCTIONS //////////
-
-///// PARSING /////
-
-/**
- * @brief Parse a CoAP message.
- *
- * @param data pointer to the start of the CoAP message
- * @param length length of the CoAP message, in bytes
- * @return the parsed CoAP message
- */
-coap_message_t coap_parse_message(uint8_t *data, uint16_t length);
-
-
-///// DESTROY /////
-
-/**
- * @brief Free the memory allocated for a CoAP message.
- *
- * @param message the CoAP message to free
- */
-void coap_free_message(coap_message_t message);
-
-
-///// PRINTING /////
-
-/**
- * @brief Print a CoAP message.
- *
- * @param message the CoAP message to print
- */
-void coap_print_message(coap_message_t message);
-
-
-#endif /* _IOTFIREWALL_COAP_ */
diff --git a/include/parsers/dhcp.h b/include/parsers/dhcp.h
deleted file mode 100644
index 9a1c386cbb3cbab19bf17d5d701e54c9d26b4260..0000000000000000000000000000000000000000
--- a/include/parsers/dhcp.h
+++ /dev/null
@@ -1,174 +0,0 @@
-/**
- * @file include/parsers/dhcp.h
- * @brief DHCP message parser
- * @date 2022-09-12
- * 
- * @copyright Copyright (c) 2022
- * 
- */
-
-#ifndef _IOTFIREWALL_DHCP_
-#define _IOTFIREWALL_DHCP_
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdint.h>
-#include <string.h>
-#include <arpa/inet.h>
-
-#define MAX_HW_LEN            16
-#define DHCP_HEADER_LEN       236
-#define DHCP_MAX_OPTION_COUNT 20
-#define DHCP_MAGIC_COOKIE     0x63825363
-
-
-////////// TYPE DEFINITIONS //////////
-
-/**
- * DHCP opcode
- */
-typedef enum
-{
-    DHCP_BOOTREQUEST = 1,
-    DHCP_BOOTREPLY = 2
-} dhcp_opcode_t;
-
-/**
- * Useful DHCP option codes
- */
-typedef enum
-{
-    DHCP_PAD = 0,
-    DHCP_MESSAGE_TYPE = 53,
-    DHCP_END = 255
-} dhcp_option_code_t;
-
-/**
- * DHCP message type
- */
-typedef enum
-{
-    DHCP_DISCOVER = 1,
-    DHCP_OFFER = 2,
-    DHCP_REQUEST = 3,
-    DHCP_DECLINE = 4,
-    DHCP_ACK = 5,
-    DHCP_NAK = 6,
-    DHCP_RELEASE = 7,
-    DHCP_INFORM = 8
-} dhcp_message_type_t;
-
-/**
- * DHCP Option
- */
-typedef struct dhcp_option {
-    dhcp_option_code_t code;
-    uint8_t length;
-    uint8_t *value;
-} dhcp_option_t;
-
-/**
- * DHCP Options
- */
-typedef struct dhcp_options {
-    uint8_t count;                     // Number of options
-    dhcp_message_type_t message_type;  // DHCP Message type (stored for convenience)
-    dhcp_option_t *options;            // List of options
-} dhcp_options_t;
-
-/**
- * DHCP Message
- */
-typedef struct dhcp_message {
-    dhcp_opcode_t op;            // DHCP opcode
-    uint8_t htype;         // Hardware address type
-    uint8_t hlen;          // Hardware address length
-    uint8_t hops;          // Number of hops
-    uint32_t xid;          // Transaction ID
-    uint16_t secs;         // Seconds elapsed since client began address acquisition or renewal process
-    uint16_t flags;        // DHCP flags
-    uint32_t ciaddr;       // Client IP address
-    uint32_t yiaddr;       // Your (client) IP address
-    uint32_t siaddr;       // Next server IP address
-    uint32_t giaddr;       // Relay agent IP address
-    uint8_t chaddr[16];    // Client hardware address
-    uint8_t sname[64];     // Optional server host name
-    uint8_t file[128];     // Boot file name
-    dhcp_options_t options;  // DHCP options
-} dhcp_message_t;
-
-
-////////// FUNCTIONS //////////
-
-///// PARSING /////
-
-/**
- * @brief Parse the header of a DHCP message (not including options)
- * 
- * @param data a pointer to the start of the DHCP message
- * @return the parsed DHCP message with the header fields filled in
- */
-dhcp_message_t dhcp_parse_header(uint8_t *data);
-
-/**
- * @brief Parse a DHCP option
- * 
- * @param data a pointer to the start of the DHCP option
- * @param offset a pointer to the current offset inside the DHCP message
- *               Its value will be updated to point to the next option
- * @return the parsed DHCP option
- */
-dhcp_option_t dhcp_parse_option(uint8_t *data, uint16_t *offset);
-
-/**
- * @brief Parse DHCP options
- * 
- * @param data a pointer to the start of the DHCP options list
- * @return a pointer to the start of the parsed DHCP options
- */
-dhcp_options_t dhcp_parse_options(uint8_t *data);
-
-/**
- * @brief Parse a DHCP message
- * 
- * @param data a pointer to the start of the DHCP message
- * @return the parsed DHCP message
- */
-dhcp_message_t dhcp_parse_message(uint8_t *data);
-
-
-///// DESTROY //////
-
-/**
- * @brief Free the memory allocated for a DHCP message.
- * 
- * @param message the DHCP message to free
- */
-void dhcp_free_message(dhcp_message_t message);
-
-
-///// PRINTING /////
-
-/**
- * @brief Print the header of a DHCP message
- * 
- * @param message the DHCP message to print the header of
- */
-void dhcp_print_header(dhcp_message_t message);
-
-/**
- * @brief Print a DHCP option
- * 
- * @param option the DHCP option to print
- */
-void dhcp_print_option(dhcp_option_t option);
-
-/**
- * @brief Print a DHCP message
- * 
- * @param message the DHCP message to print
- */
-void dhcp_print_message(dhcp_message_t message);
-
-
-#endif /* _IOTFIREWALL_DHCP_ */
diff --git a/include/parsers/dns.h b/include/parsers/dns.h
deleted file mode 100644
index 7b6f76b613d1bf76a9df4d5edde95bf4a1cc93bc..0000000000000000000000000000000000000000
--- a/include/parsers/dns.h
+++ /dev/null
@@ -1,271 +0,0 @@
-/**
- * @file include/parsers/dns.h
- * @brief DNS message parser
- * @date 2022-09-09
- * 
- * @copyright Copyright (c) 2022
- * 
- */
-
-#ifndef _IOTFIREWALL_DNS_
-#define _IOTFIREWALL_DNS_
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdint.h>
-#include <stdbool.h>
-#include <string.h>
-#include <arpa/inet.h>
-#include "packet_utils.h"
-#include "dns_map.h"
-
-#define DNS_HEADER_SIZE 12
-#define DNS_MAX_DOMAIN_NAME_LENGTH 100
-#define DNS_QR_FLAG_MASK 0x8000
-#define DNS_CLASS_MASK 0x7fff
-#define DNS_COMPRESSION_MASK 0x3fff
-
-
-////////// TYPE DEFINITIONS //////////
-
-/**
- * DNS types
- */
-typedef enum {
-    A     =  1,
-    NS    =  2,
-    MD    =  3,
-    MF    =  4,
-    CNAME =  5,
-    SOA   =  6,
-    MB    =  7,
-    MG    =  8,
-    MR    =  9,
-    NULL_ = 10,
-    WKS   = 11,
-    PTR   = 12,
-    HINFO = 13,
-    MINFO = 14,
-    MX    = 15,
-    TXT   = 16,
-    AAAA  = 28,
-    OPT   = 41,  // Used to specify extensions
-    ANY   = 255  // Used to query any type
-} dns_rr_type_t;
-
-/**
- * DNS Header
- */
-typedef struct dns_header {
-    uint16_t id;
-    uint16_t flags;
-    bool qr;           // 0 if the message is a query, 1 if it is a response
-    uint16_t qdcount;  // Number of entries in Question section
-    uint16_t ancount;  // Number of Resource Records in Answer section
-    uint16_t nscount;  // Number of Resource Records in Authority section
-    uint16_t arcount;  // Number of Resource Records in Additional section
-} dns_header_t;
-
-/**
- * DNS Question
- */
-typedef struct dns_question {
-    char *qname;
-    dns_rr_type_t qtype;
-    uint16_t qclass;
-} dns_question_t;
-
-/**
- * RDATA field of a DNS Resource Record
- */
-typedef union {
-    char *domain_name;  // Domain name, character string
-    ip_addr_t ip;       // IP (v4 or v6) address
-    uint8_t *data;      // Generic data, series of bytes
-} rdata_t;
-
-/**
- * DNS Resource Record
- */
-typedef struct dns_resource_record {
-    char *name;
-    dns_rr_type_t rtype;
-    uint16_t rclass;
-    uint32_t ttl;
-    uint16_t rdlength;
-    rdata_t rdata;
-} dns_resource_record_t;
-
-/**
- * DNS Message
- */
-typedef struct dns_message {
-    dns_header_t header;
-    dns_question_t *questions;
-    dns_resource_record_t *answers;
-    dns_resource_record_t *authorities;
-    dns_resource_record_t *additionals;
-} dns_message_t;
-
-
-////////// FUNCTIONS //////////
-
-///// PARSING /////
-
-/**
- * Parse a DNS header.
- * A DNS header is always 12 bytes.
- * 
- * @param data a pointer pointing to the start of the DNS message
- * @param offset a pointer to the current parsing offset
- * @return the parsed header
- */
-dns_header_t dns_parse_header(uint8_t *data, uint16_t *offset);
-
-/**
- * Parse a DNS question section.
- * 
- * @param qdcount the number of questions present in the question section
- * @param data a pointer pointing to the start of the DNS message
- * @param offset a pointer to the current parsing offset
- * @return the parsed question section
- */
-dns_question_t* dns_parse_questions(uint16_t qdcount, uint8_t *data, uint16_t *offset);
-
-/**
- * Parse a DNS resource record list.
- * 
- * @param count the number of resource records present in the section
- * @param data a pointer pointing to the start of the DNS message
- * @param offset a pointer to the current parsing offset
- * @return the parsed resource records list
- */
-dns_resource_record_t* dns_parse_rrs(uint16_t count, uint8_t *data, uint16_t *offset);
-
-/**
- * Parse a DNS message.
- * 
- * @param data a pointer to the start of the DNS message
- * @return the parsed DNS message
- */
-dns_message_t dns_parse_message(uint8_t *data);
-
-
-///// LOOKUP /////
-
-/**
- * @brief Check if a given DNS Questions list contains a domain name which has a given suffix.
- *
- * @param questions DNS Questions list
- * @param qdcount number of Questions in the list
- * @param suffix the domain name suffix to search for
- * @param suffix_length the length of the domain name suffix
- * @return true if a domain name with the given suffix is found is found in the Questions list,
- *         false otherwise
- */
-bool dns_contains_suffix_domain_name(dns_question_t *questions, uint16_t qdcount, char *suffix, uint16_t suffix_length);
-
-/**
- * @brief Check if a given domain name is fully contained in a DNS Questions list.
- * 
- * @param questions DNS Questions list
- * @param qdcount number of Questions in the list
- * @param domain_name the domain name to search for
- * @return true if the full domain name is found in the Questions list, false otherwise
- */
-bool dns_contains_full_domain_name(dns_question_t *questions, uint16_t qdcount, char *domain_name);
-
-/**
- * @brief Search for a specific domain name in a DNS Questions list.
- * 
- * @param questions DNS Questions list
- * @param qdcount number of Suestions in the list
- * @param domain_name the domain name to search for
- * @return the DNS Question related to the given domain name, or NULL if not found
- */
-dns_question_t* dns_get_question(dns_question_t *questions, uint16_t qdcount, char *domain_name);
-
-/**
- * @brief Retrieve the IP addresses corresponding to a given domain name in a DNS Answers list.
- * 
- * Searches a DNS Answer list for a specific domain name and returns the corresponding IP address.
- * Processes each Answer recursively if the Answer Type is a CNAME.
- * 
- * @param answers DNS Answers list to search in
- * @param ancount number of Answers in the list
- * @param domain_name domain name to search for
- * @return struct ip_list representing the list of corresponding IP addresses
- */
-ip_list_t dns_get_ip_from_name(dns_resource_record_t *answers, uint16_t ancount, char *domain_name);
-
-
-///// DESTROY /////
-
-/**
- * Free the memory allocated for a DNS message.
- * 
- * @param question the DNS message to free
- */
-void dns_free_message(dns_message_t message);
-
-
-///// PRINTING /////
-
-/**
- * Print a DNS header.
- * 
- * @param message the DNS header
- */
-void dns_print_header(dns_header_t header);
-
-/**
- * Print a DNS Question
- * 
- * @param question the DNS Question
- */
-void dns_print_question(dns_question_t question);
-
-/**
- * Print a DNS Question section.
- * 
- * @param qdcount the number of Questions in the Question section
- * @param questions the list of DNS Questions
- */
-void dns_print_questions(uint16_t qdcount, dns_question_t *questions);
-
-/**
- * Return a string representation of the given RDATA value.
- * 
- * @param rtype the type corresponding to the RDATA value
- * @param rdlength the length, in bytes, of the RDATA value
- * @param rdata the RDATA value, stored as a union type
- * @return a string representation of the RDATA value
- */
-char* dns_rdata_to_str(dns_rr_type_t rtype, uint16_t rdlength, rdata_t rdata);
-
-/**
- * Print a DNS Resource Record.
- * 
- * @param section_name the name of the Resource Record section
- * @param rr the DNS Resource Record
- */
-void dns_print_rr(char* section_name, dns_resource_record_t rr);
-
-/**
- * Print a DNS Resource Records section.
- * 
- * @param section_name the name of the Resource Record section
- * @param count the number of Resource Records in the section
- * @param rrs the list of DNS Resource Records
- */
-void dns_print_rrs(char* section_name, uint16_t count, dns_resource_record_t *rrs);
-
-/**
- * Print a DNS message.
- * 
- * @param message the DNS message
- */
-void dns_print_message(dns_message_t message);
-
-
-#endif /* _IOTFIREWALL_DNS_ */
diff --git a/include/parsers/header.h b/include/parsers/header.h
deleted file mode 100644
index d7a010552b15e762a2ee603b9c47f0b42d8132d1..0000000000000000000000000000000000000000
--- a/include/parsers/header.h
+++ /dev/null
@@ -1,140 +0,0 @@
-/**
- * @file include/parsers/header.h
- * @brief Parser for layer 3 and 4 headers (currently only IP, UDP and TCP)
- * 
- * Parser for layer 3 and 4 headers.
- * Currently supported protocols:
- *   - Layer 3:
- *     - IP
- *   - Layer 4:
- *     - UDP
- *     - TCP
- * 
- * @date 2022-09-09
- * 
- * @copyright Copyright (c) 2022
- * 
- */
-
-#ifndef _IOTFIREWALL_HEADER_
-#define _IOTFIREWALL_HEADER_
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdint.h>
-#include <string.h>
-#include <arpa/inet.h>
-#include "packet_utils.h"
-
-#define IPV6_HEADER_LENGTH 40
-#define UDP_HEADER_LENGTH  8
-
-
-/**
- * IP protocols assigned to their protocol number
- */
-typedef enum {
-    ICMP = 1,
-    IGMP = 2,
-    TCP  = 6,
-    UDP  = 17
-} ip_protocol_t;
-
-/**
- * Retrieve the length of a packet's IPv4 header.
- *
- * @param data a pointer to the start of the packet's IPv4 header
- * @return the size, in bytes, of the IPv4 header
- */
-size_t get_ipv4_header_length(uint8_t *data);
-
-/**
- * Retrieve the length of a packet's IPv6 header.
- *
- * @param data a pointer to the start of the packet's IPv6 header
- * @return the size, in bytes, of the IPv6 header
- */
-size_t get_ipv6_header_length(uint8_t *data);
-
-/**
- * Retrieve the length of a packet's UDP header.
- *
- * @param data a pointer to the start of the packet's UDP (layer 4) header
- * @return the size, in bytes, of the UDP header
- */
-size_t get_udp_header_length(uint8_t *data);
-
-/**
- * Retrieve the length of a packet's TCP header.
- * 
- * @param data a pointer to the start of the packet's TCP (layer 4) header
- * @return the size, in bytes, of the UDP header
- */
-size_t get_tcp_header_length(uint8_t *data);
-
-/**
- * Retrieve the length of a packet's layer 3 header (IPv4 or IPv6).
- *
- * @param data a pointer to the start of the packet's layer 3 header
- * @return the size, in bytes, of the layer 3 header
- */
-size_t get_l3_header_length(uint8_t *data);
-
-/**
- * Retrieve the length of a packet's layer-3 and layer-4 headers.
- * 
- * @param data a pointer to the start of the packet's layer-3 header
- * @return the size, in bytes, of the UDP header
- */
-size_t get_headers_length(uint8_t* data);
-
-/**
- * @brief Retrieve the length of a UDP payload.
- * 
- * @param data pointer to the start of the UDP header
- * @return length of the UDP payload, in bytes
- */
-uint16_t get_udp_payload_length(uint8_t *data);
-
-/**
- * @brief Retrieve the source port from a layer 4 header.
- * 
- * @param data pointer to the start of the layer 4 header
- * @return destination port
- */
-uint16_t get_dst_port(uint8_t* data);
-
-/**
- * @brief Retrieve the source address from an IPv4 header.
- *
- * @param data pointer to the start of the IPv4 header
- * @return source IPv4 address, in network byte order
- */
-uint32_t get_ipv4_src_addr(uint8_t *data);
-
-/**
- * @brief Retrieve the destination address from an IPv4 header.
- *
- * @param data pointer to the start of the IPv4 header
- * @return destination IPv4 address, in network byte order
- */
-uint32_t get_ipv4_dst_addr(uint8_t *data);
-
-/**
- * @brief Retrieve the source address from an IPv6 header.
- *
- * @param data pointer to the start of the IPv6 header
- * @return source IPv6 address, as a 16-byte array
- */
-uint8_t* get_ipv6_src_addr(uint8_t *data);
-
-/**
- * @brief Retrieve the destination address from an IPv6 header.
- *
- * @param data pointer to the start of the IPv6 header
- * @return destination IPv6 address, as a 16-byte array
- */
-uint8_t* get_ipv6_dst_addr(uint8_t *data);
-
-
-#endif /* _IOTFIREWALL_HEADER_ */
diff --git a/include/parsers/http.h b/include/parsers/http.h
deleted file mode 100644
index 28d4d6e95038ec42f280b25e941387127f3d1668..0000000000000000000000000000000000000000
--- a/include/parsers/http.h
+++ /dev/null
@@ -1,101 +0,0 @@
-/**
- * @file include/parsers/http.h
- * @brief HTTP message parser
- * @date 2022-09-09
- * 
- * @copyright Copyright (c) 2022
- * 
- */
-
-#ifndef _IOTFIREWALL_HTTP_
-#define _IOTFIREWALL_HTTP_
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdint.h>
-#include <stdbool.h>
-
-#define HTTP_MESSAGE_MIN_LEN 16   // Minimum length of a HTTP message
-#define HTTP_METHOD_MAX_LEN  7    // Maximum length of a HTTP method
-#define HTTP_URI_DEFAULT_LEN 100  // Default length of a HTTP URI
-
-
-/**
- * HTTP methods
- */
-typedef enum
-{
-    HTTP_GET,
-    HTTP_HEAD,
-    HTTP_POST,
-    HTTP_PUT,
-    HTTP_DELETE,
-    HTTP_CONNECT,
-    HTTP_OPTIONS,
-    HTTP_TRACE,
-    HTTP_UNKNOWN
-} http_method_t;
-
-/**
- * Abstraction of a HTTP message
- */
-typedef struct http_message {
-    bool is_request;       // True if the message is a request, false if it is a response
-    http_method_t method;  // HTTP method (GET, POST, etc.)
-    char *uri;             // Message URI
-} http_message_t;
-
-
-////////// FUNCTIONS //////////
-
-///// PARSING /////
-
-/**
- * @brief Check if a TCP message is a HTTP message.
- * 
- * @param data pointer to the start of the TCP payload
- * @param dst_port TCP destination port
- * @return true if the message is a HTTP message
- * @return false if the message is not a HTTP message
- */
-bool is_http(uint8_t *data);
-
-/**
- * @brief Parse the method and URI of HTTP message.
- * 
- * @param data pointer to the start of the HTTP message
- * @param src_port TCP destination port
- * @return the parsed HTTP message
- */
-http_message_t http_parse_message(uint8_t *data, uint16_t dst_port);
-
-
-///// DESTROY /////
-
-/**
- * @brief Free the memory allocated for a HTTP message.
- * 
- * @param message the HTTP message to free
- */
-void http_free_message(http_message_t message);
-
-
-///// PRINTING /////
-
-/**
- * @brief Converts a HTTP method from enum value to character string.
- * 
- * @param method the HTTP method in enum value
- * @return the same HTTP method as a character string
- */
-char* http_method_to_str(http_method_t method);
-
-/**
- * @brief Print an HTTP message.
- * 
- * @param message the HTTP message to print
- */
-void http_print_message(http_message_t message);
-
-
-#endif /* _IOTFIREWALL_HTTP_ */
diff --git a/include/parsers/igmp.h b/include/parsers/igmp.h
deleted file mode 100644
index 4a541fff8c830aa7695a3ab1c003f74b73966d85..0000000000000000000000000000000000000000
--- a/include/parsers/igmp.h
+++ /dev/null
@@ -1,121 +0,0 @@
-/**
- * @file include/parsers/igmp.h
- * @brief IGMP message parser
- * @date 2022-10-05
- *
- * IGMP message parser.
- * Supports v1 and v2, and v3 Membership Report messages.
- * TODO: support v3 Membership Query messages.
- *
- * @copyright Copyright (c) 2022
- *
- */
-
-#ifndef _IOTFIREWALL_IGMP_
-#define _IOTFIREWALL_IGMP_
-
-#include <stdio.h>
-#include <stdint.h>
-#include "packet_utils.h"
-
-
-/**
- * @brief IGMP message types
- */
-typedef enum {
-    MEMBERSHIP_QUERY     = 0x11,
-    V1_MEMBERSHIP_REPORT = 0x12,
-    V2_MEMBERSHIP_REPORT = 0x16,
-    LEAVE_GROUP          = 0x17,
-    V3_MEMBERSHIP_REPORT = 0x22
-} igmp_message_type_t;
-
-/**
- * @brief IGMPv2 message
- */
-typedef struct {
-    uint8_t max_resp_time;
-    uint16_t checksum;
-    uint32_t group_address;  // IPv4 group address, in network byte order
-} igmp_v2_message_t;
-
-/**
- * @brief IGMPv3 membership query
- */
-typedef struct {
-    uint8_t max_resp_code;
-    uint16_t checksum;
-    uint32_t group_address;  // IPv4 group address, in network byte order
-    uint8_t flags;  // Resv, S, QRV
-    uint8_t qqic;
-    uint16_t num_sources;
-    uint32_t *sources;  // Array of IPv4 addresses, in network byte order
-} igmp_v3_membership_query_t;
-
-/**
- * @brief IGMPv3 Group Record
- */
-typedef struct {
-    uint8_t type;
-    uint8_t aux_data_len;
-    uint16_t num_sources;
-    uint32_t group_address;  // IPv4 group address, in network byte order
-    uint32_t *sources;  // Array of IPv4 addresses, in network byte order
-} igmp_v3_group_record_t;
-
-/**
- * @brief IGMPv3 membership report
- */
-typedef struct {
-    uint16_t checksum;
-    uint16_t num_groups;
-    igmp_v3_group_record_t *groups;  // Array of group records
-} igmp_v3_membership_report_t;
-
-/**
- * @brief IGMP message body.
- */
-typedef union
-{
-    igmp_v2_message_t v2_message;
-    igmp_v3_membership_query_t v3_membership_query;
-    igmp_v3_membership_report_t v3_membership_report;
-} igmp_message_body_t;
-
-/**
- * @brief Generic IGMP message
- */
-typedef struct
-{
-    uint8_t version;
-    igmp_message_type_t type;
-    igmp_message_body_t body;
-} igmp_message_t;
-
-
-////////// FUNCTIONS //////////
-
-/**
- * @brief Parse an IGMP message.
- * 
- * @param data pointer to the start of the IGMP message
- * @return the parsed IGMP message
- */
-igmp_message_t igmp_parse_message(uint8_t *data);
-
-/**
- * @brief Free the memory allocated for an IGMP message.
- * 
- * @param message the IGMP message to free
- */
-void igmp_free_message(igmp_message_t message);
-
-/**
- * @brief Print an IGMP message.
- * 
- * @param message the IGMP message to print
- */
-void igmp_print_message(igmp_message_t message);
-
-
-#endif /* _IOTFIREWALL_IGMP_ */
diff --git a/include/parsers/ssdp.h b/include/parsers/ssdp.h
deleted file mode 100644
index 689e2519a6b17bb18c2b510f4f13a395499bb427..0000000000000000000000000000000000000000
--- a/include/parsers/ssdp.h
+++ /dev/null
@@ -1,73 +0,0 @@
-/**
- * @file include/parsers/ssdp.h
- * @brief SSDP message parser
- * @date 2022-11-24
- *
- * @copyright Copyright (c) 2022
- *
- */
-
-#ifndef _IOTFIREWALL_SSDP_
-#define _IOTFIREWALL_SSDP_
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdint.h>
-#include <stdbool.h>
-#include <arpa/inet.h>
-#include "packet_utils.h"
-
-#define SSDP_METHOD_MAX_LEN 8                  // Maximum length of a SSDP method
-#define SSDP_MULTICAST_ADDR "239.255.255.250"  // SSDP multicast group address
-
-/**
- * SSDP methods
- */
-typedef enum {
-    SSDP_M_SEARCH,
-    SSDP_NOTIFY,
-    SSDP_UNKNOWN
-} ssdp_method_t;
-
-/**
- * Abstraction of an SSDP message
- */
-typedef struct ssdp_message {
-    bool is_request;       // True if the message is a request, false if it is a response
-    ssdp_method_t method;  // SSDP method (M-SEARCH or NOTIFY)
-} ssdp_message_t;
-
-
-////////// FUNCTIONS //////////
-
-///// PARSING /////
-
-/**
- * @brief Parse the method and URI of SSDP message.
- *
- * @param data pointer to the start of the SSDP message
- * @param dst_addr IPv4 destination address, in network byte order
- * @return the parsed SSDP message
- */
-ssdp_message_t ssdp_parse_message(uint8_t *data, uint32_t dst_addr);
-
-
-///// PRINTING /////
-
-/**
- * @brief Converts a SSDP method from enum value to character string.
- *
- * @param method the SSDP method in enum value
- * @return the same SSDP method as a character string
- */
-char *ssdp_method_to_str(ssdp_method_t method);
-
-/**
- * @brief Print the method and URI of a SSDP message.
- *
- * @param message the message to print
- */
-void ssdp_print_message(ssdp_message_t message);
-
-
-#endif /* _IOTFIREWALL_SSDP_ */
diff --git a/include/sha256.h b/include/sha256.h
deleted file mode 100644
index 7123a30dd49628d6ca15345c33968c50ca328cb7..0000000000000000000000000000000000000000
--- a/include/sha256.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*********************************************************************
-* Filename:   sha256.h
-* Author:     Brad Conte (brad AT bradconte.com)
-* Copyright:
-* Disclaimer: This code is presented "as is" without any guarantees.
-* Details:    Defines the API for the corresponding SHA1 implementation.
-*********************************************************************/
-
-#ifndef SHA256_H
-#define SHA256_H
-
-/*************************** HEADER FILES ***************************/
-#include <stddef.h>
-
-/****************************** MACROS ******************************/
-#define SHA256_BLOCK_SIZE 32            // SHA256 outputs a 32 byte digest
-
-/**************************** DATA TYPES ****************************/
-typedef unsigned char BYTE;             // 8-bit byte
-typedef unsigned int  WORD;             // 32-bit word, change to "long" for 16-bit machines
-
-typedef struct {
-	BYTE data[64];
-	WORD datalen;
-	unsigned long long bitlen;
-	WORD state[8];
-} SHA256_CTX;
-
-/*********************** FUNCTION DECLARATIONS **********************/
-void sha256_init(SHA256_CTX *ctx);
-void sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len);
-void sha256_final(SHA256_CTX *ctx, BYTE hash[]);
-
-#endif   // SHA256_H
diff --git a/src/dns_map.c b/src/dns_map.c
deleted file mode 100644
index b5406b8f17adec239c60ae6bf716cb803870aec1..0000000000000000000000000000000000000000
--- a/src/dns_map.c
+++ /dev/null
@@ -1,203 +0,0 @@
-/**
- * @file src/dns_map.c
- * @brief Implementation of a DNS domain name to IP addresses mapping, using Joshua J Baker's hashmap.c (https://github.com/tidwall/hashmap.c)
- * @date 2022-09-06
- * 
- * @copyright Copyright (c) 2022
- * 
- */
-
-#include "dns_map.h"
-
-
-/*** Static functions for hashmap ****/
-
-/**
- * Hash function for the DNS table.
- * 
- * @param item DNS table entry to hash
- * @param seed0 first seed
- * @param seed1 second seed
- * @return hash value for the given DNS table entry
- */
-static uint64_t dns_hash(const void *item, uint64_t seed0, uint64_t seed1) {
-    const dns_entry_t *entry = (dns_entry_t *) item;
-    return hashmap_sip(entry->domain_name, strlen(entry->domain_name), seed0, seed1);
-}
-
-/**
- * Compare function for the DNS table.
- * 
- * @param a first DNS table entry to compare
- * @param a second DNS table entry to compare
- * @param udata user data, unused
- * @return an integer which takes the following value:
- *         - 0 if a and b are equal
- *         - less than 0 if a is smaller than b
- *         - greater than 0 if a is greater than b
- */
-static int dns_compare(const void *a, const void *b, void *udata) {
-    const dns_entry_t *entry1 = (dns_entry_t *) a;
-    const dns_entry_t *entry2 = (dns_entry_t *) b;
-    return strcmp(entry1->domain_name, entry2->domain_name);
-}
-
-/**
- * Free an entry of the DNS table.
- * 
- * @param item the entry to free
- */
-static void dns_free(void *item) {
-    free(((dns_entry_t *) item)->ip_list.ip_addresses);
-}
-
-
-/*** Visible functions ***/
-
-/**
- * @brief Initialize an ip_list_t structure.
- *
- * Creates an empty list of IP addresses.
- * The `ip_count` field is set to 0,
- * and the `ip_addresses` field is set to NULL.
- *
- * @return ip_list_t newly initialized structure
- */
-ip_list_t ip_list_init() {
-    ip_list_t ip_list;
-    ip_list.ip_count = 0;
-    ip_list.ip_addresses = NULL;
-    return ip_list;
-}
-
-/**
- * @brief Checks if a dns_entry_t structure contains a given IP address.
- *
- * @param dns_entry pointer to the DNS entry to process
- * @param ip_address IP address to check the presence of
- * @return true if the IP address is present in the DNS entry, false otherwise
- */
-bool dns_entry_contains(dns_entry_t *dns_entry, ip_addr_t ip_address) {
-    if (dns_entry == NULL || dns_entry->ip_list.ip_addresses == NULL) {
-        // DNS entry or IP address list is NULL
-        return false;
-    }
-
-    // Not NULL, search for the IP address
-    for (uint8_t i = 0; i < dns_entry->ip_list.ip_count; i++) {
-        if (compare_ip(*(dns_entry->ip_list.ip_addresses + i), ip_address)) {
-            // IP address found
-            return true;
-        }
-    }
-    
-    // IP address not found
-    return false;
-}
-
-/**
- * Create a new DNS table.
- * Uses random seeds for the hash function.
- * 
- * @return the newly created DNS table, or NULL if creation failed
- */
-dns_map_t* dns_map_create() {
-    return hashmap_new(
-        sizeof(dns_entry_t), // Size of one entry
-        DNS_MAP_INIT_SIZE,   // Hashmap initial size
-        rand(),              // Optional seed 1
-        rand(),              // Optional seed 2
-        &dns_hash,           // Hash function
-        &dns_compare,        // Compare function
-        &dns_free,           // Element free function
-        NULL                 // User data, unused
-    );
-}
-
-/**
- * Free the memory allocated for a DNS table.
- * 
- * @param table the DNS table to free
- */
-void dns_map_free(dns_map_t *table) {
-    hashmap_free(table);
-}
-
-/**
- * Add IP addresses corresponding to a given domain name in the DNS table.
- * If the domain name was already present, its IP addresses will be replaced by the new ones.
- *
- * @param table the DNS table to add the entry to
- * @param domain_name the domain name of the entry
- * @param ip_list an ip_list_t structure containing the list of IP addresses
- */
-void dns_map_add(dns_map_t *table, char *domain_name, ip_list_t ip_list) {
-    dns_entry_t *dns_entry = dns_map_get(table, domain_name);
-    if (dns_entry != NULL) {
-        // Domain name already present, add given IP addresses to the already existing ones
-        ip_list_t old_ip_list = dns_entry->ip_list;
-        ip_list_t new_ip_list;
-        new_ip_list.ip_count = old_ip_list.ip_count + ip_list.ip_count;
-        new_ip_list.ip_addresses = (ip_addr_t *) malloc(new_ip_list.ip_count * sizeof(ip_addr_t));
-        memcpy(new_ip_list.ip_addresses, old_ip_list.ip_addresses, old_ip_list.ip_count * sizeof(ip_addr_t));
-        memcpy(new_ip_list.ip_addresses + old_ip_list.ip_count, ip_list.ip_addresses, ip_list.ip_count * sizeof(ip_addr_t));
-        dns_entry->ip_list = new_ip_list;
-        free(old_ip_list.ip_addresses);
-        free(ip_list.ip_addresses);
-    } else {
-        // Domain name not present, create a new entry with given IP addresses
-        hashmap_set(table, &(dns_entry_t){.domain_name = domain_name, .ip_list = ip_list});
-    }
-}
-
-/**
- * Remove a domain name, and its corresponding IP addresses, from the DNS table.
- * 
- * @param table the DNS table to remove the entry from
- * @param domain_name the domain name of the entry to remove
- */
-void dns_map_remove(dns_map_t *table, char *domain_name) {
-    dns_entry_t *entry = hashmap_delete(table, &(dns_entry_t){ .domain_name = domain_name });
-    if (entry != NULL)
-        dns_free(entry);
-}
-
-/**
- * Retrieve the IP addresses corresponding to a given domain name in the DNS table.
- * 
- * @param table the DNS table to retrieve the entry from
- * @param domain_name the domain name of the entry to retrieve
- * @return a pointer to a dns_entry structure containing the IP addresses corresponding to the domain name,
- *         or NULL if the domain name was not found in the DNS table
- */
-dns_entry_t* dns_map_get(dns_map_t *table, char *domain_name) {
-    return (dns_entry_t *) hashmap_get(table, &(dns_entry_t){ .domain_name = domain_name });
-}
-
-/**
- * Retrieve the IP addresses corresponding to a given domain name,
- * and remove the domain name from the DNS table.
- * 
- * @param table the DNS table to retrieve the entry from
- * @param domain_name the domain name of the entry to retrieve
- * @return a pointer to a dns_entry structure containing the IP addresses corresponding to the domain name,
- *         or NULL if the domain name was not found in the DNS table
- */
-dns_entry_t* dns_map_pop(dns_map_t *table, char *domain_name) {
-    return (dns_entry_t *) hashmap_delete(table, &(dns_entry_t){ .domain_name = domain_name });
-}
-
-/**
- * @brief Print a DNS table entry.
- *
- * @param dns_entry the DNS table entry to print
- */
-void dns_entry_print(dns_entry_t *dns_entry) {
-    if (dns_entry != NULL) {
-        printf("Domain name: %s\n", dns_entry->domain_name);
-        printf("IP addresses:\n");
-        for (uint8_t i = 0; i < dns_entry->ip_list.ip_count; i++) {
-            printf("  %s\n", ip_net_to_str(*(dns_entry->ip_list.ip_addresses + i)));
-        }
-    }
-}
diff --git a/src/packet_utils.c b/src/packet_utils.c
deleted file mode 100644
index 8b0aec2358ec417e898759dfdd46ec602b86f90c..0000000000000000000000000000000000000000
--- a/src/packet_utils.c
+++ /dev/null
@@ -1,294 +0,0 @@
-/**
- * @file src/packet_utils.c
- * @brief Utilitaries for payload manipulation and display
- * @date 2022-09-09
- * 
- * @copyright Copyright (c) 2022
- * 
- */
-
-#include "packet_utils.h"
-
-
-/**
- * Print a packet payload.
- * 
- * @param length length of the payload in bytes
- * @param data pointer to the start of the payload
- */
-void print_payload(int length, uint8_t *data) {
-    char trailing = ' ';
-	// Iterate on the whole payload
-	for (int i = 0; i < length; i++) {
-        if (i == length - 1) {
-            // Insert newline after last byte
-            trailing = '\n';
-        }
-
-		uint8_t c = *(data + i);
-		if (c == 0) {
-			printf("0x00%c", trailing);
-		} else {
-			printf("%#.2x%c", c, trailing);
-		}
-	}
-}
-
-/**
- * Converts a hexstring payload to a data buffer.
- * 
- * @param hexstring the hexstring to convert
- * @param payload a double pointer to the payload, which will be set to the start of the payload
- * @return the length of the payload in bytes
- */
-size_t hexstr_to_payload(char *hexstring, uint8_t **payload) {
-    size_t length = strlen(hexstring) / 2;  // Size of the payload in bytes, one byte is two characters
-    *payload = (uint8_t *) malloc(length * sizeof(uint8_t));  // Allocate memory for the payload
-
-    // WARNING: no sanitization or error-checking whatsoever
-    for (size_t count = 0; count < length; count++) {
-        sscanf(hexstring + 2*count, "%2hhx", (*payload) + count);  // Convert two characters to one byte
-    }
-
-    return length;
-}
-
-/**
- * Converts a MAC address from its hexadecimal representation
- * to its string representation.
- *
- * @param mac_hex MAC address in hexadecimal representation
- * @return the same MAC address in string representation
- */
-char *mac_hex_to_str(uint8_t mac_hex[])
-{
-    char *mac_str = (char *) malloc(MAC_ADDR_STRLEN * sizeof(char));  // A string representation of a MAC address is 17 characters long + null terminator
-    int ret = snprintf(mac_str, MAC_ADDR_STRLEN, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", mac_hex[0], mac_hex[1], mac_hex[2], mac_hex[3], mac_hex[4], mac_hex[5]);
-    // Error handling
-    if (ret != MAC_ADDR_STRLEN - 1)
-    {
-        free(mac_str);
-        fprintf(stderr, "Error converting MAC address \\x%2x\\x%2x\\x%2x\\x%2x\\x%2x\\x%2x to string representation.\n", mac_hex[0], mac_hex[1], mac_hex[2], mac_hex[3], mac_hex[4], mac_hex[5]);
-        return NULL;
-    }
-    return mac_str;
-}
-
-/**
- * Converts a MAC address from its string representation
- * to its hexadecimal representation.
- *
- * @param mac_str MAC address in string representation
- * @return the same MAC address in hexadecimal representation
- */
-uint8_t *mac_str_to_hex(char *mac_str)
-{
-    uint8_t *mac_hex = (uint8_t *) malloc(MAC_ADDR_LENGTH * sizeof(uint8_t));  // A MAC address is 6 bytes long
-    int ret = sscanf(mac_str, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", mac_hex, mac_hex + 1, mac_hex + 2, mac_hex + 3, mac_hex + 4, mac_hex + 5);
-    // Error handling
-    if (ret != MAC_ADDR_LENGTH)
-    {
-        free(mac_hex);
-        fprintf(stderr, "Error converting MAC address %s to hexadecimal representation.\n", mac_str);
-        return NULL;
-    }
-    return mac_hex;
-}
-
-/**
- * Converts an IPv4 address from its network order numerical representation
- * to its string representation.
- * (Wrapper arount inet_ntoa)
- * 
- * @param ipv4_net IPv4 address in hexadecimal representation
- * @return the same IPv4 address in string representation
- */
-char* ipv4_net_to_str(uint32_t ipv4_net) {
-    return inet_ntoa((struct in_addr) {ipv4_net});
-}
-
-/**
- * Converts an IPv4 address from its string representation
- * to its network order numerical representation.
- * (Wrapper arount inet_aton)
- * 
- * @param ipv4_str IPv4 address in string representation
- * @return the same IPv4 address in network order numerical representation
- */
-uint32_t ipv4_str_to_net(char *ipv4_str) {
-    struct in_addr ipv4_addr;
-    inet_aton(ipv4_str, &ipv4_addr);
-    return ipv4_addr.s_addr;
-}
-
-/**
- * Converts an IPv4 addres from its hexadecimal representation
- * to its string representation.
- * 
- * @param ipv4_hex IPv4 address in hexadecimal representation
- * @return the same IPv4 address in string representation
- */
-char* ipv4_hex_to_str(char *ipv4_hex) {
-    char* ipv4_str = (char *) malloc(INET_ADDRSTRLEN * sizeof(char));  // A string representation of an IPv4 address is at most 15 characters long + null terminator
-    int ret = snprintf(ipv4_str, INET_ADDRSTRLEN, "%hhu.%hhu.%hhu.%hhu", *ipv4_hex, *(ipv4_hex + 1), *(ipv4_hex + 2), *(ipv4_hex + 3));
-    // Error handling
-    if (ret < 0) {
-        free(ipv4_str);
-        fprintf(stderr, "Error converting IPv4 address \\x%2x\\x%2x\\x%2x\\x%2x to string representation.\n", *ipv4_hex, *(ipv4_hex + 1), *(ipv4_hex + 2), *(ipv4_hex + 3));
-        return NULL;
-    }
-    return ipv4_str;
-}
-
-/**
- * Converts an IPv4 address from its string representation
- * to its hexadecimal representation.
- * 
- * @param ipv4_str IPv4 address in string representation
- * @return the same IPv4 address in hexadecimal representation
- */
-char* ipv4_str_to_hex(char *ipv4_str) {
-    char* ipv4_hex = (char *) malloc(4 * sizeof(char));  // An IPv4 address is 4 bytes long 
-    int ret = sscanf(ipv4_str, "%hhu.%hhu.%hhu.%hhu", ipv4_hex, ipv4_hex + 1, ipv4_hex + 2, ipv4_hex + 3);
-    // Error handling
-    if (ret != 4) {
-        free(ipv4_hex);
-        fprintf(stderr, "Error converting IPv4 address %s to hexadecimal representation.\n", ipv4_str);
-        return NULL;
-    }
-    return ipv4_hex;
-}
-
-/**
- * @brief Converts an IPv6 to its string representation.
- *
- * @param ipv6 the IPv6 address
- * @return the same IPv6 address in string representation
- */
-char* ipv6_net_to_str(uint8_t ipv6[]) {
-    char *ipv6_str = (char *) malloc(INET6_ADDRSTRLEN * sizeof(char));
-    const char *ret = inet_ntop(AF_INET6, ipv6, ipv6_str, INET6_ADDRSTRLEN);
-    // Error handling
-    if (ret == NULL) {
-        fprintf(stderr, "Error converting IPv6 address \\x%2x\\x%2x\\x%2x\\x%2x\\x%2x\\x%2x\\x%2x\\x%2x\\x%2x\\x%2x\\x%2x\\x%2x\\x%2x\\x%2x\\x%2x\\x%2x to its string representation.\n", ipv6[0], ipv6[1], ipv6[2], ipv6[3], ipv6[4], ipv6[5], ipv6[6], ipv6[7], ipv6[8], ipv6[9], ipv6[10], ipv6[11], ipv6[12], ipv6[13], ipv6[14], ipv6[15]);
-    }
-    return ipv6_str;
-}
-
-/**
- * Converts an IPv6 address from its string representation
- * to its network representation (a 16-byte array).
- *
- * @param ipv6_str IPv6 address in string representation
- * @return the same IPv6 address as a 16-byte array
- */
-uint8_t *ipv6_str_to_net(char *ipv6_str) {
-    uint8_t *ipv6 = (uint8_t *) malloc(IPV6_ADDR_LENGTH * sizeof(uint8_t));  // An IPv6 address is 16 bytes long
-    int err = inet_pton(AF_INET6, ipv6_str, ipv6);
-    // Error handling
-    if (err != 1) {
-        fprintf(stderr, "Error converting IPv6 address %s to its network representation.\n", ipv6_str);
-        return NULL;
-    }
-    return ipv6;
-}
-
-/**
- * @brief Converts an IP (v4 or v6) address to its string representation.
- *
- * Converts an IP (v4 or v6) address to its string representation.
- * If it is an IPv6 address, it must be freed after use.
- *
- * @param ip_addr the IP address, as an ip_addr_t struct
- * @return the same IP address in string representation
- */
-char* ip_net_to_str(ip_addr_t ip_addr) {
-    switch (ip_addr.version) {
-    case 4:
-        return ipv4_net_to_str(ip_addr.value.ipv4);
-        break;
-    case 6:
-        return ipv6_net_to_str(ip_addr.value.ipv6);
-        break;
-    default:
-        fprintf(stderr, "Unknown IP version: %hhu.\n", ip_addr.version);
-        return "";
-    }
-}
-
-/**
- * Converts an IP (v4 or v6) address from its string representation
- * to an ip_addr_t struct.
- *
- * @param ip_str IP (v4 or v6) address in string representation
- * @return the same IP address as a ip_addr_t struct
- */
-ip_addr_t ip_str_to_net(char *ip_str, uint8_t version) {
-    ip_addr_t ip_addr;
-    ip_addr.version = version;
-    if (version == 4) {
-        ip_addr.value.ipv4 = ipv4_str_to_net(ip_str);
-    } else if (version == 6) {
-        uint8_t *ipv6_net = ipv6_str_to_net(ip_str);
-        memcpy(ip_addr.value.ipv6, ipv6_net, IPV6_ADDR_LENGTH);
-        free(ipv6_net);
-    } else {
-        fprintf(stderr, "Error converting address %s to ip_addr_t.\n", ip_str);
-    }
-    return ip_addr;
-}
-
-/**
- * @brief Compare two IPv6 addresses.
- *
- * @param ipv6_1 first IPv6 address
- * @param ipv6_2 second IPv6 address
- * @return true if the two addresses are equal, false otherwise
- */
-bool compare_ipv6(uint8_t *ipv6_1, uint8_t *ipv6_2) {
-    return memcmp(ipv6_1, ipv6_2, 16) == 0;
-}
-
-/**
- * @brief Compare two IP (v4 or v6) addresses.
- *
- * @param ip_1 first IP address
- * @param ip_2 second IP address
- * @return true if the two addresses are equal, false otherwise
- */
-bool compare_ip(ip_addr_t ip_1, ip_addr_t ip_2) {
-    if (ip_1.version == 4 && ip_2.version == 4) {
-        return ip_1.value.ipv4 == ip_2.value.ipv4;
-    } else if (ip_1.version == 6 && ip_2.version == 6) {
-        return compare_ipv6(ip_1.value.ipv6, ip_2.value.ipv6);
-    } else {
-        return false;
-    }
-}
-
-/**
- * @brief Compute SHA256 hash of a given payload.
- * 
- * @param payload Payload to hash
- * @param payload_len Payload length, including padding (in bytes)
- * @return uint8_t* SHA256 hash of the payload
- */
-uint8_t* compute_hash(uint8_t *payload, int payload_len) {
-    uint8_t *hash = (uint8_t *) malloc(SHA256_BLOCK_SIZE * sizeof(uint8_t));
-    SHA256_CTX ctx;
-    sha256_init(&ctx);
-    sha256_update(&ctx, payload, payload_len);
-    sha256_final(&ctx, hash);
-    return hash;
-}
-
-/**
- * @brief Print a SHA256 hash.
- *
- * @param hash SHA256 hash to print
- */
-void print_hash(uint8_t *hash) {
-    for (uint16_t i = 0; i < SHA256_BLOCK_SIZE; i++) {
-        printf("%02x", *(hash + i));
-    }
-}
diff --git a/src/parsers/CMakeLists.txt b/src/parsers/CMakeLists.txt
deleted file mode 100644
index 95c212e9e6ec1a031f837603f4d1c161b330c2d0..0000000000000000000000000000000000000000
--- a/src/parsers/CMakeLists.txt
+++ /dev/null
@@ -1,31 +0,0 @@
-# Minimum required CMake version
-cmake_minimum_required(VERSION 3.20)
-
-set(INCLUDE_PARSERS_DIR ${INCLUDE_DIR}/parsers)
-
-# Header parser
-add_library(header STATIC ${INCLUDE_PARSERS_DIR}/header.h header.c)
-target_include_directories(header PRIVATE ${INCLUDE_DIR} ${INCLUDE_PARSERS_DIR})
-target_link_libraries(header packet_utils)
-# DNS parser
-add_library(dns STATIC ${INCLUDE_PARSERS_DIR}/dns.h dns.c)
-target_include_directories(dns PRIVATE ${INCLUDE_DIR} ${INCLUDE_PARSERS_DIR})
-target_link_libraries(dns packet_utils dns_map)
-# DHCP parser
-add_library(dhcp STATIC ${INCLUDE_PARSERS_DIR}/dhcp.h dhcp.c)
-target_include_directories(dhcp PRIVATE ${INCLUDE_DIR} ${INCLUDE_PARSERS_DIR})
-# HTTP parser
-add_library(http STATIC ${INCLUDE_PARSERS_DIR}/http.h http.c)
-target_include_directories(http PRIVATE ${INCLUDE_DIR} ${INCLUDE_PARSERS_DIR})
-# IGMP parser
-add_library(igmp STATIC ${INCLUDE_PARSERS_DIR}/igmp.h igmp.c)
-target_include_directories(igmp PRIVATE ${INCLUDE_DIR} ${INCLUDE_PARSERS_DIR})
-# SSDP parser
-add_library(ssdp STATIC ${INCLUDE_PARSERS_DIR}/ssdp.h ssdp.c)
-target_include_directories(ssdp PRIVATE ${INCLUDE_DIR} ${INCLUDE_PARSERS_DIR})
-# CoAP parser
-add_library(coap STATIC ${INCLUDE_PARSERS_DIR}/coap.h coap.c)
-target_include_directories(coap PRIVATE ${INCLUDE_DIR} ${INCLUDE_PARSERS_DIR})
-target_link_libraries(coap http)
-# Installation
-install(TARGETS ${PARSERS} DESTINATION ${LIB_DIR})
diff --git a/src/parsers/coap.c b/src/parsers/coap.c
deleted file mode 100644
index d4ac043cb0d914a1d97daf3af63998ba7b2fa1a3..0000000000000000000000000000000000000000
--- a/src/parsers/coap.c
+++ /dev/null
@@ -1,201 +0,0 @@
-/**
- * @file src/parsers/coap.c
- * @brief CoAP message parser
- * @date 2022-11-30
- * 
- * @copyright Copyright (c) 2022
- * 
- */
-
-#include "coap.h"
-
-
-///// PARSING /////
-
-/**
- * @brief Parse the method of a CoAP message.
- * 
- * @param code byte which encodes the CoAP method
- * @return CoAP method
- */
-static http_method_t coap_parse_method(uint8_t code) {
-    switch (code) {
-    case 1:
-        return HTTP_GET;
-        break;
-    case 2:
-        return HTTP_POST;
-        break;
-    case 3:
-        return HTTP_PUT;
-        break;
-    case 4:
-        return HTTP_DELETE;
-        break;
-    default:
-        // CoAP responses and all other codes are not supported
-        return HTTP_UNKNOWN;
-    }
-}
-
-/**
- * @brief Parse an URI option (Uri-Path or Uri-Query) of a CoAP message.
- * 
- * @param message pointer to the CoAP message, which will be updated
- * @param option CoAP option number (11 for Uri-Path, 15 for Uri-Query)
- * @param length CoAP option length
- * @param data pointer to the start of the URI option
- */
-static void coap_parse_uri_option(coap_message_t *message, coap_option_t option_num, uint16_t length, uint8_t *data) {
-    char prefix = (option_num == COAP_URI_PATH) ? '/' : '?';
-    if (message->uri == NULL) {
-        message->uri = malloc(length + 2);
-    } else {
-        message->uri = realloc(message->uri, message->uri_len + length + 2);
-    }
-    *(message->uri + message->uri_len) = prefix;
-    memcpy(message->uri + message->uri_len + 1, data, length);
-    message->uri_len += length + 1;
-    *(message->uri + message->uri_len) = '\0';
-}
-
-/**
- * @brief Parse CoAP options.
- * 
- * @param message pointer to the currently parsed CoAP message, which will be updated
- * @param data pointer to the start of the options section of a CoAP message
- * @param msg_length length of the rest of the CoAP message (after the header)
- */
-static void coap_parse_options(coap_message_t *message, uint8_t *data, uint16_t msg_length) {
-    uint16_t option_num = 0;
-    uint16_t bytes_read = 0;
-    while (bytes_read < msg_length && *data != 0b11111111)
-    {
-        // Parse option delta
-        uint16_t delta = (*data) >> 4;
-        uint8_t delta_len = 0;  // Length of the extended delta field
-        switch (delta) {
-        case 13:
-            delta = (*(data + 1)) + 13;
-            delta_len = 1;
-            break;
-        case 14:
-            delta = ntohs(*((uint16_t*) (data + 1))) + 269;
-            delta_len = 2;
-            break;
-        case 15:
-            continue;
-            break;
-        default:
-            break;
-        }
-        // Compute option number
-        option_num += delta;
-
-        // Parse option length
-        uint16_t option_length = (*data) & 0b00001111;
-        uint8_t length_len = 0;  // Length of the extended length field
-        switch (option_length)
-        {
-        case 13:
-            option_length = (*(data + 1 + delta_len)) + 13;
-            length_len = 1;
-            break;
-        case 14:
-            option_length = ntohs(*((uint16_t *)(data + 1 + delta_len))) + 269;
-            length_len = 2;
-            break;
-        case 15:
-            continue;
-            break;
-        default:
-            break;
-        }
-
-        // Parse option value
-        data += 1 + delta_len + length_len;
-        if (option_num == COAP_URI_PATH || option_num == COAP_URI_QUERY)
-        {
-            // Option Uri-Path or Uri-Query
-            coap_parse_uri_option(message, option_num, option_length, data);
-        }
-        data += option_length;
-        bytes_read += 1 + delta_len + length_len + option_length;
-        // Other options are not supported (yet)
-    }
-}
-
-/**
- * @brief Parse a CoAP message.
- *
- * @param data pointer to the start of the CoAP message
- * @param length length of the CoAP message, in bytes
- * @return the parsed CoAP message
- */
-coap_message_t coap_parse_message(uint8_t *data, uint16_t length)
-{
-    coap_message_t message;
-    message.type = (coap_type_t) (((*data) & 0b00110000) >> 4);  // CoAP type is encoded in bits 2-3
-    message.method = coap_parse_method(*(data + 1));             // CoAP method is encoded in byte 1
-    uint8_t token_length = (*data) & 0b00001111;                 // CoAP token length is encoded in bits 4-7
-    uint8_t header_length = 4 + token_length;                    // Length of the CoAP header
-    data += header_length;                                       // Skip the header
-    message.uri = NULL;                                          // Initialize the URI to NULL
-    message.uri_len = 0;
-    coap_parse_options(&message, data, length - header_length);  // Parse CoAP options
-    return message;
-}
-
-
-///// DESTROY /////
-
-/**
- * @brief Free the memory allocated for a CoAP message.
- *
- * @param message the CoAP message to free
- */
-void coap_free_message(coap_message_t message) {
-    if (message.uri != NULL)
-        free(message.uri);
-}
-
-
-///// PRINTING /////
-
-/**
- * @brief Converts a CoAP message type to its string representation.
- * 
- * @param type CoAP message type
- * @return string representation of the CoAP message type
- */
-static char* coap_type_to_str(coap_type_t type) {
-    switch (type) {
-    case COAP_CON:
-        return "Confirmable";
-        break;
-    case COAP_NON:
-        return "Non-Confirmable";
-        break;
-    case COAP_ACK:
-        return "Acknowledgement";
-        break;
-    case COAP_RST:
-        return "Reset";
-        break;
-    default:
-        return "Unknown";
-    }
-}
-
-/**
- * @brief Print a CoAP message.
- *
- * @param message the CoAP message to print
- */
-void coap_print_message(coap_message_t message)
-{
-    printf("CoAP message:\n");
-    printf("  Type: %s\n", coap_type_to_str(message.type));
-    printf("  Method: %s\n", http_method_to_str(message.method));
-    printf("  URI: %s\n", message.uri);
-}
diff --git a/src/parsers/dhcp.c b/src/parsers/dhcp.c
deleted file mode 100644
index 6ef14eb6d94f8bd5676afcde137eda2ab833476f..0000000000000000000000000000000000000000
--- a/src/parsers/dhcp.c
+++ /dev/null
@@ -1,245 +0,0 @@
-/**
- * @file src/parsers/dhcp.c
- * @brief DHCP message parser
- * @date 2022-09-12
- * 
- * @copyright Copyright (c) 2022
- * 
- */
-
-#include "parsers/dhcp.h"
-
-
-///// PARSING /////
-
-/**
- * @brief Parse the header of a DHCP message (not including options).
- * 
- * @param data a pointer to the start of the DHCP message
- * @return the parsed DHCP message with the header fields filled in
- */
-dhcp_message_t dhcp_parse_header(uint8_t *data) {
-    dhcp_message_t message;
-    // Opcode: 1 byte
-    message.op = *data;
-    // htype: 1 byte
-    message.htype = *(data + 1);
-    // hlen: 1 byte
-    message.hlen = *(data + 2);
-    // hops: 1 byte
-    message.hops = *(data + 3);
-    // xid: 4 bytes
-    message.xid = ntohl(*((uint32_t *) (data + 4)));
-    // secs: 2 bytes
-    message.secs = ntohs(*((uint16_t *) (data + 8)));
-    // flags: 2 bytes
-    message.flags = ntohs(*((uint16_t *) (data + 10)));
-    // The IP addresses are left in network byte order
-    // ciaddr: 4 bytes
-    message.ciaddr = *((uint32_t *) (data + 12));
-    // yiaddr: 4 bytes
-    message.yiaddr = *((uint32_t *) (data + 16));
-    // siaddr: 4 bytes
-    message.siaddr = *((uint32_t *) (data + 20));
-    // giaddr: 4 bytes
-    message.giaddr = *((uint32_t *) (data + 24));
-    // chaddr: 16 bytes
-    memcpy(message.chaddr, data + 28, sizeof(uint8_t) * 16);
-    // sname: 64 bytes
-    memcpy(message.sname, data + 44, sizeof(uint8_t) * 64);
-    // file: 128 bytes
-    memcpy(message.file, data + 108, sizeof(uint8_t) * 128);
-    return message;
-}
-
-/**
- * @brief Parse a DHCP option.
- * 
- * @param data a pointer to the start of the DHCP option
- * @param offset a pointer to the current offset inside the DHCP message
- *               Its value will be updated to point to the next option
- * @return the parsed DHCP option
- */
-dhcp_option_t dhcp_parse_option(uint8_t *data, uint16_t *offset) {
-    dhcp_option_t option;
-    option.code = *(data + *offset);
-    if (option.code == DHCP_PAD || option.code == DHCP_END)
-    {
-        option.length = 0;
-        option.value = NULL;
-        *offset += 1;
-    }
-    else
-    {
-        option.length = *(data + *offset + 1);
-        option.value = (uint8_t *) malloc(sizeof(uint8_t) * option.length);
-        memcpy(option.value, data + *offset + 2, option.length * sizeof(uint8_t));
-        *offset += 2 + option.length;
-    }
-    return option;
-}
-
-/**
- * @brief Parse DHCP options.
- * 
- * @param data a pointer to the start of the DHCP options list
- * @return a pointer to the start of the parsed DHCP options
- */
-dhcp_options_t dhcp_parse_options(uint8_t *data) {
-    // Init
-    uint8_t max_option_count = DHCP_MAX_OPTION_COUNT;
-    dhcp_options_t options;
-    options.count = 0;
-    // Check magic cookie is equal to 0x63825363
-    uint32_t magic_cookie = ntohl(*((uint32_t *) data));
-    if (magic_cookie != DHCP_MAGIC_COOKIE) {
-        fprintf(stderr, "Error: DHCP magic cookie is %#x, which is not equal to %#x\n", magic_cookie, DHCP_MAGIC_COOKIE);
-        return options;
-    }
-    // Parse options
-    options.options = (dhcp_option_t *) malloc(sizeof(dhcp_option_t) * max_option_count);
-    uint16_t offset = 4;
-    uint8_t code;
-    do {
-        if (options.count == max_option_count) {
-            // Realloc memory if too many options
-            max_option_count *= 2;
-            options.options = (dhcp_option_t *) realloc(options.options, sizeof(dhcp_option_t) * max_option_count);
-        }
-        dhcp_option_t option = dhcp_parse_option(data, &offset);
-        code = option.code;
-        if (code == DHCP_MESSAGE_TYPE) {
-            // Store DHCP message type
-            options.message_type = *option.value;
-        }
-        *(options.options + (options.count++)) = option;
-    } while (code != DHCP_END);
-    // Shrink allocated memory to the actual number of options, if needed
-    if (options.count < max_option_count) {
-        options.options = (dhcp_option_t *) realloc(options.options, sizeof(dhcp_option_t) * options.count);
-    }
-    return options;
-}
-
-/**
- * @brief Parse a DHCP message.
- * 
- * @param data a pointer to the start of the DHCP message
- * @return the parsed DHCP message
- */
-dhcp_message_t dhcp_parse_message(uint8_t *data) {
-    // Parse constant fields
-    dhcp_message_t message = dhcp_parse_header(data);
-    // Parse DHCP options
-    message.options = dhcp_parse_options(data + DHCP_HEADER_LEN);
-    // Return
-    return message;
-}
-
-
-///// DESTROY //////
-
-/**
- * @brief Free the memory allocated for a DHCP message.
- *
- * @param message the DHCP message to free
- */
-void dhcp_free_message(dhcp_message_t message) {
-    if (message.options.count > 0) {
-        for (uint8_t i = 0; i < message.options.count; i++) {
-            dhcp_option_t option = *(message.options.options + i);
-            if (option.length > 0) {
-                free(option.value);
-            }
-        }
-        free(message.options.options);
-    }
-}
-
-
-///// PRINTING /////
-
-/**
- * @brief Print a hardware address.
- * 
- * @param htype hardware type
- * @param chaddr the hardware address to print
- */
-static void dhcp_print_chaddr(uint8_t htype, uint8_t chaddr[]) {
-    printf("  Client hardware address: ");
-    uint8_t length = (htype == 1) ? 6 : 16;
-    printf("%02hhx", chaddr[0]);
-    for (uint8_t i = 1; i < length; i++) {
-        printf(":%02hhx", chaddr[i]);
-    }
-    printf("\n");
-}
-
-/**
- * @brief Print the header of a DHCP message.
- * 
- * @param message the DHCP message to print the header of
- */
-void dhcp_print_header(dhcp_message_t message) {
-    // Opcode
-    printf("  Opcode: %hhu\n", message.op);
-    // htype
-    printf("  Hardware type: %hhu\n", message.htype);
-    // hlen
-    printf("  Hardware address length: %hhu\n", message.hlen);
-    // hops
-    printf("  Hops: %hhu\n", message.hops);
-    // xid
-    printf("  Transaction ID: %#x\n", message.xid);
-    // secs
-    printf("  Seconds elapsed: %hu\n", message.secs);
-    // flags
-    printf("  Flags: 0x%04x\n", message.flags);
-    // ciaddr
-    printf("  Client IP address: %s\n", inet_ntoa((struct in_addr) {message.ciaddr}));
-    // yiaddr
-    printf("  Your IP address: %s\n", inet_ntoa((struct in_addr) {message.yiaddr}));
-    // siaddr
-    printf("  Server IP address: %s\n", inet_ntoa((struct in_addr) {message.siaddr}));
-    // giaddr
-    printf("  Gateway IP address: %s\n", inet_ntoa((struct in_addr) {message.giaddr}));
-    // chaddr
-    dhcp_print_chaddr(message.htype, message.chaddr);
-    // sname
-    if (strlen((char *) message.sname) > 0) {
-        printf("  Server name: %s\n", message.sname);
-    }
-    // file
-    if (strlen((char *) message.file) > 0) {
-        printf("  Boot file name: %s\n", message.file);
-    }
-}
-
-/**
- * @brief Print a DHCP option.
- * 
- * @param option the DHCP option to print
- */
-void dhcp_print_option(dhcp_option_t option) {
-    printf("    Code: %hhu;  Length: %hhu;  Value: ", option.code, option.length);
-    for (uint8_t i = 0; i < option.length; i++) {
-        printf("%02hhx ", *(option.value + i));
-    }
-    printf("\n");
-}
-
-/**
- * @brief Print a DHCP message.
- * 
- * @param message the DHCP message to print
- */
-void dhcp_print_message(dhcp_message_t message) {
-    printf("DHCP message\n");
-    // Print header fields
-    dhcp_print_header(message);
-    // Print DHCP options
-    printf("  DHCP options:\n");
-    for (uint8_t i = 0; i < message.options.count; i++) {
-        dhcp_print_option(*(message.options.options + i));
-    }
-}
diff --git a/src/parsers/dns.c b/src/parsers/dns.c
deleted file mode 100644
index 0f0fb9db2068034fbe5c9949f0bf62a74b5f3c25..0000000000000000000000000000000000000000
--- a/src/parsers/dns.c
+++ /dev/null
@@ -1,584 +0,0 @@
-/**
- * @file src/parsers/dns.c
- * @brief DNS message parser
- * @date 2022-09-09
- * 
- * @copyright Copyright (c) 2022
- * 
- */
-
-#include "dns.h"
-
-
-///// PARSING /////
-
-/**
- * Parse a DNS header.
- * A DNS header is always 12 bytes.
- * 
- * @param data a pointer pointing to the start of the DNS message
- * @param offset a pointer to the current parsing offset
- * @return the parsed header
- */
-dns_header_t dns_parse_header(uint8_t *data, uint16_t *offset) {
-    // Init
-    dns_header_t header;
-    // Parse fields
-    header.id = ntohs(*((uint16_t *) (data + *offset)));
-    header.flags = ntohs(*((uint16_t *) (data + *offset + 2)));
-    header.qr = (header.flags & DNS_QR_FLAG_MASK);
-    header.qdcount = ntohs(*((uint16_t *) (data + *offset + 4)));
-    header.ancount = ntohs(*((uint16_t *) (data + *offset + 6)));
-    header.nscount = ntohs(*((uint16_t *) (data + *offset + 8)));
-    header.arcount = ntohs(*((uint16_t *) (data + *offset + 10)));
-    // Update offset to point after header
-    *offset += DNS_HEADER_SIZE;
-
-    return header;
-}
-
-/**
- * Parse a DNS Domain Name.
- * 
- * @param data a pointer pointing to the start of the DNS message
- * @param offset a pointer to the current parsing offset
- * @return the parsed domain name
- */
-static char* dns_parse_domain_name(uint8_t *data, uint16_t *offset) {
-    if (*(data + *offset) == '\0') {
-        // Domain name is ROOT
-        (*offset)++;
-        return "";
-    }
-    uint16_t current_length = 0;
-    uint16_t max_length = DNS_MAX_DOMAIN_NAME_LENGTH;
-    char* domain_name = (char *) malloc(sizeof(char) * max_length);
-    bool compression = false;
-    uint16_t domain_name_offset = *offset;  // Other offset, might be useful for domain name compression
-    while (*(data + domain_name_offset) != '\0') {
-        uint8_t length_byte = *((uint8_t *) (data + domain_name_offset));
-        if (length_byte >> 6 == 3) {  // Length byte starts with 0b11
-            // Domain name compression
-            // Advance offset by 2 bytes, and do not update it again
-            if(!compression) {
-                *offset += 2;
-            }
-            compression = true;
-            // Retrieve new offset to parse domain name from
-            domain_name_offset = ntohs(*((uint16_t *) (data + domain_name_offset))) & DNS_COMPRESSION_MASK;
-        } else {
-            // Fully written label, parse it
-            for (int i = 1; i <= length_byte; i++) {
-                if (current_length == max_length) {
-                    // Realloc buffer
-                    max_length *= 2;
-                    void *realloc_ptr = realloc(domain_name, sizeof(char) * max_length);
-                    if (realloc_ptr == NULL) {
-                        // Handle realloc error
-                        fprintf(stderr, "Error reallocating memory for domain name %s\n", domain_name);
-                        free(domain_name);
-                        return NULL;
-                    } else {
-                        domain_name = (char*) realloc_ptr;
-                    }
-                }
-                char c = *(data + domain_name_offset + i);
-                *(domain_name + (current_length++)) = c;
-            }
-            *(domain_name + (current_length++)) = '.';
-            domain_name_offset += length_byte + 1;
-            if (!compression) {
-                *offset = domain_name_offset;
-            }
-        }
-    }
-    // Domain name was fully parsed
-    // Overwrite last '.' written with NULL byte
-    *(domain_name + (--current_length)) = '\0';
-    // Shrink allocated memory to fit domain name, if needed
-    if (current_length + 1 < max_length) {
-        void* realloc_ptr = realloc(domain_name, sizeof(char) * (current_length + 1));
-        if (realloc_ptr == NULL) {
-            fprintf(stderr, "Error shrinking memory for domain name %s\n", domain_name);
-        } else {
-            domain_name = (char*) realloc_ptr;
-        } 
-    }
-    // Advance offset after NULL terminator, if domain name compression was not used
-    if (!compression) {
-        (*offset)++;
-    }
-    return domain_name;
-}
-
-/**
- * Parse a DNS Question section.
- * 
- * @param qdcount the number of questions present in the question section
- * @param data a pointer pointing to the start of the DNS message
- * @param offset a pointer to the current parsing offset
- * @return the parsed question section
- */
-dns_question_t* dns_parse_questions(uint16_t qdcount, uint8_t *data, uint16_t *offset) {
-    // Init
-    dns_question_t *questions = (dns_question_t *) malloc(qdcount * sizeof(dns_question_t));
-    // Iterate over all questions
-    for (uint16_t i = 0; i < qdcount; i++) {
-        // Parse domain name
-        (questions + i)->qname = dns_parse_domain_name(data, offset);
-        // Parse rtype and rclass
-        (questions + i)->qtype = ntohs(*((uint16_t *) (data + *offset)));
-        (questions + i)->qclass = ntohs(*((uint16_t *) (data + *offset + 2))) & DNS_CLASS_MASK;
-        *offset += 4;
-    }
-    return questions;
-}
-
-/**
- * Parse a DNS Resource Record RDATA field.
- * 
- * @param rdlength the length, in bytes, of the RDATA field
- * @param data a pointer pointing to the start of the DNS message
- * @param offset a pointer to the current parsing offset
- * @return the parsed RDATA field
- */
-static rdata_t dns_parse_rdata(dns_rr_type_t rtype, uint16_t rdlength, uint8_t *data, uint16_t *offset) {
-    rdata_t rdata;
-    if (rdlength == 0) {
-        // RDATA field is empty
-        rdata.data = NULL;
-    } else {
-        // RDATA field is not empty
-        switch (rtype) {
-        case A:
-            // RDATA contains an IPv4 address
-            rdata.ip.version = 4;
-            rdata.ip.value.ipv4 = *((uint32_t *) (data + *offset));  // Stored in network byte order
-            *offset += rdlength;
-            break;
-        case AAAA:
-            // RDATA contains an IPv6 address
-            rdata.ip.version = 6;
-            memcpy(rdata.ip.value.ipv6, data + *offset, rdlength);
-            *offset += rdlength;
-            break;
-        case NS:
-        case CNAME:
-        case PTR:
-            // RDATA contains is a domain name
-            rdata.domain_name = dns_parse_domain_name(data, offset);
-            break;
-        default:
-            // RDATA contains is generic data
-            rdata.data = (uint8_t *) malloc(sizeof(char) * rdlength);
-            memcpy(rdata.data, data + *offset, rdlength);
-            *offset += rdlength;
-        }
-    }
-    return rdata;
-}
-
-/**
- * Parse a DNS Resource Record list.
- * @param count the number of resource records present in the section
- * @param data a pointer pointing to the start of the DNS message
- * @param offset a pointer to the current parsing offset
- * @return the parsed resource records list
- */
-dns_resource_record_t* dns_parse_rrs(uint16_t count, uint8_t *data, uint16_t *offset) {
-    dns_resource_record_t *rrs = (dns_resource_record_t *) malloc(count * sizeof(dns_resource_record_t));
-    for (uint16_t i = 0; i < count; i++) {
-        // Parse domain name
-        (rrs + i)->name = dns_parse_domain_name(data, offset);
-        // Parse rtype, rclass and TTL
-        dns_rr_type_t rtype = ntohs(*((uint16_t *) (data + *offset)));
-        (rrs + i)->rtype = rtype;
-        (rrs + i)->rclass = ntohs(*((uint16_t *) (data + *offset + 2))) & DNS_CLASS_MASK;
-        (rrs + i)->ttl = ntohl(*((uint32_t *) (data + *offset + 4)));
-        // Parse rdata
-        uint16_t rdlength = ntohs(*((uint16_t *) (data + *offset + 8)));
-        (rrs + i)->rdlength = rdlength;
-        *offset += 10;
-        (rrs + i)->rdata = dns_parse_rdata(rtype, rdlength, data, offset);
-    }
-    return rrs;
-}
-
-/**
- * Parse a DNS message.
- * 
- * @param data a pointer to the start of the DNS message
- * @return the parsed DNS message
- */
-dns_message_t dns_parse_message(uint8_t *data) {
-    // Init
-    dns_message_t message;
-    uint16_t offset = 0;
-    message.questions = NULL;
-    message.answers = NULL;
-    message.authorities = NULL;
-    message.additionals = NULL;
-
-    // Parse DNS header
-    message.header = dns_parse_header(data, &offset);
-    // If present, parse DNS Question section
-    if (message.header.qdcount > 0)
-    {
-        message.questions = dns_parse_questions(message.header.qdcount, data, &offset);
-    }
-    // If message is a response and section is present, parse DNS Answer section
-    if (message.header.qr == 1 && message.header.ancount > 0)
-    {
-        message.answers = dns_parse_rrs(message.header.ancount, data, &offset);
-    }
-
-    /* Parsing other sections is not necessary for this project
-
-    // If message is a response and section is present, parse DNS Authority section
-    if (message.header.qr == 1 && message.header.nscount > 0)
-    {
-        message.authorities = dns_parse_rrs(message.header.nscount, data, &offset);
-    }
-    // If message is a response and section is present, parse DNS Additional section
-    if (message.header.qr == 1 && message.header.arcount > 0)
-    {
-        message.additionals = dns_parse_rrs(message.header.arcount, data, &offset);
-    }
-
-    */
-
-    return message;
-}
-
-
-///// LOOKUP /////
-
-/**
- * @brief Check if a given string ends with a given suffix.
- * 
- * @param str the string to check
- * @param suffix the suffix to search for
- * @param suffix_length the length of the suffix
- * @return true if the string ends with the suffix
- * @return false if the string does not end with the suffix
- */
-static bool ends_with(char* str, char* suffix, uint16_t suffix_length) {
-    uint16_t str_length = strlen(str);
-    if (str_length < suffix_length) {
-        return false;
-    }
-    return strncmp(str + str_length - suffix_length, suffix, suffix_length) == 0;
-}
-
-/**
- * @brief Check if a given DNS Questions list contains a domain name which has a given suffix.
- *
- * @param questions DNS Questions list
- * @param qdcount number of Questions in the list
- * @param suffix the domain name suffix to search for
- * @param suffix_length the length of the domain name suffix
- * @return true if a domain name with the given suffix is found is found in the Questions list,
- *         false otherwise
- */
-bool dns_contains_suffix_domain_name(dns_question_t *questions, uint16_t qdcount, char *suffix, uint16_t suffix_length) {
-    for (uint16_t i = 0; i < qdcount; i++) {
-        if (ends_with((questions + i)-> qname, suffix, suffix_length)) {
-            return true;
-        }
-    }
-    return false;
-}
-
-/**
- * @brief Check if a given domain name is fully contained in a DNS Questions list.
- *
- * @param questions DNS Questions list
- * @param qdcount number of Questions in the list
- * @param domain_name the domain name to search for
- * @return true if the full domain name is found in the Questions list, false otherwise
- */
-bool dns_contains_full_domain_name(dns_question_t *questions, uint16_t qdcount, char *domain_name)
-{
-    for (uint16_t i = 0; i < qdcount; i++) {
-        if (strcmp((questions + i)->qname, domain_name) == 0) {
-            return true;
-        }
-    }
-    return false;
-}
-
-/**
- * @brief Search for a specific domain name in a DNS Questions list.
- * 
- * @param questions DNS Questions list
- * @param qdcount number of Suestions in the list
- * @param domain_name the domain name to search for
- * @return the DNS Question related to the given domain name, or NULL if not found
- */
-dns_question_t* dns_get_question(dns_question_t *questions, uint16_t qdcount, char *domain_name) {
-    for (uint16_t i = 0; i < qdcount; i++) {
-        if (strcmp((questions + i)->qname, domain_name) == 0) {
-            return questions + i;
-        }
-    }
-    return NULL;
-}
-
-/**
- * @brief Retrieve the IP addresses corresponding to a given domain name in a DNS Answers list.
- * 
- * Searches a DNS Answer list for a specific domain name and returns the corresponding IP address.
- * Processes each Answer recursively if the Answer Type is a CNAME.
- * 
- * @param answers DNS Answers list to search in
- * @param ancount number of Answers in the list
- * @param domain_name domain name to search for
- * @return struct ip_list representing the list of corresponding IP addresses
- */
-ip_list_t dns_get_ip_from_name(dns_resource_record_t *answers, uint16_t ancount, char *domain_name) {
-    ip_list_t ip_list;
-    ip_list.ip_count = 0;
-    ip_list.ip_addresses = NULL;
-    char *cname = domain_name;
-    for (uint16_t i = 0; i < ancount; i++) {
-        if (strcmp((answers + i)->name, cname) == 0) {
-            dns_rr_type_t rtype = (answers + i)->rtype;
-            if (rtype == A || rtype == AAAA)
-            {
-                // Handle IP list length
-                if (ip_list.ip_addresses == NULL) {
-                    ip_list.ip_addresses = (ip_addr_t *) malloc(sizeof(ip_addr_t));
-                } else {
-                    void *realloc_ptr = realloc(ip_list.ip_addresses, (ip_list.ip_count + 1) * sizeof(ip_addr_t));
-                    if (realloc_ptr == NULL) {
-                        // Handle realloc error
-                        free(ip_list.ip_addresses);
-                        fprintf(stderr, "Error reallocating memory for IP list.\n");
-                        ip_list.ip_count = 0;
-                        ip_list.ip_addresses = NULL;
-                        return ip_list;
-                    } else {
-                        ip_list.ip_addresses = (ip_addr_t*) realloc_ptr;
-                    }
-                }
-                // Handle IP version and value
-                *(ip_list.ip_addresses + ip_list.ip_count) = (answers + i)->rdata.ip;
-                ip_list.ip_count++;
-            }
-            else if ((answers + i)->rtype == CNAME)
-            {
-                cname = (answers + i)->rdata.domain_name;
-            }
-        }
-    }
-    return ip_list;
-}
-
-
-///// DESTROY /////
-
-/**
- * @brief Free the memory allocated for a DNS RDATA field.
- * 
- * @param rdata the DNS RDATA field to free
- * @param rtype the DNS Resource Record Type of the RDATA field
- */
-static void dns_free_rdata(rdata_t rdata, dns_rr_type_t rtype) {
-    switch (rtype) {
-    case A:
-    case AAAA:
-        break;  // Nothing to free for IP addresses
-    case NS:
-    case CNAME:
-    case PTR:
-        free(rdata.domain_name);
-        break;
-    default:
-        free(rdata.data);
-    }
-}
-
-/**
- * @brief Free the memory allocated for a list of DNS Resource Records.
- * 
- * @param rr the list of DNS Resource Records to free
- * @param count the number of Resource Records in the list
- */
-static void dns_free_rrs(dns_resource_record_t *rrs, uint16_t count) {
-    if (rrs != NULL && count > 0) {
-        for (uint16_t i = 0; i < count; i++) {
-            dns_resource_record_t rr = *(rrs + i);
-            if (rr.rdlength > 0) {
-                free(rr.name);
-                dns_free_rdata(rr.rdata, rr.rtype);
-            }
-        }
-        free(rrs);
-    }
-}
-
-/**
- * Free the memory allocated for a DNS message.
- *
- * @param question the DNS message to free
- */
-void dns_free_message(dns_message_t message) {
-    // Free DNS Questions
-    if (message.header.qdcount > 0) {
-        for (uint16_t i = 0; i < message.header.qdcount; i++) {
-            free((message.questions + i)->qname);
-        }
-        free(message.questions);
-    }
-
-    // Free DNS Answers
-    dns_free_rrs(message.answers, message.header.ancount);
-
-    /* Other sections are not used in this project
-
-    // Free DNS Authorities
-    dns_free_rrs(message.authorities, message.header.nscount);
-    // Free DNS Additionals
-    dns_free_rrs(message.additionals, message.header.arcount);
-
-    */
-}
-
-
-///// PRINTING /////
-
-/**
- * Print a DNS header.
- * 
- * @param message the DNS header
- */
-void dns_print_header(dns_header_t header) {
-    printf("DNS Header:\n");
-    printf("  ID: %#hx\n", header.id);
-    printf("  Flags: %#hx\n", header.flags);
-    printf("  QR: %d\n", header.qr);
-    printf("  Questions count: %hd\n", header.qdcount);
-    printf("  Answers count: %hd\n", header.ancount);
-    printf("  Authority name servers count: %hd\n", header.nscount);
-    printf("  Additional records count: %hd\n", header.arcount);
-}
-
-/**
- * Print a DNS Question
- * 
- * @param question the DNS Question
- */
-void dns_print_question(dns_question_t question) {
-    printf("  Question:\n");
-    printf("    Domain name: %s\n", question.qname);
-    printf("    Type: %hd\n", question.qtype);
-    printf("    Class: %hd\n", question.qclass);
-}
-
-/**
- * Print a DNS Question section.
- * 
- * @param qdcount the number of Questions in the Question section
- * @param questions the list of DNS Questions
- */
-void dns_print_questions(uint16_t qdcount, dns_question_t *questions) {
-    printf("DNS Question section:\n");
-    for (uint16_t i = 0; i < qdcount; i++) {
-        dns_question_t *question = questions + i;
-        if (question != NULL) {
-            dns_print_question(*question);
-        }
-    }
-}
-
-/**
- * Return a string representation of the given RDATA value.
- * 
- * @param rtype the type corresponding to the RDATA value
- * @param rdlength the length, in bytes, of the RDATA value
- * @param rdata the RDATA value, stored as a union type
- * @return a string representation of the RDATA value
- */
-char* dns_rdata_to_str(dns_rr_type_t rtype, uint16_t rdlength, rdata_t rdata) {
-    if (rdlength == 0) {
-        // RDATA is empty
-        return "";
-    }
-    switch (rtype) {
-        case A:
-        case AAAA:
-            // RDATA is an IP (v4 or v6) address
-            return ip_net_to_str(rdata.ip);
-            break;
-        case NS:
-        case CNAME:
-        case PTR:
-            // RDATA is a domain name
-            return rdata.domain_name;
-            break;
-        default: ;
-            // Generic RDATA
-            char *buffer = (char *) malloc(rdlength * 4 + 1);  // Allocate memory for each byte (4 characters) + the NULL terminator
-            for (uint8_t i = 0; i < rdlength; i++) {
-                snprintf(buffer + (i * 4), 5, "\\x%02x", *(rdata.data + i));
-            }
-            return buffer;
-    }
-}
-
-/**
- * Print a DNS Resource Record.
- * 
- * @param section_name the name of the Resource Record section
- * @param rr the DNS Resource Record
- */
-void dns_print_rr(char* section_name, dns_resource_record_t rr) {
-    printf("  %s RR:\n", section_name);
-    printf("    Name: %s\n", rr.name);
-    printf("    Type: %hd\n", rr.rtype);
-    printf("    Class: %hd\n", rr.rclass);
-    printf("    TTL [s]: %d\n", rr.ttl);
-    printf("    Data length: %hd\n", rr.rdlength);
-    printf("    RDATA: %s\n", dns_rdata_to_str(rr.rtype, rr.rdlength, rr.rdata));
-}
-
-/**
- * Print a DNS Resource Records section.
- * 
- * @param section_name the name of the Resource Record section
- * @param count the number of Resource Records in the section
- * @param rrs the list of DNS Resource Records
- */
-void dns_print_rrs(char* section_name, uint16_t count, dns_resource_record_t *rrs) {
-    printf("%s RRs:\n", section_name);
-    for (uint16_t i = 0; i < count; i++) {
-        dns_resource_record_t *rr = rrs + i;
-        if (rr != NULL)
-            dns_print_rr(section_name, *rr);
-    }
-}
-
-/**
- * Print a DNS message.
- * 
- * @param message the DNS message
- */
-void dns_print_message(dns_message_t message) {
-    // Print DNS Header
-    dns_print_header(message.header);
-
-    // Print DNS Questions, if any
-    if (message.header.qdcount > 0)
-        dns_print_questions(message.header.qdcount, message.questions);
-
-    // Print DNS Answers, if message is a response and has answers
-    if (message.header.qr == 1 && message.header.ancount > 0)
-        dns_print_rrs("Answer", message.header.ancount, message.answers);
-
-    /* Other sections are not used in this project
-
-    dns_print_rrs("Authority", message.header.nscount, message.authorities);
-    dns_print_rrs("Additional", message.header.arcount, message.additionals);
-
-    */
-}
diff --git a/src/parsers/header.c b/src/parsers/header.c
deleted file mode 100644
index f33228cf175bb0b2a6824c140390354cad7bfc09..0000000000000000000000000000000000000000
--- a/src/parsers/header.c
+++ /dev/null
@@ -1,204 +0,0 @@
-/**
- * @file src/parsers/header.c
- * @brief Parser for layer 3 and 4 headers (currently only IPv4, IPv6, UDP and TCP)
- * 
- * Parser for layer 3 and 4 headers.
- * Currently supported protocols:
- *   - Layer 3:
- *     - IPv4
- *     - IPv6
- *   - Layer 4:
- *     - UDP
- *     - TCP
- * 
- * @date 2022-09-09
- * 
- * @copyright Copyright (c) 2022
- * 
- */
-
-#include "header.h"
-
-
-/**
- * Retrieve the length of a packet's IPv4 header.
- * 
- * @param data a pointer to the start of the packet's IPv4 header
- * @return the size, in bytes, of the IPv4 header
- */
-size_t get_ipv4_header_length(uint8_t *data) {
-    // 4-bit IPv4 header length is encoded in the last 4 bits of byte 0.
-    // It indicates the number of 32-bit words.
-    // It must be multiplied by 4 to obtain the header size in bytes.
-    uint8_t length = (*data & 0x0f) * 4;
-    return length;
-}
-
-/**
- * Retrieve the length of a packet's IPv6 header.
- * 
- * @param data a pointer to the start of the packet's IPv6 header
- * @return the size, in bytes, of the IPv6 header
- */
-size_t get_ipv6_header_length(uint8_t *data) {
-    // An IPv6 header has a fixed length of 40 bytes
-    return IPV6_HEADER_LENGTH;
-}
-
-/**
- * Retrieve the length of a packet's UDP header.
- * 
- * @param data a pointer to the start of the packet's UDP (layer 4) header
- * @return the size, in bytes, of the UDP header
- */
-size_t get_udp_header_length(uint8_t *data) {
-    // A UDP header has a fixed length of 8 bytes
-    return UDP_HEADER_LENGTH;
-}
-
-/**
- * Retrieve the length of a packet's TCP header.
- * 
- * @param data a pointer to the start of the packet's TCP (layer 4) header
- * @return the size, in bytes, of the UDP header
- */
-size_t get_tcp_header_length(uint8_t *data) {
-    // 4-bit TCP header data offset is encoded in the first 4 bits of byte 12.
-    // It indicates the number of 32-bit words.
-    // It must be multiplied by 4 to obtain the header size in bytes.
-    uint8_t length = (*((data) + 12) >> 4) * 4;
-    return length;
-}
-
-/**
- * Retrieve the length of a packet's layer 3 header (IPv4 or IPv6).
- *
- * @param data a pointer to the start of the packet's layer 3 header
- * @return the size, in bytes, of the layer 3 header
- */
-size_t get_l3_header_length(uint8_t *data) {
-    uint8_t ip_version = (*data) >> 4;
-    switch (ip_version) {
-    case 4:
-        return get_ipv4_header_length(data);
-        break;
-    case 6:
-        return get_ipv6_header_length(data);
-        break;
-    default:
-        return 0;
-        break;
-    }
-}
-
-/**
- * Retrieve the length of a packet's layer-3 and layer-4 headers.
- * 
- * @param data a pointer to the start of the packet's layer-3 header
- * @return the size, in bytes, of the UDP header
- */
-size_t get_headers_length(uint8_t* data) {
-    size_t length = 0;
-
-    // Layer 3: Network
-    // Retrieve the IP version, which is encoded in the first 4 bits of byte 0
-    uint8_t ip_version = (*data) >> 4;
-    ip_protocol_t protocol = 0;
-    switch (ip_version) {
-    case 4:
-        length += get_ipv4_header_length(data);
-        protocol = *((data) + 9);  // In IPv4, the protocol number is encoded in byte 9
-        break;
-    case 6:
-        length += get_ipv6_header_length(data);
-        protocol = *((data) + 6);  // In IPv6, the protocol number is encoded in byte 6
-        break;
-    default:
-        break;
-    }
-
-    // Layer 4: Transport
-    switch (protocol) {
-    case TCP:
-        length += get_tcp_header_length(data + length);
-        break;
-    case UDP:
-        length += get_udp_header_length(data + length);
-        break;
-    default:
-        break;
-    }
-    return length;
-}
-
-/**
- * @brief Retrieve the length of a UDP payload.
- *
- * @param data pointer to the start of the UDP header
- * @return length of the UDP payload, in bytes
- */
-uint16_t get_udp_payload_length(uint8_t *data)
-{
-    // The 16-bit length of the complete UDP datagram is encoded in bytes 4 and 5 of the UDP header.
-    // The length of the UDP header (8 bytes) must then be subtracted to obtain the length of the UDP payload.
-    return ntohs(*((uint16_t *) (data + 4))) - UDP_HEADER_LENGTH;
-}
-
-/**
- * @brief Retrieve the source port from a layer 4 header.
- *
- * @param data pointer to the start of the layer 4 header
- * @return destination port
- */
-uint16_t get_dst_port(uint8_t *data) {
-    // Source port is encoded in bytes 2 and 3
-    return ntohs(*((uint16_t*) (data + 2)));
-}
-
-/**
- * @brief Retrieve the source address from an IPv4 header.
- *
- * @param data pointer to the start of the IPv4 header
- * @return source IPv4 address, in network byte order
- */
-uint32_t get_ipv4_src_addr(uint8_t *data) {
-    // Source address is encoded in bytes 12 to 15
-    return *((uint32_t*) (data + 12));
-}
-
-/**
- * @brief Retrieve the destination address from an IPv4 header.
- * 
- * @param data pointer to the start of the IPv4 header
- * @return destination IPv4 address, in network byte order
- */
-uint32_t get_ipv4_dst_addr(uint8_t* data) {
-    // Destination address is encoded in bytes 16 to 19
-    return *((uint32_t*) (data + 16));
-}
-
-/**
- * @brief Retrieve the source address from an IPv6 header.
- *
- * @param data pointer to the start of the IPv6 header
- * @return source IPv6 address, as a 16-byte array
- */
-uint8_t* get_ipv6_src_addr(uint8_t *data) {
-    // Source address is encoded in bytes 8 to 23
-    uint8_t *addr = (uint8_t *) malloc(IPV6_ADDR_LENGTH);
-    memcpy(addr, data + 8, IPV6_ADDR_LENGTH);
-    return addr;
-}
-
-/**
- * @brief Retrieve the destination address from an IPv6 header.
- *
- * @param data pointer to the start of the IPv6 header
- * @return destination IPv6 address, as a 16-byte array
- */
-uint8_t* get_ipv6_dst_addr(uint8_t *data) {
-    // Source address is encoded in bytes 24 to 39
-    uint8_t *addr = (uint8_t *) malloc(IPV6_ADDR_LENGTH);
-    memcpy(addr, data + 24, IPV6_ADDR_LENGTH);
-    return addr;
-}
diff --git a/src/parsers/http.c b/src/parsers/http.c
deleted file mode 100644
index 5f583247fa88004e9ee22e811f23d08c9850027a..0000000000000000000000000000000000000000
--- a/src/parsers/http.c
+++ /dev/null
@@ -1,229 +0,0 @@
-/**
- * @file src/parsers/http.c
- * @brief HTTP message parser
- * @date 2022-09-19
- * 
- * @copyright Copyright (c) 2022
- * 
- */
-
-#include "http.h"
-
-
-///// PARSING /////
-
-/**
- * @brief Parse the method of an HTTP message.
- * 
- * Parse a HTTP message to retrieve its method,
- * and convert it to a http_message_t.
- * Only the two first characters need to be parsed.
- * Advances the offset value after parsing.
- * 
- * @param data pointer to the start of the HTTP message
- * @param offset current offset in the message
- * @return parsed HTTP method
- */
-static http_method_t http_parse_method(uint8_t *data, uint16_t *offset) {
-    switch (*(data + *offset)) {
-    case 'G':
-        // Method is GET
-        *offset += 4;
-        return HTTP_GET;
-        break;
-    case 'H':
-        // Method is HEAD
-        *offset += 5;
-        return HTTP_HEAD;
-        break;
-    case 'P':
-        // Method is POST or PUT
-        switch (*(data + *offset + 1)) {
-        case 'O':
-            // Method is POST
-            *offset += 5;
-            return HTTP_POST;
-            break;
-        case 'U':
-            // Method is PUT
-            *offset += 4;
-            return HTTP_PUT;
-            break;
-        default:
-            // Unknown method
-            return HTTP_UNKNOWN;
-        }
-    case 'D':
-        // Method is DELETE
-        *offset += 7;
-        return HTTP_DELETE;
-        break;
-    case 'C':
-        // Method is CONNECT
-        *offset += 8;
-        return HTTP_CONNECT;
-        break;
-    case 'O':
-        // Method is OPTIONS
-        *offset += 8;
-        return HTTP_OPTIONS;
-        break;
-    case 'T':
-        // Method is TRACE
-        *offset += 6;
-        return HTTP_TRACE;
-        break;
-    default:
-        // Unknown method
-        return HTTP_UNKNOWN;
-    }
-}
-
-/**
- * @brief Check if a TCP message is a HTTP message.
- *
- * @param data pointer to the start of the TCP payload
- * @param dst_port TCP destination port
- * @return true if the message is a HTTP message
- * @return false if the message is not a HTTP message
- */
-bool is_http(uint8_t *data)
-{
-    uint16_t offset = 0;
-    return http_parse_method(data, &offset) != HTTP_UNKNOWN;
-}
-
-/**
- * @brief Parse an URI in an HTTP message.
- * 
- * Parse a HTTP message to retrieve its URI,
- * and convert it to a character string.
- * Advances the offset value after parsing.
- * 
- * @param data pointer to the start of the HTTP message
- * @param offset current offset in the message
- * @return parsed URI
- */
-static char* http_parse_uri(uint8_t *data, uint16_t *offset) {
-    uint16_t length = 1;
-    uint16_t max_length = HTTP_METHOD_MAX_LEN;
-    char *uri = (char *) malloc(sizeof(char) * max_length);
-    while (*(data + *offset) != ' ') {
-        if (length == max_length) {
-            // URI is too long, increase buffer size
-            max_length *= 2;
-            void* realloc_ptr = realloc(uri, sizeof(char) * max_length);
-            if (realloc_ptr == NULL) {
-                // Handle realloc error
-                fprintf(stderr, "Error reallocating memory for URI %s\n", uri);
-                free(uri);
-                return NULL;
-            } else {
-                uri = (char*) realloc_ptr;
-            }
-        }
-        *(uri + (length - 1)) = *(data + (*offset)++);
-        length++;
-    }
-    if (length < max_length) {
-        // URI is shorter than allocated buffer, shrink buffer
-        void *realloc_ptr = realloc(uri, sizeof(char) * length);
-        if (realloc_ptr == NULL) {
-            fprintf(stderr, "Error shrinking memory for URI %s\n", uri);
-        } else {
-            uri = (char*) realloc_ptr;
-        }
-    }
-    // Add NULL terminating character
-    *(uri + length - 1) = '\0';
-    return uri;
-}
-
-/**
- * @brief Parse the method and URI of HTTP message.
- * 
- * @param data pointer to the start of the HTTP message
- * @param dst_port TCP destination port
- * @return the parsed HTTP message
- */
-http_message_t http_parse_message(uint8_t *data, uint16_t dst_port) {
-    http_message_t message;
-    uint16_t offset = 0;
-    http_method_t http_method = http_parse_method(data, &offset);
-    message.is_request = dst_port == 80 && http_method != HTTP_UNKNOWN;
-    if (message.is_request) {
-        message.method = http_method;
-        message.uri = http_parse_uri(data, &offset);
-    } else {
-        message.method = HTTP_UNKNOWN;
-        message.uri = NULL;
-    }
-    return message;
-}
-
-
-///// DESTROY /////
-
-/**
- * @brief Free the memory allocated for a HTTP message.
- *
- * @param message the HTTP message to free
- */
-void http_free_message(http_message_t message) {
-    if (message.uri != NULL)
-        free(message.uri);
-}
-
-
-///// PRINTING /////
-
-/**
- * @brief Converts a HTTP method from enum value to character string.
- * 
- * @param method the HTTP method in enum value
- * @return the same HTTP method as a character string
- */
-char* http_method_to_str(http_method_t method) {
-    switch (method) {
-    case HTTP_GET:
-        return "GET";
-        break;
-    case HTTP_HEAD:
-        return "HEAD";
-        break;
-    case HTTP_POST:
-        return "POST";
-        break;
-    case HTTP_PUT:
-        return "PUT";
-        break;
-    case HTTP_DELETE:
-        return "DELETE";
-        break;
-    case HTTP_CONNECT:
-        return "CONNECT";
-        break;
-    case HTTP_OPTIONS:
-        return "OPTIONS";
-        break;
-    case HTTP_TRACE:
-        return "TRACE";
-        break;
-    default:
-        return "UNKNOWN";
-    }
-}
-
-/**
- * @brief Print the method and URI of a HTTP message.
- * 
- * @param message the message to print
- */
-void http_print_message(http_message_t message) {
-    printf("HTTP message:\n");
-    printf("  is request ?: %d\n", message.is_request);
-    if (message.is_request) {
-        printf("  Method: %s\n", http_method_to_str(message.method));
-        printf("  URI: %s\n", message.uri);
-    }
-}
diff --git a/src/parsers/igmp.c b/src/parsers/igmp.c
deleted file mode 100644
index e02889a7e8a2a4d5c5da261a062d33e698c18174..0000000000000000000000000000000000000000
--- a/src/parsers/igmp.c
+++ /dev/null
@@ -1,177 +0,0 @@
-/**
- * @file src/parsers/igmp.c
- * @brief IGMP message parser
- * @date 2022-10-05
- *
- * IGMP message parser.
- * Supports v1 and v2, and v3 Membership Report messages.
- * TODO: support v3 Membership Query messages.
- *
- * @copyright Copyright (c) 2022
- *
- */
-
-#include "igmp.h"
-
-
-///// PARSING /////
-
-/**
- * @brief Parse an IGMPv2 message.
- *
- * @param data pointer to the start of the IGMPv2 message
- * @return the parsed IGMPv2 message
- */
-static igmp_v2_message_t igmp_v2_parse_message(uint8_t *data) {
-    igmp_v2_message_t message;
-    message.max_resp_time = *(data + 1);
-    message.checksum = ntohs(*((uint16_t *)(data + 2)));
-    message.group_address = *((uint32_t *)(data + 4));  // Stored in network byte order
-    return message;
-}
-
-/**
- * @brief Parse an array of IGMPv3 group records.
- * 
- * @param num_groups number of group records
- * @param data pointer to the start of the group records
- * @return pointer to the array of parsed group records
- */
-static igmp_v3_group_record_t* igmp_v3_parse_groups(uint16_t num_groups, uint8_t *data) {
-    // If num_groups is 0, group list is NULL
-    if (num_groups == 0)
-        return NULL;
-
-    // num_groups is greater than 0
-    igmp_v3_group_record_t *groups = malloc(num_groups * sizeof(igmp_v3_group_record_t));
-    for (uint16_t i = 0; i < num_groups; i++) {
-        igmp_v3_group_record_t *group = groups + i;
-        group->type = *data;
-        group->aux_data_len = *(data + 1);
-        group->num_sources = ntohs(*((uint16_t *)(data + 2)));
-        group->group_address = *((uint32_t *)(data + 4));  // Stored in network byte order
-        if (group->num_sources > 0) {
-            group->sources = malloc(group->num_sources * sizeof(uint32_t));
-            for (uint16_t j = 0; j < group->num_sources; j++) {
-                *((group->sources) + j) = *((uint32_t *)(data + 8 + j * 4));  // Stored in network byte order
-            }
-        } else {
-            group->sources = NULL;
-        }
-        data += 8 + group->num_sources * 4;
-    }
-    return groups;
-}
-
-/**
- * @brief Parse an IGMPv3 Membership Report message.
- *
- * @param data pointer to the start of the IGMPv3 Membership Report message
- * @return the parsed IGMPv3 Membership Report message
- */
-static igmp_v3_membership_report_t igmp_v3_parse_membership_report(uint8_t *data) {
-    igmp_v3_membership_report_t message;
-    message.checksum = ntohs(*((uint16_t *)(data + 2)));
-    message.num_groups = ntohs(*((uint16_t *)(data + 6)));
-    message.groups = igmp_v3_parse_groups(message.num_groups, data + 8);
-    return message;
-}
-
-/**
- * @brief Parse an IGMP message.
- * 
- * @param data pointer to the start of the IGMP message
- * @return the parsed IGMP message
- */
-igmp_message_t igmp_parse_message(uint8_t *data) {
-    igmp_message_t message;
-    message.type = (igmp_message_type_t) *data;
-    // Dispatch on IGMP message type
-    switch (message.type) {
-    case MEMBERSHIP_QUERY:
-    case V1_MEMBERSHIP_REPORT:
-    case V2_MEMBERSHIP_REPORT:
-    case LEAVE_GROUP:
-        message.version = 2;
-        message.body.v2_message = igmp_v2_parse_message(data);
-        break;
-    case V3_MEMBERSHIP_REPORT:
-        message.version = 3;
-        message.body.v3_membership_report = igmp_v3_parse_membership_report(data);
-        break;
-    default:
-        break;
-    }
-    return message;
-}
-
-/**
- * @brief Free the memory allocated for an IGMP message.
- *
- * @param message the IGMP message to free
- */
-void igmp_free_message(igmp_message_t message) {
-    if (message.version == 3 && message.body.v3_membership_report.num_groups > 0) {
-        for (uint16_t i = 0; i < message.body.v3_membership_report.num_groups; i++)
-        {
-            igmp_v3_group_record_t group = *(message.body.v3_membership_report.groups + i);
-            if (group.num_sources > 0)
-                free(group.sources);
-        }
-        free(message.body.v3_membership_report.groups);
-    }
-}
-
-
-///// PRINTING /////
-
-/**
- * @brief Print an IGMPv2 message.
- *
- * @param v2_message the IGMPv2 message to print
- */
-static void igmp_v2_print_message(igmp_v2_message_t v2_message) {
-    printf("  Max resp time: %hhu\n", v2_message.max_resp_time);
-    printf("  Checksum: %#hx\n", v2_message.checksum);
-    printf("  Group address: %s\n", ipv4_net_to_str(v2_message.group_address));
-}
-
-/**
- * @brief Print an IGMPv3 Membership Report message.
- * 
- * @param group the IGMPv3 Membership Report message to print
- */
-static void igmp_v3_print_membership_report(igmp_v3_membership_report_t v3_message) {
-    printf("  Checksum: %#hx\n", v3_message.checksum);
-    printf("  Number of groups: %hu\n", v3_message.num_groups);
-    for (uint16_t i = 0; i < v3_message.num_groups; i++) {
-        igmp_v3_group_record_t group = *(v3_message.groups + i);
-        printf("  Group %d:\n", i);
-        printf("    Type: %#hhx\n", group.type);
-        printf("    Aux data len: %hhu\n", group.aux_data_len);
-        printf("    Number of sources: %hu\n", group.num_sources);
-        printf("    Group address: %s\n", ipv4_net_to_str(group.group_address));
-        for (uint16_t j = 0; j < group.num_sources; j++) {
-            printf("    Source %d: %s\n", j, ipv4_net_to_str(*(group.sources + j)));
-        }
-    }
-}
-
-/**
- * @brief Print an IGMP message.
- * 
- * @param message the IGMP message to print
- */
-void igmp_print_message(igmp_message_t message) {
-    printf("IGMP message:\n");
-    printf("  Version: %hhu\n", message.version);
-    printf("  Type: %#hhx\n", message.type);
-    switch (message.version) {
-    case 2:
-        igmp_v2_print_message(message.body.v2_message);
-        break;
-    case 3:
-        igmp_v3_print_membership_report(message.body.v3_membership_report);
-        break;
-    }
-}
diff --git a/src/parsers/ssdp.c b/src/parsers/ssdp.c
deleted file mode 100644
index b82a6cd6d0a1ff659be236058efd7378328d523f..0000000000000000000000000000000000000000
--- a/src/parsers/ssdp.c
+++ /dev/null
@@ -1,91 +0,0 @@
-/**
- * @file src/parsers/ssdp.c
- * @brief SSDP message parser
- * @date 2022-11-24
- *
- * @copyright Copyright (c) 2022
- *
- */
-
-#include "ssdp.h"
-
-
-///// PARSING /////
-
-/**
- * @brief Parse the method of an SSDP message.
- *
- * Parse a SSDP message to retrieve its method,
- * and convert it to a ssdp_message_t.
- * Only the two first characters need to be parsed.
- * Advances the offset value after parsing.
- *
- * @param data pointer to the start of the SSDP message
- * @param offset current offset in the message
- * @return parsed SSDP method
- */
-static ssdp_method_t ssdp_parse_method(uint8_t *data, uint16_t *offset) {
-    switch (*(data + *offset)) {
-    case 'M':
-        // Method is M-SEARCH
-        *offset += 9;
-        return SSDP_M_SEARCH;
-        break;
-    case 'N':
-        // Method is NOTIFY
-        *offset += 7;
-        return SSDP_NOTIFY;
-        break;
-    default:
-        // Unknown method
-        return SSDP_UNKNOWN;
-    }
-}
-
-/**
- * @brief Parse the method and URI of SSDP message.
- *
- * @param data pointer to the start of the SSDP message
- * @param dst_addr IPv4 destination address, in network byte order
- * @return the parsed SSDP message
- */
-ssdp_message_t ssdp_parse_message(uint8_t *data, uint32_t dst_addr) {
-    ssdp_message_t message;
-    message.is_request = dst_addr == ipv4_str_to_net(SSDP_MULTICAST_ADDR);
-    uint16_t offset = 0;
-    message.method = ssdp_parse_method(data, &offset);
-    return message;
-}
-
-
-///// PRINTING /////
-
-/**
- * @brief Converts a SSDP method from enum value to character string.
- *
- * @param method the SSDP method in enum value
- * @return the same SSDP method as a character string
- */
-char *ssdp_method_to_str(ssdp_method_t method) {
-    switch (method) {
-    case SSDP_M_SEARCH:
-        return "M-SEARCH";
-        break;
-    case SSDP_NOTIFY:
-        return "NOTIFY";
-        break;
-    default:
-        return "UNKNOWN";
-    }
-}
-
-/**
- * @brief Print the method and URI of a SSDP message.
- *
- * @param message the message to print
- */
-void ssdp_print_message(ssdp_message_t message) {
-    printf("SSDP message:\n");
-    printf("  is request ?: %d\n", message.is_request);
-    printf("  Method: %s\n", ssdp_method_to_str(message.method));
-}
diff --git a/src/sha256.c b/src/sha256.c
deleted file mode 100644
index eb9c5c0733e7a6234998e1dff49300c4b58e7d71..0000000000000000000000000000000000000000
--- a/src/sha256.c
+++ /dev/null
@@ -1,158 +0,0 @@
-/*********************************************************************
-* Filename:   sha256.c
-* Author:     Brad Conte (brad AT bradconte.com)
-* Copyright:
-* Disclaimer: This code is presented "as is" without any guarantees.
-* Details:    Implementation of the SHA-256 hashing algorithm.
-              SHA-256 is one of the three algorithms in the SHA2
-              specification. The others, SHA-384 and SHA-512, are not
-              offered in this implementation.
-              Algorithm specification can be found here:
-               * http://csrc.nist.gov/publications/fips/fips180-2/fips180-2withchangenotice.pdf
-              This implementation uses little endian byte order.
-*********************************************************************/
-
-/*************************** HEADER FILES ***************************/
-#include <stdlib.h>
-#include <memory.h>
-#include "sha256.h"
-
-/****************************** MACROS ******************************/
-#define ROTLEFT(a,b) (((a) << (b)) | ((a) >> (32-(b))))
-#define ROTRIGHT(a,b) (((a) >> (b)) | ((a) << (32-(b))))
-
-#define CH(x,y,z) (((x) & (y)) ^ (~(x) & (z)))
-#define MAJ(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
-#define EP0(x) (ROTRIGHT(x,2) ^ ROTRIGHT(x,13) ^ ROTRIGHT(x,22))
-#define EP1(x) (ROTRIGHT(x,6) ^ ROTRIGHT(x,11) ^ ROTRIGHT(x,25))
-#define SIG0(x) (ROTRIGHT(x,7) ^ ROTRIGHT(x,18) ^ ((x) >> 3))
-#define SIG1(x) (ROTRIGHT(x,17) ^ ROTRIGHT(x,19) ^ ((x) >> 10))
-
-/**************************** VARIABLES *****************************/
-static const WORD k[64] = {
-	0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5,
-	0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174,
-	0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da,
-	0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967,
-	0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85,
-	0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070,
-	0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3,
-	0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2
-};
-
-/*********************** FUNCTION DEFINITIONS ***********************/
-void sha256_transform(SHA256_CTX *ctx, const BYTE data[])
-{
-	WORD a, b, c, d, e, f, g, h, i, j, t1, t2, m[64];
-
-	for (i = 0, j = 0; i < 16; ++i, j += 4)
-		m[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]);
-	for ( ; i < 64; ++i)
-		m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16];
-
-	a = ctx->state[0];
-	b = ctx->state[1];
-	c = ctx->state[2];
-	d = ctx->state[3];
-	e = ctx->state[4];
-	f = ctx->state[5];
-	g = ctx->state[6];
-	h = ctx->state[7];
-
-	for (i = 0; i < 64; ++i) {
-		t1 = h + EP1(e) + CH(e,f,g) + k[i] + m[i];
-		t2 = EP0(a) + MAJ(a,b,c);
-		h = g;
-		g = f;
-		f = e;
-		e = d + t1;
-		d = c;
-		c = b;
-		b = a;
-		a = t1 + t2;
-	}
-
-	ctx->state[0] += a;
-	ctx->state[1] += b;
-	ctx->state[2] += c;
-	ctx->state[3] += d;
-	ctx->state[4] += e;
-	ctx->state[5] += f;
-	ctx->state[6] += g;
-	ctx->state[7] += h;
-}
-
-void sha256_init(SHA256_CTX *ctx)
-{
-	ctx->datalen = 0;
-	ctx->bitlen = 0;
-	ctx->state[0] = 0x6a09e667;
-	ctx->state[1] = 0xbb67ae85;
-	ctx->state[2] = 0x3c6ef372;
-	ctx->state[3] = 0xa54ff53a;
-	ctx->state[4] = 0x510e527f;
-	ctx->state[5] = 0x9b05688c;
-	ctx->state[6] = 0x1f83d9ab;
-	ctx->state[7] = 0x5be0cd19;
-}
-
-void sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len)
-{
-	WORD i;
-
-	for (i = 0; i < len; ++i) {
-		ctx->data[ctx->datalen] = data[i];
-		ctx->datalen++;
-		if (ctx->datalen == 64) {
-			sha256_transform(ctx, ctx->data);
-			ctx->bitlen += 512;
-			ctx->datalen = 0;
-		}
-	}
-}
-
-void sha256_final(SHA256_CTX *ctx, BYTE hash[])
-{
-	WORD i;
-
-	i = ctx->datalen;
-
-	// Pad whatever data is left in the buffer.
-	if (ctx->datalen < 56) {
-		ctx->data[i++] = 0x80;
-		while (i < 56)
-			ctx->data[i++] = 0x00;
-	}
-	else {
-		ctx->data[i++] = 0x80;
-		while (i < 64)
-			ctx->data[i++] = 0x00;
-		sha256_transform(ctx, ctx->data);
-		memset(ctx->data, 0, 56);
-	}
-
-	// Append to the padding the total message's length in bits and transform.
-	ctx->bitlen += ctx->datalen * 8;
-	ctx->data[63] = ctx->bitlen;
-	ctx->data[62] = ctx->bitlen >> 8;
-	ctx->data[61] = ctx->bitlen >> 16;
-	ctx->data[60] = ctx->bitlen >> 24;
-	ctx->data[59] = ctx->bitlen >> 32;
-	ctx->data[58] = ctx->bitlen >> 40;
-	ctx->data[57] = ctx->bitlen >> 48;
-	ctx->data[56] = ctx->bitlen >> 56;
-	sha256_transform(ctx, ctx->data);
-
-	// Since this implementation uses little endian byte ordering and SHA uses big endian,
-	// reverse all the bytes when copying the final state to the output hash.
-	for (i = 0; i < 4; ++i) {
-		hash[i]      = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff;
-		hash[i + 4]  = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff;
-		hash[i + 8]  = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff;
-		hash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff;
-		hash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff;
-		hash[i + 20] = (ctx->state[5] >> (24 - i * 8)) & 0x000000ff;
-		hash[i + 24] = (ctx->state[6] >> (24 - i * 8)) & 0x000000ff;
-		hash[i + 28] = (ctx->state[7] >> (24 - i * 8)) & 0x000000ff;
-	}
-}