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