1999-07-24 01:17:44 +00:00
|
|
|
/*
|
|
|
|
* Copyright (C) 1999 Internet Software Consortium.
|
|
|
|
*
|
|
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
|
|
* copyright notice and this permission notice appear in all copies.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
|
|
|
|
* ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
|
|
|
|
* OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
|
|
|
|
* CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
|
|
|
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
|
|
|
|
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
|
|
|
|
* ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
|
|
|
|
* SOFTWARE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
#include <isc/assertions.h>
|
1999-07-28 02:20:36 +00:00
|
|
|
#include <isc/buffer.h>
|
1999-07-24 01:17:44 +00:00
|
|
|
#include <isc/mem.h>
|
|
|
|
#include <isc/mutex.h>
|
|
|
|
#include <isc/result.h>
|
|
|
|
#include <isc/task.h>
|
|
|
|
#include <isc/timer.h>
|
1999-07-28 02:20:36 +00:00
|
|
|
#include <isc/event.h>
|
1999-07-24 01:17:44 +00:00
|
|
|
|
1999-07-28 02:20:36 +00:00
|
|
|
#include <dns/db.h>
|
|
|
|
#include <dns/dbtable.h>
|
1999-07-24 01:17:44 +00:00
|
|
|
#include <dns/dispatch.h>
|
|
|
|
#include <dns/events.h>
|
1999-08-12 07:54:08 +00:00
|
|
|
#include <dns/fixedname.h>
|
1999-07-28 02:20:36 +00:00
|
|
|
#include <dns/message.h>
|
|
|
|
#include <dns/name.h>
|
|
|
|
#include <dns/rdata.h>
|
|
|
|
#include <dns/rdataset.h>
|
|
|
|
#include <dns/rdatasetiter.h>
|
1999-08-05 22:14:43 +00:00
|
|
|
#include <dns/view.h>
|
1999-07-24 01:17:44 +00:00
|
|
|
|
|
|
|
#include <named/client.h>
|
1999-07-28 02:20:36 +00:00
|
|
|
#include <named/globals.h>
|
|
|
|
#include <named/query.h>
|
1999-08-20 06:04:28 +00:00
|
|
|
#include <named/xfrout.h>
|
1999-07-24 01:17:44 +00:00
|
|
|
|
|
|
|
#include "../../isc/util.h" /* XXX */
|
|
|
|
|
1999-08-05 01:51:32 +00:00
|
|
|
#define PARTIALANSWER(c) (((c)->query.attributes & \
|
|
|
|
NS_QUERYATTR_PARTIALANSWER) != 0)
|
|
|
|
|
1999-07-29 00:55:35 +00:00
|
|
|
static inline void
|
1999-08-03 01:21:00 +00:00
|
|
|
query_reset(ns_client_t *client, isc_boolean_t everything) {
|
1999-09-02 02:10:44 +00:00
|
|
|
isc_buffer_t *dbuf, *dbuf_next;
|
1999-08-18 04:23:39 +00:00
|
|
|
ns_dbversion_t *dbversion, *dbversion_next;
|
|
|
|
unsigned int i;
|
|
|
|
|
1999-09-01 18:16:43 +00:00
|
|
|
/*
|
|
|
|
* Reset the query state of a client to its default state.
|
|
|
|
*/
|
1999-08-18 04:23:39 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Cleanup any active versions.
|
|
|
|
*/
|
|
|
|
for (dbversion = ISC_LIST_HEAD(client->query.activeversions);
|
|
|
|
dbversion != NULL;
|
|
|
|
dbversion = dbversion_next) {
|
|
|
|
dbversion_next = ISC_LIST_NEXT(dbversion, link);
|
|
|
|
dns_db_closeversion(dbversion->db, &dbversion->version,
|
|
|
|
ISC_FALSE);
|
|
|
|
dns_db_detach(&dbversion->db);
|
|
|
|
ISC_LIST_APPEND(client->query.freeversions, dbversion, link);
|
|
|
|
}
|
|
|
|
ISC_LIST_INIT(client->query.activeversions);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Clean up free versions.
|
|
|
|
*/
|
|
|
|
for (dbversion = ISC_LIST_HEAD(client->query.freeversions), i = 0;
|
|
|
|
dbversion != NULL;
|
|
|
|
dbversion = dbversion_next, i++) {
|
|
|
|
dbversion_next = ISC_LIST_NEXT(dbversion, link);
|
|
|
|
/*
|
|
|
|
* If we're not freeing everything, we keep the first three
|
|
|
|
* dbversions structures around.
|
|
|
|
*/
|
|
|
|
if (i > 3 || everything) {
|
|
|
|
ISC_LIST_UNLINK(client->query.freeversions, dbversion,
|
|
|
|
link);
|
|
|
|
isc_mem_put(client->mctx, dbversion,
|
|
|
|
sizeof *dbversion);
|
|
|
|
}
|
|
|
|
}
|
1999-07-29 00:55:35 +00:00
|
|
|
|
|
|
|
for (dbuf = ISC_LIST_HEAD(client->query.namebufs);
|
|
|
|
dbuf != NULL;
|
|
|
|
dbuf = dbuf_next) {
|
|
|
|
dbuf_next = ISC_LIST_NEXT(dbuf, link);
|
|
|
|
if (dbuf_next != NULL || everything) {
|
|
|
|
ISC_LIST_UNLINK(client->query.namebufs, dbuf, link);
|
1999-09-02 02:10:44 +00:00
|
|
|
isc_buffer_free(&dbuf);
|
1999-07-29 00:55:35 +00:00
|
|
|
}
|
|
|
|
}
|
1999-08-12 07:54:08 +00:00
|
|
|
/*
|
|
|
|
* We don't need to free items from these two lists because they
|
|
|
|
* will be taken care of when the message is reset.
|
|
|
|
*/
|
|
|
|
ISC_LIST_INIT(client->query.tmpnames);
|
|
|
|
ISC_LIST_INIT(client->query.tmprdatasets);
|
1999-08-03 01:21:00 +00:00
|
|
|
client->query.attributes = (NS_QUERYATTR_RECURSIONOK|
|
|
|
|
NS_QUERYATTR_CACHEOK);
|
|
|
|
client->query.qname = NULL;
|
1999-08-12 07:54:08 +00:00
|
|
|
client->query.origqname = NULL;
|
1999-08-03 01:21:00 +00:00
|
|
|
client->query.dboptions = 0;
|
1999-07-29 00:55:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
query_next(ns_client_t *client, isc_result_t result) {
|
1999-08-03 01:21:00 +00:00
|
|
|
(void)result;
|
|
|
|
query_reset(client, ISC_FALSE);
|
1999-07-29 00:55:35 +00:00
|
|
|
}
|
|
|
|
|
1999-08-03 01:21:00 +00:00
|
|
|
void
|
|
|
|
ns_query_free(ns_client_t *client) {
|
|
|
|
query_reset(client, ISC_TRUE);
|
1999-07-29 00:55:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline isc_result_t
|
|
|
|
query_newnamebuf(ns_client_t *client) {
|
1999-09-02 02:10:44 +00:00
|
|
|
isc_buffer_t *dbuf;
|
1999-07-29 00:55:35 +00:00
|
|
|
isc_result_t result;
|
|
|
|
|
1999-09-01 18:16:43 +00:00
|
|
|
/*
|
|
|
|
* Allocate a name buffer.
|
|
|
|
*/
|
|
|
|
|
1999-07-29 00:55:35 +00:00
|
|
|
dbuf = NULL;
|
1999-09-02 02:10:44 +00:00
|
|
|
result = isc_buffer_allocate(client->mctx, &dbuf, 1024,
|
|
|
|
ISC_BUFFERTYPE_BINARY);
|
1999-07-29 00:55:35 +00:00
|
|
|
if (result != ISC_R_SUCCESS)
|
|
|
|
return (result);
|
|
|
|
ISC_LIST_APPEND(client->query.namebufs, dbuf, link);
|
|
|
|
|
|
|
|
return (ISC_R_SUCCESS);
|
|
|
|
}
|
|
|
|
|
1999-09-02 02:10:44 +00:00
|
|
|
static inline isc_buffer_t *
|
1999-07-29 00:55:35 +00:00
|
|
|
query_getnamebuf(ns_client_t *client) {
|
1999-09-02 02:10:44 +00:00
|
|
|
isc_buffer_t *dbuf;
|
1999-07-29 00:55:35 +00:00
|
|
|
isc_result_t result;
|
|
|
|
isc_region_t r;
|
|
|
|
|
1999-09-01 18:16:43 +00:00
|
|
|
/*
|
|
|
|
* Return a name buffer with space for a maximal name, allocating
|
|
|
|
* a new one if necessary.
|
|
|
|
*/
|
|
|
|
|
1999-07-29 00:55:35 +00:00
|
|
|
if (ISC_LIST_EMPTY(client->query.namebufs)) {
|
|
|
|
result = query_newnamebuf(client);
|
|
|
|
if (result != ISC_R_SUCCESS)
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
dbuf = ISC_LIST_TAIL(client->query.namebufs);
|
|
|
|
INSIST(dbuf != NULL);
|
1999-09-02 02:10:44 +00:00
|
|
|
isc_buffer_available(dbuf, &r);
|
1999-07-29 00:55:35 +00:00
|
|
|
if (r.length < 255) {
|
|
|
|
result = query_newnamebuf(client);
|
|
|
|
if (result != ISC_R_SUCCESS)
|
|
|
|
return (NULL);
|
|
|
|
dbuf = ISC_LIST_TAIL(client->query.namebufs);
|
1999-09-02 02:10:44 +00:00
|
|
|
isc_buffer_available(dbuf, &r);
|
1999-07-29 00:55:35 +00:00
|
|
|
INSIST(r.length >= 255);
|
|
|
|
}
|
|
|
|
return (dbuf);
|
|
|
|
}
|
|
|
|
|
1999-08-12 07:54:08 +00:00
|
|
|
static inline void
|
1999-09-02 02:10:44 +00:00
|
|
|
query_keepname(ns_client_t *client, dns_name_t *name, isc_buffer_t *dbuf) {
|
1999-08-12 07:54:08 +00:00
|
|
|
isc_region_t r;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 'name' is using space in 'dbuf', but 'dbuf' has not yet been
|
|
|
|
* adjusted to take account of that. We do the adjustment.
|
|
|
|
*/
|
|
|
|
|
|
|
|
REQUIRE((client->query.attributes & NS_QUERYATTR_NAMEBUFUSED) != 0);
|
|
|
|
|
|
|
|
dns_name_toregion(name, &r);
|
1999-09-02 02:10:44 +00:00
|
|
|
isc_buffer_add(dbuf, r.length);
|
1999-08-12 07:54:08 +00:00
|
|
|
dns_name_setbuffer(name, NULL);
|
|
|
|
client->query.attributes &= ~NS_QUERYATTR_NAMEBUFUSED;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
query_releasename(ns_client_t *client, dns_name_t **namep) {
|
|
|
|
dns_name_t *name = *namep;
|
|
|
|
|
1999-09-01 18:16:43 +00:00
|
|
|
/*
|
|
|
|
* 'name' is no longer needed. Return it to our pool of temporary
|
|
|
|
* names. If it is using a name buffer, relinquish its exclusive
|
|
|
|
* rights on the buffer.
|
|
|
|
*/
|
|
|
|
|
1999-08-12 07:54:08 +00:00
|
|
|
ISC_LIST_APPEND(client->query.tmpnames, name, link);
|
|
|
|
if (dns_name_hasbuffer(name)) {
|
|
|
|
INSIST((client->query.attributes & NS_QUERYATTR_NAMEBUFUSED)
|
|
|
|
!= 0);
|
|
|
|
client->query.attributes &= ~NS_QUERYATTR_NAMEBUFUSED;
|
|
|
|
}
|
|
|
|
*namep = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline dns_name_t *
|
1999-09-02 02:10:44 +00:00
|
|
|
query_newname(ns_client_t *client, isc_buffer_t *dbuf,
|
1999-08-12 07:54:08 +00:00
|
|
|
isc_buffer_t *nbuf)
|
|
|
|
{
|
|
|
|
dns_name_t *name;
|
|
|
|
isc_region_t r;
|
|
|
|
isc_result_t result;
|
|
|
|
|
|
|
|
REQUIRE((client->query.attributes & NS_QUERYATTR_NAMEBUFUSED) == 0);
|
|
|
|
|
|
|
|
name = ISC_LIST_HEAD(client->query.tmpnames);
|
|
|
|
if (name == NULL) {
|
|
|
|
result = dns_message_gettempname(client->message, &name);
|
|
|
|
if (result != ISC_R_SUCCESS)
|
|
|
|
return (NULL);
|
|
|
|
} else
|
|
|
|
ISC_LIST_UNLINK(client->query.tmpnames, name, link);
|
1999-09-02 02:10:44 +00:00
|
|
|
isc_buffer_available(dbuf, &r);
|
1999-08-12 07:54:08 +00:00
|
|
|
isc_buffer_init(nbuf, r.base, r.length, ISC_BUFFERTYPE_BINARY);
|
|
|
|
dns_name_init(name, NULL);
|
|
|
|
dns_name_setbuffer(name, nbuf);
|
|
|
|
client->query.attributes |= NS_QUERYATTR_NAMEBUFUSED;
|
|
|
|
|
|
|
|
return (name);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline dns_rdataset_t *
|
|
|
|
query_newrdataset(ns_client_t *client) {
|
|
|
|
dns_rdataset_t *rdataset;
|
|
|
|
isc_result_t result;
|
1999-09-01 18:16:43 +00:00
|
|
|
|
1999-08-12 07:54:08 +00:00
|
|
|
rdataset = ISC_LIST_HEAD(client->query.tmprdatasets);
|
|
|
|
if (rdataset == NULL) {
|
|
|
|
result = dns_message_gettemprdataset(client->message,
|
|
|
|
&rdataset);
|
|
|
|
if (result != ISC_R_SUCCESS)
|
|
|
|
return (NULL);
|
|
|
|
} else
|
|
|
|
ISC_LIST_UNLINK(client->query.tmprdatasets, rdataset, link);
|
|
|
|
dns_rdataset_init(rdataset);
|
|
|
|
|
|
|
|
return (rdataset);
|
|
|
|
}
|
|
|
|
|
1999-08-18 04:23:39 +00:00
|
|
|
static inline isc_result_t
|
|
|
|
query_newdbversion(ns_client_t *client, unsigned int n) {
|
|
|
|
unsigned int i;
|
|
|
|
ns_dbversion_t *dbversion;
|
|
|
|
|
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
dbversion = isc_mem_get(client->mctx, sizeof *dbversion);
|
|
|
|
if (dbversion != NULL) {
|
|
|
|
dbversion->db = NULL;
|
|
|
|
dbversion->version = NULL;
|
|
|
|
ISC_LIST_APPEND(client->query.freeversions, dbversion,
|
|
|
|
link);
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* We only return ISC_R_NOMEMORY if we couldn't
|
|
|
|
* allocate anything.
|
|
|
|
*/
|
|
|
|
if (i == 0)
|
|
|
|
return (ISC_R_NOMEMORY);
|
|
|
|
else
|
|
|
|
return (ISC_R_SUCCESS);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return (ISC_R_SUCCESS);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline ns_dbversion_t *
|
|
|
|
query_getdbversion(ns_client_t *client) {
|
|
|
|
isc_result_t result;
|
|
|
|
ns_dbversion_t *dbversion;
|
|
|
|
|
|
|
|
if (ISC_LIST_EMPTY(client->query.freeversions)) {
|
|
|
|
result = query_newdbversion(client, 1);
|
|
|
|
if (result != ISC_R_SUCCESS)
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
dbversion = ISC_LIST_HEAD(client->query.freeversions);
|
|
|
|
INSIST(dbversion != NULL);
|
|
|
|
ISC_LIST_UNLINK(client->query.freeversions, dbversion, link);
|
|
|
|
|
|
|
|
return (dbversion);
|
|
|
|
}
|
|
|
|
|
1999-07-29 00:55:35 +00:00
|
|
|
isc_result_t
|
|
|
|
ns_query_init(ns_client_t *client) {
|
1999-08-18 04:23:39 +00:00
|
|
|
isc_result_t result;
|
|
|
|
|
1999-07-29 00:55:35 +00:00
|
|
|
ISC_LIST_INIT(client->query.namebufs);
|
1999-08-18 04:23:39 +00:00
|
|
|
ISC_LIST_INIT(client->query.activeversions);
|
|
|
|
ISC_LIST_INIT(client->query.freeversions);
|
1999-08-03 01:21:00 +00:00
|
|
|
query_reset(client, ISC_FALSE);
|
1999-08-18 04:23:39 +00:00
|
|
|
result = query_newdbversion(client, 3);
|
|
|
|
if (result != ISC_R_SUCCESS)
|
|
|
|
return (result);
|
1999-07-29 00:55:35 +00:00
|
|
|
return (query_newnamebuf(client));
|
|
|
|
}
|
|
|
|
|
1999-08-18 04:23:39 +00:00
|
|
|
static inline dns_dbversion_t *
|
|
|
|
query_findversion(ns_client_t *client, dns_db_t *db) {
|
|
|
|
ns_dbversion_t *dbversion;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We may already have done a query related to this
|
|
|
|
* database. If so, we must be sure to make subsequent
|
|
|
|
* queries from the same version.
|
|
|
|
*/
|
|
|
|
for (dbversion = ISC_LIST_HEAD(client->query.activeversions);
|
|
|
|
dbversion != NULL;
|
|
|
|
dbversion = ISC_LIST_NEXT(dbversion, link)) {
|
|
|
|
if (dbversion->db == db)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (dbversion == NULL) {
|
|
|
|
/*
|
|
|
|
* This is a new zone for this query. Add it to
|
|
|
|
* the active list.
|
|
|
|
*/
|
|
|
|
dbversion = query_getdbversion(client);
|
|
|
|
if (dbversion == NULL)
|
|
|
|
return (NULL);
|
|
|
|
dns_db_attach(db, &dbversion->db);
|
|
|
|
dns_db_currentversion(db, &dbversion->version);
|
|
|
|
ISC_LIST_APPEND(client->query.activeversions,
|
|
|
|
dbversion, link);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (dbversion->version);
|
|
|
|
}
|
|
|
|
|
1999-08-03 01:21:00 +00:00
|
|
|
static isc_result_t
|
|
|
|
query_addadditional(void *arg, dns_name_t *name, dns_rdatatype_t type) {
|
|
|
|
ns_client_t *client = arg;
|
1999-08-12 07:54:08 +00:00
|
|
|
isc_result_t result, eresult;
|
1999-08-03 01:21:00 +00:00
|
|
|
dns_dbnode_t *node;
|
|
|
|
dns_db_t *db;
|
|
|
|
dns_name_t *fname, *mname;
|
1999-09-01 18:16:43 +00:00
|
|
|
dns_rdataset_t *rdataset, *sigrdataset;
|
1999-08-03 01:21:00 +00:00
|
|
|
dns_section_t section;
|
1999-09-02 02:10:44 +00:00
|
|
|
isc_buffer_t *dbuf;
|
1999-08-03 01:21:00 +00:00
|
|
|
isc_buffer_t b;
|
1999-08-18 04:23:39 +00:00
|
|
|
dns_dbversion_t *version;
|
1999-08-03 01:21:00 +00:00
|
|
|
|
|
|
|
REQUIRE(NS_CLIENT_VALID(client));
|
|
|
|
REQUIRE(type != dns_rdatatype_any);
|
|
|
|
/* XXXRTH Other requirements. */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* XXXRTH Should special case 'A' type. If type is 'A', we should
|
|
|
|
* look for A6 and AAAA too.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
1999-08-12 07:54:08 +00:00
|
|
|
* Initialization.
|
1999-08-03 01:21:00 +00:00
|
|
|
*/
|
1999-08-12 07:54:08 +00:00
|
|
|
eresult = ISC_R_SUCCESS;
|
1999-08-03 01:21:00 +00:00
|
|
|
fname = NULL;
|
|
|
|
rdataset = NULL;
|
1999-09-01 18:16:43 +00:00
|
|
|
sigrdataset = NULL;
|
1999-08-12 07:54:08 +00:00
|
|
|
db = NULL;
|
1999-08-18 04:23:39 +00:00
|
|
|
version = NULL;
|
1999-08-12 07:54:08 +00:00
|
|
|
node = NULL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get some resources...
|
|
|
|
*/
|
|
|
|
dbuf = query_getnamebuf(client);
|
|
|
|
if (dbuf == NULL)
|
|
|
|
goto cleanup;
|
|
|
|
fname = query_newname(client, dbuf, &b);
|
|
|
|
rdataset = query_newrdataset(client);
|
1999-09-01 18:16:43 +00:00
|
|
|
sigrdataset = query_newrdataset(client);
|
|
|
|
if (fname == NULL || rdataset == NULL || sigrdataset == NULL)
|
1999-08-12 07:54:08 +00:00
|
|
|
goto cleanup;
|
1999-08-03 01:21:00 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Find a database to answer the query.
|
|
|
|
*/
|
1999-08-05 22:14:43 +00:00
|
|
|
result = dns_dbtable_find(client->view->dbtable, name, &db);
|
1999-08-03 01:21:00 +00:00
|
|
|
if (result != ISC_R_SUCCESS && result != DNS_R_PARTIALMATCH)
|
1999-08-12 07:54:08 +00:00
|
|
|
goto cleanup;
|
1999-08-03 01:21:00 +00:00
|
|
|
|
1999-08-18 04:23:39 +00:00
|
|
|
/*
|
|
|
|
* Get the current version of this database.
|
|
|
|
*/
|
|
|
|
if (dns_db_iszone(db)) {
|
|
|
|
version = query_findversion(client, db);
|
|
|
|
if (version == NULL)
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
1999-08-03 01:21:00 +00:00
|
|
|
/*
|
|
|
|
* Now look for an answer in the database.
|
|
|
|
*/
|
|
|
|
node = NULL;
|
1999-08-18 04:23:39 +00:00
|
|
|
result = dns_db_find(db, name, version, type, client->query.dboptions,
|
1999-08-31 22:14:06 +00:00
|
|
|
client->requesttime, &node, fname, rdataset,
|
1999-09-01 18:16:43 +00:00
|
|
|
sigrdataset);
|
1999-08-03 01:21:00 +00:00
|
|
|
switch (result) {
|
|
|
|
case DNS_R_SUCCESS:
|
|
|
|
case DNS_R_GLUE:
|
|
|
|
break;
|
|
|
|
default:
|
1999-08-12 07:54:08 +00:00
|
|
|
goto cleanup;
|
1999-08-03 01:21:00 +00:00
|
|
|
}
|
|
|
|
|
1999-08-12 07:54:08 +00:00
|
|
|
query_keepname(client, fname, dbuf);
|
1999-08-03 01:21:00 +00:00
|
|
|
|
1999-08-03 20:56:08 +00:00
|
|
|
/*
|
|
|
|
* Suppress duplicates.
|
|
|
|
*/
|
|
|
|
for (section = DNS_SECTION_ANSWER;
|
|
|
|
section <= DNS_SECTION_ADDITIONAL;
|
|
|
|
section++) {
|
1999-08-03 01:21:00 +00:00
|
|
|
mname = NULL;
|
|
|
|
result = dns_message_findname(client->message, section,
|
1999-09-01 18:16:43 +00:00
|
|
|
fname, type, 0, &mname, NULL);
|
1999-08-03 01:21:00 +00:00
|
|
|
if (result == ISC_R_SUCCESS) {
|
|
|
|
/*
|
|
|
|
* We've already got this RRset in the response.
|
|
|
|
*/
|
1999-08-12 07:54:08 +00:00
|
|
|
goto cleanup;
|
1999-08-03 01:21:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
ISC_LIST_APPEND(fname->list, rdataset, link);
|
1999-08-12 07:54:08 +00:00
|
|
|
rdataset = NULL;
|
1999-09-01 18:16:43 +00:00
|
|
|
/*
|
|
|
|
* Note: we only add SIGs if we've added the type they cover, so
|
|
|
|
* we do not need to check if the SIG rdataset is already in the
|
|
|
|
* response.
|
|
|
|
*/
|
|
|
|
if (sigrdataset->methods != NULL) {
|
|
|
|
ISC_LIST_APPEND(fname->list, sigrdataset, link);
|
|
|
|
sigrdataset = NULL;
|
|
|
|
}
|
1999-08-03 01:21:00 +00:00
|
|
|
|
|
|
|
dns_message_addname(client->message, fname, DNS_SECTION_ADDITIONAL);
|
1999-08-12 07:54:08 +00:00
|
|
|
fname = NULL;
|
1999-08-03 01:21:00 +00:00
|
|
|
|
1999-08-03 20:56:08 +00:00
|
|
|
/*
|
|
|
|
* In a few cases, we want to add additional data for additional
|
|
|
|
* data. It's simpler to just deal with special cases here than
|
|
|
|
* to try to create a general purpose mechanism and allow the
|
|
|
|
* rdata implementations to do it themselves.
|
|
|
|
*
|
|
|
|
* This involves recursion, but the depth is limited. The
|
|
|
|
* most complex case is adding a SRV rdataset, which involves
|
|
|
|
* recursing to add address records, which in turn can cause
|
|
|
|
* recursion to add KEYs.
|
|
|
|
*/
|
|
|
|
if (type == dns_rdatatype_a || type == dns_rdatatype_aaaa ||
|
|
|
|
type == dns_rdatatype_a6) {
|
|
|
|
/*
|
|
|
|
* RFC 2535 section 3.5 says that when A or AAAA records are
|
|
|
|
* retrieved as additional data, any KEY RRs for the owner name
|
|
|
|
* should be added to the additional data section. We include
|
|
|
|
* A6 in the list of types with such treatment.
|
|
|
|
*
|
|
|
|
* XXXRTH We should lower the priority here. Alternatively,
|
|
|
|
* we could raise the priority of glue records.
|
|
|
|
*/
|
1999-08-12 07:54:08 +00:00
|
|
|
eresult = query_addadditional(client, name, dns_rdatatype_key);
|
1999-08-03 20:56:08 +00:00
|
|
|
} else if (type == dns_rdatatype_srv) {
|
|
|
|
/*
|
|
|
|
* If we're adding SRV records to the additional data
|
|
|
|
* section, it's helpful if we add the SRV additional data
|
|
|
|
* as well.
|
|
|
|
*/
|
1999-08-12 07:54:08 +00:00
|
|
|
eresult = dns_rdataset_additionaldata(rdataset,
|
|
|
|
query_addadditional,
|
|
|
|
client);
|
1999-08-03 20:56:08 +00:00
|
|
|
}
|
|
|
|
|
1999-08-12 07:54:08 +00:00
|
|
|
cleanup:
|
1999-09-01 18:16:43 +00:00
|
|
|
if (sigrdataset != NULL) {
|
|
|
|
if (sigrdataset->methods != NULL)
|
|
|
|
dns_rdataset_disassociate(sigrdataset);
|
|
|
|
ISC_LIST_APPEND(client->query.tmprdatasets, sigrdataset, link);
|
|
|
|
}
|
1999-08-12 07:54:08 +00:00
|
|
|
if (rdataset != NULL) {
|
|
|
|
if (rdataset->methods != NULL)
|
|
|
|
dns_rdataset_disassociate(rdataset);
|
|
|
|
ISC_LIST_APPEND(client->query.tmprdatasets, rdataset, link);
|
|
|
|
}
|
|
|
|
if (fname != NULL)
|
|
|
|
query_releasename(client, &fname);
|
|
|
|
if (node != NULL)
|
|
|
|
dns_db_detachnode(db, &node);
|
|
|
|
if (db != NULL)
|
|
|
|
dns_db_detach(&db);
|
|
|
|
|
|
|
|
return (eresult);
|
1999-08-03 01:21:00 +00:00
|
|
|
}
|
|
|
|
|
1999-08-03 20:56:08 +00:00
|
|
|
static inline void
|
|
|
|
query_addrdataset(ns_client_t *client, dns_name_t *fname,
|
|
|
|
dns_rdataset_t *rdataset)
|
|
|
|
{
|
|
|
|
dns_rdatatype_t type = rdataset->type;
|
|
|
|
|
|
|
|
ISC_LIST_APPEND(fname->list, rdataset, link);
|
|
|
|
/*
|
|
|
|
* We don't care if dns_rdataset_additionaldata() fails.
|
|
|
|
*/
|
|
|
|
(void)dns_rdataset_additionaldata(rdataset, query_addadditional,
|
|
|
|
client);
|
|
|
|
/*
|
|
|
|
* RFC 2535 section 3.5 says that when NS, SOA, A, or AAAA records
|
|
|
|
* are retrieved, any KEY RRs for the owner name should be added
|
|
|
|
* to the additional data section. We include A6 in the list of
|
|
|
|
* types with such treatment.
|
|
|
|
*
|
|
|
|
* We don't care if query_additional() fails.
|
|
|
|
*/
|
|
|
|
if (type == dns_rdatatype_ns || type == dns_rdatatype_soa ||
|
|
|
|
type == dns_rdatatype_a || type == dns_rdatatype_aaaa ||
|
|
|
|
type == dns_rdatatype_a6) {
|
|
|
|
/*
|
|
|
|
* XXXRTH We should lower the priority here. Alternatively,
|
|
|
|
* we could raise the priority of glue records.
|
|
|
|
*/
|
|
|
|
(void)query_addadditional(client, fname, dns_rdatatype_key);
|
|
|
|
}
|
|
|
|
}
|
1999-08-05 01:51:32 +00:00
|
|
|
|
1999-08-12 07:54:08 +00:00
|
|
|
#define ANSWERED(rds) (((rds)->attributes & DNS_RDATASETATTR_ANSWERED) != 0)
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
query_addrrset(ns_client_t *client, dns_name_t **namep,
|
1999-08-31 22:14:06 +00:00
|
|
|
dns_rdataset_t **rdatasetp, dns_rdataset_t **sigrdatasetp,
|
1999-09-02 02:10:44 +00:00
|
|
|
isc_buffer_t *dbuf, dns_section_t section)
|
1999-08-12 07:54:08 +00:00
|
|
|
{
|
|
|
|
dns_name_t *name, *mname;
|
1999-08-31 22:14:06 +00:00
|
|
|
dns_rdataset_t *rdataset, *mrdataset, *sigrdataset;
|
1999-08-12 07:54:08 +00:00
|
|
|
isc_result_t result;
|
|
|
|
|
|
|
|
name = *namep;
|
|
|
|
rdataset = *rdatasetp;
|
1999-08-31 22:14:06 +00:00
|
|
|
if (sigrdatasetp != NULL)
|
|
|
|
sigrdataset = *sigrdatasetp;
|
|
|
|
else
|
|
|
|
sigrdataset = NULL;
|
1999-08-12 07:54:08 +00:00
|
|
|
mname = NULL;
|
|
|
|
mrdataset = NULL;
|
|
|
|
result = dns_message_findname(client->message, section,
|
1999-08-31 22:14:06 +00:00
|
|
|
name, rdataset->type, rdataset->covers,
|
1999-08-12 07:54:08 +00:00
|
|
|
&mname, &mrdataset);
|
|
|
|
if (result == ISC_R_SUCCESS) {
|
|
|
|
/*
|
|
|
|
* We've already got an RRset of the given name and type.
|
|
|
|
* There's nothing else to do;
|
|
|
|
*/
|
|
|
|
return;
|
1999-08-19 20:46:59 +00:00
|
|
|
} else if (result == DNS_R_NXDOMAIN) {
|
1999-08-12 07:54:08 +00:00
|
|
|
/*
|
|
|
|
* The name doesn't exist.
|
|
|
|
*/
|
|
|
|
if (dbuf != NULL)
|
|
|
|
query_keepname(client, name, dbuf);
|
|
|
|
dns_message_addname(client->message, name, section);
|
|
|
|
*namep = NULL;
|
|
|
|
mname = name;
|
|
|
|
} else
|
|
|
|
RUNTIME_CHECK(result == DNS_R_NXRDATASET);
|
|
|
|
|
1999-08-31 22:14:06 +00:00
|
|
|
/*
|
|
|
|
* Note: we only add SIGs if we've added the type they cover, so
|
|
|
|
* we do not need to check if the SIG rdataset is already in the
|
|
|
|
* response.
|
|
|
|
*/
|
1999-08-12 07:54:08 +00:00
|
|
|
query_addrdataset(client, mname, rdataset);
|
|
|
|
*rdatasetp = NULL;
|
1999-08-31 22:14:06 +00:00
|
|
|
if (sigrdataset != NULL && sigrdataset->methods != NULL) {
|
|
|
|
/*
|
|
|
|
* We have a signature. Add it to the response.
|
|
|
|
*/
|
|
|
|
query_addrdataset(client, mname, sigrdataset);
|
|
|
|
*sigrdatasetp = NULL;
|
|
|
|
}
|
1999-08-12 07:54:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline isc_result_t
|
|
|
|
query_addsoa(ns_client_t *client, dns_db_t *db) {
|
|
|
|
dns_name_t *name, *fname;
|
|
|
|
dns_dbnode_t *node;
|
|
|
|
isc_result_t result, eresult;
|
|
|
|
dns_fixedname_t foundname;
|
1999-08-31 22:14:06 +00:00
|
|
|
dns_rdataset_t *rdataset, *sigrdataset;
|
1999-08-05 01:51:32 +00:00
|
|
|
|
|
|
|
/*
|
1999-08-12 07:54:08 +00:00
|
|
|
* Initialization.
|
1999-08-05 01:51:32 +00:00
|
|
|
*/
|
1999-08-12 07:54:08 +00:00
|
|
|
eresult = ISC_R_SUCCESS;
|
|
|
|
name = NULL;
|
|
|
|
rdataset = NULL;
|
|
|
|
node = NULL;
|
|
|
|
dns_fixedname_init(&foundname);
|
|
|
|
fname = dns_fixedname_name(&foundname);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get resources and make 'name' be the database origin.
|
|
|
|
*/
|
|
|
|
result = dns_message_gettempname(client->message, &name);
|
|
|
|
if (result != ISC_R_SUCCESS)
|
|
|
|
return (result);
|
|
|
|
dns_name_init(name, NULL);
|
|
|
|
dns_name_clone(dns_db_origin(db), name);
|
|
|
|
rdataset = query_newrdataset(client);
|
1999-08-31 22:14:06 +00:00
|
|
|
sigrdataset = query_newrdataset(client);
|
|
|
|
if (rdataset == NULL || sigrdataset == NULL) {
|
1999-08-12 07:54:08 +00:00
|
|
|
eresult = DNS_R_SERVFAIL;
|
|
|
|
goto cleanup;
|
1999-08-05 01:51:32 +00:00
|
|
|
}
|
1999-08-12 07:54:08 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Find the SOA.
|
|
|
|
*/
|
|
|
|
result = dns_db_find(db, name, NULL, dns_rdatatype_soa, 0, 0, &node,
|
1999-08-31 22:14:06 +00:00
|
|
|
fname, rdataset, sigrdataset);
|
1999-08-12 07:54:08 +00:00
|
|
|
if (result != ISC_R_SUCCESS) {
|
|
|
|
/*
|
|
|
|
* This is bad. We tried to get the SOA RR at the zone top
|
|
|
|
* and it didn't work!
|
|
|
|
*/
|
|
|
|
eresult = DNS_R_SERVFAIL;
|
1999-08-31 22:14:06 +00:00
|
|
|
} else {
|
|
|
|
query_addrrset(client, &name, &rdataset, &sigrdataset, NULL,
|
1999-08-12 07:54:08 +00:00
|
|
|
DNS_SECTION_AUTHORITY);
|
1999-08-31 22:14:06 +00:00
|
|
|
}
|
1999-08-12 07:54:08 +00:00
|
|
|
|
|
|
|
cleanup:
|
1999-08-31 22:14:06 +00:00
|
|
|
if (sigrdataset != NULL) {
|
|
|
|
if (sigrdataset->methods != NULL)
|
|
|
|
dns_rdataset_disassociate(sigrdataset);
|
|
|
|
ISC_LIST_APPEND(client->query.tmprdatasets, sigrdataset, link);
|
|
|
|
}
|
1999-08-12 07:54:08 +00:00
|
|
|
if (rdataset != NULL) {
|
|
|
|
if (rdataset->methods != NULL)
|
|
|
|
dns_rdataset_disassociate(rdataset);
|
|
|
|
ISC_LIST_APPEND(client->query.tmprdatasets, rdataset, link);
|
|
|
|
}
|
|
|
|
if (name != NULL)
|
|
|
|
query_releasename(client, &name);
|
|
|
|
if (node != NULL)
|
|
|
|
dns_db_detachnode(db, &node);
|
|
|
|
|
|
|
|
return (eresult);
|
1999-08-05 01:51:32 +00:00
|
|
|
}
|
1999-08-03 20:56:08 +00:00
|
|
|
|
1999-08-12 07:54:08 +00:00
|
|
|
static inline isc_result_t
|
|
|
|
query_checktype(dns_rdatatype_t type) {
|
|
|
|
|
|
|
|
/*
|
1999-08-31 22:14:06 +00:00
|
|
|
* XXXRTH OPT still needs to be added.
|
1999-08-12 07:54:08 +00:00
|
|
|
* Should get help with this from rdata.c
|
|
|
|
*/
|
|
|
|
switch (type) {
|
|
|
|
case dns_rdatatype_tkey:
|
|
|
|
return (DNS_R_NOTIMP);
|
|
|
|
case dns_rdatatype_tsig:
|
|
|
|
return (DNS_R_FORMERR);
|
|
|
|
case dns_rdatatype_ixfr:
|
|
|
|
case dns_rdatatype_axfr:
|
|
|
|
case dns_rdatatype_mailb:
|
|
|
|
case dns_rdatatype_maila:
|
|
|
|
return (DNS_R_REFUSED);
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (ISC_R_SUCCESS);
|
|
|
|
}
|
|
|
|
|
1999-09-01 18:16:43 +00:00
|
|
|
static inline void
|
|
|
|
query_a6additional(ns_client_t *client, dns_db_t *db, dns_dbnode_t *node,
|
|
|
|
dns_dbversion_t *version, dns_name_t *name)
|
|
|
|
{
|
|
|
|
dns_name_t *fname, *tname;
|
|
|
|
dns_rdatatype_t type;
|
|
|
|
dns_rdataset_t *rdataset;
|
|
|
|
dns_rdatasetiter_t *rdsiter;
|
|
|
|
isc_result_t result;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Doing an A6 query causes type A and type AAAA additional section
|
|
|
|
* processing for QNAME, which we handle here.
|
|
|
|
*/
|
|
|
|
|
|
|
|
fname = NULL;
|
|
|
|
result = dns_message_gettempname(client->message, &fname);
|
|
|
|
if (result != ISC_R_SUCCESS)
|
|
|
|
return;
|
|
|
|
dns_name_init(fname, NULL);
|
|
|
|
dns_name_clone(name, fname);
|
|
|
|
rdsiter = NULL;
|
|
|
|
result = dns_db_allrdatasets(db, node, version, 0, &rdsiter);
|
|
|
|
if (result != ISC_R_SUCCESS)
|
|
|
|
return;
|
|
|
|
result = dns_rdatasetiter_first(rdsiter);
|
|
|
|
while (result == ISC_R_SUCCESS) {
|
|
|
|
rdataset = query_newrdataset(client);
|
|
|
|
if (rdataset == NULL)
|
|
|
|
break;
|
|
|
|
dns_rdatasetiter_current(rdsiter, rdataset);
|
|
|
|
type = rdataset->type;
|
|
|
|
if (type == dns_rdatatype_sig)
|
|
|
|
type = rdataset->covers;
|
|
|
|
if (type == dns_rdatatype_a || type == dns_rdatatype_aaaa) {
|
|
|
|
tname = fname;
|
|
|
|
query_addrrset(client, &tname, &rdataset, NULL,
|
|
|
|
NULL, DNS_SECTION_ADDITIONAL);
|
|
|
|
if (rdataset != NULL) {
|
|
|
|
/*
|
|
|
|
* We've already got this one.
|
|
|
|
*/
|
|
|
|
dns_rdataset_disassociate(rdataset);
|
|
|
|
ISC_LIST_APPEND(client->query.tmprdatasets,
|
|
|
|
rdataset, link);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* We're not interested in this rdataset.
|
|
|
|
*/
|
|
|
|
dns_rdataset_disassociate(rdataset);
|
|
|
|
}
|
|
|
|
result = dns_rdatasetiter_next(rdsiter);
|
|
|
|
}
|
|
|
|
dns_rdatasetiter_destroy(&rdsiter);
|
|
|
|
}
|
|
|
|
|
1999-08-12 07:54:08 +00:00
|
|
|
#define MAX_RESTARTS 16
|
|
|
|
|
|
|
|
#define QUERY_ERROR(r) \
|
|
|
|
do { \
|
|
|
|
eresult = r; \
|
|
|
|
want_restart = ISC_FALSE; \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
static void
|
|
|
|
query_find(ns_client_t *client) {
|
1999-07-24 01:17:44 +00:00
|
|
|
dns_db_t *db;
|
1999-08-12 07:54:08 +00:00
|
|
|
dns_dbnode_t *node;
|
|
|
|
dns_rdatatype_t qtype, type;
|
|
|
|
dns_name_t *fname, *tname, *prefix;
|
|
|
|
dns_rdataset_t *rdataset, *qrdataset, *trdataset;
|
1999-08-31 22:14:06 +00:00
|
|
|
dns_rdataset_t *sigrdataset;
|
1999-08-12 07:54:08 +00:00
|
|
|
dns_rdata_t rdata;
|
1999-07-24 01:17:44 +00:00
|
|
|
dns_rdatasetiter_t *rdsiter;
|
1999-08-12 07:54:08 +00:00
|
|
|
isc_boolean_t use_cache, recursion_ok, want_restart;
|
1999-09-01 18:16:43 +00:00
|
|
|
isc_boolean_t auth, is_zone, clear_fname;
|
1999-08-12 07:54:08 +00:00
|
|
|
unsigned int restarts, qcount, n, nlabels, nbits;
|
|
|
|
dns_namereln_t namereln;
|
|
|
|
int order;
|
1999-09-02 02:10:44 +00:00
|
|
|
isc_buffer_t *dbuf;
|
1999-07-28 02:20:36 +00:00
|
|
|
isc_region_t r;
|
|
|
|
isc_buffer_t b;
|
1999-08-12 07:54:08 +00:00
|
|
|
isc_result_t result, eresult;
|
|
|
|
dns_fixedname_t fixed;
|
1999-08-18 04:23:39 +00:00
|
|
|
dns_dbversion_t *version;
|
1999-09-01 18:16:43 +00:00
|
|
|
unsigned int dboptions;
|
1999-07-24 01:17:44 +00:00
|
|
|
|
1999-08-12 07:54:08 +00:00
|
|
|
/*
|
|
|
|
* One-time initialization.
|
1999-08-05 01:51:32 +00:00
|
|
|
*
|
1999-08-12 07:54:08 +00:00
|
|
|
* It's especially important to initialize anything that the cleanup
|
|
|
|
* code might cleanup.
|
1999-08-05 01:51:32 +00:00
|
|
|
*/
|
|
|
|
|
1999-08-12 07:54:08 +00:00
|
|
|
eresult = ISC_R_SUCCESS;
|
|
|
|
restarts = 0;
|
|
|
|
use_cache = ISC_FALSE;
|
|
|
|
recursion_ok = ISC_FALSE;
|
|
|
|
fname = NULL;
|
|
|
|
rdataset = NULL;
|
1999-08-31 22:14:06 +00:00
|
|
|
sigrdataset = NULL;
|
1999-08-12 07:54:08 +00:00
|
|
|
node = NULL;
|
|
|
|
db = NULL;
|
1999-08-18 04:23:39 +00:00
|
|
|
version = NULL;
|
1999-08-12 07:54:08 +00:00
|
|
|
|
|
|
|
if (client->view->cachedb == NULL ||
|
|
|
|
client->view->resolver == NULL) {
|
|
|
|
use_cache = ISC_FALSE;
|
|
|
|
recursion_ok = ISC_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
restart:
|
|
|
|
want_restart = ISC_FALSE;
|
1999-07-28 02:20:36 +00:00
|
|
|
auth = ISC_FALSE;
|
1999-09-01 18:16:43 +00:00
|
|
|
clear_fname = ISC_FALSE;
|
1999-07-28 02:20:36 +00:00
|
|
|
|
1999-07-24 01:17:44 +00:00
|
|
|
/*
|
1999-08-12 07:54:08 +00:00
|
|
|
* First we must find the right database.
|
1999-07-24 01:17:44 +00:00
|
|
|
*/
|
1999-08-12 07:54:08 +00:00
|
|
|
result = dns_dbtable_find(client->view->dbtable,
|
|
|
|
client->query.qname, &db);
|
|
|
|
if (result == ISC_R_NOTFOUND) {
|
1999-07-28 02:20:36 +00:00
|
|
|
/*
|
1999-08-12 07:54:08 +00:00
|
|
|
* We're not directly authoritative for this query name, nor
|
|
|
|
* is it a subdomain of any zone for which we're
|
|
|
|
* authoritative.
|
1999-07-28 02:20:36 +00:00
|
|
|
*/
|
1999-08-12 07:54:08 +00:00
|
|
|
if (!use_cache) {
|
|
|
|
/*
|
|
|
|
* If we can't use the cache, either because we
|
|
|
|
* don't have one or because its use has been
|
|
|
|
* disallowed, there's no more progress we can make
|
|
|
|
* on this query.
|
|
|
|
*/
|
|
|
|
QUERY_ERROR(DNS_R_REFUSED);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
INSIST(client->view->cachedb != NULL);
|
|
|
|
dns_db_attach(client->view->cachedb, &db);
|
|
|
|
} else if (result != ISC_R_SUCCESS && result != DNS_R_PARTIALMATCH) {
|
|
|
|
/*
|
|
|
|
* Something is broken.
|
|
|
|
*/
|
|
|
|
QUERY_ERROR(DNS_R_SERVFAIL);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
is_zone = dns_db_iszone(db);
|
1999-08-18 04:23:39 +00:00
|
|
|
if (is_zone) {
|
1999-08-12 07:54:08 +00:00
|
|
|
auth = ISC_TRUE;
|
|
|
|
|
1999-08-18 04:23:39 +00:00
|
|
|
/*
|
|
|
|
* Get the current version of this database.
|
|
|
|
*/
|
|
|
|
version = query_findversion(client, db);
|
|
|
|
if (version == NULL) {
|
|
|
|
QUERY_ERROR(DNS_R_SERVFAIL);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1999-08-12 07:54:08 +00:00
|
|
|
/*
|
|
|
|
* Find the first unanswered type in the question section.
|
|
|
|
*/
|
|
|
|
qcount = 0;
|
|
|
|
qrdataset = NULL;
|
|
|
|
qtype = dns_rdatatype_null;
|
|
|
|
for (trdataset = ISC_LIST_HEAD(client->query.origqname->list);
|
|
|
|
trdataset != NULL;
|
|
|
|
trdataset = ISC_LIST_NEXT(trdataset, link)) {
|
|
|
|
if (!ANSWERED(trdataset)) {
|
|
|
|
if (qrdataset == NULL) {
|
|
|
|
qrdataset = trdataset;
|
|
|
|
qtype = trdataset->type;
|
|
|
|
}
|
|
|
|
qcount++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* We had better have found something!
|
|
|
|
*/
|
|
|
|
INSIST(qrdataset != NULL && qcount > 0);
|
|
|
|
/*
|
|
|
|
* If there's more than one question, we'll retrieve the node and
|
|
|
|
* iterate it, trying to find answers.
|
|
|
|
*/
|
|
|
|
if (qcount == 1)
|
|
|
|
type = qtype;
|
|
|
|
else {
|
|
|
|
type = dns_rdatatype_any;
|
|
|
|
/* XXXRTH */
|
|
|
|
QUERY_ERROR(DNS_R_NOTIMP);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* See if the type is OK.
|
|
|
|
*/
|
|
|
|
result = query_checktype(qtype);
|
|
|
|
if (result != ISC_R_SUCCESS) {
|
|
|
|
QUERY_ERROR(result);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
1999-08-31 22:14:06 +00:00
|
|
|
/*
|
|
|
|
* If it's a SIG query, we'll iterate the node.
|
|
|
|
*/
|
|
|
|
if (qtype == dns_rdatatype_sig)
|
|
|
|
type = dns_rdatatype_any;
|
|
|
|
|
1999-08-12 07:54:08 +00:00
|
|
|
/*
|
|
|
|
* We'll need some resources...
|
|
|
|
*/
|
|
|
|
dbuf = query_getnamebuf(client);
|
|
|
|
if (dbuf == NULL) {
|
|
|
|
QUERY_ERROR(DNS_R_SERVFAIL);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
fname = query_newname(client, dbuf, &b);
|
|
|
|
rdataset = query_newrdataset(client);
|
1999-08-31 22:14:06 +00:00
|
|
|
sigrdataset = query_newrdataset(client);
|
|
|
|
if (fname == NULL || rdataset == NULL || sigrdataset == NULL) {
|
1999-08-12 07:54:08 +00:00
|
|
|
QUERY_ERROR(DNS_R_SERVFAIL);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
1999-07-24 01:17:44 +00:00
|
|
|
|
1999-08-27 18:40:57 +00:00
|
|
|
#if notyet
|
1999-08-12 07:54:08 +00:00
|
|
|
db_find:
|
1999-08-27 18:40:57 +00:00
|
|
|
#endif
|
1999-08-12 07:54:08 +00:00
|
|
|
/*
|
|
|
|
* Now look for an answer in the database.
|
|
|
|
*/
|
1999-08-18 04:23:39 +00:00
|
|
|
result = dns_db_find(db, client->query.qname, version, type, 0,
|
1999-08-31 22:14:06 +00:00
|
|
|
client->requesttime, &node, fname, rdataset,
|
|
|
|
sigrdataset);
|
1999-08-12 07:54:08 +00:00
|
|
|
switch (result) {
|
|
|
|
case DNS_R_SUCCESS:
|
1999-09-01 18:16:43 +00:00
|
|
|
/*
|
|
|
|
* This case is handled in the main line below.
|
|
|
|
*/
|
|
|
|
break;
|
|
|
|
case DNS_R_GLUE:
|
1999-08-12 07:54:08 +00:00
|
|
|
case DNS_R_ZONECUT:
|
1999-07-28 02:20:36 +00:00
|
|
|
/*
|
1999-08-12 07:54:08 +00:00
|
|
|
* These cases are handled in the main line below.
|
1999-07-28 02:20:36 +00:00
|
|
|
*/
|
1999-09-01 18:16:43 +00:00
|
|
|
auth = ISC_FALSE;
|
1999-08-12 07:54:08 +00:00
|
|
|
break;
|
|
|
|
case DNS_R_DELEGATION:
|
1999-09-01 18:16:43 +00:00
|
|
|
auth = ISC_FALSE;
|
1999-08-12 07:54:08 +00:00
|
|
|
if (is_zone) {
|
|
|
|
/*
|
|
|
|
* We're authoritative for an ancestor of QNAME.
|
|
|
|
*/
|
|
|
|
if (!use_cache) {
|
|
|
|
/*
|
|
|
|
* We don't have a cache, so this is the best
|
|
|
|
* answer.
|
1999-09-01 18:16:43 +00:00
|
|
|
*
|
|
|
|
* We enable the retrieval of glue so we can
|
|
|
|
* put it into the additional data section.
|
1999-08-12 07:54:08 +00:00
|
|
|
*/
|
1999-09-01 18:16:43 +00:00
|
|
|
dboptions = client->query.dboptions;
|
|
|
|
client->query.dboptions |= DNS_DBFIND_GLUEOK;
|
1999-08-31 22:14:06 +00:00
|
|
|
query_addrrset(client, &fname, &rdataset,
|
|
|
|
&sigrdataset, dbuf,
|
1999-08-12 07:54:08 +00:00
|
|
|
DNS_SECTION_AUTHORITY);
|
1999-09-01 18:16:43 +00:00
|
|
|
client->query.dboptions = dboptions;
|
1999-08-12 07:54:08 +00:00
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* We might have a better answer or delegation
|
|
|
|
* in the cache. We'll remember the current
|
|
|
|
* values of fname and rdataset, and then
|
|
|
|
* go looking for QNAME in the cache. If we
|
|
|
|
* find something better, we'll use it instead.
|
|
|
|
*/
|
|
|
|
QUERY_ERROR(DNS_R_NOTIMP);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
INSIST(recursion_ok);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Recurse using the best delegation.
|
|
|
|
*/
|
|
|
|
QUERY_ERROR(DNS_R_NOTIMP);
|
1999-07-24 01:17:44 +00:00
|
|
|
}
|
1999-08-12 07:54:08 +00:00
|
|
|
goto cleanup;
|
|
|
|
case DNS_R_NXRDATASET:
|
|
|
|
INSIST(is_zone);
|
1999-08-19 20:46:59 +00:00
|
|
|
if (dns_rdataset_isassociated(rdataset)) {
|
|
|
|
/*
|
|
|
|
* If we've got a NXT record, we need to save the
|
|
|
|
* name now because we're going call query_addsoa()
|
|
|
|
* below, and it needs to use the name buffer.
|
|
|
|
*/
|
|
|
|
query_keepname(client, fname, dbuf);
|
|
|
|
/*
|
|
|
|
* We don't want the cleanup code to try to release
|
|
|
|
* fname if we fail below, so we set it to NULL.
|
|
|
|
*/
|
|
|
|
tname = fname;
|
|
|
|
fname = NULL;
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* We're not going to use fname, and need to release
|
|
|
|
* our hold on the name buffer so query_addsoa()
|
|
|
|
* may use it.
|
|
|
|
*/
|
|
|
|
query_releasename(client, &fname);
|
|
|
|
}
|
1999-08-12 07:54:08 +00:00
|
|
|
/*
|
|
|
|
* Add SOA.
|
|
|
|
*/
|
|
|
|
result = query_addsoa(client, db);
|
1999-08-03 20:56:08 +00:00
|
|
|
if (result != ISC_R_SUCCESS) {
|
1999-08-12 07:54:08 +00:00
|
|
|
QUERY_ERROR(result);
|
|
|
|
goto cleanup;
|
1999-08-03 20:56:08 +00:00
|
|
|
}
|
1999-08-12 07:54:08 +00:00
|
|
|
/*
|
1999-08-19 20:46:59 +00:00
|
|
|
* Add NXT record if we found one.
|
1999-08-12 07:54:08 +00:00
|
|
|
*/
|
1999-08-19 20:46:59 +00:00
|
|
|
if (dns_rdataset_isassociated(rdataset))
|
1999-08-31 22:14:06 +00:00
|
|
|
query_addrrset(client, &tname, &rdataset, &sigrdataset,
|
|
|
|
NULL, DNS_SECTION_AUTHORITY);
|
1999-08-12 07:54:08 +00:00
|
|
|
goto cleanup;
|
|
|
|
case DNS_R_NXDOMAIN:
|
|
|
|
if (restarts > 0) {
|
|
|
|
/*
|
|
|
|
* We hit a dead end following a CNAME or DNAME.
|
|
|
|
*/
|
|
|
|
goto cleanup;
|
1999-08-03 20:56:08 +00:00
|
|
|
}
|
1999-08-12 07:54:08 +00:00
|
|
|
INSIST(is_zone);
|
1999-08-26 07:14:06 +00:00
|
|
|
if (dns_rdataset_isassociated(rdataset)) {
|
|
|
|
/*
|
|
|
|
* If we've got a NXT record, we need to save the
|
|
|
|
* name now because we're going call query_addsoa()
|
|
|
|
* below, and it needs to use the name buffer.
|
|
|
|
*/
|
|
|
|
query_keepname(client, fname, dbuf);
|
|
|
|
/*
|
|
|
|
* We don't want the cleanup code to try to release
|
|
|
|
* fname if we fail below, so we set it to NULL.
|
|
|
|
*/
|
|
|
|
tname = fname;
|
|
|
|
fname = NULL;
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* We're not going to use fname, and need to release
|
|
|
|
* our hold on the name buffer so query_addsoa()
|
|
|
|
* may use it.
|
|
|
|
*/
|
|
|
|
query_releasename(client, &fname);
|
|
|
|
}
|
1999-07-28 02:20:36 +00:00
|
|
|
/*
|
1999-08-12 07:54:08 +00:00
|
|
|
* Add SOA.
|
1999-07-28 02:20:36 +00:00
|
|
|
*/
|
1999-08-12 07:54:08 +00:00
|
|
|
result = query_addsoa(client, db);
|
|
|
|
if (result != ISC_R_SUCCESS) {
|
|
|
|
QUERY_ERROR(result);
|
|
|
|
goto cleanup;
|
1999-07-28 02:20:36 +00:00
|
|
|
}
|
|
|
|
/*
|
1999-08-26 07:14:06 +00:00
|
|
|
* Add NXT record if we found one.
|
|
|
|
*/
|
|
|
|
if (dns_rdataset_isassociated(rdataset))
|
1999-08-31 22:14:06 +00:00
|
|
|
query_addrrset(client, &tname, &rdataset, &sigrdataset,
|
|
|
|
NULL, DNS_SECTION_AUTHORITY);
|
1999-08-26 07:14:06 +00:00
|
|
|
/*
|
|
|
|
* Set message rcode.
|
1999-07-28 02:20:36 +00:00
|
|
|
*/
|
1999-08-26 07:14:06 +00:00
|
|
|
client->message->rcode = dns_rcode_nxdomain;
|
1999-08-12 07:54:08 +00:00
|
|
|
goto cleanup;
|
|
|
|
case DNS_R_NOTFOUND:
|
|
|
|
QUERY_ERROR(DNS_R_NOTIMP);
|
|
|
|
goto cleanup;
|
|
|
|
case DNS_R_CNAME:
|
1999-07-28 02:20:36 +00:00
|
|
|
/*
|
1999-08-12 07:54:08 +00:00
|
|
|
* Keep a copy of the rdataset. We have to do this because
|
|
|
|
* query_addrrset may clear 'rdataset' (to prevent the
|
|
|
|
* cleanup code from cleaning it up).
|
1999-07-28 02:20:36 +00:00
|
|
|
*/
|
1999-08-12 07:54:08 +00:00
|
|
|
trdataset = rdataset;
|
|
|
|
/*
|
|
|
|
* Add the CNAME to the answer section.
|
|
|
|
*/
|
1999-08-31 22:14:06 +00:00
|
|
|
query_addrrset(client, &fname, &rdataset, &sigrdataset, dbuf,
|
1999-08-12 07:54:08 +00:00
|
|
|
DNS_SECTION_ANSWER);
|
|
|
|
/*
|
|
|
|
* We set the PARTIALANSWER attribute so that if anything goes
|
|
|
|
* wrong later on, we'll return what we've got so far.
|
|
|
|
*/
|
|
|
|
client->query.attributes |= NS_QUERYATTR_PARTIALANSWER;
|
|
|
|
/*
|
|
|
|
* Reset qname to be the target name of the CNAME and restart
|
|
|
|
* the query.
|
|
|
|
*/
|
|
|
|
tname = NULL;
|
|
|
|
result = dns_message_gettempname(client->message, &tname);
|
|
|
|
if (result != ISC_R_SUCCESS)
|
|
|
|
goto cleanup;
|
|
|
|
result = dns_rdataset_first(trdataset);
|
|
|
|
if (result != ISC_R_SUCCESS)
|
|
|
|
goto cleanup;
|
|
|
|
dns_rdataset_current(trdataset, &rdata);
|
|
|
|
r.base = rdata.data;
|
|
|
|
r.length = rdata.length;
|
|
|
|
dns_name_init(tname, NULL);
|
|
|
|
dns_name_fromregion(tname, &r);
|
|
|
|
client->query.qname = tname;
|
|
|
|
want_restart = ISC_TRUE;
|
|
|
|
goto cleanup;
|
|
|
|
case DNS_R_DNAME:
|
|
|
|
/*
|
|
|
|
* Compare the current qname to the found name. We need
|
|
|
|
* to know how many labels and bits are in common because
|
|
|
|
* we're going to have to split qname later on.
|
|
|
|
*/
|
|
|
|
namereln = dns_name_fullcompare(client->query.qname, fname,
|
|
|
|
&order, &nlabels, &nbits);
|
|
|
|
INSIST(namereln == dns_namereln_subdomain);
|
|
|
|
/*
|
|
|
|
* Keep a copy of the rdataset. We have to do this because
|
|
|
|
* query_addrrset may clear 'rdataset' (to prevent the
|
|
|
|
* cleanup code from cleaning it up).
|
|
|
|
*/
|
|
|
|
trdataset = rdataset;
|
|
|
|
/*
|
|
|
|
* Add the DNAME to the answer section.
|
|
|
|
*/
|
1999-08-31 22:14:06 +00:00
|
|
|
query_addrrset(client, &fname, &rdataset, &sigrdataset, dbuf,
|
1999-08-12 07:54:08 +00:00
|
|
|
DNS_SECTION_ANSWER);
|
|
|
|
/*
|
|
|
|
* We set the PARTIALANSWER attribute so that if anything goes
|
|
|
|
* wrong later on, we'll return what we've got so far.
|
|
|
|
*/
|
|
|
|
client->query.attributes |= NS_QUERYATTR_PARTIALANSWER;
|
1999-07-28 02:20:36 +00:00
|
|
|
/*
|
1999-08-12 07:54:08 +00:00
|
|
|
* Get the target name of the DNAME.
|
1999-07-28 02:20:36 +00:00
|
|
|
*/
|
1999-08-12 07:54:08 +00:00
|
|
|
tname = NULL;
|
|
|
|
result = dns_message_gettempname(client->message, &tname);
|
|
|
|
if (result != ISC_R_SUCCESS)
|
|
|
|
goto cleanup;
|
|
|
|
result = dns_rdataset_first(trdataset);
|
|
|
|
if (result != ISC_R_SUCCESS)
|
|
|
|
goto cleanup;
|
|
|
|
dns_rdataset_current(trdataset, &rdata);
|
|
|
|
r.base = rdata.data;
|
|
|
|
r.length = rdata.length;
|
|
|
|
dns_name_init(tname, NULL);
|
|
|
|
dns_name_fromregion(tname, &r);
|
|
|
|
/*
|
|
|
|
* Construct the new qname and restart the query.
|
|
|
|
*/
|
|
|
|
dns_fixedname_init(&fixed);
|
|
|
|
prefix = dns_fixedname_name(&fixed);
|
|
|
|
result = dns_name_split(client->query.qname, nlabels, nbits,
|
|
|
|
prefix, NULL);
|
|
|
|
if (result != ISC_R_SUCCESS)
|
|
|
|
goto cleanup;
|
|
|
|
if (fname != NULL)
|
|
|
|
query_releasename(client, &fname);
|
|
|
|
dbuf = query_getnamebuf(client);
|
|
|
|
if (dbuf == NULL)
|
|
|
|
goto cleanup;
|
|
|
|
fname = query_newname(client, dbuf, &b);
|
|
|
|
if (fname == NULL)
|
|
|
|
goto cleanup;
|
|
|
|
result = dns_name_concatenate(prefix, tname, fname, NULL);
|
1999-08-12 20:27:24 +00:00
|
|
|
if (result != ISC_R_SUCCESS) {
|
|
|
|
if (result == ISC_R_NOSPACE) {
|
|
|
|
/*
|
1999-08-31 22:14:06 +00:00
|
|
|
* RFC 2672, section 4.1, subsection 3c says
|
|
|
|
* we should return YXDOMAIN if the constructed
|
1999-08-12 20:27:24 +00:00
|
|
|
* name would be too long.
|
|
|
|
*/
|
|
|
|
client->message->rcode = dns_rcode_yxdomain;
|
|
|
|
}
|
1999-08-12 07:54:08 +00:00
|
|
|
goto cleanup;
|
1999-08-12 20:27:24 +00:00
|
|
|
}
|
1999-08-12 07:54:08 +00:00
|
|
|
query_keepname(client, fname, dbuf);
|
|
|
|
client->query.qname = fname;
|
|
|
|
fname = NULL;
|
|
|
|
want_restart = ISC_TRUE;
|
|
|
|
goto cleanup;
|
|
|
|
default:
|
|
|
|
/*
|
|
|
|
* Something has gone wrong.
|
|
|
|
*/
|
|
|
|
QUERY_ERROR(DNS_R_SERVFAIL);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
1999-07-24 01:17:44 +00:00
|
|
|
|
1999-08-12 07:54:08 +00:00
|
|
|
if (type == dns_rdatatype_any) {
|
|
|
|
/*
|
|
|
|
* XXXRTH Need to handle zonecuts with special case
|
|
|
|
* code.
|
|
|
|
*/
|
|
|
|
n = 0;
|
|
|
|
rdsiter = NULL;
|
1999-08-31 22:14:06 +00:00
|
|
|
result = dns_db_allrdatasets(db, node, version, 0, &rdsiter);
|
1999-08-12 07:54:08 +00:00
|
|
|
if (result != ISC_R_SUCCESS) {
|
|
|
|
QUERY_ERROR(DNS_R_SERVFAIL);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
result = dns_rdatasetiter_first(rdsiter);
|
|
|
|
while (result == ISC_R_SUCCESS) {
|
|
|
|
dns_rdatasetiter_current(rdsiter, rdataset);
|
1999-08-31 22:14:06 +00:00
|
|
|
if (qtype == dns_rdatatype_any ||
|
|
|
|
rdataset->type == qtype) {
|
|
|
|
tname = fname;
|
|
|
|
query_addrrset(client, &tname, &rdataset, NULL,
|
|
|
|
dbuf, DNS_SECTION_ANSWER);
|
|
|
|
n++;
|
1999-09-01 18:16:43 +00:00
|
|
|
if (tname == NULL) {
|
|
|
|
clear_fname = ISC_TRUE;
|
|
|
|
/*
|
|
|
|
* We set dbuf to NULL because we only
|
|
|
|
* want the query_keepname() call in
|
|
|
|
* query_addrrset() to be called once.
|
|
|
|
*/
|
|
|
|
dbuf = NULL;
|
|
|
|
}
|
|
|
|
|
1999-08-31 22:14:06 +00:00
|
|
|
/*
|
|
|
|
* We shouldn't ever fail to add 'rdataset'
|
|
|
|
* because it's already in the answer.
|
|
|
|
*/
|
|
|
|
INSIST(rdataset == NULL);
|
|
|
|
rdataset = query_newrdataset(client);
|
|
|
|
if (rdataset == NULL)
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* We're not interested in this rdataset.
|
|
|
|
*/
|
|
|
|
dns_rdataset_disassociate(rdataset);
|
|
|
|
}
|
|
|
|
result = dns_rdatasetiter_next(rdsiter);
|
|
|
|
}
|
|
|
|
if (n > 0) {
|
1999-09-01 18:16:43 +00:00
|
|
|
if (clear_fname)
|
|
|
|
fname = NULL;
|
1999-08-31 22:14:06 +00:00
|
|
|
} else {
|
1999-07-28 02:20:36 +00:00
|
|
|
/*
|
1999-08-31 22:14:06 +00:00
|
|
|
* We didn't match any rdatasets.
|
1999-07-28 02:20:36 +00:00
|
|
|
*/
|
1999-08-31 22:14:06 +00:00
|
|
|
if (qtype == dns_rdatatype_sig &&
|
|
|
|
result == DNS_R_NOMORE) {
|
|
|
|
/*
|
|
|
|
* XXXRTH If this is a secure zone and we
|
|
|
|
* didn't find any SIGs, we should generate
|
|
|
|
* an error unless we were searching for
|
|
|
|
* glue. Ugh.
|
|
|
|
*/
|
|
|
|
/*
|
|
|
|
* We were searching for SIG records in
|
|
|
|
* a nonsecure zone. Send a "no error,
|
|
|
|
* no data" response.
|
|
|
|
*
|
|
|
|
* First we must release fname.
|
|
|
|
*/
|
|
|
|
query_releasename(client, &fname);
|
|
|
|
/*
|
|
|
|
* Add SOA.
|
|
|
|
*/
|
|
|
|
result = query_addsoa(client, db);
|
1999-08-31 22:24:36 +00:00
|
|
|
if (result == ISC_R_SUCCESS)
|
|
|
|
result = DNS_R_NOMORE;
|
1999-08-31 22:14:06 +00:00
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* Something went wrong.
|
|
|
|
*/
|
|
|
|
result = DNS_R_SERVFAIL;
|
1999-07-28 02:20:36 +00:00
|
|
|
}
|
1999-08-03 01:21:00 +00:00
|
|
|
}
|
1999-08-12 07:54:08 +00:00
|
|
|
dns_rdatasetiter_destroy(&rdsiter);
|
|
|
|
if (result != DNS_R_NOMORE) {
|
|
|
|
QUERY_ERROR(DNS_R_SERVFAIL);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* This is the "normal" case -- an ordinary question to which
|
|
|
|
* we know the answer.
|
|
|
|
*/
|
1999-09-01 18:16:43 +00:00
|
|
|
tname = fname;
|
|
|
|
query_addrrset(client, &tname, &rdataset, &sigrdataset, dbuf,
|
1999-08-12 07:54:08 +00:00
|
|
|
DNS_SECTION_ANSWER);
|
1999-09-01 18:16:43 +00:00
|
|
|
if (tname == NULL)
|
|
|
|
clear_fname = ISC_TRUE;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We shouldn't ever fail to add 'rdataset'
|
|
|
|
* because it's already in the answer.
|
|
|
|
*/
|
|
|
|
INSIST(rdataset == NULL);
|
|
|
|
|
1999-08-12 07:54:08 +00:00
|
|
|
/*
|
|
|
|
* Remember that we've answered this question.
|
|
|
|
*/
|
|
|
|
qrdataset->attributes |= DNS_RDATASETATTR_ANSWERED;
|
1999-09-01 18:16:43 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* A6 records cause type A and AAAA additional
|
|
|
|
* section processign for the QNAME.
|
|
|
|
*/
|
|
|
|
if (qtype == dns_rdatatype_a6) {
|
|
|
|
if (!clear_fname) {
|
|
|
|
/*
|
|
|
|
* We haven't used fname yet, but we're going
|
|
|
|
* to need it now.
|
|
|
|
*/
|
|
|
|
query_keepname(client, fname, dbuf);
|
|
|
|
clear_fname = ISC_TRUE;
|
|
|
|
}
|
|
|
|
query_a6additional(client, db, node, version, fname);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (clear_fname)
|
|
|
|
fname = NULL;
|
1999-08-12 07:54:08 +00:00
|
|
|
}
|
1999-07-24 01:17:44 +00:00
|
|
|
|
1999-08-12 07:54:08 +00:00
|
|
|
/*
|
|
|
|
* XXXRTH Handle additional questions above. Find all the question
|
|
|
|
* types we can from the node we found, and (if recursion is
|
|
|
|
* OK) launch queries for any types we don't have answers to.
|
|
|
|
*
|
|
|
|
* Special case: they make an ANY query as well as some
|
|
|
|
* other type. Perhaps ANY should be disallowed in a
|
|
|
|
* multiple question query?
|
|
|
|
*/
|
1999-07-24 01:17:44 +00:00
|
|
|
|
1999-08-12 07:54:08 +00:00
|
|
|
cleanup:
|
1999-08-31 22:14:06 +00:00
|
|
|
if (sigrdataset != NULL) {
|
|
|
|
if (sigrdataset->methods != NULL)
|
|
|
|
dns_rdataset_disassociate(sigrdataset);
|
|
|
|
ISC_LIST_APPEND(client->query.tmprdatasets, sigrdataset, link);
|
|
|
|
}
|
1999-08-12 07:54:08 +00:00
|
|
|
if (rdataset != NULL) {
|
|
|
|
if (rdataset->methods != NULL)
|
|
|
|
dns_rdataset_disassociate(rdataset);
|
|
|
|
ISC_LIST_APPEND(client->query.tmprdatasets, rdataset, link);
|
|
|
|
}
|
|
|
|
if (fname != NULL)
|
|
|
|
query_releasename(client, &fname);
|
|
|
|
if (node != NULL)
|
1999-07-28 02:20:36 +00:00
|
|
|
dns_db_detachnode(db, &node);
|
1999-08-12 07:54:08 +00:00
|
|
|
if (db != NULL)
|
1999-07-28 02:20:36 +00:00
|
|
|
dns_db_detach(&db);
|
1999-07-24 01:17:44 +00:00
|
|
|
|
1999-08-12 07:54:08 +00:00
|
|
|
if (restarts == 0 && !auth) {
|
|
|
|
/*
|
|
|
|
* We're not authoritative, so we must ensure the AA bit
|
|
|
|
* isn't set.
|
|
|
|
*/
|
|
|
|
client->message->flags &= ~DNS_MESSAGEFLAG_AA;
|
|
|
|
}
|
1999-07-24 01:17:44 +00:00
|
|
|
|
1999-08-12 07:54:08 +00:00
|
|
|
if (want_restart && restarts < MAX_RESTARTS) {
|
|
|
|
restarts++;
|
|
|
|
goto restart;
|
|
|
|
}
|
1999-07-24 01:17:44 +00:00
|
|
|
|
1999-08-12 07:54:08 +00:00
|
|
|
if (eresult != ISC_R_SUCCESS && !PARTIALANSWER(client))
|
|
|
|
ns_client_error(client, eresult);
|
|
|
|
else
|
|
|
|
ns_client_send(client);
|
1999-07-24 01:17:44 +00:00
|
|
|
}
|
|
|
|
|
1999-08-12 07:54:08 +00:00
|
|
|
/*
|
|
|
|
* XXXRTH Problem areas.
|
|
|
|
*
|
|
|
|
* If we're authoritative for both a parent and a child, the
|
|
|
|
* child is non-secure, and we are asked for the KEY of the
|
|
|
|
* nonsecure child, we need to get it from the parent.
|
|
|
|
* If we're not auth for the parent, then we have to go
|
|
|
|
* looking for it in the cache. How do we even know who
|
|
|
|
* the parent is? We probably won't find this KEY when doing
|
|
|
|
* additional data KEY retrievals, but that's probably OK,
|
|
|
|
* since it's a SHOULD not a MUST. We don't want to be doing
|
|
|
|
* tons of work just to fill out additional data.
|
|
|
|
*
|
|
|
|
* Similar problems occur with NXT queries, since there can
|
|
|
|
* be NXT records at a delegation point in both the parent
|
|
|
|
* and the child. RFC 2535 section 5.5 says that on explicit
|
|
|
|
* query we should return both, if available. That seems
|
|
|
|
* to imply we shouldn't recurse to get the missing one
|
|
|
|
* if only one is available. Is that right?
|
|
|
|
*/
|
|
|
|
|
1999-07-24 01:17:44 +00:00
|
|
|
void
|
|
|
|
ns_query_start(ns_client_t *client) {
|
1999-07-28 02:20:36 +00:00
|
|
|
isc_result_t result;
|
1999-08-03 01:21:00 +00:00
|
|
|
dns_message_t *message = client->message;
|
1999-08-16 09:46:20 +00:00
|
|
|
dns_rdataset_t *rdataset;
|
1999-08-03 01:21:00 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Ensure that appropriate cleanups occur.
|
|
|
|
*/
|
|
|
|
client->next = query_next;
|
1999-07-28 02:20:36 +00:00
|
|
|
|
1999-08-03 01:21:00 +00:00
|
|
|
/*
|
1999-08-12 07:54:08 +00:00
|
|
|
* XXXRTH Deal with allow-query and allow-recursion here.
|
1999-08-03 01:21:00 +00:00
|
|
|
*/
|
|
|
|
|
1999-07-28 02:20:36 +00:00
|
|
|
/*
|
1999-08-03 01:21:00 +00:00
|
|
|
* If the client doesn't want recursion, turn it off.
|
1999-07-28 02:20:36 +00:00
|
|
|
*/
|
1999-08-03 01:21:00 +00:00
|
|
|
if ((message->flags & DNS_MESSAGEFLAG_RD) == 0)
|
|
|
|
client->query.attributes &= ~NS_QUERYATTR_RECURSIONOK;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get the question name.
|
|
|
|
*/
|
|
|
|
result = dns_message_firstname(message, DNS_SECTION_QUESTION);
|
|
|
|
if (result != ISC_R_SUCCESS) {
|
|
|
|
ns_client_error(client, result);
|
|
|
|
return;
|
1999-07-28 02:20:36 +00:00
|
|
|
}
|
1999-08-03 01:21:00 +00:00
|
|
|
dns_message_currentname(message, DNS_SECTION_QUESTION,
|
|
|
|
&client->query.qname);
|
1999-08-12 07:54:08 +00:00
|
|
|
client->query.origqname = client->query.qname;
|
1999-08-03 01:21:00 +00:00
|
|
|
result = dns_message_nextname(message, DNS_SECTION_QUESTION);
|
1999-07-28 02:20:36 +00:00
|
|
|
if (result != ISC_R_NOMORE) {
|
1999-08-03 01:21:00 +00:00
|
|
|
if (result == ISC_R_SUCCESS) {
|
|
|
|
/*
|
|
|
|
* There's more than one QNAME in the question
|
|
|
|
* section.
|
|
|
|
*/
|
|
|
|
ns_client_error(client, DNS_R_FORMERR);
|
|
|
|
} else
|
|
|
|
ns_client_error(client, result);
|
1999-07-28 02:20:36 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
1999-08-16 09:46:20 +00:00
|
|
|
/*
|
|
|
|
* Check for illegal meta-classes and meta-types in
|
|
|
|
* multiple question queries (edns1 section 5.1).
|
|
|
|
*/
|
|
|
|
if (message->counts[DNS_SECTION_QUESTION] > 1) {
|
|
|
|
if (dns_rdataclass_ismeta(message->rdclass)) {
|
|
|
|
ns_client_error(client, DNS_R_FORMERR);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
for (rdataset = ISC_LIST_HEAD(client->query.qname->list);
|
|
|
|
rdataset != NULL;
|
|
|
|
rdataset = ISC_LIST_NEXT(rdataset, link)) {
|
|
|
|
if (dns_rdatatype_ismeta(rdataset->type)) {
|
|
|
|
ns_client_error(client, DNS_R_FORMERR);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check for meta-queries like IXFR and AXFR.
|
|
|
|
*/
|
|
|
|
if (message->counts[DNS_SECTION_QUESTION] == 1) {
|
|
|
|
rdataset = ISC_LIST_HEAD(client->query.qname->list);
|
|
|
|
INSIST(rdataset != NULL);
|
|
|
|
if (dns_rdatatype_ismeta(rdataset->type)) {
|
|
|
|
switch (rdataset->type) {
|
|
|
|
case dns_rdatatype_any:
|
|
|
|
break; /* Let query_find handle it. */
|
|
|
|
case dns_rdatatype_ixfr:
|
|
|
|
case dns_rdatatype_axfr:
|
|
|
|
ns_xfr_start(client, rdataset->type);
|
|
|
|
return;
|
|
|
|
case dns_rdatatype_maila:
|
|
|
|
case dns_rdatatype_mailb:
|
|
|
|
ns_client_error(client, DNS_R_NOTIMP);
|
|
|
|
return;
|
|
|
|
default: /* TSIG, etc. */
|
|
|
|
ns_client_error(client, DNS_R_FORMERR);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* This is an ordinary query. */
|
|
|
|
|
|
|
|
result = dns_message_reply(message, ISC_TRUE);
|
|
|
|
if (result != ISC_R_SUCCESS) {
|
|
|
|
ns_client_next(client, result);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Assume authoritative response until it is known to be
|
|
|
|
* otherwise.
|
|
|
|
*/
|
|
|
|
message->flags |= DNS_MESSAGEFLAG_AA;
|
|
|
|
|
1999-08-12 07:54:08 +00:00
|
|
|
query_find(client);
|
1999-07-24 01:17:44 +00:00
|
|
|
}
|