torture: smbtorture test case to verify Compound related handling
[Samba.git] / lib / addns / dnsquery.c
blob869d4597ee40f58916cdbf031c8ad493522f352c
1 /*
2 Unix SMB/CIFS implementation.
3 DNS utility library
4 Copyright (C) Gerald (Jerry) Carter 2006.
5 Copyright (C) Jeremy Allison 2007.
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include "includes.h"
22 #include "lib/util/util_net.h"
23 #include "lib/util/tsort.h"
24 #include "librpc/gen_ndr/dns.h"
25 #include "libcli/dns/dns_lookup.h"
26 #include "lib/util/tevent_ntstatus.h"
27 #include "dnsquery.h"
29 /*********************************************************************
30 Sort SRV record list based on weight and priority. See RFC 2782.
31 *********************************************************************/
33 static int dnssrvcmp( struct dns_rr_srv *a, struct dns_rr_srv *b )
35 if ( a->priority == b->priority ) {
37 /* randomize entries with an equal weight and priority */
38 if ( a->weight == b->weight )
39 return 0;
41 /* higher weights should be sorted lower */
42 if ( a->weight > b->weight )
43 return -1;
44 else
45 return 1;
48 if ( a->priority < b->priority )
49 return -1;
51 return 1;
54 struct ads_dns_lookup_srv_state {
55 struct dns_rr_srv *srvs;
56 size_t num_srvs;
59 static void ads_dns_lookup_srv_done(struct tevent_req *subreq);
61 struct tevent_req *ads_dns_lookup_srv_send(TALLOC_CTX *mem_ctx,
62 struct tevent_context *ev,
63 const char *name)
65 struct tevent_req *req, *subreq;
66 struct ads_dns_lookup_srv_state *state;
68 req = tevent_req_create(mem_ctx, &state,
69 struct ads_dns_lookup_srv_state);
70 if (req == NULL) {
71 return NULL;
74 subreq = dns_lookup_send(
75 state,
76 ev,
77 NULL,
78 name,
79 DNS_QCLASS_IN,
80 DNS_QTYPE_SRV);
82 if (tevent_req_nomem(subreq, req)) {
83 return tevent_req_post(req, ev);
85 tevent_req_set_callback(subreq, ads_dns_lookup_srv_done, req);
86 return req;
89 static void ads_dns_lookup_srv_done(struct tevent_req *subreq)
91 struct tevent_req *req = tevent_req_callback_data(
92 subreq, struct tevent_req);
93 struct ads_dns_lookup_srv_state *state = tevent_req_data(
94 req, struct ads_dns_lookup_srv_state);
95 int ret;
96 struct dns_name_packet *reply;
97 uint16_t i, idx;
99 ret = dns_lookup_recv(subreq, state, &reply);
100 TALLOC_FREE(subreq);
101 if (ret != 0) {
102 tevent_req_nterror(req, map_nt_error_from_unix_common(ret));
103 return;
106 for (i=0; i<reply->ancount; i++) {
107 if (reply->answers[i].rr_type == DNS_QTYPE_SRV) {
108 /* uint16_t can't wrap here. */
109 state->num_srvs += 1;
113 state->srvs = talloc_array(state, struct dns_rr_srv, state->num_srvs);
114 if (tevent_req_nomem(state->srvs, req)) {
115 return;
118 idx = 0;
120 for (i=0; i<reply->ancount; i++) {
121 struct dns_res_rec *an = &reply->answers[i];
122 struct dns_rr_srv *dst = &state->srvs[idx];
123 struct dns_srv_record *src;
125 if (an->rr_type != DNS_QTYPE_SRV) {
126 continue;
128 src = &an->rdata.srv_record;
130 *dst = (struct dns_rr_srv) {
131 .hostname = talloc_move(state->srvs, &src->target),
132 .priority = src->priority,
133 .weight = src->weight,
134 .port = src->port,
136 idx += 1;
139 for (i=0; i<reply->arcount; i++) {
140 struct dns_res_rec *ar = &reply->additional[i];
141 struct sockaddr_storage addr;
142 bool ok;
143 size_t j;
145 ok = dns_res_rec_get_sockaddr(ar, &addr);
146 if (!ok) {
147 continue;
150 for (j=0; j<state->num_srvs; j++) {
151 struct dns_rr_srv *srv = &state->srvs[j];
152 struct sockaddr_storage *tmp;
154 if (strcmp(srv->hostname, ar->name) != 0) {
155 continue;
157 /* uint16_t can't wrap here. */
158 tmp = talloc_realloc(
159 state->srvs,
160 srv->ss_s,
161 struct sockaddr_storage,
162 srv->num_ips+1);
164 if (tevent_req_nomem(tmp, req)) {
165 return;
167 srv->ss_s = tmp;
169 srv->ss_s[srv->num_ips] = addr;
170 srv->num_ips += 1;
174 TYPESAFE_QSORT(state->srvs, state->num_srvs, dnssrvcmp);
176 tevent_req_done(req);
179 NTSTATUS ads_dns_lookup_srv_recv(struct tevent_req *req,
180 TALLOC_CTX *mem_ctx,
181 struct dns_rr_srv **srvs,
182 size_t *num_srvs)
184 struct ads_dns_lookup_srv_state *state = tevent_req_data(
185 req, struct ads_dns_lookup_srv_state);
186 NTSTATUS status;
188 if (tevent_req_is_nterror(req, &status)) {
189 return status;
191 *srvs = talloc_move(mem_ctx, &state->srvs);
192 *num_srvs = state->num_srvs;
193 tevent_req_received(req);
194 return NT_STATUS_OK;
197 /*********************************************************************
198 Simple wrapper for a DNS SRV query
199 *********************************************************************/
201 NTSTATUS ads_dns_lookup_srv(TALLOC_CTX *ctx,
202 const char *name,
203 struct dns_rr_srv **dclist,
204 size_t *numdcs)
206 struct tevent_context *ev;
207 struct tevent_req *req;
208 NTSTATUS status = NT_STATUS_NO_MEMORY;
209 size_t num_srvs = 0;
211 ev = samba_tevent_context_init(ctx);
212 if (ev == NULL) {
213 goto fail;
215 req = ads_dns_lookup_srv_send(ev, ev, name);
216 if (req == NULL) {
217 goto fail;
219 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
220 goto fail;
222 status = ads_dns_lookup_srv_recv(req, ctx, dclist, &num_srvs);
223 if (NT_STATUS_IS_OK(status)) {
224 *numdcs = num_srvs;
226 fail:
227 TALLOC_FREE(ev);
228 return status;
231 struct ads_dns_lookup_ns_state {
232 struct dns_rr_ns *nss;
233 size_t num_nss;
236 static void ads_dns_lookup_ns_done(struct tevent_req *subreq);
238 struct tevent_req *ads_dns_lookup_ns_send(TALLOC_CTX *mem_ctx,
239 struct tevent_context *ev,
240 const char *name)
242 struct tevent_req *req, *subreq;
243 struct ads_dns_lookup_ns_state *state;
245 req = tevent_req_create(mem_ctx, &state,
246 struct ads_dns_lookup_ns_state);
247 if (req == NULL) {
248 return NULL;
251 subreq = dns_lookup_send(state, ev, NULL, name, DNS_QCLASS_IN,
252 DNS_QTYPE_NS);
253 if (tevent_req_nomem(subreq, req)) {
254 return tevent_req_post(req, ev);
256 tevent_req_set_callback(subreq, ads_dns_lookup_ns_done, req);
257 return req;
260 static void ads_dns_lookup_ns_done(struct tevent_req *subreq)
262 struct tevent_req *req = tevent_req_callback_data(
263 subreq, struct tevent_req);
264 struct ads_dns_lookup_ns_state *state = tevent_req_data(
265 req, struct ads_dns_lookup_ns_state);
266 int ret;
267 struct dns_name_packet *reply;
268 uint16_t i, idx;
270 ret = dns_lookup_recv(subreq, state, &reply);
271 TALLOC_FREE(subreq);
272 if (ret != 0) {
273 tevent_req_nterror(req, map_nt_error_from_unix_common(ret));
274 return;
277 for (i=0; i<reply->ancount; i++) {
278 if (reply->answers[i].rr_type == DNS_QTYPE_NS) {
279 state->num_nss += 1;
283 state->nss = talloc_array(state, struct dns_rr_ns, state->num_nss);
284 if (tevent_req_nomem(state->nss, req)) {
285 return;
288 idx = 0;
290 for (i=0; i<reply->ancount; i++) {
291 struct dns_res_rec *an = &reply->answers[i];
293 if (an->rr_type != DNS_QTYPE_NS) {
294 continue;
297 state->nss[idx].hostname = talloc_move(state->nss,
298 &an->rdata.ns_record);
299 idx += 1;
302 for (i=0; i<reply->arcount; i++) {
303 struct dns_res_rec *ar = &reply->additional[i];
304 struct sockaddr_storage addr;
305 bool ok;
306 size_t j;
308 ok = dns_res_rec_get_sockaddr(ar, &addr);
309 if (!ok) {
310 continue;
313 for (j=0; j<state->num_nss; j++) {
314 struct dns_rr_ns *ns = &state->nss[j];
316 if (strcmp(ns->hostname, ar->name) == 0) {
317 ns->ss = addr;
322 tevent_req_done(req);
325 NTSTATUS ads_dns_lookup_ns_recv(struct tevent_req *req,
326 TALLOC_CTX *mem_ctx,
327 struct dns_rr_ns **nss,
328 size_t *num_nss)
330 struct ads_dns_lookup_ns_state *state = tevent_req_data(
331 req, struct ads_dns_lookup_ns_state);
332 NTSTATUS status;
334 if (tevent_req_is_nterror(req, &status)) {
335 return status;
337 *nss = talloc_move(mem_ctx, &state->nss);
338 *num_nss = state->num_nss;
339 tevent_req_received(req);
340 return NT_STATUS_OK;
343 /*********************************************************************
344 Simple wrapper for a DNS NS query
345 *********************************************************************/
347 NTSTATUS ads_dns_lookup_ns(TALLOC_CTX *ctx,
348 const char *dnsdomain,
349 struct dns_rr_ns **nslist,
350 size_t *numns)
352 struct tevent_context *ev;
353 struct tevent_req *req;
354 NTSTATUS status = NT_STATUS_NO_MEMORY;
355 size_t num_ns = 0;
357 ev = samba_tevent_context_init(ctx);
358 if (ev == NULL) {
359 goto fail;
361 req = ads_dns_lookup_ns_send(ev, ev, dnsdomain);
362 if (req == NULL) {
363 goto fail;
365 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
366 goto fail;
368 status = ads_dns_lookup_ns_recv(req, ctx, nslist, &num_ns);
369 *numns = num_ns;
370 fail:
371 TALLOC_FREE(ev);
372 return status;
375 /*********************************************************************
376 Async A record lookup.
377 *********************************************************************/
379 struct ads_dns_lookup_a_state {
380 uint8_t rcode;
381 size_t num_names;
382 char **hostnames;
383 struct samba_sockaddr *addrs;
386 static void ads_dns_lookup_a_done(struct tevent_req *subreq);
388 struct tevent_req *ads_dns_lookup_a_send(TALLOC_CTX *mem_ctx,
389 struct tevent_context *ev,
390 const char *name)
392 struct tevent_req *req = NULL, *subreq = NULL;
393 struct ads_dns_lookup_a_state *state = NULL;
395 req = tevent_req_create(mem_ctx, &state,
396 struct ads_dns_lookup_a_state);
397 if (req == NULL) {
398 return NULL;
401 subreq = dns_lookup_send(
402 state,
404 NULL,
405 name,
406 DNS_QCLASS_IN,
407 DNS_QTYPE_A);
409 if (tevent_req_nomem(subreq, req)) {
410 return tevent_req_post(req, ev);
412 tevent_req_set_callback(subreq, ads_dns_lookup_a_done, req);
413 return req;
416 static void ads_dns_lookup_a_done(struct tevent_req *subreq)
418 struct tevent_req *req = tevent_req_callback_data(
419 subreq, struct tevent_req);
420 struct ads_dns_lookup_a_state *state = tevent_req_data(
421 req, struct ads_dns_lookup_a_state);
422 int ret;
423 struct dns_name_packet *reply = NULL;
424 uint16_t i;
426 ret = dns_lookup_recv(subreq, state, &reply);
427 TALLOC_FREE(subreq);
428 if (ret != 0) {
429 tevent_req_nterror(req, map_nt_error_from_unix_common(ret));
430 return;
433 state->rcode = (reply->operation & DNS_RCODE);
434 if (state->rcode != DNS_RCODE_OK) {
435 /* Don't bother looking for answers. */
436 tevent_req_done(req);
437 return;
441 * We don't care about CNAME answers here. We're
442 * just wanting an async name -> IPv4 lookup.
444 for (i = 0; i < reply->ancount; i++) {
445 if (reply->answers[i].rr_type == DNS_QTYPE_A) {
446 state->num_names += 1;
450 state->hostnames = talloc_zero_array(state,
451 char *,
452 state->num_names);
453 if (tevent_req_nomem(state->hostnames, req)) {
454 return;
456 state->addrs = talloc_zero_array(state,
457 struct samba_sockaddr,
458 state->num_names);
459 if (tevent_req_nomem(state->addrs, req)) {
460 return;
463 state->num_names = 0;
465 for (i = 0; i < reply->ancount; i++) {
466 bool ok;
467 struct sockaddr_storage ss = {0};
468 struct dns_res_rec *an = &reply->answers[i];
470 if (an->rr_type != DNS_QTYPE_A) {
471 continue;
473 if (an->name == NULL) {
474 /* Can this happen? */
475 continue;
477 if (an->rdata.ipv4_record == NULL) {
478 /* Can this happen? */
479 continue;
481 ok = dns_res_rec_get_sockaddr(an,
482 &ss);
483 if (!ok) {
484 continue;
486 if (is_zero_addr(&ss)) {
487 continue;
489 state->addrs[state->num_names].u.ss = ss;
490 state->addrs[state->num_names].sa_socklen =
491 sizeof(struct sockaddr_in);
492 state->hostnames[state->num_names] = talloc_strdup(
493 state->hostnames,
494 an->name);
495 if (tevent_req_nomem(state->hostnames[state->num_names], req)) {
496 return;
498 state->num_names += 1;
501 tevent_req_done(req);
504 NTSTATUS ads_dns_lookup_a_recv(struct tevent_req *req,
505 TALLOC_CTX *mem_ctx,
506 uint8_t *rcode_out,
507 size_t *num_names_out,
508 char ***hostnames_out,
509 struct samba_sockaddr **addrs_out)
511 struct ads_dns_lookup_a_state *state = tevent_req_data(
512 req, struct ads_dns_lookup_a_state);
513 NTSTATUS status;
515 if (tevent_req_is_nterror(req, &status)) {
516 return status;
518 if (rcode_out != NULL) {
520 * If we got no names, an upper layer may
521 * want to print a debug message.
523 *rcode_out = state->rcode;
525 if (hostnames_out != NULL) {
526 *hostnames_out = talloc_move(mem_ctx,
527 &state->hostnames);
529 if (addrs_out != NULL) {
530 *addrs_out = talloc_move(mem_ctx,
531 &state->addrs);
533 *num_names_out = state->num_names;
534 tevent_req_received(req);
535 return NT_STATUS_OK;
538 /*********************************************************************
539 Simple wrapper for a DNS A query
540 *********************************************************************/
542 NTSTATUS ads_dns_lookup_a(TALLOC_CTX *ctx,
543 const char *name_in,
544 size_t *num_names_out,
545 char ***hostnames_out,
546 struct samba_sockaddr **addrs_out)
548 struct tevent_context *ev;
549 struct tevent_req *req;
550 NTSTATUS status = NT_STATUS_NO_MEMORY;
552 ev = samba_tevent_context_init(ctx);
553 if (ev == NULL) {
554 goto fail;
556 req = ads_dns_lookup_a_send(ev, ev, name_in);
557 if (req == NULL) {
558 goto fail;
560 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
561 goto fail;
564 * Sychronous doesn't need to care about the rcode or
565 * a copy of the name_in.
567 status = ads_dns_lookup_a_recv(req,
568 ctx,
569 NULL,
570 num_names_out,
571 hostnames_out,
572 addrs_out);
573 fail:
574 TALLOC_FREE(ev);
575 return status;
578 #if defined(HAVE_IPV6)
579 /*********************************************************************
580 Async AAAA record lookup.
581 *********************************************************************/
583 struct ads_dns_lookup_aaaa_state {
584 uint8_t rcode;
585 size_t num_names;
586 char **hostnames;
587 struct samba_sockaddr *addrs;
590 static void ads_dns_lookup_aaaa_done(struct tevent_req *subreq);
592 struct tevent_req *ads_dns_lookup_aaaa_send(TALLOC_CTX *mem_ctx,
593 struct tevent_context *ev,
594 const char *name)
596 struct tevent_req *req, *subreq = NULL;
597 struct ads_dns_lookup_aaaa_state *state = NULL;
599 req = tevent_req_create(mem_ctx, &state,
600 struct ads_dns_lookup_aaaa_state);
601 if (req == NULL) {
602 return NULL;
605 subreq = dns_lookup_send(
606 state,
608 NULL,
609 name,
610 DNS_QCLASS_IN,
611 DNS_QTYPE_AAAA);
613 if (tevent_req_nomem(subreq, req)) {
614 return tevent_req_post(req, ev);
616 tevent_req_set_callback(subreq, ads_dns_lookup_aaaa_done, req);
617 return req;
620 static void ads_dns_lookup_aaaa_done(struct tevent_req *subreq)
622 struct tevent_req *req = tevent_req_callback_data(
623 subreq, struct tevent_req);
624 struct ads_dns_lookup_aaaa_state *state = tevent_req_data(
625 req, struct ads_dns_lookup_aaaa_state);
626 int ret;
627 struct dns_name_packet *reply = NULL;
628 uint16_t i;
630 ret = dns_lookup_recv(subreq, state, &reply);
631 TALLOC_FREE(subreq);
632 if (ret != 0) {
633 tevent_req_nterror(req, map_nt_error_from_unix_common(ret));
634 return;
637 state->rcode = (reply->operation & DNS_RCODE);
638 if (state->rcode != DNS_RCODE_OK) {
639 /* Don't bother looking for answers. */
640 tevent_req_done(req);
641 return;
645 * We don't care about CNAME answers here. We're
646 * just wanting an async name -> IPv6 lookup.
648 for (i = 0; i < reply->ancount; i++) {
649 if (reply->answers[i].rr_type == DNS_QTYPE_AAAA) {
650 state->num_names += 1;
654 state->hostnames = talloc_zero_array(state,
655 char *,
656 state->num_names);
657 if (tevent_req_nomem(state->hostnames, req)) {
658 return;
660 state->addrs = talloc_zero_array(state,
661 struct samba_sockaddr,
662 state->num_names);
663 if (tevent_req_nomem(state->addrs, req)) {
664 return;
667 state->num_names = 0;
669 for (i = 0; i < reply->ancount; i++) {
670 bool ok;
671 struct sockaddr_storage ss = {0};
672 struct dns_res_rec *an = &reply->answers[i];
674 if (an->rr_type != DNS_QTYPE_AAAA) {
675 continue;
677 if (an->name == NULL) {
678 /* Can this happen? */
679 continue;
681 if (an->rdata.ipv6_record == NULL) {
682 /* Can this happen? */
683 continue;
685 ok = dns_res_rec_get_sockaddr(an,
686 &ss);
687 if (!ok) {
688 continue;
690 if (is_zero_addr(&ss)) {
691 continue;
693 state->addrs[state->num_names].u.ss = ss;
694 state->addrs[state->num_names].sa_socklen =
695 sizeof(struct sockaddr_in6);
697 state->hostnames[state->num_names] = talloc_strdup(
698 state->hostnames,
699 an->name);
700 if (tevent_req_nomem(state->hostnames[state->num_names], req)) {
701 return;
703 state->num_names += 1;
706 tevent_req_done(req);
709 NTSTATUS ads_dns_lookup_aaaa_recv(struct tevent_req *req,
710 TALLOC_CTX *mem_ctx,
711 uint8_t *rcode_out,
712 size_t *num_names_out,
713 char ***hostnames_out,
714 struct samba_sockaddr **addrs_out)
716 struct ads_dns_lookup_aaaa_state *state = tevent_req_data(
717 req, struct ads_dns_lookup_aaaa_state);
718 NTSTATUS status;
720 if (tevent_req_is_nterror(req, &status)) {
721 return status;
723 if (rcode_out != NULL) {
725 * If we got no names, an upper layer may
726 * want to print a debug message.
728 *rcode_out = state->rcode;
730 if (hostnames_out != NULL) {
731 *hostnames_out = talloc_move(mem_ctx,
732 &state->hostnames);
734 if (addrs_out != NULL) {
735 *addrs_out = talloc_move(mem_ctx,
736 &state->addrs);
738 *num_names_out = state->num_names;
739 tevent_req_received(req);
740 return NT_STATUS_OK;
743 /*********************************************************************
744 Simple wrapper for a DNS AAAA query
745 *********************************************************************/
747 NTSTATUS ads_dns_lookup_aaaa(TALLOC_CTX *ctx,
748 const char *name_in,
749 size_t *num_names_out,
750 char ***hostnames_out,
751 struct samba_sockaddr **addrs_out)
753 struct tevent_context *ev = NULL;
754 struct tevent_req *req = NULL;
755 NTSTATUS status = NT_STATUS_NO_MEMORY;
757 ev = samba_tevent_context_init(ctx);
758 if (ev == NULL) {
759 goto fail;
761 req = ads_dns_lookup_aaaa_send(ev, ev, name_in);
762 if (req == NULL) {
763 goto fail;
765 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
766 goto fail;
769 * Sychronous doesn't need to care about the rcode or
770 * a copy of the name_in.
772 status = ads_dns_lookup_aaaa_recv(req,
773 ctx,
774 NULL,
775 num_names_out,
776 hostnames_out,
777 addrs_out);
778 fail:
779 TALLOC_FREE(ev);
780 return status;
782 #endif