Commit e2fe1fda authored by Tatuya JINMEI 神明達哉's avatar Tatuya JINMEI 神明達哉
Browse files

2417. [bug] Connecting UDP sockets for outgoing queries could

			unexpectedly fail with an 'address already in use'
			error. [RT #18411]
parent cf225ed6
2417. [bug] Connecting UDP sockets for outgoing queries could
unexpectedly fail with an 'address already in use'
error. [RT #18411]
2416. [func] Log file descriptors that cause exceeding the
internal maximum. [RT #18460]
......
......@@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: dispatch.c,v 1.150 2008/08/05 19:18:02 jinmei Exp $ */
/* $Id: dispatch.c,v 1.151 2008/08/15 17:29:52 jinmei Exp $ */
/*! \file */
......@@ -49,6 +49,9 @@
typedef ISC_LIST(dns_dispentry_t) dns_displist_t;
typedef struct dispsocket dispsocket_t;
typedef ISC_LIST(dispsocket_t) dispsocketlist_t;
/* ARC4 Random generator state */
typedef struct arc4ctx {
isc_uint8_t i;
......@@ -65,7 +68,7 @@ typedef struct dns_qid {
unsigned int qid_increment; /*%< id increment on collision */
isc_mutex_t lock;
dns_displist_t *qid_table; /*%< the table itself */
dns_displist_t *addr_table; /*%< address/port table */
dispsocketlist_t *sock_table; /*%< socket table */
} dns_qid_t;
struct dns_dispatchmgr {
......@@ -127,15 +130,12 @@ struct dns_dispatchmgr {
#define IS_PRIVATE(d) (((d)->attributes & DNS_DISPATCHATTR_PRIVATE) != 0)
typedef struct dispsocket dispsocket_t;
struct dns_dispentry {
unsigned int magic;
dns_dispatch_t *disp;
dns_messageid_t id;
in_port_t port;
unsigned int bucket;
unsigned int abucket;
isc_sockaddr_t host;
isc_task_t *task;
isc_taskaction_t action;
......@@ -144,7 +144,6 @@ struct dns_dispentry {
dispsocket_t *dispsocket;
ISC_LIST(dns_dispatchevent_t) items;
ISC_LINK(dns_dispentry_t) link;
ISC_LINK(dns_dispentry_t) alink;
};
/*%
......@@ -172,9 +171,13 @@ struct dispsocket {
unsigned int magic;
isc_socket_t *socket;
dns_dispatch_t *disp;
isc_sockaddr_t host;
in_port_t localport;
dns_dispentry_t *resp;
isc_task_t *task;
ISC_LINK(dispsocket_t) link;
unsigned int bucket;
ISC_LINK(dispsocket_t) blink;
};
#define INVALID_BUCKET (0xffffdead)
......@@ -261,9 +264,8 @@ struct dns_dispatch {
/*
* Statics.
*/
static dns_dispentry_t *bucket_search(dns_qid_t *, dns_displist_t *,
isc_sockaddr_t *, dns_messageid_t,
in_port_t, unsigned int, isc_boolean_t);
static dns_dispentry_t *entry_search(dns_qid_t *, isc_sockaddr_t *,
dns_messageid_t, in_port_t, unsigned int);
static isc_boolean_t destroy_disp_ok(dns_dispatch_t *);
static void destroy_disp(isc_task_t *task, isc_event_t *event);
static void destroy_dispsocket(dns_dispatch_t *, dispsocket_t **);
......@@ -677,6 +679,30 @@ destroy_disp(isc_task_t *task, isc_event_t *event) {
destroy_mgr(&mgr);
}
/*%
* Find a dispsocket for socket address 'dest', and port number 'port'.
* Return NULL if no such entry exists.
*/
static dispsocket_t *
socket_search(dns_qid_t *qid, isc_sockaddr_t *dest, in_port_t port,
unsigned int bucket)
{
dispsocket_t *dispsock;
REQUIRE(bucket < qid->qid_nbuckets);
dispsock = ISC_LIST_HEAD(qid->sock_table[bucket]);
while (dispsock != NULL) {
if (isc_sockaddr_equal(dest, &dispsock->host) &&
dispsock->localport == port)
return (dispsock);
dispsock = ISC_LIST_NEXT(dispsock, blink);
}
return (NULL);
}
/*%
* Make a new socket for a single dispatch with a random port number.
* The caller must hold the disp->lock and qid->lock.
......@@ -684,8 +710,7 @@ destroy_disp(isc_task_t *task, isc_event_t *event) {
static isc_result_t
get_dispsocket(dns_dispatch_t *disp, isc_sockaddr_t *dest,
isc_socketmgr_t *sockmgr, dns_qid_t *qid,
dispsocket_t **dispsockp, unsigned int *abucketp,
in_port_t *portp)
dispsocket_t **dispsockp, in_port_t *portp)
{
int i;
isc_uint32_t r;
......@@ -694,7 +719,7 @@ get_dispsocket(dns_dispatch_t *disp, isc_sockaddr_t *dest,
isc_result_t result = ISC_R_FAILURE;
in_port_t port;
isc_sockaddr_t localaddr;
unsigned int abucket = 0;
unsigned int bucket = 0;
dispsocket_t *dispsock;
unsigned int nports;
in_port_t *ports;
......@@ -727,6 +752,7 @@ get_dispsocket(dns_dispatch_t *disp, isc_sockaddr_t *dest,
dispsock->task = NULL;
isc_task_attach(disp->task[r % disp->ntasks], &dispsock->task);
ISC_LINK_INIT(dispsock, link);
ISC_LINK_INIT(dispsock, blink);
dispsock->magic = DISPSOCK_MAGIC;
}
......@@ -741,11 +767,9 @@ get_dispsocket(dns_dispatch_t *disp, isc_sockaddr_t *dest,
nports)];
isc_sockaddr_setport(&localaddr, port);
abucket = dns_hash(qid, dest, 0, port);
if (bucket_search(qid, qid->addr_table, dest, 0, port, abucket,
ISC_TRUE) != NULL) {
bucket = dns_hash(qid, dest, 0, port);
if (socket_search(qid, dest, port, bucket) != NULL)
continue;
}
result = open_socket(sockmgr, &localaddr, 0, &sock);
if (result == ISC_R_SUCCESS || result != ISC_R_ADDRINUSE)
......@@ -754,8 +778,11 @@ get_dispsocket(dns_dispatch_t *disp, isc_sockaddr_t *dest,
if (result == ISC_R_SUCCESS) {
dispsock->socket = sock;
dispsock->host = *dest;
dispsock->localport = port;
dispsock->bucket = bucket;
ISC_LIST_APPEND(qid->sock_table[bucket], dispsock, blink);
*dispsockp = dispsock;
*abucketp = abucket;
*portp = port;
} else {
/*
......@@ -777,6 +804,7 @@ get_dispsocket(dns_dispatch_t *disp, isc_sockaddr_t *dest,
static void
destroy_dispsocket(dns_dispatch_t *disp, dispsocket_t **dispsockp) {
dispsocket_t *dispsock;
dns_qid_t *qid;
/*
* The dispatch must be locked.
......@@ -790,6 +818,13 @@ destroy_dispsocket(dns_dispatch_t *disp, dispsocket_t **dispsockp) {
dispsock->magic = 0;
if (dispsock->socket != NULL)
isc_socket_detach(&dispsock->socket);
if (ISC_LINK_LINKED(dispsock, blink)) {
qid = DNS_QID(disp);
LOCK(&qid->lock);
ISC_LIST_UNLINK(qid->sock_table[dispsock->bucket], dispsock,
blink);
UNLOCK(&qid->lock);
}
if (dispsock->task != NULL)
isc_task_detach(&dispsock->task);
isc_mempool_put(disp->mgr->spool, dispsock);
......@@ -804,6 +839,7 @@ destroy_dispsocket(dns_dispatch_t *disp, dispsocket_t **dispsockp) {
static void
deactivate_dispsocket(dns_dispatch_t *disp, dispsocket_t *dispsock) {
isc_result_t result;
dns_qid_t *qid;
/*
* The dispatch must be locked.
......@@ -818,6 +854,13 @@ deactivate_dispsocket(dns_dispatch_t *disp, dispsocket_t *dispsock) {
destroy_dispsocket(disp, &dispsock);
else {
result = isc_socket_close(dispsock->socket);
qid = DNS_QID(disp);
LOCK(&qid->lock);
ISC_LIST_UNLINK(qid->sock_table[dispsock->bucket], dispsock,
blink);
UNLOCK(&qid->lock);
if (result == ISC_R_SUCCESS)
ISC_LIST_APPEND(disp->inactivesockets, dispsock, link);
else {
......@@ -834,23 +877,21 @@ deactivate_dispsocket(dns_dispatch_t *disp, dispsocket_t *dispsock) {
/*
* Find an entry for query ID 'id', socket address 'dest', and port number
* 'port' in 'table'.
* 'port'.
* Return NULL if no such entry exists.
*/
static dns_dispentry_t *
bucket_search(dns_qid_t *qid, dns_displist_t *table, isc_sockaddr_t *dest,
dns_messageid_t id, in_port_t port, unsigned int bucket,
isc_boolean_t ignoreid)
entry_search(dns_qid_t *qid, isc_sockaddr_t *dest, dns_messageid_t id,
in_port_t port, unsigned int bucket)
{
dns_dispentry_t *res;
REQUIRE(bucket < qid->qid_nbuckets);
res = ISC_LIST_HEAD(table[bucket]);
res = ISC_LIST_HEAD(qid->qid_table[bucket]);
while (res != NULL) {
if ((ignoreid || res->id == id) &&
isc_sockaddr_equal(dest, &res->host) &&
if (res->id == id && isc_sockaddr_equal(dest, &res->host) &&
res->port == port) {
return (res);
}
......@@ -1116,8 +1157,8 @@ udp_recv(isc_event_t *ev_in, dns_dispatch_t *disp, dispsocket_t *dispsock) {
bucket = dns_hash(qid, &ev->address, id, disp->localport);
LOCK(&qid->lock);
qidlocked = ISC_TRUE;
resp = bucket_search(qid, qid->qid_table, &ev->address, id,
disp->localport, bucket, ISC_FALSE);
resp = entry_search(qid, &ev->address, id, disp->localport,
bucket);
dispatch_log(disp, LVL(90),
"search for response in bucket %d: %s",
bucket, (resp == NULL ? "not found" : "found"));
......@@ -1374,8 +1415,7 @@ tcp_recv(isc_task_t *task, isc_event_t *ev_in) {
*/
bucket = dns_hash(qid, &tcpmsg->address, id, disp->localport);
LOCK(&qid->lock);
resp = bucket_search(qid, qid->qid_table, &tcpmsg->address, id,
disp->localport, bucket, ISC_FALSE);
resp = entry_search(qid, &tcpmsg->address, id, disp->localport, bucket);
dispatch_log(disp, LVL(90),
"search for response in bucket %d: %s",
bucket, (resp == NULL ? "not found" : "found"));
......@@ -2127,7 +2167,7 @@ dispatch_find(dns_dispatchmgr_t *mgr, isc_sockaddr_t *local,
static isc_result_t
qid_allocate(dns_dispatchmgr_t *mgr, unsigned int buckets,
unsigned int increment, dns_qid_t **qidp,
isc_boolean_t needaddrtable)
isc_boolean_t needsocktable)
{
dns_qid_t *qid;
unsigned int i;
......@@ -2149,11 +2189,11 @@ qid_allocate(dns_dispatchmgr_t *mgr, unsigned int buckets,
return (ISC_R_NOMEMORY);
}
qid->addr_table = NULL;
if (needaddrtable) {
qid->addr_table = isc_mem_get(mgr->mctx,
buckets * sizeof(dns_displist_t));
if (qid->addr_table == NULL) {
qid->sock_table = NULL;
if (needsocktable) {
qid->sock_table = isc_mem_get(mgr->mctx, buckets *
sizeof(dispsocketlist_t));
if (qid->sock_table == NULL) {
isc_mem_put(mgr->mctx, qid, sizeof(*qid));
isc_mem_put(mgr->mctx, qid->qid_table,
buckets * sizeof(dns_displist_t));
......@@ -2163,8 +2203,8 @@ qid_allocate(dns_dispatchmgr_t *mgr, unsigned int buckets,
result = isc_mutex_init(&qid->lock);
if (result != ISC_R_SUCCESS) {
if (qid->addr_table != NULL) {
isc_mem_put(mgr->mctx, qid->addr_table,
if (qid->sock_table != NULL) {
isc_mem_put(mgr->mctx, qid->sock_table,
buckets * sizeof(dns_displist_t));
}
isc_mem_put(mgr->mctx, qid->qid_table,
......@@ -2175,8 +2215,8 @@ qid_allocate(dns_dispatchmgr_t *mgr, unsigned int buckets,
for (i = 0; i < buckets; i++) {
ISC_LIST_INIT(qid->qid_table[i]);
if (qid->addr_table != NULL)
ISC_LIST_INIT(qid->addr_table[i]);
if (qid->sock_table != NULL)
ISC_LIST_INIT(qid->sock_table[i]);
}
qid->qid_nbuckets = buckets;
......@@ -2199,8 +2239,8 @@ qid_destroy(isc_mem_t *mctx, dns_qid_t **qidp) {
qid->magic = 0;
isc_mem_put(mctx, qid->qid_table,
qid->qid_nbuckets * sizeof(dns_displist_t));
if (qid->addr_table != NULL) {
isc_mem_put(mctx, qid->addr_table,
if (qid->sock_table != NULL) {
isc_mem_put(mctx, qid->sock_table,
qid->qid_nbuckets * sizeof(dns_displist_t));
}
DESTROYLOCK(&qid->lock);
......@@ -2739,7 +2779,6 @@ dns_dispatch_addresponse2(dns_dispatch_t *disp, isc_sockaddr_t *dest,
{
dns_dispentry_t *res;
unsigned int bucket;
unsigned int abucket;
in_port_t localport = 0;
dns_messageid_t id;
int i;
......@@ -2813,14 +2852,13 @@ dns_dispatch_addresponse2(dns_dispatch_t *disp, isc_sockaddr_t *dest,
* Get a separate UDP socket with a random port number.
*/
result = get_dispsocket(disp, dest, sockmgr, qid, &dispsocket,
&abucket, &localport);
&localport);
if (result != ISC_R_SUCCESS) {
UNLOCK(&qid->lock);
UNLOCK(&disp->lock);
return (result);
}
} else {
abucket = 0; /* meaningless, but set explicitly */
localport = disp->localport;
}
......@@ -2831,8 +2869,7 @@ dns_dispatch_addresponse2(dns_dispatch_t *disp, isc_sockaddr_t *dest,
bucket = dns_hash(qid, dest, id, localport);
ok = ISC_FALSE;
for (i = 0; i < 64; i++) {
if (bucket_search(qid, qid->qid_table, dest, id, localport,
bucket, ISC_FALSE) == NULL) {
if (entry_search(qid, dest, id, localport, bucket) == NULL) {
ok = ISC_TRUE;
break;
}
......@@ -2864,7 +2901,6 @@ dns_dispatch_addresponse2(dns_dispatch_t *disp, isc_sockaddr_t *dest,
res->id = id;
res->port = localport;
res->bucket = bucket;
res->abucket = abucket;
res->host = *dest;
res->action = action;
res->arg = arg;
......@@ -2874,11 +2910,8 @@ dns_dispatch_addresponse2(dns_dispatch_t *disp, isc_sockaddr_t *dest,
res->item_out = ISC_FALSE;
ISC_LIST_INIT(res->items);
ISC_LINK_INIT(res, link);
ISC_LINK_INIT(res, alink);
res->magic = RESPONSE_MAGIC;
ISC_LIST_APPEND(qid->qid_table[bucket], res, link);
if (dispsocket != NULL)
ISC_LIST_APPEND(qid->addr_table[abucket], res, alink);
UNLOCK(&qid->lock);
request_log(disp, res, LVL(90),
......@@ -2890,10 +2923,6 @@ dns_dispatch_addresponse2(dns_dispatch_t *disp, isc_sockaddr_t *dest,
if (result != ISC_R_SUCCESS) {
LOCK(&qid->lock);
ISC_LIST_UNLINK(qid->qid_table[bucket], res, link);
if (ISC_LINK_LINKED(res, alink)) {
ISC_LIST_UNLINK(qid->addr_table[abucket], res,
alink);
}
UNLOCK(&qid->lock);
if (dispsocket != NULL)
......@@ -3008,8 +3037,6 @@ dns_dispatch_removeresponse(dns_dispentry_t **resp,
LOCK(&qid->lock);
ISC_LIST_UNLINK(qid->qid_table[bucket], res, link);
if (ISC_LINK_LINKED(res, alink))
ISC_LIST_UNLINK(qid->addr_table[res->abucket], res, alink);
UNLOCK(&qid->lock);
if (ev == NULL && res->item_out) {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment