dispatch.c revision f6407f9a0b890bebbfd5f738d9c4aef3d3315fe9
/*
* 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.
*/
#include <config.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <isc/assertions.h>
#include <dns/dispatch.h>
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 *);
#define LVL(x) \
/*
* Format a human-readable representation of the socket address '*sa'
* into the character array 'array', which is of size 'size'.
* The resulting string is guaranteed to be null-terminated.
*/
static void
{
if (result != ISC_R_SUCCESS) {
"<unknown address, family %u>",
}
}
static void
const char *fmt, ...)
{
char msgbuf[2048];
}
static void
const char *fmt, ...)
{
char msgbuf[2048];
char peerbuf[256];
if (VALID_RESPONSE(resp)) {
} else if (VALID_REQUEST(resp)) {
msgbuf);
} else {
}
}
static void
{
}
/*
* 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_dispatchmethods_t dns_wire_methods = {
};
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);
}
/*
* Called when refcount reaches 0 (and safe to destroy)
*/
static void
{
"shutting down; detaching from sock %p, task %p",
/*
* Final cleanup of packets on the request list.
*/
}
}
static dns_dispentry_t *
unsigned int bucket)
{
return (res);
}
return (NULL);
}
static void
{
switch (socktype) {
case isc_sockettype_tcp:
break;
case isc_sockettype_udp:
else
break;
default:
INSIST(0);
break;
}
}
static void *
{
void *temp;
else
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",
/*
* This dispatcher is shutting down.
*/
if (killit)
return;
}
/*
* If the recv() was canceled pass the word on.
*/
return;
}
/*
* 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:
/*
* 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;
}
/*
* 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) {
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 {
"[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;
switch (socktype) {
/*
* 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;
}
}
}
/*
* Publics.
*/
unsigned int maxbuffersize,
unsigned int maxbuffers, unsigned int maxrequests,
{
unsigned int i;
REQUIRE(maxbuffers > 0);
res = ISC_R_SUCCESS;
return (ISC_R_NOMEMORY);
if (socktype == isc_sockettype_udp) {
} else {
}
disp->shutting_down = 0;
disp->shutdown_out = 0;
else
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);
return (ISC_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 (ISC_R_NOMORE);
}
return (ISC_R_NOMEMORY);
}
return (ISC_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);
}
}
/*
* Free any buffered requests as well
*/
}
else
if (killit)
}
{
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 {
}
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)
}
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.
*/
}
{
}
void
{
return;
}
return;
}