dispatch.c revision 24675e8e132e068cbb5c6e13dc147dcba52919c4
/*
* Copyright (C) 1999, 2000 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.
*/
/* $Id: dispatch.c,v 1.58 2000/07/04 01:48:10 tale Exp $ */
#include <config.h>
#include <stdlib.h>
#include <dns/dispatch.h>
struct dns_dispatchmgr {
/* Unlocked. */
unsigned int magic;
/* Locked by "lock". */
unsigned int state;
/* Locked internally. */
};
#define MGR_SHUTTINGDOWN 0x00000001U
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 by mgr->lock. */
/* Locked by "lock". */
unsigned int attributes;
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,
connected : 1,
tcpmsg_valid : 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_disp(dns_dispatch_t **);
static inline void startrecv(dns_dispatch_t *);
unsigned int buffersize,
unsigned int maxbuffers,
unsigned int maxrequests,
unsigned int buckets,
unsigned int increment,
unsigned int attributes,
dns_dispatch_t **dispp);
#define LVL(x) ISC_LOG_DEBUG(x)
static void
char msgbuf[2048];
}
static void
char msgbuf[2048];
}
static void
{
char msgbuf[2048];
char peerbuf[256];
if (VALID_RESPONSE(resp)) {
} else if (VALID_REQUEST(resp)) {
msgbuf);
} else {
msgbuf);
}
}
static void
{
NULL, 0);
return;
}
}
/*
* Return an unpredictable message ID.
*/
static isc_uint32_t
return (id & 0x0000ffffU);
}
/*
* Return a hash of the destination and message id.
*/
static isc_uint32_t
unsigned int ret;
return (ret);
}
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);
}
/*
* The dispatch must be locked.
*/
static isc_boolean_t
{
return (ISC_FALSE);
return (ISC_FALSE);
if (disp->shutting_down == 0)
return (ISC_FALSE);
return (ISC_TRUE);
}
/*
* Called when refcount reaches 0 (and safe to destroy).
*
* The dispatcher must not be locked.
* The manager must be locked.
*/
static void
"shutting down; detaching from sock %p, task %p",
}
static dns_dispentry_t *
unsigned int bucket)
{
return (res);
}
return (NULL);
}
static void
case isc_sockettype_tcp:
break;
case isc_sockettype_udp:
break;
default:
INSIST(0);
break;
}
}
static void *
void *temp;
return (temp);
}
static inline void
disp->shutdown_out = 0;
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;
"got packet: requests %d, buffers %d, recvs %d",
if (disp->shutting_down) {
/*
* This dispatcher is shutting down.
*/
if (killit) {
destroy_disp(&disp);
if (killit)
destroy_mgr(&mgr);
}
return;
}
/*
* If the recv() was canceled pass the word on.
*/
return;
}
"odd socket result in udp_recv(): %s\n",
/*
* otherwise, on strange error, log it and restart.
* XXXMLG
*/
goto restart;
}
/*
* Peek into the buffer to see what we can see.
*/
if (dres != ISC_R_SUCCESS) {
goto restart;
}
"got valid DNS message header, /QR %c, id %u",
/*
* 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 */
"search for response in bucket %d: %s",
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 {
"[a] Sent event %p buffer %p len %d to task %p",
}
/*
* 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;
"got TCP packet: requests %d, buffers %d, recvs %d",
/*
* This dispatcher is shutting down. Force cancelation.
*/
}
case ISC_R_SUCCESS:
break;
case ISC_R_EOF:
/* FALLTHROUGH */
case ISC_R_CANCELED:
/*
* The event is statically allocated in the tcpmsg
* structure, and destroy_disp() frees the tcpmsg, so we must
* free the event *before* calling destroy_disp().
*/
/*
* If the recv() was canceled pass the word on.
*/
if (killit) {
destroy_disp(&disp);
if (killit)
destroy_mgr(&mgr);
}
return;
default:
/*
* otherwise, on strange error, log it and restart.
* XXXMLG
*/
goto restart;
}
/*
* Peek into the buffer to see what we can see.
*/
if (dres != ISC_R_SUCCESS) {
goto restart;
}
"got valid DNS message header, /QR %c, id %u",
/*
* 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) {
/*
* Query.
*/
break;
}
goto restart;
} else {
/*
* Response.
*/
"search for response in bucket %d: %s",
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 {
"[b] Sent event %p buffer %p len %d to task %p",
}
/*
* Restart recv() to get the next packet.
*/
}
/*
* disp must be locked.
*/
static void
unsigned int wanted;
return;
if (wanted == 0)
return;
return;
return;
/*
* UDP reads are always maximal.
*/
case isc_sockettype_udp:
return;
if (res != ISC_R_SUCCESS) {
return;
}
break;
case isc_sockettype_tcp:
disp);
if (res != ISC_R_SUCCESS) {
return;
}
break;
}
}
}
/*
* Mgr must be locked when calling this function.
*/
static isc_boolean_t
"destroy_mgr_ok: shuttingdown=%d, listnonempty=%d, "
"epool=%d, rpool=%d, dpool=%d",
if (!MGR_IS_SHUTTINGDOWN(mgr))
return (ISC_FALSE);
return (ISC_FALSE);
return (ISC_FALSE);
return (ISC_FALSE);
return (ISC_FALSE);
return (ISC_TRUE);
}
/*
* Mgr must be unlocked when calling this function.
*/
static void
}
static isc_result_t
{
if (result != ISC_R_SUCCESS)
return (result);
if (result != ISC_R_SUCCESS) {
return (result);
}
return (ISC_R_SUCCESS);
}
/*
* Publics.
*/
{
return (ISC_R_NOMEMORY);
if (result != ISC_R_SUCCESS)
goto deallocate;
if (result != ISC_R_SUCCESS)
goto kill_lock;
goto kill_pool_lock;
}
goto kill_epool;
}
goto kill_rpool;
}
return (ISC_R_SUCCESS);
#if 0
#endif
return (result);
}
void
if (killit)
destroy_mgr(&mgr);
}
static isc_boolean_t
return (ISC_TRUE);
if (port == 0)
else
}
/*
* Requires mgr be locked.
*
* No dispatcher can be locked by this thread when calling this function.
*
*
* NOTE:
* If a matching dispatcher is found, it is locked after this function
* returns, and must be unlocked by the caller.
*/
static isc_result_t
unsigned int attributes, unsigned int mask,
{
/*
* Make certain that we will not match a private dispatch.
*/
if ((disp->shutting_down == 0)
break;
}
goto out;
}
out:
return (result);
}
/*
* Allocate and set important limits.
*/
static isc_result_t
unsigned int maxbuffers, unsigned int maxrequests,
{
unsigned int i;
REQUIRE(maxbuffers > 0);
/*
* Set up the dispatcher, mostly. Don't bother setting some of
* the options that are controlled by tcp vs. udp, etc.
*/
return (ISC_R_NOMEMORY);
disp->attributes = 0;
disp->shutting_down = 0;
disp->shutdown_out = 0;
disp->tcpmsg_valid = 0;
buckets * sizeof(dns_displist_t));
goto deallocate;
}
for (i = 0 ; i < buckets ; i++)
goto deallocate_qidtable;
}
goto kill_lock;
}
/*
* 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 kill_bpool;
}
/*
* 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);
return (ISC_R_SUCCESS);
/*
* error returns
*/
return (res);
}
/*
* MUST be unlocked, and not used by anthing.
*/
static void
{
if (disp->tcpmsg_valid) {
disp->tcpmsg_valid = 0;
}
/*
* Final cleanup of packets on the request list.
*/
}
}
unsigned int maxbuffers, unsigned int maxrequests,
{
/*
* dispatch_allocate() checks mgr, buffersize, maxbuffers,
* buckets, and increment for us.
*/
if (result != ISC_R_SUCCESS) {
return (result);
}
if (result != ISC_R_SUCCESS)
goto kill_socket;
/*
* Append it to the dispatcher list.
*/
return (ISC_R_SUCCESS);
/*
* Error returns.
*/
return (result);
}
unsigned int buffersize,
unsigned int maxbuffers, unsigned int maxrequests,
unsigned int attributes, unsigned int mask,
{
REQUIRE(maxbuffers > 0);
/*
* First, see if we have a dispatcher that matches.
*/
if (result == ISC_R_SUCCESS) {
}
return (ISC_R_SUCCESS);
}
/*
* Nope, create one.
*/
if (result != ISC_R_SUCCESS) {
return (result);
}
return (ISC_R_SUCCESS);
}
/*
* mgr should be locked.
*/
static isc_result_t
unsigned int maxbuffers, unsigned int maxrequests,
unsigned int attributes,
{
/*
* dispatch_allocate() checks mgr, buffersize, maxbuffers,
* buckets, and increment for us.
*/
if (result != ISC_R_SUCCESS)
return (result);
if (result != ISC_R_SUCCESS)
goto deallocate_dispatch;
if (result != ISC_R_SUCCESS)
goto kill_socket;
/*
* Append it to the dispatcher list.
*/
return (ISC_R_SUCCESS);
/*
* Error returns.
*/
return (result);
}
void
}
/*
* It is important to lock the manager while we are deleting the dispatch,
* since dns_dispatch_getudp will call dispatch_find, which returns to
* the caller a dispatch but does not attach to it until later. _getudp
* locks the manager, however, so locking it here will keep us from attaching
* to a dispatcher that is in the process of going away.
*/
void
}
if (killit) {
destroy_disp(&disp);
if (killit)
destroy_mgr(&mgr);
}
}
{
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 (ISC_R_NOMORE);
}
return (ISC_R_NOMEMORY);
}
return (ISC_R_SUCCESS);
}
void
}
void
{
unsigned int bucket;
unsigned int n;
} 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);
}
}
/*
* Free any buffered requests as well
*/
}
else
if (killit) {
destroy_disp(&disp);
if (killit)
destroy_mgr(&mgr);
}
}
{
return (ISC_R_SHUTTINGDOWN);
}
return (ISC_R_QUOTA);
}
return (ISC_R_NOMEMORY);
}
/*
* If there are queries waiting to be processed, give this critter
* one of them.
*/
return (ISC_R_SUCCESS);
}
void
{
unsigned int n;
} 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);
}
}
else
if (killit) {
destroy_disp(&disp);
if (killit)
destroy_mgr(&mgr);
}
}
void
{
if (VALID_RESPONSE(resp)) {
} else {
}
if (response)
else
if (disp->shutting_down == 0)
}
static void
return;
}
"[c] Sent event %p buffer %p len %d to task %p",
}
static void
return;
}
"[d] Sent event %p buffer %p len %d to task %p",
}
static void
return;
/*
* If no target given, find the first request handler. If
* there are packets waiting for any handler, however, don't
* kill them.
*/
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.
*/
"cancel: failsafe event %p -> task %p",
}
}
void
return;
}
return;
}
void
unsigned int attributes, unsigned int mask)
{
}
#if 0
void
char foo[1024];
}
}
#endif