client.c revision ae51af4776c0b32780b2723a55f8ac52a5377e26
/*
* 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>
#define NS_CLIENT_TRACE
#ifdef NS_CLIENT_TRACE
#include <stdio.h>
#else
#define CTRACE(m)
#define MTRACE(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 inline void
CTRACE("free");
else
deventp);
}
}
}
static void
CTRACE("destroy");
}
static void
CTRACE("shutdown");
}
void
CTRACE("next");
}
/*
* XXXRTH If result != ISC_R_SUCCESS:
* Log result if there is interest in doing so.
*/
} else if (TCP_CLIENT(client)) {
if (result == ISC_R_SUCCESS)
else {
}
}
}
}
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;
}
DNS_SECTION_QUESTION, 0, 0);
if (result != ISC_R_SUCCESS)
goto done;
DNS_SECTION_ANSWER, 0, 0);
if (result == ISC_R_NOSPACE) {
goto renderend;
}
if (result != ISC_R_SUCCESS)
goto done;
DNS_SECTION_AUTHORITY, 0, 0);
if (result == ISC_R_NOSPACE) {
goto renderend;
}
if (result != ISC_R_SUCCESS)
goto done;
DNS_SECTION_ADDITIONAL, 0, 0);
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);
}
static void
} else {
}
CTRACE("request");
client->requesttime = 0;
if (result != ISC_R_SUCCESS) {
if (TCP_CLIENT(client))
else
return;
}
if (result != ISC_R_SUCCESS) {
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;
}
}
/*
* 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;
}
/*
* 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. 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");
} 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;
}
}
/***
*** 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("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.
*/
for (i = 0; i < n; i++) {
&client);
if (result != ISC_R_SUCCESS)
break;
if (result != ISC_R_SUCCESS) {
break;
}
}
if (i != 0) {
/*
* We managed to create at least one client, so we
* declare victory.
*/
}
return (result);
}
unsigned int n)
{
unsigned int i;
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.
*/
for (i = 0; i < n; i++) {
if (result != ISC_R_SUCCESS)
break;
}
if (i != 0) {
/*
* We managed to create at least one client, so we
* declare victory.
*/
}
return (result);
}