client.c revision cc1b6f86f2e0bff7e903a4e7d2cd10e6a59b8b9c
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff/*
ca41b452ede6feaa9d8739ec3cae19389a7b0d03Bob Halley * Copyright (C) 1999 Internet Software Consortium.
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff *
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff * Permission to use, copy, modify, and distribute this software for any
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff * purpose with or without fee is hereby granted, provided that the above
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff * copyright notice and this permission notice appear in all copies.
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff *
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff * SOFTWARE.
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff */
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff#include <config.h>
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff#include <isc/assertions.h>
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff#include <isc/mem.h>
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff#include <isc/mutex.h>
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff#include <isc/result.h>
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff#include <isc/task.h>
86944a4c8002e80ae9b6eb5a5e29b797879be45fMichael Graff#include <isc/timer.h>
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff#include <isc/util.h>
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff#include <dns/acl.h>
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff#include <dns/dispatch.h>
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff#include <dns/events.h>
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff#include <dns/message.h>
efc8a09e19dfcfafa92fd2ad113073a4f5295e9bMichael Graff#include <dns/rdata.h>
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff#include <dns/rdatalist.h>
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff#include <dns/rdataset.h>
efc8a09e19dfcfafa92fd2ad113073a4f5295e9bMichael Graff#include <dns/view.h>
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff#include <dns/xfrin.h>
364a82f7c25b62967678027043425201a5e5171aBob Halley
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff#include <named/globals.h>
e45d323a2a0f4ca08d4b139546e60a5fa7bd3f0cMichael Graff#include <named/client.h>
fd15c8e32ed0c1cfd3ed737858a81966e7fbaeacAndreas Gustafsson#include <named/log.h>
efc8a09e19dfcfafa92fd2ad113073a4f5295e9bMichael Graff#include <named/query.h>
efc8a09e19dfcfafa92fd2ad113073a4f5295e9bMichael Graff#include <named/server.h>
41ac1e406f1be4ec2eba2d8bae673e762d54ea74Andreas Gustafsson#include <named/update.h>
efa4ebbff3c9f6f38ab8b55540fb696243c1172cAndreas Gustafsson#include <named/notify.h>
efa4ebbff3c9f6f38ab8b55540fb696243c1172cAndreas Gustafsson
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff#define NS_CLIENT_TRACE
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff#ifdef NS_CLIENT_TRACE
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff#define CTRACE(m) isc_log_write(ns_g_lctx, \
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff NS_LOGCATEGORY_CLIENT, \
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff NS_LOGMODULE_CLIENT, \
c803787146cadcb2d7e10cbf4491f3be513dfa1aMichael Graff ISC_LOG_DEBUG(3), \
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff "client %p: %s", client, (m))
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff#define MTRACE(m) isc_log_write(ns_g_lctx, \
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff NS_LOGCATEGORY_GENERAL, \
2bcb48cfcae36398454c98e40c563e2cde748e07Michael Graff NS_LOGMODULE_CLIENT, \
2bcb48cfcae36398454c98e40c563e2cde748e07Michael Graff ISC_LOG_DEBUG(3), \
e198cb953c1a5bc189ae21dc3f8d622f5a08bc34Bob Halley "clientmgr %p: %s", manager, (m))
3ac63b472022ff92691d1fe69ac715a729671965Michael Graff#endif
efc8a09e19dfcfafa92fd2ad113073a4f5295e9bMichael Graff
efc8a09e19dfcfafa92fd2ad113073a4f5295e9bMichael Graff#define TCP_CLIENT(c) (((c)->attributes & NS_CLIENTATTR_TCP) != 0)
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff
efc8a09e19dfcfafa92fd2ad113073a4f5295e9bMichael Graff#define SEND_BUFFER_SIZE 2048
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graffstruct ns_clientmgr {
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff /* Unlocked. */
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff unsigned int magic;
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff isc_mem_t * mctx;
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff isc_taskmgr_t * taskmgr;
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff isc_timermgr_t * timermgr;
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff isc_mutex_t lock;
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff /* Locked by lock. */
86944a4c8002e80ae9b6eb5a5e29b797879be45fMichael Graff isc_boolean_t exiting;
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff unsigned int nclients;
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff ISC_LIST(ns_client_t) clients;
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff};
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff#define MANAGER_MAGIC 0x4E53436DU /* NSCm */
fc4fe6e5e61dbc916707ab2e071ffd22aef9de7fMichael Graff#define VALID_MANAGER(m) ((m) != NULL && \
86944a4c8002e80ae9b6eb5a5e29b797879be45fMichael Graff (m)->magic == MANAGER_MAGIC)
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff
910df98b0efcbe8380b952887f4071051cc39a25Michael Graffstatic void clientmgr_destroy(ns_clientmgr_t *manager);
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff/***
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff *** Client
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff ***/
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff/*
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff * Important note!
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff *
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff * All client state changes, other than that from idle to listening, occur
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff * as a result of events. This guarantees serialization and avoids the
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff * need for locking.
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff *
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff * If a routine is ever created that allows someone other than the client's
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff * task to change the client, then the client will have to be locked.
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff */
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff
2bcb48cfcae36398454c98e40c563e2cde748e07Michael Graff/*
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff * Free a client immediately if possible, otherwise start
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff * shutting it down and postpone freeing to later.
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff */
c803787146cadcb2d7e10cbf4491f3be513dfa1aMichael Graffstatic void
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graffmaybe_free(ns_client_t *client) {
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff isc_boolean_t need_clientmgr_destroy = ISC_FALSE;
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff REQUIRE(NS_CLIENT_VALID(client));
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff REQUIRE(client->shuttingdown == ISC_TRUE);
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff if (client->naccepts > 0)
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff isc_socket_cancel(client->tcplistener, client->task,
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff ISC_SOCKCANCEL_ACCEPT);
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff if (client->nreads > 0)
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff dns_tcpmsg_cancelread(&client->tcpmsg);
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff if (client->nsends > 0)
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff isc_socket_cancel(client->tcpsocket, client->task,
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff ISC_SOCKCANCEL_SEND);
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff
c803787146cadcb2d7e10cbf4491f3be513dfa1aMichael Graff if (!(client->nreads == 0 && client->naccepts == 0 &&
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff client->nsends == 0 && client->nwaiting == 0)) {
2bcb48cfcae36398454c98e40c563e2cde748e07Michael Graff /* Still waiting for events. */
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff return;
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff }
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff /* We have received our last event. */
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff ns_query_free(client);
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff isc_mempool_destroy(&client->sendbufs);
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff isc_timer_detach(&client->timer);
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff if (client->dispentry != NULL) {
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff dns_dispatchevent_t **deventp;
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff if (client->dispevent != NULL)
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff deventp = &client->dispevent;
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff else
efc8a09e19dfcfafa92fd2ad113073a4f5295e9bMichael Graff deventp = NULL;
86944a4c8002e80ae9b6eb5a5e29b797879be45fMichael Graff dns_dispatch_removerequest(client->dispatch,
86944a4c8002e80ae9b6eb5a5e29b797879be45fMichael Graff &client->dispentry,
86944a4c8002e80ae9b6eb5a5e29b797879be45fMichael Graff deventp);
2bcb48cfcae36398454c98e40c563e2cde748e07Michael Graff }
86944a4c8002e80ae9b6eb5a5e29b797879be45fMichael Graff if (client->view != NULL)
e44487bfc23599b6b240e09d83d1c862fecfcc82Michael Graff dns_view_detach(&client->view);
e44487bfc23599b6b240e09d83d1c862fecfcc82Michael Graff if (client->opt != NULL) {
86944a4c8002e80ae9b6eb5a5e29b797879be45fMichael Graff INSIST(dns_rdataset_isassociated(client->opt));
2bcb48cfcae36398454c98e40c563e2cde748e07Michael Graff dns_rdataset_disassociate(client->opt);
e44487bfc23599b6b240e09d83d1c862fecfcc82Michael Graff dns_message_puttemprdataset(client->message, &client->opt);
897c9ddb4d745b2bfecf98b17e5487bb6656299aMichael Graff }
897c9ddb4d745b2bfecf98b17e5487bb6656299aMichael Graff dns_message_destroy(&client->message);
2bcb48cfcae36398454c98e40c563e2cde748e07Michael Graff if (client->tcpmsg_valid)
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff dns_tcpmsg_invalidate(&client->tcpmsg);
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff if (client->dispatch != NULL)
c803787146cadcb2d7e10cbf4491f3be513dfa1aMichael Graff dns_dispatch_detach(&client->dispatch);
c803787146cadcb2d7e10cbf4491f3be513dfa1aMichael Graff if (client->tcpsocket != NULL)
2bcb48cfcae36398454c98e40c563e2cde748e07Michael Graff isc_socket_detach(&client->tcpsocket);
2bcb48cfcae36398454c98e40c563e2cde748e07Michael Graff if (client->tcplistener != NULL)
2bcb48cfcae36398454c98e40c563e2cde748e07Michael Graff isc_socket_detach(&client->tcplistener);
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff if (client->task != NULL)
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff isc_task_detach(&client->task);
86944a4c8002e80ae9b6eb5a5e29b797879be45fMichael Graff if (client->manager != NULL) {
86944a4c8002e80ae9b6eb5a5e29b797879be45fMichael Graff ns_clientmgr_t *manager = client->manager;
efc8a09e19dfcfafa92fd2ad113073a4f5295e9bMichael Graff LOCK(&manager->lock);
efc8a09e19dfcfafa92fd2ad113073a4f5295e9bMichael Graff
efc8a09e19dfcfafa92fd2ad113073a4f5295e9bMichael Graff INSIST(manager->nclients > 0);
efc8a09e19dfcfafa92fd2ad113073a4f5295e9bMichael Graff manager->nclients--;
efc8a09e19dfcfafa92fd2ad113073a4f5295e9bMichael Graff if (manager->nclients == 0 && manager->exiting)
3ac63b472022ff92691d1fe69ac715a729671965Michael Graff need_clientmgr_destroy = ISC_TRUE;
3ac63b472022ff92691d1fe69ac715a729671965Michael Graff ISC_LIST_UNLINK(manager->clients, client, link);
efc8a09e19dfcfafa92fd2ad113073a4f5295e9bMichael Graff
efc8a09e19dfcfafa92fd2ad113073a4f5295e9bMichael Graff UNLOCK(&manager->lock);
efc8a09e19dfcfafa92fd2ad113073a4f5295e9bMichael Graff }
efc8a09e19dfcfafa92fd2ad113073a4f5295e9bMichael Graff CTRACE("free");
efc8a09e19dfcfafa92fd2ad113073a4f5295e9bMichael Graff client->magic = 0;
efc8a09e19dfcfafa92fd2ad113073a4f5295e9bMichael Graff isc_mem_put(client->mctx, client, sizeof *client);
efc8a09e19dfcfafa92fd2ad113073a4f5295e9bMichael Graff
efc8a09e19dfcfafa92fd2ad113073a4f5295e9bMichael Graff if (need_clientmgr_destroy)
efc8a09e19dfcfafa92fd2ad113073a4f5295e9bMichael Graff clientmgr_destroy(client->manager);
efc8a09e19dfcfafa92fd2ad113073a4f5295e9bMichael Graff}
efc8a09e19dfcfafa92fd2ad113073a4f5295e9bMichael Graff
efc8a09e19dfcfafa92fd2ad113073a4f5295e9bMichael Graff/*
efc8a09e19dfcfafa92fd2ad113073a4f5295e9bMichael Graff * The client's task has received a shutdown event.
efc8a09e19dfcfafa92fd2ad113073a4f5295e9bMichael Graff */
fd15c8e32ed0c1cfd3ed737858a81966e7fbaeacAndreas Gustafssonstatic void
efc8a09e19dfcfafa92fd2ad113073a4f5295e9bMichael Graffclient_shutdown(isc_task_t *task, isc_event_t *event) {
efc8a09e19dfcfafa92fd2ad113073a4f5295e9bMichael Graff ns_client_t *client;
efc8a09e19dfcfafa92fd2ad113073a4f5295e9bMichael Graff
efc8a09e19dfcfafa92fd2ad113073a4f5295e9bMichael Graff REQUIRE(event != NULL);
efc8a09e19dfcfafa92fd2ad113073a4f5295e9bMichael Graff REQUIRE(event->type == ISC_TASKEVENT_SHUTDOWN);
efc8a09e19dfcfafa92fd2ad113073a4f5295e9bMichael Graff client = event->arg;
efc8a09e19dfcfafa92fd2ad113073a4f5295e9bMichael Graff REQUIRE(NS_CLIENT_VALID(client));
efc8a09e19dfcfafa92fd2ad113073a4f5295e9bMichael Graff REQUIRE(task == client->task);
efc8a09e19dfcfafa92fd2ad113073a4f5295e9bMichael Graff
efc8a09e19dfcfafa92fd2ad113073a4f5295e9bMichael Graff CTRACE("shutdown");
efc8a09e19dfcfafa92fd2ad113073a4f5295e9bMichael Graff
efc8a09e19dfcfafa92fd2ad113073a4f5295e9bMichael Graff client->shuttingdown = ISC_TRUE;
fd15c8e32ed0c1cfd3ed737858a81966e7fbaeacAndreas Gustafsson maybe_free(client);
fd15c8e32ed0c1cfd3ed737858a81966e7fbaeacAndreas Gustafsson
fd15c8e32ed0c1cfd3ed737858a81966e7fbaeacAndreas Gustafsson isc_event_free(&event);
fd15c8e32ed0c1cfd3ed737858a81966e7fbaeacAndreas Gustafsson}
fd15c8e32ed0c1cfd3ed737858a81966e7fbaeacAndreas Gustafsson
fd15c8e32ed0c1cfd3ed737858a81966e7fbaeacAndreas Gustafssonstatic void client_read(ns_client_t *client);
efc8a09e19dfcfafa92fd2ad113073a4f5295e9bMichael Graffstatic void client_accept(ns_client_t *client);
efc8a09e19dfcfafa92fd2ad113073a4f5295e9bMichael Graff
efc8a09e19dfcfafa92fd2ad113073a4f5295e9bMichael Graffvoid
efc8a09e19dfcfafa92fd2ad113073a4f5295e9bMichael Graffns_client_next(ns_client_t *client, isc_result_t result) {
efc8a09e19dfcfafa92fd2ad113073a4f5295e9bMichael Graff
efc8a09e19dfcfafa92fd2ad113073a4f5295e9bMichael Graff REQUIRE(NS_CLIENT_VALID(client));
efc8a09e19dfcfafa92fd2ad113073a4f5295e9bMichael Graff
3ac63b472022ff92691d1fe69ac715a729671965Michael Graff CTRACE("next");
5bd937c666375d03ee6a366a13d0bdb31aa3f07fBob Halley
ff9bb3fc5453bbf310b67c560fbf04a5c0fb60daMichael Graff if (client->next != NULL) {
efc8a09e19dfcfafa92fd2ad113073a4f5295e9bMichael Graff (client->next)(client, result);
efa4ebbff3c9f6f38ab8b55540fb696243c1172cAndreas Gustafsson client->next = NULL;
e592dd7c344052ee51eb707cd744b48b34f4c74eBob Halley }
efa4ebbff3c9f6f38ab8b55540fb696243c1172cAndreas Gustafsson
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff /*
36ca83769dbba29a3d8670eef9acd95c7a71a7f6Michael Graff * XXXRTH If result != ISC_R_SUCCESS:
efc8a09e19dfcfafa92fd2ad113073a4f5295e9bMichael Graff * Log result if there is interest in doing so.
efc8a09e19dfcfafa92fd2ad113073a4f5295e9bMichael Graff */
efc8a09e19dfcfafa92fd2ad113073a4f5295e9bMichael Graff
efc8a09e19dfcfafa92fd2ad113073a4f5295e9bMichael Graff if (client->view != NULL)
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff dns_view_detach(&client->view);
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff if (client->opt != NULL) {
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff INSIST(dns_rdataset_isassociated(client->opt));
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff dns_rdataset_disassociate(client->opt);
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff dns_message_puttemprdataset(client->message, &client->opt);
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff }
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff client->udpsize = 512;
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff dns_message_reset(client->message, DNS_MESSAGE_INTENTPARSE);
528829aa8ad69238e674cd81078bc14d4199691bMichael Graff if (client->dispevent != NULL) {
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff dns_dispatch_freeevent(client->dispatch, client->dispentry,
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff &client->dispevent);
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff } else if (TCP_CLIENT(client)) {
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff if (result == ISC_R_SUCCESS)
6e49e91bd08778d7eae45a2229dcf41ed97cc636David Lawrence client_read(client);
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff else {
6e49e91bd08778d7eae45a2229dcf41ed97cc636David Lawrence if (client->tcpsocket != NULL) {
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff /*
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff * XXXAG Destroying the tcpmsg here
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff * looks bogus - it may still get events.
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff */
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff INSIST(client->tcpmsg_valid == ISC_TRUE);
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff dns_tcpmsg_invalidate(&client->tcpmsg);
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff client->tcpmsg_valid = ISC_FALSE;
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff isc_socket_detach(&client->tcpsocket);
cebd4498636d3d480f6f2a7aa2eb72bd2ed64010Michael Graff }
cebd4498636d3d480f6f2a7aa2eb72bd2ed64010Michael Graff client_accept(client);
cebd4498636d3d480f6f2a7aa2eb72bd2ed64010Michael Graff }
cebd4498636d3d480f6f2a7aa2eb72bd2ed64010Michael Graff }
1a0e33bc2044e1902493111db14cbf793083ac47Michael Graff}
1a0e33bc2044e1902493111db14cbf793083ac47Michael Graff
897c9ddb4d745b2bfecf98b17e5487bb6656299aMichael Graffstatic void
528829aa8ad69238e674cd81078bc14d4199691bMichael Graffclient_senddone(isc_task_t *task, isc_event_t *event) {
6a68df334f6dd02429d3e7a8866e552e9e9d84ecBob Halley ns_client_t *client;
ff9bb3fc5453bbf310b67c560fbf04a5c0fb60daMichael Graff isc_socketevent_t *sevent = (isc_socketevent_t *)event;
c803787146cadcb2d7e10cbf4491f3be513dfa1aMichael Graff
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff REQUIRE(sevent != NULL);
c803787146cadcb2d7e10cbf4491f3be513dfa1aMichael Graff REQUIRE(sevent->type == ISC_SOCKEVENT_SENDDONE);
2bcb48cfcae36398454c98e40c563e2cde748e07Michael Graff client = sevent->arg;
2bcb48cfcae36398454c98e40c563e2cde748e07Michael Graff REQUIRE(NS_CLIENT_VALID(client));
c803787146cadcb2d7e10cbf4491f3be513dfa1aMichael Graff REQUIRE(task == client->task);
2bcb48cfcae36398454c98e40c563e2cde748e07Michael Graff
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff CTRACE("senddone");
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff INSIST(client->nsends > 0);
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff client->nsends--;
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff isc_mempool_put(client->sendbufs, sevent->region.base);
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff isc_event_free(&event);
ff9bb3fc5453bbf310b67c560fbf04a5c0fb60daMichael Graff
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff /*
da547174e2b7beb6d6119d58197ad0bc85b91179Michael Graff * If all of its sendbufs buffers were busy, the client might be
da547174e2b7beb6d6119d58197ad0bc85b91179Michael Graff * waiting for one to become available.
da547174e2b7beb6d6119d58197ad0bc85b91179Michael Graff */
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff if (client->waiting_for_bufs == ISC_TRUE) {
86944a4c8002e80ae9b6eb5a5e29b797879be45fMichael Graff client->waiting_for_bufs = ISC_FALSE;
86944a4c8002e80ae9b6eb5a5e29b797879be45fMichael Graff ns_client_send(client);
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff return;
58aaab3687aac838542ee4ef65a9c094a5d34ab0Michael Graff }
f00d96a15cdd11e764437f9359e67328631caaeaMichael Graff /* XXXRTH need to add exit draining mode. */
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff}
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graffvoid
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graffns_client_send(ns_client_t *client) {
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff isc_result_t result;
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff unsigned char *data;
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff isc_buffer_t buffer;
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff isc_buffer_t tcpbuffer;
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff isc_region_t r;
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff isc_socket_t *socket;
2bcb48cfcae36398454c98e40c563e2cde748e07Michael Graff isc_sockaddr_t *address;
2bcb48cfcae36398454c98e40c563e2cde748e07Michael Graff unsigned int bufsize = 512;
2bcb48cfcae36398454c98e40c563e2cde748e07Michael Graff
2bcb48cfcae36398454c98e40c563e2cde748e07Michael Graff REQUIRE(NS_CLIENT_VALID(client));
2bcb48cfcae36398454c98e40c563e2cde748e07Michael Graff
e198cb953c1a5bc189ae21dc3f8d622f5a08bc34Bob Halley CTRACE("send");
2bcb48cfcae36398454c98e40c563e2cde748e07Michael Graff
e198cb953c1a5bc189ae21dc3f8d622f5a08bc34Bob Halley if ((client->attributes & NS_CLIENTATTR_RA) != 0)
2bcb48cfcae36398454c98e40c563e2cde748e07Michael Graff client->message->flags |= DNS_MESSAGEFLAG_RA;
ff9bb3fc5453bbf310b67c560fbf04a5c0fb60daMichael Graff
ff9bb3fc5453bbf310b67c560fbf04a5c0fb60daMichael Graff data = isc_mempool_get(client->sendbufs);
ff9bb3fc5453bbf310b67c560fbf04a5c0fb60daMichael Graff if (data == NULL) {
ff9bb3fc5453bbf310b67c560fbf04a5c0fb60daMichael Graff CTRACE("no buffers available");
ff9bb3fc5453bbf310b67c560fbf04a5c0fb60daMichael Graff if (client->nsends > 0) {
ff9bb3fc5453bbf310b67c560fbf04a5c0fb60daMichael Graff /*
ff9bb3fc5453bbf310b67c560fbf04a5c0fb60daMichael Graff * We couldn't get memory, but there is at least one
e198cb953c1a5bc189ae21dc3f8d622f5a08bc34Bob Halley * send outstanding. We arrange to be restarted when a
ff9bb3fc5453bbf310b67c560fbf04a5c0fb60daMichael Graff * send completes.
ff9bb3fc5453bbf310b67c560fbf04a5c0fb60daMichael Graff */
ff9bb3fc5453bbf310b67c560fbf04a5c0fb60daMichael Graff CTRACE("waiting");
ff9bb3fc5453bbf310b67c560fbf04a5c0fb60daMichael Graff client->waiting_for_bufs = ISC_TRUE;
e198cb953c1a5bc189ae21dc3f8d622f5a08bc34Bob Halley } else
ff9bb3fc5453bbf310b67c560fbf04a5c0fb60daMichael Graff ns_client_next(client, ISC_R_NOMEMORY);
ff9bb3fc5453bbf310b67c560fbf04a5c0fb60daMichael Graff return;
ff9bb3fc5453bbf310b67c560fbf04a5c0fb60daMichael Graff }
ff9bb3fc5453bbf310b67c560fbf04a5c0fb60daMichael Graff
ff9bb3fc5453bbf310b67c560fbf04a5c0fb60daMichael Graff /*
ff9bb3fc5453bbf310b67c560fbf04a5c0fb60daMichael Graff * XXXRTH The following doesn't deal with TSIGs, TCP buffer resizing,
ff9bb3fc5453bbf310b67c560fbf04a5c0fb60daMichael Graff * or ENDS1 more data packets.
efc8a09e19dfcfafa92fd2ad113073a4f5295e9bMichael Graff */
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff if (TCP_CLIENT(client)) {
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff /*
738b9aa3ded1ef724922d6695cb04ec2e721bdd1Bob Halley * XXXRTH "tcpbuffer" is a hack to get things working.
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff */
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff isc_buffer_init(&tcpbuffer, data, SEND_BUFFER_SIZE,
738b9aa3ded1ef724922d6695cb04ec2e721bdd1Bob Halley ISC_BUFFERTYPE_BINARY);
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff isc_buffer_init(&buffer, data + 2, SEND_BUFFER_SIZE - 2,
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff ISC_BUFFERTYPE_BINARY);
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff } else {
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff if (client->udpsize < SEND_BUFFER_SIZE)
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff bufsize = client->udpsize;
efc8a09e19dfcfafa92fd2ad113073a4f5295e9bMichael Graff else
29f28fe573d4b3b318b3b026d567c1eb86738015Michael Graff bufsize = SEND_BUFFER_SIZE;
ff9bb3fc5453bbf310b67c560fbf04a5c0fb60daMichael Graff isc_buffer_init(&buffer, data, bufsize, ISC_BUFFERTYPE_BINARY);
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff }
29f28fe573d4b3b318b3b026d567c1eb86738015Michael Graff
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff result = dns_message_renderbegin(client->message, &buffer);
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff if (result != ISC_R_SUCCESS)
29f28fe573d4b3b318b3b026d567c1eb86738015Michael Graff goto done;
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff if (client->opt != NULL) {
cebd4498636d3d480f6f2a7aa2eb72bd2ed64010Michael Graff result = dns_message_setopt(client->message, client->opt);
cebd4498636d3d480f6f2a7aa2eb72bd2ed64010Michael Graff if (result != ISC_R_SUCCESS)
cebd4498636d3d480f6f2a7aa2eb72bd2ed64010Michael Graff goto done;
cebd4498636d3d480f6f2a7aa2eb72bd2ed64010Michael Graff /*
1c3bc66ada38236cc81c41b7174a9f0a872c9ab6Michael Graff * XXXRTH dns_message_setopt() should probably do this...
1c3bc66ada38236cc81c41b7174a9f0a872c9ab6Michael Graff */
1c3bc66ada38236cc81c41b7174a9f0a872c9ab6Michael Graff client->opt = NULL;
528829aa8ad69238e674cd81078bc14d4199691bMichael Graff }
f942258b6380ba1f2c2f057a79ffc37bc3436488Michael Graff result = dns_message_rendersection(client->message,
f942258b6380ba1f2c2f057a79ffc37bc3436488Michael Graff DNS_SECTION_QUESTION, 0);
f942258b6380ba1f2c2f057a79ffc37bc3436488Michael Graff if (result != ISC_R_SUCCESS)
f942258b6380ba1f2c2f057a79ffc37bc3436488Michael Graff goto done;
f942258b6380ba1f2c2f057a79ffc37bc3436488Michael Graff result = dns_message_rendersection(client->message,
1a0e33bc2044e1902493111db14cbf793083ac47Michael Graff DNS_SECTION_ANSWER, 0);
f942258b6380ba1f2c2f057a79ffc37bc3436488Michael Graff if (result == ISC_R_NOSPACE) {
f942258b6380ba1f2c2f057a79ffc37bc3436488Michael Graff client->message->flags |= DNS_MESSAGEFLAG_TC;
f942258b6380ba1f2c2f057a79ffc37bc3436488Michael Graff goto renderend;
1a0e33bc2044e1902493111db14cbf793083ac47Michael Graff }
f942258b6380ba1f2c2f057a79ffc37bc3436488Michael Graff if (result != ISC_R_SUCCESS)
f942258b6380ba1f2c2f057a79ffc37bc3436488Michael Graff goto done;
f942258b6380ba1f2c2f057a79ffc37bc3436488Michael Graff result = dns_message_rendersection(client->message,
f942258b6380ba1f2c2f057a79ffc37bc3436488Michael Graff DNS_SECTION_AUTHORITY, 0);
f942258b6380ba1f2c2f057a79ffc37bc3436488Michael Graff if (result == ISC_R_NOSPACE) {
f942258b6380ba1f2c2f057a79ffc37bc3436488Michael Graff client->message->flags |= DNS_MESSAGEFLAG_TC;
f942258b6380ba1f2c2f057a79ffc37bc3436488Michael Graff goto renderend;
f942258b6380ba1f2c2f057a79ffc37bc3436488Michael Graff }
f942258b6380ba1f2c2f057a79ffc37bc3436488Michael Graff if (result != ISC_R_SUCCESS)
f942258b6380ba1f2c2f057a79ffc37bc3436488Michael Graff goto done;
f942258b6380ba1f2c2f057a79ffc37bc3436488Michael Graff result = dns_message_rendersection(client->message,
f942258b6380ba1f2c2f057a79ffc37bc3436488Michael Graff DNS_SECTION_ADDITIONAL, 0);
78854e02c127f31ab90f56da0531542004b45377Michael Graff if (result != ISC_R_SUCCESS && result != ISC_R_NOSPACE)
970cccf46e0f949a1a9edbcd8302dd2a112b43c2Michael Graff goto done;
29f28fe573d4b3b318b3b026d567c1eb86738015Michael Graff renderend:
29f28fe573d4b3b318b3b026d567c1eb86738015Michael Graff result = dns_message_renderend(client->message);
29f28fe573d4b3b318b3b026d567c1eb86738015Michael Graff if (result != ISC_R_SUCCESS)
86944a4c8002e80ae9b6eb5a5e29b797879be45fMichael Graff goto done;
86944a4c8002e80ae9b6eb5a5e29b797879be45fMichael Graff
86944a4c8002e80ae9b6eb5a5e29b797879be45fMichael Graff if (TCP_CLIENT(client)) {
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff socket = client->tcpsocket;
30251e07d1705d1a85b0e1d5a969496e1aed612eMichael Graff address = NULL;
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff isc_buffer_used(&buffer, &r);
30251e07d1705d1a85b0e1d5a969496e1aed612eMichael Graff isc_buffer_putuint16(&tcpbuffer, (isc_uint16_t)r.length);
30251e07d1705d1a85b0e1d5a969496e1aed612eMichael Graff isc_buffer_add(&tcpbuffer, r.length);
2bcb48cfcae36398454c98e40c563e2cde748e07Michael Graff isc_buffer_used(&tcpbuffer, &r);
1c3bc66ada38236cc81c41b7174a9f0a872c9ab6Michael Graff } else {
d4d2a13916a114879763562db6a19b70b1444ec1Michael Graff socket = dns_dispatch_getsocket(client->dispatch);
ff9bb3fc5453bbf310b67c560fbf04a5c0fb60daMichael Graff address = &client->dispevent->addr;
efc8a09e19dfcfafa92fd2ad113073a4f5295e9bMichael Graff isc_buffer_used(&buffer, &r);
efc8a09e19dfcfafa92fd2ad113073a4f5295e9bMichael Graff }
efc8a09e19dfcfafa92fd2ad113073a4f5295e9bMichael Graff CTRACE("sendto");
a9ece9973c35d4d780338e89e288fb6a59575324Michael Graff result = isc_socket_sendto(socket, &r, client->task, client_senddone,
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff client, address, NULL);
3ac63b472022ff92691d1fe69ac715a729671965Michael Graff if (result == ISC_R_SUCCESS)
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff client->nsends++;
2bcb48cfcae36398454c98e40c563e2cde748e07Michael Graff
2bcb48cfcae36398454c98e40c563e2cde748e07Michael Graff done:
2bcb48cfcae36398454c98e40c563e2cde748e07Michael Graff if (result != ISC_R_SUCCESS)
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff isc_mempool_put(client->sendbufs, data);
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff
910df98b0efcbe8380b952887f4071051cc39a25Michael Graff ns_client_next(client, result);
86944a4c8002e80ae9b6eb5a5e29b797879be45fMichael Graff}
86944a4c8002e80ae9b6eb5a5e29b797879be45fMichael Graff
910df98b0efcbe8380b952887f4071051cc39a25Michael Graffvoid
910df98b0efcbe8380b952887f4071051cc39a25Michael Graffns_client_error(ns_client_t *client, isc_result_t result) {
dns_rcode_t rcode;
dns_message_t *message;
REQUIRE(NS_CLIENT_VALID(client));
CTRACE("error");
message = client->message;
rcode = dns_result_torcode(result);
/*
* message may be an in-progress reply that we had trouble
* with, in which case QR will be set. We need to clear QR before
* calling dns_message_reply() to avoid triggering an assertion.
*/
message->flags &= ~DNS_MESSAGEFLAG_QR;
/*
* AA and AD shouldn't be set.
*/
message->flags &= ~(DNS_MESSAGEFLAG_AA | DNS_MESSAGEFLAG_AD);
result = dns_message_reply(message, ISC_TRUE);
if (result != ISC_R_SUCCESS) {
/*
* It could be that we've got a query with a good header,
* but a bad question section, so we try again with
* want_question_section set to ISC_FALSE.
*/
result = dns_message_reply(message, ISC_FALSE);
if (result != ISC_R_SUCCESS) {
/*
* There's no hope of replying to this request.
*
* XXXRTH Mark this client to that if it is a
* TCP session, the session will be closed.
*/
ns_client_next(client, result);
return;
}
}
message->rcode = rcode;
ns_client_send(client);
}
static inline isc_result_t
client_addopt(ns_client_t *client) {
dns_rdataset_t *rdataset;
dns_rdatalist_t *rdatalist;
dns_rdata_t *rdata;
isc_result_t result;
REQUIRE(client->opt == NULL); /* XXXRTH free old. */
rdatalist = NULL;
result = dns_message_gettemprdatalist(client->message, &rdatalist);
if (result != ISC_R_SUCCESS)
return (result);
rdata = NULL;
result = dns_message_gettemprdata(client->message, &rdata);
if (result != ISC_R_SUCCESS)
return (result);
rdataset = NULL;
result = dns_message_gettemprdataset(client->message, &rdataset);
if (result != ISC_R_SUCCESS)
return (result);
dns_rdataset_init(rdataset);
rdatalist->type = dns_rdatatype_opt;
rdatalist->covers = 0;
/*
* Set Maximum UDP buffer size.
*/
rdatalist->rdclass = SEND_BUFFER_SIZE;
/*
* Set EXTENDED-RCODE, VERSION, and Z to 0.
*/
rdatalist->ttl = 0;
/*
* No ENDS options.
*/
rdata->data = NULL;
rdata->length = 0;
ISC_LIST_INIT(rdatalist->rdata);
ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
dns_rdatalist_tordataset(rdatalist, rdataset);
client->opt = rdataset;
return (ISC_R_SUCCESS);
}
/*
* Handle an incoming request event from the dispatch (UDP case)
* or tcpmsg (TCP case).
*/
static void
client_request(isc_task_t *task, isc_event_t *event) {
ns_client_t *client;
dns_dispatchevent_t *devent;
isc_result_t result;
isc_buffer_t *buffer;
dns_view_t *view;
dns_rdataset_t *opt;
isc_boolean_t ra; /* Recursion available. */
REQUIRE(event != NULL);
client = event->arg;
REQUIRE(NS_CLIENT_VALID(client));
REQUIRE(task == client->task);
if (event->type == DNS_EVENT_DISPATCH) {
devent = (dns_dispatchevent_t *)event;
REQUIRE(client->dispentry != NULL);
client->dispevent = devent;
buffer = &devent->buffer;
result = devent->result;
} else {
REQUIRE(event->type == DNS_EVENT_TCPMSG);
REQUIRE(event->sender == &client->tcpmsg);
buffer = &client->tcpmsg.buffer;
result = client->tcpmsg.result;
INSIST(client->nreads == 1);
client->nreads--;
}
CTRACE("request");
if (client->shuttingdown) {
maybe_free(client);
return;
}
isc_stdtime_get(&client->requesttime);
client->now = client->requesttime;
if (result != ISC_R_SUCCESS) {
if (TCP_CLIENT(client))
ns_client_next(client, result);
else
isc_task_shutdown(client->task);
return;
}
result = dns_message_parse(client->message, buffer, ISC_FALSE);
if (result != ISC_R_SUCCESS) {
ns_client_error(client, result);
return;
}
/*
* We expect a query, not a response. Unexpected UDP responses
* are discarded early by the dispatcher, but TCP responses
* bypass the dispatcher and must be discarded here.
*/
if ((client->message->flags & DNS_MESSAGEFLAG_QR) != 0) {
CTRACE("unexpected response");
ns_client_next(client, DNS_R_FORMERR);
return;
}
/*
* Deal with EDNS.
*/
opt = dns_message_getopt(client->message);
if (opt != NULL) {
unsigned int version;
/*
* Set the client's UDP buffer size.
*/
client->udpsize = opt->rdclass;
/*
* Create an OPT for our reply.
*/
result = client_addopt(client);
if (result != ISC_R_SUCCESS) {
ns_client_error(client, result);
return;
}
/*
* Do we understand this version of ENDS?
*
* XXXRTH need library support for this!
*/
version = (opt->ttl & 0x00FF0000) >> 16;
if (version != 0) {
ns_client_error(client, DNS_R_BADVERS);
return;
}
}
/*
* Check for a signature. We log bad signatures regardless of
* whether they ultimately cause the request to be rejected or
* not. We do not log the lack of a signature unless we are
* debugging.
*/
client->signer = NULL;
result = dns_message_signer(client->message, &client->signername);
if (result == DNS_R_SUCCESS) {
isc_log_write(dns_lctx, DNS_LOGCATEGORY_SECURITY,
NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
"request has valid signature");
client->signer = &client->signername;
} else if (result == DNS_R_NOTFOUND) {
isc_log_write(dns_lctx, DNS_LOGCATEGORY_SECURITY,
NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
"request is not signed");
} else {
/* There is a signature, but it is bad. */
isc_log_write(dns_lctx, DNS_LOGCATEGORY_SECURITY,
NS_LOGMODULE_CLIENT, ISC_LOG_ERROR,
"request has invalid signature: %s",
isc_result_totext(result));
}
/*
* XXXRTH View list management code will be moving to its own module
* soon.
*/
RWLOCK(&ns_g_server->viewlock, isc_rwlocktype_read);
for (view = ISC_LIST_HEAD(ns_g_server->viewlist);
view != NULL;
view = ISC_LIST_NEXT(view, link)) {
/*
* XXXRTH View matching will become more powerful later.
*/
if (client->message->rdclass == view->rdclass) {
dns_view_attach(view, &client->view);
break;
}
}
RWUNLOCK(&ns_g_server->viewlock, isc_rwlocktype_read);
if (view == NULL) {
CTRACE("no view");
ns_client_error(client, DNS_R_REFUSED);
return;
}
/*
* Decide whether recursive service is available to this client.
* We do this here rather than in the query code so that we can
* set the RA bit correctly on all kinds of responses, not just
* responses to ordinary queries.
*/
if (client->view->resolver == NULL) {
ra = ISC_FALSE;
} else {
ra = ISC_TRUE;
if (ns_g_server->recursion == ISC_TRUE) {
/* XXX ACL should be view specific. */
/* XXX this will log too much too early */
result = dns_acl_checkrequest(client->signer,
ns_client_getsockaddr(client),
"recursion",
ns_g_server->recursionacl,
NULL, ISC_TRUE);
if (result != DNS_R_SUCCESS)
ra = ISC_FALSE;
}
}
if (ra == ISC_TRUE)
client->attributes |= NS_CLIENTATTR_RA;
/*
* Dispatch the request.
*/
switch (client->message->opcode) {
case dns_opcode_query:
CTRACE("query");
ns_query_start(client);
break;
case dns_opcode_update:
CTRACE("update");
ns_update_start(client);
break;
case dns_opcode_notify:
CTRACE("notify");
ns_notify_start(client);
break;
case dns_opcode_iquery:
CTRACE("iquery");
ns_client_error(client, DNS_R_NOTIMP);
default:
CTRACE("unknown opcode");
ns_client_error(client, DNS_R_NOTIMP);
}
}
static void
client_timeout(isc_task_t *task, isc_event_t *event) {
ns_client_t *client;
REQUIRE(event != NULL);
REQUIRE(event->type == ISC_TIMEREVENT_LIFE ||
event->type == ISC_TIMEREVENT_IDLE);
client = event->arg;
REQUIRE(NS_CLIENT_VALID(client));
REQUIRE(task == client->task);
REQUIRE(client->timer != NULL);
CTRACE("timeout");
isc_event_free(&event);
ns_client_next(client, ISC_R_TIMEDOUT);
}
static isc_result_t
client_create(ns_clientmgr_t *manager, ns_clienttype_t type,
ns_client_t **clientp)
{
ns_client_t *client;
isc_result_t result;
/*
* Caller must be holding the manager lock.
*
* Note: creating a client does not add the client to the
* manager's client list or set the client's manager pointer.
* The caller is responsible for that.
*/
REQUIRE(clientp != NULL && *clientp == NULL);
client = isc_mem_get(manager->mctx, sizeof *client);
if (client == NULL)
return (ISC_R_NOMEMORY);
client->task = NULL;
result = isc_task_create(manager->taskmgr, manager->mctx, 0,
&client->task);
if (result != ISC_R_SUCCESS)
goto cleanup_client;
result = isc_task_onshutdown(client->task, client_shutdown, client);
if (result != ISC_R_SUCCESS)
goto cleanup_task;
client->timer = NULL;
result = isc_timer_create(manager->timermgr, isc_timertype_inactive,
NULL, NULL, client->task, client_timeout,
client, &client->timer);
if (result != ISC_R_SUCCESS)
goto cleanup_task;
client->message = NULL;
result = dns_message_create(manager->mctx, DNS_MESSAGE_INTENTPARSE,
&client->message);
if (result != ISC_R_SUCCESS)
goto cleanup_timer;
/* XXXRTH Hardwired constants */
client->sendbufs = NULL;
result = isc_mempool_create(manager->mctx, SEND_BUFFER_SIZE,
&client->sendbufs);
if (result != ISC_R_SUCCESS)
goto cleanup_message;
isc_mempool_setfreemax(client->sendbufs, 3);
isc_mempool_setmaxalloc(client->sendbufs, 3);
client->magic = NS_CLIENT_MAGIC;
client->mctx = manager->mctx;
client->manager = NULL;
client->type = type;
client->shuttingdown = ISC_FALSE;
client->waiting_for_bufs = ISC_FALSE;
client->naccepts = 0;
client->nreads = 0;
client->nsends = 0;
client->nwaiting = 0;
client->attributes = 0;
client->view = NULL;
client->dispatch = NULL;
client->dispentry = NULL;
client->dispevent = NULL;
client->tcplistener = NULL;
client->tcpsocket = NULL;
client->tcpmsg_valid = ISC_FALSE;
client->opt = NULL;
client->udpsize = 512;
client->next = NULL;
dns_name_init(&client->signername, NULL);
ISC_LINK_INIT(client, link);
/*
* We call the init routines for the various kinds of client here,
* after we have created an otherwise valid client, because some
* of them call routines that REQUIRE(NS_CLIENT_VALID(client)).
*/
result = ns_query_init(client);
if (result != ISC_R_SUCCESS)
goto cleanup_sendbufs;
CTRACE("create");
*clientp = client;
return (ISC_R_SUCCESS);
cleanup_sendbufs:
isc_mempool_destroy(&client->sendbufs);
client->magic = 0;
cleanup_message:
dns_message_destroy(&client->message);
cleanup_timer:
isc_timer_detach(&client->timer);
cleanup_task:
isc_task_detach(&client->task);
cleanup_client:
isc_mem_put(manager->mctx, client, sizeof *client);
return (result);
}
static void
client_read(ns_client_t *client) {
isc_result_t result;
CTRACE("read");
result = dns_tcpmsg_readmessage(&client->tcpmsg, client->task,
client_request, client);
if (result != ISC_R_SUCCESS)
ns_client_next(client, result);
INSIST(client->nreads == 0);
client->nreads++;
}
static void
client_newconn(isc_task_t *task, isc_event_t *event) {
ns_client_t *client = event->arg;
isc_socket_newconnev_t *nevent = (isc_socket_newconnev_t *)event;
REQUIRE(event->type == ISC_SOCKEVENT_NEWCONN);
REQUIRE(NS_CLIENT_VALID(client));
REQUIRE(client->task == task);
CTRACE("newconn");
INSIST(client->naccepts == 1);
client->naccepts--;
if (client->shuttingdown) {
maybe_free(client);
} else if (nevent->result == ISC_R_SUCCESS) {
client->tcpsocket = nevent->newsocket;
INSIST(client->tcpmsg_valid == ISC_FALSE);
dns_tcpmsg_init(client->mctx, client->tcpsocket,
&client->tcpmsg);
client->tcpmsg_valid = ISC_TRUE;
client_read(client);
} else {
/*
* XXXRTH What should we do? We're trying to accept but
* it didn't work. If we just give up, then TCP
* service may eventually stop.
*
* For now, we just go idle.
*
* Going idle is probably the right thing if the
* I/O was canceled.
*/
}
isc_event_free(&event);
}
static void
client_accept(ns_client_t *client) {
isc_result_t result;
CTRACE("accept");
result = isc_socket_accept(client->tcplistener, client->task,
client_newconn, client);
if (result != ISC_R_SUCCESS) {
UNEXPECTED_ERROR(__FILE__, __LINE__,
"isc_socket_accept() failed: %s",
isc_result_totext(result));
/*
* XXXRTH What should we do? We're trying to accept but
* it didn't work. If we just give up, then TCP
* service may eventually stop.
*
* For now, we just go idle.
*/
return;
}
INSIST(client->naccepts == 0);
client->naccepts++;
}
void
ns_client_wait(ns_client_t *client) {
client->nwaiting++;
}
isc_boolean_t
ns_client_shuttingdown(ns_client_t *client) {
return (client->shuttingdown);
}
void
ns_client_unwait(ns_client_t *client) {
client->nwaiting--;
INSIST(client->nwaiting >= 0);
if (client->shuttingdown)
maybe_free(client);
}
/***
*** Client Manager
***/
static void
clientmgr_destroy(ns_clientmgr_t *manager) {
REQUIRE(manager->nclients == 0);
REQUIRE(ISC_LIST_EMPTY(manager->clients));
MTRACE("clientmgr_destroy");
manager->magic = 0;
isc_mem_put(manager->mctx, manager, sizeof *manager);
}
isc_result_t
ns_clientmgr_create(isc_mem_t *mctx, isc_taskmgr_t *taskmgr,
isc_timermgr_t *timermgr, ns_clientmgr_t **managerp)
{
ns_clientmgr_t *manager;
isc_result_t result;
manager = isc_mem_get(mctx, sizeof *manager);
if (manager == NULL)
return (ISC_R_NOMEMORY);
result = isc_mutex_init(&manager->lock);
if (result != ISC_R_SUCCESS)
goto cleanup_manager;
manager->mctx = mctx;
manager->taskmgr = taskmgr;
manager->timermgr = timermgr;
manager->exiting = ISC_FALSE;
manager->nclients = 0;
ISC_LIST_INIT(manager->clients);
manager->magic = MANAGER_MAGIC;
MTRACE("create");
*managerp = manager;
return (ISC_R_SUCCESS);
cleanup_manager:
isc_mem_put(manager->mctx, manager, sizeof *manager);
return (result);
}
void
ns_clientmgr_destroy(ns_clientmgr_t **managerp) {
ns_clientmgr_t *manager;
ns_client_t *client;
isc_boolean_t need_destroy = ISC_FALSE;
REQUIRE(managerp != NULL);
manager = *managerp;
REQUIRE(VALID_MANAGER(manager));
MTRACE("destroy");
LOCK(&manager->lock);
manager->exiting = ISC_TRUE;
for (client = ISC_LIST_HEAD(manager->clients);
client != NULL;
client = ISC_LIST_NEXT(client, link))
isc_task_shutdown(client->task);
if (ISC_LIST_EMPTY(manager->clients))
need_destroy = ISC_TRUE;
UNLOCK(&manager->lock);
if (need_destroy)
clientmgr_destroy(manager);
*managerp = NULL;
}
isc_result_t
ns_clientmgr_addtodispatch(ns_clientmgr_t *manager, unsigned int n,
dns_dispatch_t *dispatch)
{
isc_result_t result = ISC_R_SUCCESS;
unsigned int i;
ns_client_t *client;
REQUIRE(VALID_MANAGER(manager));
REQUIRE(n > 0);
MTRACE("addtodispatch");
/*
* We MUST lock the manager lock for the entire client creation
* process. If we didn't do this, then a client could get a
* shutdown event and disappear out from under us.
*/
LOCK(&manager->lock);
for (i = 0; i < n; i++) {
client = NULL;
result = client_create(manager, ns_clienttype_basic,
&client);
if (result != ISC_R_SUCCESS)
break;
dns_dispatch_attach(dispatch, &client->dispatch);
result = dns_dispatch_addrequest(dispatch, client->task,
client_request,
client, &client->dispentry);
if (result != ISC_R_SUCCESS) {
client->shuttingdown = ISC_TRUE;
maybe_free(client); /* Will free immediately. */
break;
}
client->manager = manager;
ISC_LIST_APPEND(manager->clients, client, link);
manager->nclients++;
}
if (i != 0) {
/*
* We managed to create at least one client, so we
* declare victory.
*/
result = ISC_R_SUCCESS;
}
UNLOCK(&manager->lock);
return (result);
}
isc_result_t
ns_clientmgr_accepttcp(ns_clientmgr_t *manager, isc_socket_t *socket,
unsigned int n)
{
isc_result_t result = ISC_R_SUCCESS;
unsigned int i;
ns_client_t *client;
REQUIRE(VALID_MANAGER(manager));
REQUIRE(n > 0);
MTRACE("accepttcp");
/*
* XXXRTH
*
* This does not represent the planned method for TCP support,
* because we are dedicating a few clients to servicing TCP requests
* instead of allocating TCP clients from a pool and applying quotas.
*
* All this will be fixed later, but this code will allow parts of
* the server that need TCP support, e.g. IXFR and AXFR, to progress.
*/
/*
* We MUST lock the manager lock for the entire client creation
* process. If we didn't do this, then a client could get a
* shutdown event and disappear out from under us.
*/
LOCK(&manager->lock);
for (i = 0; i < n; i++) {
client = NULL;
result = client_create(manager, ns_clienttype_tcp, &client);
if (result != ISC_R_SUCCESS)
break;
client->attributes |= NS_CLIENTATTR_TCP;
isc_socket_attach(socket, &client->tcplistener);
client_accept(client);
client->manager = manager;
ISC_LIST_APPEND(manager->clients, client, link);
manager->nclients++;
}
if (i != 0) {
/*
* We managed to create at least one client, so we
* declare victory.
*/
result = ISC_R_SUCCESS;
}
UNLOCK(&manager->lock);
return (result);
}
isc_sockaddr_t *
ns_client_getsockaddr(ns_client_t *client) {
if (TCP_CLIENT(client))
return (&client->tcpmsg.address);
else
return (&client->dispevent->addr);
}