From 6b894a41294ee4f965e861ed5ca6fb828e13f2c5 Mon Sep 17 00:00:00 2001 From: Jan Vcelak <jan.vcelak@nic.cz> Date: Fri, 24 May 2013 13:28:11 +0200 Subject: [PATCH] implement server identification using TXT/CH refs #2463 Change-Id: I7a686f92bf9879e248f7878974f1840d179d84d0 --- Knot.files | 2 + src/Makefile.am | 2 + src/knot/server/zones.c | 78 +++++++-------- src/libknot/nameserver/chaos.c | 138 +++++++++++++++++++++++++++ src/libknot/nameserver/chaos.h | 50 ++++++++++ src/libknot/nameserver/name-server.c | 14 ++- src/libknot/nameserver/name-server.h | 4 + 7 files changed, 241 insertions(+), 47 deletions(-) create mode 100644 src/libknot/nameserver/chaos.c create mode 100644 src/libknot/nameserver/chaos.h diff --git a/Knot.files b/Knot.files index 3561585561..4190a02155 100644 --- a/Knot.files +++ b/Knot.files @@ -147,6 +147,8 @@ src/libknot/dname.h src/libknot/edns.c src/libknot/edns.h src/libknot/libknot.h +src/libknot/nameserver/chaos.c +src/libknot/nameserver/chaos.h src/libknot/nameserver/name-server.c src/libknot/nameserver/name-server.h src/libknot/nsec3.c diff --git a/src/Makefile.am b/src/Makefile.am index 7053302097..5cafe23637 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -168,6 +168,8 @@ libknot_la_SOURCES = \ libknot/zone/zone-diff.c \ libknot/nameserver/name-server.h \ libknot/nameserver/name-server.c \ + libknot/nameserver/chaos.h \ + libknot/nameserver/chaos.c \ libknot/updates/changesets.h \ libknot/updates/changesets.c \ libknot/updates/xfr-in.h \ diff --git a/src/knot/server/zones.c b/src/knot/server/zones.c index a9931bedd5..4bf4a20be9 100644 --- a/src/knot/server/zones.c +++ b/src/knot/server/zones.c @@ -34,6 +34,7 @@ #include "libknot/updates/xfr-in.h" #include "knot/server/zones.h" #include "libknot/nameserver/name-server.h" +#include "libknot/nameserver/chaos.h" #include "libknot/updates/changesets.h" #include "libknot/tsig-op.h" #include "common/descriptor.h" @@ -2051,8 +2052,10 @@ int zones_normal_query_answer(knot_nameserver_t *nameserver, { rcu_read_lock(); + knot_rcode_t rcode = 0; knot_packet_t *resp = NULL; const knot_zone_t *zone = NULL; + const uint16_t qclass = knot_packet_qclass(query); dbg_zones_verb("Preparing response structure.\n"); int ret = knot_ns_prep_normal_response(nameserver, query, &resp, &zone, @@ -2060,20 +2063,6 @@ int zones_normal_query_answer(knot_nameserver_t *nameserver, ? *rsize : 0); query->zone = zone; - // check for TSIG in the query - // not required, TSIG is already found if it is there -// if (knot_packet_additional_rrset_count(query) > 0) { -// /*! \todo warning */ -// const knot_rrset_t *tsig = knot_packet_additional_rrset(query, -// knot_packet_additional_rrset_count(query) - 1); -// if (knot_rrset_type(tsig) == KNOT_RRTYPE_TSIG) { -// dbg_zones_verb("found TSIG in normal query\n"); -// knot_packet_set_tsig(query, tsig); -// } -// } - - knot_rcode_t rcode = 0; - switch (ret) { case KNOT_EOK: rcode = KNOT_RCODE_NOERROR; @@ -2088,14 +2077,15 @@ int zones_normal_query_answer(knot_nameserver_t *nameserver, break; } - if (rcode == KNOT_RCODE_NOERROR - && ((zone == NULL && knot_packet_tsig(query) == NULL) - || (knot_packet_qclass(query) != KNOT_CLASS_IN - && knot_packet_qclass(query) != KNOT_CLASS_ANY))) { - /*! \todo If there is TSIG, this should be probably handled - * as a key error. - */ - rcode = KNOT_RCODE_REFUSED; + if (rcode == KNOT_RCODE_NOERROR) { + switch (qclass) { + case KNOT_CLASS_IN: + case KNOT_CLASS_CH: + case KNOT_CLASS_ANY: + break; + default: + rcode = KNOT_RCODE_REFUSED; + } } if (rcode != KNOT_RCODE_NOERROR) { @@ -2124,25 +2114,19 @@ int zones_normal_query_answer(knot_nameserver_t *nameserver, size_t answer_size = *rsize; int ret = KNOT_EOK; - if (zone == NULL) { - assert(knot_packet_tsig(query) != NULL); - // treat as BADKEY error - /*! \todo Is this OK?? */ - rcode = KNOT_RCODE_NOTAUTH; - tsig_rcode = KNOT_RCODE_BADKEY; - ret = KNOT_TSIG_EBADKEY; - } else { + const knot_rrset_t *tsig = knot_packet_tsig(query); + if (tsig != NULL) { dbg_zones_verb("Checking TSIG in query.\n"); - const knot_rrset_t *tsig = knot_packet_tsig(query); - if (tsig == NULL) { - // no TSIG, this is completely valid - tsig_rcode = 0; - ret = KNOT_EOK; + if (zone == NULL) { + // treat as BADKEY error + /*! \todo Is this OK?? */ + rcode = KNOT_RCODE_NOTAUTH; + tsig_rcode = KNOT_RCODE_BADKEY; + ret = KNOT_TSIG_EBADKEY; } else { ret = zones_check_tsig_query(zone, query, addr, - &rcode, &tsig_rcode, - &tsig_key_zone, - &tsig_prev_time_signed); + &rcode, &tsig_rcode, &tsig_key_zone, + &tsig_prev_time_signed); } } @@ -2165,11 +2149,14 @@ int zones_normal_query_answer(knot_nameserver_t *nameserver, resp, resp_wire, &answer_size); } else { - ret = knot_ns_answer_normal(nameserver, zone, - resp, resp_wire, - &answer_size, - transport == - NS_TRANSPORT_UDP); + if (qclass == KNOT_CLASS_CH) { + ret = knot_ns_answer_chaos(nameserver, + resp, resp_wire, &answer_size); + } else { + ret = knot_ns_answer_normal(nameserver, + zone, resp, resp_wire, &answer_size, + transport == NS_TRANSPORT_UDP); + } query->flags = resp->flags; /* Copy markers. */ } @@ -2693,6 +2680,11 @@ int zones_ns_conf_hook(const struct conf_t *conf, void *data) /* Set NSID. */ knot_ns_set_nsid(ns, conf->nsid, conf->nsid_len); + /* Server identification, RFC 4892. */ + ns->identity = conf->identity; + ns->version = conf->version; + ns->hostname = conf->hostname; + knot_zonedb_t *old_db = 0; int ret = zones_update_db_from_config(conf, ns, &old_db); diff --git a/src/libknot/nameserver/chaos.c b/src/libknot/nameserver/chaos.c new file mode 100644 index 0000000000..389da12c3d --- /dev/null +++ b/src/libknot/nameserver/chaos.c @@ -0,0 +1,138 @@ +/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <strings.h> + +#include "chaos.h" +#include "common/descriptor.h" +#include "packet/packet.h" +#include "packet/response.h" + +/*! + * \brief Get a string result for a given TXT query. + */ +static const char *get_txt_response_string(const knot_nameserver_t *nameserver, + const knot_dname_t *qname) +{ + char *qname_str = knot_dname_to_str(qname); + const char *response = NULL; + + if (strcasecmp("id.server.", qname_str) == 0) { + response = nameserver->identity; + } else if (strcasecmp("version.server.", qname_str) == 0) { + response = nameserver->version; + } else if (strcasecmp("hostname.server.", qname_str) == 0) { + response = nameserver->hostname; + } + + free(qname_str); + + return response; +} + +/*! + * \brief Create TXT RR with a given string content. + * + * \param owner RR owner name. + * \param response String to be saved in RDATA. Truncated to 255 chars. + * + * \return Allocated RRset or NULL in case of error. + */ +static knot_rrset_t *create_txt_rrset(const knot_dname_t *owner, + const char *response) +{ + // truncate response to one TXT label + size_t response_len = strlen(response); + if (response_len > 255) + response_len = 255; + + knot_dname_t *rowner = knot_dname_deep_copy(owner); + if (!rowner) + return NULL; + + knot_rrset_t *rrset; + rrset = knot_rrset_new(rowner, KNOT_RRTYPE_TXT, KNOT_CLASS_CH, 0); + knot_dname_release(rowner); + if (!rrset) + return NULL; + + uint8_t *rdata = knot_rrset_create_rdata(rrset, response_len + 1); + if (!rdata) { + knot_rrset_deep_free(&rrset, 1, 0); + return NULL; + } + + rdata[0] = response_len; + memcpy(&rdata[1], response, response_len); + + return rrset; +} + +/*! + * \brief Create a response for a TXT CHAOS query. + * + * \param return KNOT_RCODE_NOERROR if the response was succesfully created, + * otherwise an RCODE representing the failure. + */ +static int answer_txt(knot_nameserver_t *nameserver, knot_packet_t *response, + uint8_t *response_wire, size_t *response_size) +{ + const knot_dname_t *qname = knot_packet_qname(response); + const char *response_str = get_txt_response_string(nameserver, qname); + if (response_str == NULL || response_str[0] == '\0') + return KNOT_RCODE_REFUSED; + + knot_rrset_t *rrset = create_txt_rrset(qname, response_str); + if (!rrset) + return KNOT_RCODE_SERVFAIL; + + int result = knot_response_add_rrset_answer(response, rrset, 1, 0, 0); + if (result != KNOT_EOK) { + knot_rrset_deep_free(&rrset, 1, 0); + return KNOT_RCODE_SERVFAIL; + } + + result = ns_response_to_wire(response, response_wire, response_size); + if (result != KNOT_EOK) { + knot_rrset_deep_free(&rrset, 1, 0); + return KNOT_RCODE_SERVFAIL; + } + + knot_rrset_deep_free(&rrset, 1, 0); + knot_response_set_rcode(response, KNOT_RCODE_NOERROR); + + return KNOT_RCODE_NOERROR; +} + +/*! + * \brief Create a response for a given query in the CHAOS class. + */ +int knot_ns_answer_chaos(knot_nameserver_t *nameserver, knot_packet_t *resp, + uint8_t *resp_wire, size_t *resp_size) +{ + int rcode = KNOT_RCODE_REFUSED; + + if (knot_packet_qtype(resp) == KNOT_RRTYPE_TXT) { + rcode = answer_txt(nameserver, resp, resp_wire, resp_size); + } + + if (rcode != KNOT_RCODE_NOERROR) { + knot_ns_error_response_full(nameserver, resp, rcode, + resp_wire, resp_size); + } + + return KNOT_EOK; +} diff --git a/src/libknot/nameserver/chaos.h b/src/libknot/nameserver/chaos.h new file mode 100644 index 0000000000..2c1e55432f --- /dev/null +++ b/src/libknot/nameserver/chaos.h @@ -0,0 +1,50 @@ +/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/*! + * \file chaos.h + * + * \author Jan Vcelak <jvcelak@nic.cz> + * + * \addtogroup query_processing + * @{ + */ + +#ifndef _KNOT_CHAOS_H_ +#define _KNOT_CHAOS_H_ + +#include <stdlib.h> +#include <stdint.h> + +#include "name-server.h" +#include "packet/packet.h" + +/*! + * \brief Create a response for a given query in the CHAOS class. + * + * \param nameserver Name server structure. + * \param response Response structure with parsed query. + * \param response_wire Output for response in wire format. + * \param response_size IN: maximum acceptable size of input, OUT: real size. + * + * \return Always KNOT_EOK. + */ +int knot_ns_answer_chaos(knot_nameserver_t *nameserver, knot_packet_t *response, + uint8_t *response_wire, size_t *response_size); + +#endif // _KNOT_CHAOS_H_ + +/*! @} */ diff --git a/src/libknot/nameserver/name-server.c b/src/libknot/nameserver/name-server.c index 40625bd77b..1646bad5af 100644 --- a/src/libknot/nameserver/name-server.c +++ b/src/libknot/nameserver/name-server.c @@ -3040,6 +3040,10 @@ knot_nameserver_t *knot_ns_create() ns->opt_rr = NULL; } + ns->identity = NULL; + ns->version = NULL; + ns->hostname = NULL; + knot_packet_free(&err); return ns; @@ -3404,10 +3408,6 @@ int knot_ns_prep_normal_response(knot_nameserver_t *nameserver, query->parsed, query->size); dbg_ns_detail("Opt RR: version: %d, payload: %d\n", query->opt_rr.version, query->opt_rr.payload); - - // get the answer for the query - knot_zonedb_t *zonedb = rcu_dereference(nameserver->zone_db); - dbg_ns_detail("EDNS supported in query: %d\n", knot_query_edns_supported(query)); @@ -3428,6 +3428,11 @@ int knot_ns_prep_normal_response(knot_nameserver_t *nameserver, dbg_ns_verb("Response max size: %zu\n", (*resp)->max_size); + // search for zone only for IN and ANY classes + uint16_t qclass = knot_packet_qclass(*resp); + if (qclass != KNOT_CLASS_IN && qclass != KNOT_CLASS_ANY) + return KNOT_EOK; + const knot_dname_t *qname = knot_packet_qname(*resp); assert(qname != NULL); @@ -3438,6 +3443,7 @@ dbg_ns_exec_verb( free(name_str); ); // find zone in which to search for the name + knot_zonedb_t *zonedb = rcu_dereference(nameserver->zone_db); *zone = ns_get_zone_for_qname(zonedb, qname, qtype); return KNOT_EOK; diff --git a/src/libknot/nameserver/name-server.h b/src/libknot/nameserver/name-server.h index 7f36ed5189..58ea212b9c 100644 --- a/src/libknot/nameserver/name-server.h +++ b/src/libknot/nameserver/name-server.h @@ -68,6 +68,10 @@ typedef struct knot_nameserver { size_t err_resp_size; /*!< Size of the prepared error response. */ knot_opt_rr_t *opt_rr; /*!< OPT RR with the server's EDNS0 info. */ + const char *identity; //!< RFC 4892, server identity (id.server). + const char *version; //!< RFC 4892, server version (version.server). + const char *hostname; //!< RFC 4892, server host name (hostname.server). + void *data; } knot_nameserver_t; -- GitLab