Commit ac77fece authored by Bob Halley's avatar Bob Halley

EDNS0

parent 94c9d5a8
......@@ -27,6 +27,9 @@
#include <dns/dispatch.h>
#include <dns/events.h>
#include <dns/message.h>
#include <dns/rdata.h>
#include <dns/rdatalist.h>
#include <dns/rdataset.h>
#include <dns/view.h>
#include <named/client.h>
......@@ -190,6 +193,8 @@ ns_client_next(ns_client_t *client, isc_result_t result) {
if (client->view != NULL)
dns_view_detach(&client->view);
if (client->opt != NULL)
client->opt = NULL;
dns_message_reset(client->message, DNS_MESSAGE_INTENTPARSE);
if (client->dispevent != NULL) {
dns_dispatch_freeevent(client->dispatch, client->dispentry,
......@@ -248,6 +253,7 @@ ns_client_send(ns_client_t *client) {
isc_region_t r;
isc_socket_t *socket;
isc_sockaddr_t *address;
unsigned int bufsize = 512;
REQUIRE(NS_CLIENT_VALID(client));
......@@ -272,7 +278,7 @@ ns_client_send(ns_client_t *client) {
/*
* XXXRTH The following doesn't deal with TSIGs, TCP buffer resizing,
* EDNS0 UDP buffer limits, or ENDS1 more data packets.
* or ENDS1 more data packets.
*/
if (TCP_CLIENT(client)) {
/*
......@@ -283,13 +289,23 @@ ns_client_send(ns_client_t *client) {
isc_buffer_init(&buffer, data + 2, SEND_BUFFER_SIZE - 2,
ISC_BUFFERTYPE_BINARY);
} else {
isc_buffer_init(&buffer, data, 512,
ISC_BUFFERTYPE_BINARY);
if (client->opt != NULL) {
if (client->opt->rdclass < SEND_BUFFER_SIZE)
bufsize = client->opt->rdclass;
else
bufsize = SEND_BUFFER_SIZE;
}
isc_buffer_init(&buffer, data, bufsize, ISC_BUFFERTYPE_BINARY);
}
result = dns_message_renderbegin(client->message, &buffer);
if (result != ISC_R_SUCCESS)
goto done;
if (client->opt != NULL) {
result = dns_message_setopt(client->message, client->opt);
if (result != ISC_R_SUCCESS)
goto done;
}
result = dns_message_rendersection(client->message,
DNS_SECTION_QUESTION, 0, 0);
if (result != ISC_R_SUCCESS)
......@@ -385,6 +401,57 @@ ns_client_error(ns_client_t *client, isc_result_t result) {
ns_client_send(client);
}
static inline isc_result_t
client_addopt(ns_client_t *client) {
dns_rdataset_t *rdataset;
dns_rdatalist_t *rdatalist;
dns_rdata_t *rdata;
isc_result_t result;
REQUIRE(client->opt == NULL); /* XXXRTH free old. */
rdataset = NULL;
result = dns_message_gettemprdataset(client->message, &rdataset);
if (result != ISC_R_SUCCESS)
return (result);
dns_rdataset_init(rdataset);
rdatalist = NULL;
result = dns_message_gettemprdatalist(client->message, &rdatalist);
if (result != ISC_R_SUCCESS)
return (result);
rdata = NULL;
result = dns_message_gettemprdata(client->message, &rdata);
if (result != ISC_R_SUCCESS)
return (result);
rdatalist->type = dns_rdatatype_opt;
rdatalist->covers = 0;
/*
* Set Maximum UDP buffer size.
*/
rdatalist->rdclass = SEND_BUFFER_SIZE;
/*
* Set EXTENDED-RCODE, VERSION, and Z to 0.
*/
rdatalist->ttl = 0;
/*
* No ENDS options.
*/
rdata->data = NULL;
rdata->length = 0;
ISC_LIST_INIT(rdatalist->rdata);
ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
dns_rdatalist_tordataset(rdatalist, rdataset);
client->opt = rdataset;
return (ISC_R_SUCCESS);
}
static void
client_request(isc_task_t *task, isc_event_t *event) {
ns_client_t *client;
......@@ -392,6 +459,7 @@ client_request(isc_task_t *task, isc_event_t *event) {
isc_result_t result;
isc_buffer_t *buffer;
dns_view_t *view;
dns_rdataset_t *opt;
REQUIRE(event != NULL);
client = event->arg;
......@@ -432,6 +500,34 @@ client_request(isc_task_t *task, isc_event_t *event) {
}
INSIST((client->message->flags & DNS_MESSAGEFLAG_QR) == 0);
/*
* Deal with EDNS.
*/
opt = dns_message_getopt(client->message);
if (opt != NULL) {
unsigned int version;
/*
* Create an OPT for our reply.
*/
result = client_addopt(client);
if (result != ISC_R_SUCCESS) {
ns_client_error(client, result);
return;
}
/*
* Do we understand this version of ENDS?
*
* XXXRTH need library support for this!
*/
version = (opt->ttl & 0x00FF0000) >> 16;
if (version != 0) {
ns_client_error(client, DNS_R_BADVERS);
return;
}
}
/*
* XXXRTH View list management code will be moving to its own module
* soon.
......@@ -565,6 +661,7 @@ client_create(ns_clientmgr_t *manager, ns_clienttype_t type,
client->tcplistener = NULL;
client->tcpsocket = NULL;
client->nsends = 0;
client->opt = NULL;
client->next = NULL;
ISC_LINK_INIT(client, link);
......
......@@ -63,6 +63,7 @@ struct ns_client {
dns_message_t * message;
unsigned int nsends;
isc_mempool_t * sendbufs;
dns_rdataset_t * opt;
void (*next)(ns_client_t *, isc_result_t);
ns_query_t query;
isc_stdtime_t requesttime;
......
......@@ -75,7 +75,8 @@ static char *rcodetext[] = {
"RESERVED12",
"RESERVED13",
"RESERVED14",
"RESERVED15"
"RESERVED15",
"BADVERS"
};
static dns_result_t
......@@ -87,7 +88,7 @@ printsection(dns_message_t *msg, dns_section_t sectionid, char *section_name)
dns_result_t result;
isc_region_t r;
dns_name_t empty_name;
char t[1000];
char t[4096];
isc_boolean_t first;
isc_boolean_t no_rdata;
......@@ -117,22 +118,13 @@ printsection(dns_message_t *msg, dns_section_t sectionid, char *section_name)
for (rdataset = ISC_LIST_HEAD(name->list);
rdataset != NULL;
rdataset = ISC_LIST_NEXT(rdataset, link)) {
if (rdataset->type == dns_rdatatype_opt) {
/*
* XXX
*/
printf("OPT: udp=%u, ttl=%u\n",
(unsigned int)rdataset->rdclass,
(unsigned int)rdataset->ttl);
} else {
result = dns_rdataset_totext(rdataset,
print_name,
ISC_FALSE,
no_rdata,
&target);
if (result != DNS_R_SUCCESS)
return (result);
}
result = dns_rdataset_totext(rdataset,
print_name,
ISC_FALSE,
no_rdata,
&target);
if (result != DNS_R_SUCCESS)
return (result);
#ifdef USEINITALWS
if (first) {
print_name = &empty_name;
......@@ -157,9 +149,11 @@ dns_result_t
printmessage(dns_message_t *msg) {
isc_boolean_t did_flag = ISC_FALSE;
dns_result_t result;
dns_rdataset_t *opt;
result = DNS_R_SUCCESS;
printf("rcode = %u\n", msg->rcode);
printf(";; ->>HEADER<<- opcode: %s, status: %s, id: %u\n",
opcodetext[msg->opcode], rcodetext[msg->rcode], msg->id);
......@@ -189,6 +183,12 @@ printmessage(dns_message_t *msg) {
msg->counts[DNS_SECTION_ANSWER],
msg->counts[DNS_SECTION_AUTHORITY],
msg->counts[DNS_SECTION_ADDITIONAL]);
opt = dns_message_getopt(msg);
if (opt != NULL)
printf(";; EDNS: version: %u, udp=%u\n",
(unsigned int)((opt->ttl & 0x00ff0000) >> 16),
(unsigned int)opt->rdclass);
if (msg->counts[DNS_SECTION_TSIG] > 0)
printf(";; PSEUDOSECTIONS: TSIG: %u\n",
msg->counts[DNS_SECTION_TSIG]);
......
......@@ -94,7 +94,7 @@ add_type(dns_message_t *message, dns_name_t *name, dns_rdataclass_t rdclass,
}
static void
add_opt(dns_message_t *message, dns_name_t *name, isc_uint16_t udpsize) {
add_opt(dns_message_t *message, isc_uint16_t udpsize) {
dns_rdataset_t *rdataset;
dns_rdatalist_t *rdatalist;
dns_rdata_t *rdata;
......@@ -133,7 +133,9 @@ add_opt(dns_message_t *message, dns_name_t *name, isc_uint16_t udpsize) {
ISC_LIST_INIT(rdatalist->rdata);
ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
dns_rdatalist_tordataset(rdatalist, rdataset);
ISC_LIST_APPEND(name->list, rdataset, link);
result = dns_message_setopt(message, rdataset);
check_result(result, "dns_message_setopt()");
}
static void
......@@ -217,7 +219,6 @@ main(int argc, char *argv[]) {
isc_boolean_t vc, have_name, have_type, edns0;
dns_fixedname_t fname;
dns_name_t *name;
dns_name_t optname;
dns_rdatatype_t rdtype;
dns_rdataclass_t rdclass, nclass;
size_t len;
......@@ -272,8 +273,6 @@ main(int argc, char *argv[]) {
dns_fixedname_init(&fname);
name = dns_fixedname_name(&fname);
dns_name_init(&optname, NULL);
dns_name_clone(dns_rootname, &optname);
message = NULL;
result = dns_message_create(mctx, DNS_MESSAGE_INTENTRENDER, &message);
check_result(result, "dns_message_create()");
......@@ -295,11 +294,7 @@ main(int argc, char *argv[]) {
} else if (strcmp(argv[0], "+vc") == 0) {
fatal("TCP transport not yet implemented");
} else if (strcmp(argv[0], "+edns0") == 0) {
if (!edns0) {
add_opt(message, &optname,
(isc_uint16_t)SDIG_BUFFER_SIZE);
edns0 = ISC_TRUE;
}
edns0 = ISC_TRUE;
} else {
len = strlen(argv[0]);
tr.base = argv[0];
......@@ -340,21 +335,15 @@ main(int argc, char *argv[]) {
message->opcode = dns_opcode_query;
message->flags |= DNS_MESSAGEFLAG_RD;
dns_message_addname(message, name, DNS_SECTION_QUESTION);
if (edns0)
dns_message_addname(message, &optname, DNS_SECTION_ADDITIONAL);
isc_buffer_init(&b, data, sizeof data, ISC_BUFFERTYPE_BINARY);
result = dns_message_renderbegin(message, &b);
check_result(result, "dns_message_renderbegin()");
if (edns0)
add_opt(message, (isc_uint16_t)SDIG_BUFFER_SIZE);
result = dns_message_rendersection(message, DNS_SECTION_QUESTION,
0, 0);
check_result(result, "dns_message_rendersection()");
if (edns0) {
result = dns_message_rendersection(message,
DNS_SECTION_ADDITIONAL,
0, 0);
check_result(result, "dns_message_rendersection()");
}
result = dns_message_renderend(message);
check_result(result, "dns_message_renderend()");
......
......@@ -699,7 +699,7 @@ dns_message_reply(dns_message_t *msg, isc_boolean_t want_question_section);
*
* Requires:
*
* 'msg' is a valid message with parsing intent, and contains a query
* 'msg' is a valid message with parsing intent, and contains a query.
*
* Ensures:
*
......@@ -723,6 +723,45 @@ dns_message_reply(dns_message_t *msg, isc_boolean_t want_question_section);
* sections may be bad.
*/
dns_rdataset_t *
dns_message_getopt(dns_message_t *msg);
/*
* Get the OPT record for 'msg'.
*
* Requires:
*
* 'msg' is a valid message.
*
* Returns:
*
* The OPT rdataset of 'msg', or NULL if there isn't one.
*/
dns_result_t
dns_message_setopt(dns_message_t *msg, dns_rdataset_t *opt);
/*
* Set the OPT record for 'msg'.
*
* Requires:
*
* 'msg' is a valid message with rendering intent,
* dns_message_renderbegin() has been called, and no sections have been
* rendered.
*
* 'opt' is a valid OPT record.
*
* Ensures:
*
* The OPT record will be rendered when dns_message_renderend() is
* called.
*
* Returns:
*
* DNS_R_SUCCESS -- all is well.
*
* DNS_R_NOSPACE -- there is no space for the OPT record.
*/
ISC_LANG_ENDDECLS
#endif /* DNS_DNS_H */
......@@ -39,9 +39,13 @@
#include <dns/tsig.h>
#define DNS_MESSAGE_OPCODE_MASK 0x7800U
#define DNS_MESSAGE_OPCODE_SHIFT 11
#define DNS_MESSAGE_OPCODE_SHIFT 11
#define DNS_MESSAGE_RCODE_MASK 0x000fU
#define DNS_MESSAGE_FLAG_MASK 0x8ff0U
#define DNS_MESSAGE_EDNSRCODE_MASK 0xff000000U
#define DNS_MESSAGE_EDNSRCODE_SHIFT 24
#define DNS_MESSAGE_EDNSVERSION_MASK 0x00ff0000U
#define DNS_MESSAGE_EDNSVERSION_SHIFT 16
#define VALID_NAMED_SECTION(s) (((s) > DNS_SECTION_ANY) \
&& ((s) < DNS_SECTION_MAX))
......@@ -413,6 +417,10 @@ msgreset(dns_message_t *msg, isc_boolean_t everything)
msgresetnames(msg, 0);
if (msg->opt != NULL)
dns_rdataset_disassociate(msg->opt);
msg->opt = NULL;
/*
* Clean up linked lists.
*/
......@@ -516,7 +524,8 @@ msgreset(dns_message_t *msg, isc_boolean_t everything)
if (msg->tsig != NULL) {
dns_rdata_freestruct(msg->tsig);
isc_mem_put(msg->mctx, msg->tsig, sizeof(dns_rdata_any_tsig_t));
isc_mem_put(msg->mctx, msg->tsig,
sizeof(dns_rdata_any_tsig_t));
}
if (msg->querytsig != NULL) {
......@@ -981,6 +990,7 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
*/
if (msg->opcode != dns_opcode_update
&& rdtype != dns_rdatatype_tsig
&& rdtype != dns_rdatatype_opt
&& msg->rdclass != rdclass)
return (DNS_R_FORMERR);
......@@ -1028,7 +1038,8 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
*/
if (preserve_order || msg->opcode == dns_opcode_update ||
skip_search) {
ISC_LIST_APPEND(*section, name, link);
if (rdtype != dns_rdatatype_opt)
ISC_LIST_APPEND(*section, name, link);
} else {
/*
* Run through the section, looking to see if this name
......@@ -1107,7 +1118,8 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
dns_rdataset_init(rdataset);
dns_rdatalist_tordataset(rdatalist, rdataset);
ISC_LIST_APPEND(name->list, rdataset, link);
if (rdtype != dns_rdatatype_opt)
ISC_LIST_APPEND(name->list, rdataset, link);
}
/*
......@@ -1125,10 +1137,17 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
/*
* If this is an OPT record, remember it.
* If this is an OPT record, remember it. Also, set
* the extended rcode.
*/
if (rdtype == dns_rdatatype_opt)
if (rdtype == dns_rdatatype_opt) {
unsigned int ercode;
msg->opt = rdataset;
ercode = (msg->opt->ttl & DNS_MESSAGE_EDNSRCODE_MASK)
>> 20;
msg->rcode |= ercode;
}
}
return (DNS_R_SUCCESS);
......@@ -1412,7 +1431,7 @@ dns_message_renderheader(dns_message_t *msg, isc_buffer_t *target)
tmp = ((msg->opcode << DNS_MESSAGE_OPCODE_SHIFT)
& DNS_MESSAGE_OPCODE_MASK);
tmp |= (msg->rcode & DNS_MESSAGE_RCODE_MASK); /* XXX edns? */
tmp |= (msg->rcode & DNS_MESSAGE_RCODE_MASK);
tmp |= (msg->flags & DNS_MESSAGE_FLAG_MASK);
isc_buffer_putuint16(target, tmp);
......@@ -1430,10 +1449,47 @@ dns_message_renderend(dns_message_t *msg)
isc_buffer_t tmpbuf;
isc_region_t r;
int result;
dns_rdata_t rdata;
unsigned int count;
REQUIRE(DNS_MESSAGE_VALID(msg));
REQUIRE(msg->buffer != NULL);
if ((msg->rcode & ~DNS_MESSAGE_RCODE_MASK) != 0 && msg->opt == NULL) {
/*
* We have an extended rcode but are not using EDNS.
*/
return (DNS_R_FORMERR);
}
/*
* If we've got an OPT record, render it.
*/
if (msg->opt != NULL) {
result = dns_rdataset_first(msg->opt);
if (result != ISC_R_SUCCESS)
return (result);
dns_rdataset_current(msg->opt, &rdata);
result = dns_message_renderrelease(msg, 11 + rdata.length);
if (result != ISC_R_SUCCESS)
return (result);
/*
* Set the extended rcode.
*/
msg->opt->ttl &= ~DNS_MESSAGE_EDNSRCODE_MASK;
msg->opt->ttl |= ((msg->rcode << 20) &
DNS_MESSAGE_EDNSRCODE_MASK);
/*
* Render.
*/
count = 0;
result = dns_rdataset_towire(msg->opt, dns_rootname,
&msg->cctx, msg->buffer, &count);
msg->counts[DNS_SECTION_ADDITIONAL] += count;
if (result != ISC_R_SUCCESS)
return (result);
}
if (msg->tsigkey != NULL ||
((msg->flags & DNS_MESSAGEFLAG_QR) != 0 &&
msg->querytsigstatus != dns_rcode_noerror))
......@@ -1441,7 +1497,8 @@ dns_message_renderend(dns_message_t *msg)
result = dns_tsig_sign(msg);
if (result != DNS_R_SUCCESS)
return (result);
result = dns_message_rendersection(msg, DNS_SECTION_TSIG, 0, 0);
result = dns_message_rendersection(msg, DNS_SECTION_TSIG, 0,
0);
if (result != DNS_R_SUCCESS)
return (result);
}
......@@ -1744,3 +1801,67 @@ dns_message_reply(dns_message_t *msg, isc_boolean_t want_question_section) {
return (DNS_R_SUCCESS);
}
dns_rdataset_t *
dns_message_getopt(dns_message_t *msg) {
/*
* Get the OPT record for 'msg'.
*/
REQUIRE(DNS_MESSAGE_VALID(msg));
return (msg->opt);
}
dns_result_t
dns_message_setopt(dns_message_t *msg, dns_rdataset_t *opt) {
dns_result_t result;
dns_rdata_t rdata;
/*
* Set the OPT record for 'msg'.
*/
/*
* The space required for an OPT record is:
*
* 1 byte for the name
* 2 bytes for the type
* 2 bytes for the class
* 4 bytes for the ttl
* 2 bytes for the rdata length
* ---------------------------------
* 11 bytes
*
* plus the length of the rdata.
*/
REQUIRE(DNS_MESSAGE_VALID(msg));
REQUIRE(opt->type == dns_rdatatype_opt);
REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER);
REQUIRE(msg->buffer != NULL);
REQUIRE(msg->state == DNS_SECTION_ANY);
if (msg->opt != NULL) {
result = dns_rdataset_first(msg->opt);
if (result != ISC_R_SUCCESS)
return (result);
dns_rdataset_current(msg->opt, &rdata);
result = dns_message_renderrelease(msg, 11 + rdata.length);
dns_rdataset_disassociate(msg->opt);
msg->opt = NULL;
}
result = dns_rdataset_first(opt);
if (result != ISC_R_SUCCESS)
return (result);
dns_rdataset_current(opt, &rdata);
result = dns_message_renderreserve(msg, 11 + rdata.length);
if (result != ISC_R_SUCCESS)
return (result);
msg->opt = opt;
return (DNS_R_SUCCESS);
}
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