diff --git a/bin/named/client.c b/bin/named/client.c index ef3ff05346..cdd7370a63 100644 --- a/bin/named/client.c +++ b/bin/named/client.c @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: client.c,v 1.111 2000/09/12 18:45:30 explorer Exp $ */ +/* $Id: client.c,v 1.112 2000/09/13 01:30:30 marka Exp $ */ #include @@ -641,6 +641,93 @@ client_senddone(isc_task_t *task, isc_event_t *event) { ns_client_next(client, ISC_R_SUCCESS); } +void +ns_client_sendraw(ns_client_t *client, dns_message_t *message) { + isc_result_t result; + unsigned char *data; + isc_buffer_t buffer; + isc_region_t r; + isc_region_t *mr; + isc_socket_t *socket; + isc_sockaddr_t *address; + struct in6_pktinfo *pktinfo; + unsigned int bufsize = 512; + + REQUIRE(NS_CLIENT_VALID(client)); + + CTRACE("sendraw"); + + mr = dns_message_getrawmessage(message); + if (mr == NULL) { + result = ISC_R_UNEXPECTEDEND; + goto done; + } + + if (TCP_CLIENT(client)) { + INSIST(client->tcpbuf == NULL); + if (mr->length + 2 > TCP_BUFFER_SIZE) { + result = ISC_R_NOSPACE; + goto done; + } + client->tcpbuf = isc_mem_get(client->mctx, TCP_BUFFER_SIZE); + if (client->tcpbuf == NULL) { + result = ISC_R_NOMEMORY; + goto done; + } + data = client->tcpbuf; + isc_buffer_init(&buffer, data, TCP_BUFFER_SIZE); + isc_buffer_putuint16(&buffer, mr->length); + } else { + data = client->sendbuf; + if (client->udpsize < SEND_BUFFER_SIZE) + bufsize = client->udpsize; + else + bufsize = SEND_BUFFER_SIZE; + if (mr->length + 2 > bufsize) { + result = ISC_R_NOSPACE; + goto done; + } + isc_buffer_init(&buffer, data, bufsize); + } + + /* + * Copy message to buffer and fixup id. + */ + isc_buffer_availableregion(&buffer, &r); + result = isc_buffer_copyregion(&buffer, mr); + if (result != ISC_R_SUCCESS) + goto done; + r.base[0] = (client->message->id >> 8) & 0xff; + r.base[1] = client->message->id & 0xff; + + if (TCP_CLIENT(client)) { + socket = client->tcpsocket; + address = NULL; + } else { + socket = dns_dispatch_getsocket(client->dispatch); + address = &client->dispevent->addr; + } + isc_buffer_usedregion(&buffer, &r); + CTRACE("sendto"); + if ((client->attributes & NS_CLIENTATTR_PKTINFO) != 0) + pktinfo = &client->pktinfo; + else + pktinfo = NULL; + result = isc_socket_sendto(socket, &r, client->task, client_senddone, + client, address, pktinfo); + if (result == ISC_R_SUCCESS) { + client->nsends++; + return; + } + + done: + if (client->tcpbuf != NULL) { + isc_mem_put(client->mctx, client->tcpbuf, TCP_BUFFER_SIZE); + client->tcpbuf = NULL; + } + ns_client_next(client, result); +} + void ns_client_send(ns_client_t *client) { isc_result_t result; @@ -861,6 +948,7 @@ client_request(isc_task_t *task, isc_event_t *event) { ns_client_t *client; dns_dispatchevent_t *devent; isc_result_t result; + isc_result_t sigresult; isc_buffer_t *buffer; dns_view_t *view; dns_rdataset_t *opt; @@ -1064,12 +1152,7 @@ client_request(isc_task_t *task, isc_event_t *event) { * not. We do not log the lack of a signature unless we are * debugging. */ - result = dns_message_checksig(client->message, client->view); - if (result != ISC_R_SUCCESS) { - ns_client_error(client, result); - goto cleanup_viewlock; - } - + sigresult = dns_message_checksig(client->message, client->view); client->signer = NULL; dns_name_init(&client->signername, NULL); result = dns_message_signer(client->message, &client->signername); @@ -1086,12 +1169,22 @@ client_request(isc_task_t *task, isc_event_t *event) { ns_client_log(client, DNS_LOGCATEGORY_SECURITY, NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3), "request is signed by a nonauthoritative key"); + if (client->message->tsigstatus != dns_tsigerror_badkey && + client->message->opcode != dns_opcode_update) { + ns_client_error(client, sigresult); + goto cleanup_viewlock; + } } else { /* There is a signature, but it is bad. */ ns_client_log(client, DNS_LOGCATEGORY_SECURITY, NS_LOGMODULE_CLIENT, ISC_LOG_ERROR, "request has invalid signature: %s", isc_result_totext(result)); + if (client->message->tsigstatus != dns_tsigerror_badkey && + client->message->opcode != dns_opcode_update) { + ns_client_error(client, sigresult); + goto cleanup_viewlock; + } } /* @@ -1122,7 +1215,7 @@ client_request(isc_task_t *task, isc_event_t *event) { break; case dns_opcode_update: CTRACE("update"); - ns_update_start(client); + ns_update_start(client, sigresult); break; case dns_opcode_notify: CTRACE("notify"); @@ -1437,6 +1530,9 @@ ns_client_attach(ns_client_t *source, ns_client_t **targetp) { REQUIRE(targetp != NULL && *targetp == NULL); source->references++; + ns_client_log(source, NS_LOGCATEGORY_CLIENT, + NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(10), + "ns_client_attach: ref = %d", source->references); *targetp = source; } @@ -1447,6 +1543,9 @@ ns_client_detach(ns_client_t **clientp) { client->references--; INSIST(client->references >= 0); *clientp = NULL; + ns_client_log(client, NS_LOGCATEGORY_CLIENT, + NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(10), + "ns_client_detach: ref = %d", client->references); (void) exit_check(client); } diff --git a/bin/named/include/named/client.h b/bin/named/include/named/client.h index 5662e73d0a..2b499ac974 100644 --- a/bin/named/include/named/client.h +++ b/bin/named/include/named/client.h @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: client.h,v 1.41 2000/09/12 18:45:39 explorer Exp $ */ +/* $Id: client.h,v 1.42 2000/09/13 01:30:33 marka Exp $ */ #ifndef NAMED_CLIENT_H #define NAMED_CLIENT_H 1 @@ -154,6 +154,13 @@ ns_client_send(ns_client_t *client); * send client->message as a response. */ +void +ns_client_sendraw(ns_client_t *client, dns_message_t *msg); +/* + * Finish processing the current client request and + * send client->message as a response. + */ + void ns_client_error(ns_client_t *client, isc_result_t result); /* diff --git a/bin/named/include/named/update.h b/bin/named/include/named/update.h index a8a756ee12..227e0980c9 100644 --- a/bin/named/include/named/update.h +++ b/bin/named/include/named/update.h @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: update.h,v 1.6 2000/08/01 01:12:18 tale Exp $ */ +/* $Id: update.h,v 1.7 2000/09/13 01:30:34 marka Exp $ */ #ifndef NAMED_UPDATE_H #define NAMED_UPDATE_H 1 @@ -44,6 +44,6 @@ ***/ void -ns_update_start(ns_client_t *client); +ns_update_start(ns_client_t *client, isc_result_t sigresult); #endif /* NAMED_UPDATE_H */ diff --git a/bin/named/update.c b/bin/named/update.c index 30deffb037..714c46d485 100644 --- a/bin/named/update.c +++ b/bin/named/update.c @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: update.c,v 1.62 2000/09/12 18:45:34 explorer Exp $ */ +/* $Id: update.c,v 1.63 2000/09/13 01:30:32 marka Exp $ */ #include @@ -48,7 +48,6 @@ /* XXX TODO: - - forwarding - document strict minimality */ @@ -157,6 +156,7 @@ struct update_event { static void update_action(isc_task_t *task, isc_event_t *event); static void updatedone_action(isc_task_t *task, isc_event_t *event); +static isc_result_t send_forward_event(ns_client_t *client, dns_zone_t *zone); /**************************************************************************/ @@ -1822,7 +1822,7 @@ respond(ns_client_t *client, isc_result_t result) { } void -ns_update_start(ns_client_t *client) { +ns_update_start(ns_client_t *client, isc_result_t sigresult) { dns_message_t *request = client->message; isc_result_t result; dns_name_t *zonename; @@ -1865,11 +1865,20 @@ ns_update_start(ns_client_t *client) { switch(dns_zone_gettype(zone)) { case dns_zone_master: + /* + * We can now fail due to a bad signature as we now know + * that we are the master. + */ + if (sigresult != ISC_R_SUCCESS) + FAIL(sigresult); CHECK(send_update_event(client, zone)); break; /* OK. */ case dns_zone_slave: - FAILS(DNS_R_NOTIMP, - "update forwarding"); /* XXX implement */ + if (dns_message_gettsig(client->message, NULL) == NULL) + FAILS(DNS_R_NOTIMP, + "unsigned updates not forwarded"); + CHECK(send_forward_event(client, zone)); + break; /* OK. */ default: FAILC(DNS_R_NOTAUTH, "not authoritative for update zone"); @@ -1883,6 +1892,8 @@ ns_update_start(ns_client_t *client) { * simply give an error response without switching tasks. */ respond(client, result); + if (zone != NULL) + dns_zone_detach(&zone); } static void @@ -2372,3 +2383,70 @@ updatedone_action(isc_task_t *task, isc_event_t *event) { ns_client_detach(&client); isc_event_free(&event); } + +/* + * Update forwarding support. + */ + +static void +forward_fail(ns_client_t *client, isc_result_t result) { + UNUSED(result); + respond(client, DNS_R_SERVFAIL); +} + +static void +forward_callback(void *arg, isc_result_t result, dns_message_t *answer) { + ns_client_t *client = arg; + + if (result != ISC_R_SUCCESS) + forward_fail(client, result); + else + ns_client_sendraw(client, answer); + ns_client_detach(&client); +} + +static void +forward_action(isc_task_t *task, isc_event_t *event) { + update_event_t *uev = (update_event_t *) event; + dns_zone_t *zone = uev->zone; + ns_client_t *client = (ns_client_t *)event->ev_arg; + isc_result_t result; + + result = dns_zone_forwardupdate(zone, client->message, + forward_callback, client); + if (result != ISC_R_SUCCESS) { + forward_fail(client, result); + ns_client_detach(&client); + } + dns_zone_detach(&zone); + isc_event_free(&event); + isc_task_detach(&task); +} + +static isc_result_t +send_forward_event(ns_client_t *client, dns_zone_t *zone) { + isc_result_t result = ISC_R_SUCCESS; + update_event_t *event = NULL; + isc_task_t *zonetask = NULL; + ns_client_t *evclient; + + event = (update_event_t *) + isc_event_allocate(client->mctx, client, DNS_EVENT_UPDATE, + forward_action, NULL, sizeof(*event)); + if (event == NULL) + FAIL(ISC_R_NOMEMORY); + event->zone = zone; + event->result = ISC_R_SUCCESS; + + evclient = NULL; + ns_client_attach(client, &evclient); + event->ev_arg = evclient; + + dns_zone_gettask(zone, &zonetask); + isc_task_send(zonetask, (isc_event_t **)&event); + + failure: + if (event != NULL) + isc_event_free((isc_event_t **)&event); + return (result); +}