diff --git a/CuteDNS.files b/CuteDNS.files index 853e800d583ca247bcdbe8c9f83f36cbc4ec358d..cf56fc22248fd6773b97da78fa463ea75481e567 100644 --- a/CuteDNS.files +++ b/CuteDNS.files @@ -83,3 +83,5 @@ src/dnslib/packet.h src/dnslib/response.c src/dnslib/edns.h src/dnslib/utils.h +src/dnslib/zonedb.h +src/dnslib/zonedb.c diff --git a/src/dnslib/zonedb.c b/src/dnslib/zonedb.c new file mode 100644 index 0000000000000000000000000000000000000000..41a3de4353c0da0857fb61e5f33d802f79fb0a60 --- /dev/null +++ b/src/dnslib/zonedb.c @@ -0,0 +1,135 @@ +#include <stdlib.h> +#include <assert.h> + +#include <urcu.h> + +#include "zonedb.h" +#include "common.h" +#include "skip-list.h" +#include "zone.h" +#include "dname.h" +#include "node.h" + +/*----------------------------------------------------------------------------*/ +/* Non-API functions */ +/*----------------------------------------------------------------------------*/ + +static int dnslib_zonedb_compare_zone_names(void *d1, void *d2) +{ + const dnslib_dname_t *dname1 = (const dnslib_dname_t *)d1; + const dnslib_dname_t *dname2 = (const dnslib_dname_t *)d2; + + return (dnslib_dname_compare(dname1, dname2)); +} + +/*----------------------------------------------------------------------------*/ +/* API functions */ +/*----------------------------------------------------------------------------*/ + +dnslib_zonedb_t *dnslib_zonedb_new() +{ + dnslib_zonedb_t *db = + (dnslib_zonedb_t *)malloc(sizeof(dnslib_zonedb_t)); + CHECK_ALLOC_LOG(db, NULL); + + db->zones = skip_create_list(dnslib_zonedb_compare_zone_names); + if (db->zones == NULL) { + free(db); + return NULL; + } + + return db; +} + +/*----------------------------------------------------------------------------*/ + +int dnslib_zonedb_add_zone(dnslib_zonedb_t *db, dnslib_zone_t *zone) +{ + int ret = skip_insert(db->zones, zone->apex->owner, zone, NULL); + assert(ret == 0 || ret == 1 || ret == -1); + return ret; +} + +/*----------------------------------------------------------------------------*/ + +int dnslib_zonedb_remove_zone(dnslib_zonedb_t *db, dnslib_dname_t *zone_name) +{ + // add some lock to avoid multiple removals + dnslib_zone_t *z = (dnslib_zone_t *)skip_find(db->zones, zone_name); + + if (z == NULL) { + return -1; + } + + // remove the zone from the skip list, but do not destroy it + int ret = skip_remove(db->zones, zone_name, NULL, NULL); + assert(ret == 0); + + // wait for all readers to finish + synchronize_rcu(); + + // properly destroy the zone and all its contents + dnslib_zone_deep_free(&z); + + return 0; +} + +/*----------------------------------------------------------------------------*/ + +const dnslib_zone_t *dnslib_zonedb_find_zone_for_name(dnslib_zonedb_t *db, + const dnslib_dname_t *dname) +{ + rcu_read_lock(); + + dnslib_zone_t *zone = skip_find(db->zones, (void *)dname); + + rcu_read_unlock(); + + return zone; +} + +/*----------------------------------------------------------------------------*/ + +const dnslib_node_t *dnslib_zonedb_find_name_in_zone(const dnslib_zone_t *zone, + const dnslib_dname_t *dname) +{ + assert(zone != NULL && dname != NULL); + + // start of RCU reader critical section + rcu_read_lock(); + + const dnslib_node_t *n = dnslib_zonedb_find_name_in_zone(zone, dname); + + // end of RCU reader critical section + rcu_read_unlock(); + + return n; +} + +/*----------------------------------------------------------------------------*/ + +void dnslib_zonedb_deep_free(dnslib_zonedb_t **db) +{ + const skip_node_t *zn = skip_first((*db)->zones); + dnslib_zone_t *zone = NULL; + + while (zn != NULL) { + zone = (dnslib_zone_t *)zn->value; + assert(zone != NULL); + + // remove the zone from the database + skip_remove((*db)->zones, zn->key, NULL, NULL); + // wait for all readers to finish + synchronize_rcu(); + // destroy the zone + dnslib_zone_deep_free(&zone); + + zn = skip_first((*db)->zones); + } + + assert(skip_is_empty((*db)->zones)); + + skip_destroy_list(&(*db)->zones, NULL, NULL); + free(*db); + *db = NULL; +} diff --git a/src/dnslib/zonedb.h b/src/dnslib/zonedb.h new file mode 100644 index 0000000000000000000000000000000000000000..79987be55637470edc9252be478ce95b8df8dba2 --- /dev/null +++ b/src/dnslib/zonedb.h @@ -0,0 +1,109 @@ +/*! + * \file zonedb.h + * + * \author Lubos Slovak <lubos.slovak@nic.cz> + * + * \brief Zone database structure and API for manipulating it. + * + * Zone database groups several zones and provides functions for finding + * suitable zone for a domain name, for searching in a particular zone, etc. + * + * \addtogroup dnslib + * @{ + */ + +#ifndef _CUTEDNS_DNSLIB_ZONEDB_H_ +#define _CUTEDNS_DNSLIB_ZONEDB_H_ + +#include "skip-list.h" +#include "zone.h" +#include "node.h" +#include "dname.h" + +struct dnslib_zonedb { + skip_list_t *zones; +}; + +typedef struct dnslib_zonedb dnslib_zonedb_t; + +/*----------------------------------------------------------------------------*/ + +/*! + * \brief Allocates and initializes the zone database structure. + * + * \return Pointer to the created zone database structure. + */ +dnslib_zonedb_t *dnslib_zonedb_new(); + +/*! + * \brief Adds new zone to the database. + * + * \param database Zone database to store the zone. + * \param zone Parsed zone in ldns_zone format. + * + * \retval 0 On success. + * \retval -1 On failure. + */ +int dnslib_zonedb_add_zone(dnslib_zonedb_t *db, dnslib_zone_t *zone); + +/*! + * \brief Removes the given zone from the database if it exists. + * + * \param db Zone database to remove from. + * \param zone_name Name of the zone to be removed. + * + * The removal of a zone is synchronized using RCU mechanism, so the zone data + * will not be destroyed while some thread may be using it. + * + * \retval 0 On success. + * \retval -1 If the zone was not found. + */ +int dnslib_zonedb_remove_zone(dnslib_zonedb_t *db, dnslib_dname_t *zone_name); + +/*! + * \brief Finds zone the given domain name should belong to. + * + * \param database Zone database to search in. + * \param dname Domain name to find zone for. + * + * \retval Zone in which the domain name should be present. +// * +// * \note As the zones are ordered in reverse canonical order, a possible parent +// * of the returned zone may be retrieved easily as it is the next item +// * in the linked list (zdb_zone.next). + */ +const dnslib_zone_t *dnslib_zonedb_find_zone_for_name(dnslib_zonedb_t *db, + const dnslib_dname_t *dname); + +/*! + * \brief Finds the given name in the zone database and returns corresponding + * zone node. + * + * \param database Zone database to search in. + * \param dname Domain name to find. + * + * \return Proper zone node for the given name or NULL if not found. + */ +const dnslib_node_t *dnslib_zonedb_find_name_in_zone(const dnslib_zone_t *zone, + const dnslib_dname_t *dname); + +/*! + * \brief Destroys and deallocates the whole zone database. + * + * \param database Pointer to pointer to the zone database to be destroyed. + * + * The zones are destroyed one-by-one and the process is synchronized using + * RCU mechanism, so the zone data will not be destroyed while some thread may + * be using it. +// * +// * \todo Destroy nodes which are not hashed into the table. Best will be to +// * destroy zone nodes from the list and tell zds_destroy() not to destroy +// * the stored items. + */ +void dnslib_zonedb_deep_free(dnslib_zonedb_t **db); + +/*----------------------------------------------------------------------------*/ + +#endif /* _CUTEDNS_DNSLIB_ZONEDB_H_ */ + +/*! @} */