Skip to content
Snippets Groups Projects
Commit 42967d5e authored by Marek Vavrusa's avatar Marek Vavrusa
Browse files

Implemented partial IN processing.

No DNAME, CNAME, DNSKEY, NSEC/NSEC3.
parent 9f0dbc2c
No related branches found
No related tags found
1 merge request!147Packet Api Rewrite
......@@ -122,6 +122,8 @@ libknot_la_SOURCES = \
libknot/nameserver/name-server.c \
libknot/nameserver/chaos.h \
libknot/nameserver/chaos.c \
libknot/nameserver/ns_proc_query.h \
libknot/nameserver/ns_proc_query.c \
libknot/updates/changesets.h \
libknot/updates/changesets.c \
libknot/updates/xfr-in.h \
......
......@@ -9,12 +9,22 @@
#include "libknot/util/debug.h"
#include "libknot/nameserver/chaos.h"
struct query_data {
int state;
uint16_t rcode;
uint16_t rcode_tsig;
knot_pkt_t *pkt;
const knot_node_t *node, *encloser, *previous;
};
/* Forward decls. */
int answer_internet(knot_pkt_t *pkt, ns_proc_context_t *ctx);
int answer_chaos(knot_pkt_t *pkt, ns_proc_context_t *ctx);
static int tsig_check(knot_pkt_t *pkt);
static void find_query_zone(knot_pkt_t *pkt, knot_nameserver_t *ns);
static int zone_state(const knot_zone_t *zone);
static const knot_zone_t *find_query_zone(knot_pkt_t *pkt, knot_nameserver_t *ns);
static int prepare_answer(knot_pkt_t *query, knot_pkt_t *resp, knot_nameserver_t *ns);
static int in_zone_answer(knot_pkt_t *resp, struct query_data *qdata);
/*! \brief Module implementation. */
const ns_proc_module_t _ns_proc_query = {
......@@ -22,14 +32,8 @@ const ns_proc_module_t _ns_proc_query = {
&ns_proc_query_reset,
&ns_proc_query_finish,
&ns_proc_query_in,
&ns_proc_query_out
};
struct query_data {
int state;
uint16_t rcode;
uint16_t rcode_tsig;
knot_pkt_t *pkt;
&ns_proc_query_out,
&ns_proc_query_err
};
#define QUERY_DATA(ctx) ((struct query_data *)(ctx)->data)
......@@ -116,7 +120,7 @@ int ns_proc_query_out(knot_pkt_t *pkt, ns_proc_context_t *ctx)
data->rcode = KNOT_RCODE_NOERROR;
}
/* Answer from qclass. */
/* Answer based on qclass. */
switch (knot_pkt_qclass(pkt)) {
case KNOT_CLASS_CH:
next_state = answer_chaos(pkt, ctx);
......@@ -144,10 +148,7 @@ int ns_proc_query_err(knot_pkt_t *pkt, ns_proc_context_t *ctx)
dbg_ns("%s: making error response, rcode = %d\n",
__func__, data->rcode);
/* Servfail error, make synthetic response. */
if (data->rcode != KNOT_RCODE_SERVFAIL) {
/*! \todo Better response for parsed packets. */
}
/*! \todo Prettier error response. */
/* Clear packet. */
knot_pkt_clear(pkt);
......@@ -156,6 +157,7 @@ int ns_proc_query_err(knot_pkt_t *pkt, ns_proc_context_t *ctx)
knot_pkt_t *query = data->pkt;
knot_wire_set_id(pkt->wire, knot_wire_get_id(query->wire));
knot_wire_set_opcode(pkt->wire, knot_wire_get_opcode(query->wire));
knot_wire_set_qr(pkt->wire);
knot_wire_set_rcode(pkt->wire, data->rcode);
if (knot_wire_get_rd(query->wire)) {
knot_wire_set_rd(pkt->wire);
......@@ -170,7 +172,17 @@ int ns_proc_query_err(knot_pkt_t *pkt, ns_proc_context_t *ctx)
*/
int answer_internet(knot_pkt_t *pkt, ns_proc_context_t *ctx)
{
return NS_PROC_FAIL;
struct query_data *data = QUERY_DATA(ctx);
int next_state = NS_PROC_FAIL;
/* Check zone validity. */
switch(zone_state(pkt->zone)) {
case KNOT_EOK: next_state = in_zone_answer(pkt, data); break;
case KNOT_ENOENT: data->rcode = KNOT_RCODE_REFUSED; break;
default: data->rcode = KNOT_RCODE_SERVFAIL; break;
}
return next_state;
}
/*!
......@@ -178,9 +190,11 @@ int answer_internet(knot_pkt_t *pkt, ns_proc_context_t *ctx)
*/
int answer_chaos(knot_pkt_t *pkt, ns_proc_context_t *ctx)
{
dbg_ns("%s(%p, %p)\n", __func__, pkt, ctx);
struct query_data *data = QUERY_DATA(ctx);
data->rcode = knot_chaos_answer(pkt, ctx->ns);
if (data->rcode == KNOT_RCODE_NOERROR) {
if (data->rcode != KNOT_RCODE_NOERROR) {
return NS_PROC_FAIL;
}
......@@ -197,7 +211,19 @@ static int tsig_check(knot_pkt_t *pkt)
return KNOT_EOK;
}
static void find_query_zone(knot_pkt_t *pkt, knot_nameserver_t *ns)
static int zone_state(const knot_zone_t *zone)
{
if (zone == NULL) {
dbg_ns("%s: zone not found\n", __func__);
return KNOT_ENOENT;
} else if (zone->contents == NULL) {
dbg_ns("%s: zone expired or stub\n", __func__);
return KNOT_ENOZONE;
}
return KNOT_EOK;
}
static const knot_zone_t *find_query_zone(knot_pkt_t *pkt, knot_nameserver_t *ns)
{
uint16_t qtype = knot_pkt_qtype(pkt);
uint16_t qclass = knot_pkt_qclass(pkt);
......@@ -205,12 +231,12 @@ static void find_query_zone(knot_pkt_t *pkt, knot_nameserver_t *ns)
// search for zone only for IN and ANY classes
if (qclass != KNOT_CLASS_IN && qclass != KNOT_CLASS_ANY) {
return;
return NULL;
}
// find zone in which to search for the name
knot_zonedb_t *zonedb = rcu_dereference(ns->zone_db);
pkt->zone = ns_get_zone_for_qname(zonedb, qname, qtype);
return ns_get_zone_for_qname(zonedb, qname, qtype);
}
static int prepare_answer(knot_pkt_t *query, knot_pkt_t *resp, knot_nameserver_t *ns)
......@@ -219,11 +245,13 @@ static int prepare_answer(knot_pkt_t *query, knot_pkt_t *resp, knot_nameserver_t
int ret = knot_pkt_init_response(resp, query);
if (ret != KNOT_EOK) {
dbg_ns("%s: can't init response pkt (%d)\n", __func__, ret);
return ret;
}
// find zone for qname
find_query_zone(query, ns);
resp->zone = find_query_zone(query, ns);
dbg_ns("%s: found zone %p for pkt %p\n", __func__, resp->zone, query);
/* Check if EDNS is supported. */
if (!knot_pkt_have_edns(query)) {
......@@ -235,11 +263,221 @@ static int prepare_answer(knot_pkt_t *query, knot_pkt_t *resp, knot_nameserver_t
if (ret == KNOT_EOK) {
// copy the DO bit from the query
if (knot_pkt_have_dnssec(query)) {
dbg_ns("%s: setting DO=1 in OPT RR\n", __func__);
knot_edns_set_do(&(resp)->opt_rr);
}
} else {
dbg_ns("%s: can't add OPT RR (%d)\n", __func__, ret);
}
return ret;
}
enum {
BEGIN,
NODATA,
HIT,
MISS,
DELEG,
CNAME,
DNAME,
ERROR
};
static int in_zone_name_found(knot_pkt_t *pkt, const knot_dname_t **name,
struct query_data *qdata)
{
uint16_t qtype = knot_pkt_qtype(pkt);
dbg_ns("%s(%p, %p, %p)\n", __func__, pkt, name, qdata);
if (knot_node_rrset(qdata->node, KNOT_RRTYPE_CNAME) != NULL
&& qtype != KNOT_RRTYPE_CNAME && qtype != KNOT_RRTYPE_RRSIG) {
dbg_ns("%s: solving CNAME\n", __func__);
qdata->rcode = KNOT_RCODE_NOTIMPL;
return ERROR;
assert(0); /*! \todo Implement CNAME solving. */
}
// now we have the node for answering
if (qtype != KNOT_RRTYPE_DS && // DS query is answered normally
(knot_node_is_deleg_point(qdata->node) || knot_node_is_non_auth(qdata->node))) {
dbg_ns("%s: solving REFERRAL\n", __func__);
return DELEG;
}
/*! \todo Implement normal answer. */
int added = 0; /*! \todo useless */
int ret = ns_put_answer(qdata->node, pkt->zone->contents, *name, qtype, pkt, &added, 0 /*! \todo check from pkt */);
if (ret != KNOT_EOK) {
dbg_ns("%s: failed answer from node %p (%d)\n", __func__, qdata->node, ret);
/*! \todo set rcode */
return ERROR;
} else {
dbg_ns("%s: answered, %d added\n", __func__, added);
}
// this is the only case when the servers answers from
// particular node, i.e. the only case when it may return SOA
// or NS records in Answer section
if (knot_wire_get_tc(pkt->wire) == 0
&& knot_pkt_have_dnssec(pkt->query)
&& qdata->node == knot_zone_contents_apex(pkt->zone->contents)
&& (qtype == KNOT_RRTYPE_SOA || qtype == KNOT_RRTYPE_NS)) {
qdata->rcode = KNOT_RCODE_NOTIMPL;
return ERROR;
assert(0); /*! \todo Implement DNSKEY */
/*ret = ns_add_dnskey(node, resp);*/
}
/* Check for NODATA. */
if (added == 0) {
return NODATA;
} else {
return HIT;
}
}
static int in_zone_name_not_found(knot_pkt_t *pkt, const knot_dname_t **name,
struct query_data *qdata)
{
dbg_ns("%s(%p, %p, %p)\n", __func__, pkt, name, qdata);
// else check for a wildcard child
const knot_node_t *wildcard_node = knot_node_wildcard_child(qdata->encloser);
if (wildcard_node) {
dbg_ns("%s: name %p covered by wildcard\n", __func__, *name);
qdata->node = wildcard_node;
qdata->encloser = wildcard_node;
qdata->previous = NULL;
return in_zone_name_found(pkt, name, qdata);
}
// DNAME?
knot_rrset_t *dname_rrset = knot_node_get_rrset(qdata->encloser, KNOT_RRTYPE_DNAME);
if (dname_rrset != NULL
&& knot_rrset_rdata_rr_count(dname_rrset) > 0) {
/*! \todo Implement DNAME processing */
dbg_ns("%s: solving DNAME for name %p\n", __func__, *name);
qdata->rcode = KNOT_RCODE_NOTIMPL;
return ERROR;
/*
int ret = ns_process_dname(dname_rrset, name, pkt);
if (ret != KNOT_EOK) {
return ERROR;
}
// (out-of-bailiwick), just in the current zone if it
// belongs there*/
return DNAME;
}
dbg_ns("%s: name not found in zone %p\n", __func__, *name);
return MISS;
}
static int in_zone_process_name(int state, const knot_dname_t **name,
knot_pkt_t *pkt, struct query_data *qdata)
{
dbg_ns("%s(%d, %p, %p, %p)\n", __func__, state, name, pkt, qdata);
int ret = knot_zone_contents_find_dname(pkt->zone->contents, *name,
&qdata->node, &qdata->encloser,
&qdata->previous);
switch(ret) {
case KNOT_ZONE_NAME_FOUND:
return in_zone_name_found(pkt, name, qdata);
case KNOT_ZONE_NAME_NOT_FOUND:
return in_zone_name_not_found(pkt, name, qdata);
case KNOT_EOUTOFZONE:
assert(state == CNAME || state == DNAME);
return HIT;
default:
return ERROR;
}
}
static int in_zone_answer(knot_pkt_t *resp, struct query_data *qdata)
{
dbg_ns("%s(%p, %p)\n", __func__, resp, qdata);
/* Write answer RRs for QNAME. */
dbg_ns("%s: writing %p ANSWER\n", __func__, resp);
knot_pkt_begin(resp, KNOT_ANSWER);
const knot_dname_t *qname = knot_pkt_qname(resp);
/* Get answer to QNAME. */
int ret = KNOT_EOK;
int state = in_zone_process_name(BEGIN, &qname, resp, qdata);
/* Is authoritative unless referral.
* Before we chase the CNAME chain. */
if (state != DELEG) {
knot_wire_set_aa(resp->wire);
}
/* Additional resolving for CNAME/DNAME. */
while (state == CNAME || state == DNAME) {
state = in_zone_process_name(state, &qname, resp, qdata);
/* Resolved CNAME chain lead to NXDOMAIN, this is okay since
* the first CNAME/DNAME is a valid answer. */
if (state == MISS) {
state = HIT;
}
}
/* Resolve AUTHORITY. */
dbg_ns("%s: writing %p AUTHORITY\n", __func__, resp);
knot_pkt_begin(resp, KNOT_AUTHORITY);
switch (state) {
case HIT: /* Positive response, add (optional) AUTHORITY NS. */
ret = ns_put_authority_ns(resp->zone->contents, resp);
dbg_ns("%s: putting authority NS = %d\n", __func__, ret);
break;
case MISS: /* MISS, set NXDOMAIN RCODE. */
qdata->rcode = KNOT_RCODE_NXDOMAIN;
dbg_ns("%s: answer is NXDOMAIN\n", __func__);
case NODATA: /* NODATA or NXDOMAIN, append AUTHORITY SOA. */
ret = ns_put_authority_soa(resp->zone->contents, resp);
dbg_ns("%s: putting authority SOA = %d\n", __func__, ret);
break;
case DELEG: /* Referral response. */ /*! \todo DS + NS */
ret = ns_referral(qdata->node, resp->zone->contents, qname, resp, knot_pkt_qtype(resp));
break;
case ERROR:
dbg_ns("%s: failed to resolve qname\n", __func__);
ret = KNOT_ERROR;
break;
default:
dbg_ns("%s: invalid state after qname processing = %d\n",
__func__, state);
assert(0);
qdata->rcode = KNOT_RCODE_SERVFAIL;
ret = KNOT_ERROR;
break;
}
/* Check errors after AUTHORITY processing. */
if (ret != KNOT_EOK) {
return NS_PROC_FAIL;
}
// add all missing NSECs/NSEC3s for wildcard nodes
/*! \todo Make function accept query_data with zone+wcnodes */
if (ns_put_nsec_nsec3_wildcard_nodes(resp, resp->zone->contents) != KNOT_EOK) {
return NS_PROC_FAIL;
}
/* Resolve ADDITIONAL. */
/*! \todo no ADDITIONAL for referral response */
dbg_ns("%s: writing %p ADDITIONAL\n", __func__, resp);
knot_pkt_begin(resp, KNOT_ADDITIONAL);
ns_put_additional(resp);
/* Write RCODE. */
knot_wire_set_rcode(resp->wire, qdata->rcode);
/* Complete response. */
return NS_PROC_FINISH;
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment