strongswan/src/libstrongswan/plugins/unbound/unbound_response.c

259 lines
5.5 KiB
C

/*
* Copyright (C) 2012 Reto Guadagnini
* HSR Hochschule fuer Technik Rapperswil
*
* 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 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* 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.
*/
#include <resolver/resolver_response.h>
#include <resolver/rr.h>
#include "unbound_rr.h"
#include "unbound_response.h"
#include <library.h>
#include <utils/debug.h>
#include <unbound.h>
#include <ldns/ldns.h>
typedef struct private_unbound_response_t private_unbound_response_t;
/**
* private data of an unbound_response_t object.
*/
struct private_unbound_response_t {
/**
* Public data
*/
unbound_response_t public;
/**
* Original question string
*/
char* query_name;
/**
* Canonical name of the response
*/
char* canon_name;
/**
* Are the some RRs in the RRset of this response?
*/
bool has_data;
/*
* Does the queried name exist?
*/
bool query_name_exist;
/**
* DNSSEC security state
*/
dnssec_status_t security_state;
/**
* RRset
*/
rr_set_t *rr_set;
};
METHOD(resolver_response_t, get_query_name, char*,
private_unbound_response_t *this)
{
return this->query_name;
}
METHOD(resolver_response_t, get_canon_name, char*,
private_unbound_response_t *this)
{
return this->canon_name;
}
METHOD(resolver_response_t, has_data, bool,
private_unbound_response_t *this)
{
return this->has_data;
}
METHOD(resolver_response_t, query_name_exist, bool,
private_unbound_response_t *this)
{
return this->query_name_exist;
}
METHOD(resolver_response_t, get_security_state, dnssec_status_t,
private_unbound_response_t *this)
{
return this->security_state;
}
METHOD(resolver_response_t, get_rr_set, rr_set_t*,
private_unbound_response_t *this)
{
return this->rr_set;
}
METHOD(resolver_response_t, destroy, void,
private_unbound_response_t *this)
{
free(this->query_name);
free(this->canon_name);
DESTROY_IF(this->rr_set);
free(this);
}
/*
* Described in header.
*/
unbound_response_t *unbound_response_create_frm_libub_response(
struct ub_result *libub_response)
{
private_unbound_response_t *this = NULL;
INIT(this,
.public = {
.interface = {
.get_query_name = _get_query_name,
.get_canon_name = _get_canon_name,
.has_data = _has_data,
.query_name_exist = _query_name_exist,
.get_security_state = _get_security_state,
.get_rr_set = _get_rr_set,
.destroy = _destroy,
},
},
);
this->query_name = strdup(libub_response->qname);
if (libub_response->canonname)
{
this->canon_name = strdup(libub_response->canonname);
}
this->has_data = libub_response->havedata;
this->query_name_exist = !(libub_response->nxdomain);
if (libub_response->secure)
{
this->security_state = SECURE;
}
else if (libub_response->bogus)
{
this->security_state = BOGUS;
}
else
{
this->security_state = INDETERMINATE;
}
/**
* Create RRset
*/
if (this->query_name_exist && this->has_data)
{
ldns_pkt *dns_pkt = NULL;
ldns_rr_list *orig_rr_list = NULL;
size_t orig_rr_count;
ldns_rr *orig_rr = NULL;
ldns_rdf *orig_rdf = NULL;
ldns_status status;
linked_list_t *rr_list = NULL, *rrsig_list = NULL;
unbound_rr_t *rr = NULL;
int i;
/**Parse the received DNS packet using the ldns library */
status = ldns_wire2pkt(&dns_pkt, libub_response->answer_packet,
libub_response->answer_len);
if (status != LDNS_STATUS_OK)
{
DBG1(DBG_LIB, "failed to parse DNS packet");
destroy(this);
return NULL;
}
/* Create a list with the queried RRs. If there are corresponding RRSIGs
* create also a list with these.
*/
rr_list = linked_list_create();
orig_rr_list = ldns_pkt_answer(dns_pkt);
orig_rr_count = ldns_rr_list_rr_count(orig_rr_list);
for (i = 0; i < orig_rr_count; i++)
{
orig_rr = ldns_rr_list_rr(orig_rr_list, i);
if (ldns_rr_get_type(orig_rr) == libub_response->qtype &&
ldns_rr_get_class(orig_rr) == libub_response->qclass)
{
/* RR is part of the queried RRset.
* => add it to the list of Resource Records.
*/
rr = unbound_rr_create_frm_ldns_rr(orig_rr);
if (rr)
{
rr_list->insert_last(rr_list, rr);
}
else
{
DBG1(DBG_LIB, "failed to create RR");
}
}
if (ldns_rr_get_type(orig_rr) == LDNS_RR_TYPE_RRSIG)
{
orig_rdf = ldns_rr_rrsig_typecovered(orig_rr);
if (!orig_rdf)
{
DBG1(DBG_LIB, "failed to get the type covered by an RRSIG");
}
else if (ldns_rdf2native_int16(orig_rdf) == libub_response->qtype)
{
/* The current RR represent a signature (RRSIG)
* which belongs to the queried RRset.
* => add it to the list of signatures.
*/
rr = unbound_rr_create_frm_ldns_rr(orig_rr);
if (rr)
{
if (!rrsig_list)
{
rrsig_list = linked_list_create();
}
rrsig_list->insert_last(rrsig_list, rr);
}
else
{
DBG1(DBG_LIB, "failed to create RRSIG");
}
}
else
{
DBG1(DBG_LIB, "failed to determine the RR type "
"covered by RRSIG RR");
}
}
}
/**
* Create the RRset for which the query was performed.
*/
this->rr_set = rr_set_create(rr_list, rrsig_list);
ldns_pkt_free(dns_pkt);
}
return &this->public;
}