1 /*
2 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3 *
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, you can obtain one at https://mozilla.org/MPL/2.0/.
7 *
8 * See the COPYRIGHT file distributed with this work for additional
9 * information regarding copyright ownership.
10 */
11
12 /*! \file */
13
14 #include <config.h>
15
16 #include <inttypes.h>
17
18 #include <isc/mem.h>
19 #include <isc/socket.h>
20 #include <isc/string.h> /* Required for HP/UX (and others?) */
21 #include <isc/util.h>
22
23 #include <dns/db.h>
24 #include <dns/lookup.h>
25 #include <dns/rdata.h>
26 #include <dns/rdataset.h>
27 #include <dns/rdatasetiter.h>
28 #include <dns/result.h>
29 #include <dns/view.h>
30
31 #include <named/types.h>
32 #include <named/lwdclient.h>
33 #include <named/lwresd.h>
34 #include <named/lwsearch.h>
35
36 static void start_lookup(ns_lwdclient_t *);
37
38 static isc_result_t
fill_array(int * pos,dns_rdataset_t * rdataset,int size,unsigned char ** rdatas,uint16_t * rdatalen)39 fill_array(int *pos, dns_rdataset_t *rdataset,
40 int size, unsigned char **rdatas, uint16_t *rdatalen)
41 {
42 dns_rdata_t rdata;
43 isc_result_t result;
44 isc_region_t r;
45
46 UNUSED(size);
47
48 dns_rdata_init(&rdata);
49 for (result = dns_rdataset_first(rdataset);
50 result == ISC_R_SUCCESS;
51 result = dns_rdataset_next(rdataset))
52 {
53 INSIST(*pos < size);
54 dns_rdataset_current(rdataset, &rdata);
55 dns_rdata_toregion(&rdata, &r);
56 rdatas[*pos] = r.base;
57 rdatalen[*pos] = r.length;
58 dns_rdata_reset(&rdata);
59 (*pos)++;
60 }
61 if (result == ISC_R_NOMORE)
62 result = ISC_R_SUCCESS;
63 return (result);
64 }
65
66 static isc_result_t
iterate_node(lwres_grbnresponse_t * grbn,dns_db_t * db,dns_dbnode_t * node,isc_mem_t * mctx)67 iterate_node(lwres_grbnresponse_t *grbn, dns_db_t *db, dns_dbnode_t *node,
68 isc_mem_t *mctx)
69 {
70 int used = 0, count;
71 int size = 8, oldsize = 0;
72 unsigned char **rdatas = NULL, **oldrdatas = NULL, **newrdatas = NULL;
73 uint16_t *lens = NULL, *oldlens = NULL, *newlens = NULL;
74 dns_rdatasetiter_t *iter = NULL;
75 dns_rdataset_t set;
76 dns_ttl_t ttl = INT32_MAX;
77 uint32_t flags = LWRDATA_VALIDATED;
78 isc_result_t result = ISC_R_NOMEMORY;
79
80 result = dns_db_allrdatasets(db, node, NULL, 0, &iter);
81 if (result != ISC_R_SUCCESS)
82 goto out;
83
84 rdatas = isc_mem_get(mctx, size * sizeof(*rdatas));
85 if (rdatas == NULL)
86 goto out;
87 lens = isc_mem_get(mctx, size * sizeof(*lens));
88 if (lens == NULL)
89 goto out;
90
91 for (result = dns_rdatasetiter_first(iter);
92 result == ISC_R_SUCCESS;
93 result = dns_rdatasetiter_next(iter))
94 {
95 result = ISC_R_NOMEMORY;
96 dns_rdataset_init(&set);
97 dns_rdatasetiter_current(iter, &set);
98
99 if (set.type != dns_rdatatype_rrsig) {
100 dns_rdataset_disassociate(&set);
101 continue;
102 }
103
104 count = dns_rdataset_count(&set);
105 if (used + count > size) {
106 /* copy & reallocate */
107 oldsize = size;
108 oldrdatas = rdatas;
109 oldlens = lens;
110 rdatas = NULL;
111 lens = NULL;
112
113 size *= 2;
114
115 rdatas = isc_mem_get(mctx, size * sizeof(*rdatas));
116 if (rdatas == NULL)
117 goto out;
118 lens = isc_mem_get(mctx, size * sizeof(*lens));
119 if (lens == NULL)
120 goto out;
121 memmove(rdatas, oldrdatas, used * sizeof(*rdatas));
122 memmove(lens, oldlens, used * sizeof(*lens));
123 isc_mem_put(mctx, oldrdatas,
124 oldsize * sizeof(*oldrdatas));
125 isc_mem_put(mctx, oldlens, oldsize * sizeof(*oldlens));
126 oldrdatas = NULL;
127 oldlens = NULL;
128 }
129 if (set.ttl < ttl)
130 ttl = set.ttl;
131 if (set.trust != dns_trust_secure)
132 flags &= (~LWRDATA_VALIDATED);
133 result = fill_array(&used, &set, size, rdatas, lens);
134 dns_rdataset_disassociate(&set);
135 if (result != ISC_R_SUCCESS)
136 goto out;
137 }
138 if (result == ISC_R_NOMORE)
139 result = ISC_R_SUCCESS;
140 if (result != ISC_R_SUCCESS)
141 goto out;
142 dns_rdatasetiter_destroy(&iter);
143
144 /*
145 * If necessary, shrink and copy the arrays.
146 */
147 if (size != used) {
148 result = ISC_R_NOMEMORY;
149 newrdatas = isc_mem_get(mctx, used * sizeof(*rdatas));
150 if (newrdatas == NULL)
151 goto out;
152 newlens = isc_mem_get(mctx, used * sizeof(*lens));
153 if (newlens == NULL)
154 goto out;
155 memmove(newrdatas, rdatas, used * sizeof(*rdatas));
156 memmove(newlens, lens, used * sizeof(*lens));
157 isc_mem_put(mctx, rdatas, size * sizeof(*rdatas));
158 isc_mem_put(mctx, lens, size * sizeof(*lens));
159 grbn->rdatas = newrdatas;
160 grbn->rdatalen = newlens;
161 } else {
162 grbn->rdatas = rdatas;
163 grbn->rdatalen = lens;
164 }
165 grbn->nrdatas = used;
166 grbn->ttl = ttl;
167 grbn->flags = flags;
168 return (ISC_R_SUCCESS);
169
170 out:
171 dns_rdatasetiter_destroy(&iter);
172 if (rdatas != NULL)
173 isc_mem_put(mctx, rdatas, size * sizeof(*rdatas));
174 if (lens != NULL)
175 isc_mem_put(mctx, lens, size * sizeof(*lens));
176 if (oldrdatas != NULL)
177 isc_mem_put(mctx, oldrdatas, oldsize * sizeof(*oldrdatas));
178 if (oldlens != NULL)
179 isc_mem_put(mctx, oldlens, oldsize * sizeof(*oldlens));
180 if (newrdatas != NULL)
181 isc_mem_put(mctx, newrdatas, used * sizeof(*newrdatas));
182 return (result);
183 }
184
185 static void
lookup_done(isc_task_t * task,isc_event_t * event)186 lookup_done(isc_task_t *task, isc_event_t *event) {
187 ns_lwdclient_t *client;
188 ns_lwdclientmgr_t *cm;
189 dns_lookupevent_t *levent;
190 lwres_buffer_t lwb;
191 dns_name_t *name;
192 dns_rdataset_t *rdataset;
193 dns_rdataset_t *sigrdataset;
194 isc_result_t result;
195 lwres_result_t lwresult;
196 isc_region_t r;
197 isc_buffer_t b;
198 lwres_grbnresponse_t *grbn;
199 int i;
200
201 REQUIRE(event != NULL);
202
203 UNUSED(task);
204
205 lwb.base = NULL;
206 client = event->ev_arg;
207 cm = client->clientmgr;
208 INSIST(client->lookup == (dns_lookup_t *)event->ev_sender);
209
210 levent = (dns_lookupevent_t *)event;
211 grbn = &client->grbn;
212
213 ns_lwdclient_log(50, "lookup event result = %s",
214 isc_result_totext(levent->result));
215
216 result = levent->result;
217 if (result != ISC_R_SUCCESS) {
218 dns_lookup_destroy(&client->lookup);
219 isc_event_free(&event);
220 levent = NULL;
221
222 switch (result) {
223 case DNS_R_NXDOMAIN:
224 case DNS_R_NCACHENXDOMAIN:
225 result = ns_lwsearchctx_next(&client->searchctx);
226 if (result != ISC_R_SUCCESS)
227 lwresult = LWRES_R_NOTFOUND;
228 else {
229 start_lookup(client);
230 return;
231 }
232 break;
233 case DNS_R_NXRRSET:
234 case DNS_R_NCACHENXRRSET:
235 lwresult = LWRES_R_TYPENOTFOUND;
236 break;
237 default:
238 lwresult = LWRES_R_FAILURE;
239 }
240 ns_lwdclient_errorpktsend(client, lwresult);
241 return;
242 }
243
244 name = levent->name;
245 b = client->recv_buffer;
246
247 grbn->flags = 0;
248
249 grbn->nrdatas = 0;
250 grbn->rdatas = NULL;
251 grbn->rdatalen = NULL;
252
253 grbn->nsigs = 0;
254 grbn->sigs = NULL;
255 grbn->siglen = NULL;
256
257 result = dns_name_totext(name, true, &client->recv_buffer);
258 if (result != ISC_R_SUCCESS)
259 goto out;
260 grbn->realname = (char *)isc_buffer_used(&b);
261 grbn->realnamelen = isc_buffer_usedlength(&client->recv_buffer) -
262 isc_buffer_usedlength(&b);
263 ns_lwdclient_log(50, "found name '%.*s'", grbn->realnamelen,
264 grbn->realname);
265
266 grbn->rdclass = cm->view->rdclass;
267 grbn->rdtype = client->rdtype;
268
269 rdataset = levent->rdataset;
270 if (rdataset != NULL) {
271 /* The normal case */
272 grbn->nrdatas = dns_rdataset_count(rdataset);
273 grbn->rdatas = isc_mem_get(cm->mctx, grbn->nrdatas *
274 sizeof(unsigned char *));
275 if (grbn->rdatas == NULL)
276 goto out;
277 grbn->rdatalen = isc_mem_get(cm->mctx, grbn->nrdatas *
278 sizeof(uint16_t));
279 if (grbn->rdatalen == NULL)
280 goto out;
281
282 i = 0;
283 result = fill_array(&i, rdataset, grbn->nrdatas, grbn->rdatas,
284 grbn->rdatalen);
285 if (result != ISC_R_SUCCESS)
286 goto out;
287 INSIST(i == grbn->nrdatas);
288 grbn->ttl = rdataset->ttl;
289 if (rdataset->trust == dns_trust_secure)
290 grbn->flags |= LWRDATA_VALIDATED;
291 } else {
292 /* The SIG query case */
293 result = iterate_node(grbn, levent->db, levent->node,
294 cm->mctx);
295 if (result != ISC_R_SUCCESS)
296 goto out;
297 }
298 ns_lwdclient_log(50, "filled in %d rdata%s", grbn->nrdatas,
299 (grbn->nrdatas == 1) ? "" : "s");
300
301 sigrdataset = levent->sigrdataset;
302 if (sigrdataset != NULL) {
303 grbn->nsigs = dns_rdataset_count(sigrdataset);
304 grbn->sigs = isc_mem_get(cm->mctx, grbn->nsigs *
305 sizeof(unsigned char *));
306 if (grbn->sigs == NULL)
307 goto out;
308 grbn->siglen = isc_mem_get(cm->mctx, grbn->nsigs *
309 sizeof(uint16_t));
310 if (grbn->siglen == NULL)
311 goto out;
312
313 i = 0;
314 result = fill_array(&i, sigrdataset, grbn->nsigs, grbn->sigs,
315 grbn->siglen);
316 if (result != ISC_R_SUCCESS)
317 goto out;
318 INSIST(i == grbn->nsigs);
319 ns_lwdclient_log(50, "filled in %d signature%s", grbn->nsigs,
320 (grbn->nsigs == 1) ? "" : "s");
321 }
322
323 /*
324 * Render the packet.
325 */
326 client->pkt.recvlength = LWRES_RECVLENGTH;
327 client->pkt.authtype = 0; /* XXXMLG */
328 client->pkt.authlength = 0;
329 client->pkt.result = LWRES_R_SUCCESS;
330
331 lwresult = lwres_grbnresponse_render(cm->lwctx,
332 grbn, &client->pkt, &lwb);
333 if (lwresult != LWRES_R_SUCCESS)
334 goto out;
335
336 isc_mem_put(cm->mctx, grbn->rdatas,
337 grbn->nrdatas * sizeof(unsigned char *));
338 isc_mem_put(cm->mctx, grbn->rdatalen,
339 grbn->nrdatas * sizeof(uint16_t));
340
341 if (grbn->sigs != NULL)
342 isc_mem_put(cm->mctx, grbn->sigs,
343 grbn->nsigs * sizeof(unsigned char *));
344 if (grbn->siglen != NULL)
345 isc_mem_put(cm->mctx, grbn->siglen,
346 grbn->nsigs * sizeof(uint16_t));
347
348 r.base = lwb.base;
349 r.length = lwb.used;
350 client->sendbuf = r.base;
351 client->sendlength = r.length;
352 result = ns_lwdclient_sendreply(client, &r);
353 if (result != ISC_R_SUCCESS)
354 goto out2;
355
356 NS_LWDCLIENT_SETSEND(client);
357
358 dns_lookup_destroy(&client->lookup);
359 isc_event_free(&event);
360
361 return;
362
363 out:
364 if (grbn->rdatas != NULL)
365 isc_mem_put(cm->mctx, grbn->rdatas,
366 grbn->nrdatas * sizeof(unsigned char *));
367 if (grbn->rdatalen != NULL)
368 isc_mem_put(cm->mctx, grbn->rdatalen,
369 grbn->nrdatas * sizeof(uint16_t));
370
371 if (grbn->sigs != NULL)
372 isc_mem_put(cm->mctx, grbn->sigs,
373 grbn->nsigs * sizeof(unsigned char *));
374 if (grbn->siglen != NULL)
375 isc_mem_put(cm->mctx, grbn->siglen,
376 grbn->nsigs * sizeof(uint16_t));
377 out2:
378 if (client->lookup != NULL)
379 dns_lookup_destroy(&client->lookup);
380 if (lwb.base != NULL)
381 lwres_context_freemem(cm->lwctx, lwb.base, lwb.length);
382
383 isc_event_free(&event);
384
385 ns_lwdclient_log(50, "error constructing getrrsetbyname response");
386 ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
387 }
388
389 static void
start_lookup(ns_lwdclient_t * client)390 start_lookup(ns_lwdclient_t *client) {
391 isc_result_t result;
392 ns_lwdclientmgr_t *cm;
393 dns_fixedname_t absname;
394
395 cm = client->clientmgr;
396
397 INSIST(client->lookup == NULL);
398
399 dns_fixedname_init(&absname);
400
401 /*
402 * Perform search across all search domains until success
403 * is returned. Return in case of failure.
404 */
405 while (ns_lwsearchctx_current(&client->searchctx,
406 dns_fixedname_name(&absname)) != ISC_R_SUCCESS) {
407 if (ns_lwsearchctx_next(&client->searchctx) != ISC_R_SUCCESS) {
408 ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
409 return;
410 }
411 }
412
413 result = dns_lookup_create(cm->mctx,
414 dns_fixedname_name(&absname),
415 client->rdtype, cm->view,
416 client->options, cm->task, lookup_done,
417 client, &client->lookup);
418 if (result != ISC_R_SUCCESS) {
419 ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
420 return;
421 }
422 }
423
424 static void
init_grbn(ns_lwdclient_t * client)425 init_grbn(ns_lwdclient_t *client) {
426 client->grbn.rdclass = 0;
427 client->grbn.rdtype = 0;
428 client->grbn.ttl = 0;
429 client->grbn.nrdatas = 0;
430 client->grbn.realname = NULL;
431 client->grbn.realnamelen = 0;
432 client->grbn.rdatas = 0;
433 client->grbn.rdatalen = 0;
434 client->grbn.base = NULL;
435 client->grbn.baselen = 0;
436 isc_buffer_init(&client->recv_buffer, client->buffer, LWRES_RECVLENGTH);
437 }
438
439 void
ns_lwdclient_processgrbn(ns_lwdclient_t * client,lwres_buffer_t * b)440 ns_lwdclient_processgrbn(ns_lwdclient_t *client, lwres_buffer_t *b) {
441 lwres_grbnrequest_t *req;
442 isc_result_t result;
443 ns_lwdclientmgr_t *cm;
444 isc_buffer_t namebuf;
445
446 REQUIRE(NS_LWDCLIENT_ISRECVDONE(client));
447 INSIST(client->byaddr == NULL);
448
449 cm = client->clientmgr;
450 req = NULL;
451
452 result = lwres_grbnrequest_parse(cm->lwctx,
453 b, &client->pkt, &req);
454 if (result != LWRES_R_SUCCESS)
455 goto out;
456 if (req->name == NULL)
457 goto out;
458
459 client->options = 0;
460 if (req->rdclass != cm->view->rdclass)
461 goto out;
462
463 if (req->rdclass == dns_rdataclass_any ||
464 req->rdtype == dns_rdatatype_any)
465 goto out;
466
467 client->rdtype = req->rdtype;
468
469 isc_buffer_init(&namebuf, req->name, req->namelen);
470 isc_buffer_add(&namebuf, req->namelen);
471
472 dns_fixedname_init(&client->query_name);
473 result = dns_name_fromtext(dns_fixedname_name(&client->query_name),
474 &namebuf, NULL, 0, NULL);
475 if (result != ISC_R_SUCCESS)
476 goto out;
477 ns_lwsearchctx_init(&client->searchctx,
478 cm->listener->manager->search,
479 dns_fixedname_name(&client->query_name),
480 cm->listener->manager->ndots);
481 ns_lwsearchctx_first(&client->searchctx);
482
483 ns_lwdclient_log(50, "client %p looking for type %d",
484 client, client->rdtype);
485
486 /*
487 * We no longer need to keep this around.
488 */
489 lwres_grbnrequest_free(cm->lwctx, &req);
490
491 /*
492 * Initialize the real name and alias arrays in the reply we're
493 * going to build up.
494 */
495 init_grbn(client);
496
497 /*
498 * Start the find.
499 */
500 start_lookup(client);
501
502 return;
503
504 /*
505 * We're screwed. Return an error packet to our caller.
506 */
507 out:
508 if (req != NULL)
509 lwres_grbnrequest_free(cm->lwctx, &req);
510
511 ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
512 }
513