client.c revision d468db05b3aea6d745ee188258e59da11510d637
/*
* 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>
#include <dns/dispatch.h>
#include <dns/rdatalist.h>
#include <dns/rdataset.h>
#include <named/interfacemgr.h>
#define NS_CLIENT_TRACE
#ifdef NS_CLIENT_TRACE
ISC_LOG_DEBUG(3), \
"client %p: %s", client, (m))
ISC_LOG_DEBUG(3), \
"clientmgr %p: %s", manager, (m))
#endif
#define SEND_BUFFER_SIZE 2048
struct ns_clientmgr {
/* Unlocked. */
unsigned int magic;
/* Locked by lock. */
unsigned int nclients;
};
#define VALID_MANAGER(m) ((m) != NULL && \
(m)->magic == MANAGER_MAGIC)
/***
*** Client
***/
/*
* Important note!
*
* All client state changes, other than that from idle to listening, occur
* as a result of events. This guarantees serialization and avoids the
* need for locking.
*
* If a routine is ever created that allows someone other than the client's
* task to change the client, then the client will have to be locked.
*/
static void
}
/*
* Free a client immediately if possible, otherwise start
* shutting it down and postpone freeing to later.
*/
static void
/*
* When "shuttingdown" is true, either the task has received
* its shutdown event or no shutdown event has ever been
* set up. Thus, we have no outstanding shutdown
* event at this point.
*/
if (TCP_CLIENT(client))
else
}
/* Still waiting for events. */
return;
}
/* We have received our last event. */
else
deventp);
}
}
if (client->tcpmsg_valid)
}
CTRACE("free");
}
/*
* The client's task has received a shutdown event.
*/
static void
CTRACE("shutdown");
}
void
CTRACE("next");
}
/*
* XXXRTH If result != ISC_R_SUCCESS:
* Log result if there is interest in doing so.
*/
}
/*
* This client object is supposed to die now, but if we
* have fewer client objects than planned due to
* quota exhaustion, don't.
*/
if (TCP_CLIENT(client)) {
} else {
/*
* The UDP client quota is enforced by making
* requests fail rather than by not listening
* for new ones. Therefore, there is always a
* full set of UDP clients listening.
*/
}
if (! need_another_client) {
/* XXX should put in "idle client pool" instead. */
return;
}
}
/*
* Give the processed dispatch event back to the dispatch.
* This tells the dispatch that we are ready to receive
* the next event.
*/
} else if (TCP_CLIENT(client)) {
if (result == ISC_R_SUCCESS) {
} else {
/*
* There was an error processing a TCP request.
* It may have have left the connection out of
* sync. Close the connection and listen for a
* new one.
*/
/*
* There should be no outstanding read
* request on the TCP socket at this point,
* therefore invalidating the tcpmsg is safe.
*/
}
}
}
}
static void
CTRACE("senddone");
/*
* If all of its sendbufs buffers were busy, the client might be
* waiting for one to become available.
*/
return;
}
/* XXXRTH need to add exit draining mode. */
}
void
unsigned char *data;
isc_region_t r;
unsigned int bufsize = 512;
CTRACE("send");
CTRACE("no buffers available");
/*
* We couldn't get memory, but there is at least one
* send outstanding. We arrange to be restarted when a
* send completes.
*/
CTRACE("waiting");
} else
return;
}
/*
* XXXRTH The following doesn't deal with TSIGs, TCP buffer resizing,
* or ENDS1 more data packets.
*/
if (TCP_CLIENT(client)) {
/*
* XXXRTH "tcpbuffer" is a hack to get things working.
*/
} else {
else
}
if (result != ISC_R_SUCCESS)
goto done;
if (result != ISC_R_SUCCESS)
goto done;
/*
* XXXRTH dns_message_setopt() should probably do this...
*/
}
DNS_SECTION_QUESTION, 0);
if (result != ISC_R_SUCCESS)
goto done;
DNS_SECTION_ANSWER, 0);
if (result == ISC_R_NOSPACE) {
goto renderend;
}
if (result != ISC_R_SUCCESS)
goto done;
if (result == ISC_R_NOSPACE) {
goto renderend;
}
if (result != ISC_R_SUCCESS)
goto done;
goto done;
if (result != ISC_R_SUCCESS)
goto done;
if (TCP_CLIENT(client)) {
isc_buffer_used(&buffer, &r);
isc_buffer_used(&tcpbuffer, &r);
} else {
isc_buffer_used(&buffer, &r);
}
CTRACE("sendto");
if (result == ISC_R_SUCCESS)
done:
if (result != ISC_R_SUCCESS)
}
void
CTRACE("error");
/*
* 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.
*/
/*
* AA and AD shouldn't be set.
*/
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.
*/
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.
*/
return;
}
}
}
static inline isc_result_t
if (result != ISC_R_SUCCESS)
return (result);
if (result != ISC_R_SUCCESS)
return (result);
if (result != ISC_R_SUCCESS)
return (result);
/*
* Set Maximum UDP buffer size.
*/
/*
* Set EXTENDED-RCODE, VERSION, and Z to 0.
*/
/*
* No ENDS options.
*/
return (ISC_R_SUCCESS);
}
/*
* Handle an incoming request event from the dispatch (UDP case)
* or tcpmsg (TCP case).
*/
static void
} else {
}
CTRACE("request");
if (client->shuttingdown) {
return;
}
if (result != ISC_R_SUCCESS) {
if (TCP_CLIENT(client))
else
return;
}
if (result != ISC_R_SUCCESS) {
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.
*/
CTRACE("unexpected response");
return;
}
/*
* Deal with EDNS.
*/
unsigned int version;
/*
* Set the client's UDP buffer size.
*/
/*
* Create an OPT for our reply.
*/
if (result != ISC_R_SUCCESS) {
return;
}
/*
* Do we understand this version of ENDS?
*
* XXXRTH need library support for this!
*/
if (version != 0) {
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.
*/
if (result == DNS_R_SUCCESS) {
"request has valid signature");
} else if (result == DNS_R_NOTFOUND) {
"request is not signed");
} else {
/* There is a signature, but it is bad. */
"request has invalid signature: %s",
}
/*
* XXXRTH View list management code will be moving to its own module
* soon.
*/
/*
* XXXRTH View matching will become more powerful later.
*/
break;
}
}
CTRACE("no view");
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.
*/
} else {
/* XXX ACL should be view specific. */
/* XXX this will log too much too early */
"recursion",
if (result != DNS_R_SUCCESS)
}
}
/*
* Dispatch the request.
*/
case dns_opcode_query:
CTRACE("query");
break;
case dns_opcode_update:
CTRACE("update");
break;
case dns_opcode_notify:
CTRACE("notify");
break;
case dns_opcode_iquery:
CTRACE("iquery");
default:
CTRACE("unknown opcode");
}
}
static void
CTRACE("timeout");
}
static isc_result_t
{
/*
* 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.
*/
return (ISC_R_NOMEMORY);
if (result != ISC_R_SUCCESS)
goto cleanup_client;
if (result != ISC_R_SUCCESS)
goto cleanup_task;
if (result != ISC_R_SUCCESS)
goto cleanup_task;
if (result != ISC_R_SUCCESS)
goto cleanup_timer;
/* XXXRTH Hardwired constants */
if (result != ISC_R_SUCCESS)
goto cleanup_message;
client->attributes = 0;
/*
* 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)).
*/
if (result != ISC_R_SUCCESS)
goto cleanup_sendbufs;
CTRACE("create");
return (ISC_R_SUCCESS);
return (result);
}
static void
CTRACE("read");
if (result != ISC_R_SUCCESS)
}
static void
CTRACE("newconn");
if (client->shuttingdown) {
/*
* Let a new client take our place immediately, before
* we wait for a request packet. If we don't,
* telnetting to port 35 (once per CPU) will
* deny service to legititmate TCP clients.
*/
if (result == ISC_R_SUCCESS)
if (result != ISC_R_SUCCESS) {
"no more TCP clients: %s",
}
} 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.
*/
}
}
static void
CTRACE("accept");
if (result != ISC_R_SUCCESS) {
"isc_socket_accept() failed: %s",
/*
* 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;
}
}
void
}
return (client->shuttingdown);
}
void
if (client->shuttingdown)
}
CTRACE("replace");
(TCP_CLIENT(client) ?
if (result != ISC_R_SUCCESS)
return (result);
/*
* The responsibility for listening for new requests is hereby
* transferred to the new client. Therefore, the old client
* should refrain from listening for any more requests.
*/
return (ISC_R_SUCCESS);
}
/***
*** Client Manager
***/
static void
MTRACE("clientmgr_destroy");
}
{
return (ISC_R_NOMEMORY);
if (result != ISC_R_SUCCESS)
goto cleanup_manager;
MTRACE("create");
return (ISC_R_SUCCESS);
return (result);
}
void
MTRACE("destroy");
if (need_destroy)
}
{
unsigned int i;
REQUIRE(n > 0);
MTRACE("createclients");
/*
* 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.
*/
for (i = 0; i < n; i++) {
if (result != ISC_R_SUCCESS)
break;
if (tcp) {
} else {
if (result != ISC_R_SUCCESS) {
"dns_dispatch_addrequest() failed: %s",
break;
}
}
}
if (i != 0) {
/*
* We managed to create at least one client, so we
* declare victory.
*/
}
return (result);
}
if (TCP_CLIENT(client))
else
}