summaryrefslogtreecommitdiff
path: root/lib/dns/dispatch.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/dns/dispatch.c')
-rw-r--r--lib/dns/dispatch.c47
1 files changed, 27 insertions, 20 deletions
diff --git a/lib/dns/dispatch.c b/lib/dns/dispatch.c
index 5063914a9b296..7af8e644b1971 100644
--- a/lib/dns/dispatch.c
+++ b/lib/dns/dispatch.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2004-2009, 2011-2013 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2004-2009, 2011-2014 Internet Systems Consortium, Inc. ("ISC")
* Copyright (C) 1999-2003 Internet Software Consortium.
*
* Permission to use, copy, modify, and/or distribute this software for any
@@ -706,8 +706,8 @@ destroy_disp_ok(dns_dispatch_t *disp)
/*
* Called when refcount reaches 0 (and safe to destroy).
*
- * The dispatcher must not be locked.
- * The manager must be locked.
+ * The dispatcher must be locked.
+ * The manager must not be locked.
*/
static void
destroy_disp(isc_task_t *task, isc_event_t *event) {
@@ -778,6 +778,7 @@ port_search(dns_dispatch_t *disp, in_port_t port) {
static dispportentry_t *
new_portentry(dns_dispatch_t *disp, in_port_t port) {
dispportentry_t *portentry;
+ dns_qid_t *qid;
REQUIRE(disp->port_table != NULL);
@@ -786,10 +787,13 @@ new_portentry(dns_dispatch_t *disp, in_port_t port) {
return (portentry);
portentry->port = port;
- portentry->refs = 0;
+ portentry->refs = 1;
ISC_LINK_INIT(portentry, link);
+ qid = DNS_QID(disp);
+ LOCK(&qid->lock);
ISC_LIST_APPEND(disp->port_table[port % DNS_DISPATCH_PORTTABLESIZE],
portentry, link);
+ UNLOCK(&qid->lock);
return (portentry);
}
@@ -800,7 +804,6 @@ new_portentry(dns_dispatch_t *disp, in_port_t port) {
static void
deref_portentry(dns_dispatch_t *disp, dispportentry_t **portentryp) {
dispportentry_t *portentry = *portentryp;
- isc_boolean_t unlink = ISC_FALSE;
dns_qid_t *qid;
REQUIRE(disp->port_table != NULL);
@@ -809,15 +812,14 @@ deref_portentry(dns_dispatch_t *disp, dispportentry_t **portentryp) {
qid = DNS_QID(disp);
LOCK(&qid->lock);
portentry->refs--;
- unlink = ISC_TF(portentry->refs == 0);
- UNLOCK(&qid->lock);
- if (unlink) {
+ if (portentry->refs == 0) {
ISC_LIST_UNLINK(disp->port_table[portentry->port %
DNS_DISPATCH_PORTTABLESIZE],
portentry, link);
isc_mempool_put(disp->portpool, portentry);
}
+ UNLOCK(&qid->lock);
*portentryp = NULL;
}
@@ -832,6 +834,7 @@ socket_search(dns_qid_t *qid, isc_sockaddr_t *dest, in_port_t port,
{
dispsocket_t *dispsock;
+ REQUIRE(VALID_QID(qid));
REQUIRE(bucket < qid->qid_nbuckets);
dispsock = ISC_LIST_HEAD(qid->sock_table[bucket]);
@@ -938,8 +941,11 @@ get_dispsocket(dns_dispatch_t *disp, isc_sockaddr_t *dest,
result = ISC_R_NOMEMORY;
break;
}
+ } else {
+ LOCK(&qid->lock);
+ portentry->refs++;
+ UNLOCK(&qid->lock);
}
- portentry->refs++;
break;
} else if (result == ISC_R_NOPERM) {
char buf[ISC_SOCKADDR_FORMATSIZE];
@@ -1076,6 +1082,7 @@ entry_search(dns_qid_t *qid, isc_sockaddr_t *dest, dns_messageid_t id,
{
dns_dispentry_t *res;
+ REQUIRE(VALID_QID(qid));
REQUIRE(bucket < qid->qid_nbuckets);
res = ISC_LIST_HEAD(qid->qid_table[bucket]);
@@ -1428,8 +1435,8 @@ udp_recv(isc_event_t *ev_in, dns_dispatch_t *disp, dispsocket_t *dispsock) {
}
/*
- * If both dispatches are bound to an address then fail as
- * the addresses can't be equal (enforced by the IP stack).
+ * If each dispatch is bound to a different address
+ * then fail.
*
* Note under Linux a packet can be sent out via IPv4 socket
* and the response be received via a IPv6 socket.
@@ -1444,7 +1451,8 @@ udp_recv(isc_event_t *ev_in, dns_dispatch_t *disp, dispsocket_t *dispsock) {
}
isc_sockaddr_anyofpf(&a1, isc_sockaddr_pf(&resp->disp->local));
isc_sockaddr_anyofpf(&a2, isc_sockaddr_pf(&disp->local));
- if (!isc_sockaddr_eqaddr(&a1, &resp->disp->local) &&
+ if (!isc_sockaddr_eqaddr(&disp->local, &resp->disp->local) &&
+ !isc_sockaddr_eqaddr(&a1, &resp->disp->local) &&
!isc_sockaddr_eqaddr(&a2, &disp->local)) {
free_buffer(disp, ev->region.base, ev->region.length);
goto unlock;
@@ -2647,8 +2655,7 @@ dispatch_allocate(dns_dispatchmgr_t *mgr, unsigned int maxrequests,
* MUST be unlocked, and not used by anything.
*/
static void
-dispatch_free(dns_dispatch_t **dispp)
-{
+dispatch_free(dns_dispatch_t **dispp) {
dns_dispatch_t *disp;
dns_dispatchmgr_t *mgr;
int i;
@@ -3311,17 +3318,17 @@ dns_dispatch_addresponse2(dns_dispatch_t *disp, isc_sockaddr_t *dest,
*/
LOCK(&qid->lock);
id = (dns_messageid_t)dispatch_random(DISP_ARC4CTX(disp));
- bucket = dns_hash(qid, dest, id, localport);
ok = ISC_FALSE;
- for (i = 0; i < 64; i++) {
+ i = 0;
+ do {
+ bucket = dns_hash(qid, dest, id, localport);
if (entry_search(qid, dest, id, localport, bucket) == NULL) {
ok = ISC_TRUE;
break;
}
id += qid->qid_increment;
id &= 0x0000ffff;
- bucket = dns_hash(qid, dest, id, localport);
- }
+ } while (i++ < 64);
UNLOCK(&qid->lock);
if (!ok) {
@@ -3331,9 +3338,9 @@ dns_dispatch_addresponse2(dns_dispatch_t *disp, isc_sockaddr_t *dest,
res = isc_mempool_get(disp->mgr->rpool);
if (res == NULL) {
- UNLOCK(&disp->lock);
if (dispsocket != NULL)
destroy_dispsocket(disp, &dispsocket);
+ UNLOCK(&disp->lock);
return (ISC_R_NOMEMORY);
}
@@ -3708,7 +3715,7 @@ dns_dispatch_importrecv(dns_dispatch_t *disp, isc_event_t *event) {
isc_event_free(ISC_EVENT_PTR(&newsevent));
return;
}
- memcpy(buf, sevent->region.base, sevent->n);
+ memmove(buf, sevent->region.base, sevent->n);
newsevent->region.base = buf;
newsevent->region.length = disp->mgr->buffersize;
newsevent->n = sevent->n;