dispatch.c revision 04458c8b5da001294e820289b34bf3aee6258f27
/*
* 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 <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <isc/assertions.h>
#include <dns/dispatch.h>
#ifdef DISPATCH_DEBUG
#else
#define XDEBUG(x)
#endif
struct dns_dispentry {
unsigned int magic;
unsigned int bucket;
void *arg;
};
#define INVALID_BUCKET (0xffffdead)
struct dns_dispatch {
/* Unlocked. */
unsigned int magic; /* magic */
unsigned int buffersize; /* size of each buffer */
unsigned int maxrequests; /* max requests */
unsigned int maxbuffers; /* max buffers */
/* Locked. */
unsigned int refcount; /* number of users */
unsigned int recvs; /* recv() calls outstanding */
unsigned int recvs_wanted; /* recv() calls wanted */
unsigned int shutting_down : 1,
shutdown_out : 1;
unsigned int requests; /* how many requests we have */
unsigned int buffers; /* allocated buffers */
unsigned int qid_nbuckets; /* hash table size */
unsigned int qid_increment; /* id increment on collision */
};
/*
* statics.
*/
dns_messageid_t, unsigned int);
static void destroy(dns_dispatch_t *);
static inline void startrecv(dns_dispatch_t *);
static void
{
}
/*
* Return an unpredictable message ID.
*/
static inline dns_messageid_t
{
}
static dns_dispentry_t *
{
unsigned int bucket;
bucket = 0;
return (ret);
bucket++;
}
return (NULL);
}
static dns_dispentry_t *
{
unsigned int bucket;
return (ret);
return (ret);
bucket++;
}
return (NULL);
}
/*
* Return a hash of the destination and message id.
*/
static unsigned int
{
unsigned int ret;
return (ret);
}
/*
* Called when refcount reaches 0 (and safe to destroy)
*/
static void
{
XDEBUG(("dispatch::destroy: detaching from sock %p and task %p\n",
/*
* Final cleanup of packets on the request list.
*/
}
}
static dns_dispentry_t *
unsigned int bucket)
{
return (res);
XDEBUG(("lengths (%d, %d), ids (%d, %d)\n",
}
return (NULL);
}
static void
{
switch (socktype) {
case isc_sockettype_tcp:
break;
case isc_sockettype_udp:
XDEBUG(("Freeing buffer %p, length %d, into %s, %d remain\n",
else
break;
default:
INSIST(0);
break;
}
}
static void *
{
void *temp;
else
XDEBUG(("Allocated buffer %p, length %d, from %s, %d total\n",
}
return (temp);
}
static inline void
{
disp->shutdown_out = 0;
XDEBUG(("Returning failsafe event to dispatcher\n"));
return;
}
}
static inline dns_dispatchevent_t *
{
return (ev);
}
/*
* General flow:
*
* If I/O result == CANCELED, free the buffer and notify everyone as
* the various queues drain.
*
* If I/O is error (not canceled and not success) log it, free the buffer,
* and restart.
*
* If query:
* if no listeners: free the buffer, restart.
* if listener: allocate event, fill in details.
* If cannot allocate, free buffer, restart.
* if rq event queue is not empty, queue. else, send.
* restart.
*
* If response:
* Allocate event, fill in details.
* If cannot allocate, free buffer, restart.
* find target. If not found, free buffer, restart.
* if event queue is not empty, queue. else, send.
* restart.
*/
static void
{
unsigned int flags;
unsigned int bucket;
(void)task; /* shut up compiler */
XDEBUG(("Got packet!\n"));
XDEBUG(("requests: %d, buffers: %d, recvs: %d\n",
/*
* If the recv() was canceled pass the word on.
*/
if (killit)
return;
}
/*
* otherwise, on strange error, log it and restart.
* XXXMLG
*/
goto restart;
}
XDEBUG(("length == %d, buflen = %d, addr = %p\n",
/*
* Peek into the buffer to see what we can see.
*/
if (dres != DNS_R_SUCCESS) {
XDEBUG(("dns_message_peekheader(): %s\n",
/* XXXMLG log something here... */
goto restart;
}
XDEBUG(("Got valid DNS message header, /QR %c, id %d\n",
/*
* Look at flags. If query, check to see if we have someone handling
* them. If response, look to see where it goes.
*/
if ((flags & DNS_MESSAGEFLAG_QR) == 0) {
break;
}
goto restart;
}
/* query */
} else {
/* response */
XDEBUG(("Search for response in bucket %d: %s\n",
goto restart;
}
goto restart;
}
}
/*
* At this point, rev contains the event we want to fill in, and
* resp contains the information on the place to send it to.
* Send the event off.
*/
if (queue_request) {
} else if (queue_response) {
} else {
XDEBUG(("Sent event %p buffer %p len %d to task %p, resp %p\n",
}
/*
* Restart recv() to get the next packet.
*/
}
/*
* General flow:
*
* If I/O result == CANCELED, free the buffer and notify everyone as
* the various queues drain.
*
* If I/O is error (not canceled and not success) log it, free the buffer,
* and restart.
*
* If query:
* if no listeners: free the buffer, restart.
* if listener: allocate event, fill in details.
* If cannot allocate, free buffer, restart.
* if rq event queue is not empty, queue. else, send.
* restart.
*
* If response:
* Allocate event, fill in details.
* If cannot allocate, free buffer, restart.
* find target. If not found, free buffer, restart.
* if event queue is not empty, queue. else, send.
* restart.
*/
static void
{
unsigned int flags;
unsigned int bucket;
(void)task; /* shut up compiler */
XDEBUG(("Got TCP packet!\n"));
case ISC_R_SUCCESS:
break;
case ISC_R_EOF:
XDEBUG(("Shutting down on EOF\n"));
/* FALLTHROUGH */
case ISC_R_CANCELED:
/*
* If the recv() was canceled pass the word on.
*/
/*
* The event is statically allocated in the tcpmsg
* structure, and destroy() frees the tcpmsg, so we must
* free the event *before* calling destroy().
*/
if (killit)
return;
default:
/*
* otherwise, on strange error, log it and restart.
* XXXMLG
*/
goto restart;
}
XDEBUG(("result %d, length == %d, addr = %p\n",
/*
* Peek into the buffer to see what we can see.
*/
if (dres != DNS_R_SUCCESS) {
XDEBUG(("dns_message_peekheader(): %s\n",
/* XXXMLG log something here... */
goto restart;
}
XDEBUG(("Got valid DNS message header, /QR %c, id %d\n",
/*
* Allocate an event to send to the query or response client, and
* allocate a new buffer for our use.
*/
/*
* Look at flags. If query, check to see if we have someone handling
* them. If response, look to see where it goes.
*/
if ((flags & DNS_MESSAGEFLAG_QR) == 0) {
break;
}
goto restart;
/* query */
} else {
/* response */
XDEBUG(("Search for response in bucket %d: %s\n",
goto restart;
goto restart;
}
/*
* At this point, rev contains the event we want to fill in, and
* resp contains the information on the place to send it to.
* Send the event off.
*/
if (queue_request) {
} else if (queue_response) {
} else {
XDEBUG(("Sent event %p buffer %p len %d to task %p, resp %p\n",
}
/*
* Restart recv() to get the next packet.
*/
}
/*
* disp must be locked
*/
static void
{
unsigned int wanted;
return;
if (wanted == 0)
return;
return;
return;
switch (socktype) {
/*
* UDP reads are always maximal.
*/
case isc_sockettype_udp:
return;
if (res != ISC_R_SUCCESS) {
return;
}
break;
case isc_sockettype_tcp:
XDEBUG(("Starting tcp receive\n"));
disp);
if (res != ISC_R_SUCCESS) {
return;
}
break;
}
}
}
/*
* Publics.
*/
unsigned int maxbuffersize,
unsigned int maxbuffers, unsigned int maxrequests,
{
unsigned int i;
REQUIRE(maxbuffers > 0);
res = DNS_R_SUCCESS;
return (DNS_R_NOMEMORY);
if (socktype == isc_sockettype_udp) {
} else {
}
disp->shutting_down = 0;
disp->shutdown_out = 0;
buckets * sizeof(dns_displist_t));
goto out1;
}
for (i = 0 ; i < buckets ; i++)
goto out2;
}
goto out3;
}
goto out4;
}
goto out5;
}
/*
* Keep some number of items around. This should be a config
* option. For now, keep 8, but later keep at least two even
* if the caller wants less. This allows us to ensure certain
* things, like an event can be "freed" and the next allocation
* will always succeed.
*
* Note that if limits are placed on anything here, we use one
* event internally, so the actual limit should be "wanted + 1."
*
* XXXMLG
*/
goto out6;
}
/*
* Initialize to a 32-bit LFSR. Both of these are from Applied
* Cryptography.
*
* lfsr1:
* x^32 + x^7 + x^5 + x^3 + x^2 + x + 1
*
* lfsr2:
* x^32 + x^7 + x^6 + x^2 + 1
*/
0, reseed_lfsr, disp);
0, reseed_lfsr, disp);
XDEBUG(("dns_dispatch_create: attaching to socket %p\n",
return (DNS_R_SUCCESS);
/*
* error returns
*/
out6:
out5:
out4:
out3:
out2:
out1:
return (res);
}
void
{
}
void
{
else
}
if (killit)
}
{
unsigned int bucket;
int i;
return (ISC_R_SHUTTINGDOWN);
}
return (ISC_R_QUOTA);
}
/*
* Try somewhat hard to find an unique ID.
*/
for (i = 0 ; i < 64 ; i++) {
break;
}
id &= 0x0000ffff;
}
if (!ok) {
return (DNS_R_NOMORE);
}
return (DNS_R_NOMEMORY);
}
XDEBUG(("dns_dispatch_addresponse: attaching to task %p\n",
return (DNS_R_SUCCESS);
}
void
{
unsigned int bucket;
unsigned int n;
} else {
}
else
}
/*
* We've posted our event, but the caller hasn't gotten it
* yet. Take it back.
*/
/*
* We had better have gotten it back.
*/
INSIST(n == 1);
}
}
XDEBUG(("dns_dispatch_removeresponse: detaching from task %p\n",
/*
* Free any buffered requests as well
*/
}
else
if (killit)
}
{
return (ISC_R_SHUTTINGDOWN);
}
return (ISC_R_QUOTA);
}
return (DNS_R_NOMEMORY);
}
XDEBUG(("dns_dispatch_addrequest: attaching to task %p\n",
/*
* If there are queries waiting to be processed, give this critter
* one of them.
*/
return (DNS_R_SUCCESS);
}
void
{
unsigned int n;
} else {
}
else
}
/*
* We've posted our event, but the caller hasn't gotten it
* yet. Take it back.
*/
/*
* We had better have gotten it back.
*/
INSIST(n == 1);
}
}
XDEBUG(("dns_dispatch_removerequest: detaching from task %p\n",
else
if (killit)
}
void
{
if (VALID_RESPONSE(resp)) {
} else {
}
if (response)
else
if (disp->shutting_down == 0)
}
static void
{
return;
}
XDEBUG(("Sent event %p for buffer %p (len %d) to task %p, resp %p\n",
}
static void
{
return;
}
XDEBUG(("Sent event %p for buffer %p (len %d) to task %p, resp %p\n",
}
static void
{
XDEBUG(("do_cancel() call ignored\n"));
return;
}
/*
* If no target given, find the first request handler. If
* there are packets waiting for any handler, however, don't
* kill them.
*/
XDEBUG(("do_cancel: passed a NULL response, searching...\n"));
XDEBUG(("do_cancel: non-empty request list.\n"));
XDEBUG(("do_cancel: resp %p, item_out %s\n",
resp,
break;
}
}
}
/*
* Search for the first response handler without packets outstanding.
*/
return;
do {
break;
}
/*
* No one to send the cancel event to, so nothing to do.
*/
return;
/*
* Send the shutdown failsafe event to this resp.
*/
XDEBUG(("Sending failsafe event %p to task %p, resp %p\n",
}
{
}
void
{
return;
}
return;
}