s3:utils: Fix 'Usage:' for 'net ads enctypes'
[samba4-gss.git] / lib / addns / dnsquery.c
blobc997077bb539fc4a748a77d19fc73a227700704c
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;
155 * sometimes the name gets messed up
156 * with upper and lower case...
158 if (!strequal(srv->hostname, ar->name)) {
159 continue;
161 /* uint16_t can't wrap here. */
162 tmp = talloc_realloc(
163 state->srvs,
164 srv->ss_s,
165 struct sockaddr_storage,
166 srv->num_ips+1);
168 if (tevent_req_nomem(tmp, req)) {
169 return;
171 srv->ss_s = tmp;
173 srv->ss_s[srv->num_ips] = addr;
174 srv->num_ips += 1;
178 TYPESAFE_QSORT(state->srvs, state->num_srvs, dnssrvcmp);
180 tevent_req_done(req);
183 NTSTATUS ads_dns_lookup_srv_recv(struct tevent_req *req,
184 TALLOC_CTX *mem_ctx,
185 struct dns_rr_srv **srvs,
186 size_t *num_srvs)
188 struct ads_dns_lookup_srv_state *state = tevent_req_data(
189 req, struct ads_dns_lookup_srv_state);
190 NTSTATUS status;
192 if (tevent_req_is_nterror(req, &status)) {
193 return status;
195 *srvs = talloc_move(mem_ctx, &state->srvs);
196 *num_srvs = state->num_srvs;
197 tevent_req_received(req);
198 return NT_STATUS_OK;
201 /*********************************************************************
202 Simple wrapper for a DNS SRV query
203 *********************************************************************/
205 NTSTATUS ads_dns_lookup_srv(TALLOC_CTX *ctx,
206 const char *name,
207 struct dns_rr_srv **dclist,
208 size_t *numdcs)
210 struct tevent_context *ev;
211 struct tevent_req *req;
212 NTSTATUS status = NT_STATUS_NO_MEMORY;
213 size_t num_srvs = 0;
215 ev = samba_tevent_context_init(ctx);
216 if (ev == NULL) {
217 goto fail;
219 req = ads_dns_lookup_srv_send(ev, ev, name);
220 if (req == NULL) {
221 goto fail;
223 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
224 goto fail;
226 status = ads_dns_lookup_srv_recv(req, ctx, dclist, &num_srvs);
227 if (NT_STATUS_IS_OK(status)) {
228 *numdcs = num_srvs;
230 fail:
231 TALLOC_FREE(ev);
232 return status;
235 struct ads_dns_lookup_ns_state {
236 struct dns_rr_ns *nss;
237 size_t num_nss;
240 static void ads_dns_lookup_ns_done(struct tevent_req *subreq);
242 struct tevent_req *ads_dns_lookup_ns_send(TALLOC_CTX *mem_ctx,
243 struct tevent_context *ev,
244 const char *name)
246 struct tevent_req *req, *subreq;
247 struct ads_dns_lookup_ns_state *state;
249 req = tevent_req_create(mem_ctx, &state,
250 struct ads_dns_lookup_ns_state);
251 if (req == NULL) {
252 return NULL;
255 subreq = dns_lookup_send(state, ev, NULL, name, DNS_QCLASS_IN,
256 DNS_QTYPE_NS);
257 if (tevent_req_nomem(subreq, req)) {
258 return tevent_req_post(req, ev);
260 tevent_req_set_callback(subreq, ads_dns_lookup_ns_done, req);
261 return req;
264 static void ads_dns_lookup_ns_done(struct tevent_req *subreq)
266 struct tevent_req *req = tevent_req_callback_data(
267 subreq, struct tevent_req);
268 struct ads_dns_lookup_ns_state *state = tevent_req_data(
269 req, struct ads_dns_lookup_ns_state);
270 int ret;
271 struct dns_name_packet *reply;
272 uint16_t i, idx;
274 ret = dns_lookup_recv(subreq, state, &reply);
275 TALLOC_FREE(subreq);
276 if (ret != 0) {
277 tevent_req_nterror(req, map_nt_error_from_unix_common(ret));
278 return;
281 for (i=0; i<reply->ancount; i++) {
282 if (reply->answers[i].rr_type == DNS_QTYPE_NS) {
283 state->num_nss += 1;
287 state->nss = talloc_array(state, struct dns_rr_ns, state->num_nss);
288 if (tevent_req_nomem(state->nss, req)) {
289 return;
292 idx = 0;
294 for (i=0; i<reply->ancount; i++) {
295 struct dns_res_rec *an = &reply->answers[i];
297 if (an->rr_type != DNS_QTYPE_NS) {
298 continue;
301 state->nss[idx].hostname = talloc_move(state->nss,
302 &an->rdata.ns_record);
303 idx += 1;
306 for (i=0; i<reply->arcount; i++) {
307 struct dns_res_rec *ar = &reply->additional[i];
308 struct sockaddr_storage addr;
309 bool ok;
310 size_t j;
312 ok = dns_res_rec_get_sockaddr(ar, &addr);
313 if (!ok) {
314 continue;
317 for (j=0; j<state->num_nss; j++) {
318 struct dns_rr_ns *ns = &state->nss[j];
320 if (strcmp(ns->hostname, ar->name) == 0) {
321 ns->ss = addr;
326 tevent_req_done(req);
329 NTSTATUS ads_dns_lookup_ns_recv(struct tevent_req *req,
330 TALLOC_CTX *mem_ctx,
331 struct dns_rr_ns **nss,
332 size_t *num_nss)
334 struct ads_dns_lookup_ns_state *state = tevent_req_data(
335 req, struct ads_dns_lookup_ns_state);
336 NTSTATUS status;
338 if (tevent_req_is_nterror(req, &status)) {
339 return status;
341 *nss = talloc_move(mem_ctx, &state->nss);
342 *num_nss = state->num_nss;
343 tevent_req_received(req);
344 return NT_STATUS_OK;
347 /*********************************************************************
348 Simple wrapper for a DNS NS query
349 *********************************************************************/
351 NTSTATUS ads_dns_lookup_ns(TALLOC_CTX *ctx,
352 const char *dnsdomain,
353 struct dns_rr_ns **nslist,
354 size_t *numns)
356 struct tevent_context *ev;
357 struct tevent_req *req;
358 NTSTATUS status = NT_STATUS_NO_MEMORY;
359 size_t num_ns = 0;
361 ev = samba_tevent_context_init(ctx);
362 if (ev == NULL) {
363 goto fail;
365 req = ads_dns_lookup_ns_send(ev, ev, dnsdomain);
366 if (req == NULL) {
367 goto fail;
369 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
370 goto fail;
372 status = ads_dns_lookup_ns_recv(req, ctx, nslist, &num_ns);
373 *numns = num_ns;
374 fail:
375 TALLOC_FREE(ev);
376 return status;
379 /*********************************************************************
380 Async A record lookup.
381 *********************************************************************/
383 struct ads_dns_lookup_a_state {
384 uint8_t rcode;
385 size_t num_names;
386 char **hostnames;
387 struct samba_sockaddr *addrs;
390 static void ads_dns_lookup_a_done(struct tevent_req *subreq);
392 struct tevent_req *ads_dns_lookup_a_send(TALLOC_CTX *mem_ctx,
393 struct tevent_context *ev,
394 const char *name)
396 struct tevent_req *req = NULL, *subreq = NULL;
397 struct ads_dns_lookup_a_state *state = NULL;
399 req = tevent_req_create(mem_ctx, &state,
400 struct ads_dns_lookup_a_state);
401 if (req == NULL) {
402 return NULL;
405 subreq = dns_lookup_send(
406 state,
408 NULL,
409 name,
410 DNS_QCLASS_IN,
411 DNS_QTYPE_A);
413 if (tevent_req_nomem(subreq, req)) {
414 return tevent_req_post(req, ev);
416 tevent_req_set_callback(subreq, ads_dns_lookup_a_done, req);
417 return req;
420 static void ads_dns_lookup_a_done(struct tevent_req *subreq)
422 struct tevent_req *req = tevent_req_callback_data(
423 subreq, struct tevent_req);
424 struct ads_dns_lookup_a_state *state = tevent_req_data(
425 req, struct ads_dns_lookup_a_state);
426 int ret;
427 struct dns_name_packet *reply = NULL;
428 uint16_t i;
430 ret = dns_lookup_recv(subreq, state, &reply);
431 TALLOC_FREE(subreq);
432 if (ret != 0) {
433 tevent_req_nterror(req, map_nt_error_from_unix_common(ret));
434 return;
437 state->rcode = (reply->operation & DNS_RCODE);
438 if (state->rcode != DNS_RCODE_OK) {
439 /* Don't bother looking for answers. */
440 tevent_req_done(req);
441 return;
445 * We don't care about CNAME answers here. We're
446 * just wanting an async name -> IPv4 lookup.
448 for (i = 0; i < reply->ancount; i++) {
449 if (reply->answers[i].rr_type == DNS_QTYPE_A) {
450 state->num_names += 1;
454 state->hostnames = talloc_zero_array(state,
455 char *,
456 state->num_names);
457 if (tevent_req_nomem(state->hostnames, req)) {
458 return;
460 state->addrs = talloc_zero_array(state,
461 struct samba_sockaddr,
462 state->num_names);
463 if (tevent_req_nomem(state->addrs, req)) {
464 return;
467 state->num_names = 0;
469 for (i = 0; i < reply->ancount; i++) {
470 bool ok;
471 struct sockaddr_storage ss = {0};
472 struct dns_res_rec *an = &reply->answers[i];
474 if (an->rr_type != DNS_QTYPE_A) {
475 continue;
477 if (an->name == NULL) {
478 /* Can this happen? */
479 continue;
481 if (an->rdata.ipv4_record == NULL) {
482 /* Can this happen? */
483 continue;
485 ok = dns_res_rec_get_sockaddr(an,
486 &ss);
487 if (!ok) {
488 continue;
490 if (is_zero_addr(&ss)) {
491 continue;
493 state->addrs[state->num_names].u.ss = ss;
494 state->addrs[state->num_names].sa_socklen =
495 sizeof(struct sockaddr_in);
496 state->hostnames[state->num_names] = talloc_strdup(
497 state->hostnames,
498 an->name);
499 if (tevent_req_nomem(state->hostnames[state->num_names], req)) {
500 return;
502 state->num_names += 1;
505 tevent_req_done(req);
508 NTSTATUS ads_dns_lookup_a_recv(struct tevent_req *req,
509 TALLOC_CTX *mem_ctx,
510 uint8_t *rcode_out,
511 size_t *num_names_out,
512 char ***hostnames_out,
513 struct samba_sockaddr **addrs_out)
515 struct ads_dns_lookup_a_state *state = tevent_req_data(
516 req, struct ads_dns_lookup_a_state);
517 NTSTATUS status;
519 if (tevent_req_is_nterror(req, &status)) {
520 return status;
522 if (rcode_out != NULL) {
524 * If we got no names, an upper layer may
525 * want to print a debug message.
527 *rcode_out = state->rcode;
529 if (hostnames_out != NULL) {
530 *hostnames_out = talloc_move(mem_ctx,
531 &state->hostnames);
533 if (addrs_out != NULL) {
534 *addrs_out = talloc_move(mem_ctx,
535 &state->addrs);
537 *num_names_out = state->num_names;
538 tevent_req_received(req);
539 return NT_STATUS_OK;
542 /*********************************************************************
543 Simple wrapper for a DNS A query
544 *********************************************************************/
546 NTSTATUS ads_dns_lookup_a(TALLOC_CTX *ctx,
547 const char *name_in,
548 size_t *num_names_out,
549 char ***hostnames_out,
550 struct samba_sockaddr **addrs_out)
552 struct tevent_context *ev;
553 struct tevent_req *req;
554 NTSTATUS status = NT_STATUS_NO_MEMORY;
556 ev = samba_tevent_context_init(ctx);
557 if (ev == NULL) {
558 goto fail;
560 req = ads_dns_lookup_a_send(ev, ev, name_in);
561 if (req == NULL) {
562 goto fail;
564 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
565 goto fail;
568 * Synchronous doesn't need to care about the rcode or
569 * a copy of the name_in.
571 status = ads_dns_lookup_a_recv(req,
572 ctx,
573 NULL,
574 num_names_out,
575 hostnames_out,
576 addrs_out);
577 fail:
578 TALLOC_FREE(ev);
579 return status;
582 #if defined(HAVE_IPV6)
583 /*********************************************************************
584 Async AAAA record lookup.
585 *********************************************************************/
587 struct ads_dns_lookup_aaaa_state {
588 uint8_t rcode;
589 size_t num_names;
590 char **hostnames;
591 struct samba_sockaddr *addrs;
594 static void ads_dns_lookup_aaaa_done(struct tevent_req *subreq);
596 struct tevent_req *ads_dns_lookup_aaaa_send(TALLOC_CTX *mem_ctx,
597 struct tevent_context *ev,
598 const char *name)
600 struct tevent_req *req, *subreq = NULL;
601 struct ads_dns_lookup_aaaa_state *state = NULL;
603 req = tevent_req_create(mem_ctx, &state,
604 struct ads_dns_lookup_aaaa_state);
605 if (req == NULL) {
606 return NULL;
609 subreq = dns_lookup_send(
610 state,
612 NULL,
613 name,
614 DNS_QCLASS_IN,
615 DNS_QTYPE_AAAA);
617 if (tevent_req_nomem(subreq, req)) {
618 return tevent_req_post(req, ev);
620 tevent_req_set_callback(subreq, ads_dns_lookup_aaaa_done, req);
621 return req;
624 static void ads_dns_lookup_aaaa_done(struct tevent_req *subreq)
626 struct tevent_req *req = tevent_req_callback_data(
627 subreq, struct tevent_req);
628 struct ads_dns_lookup_aaaa_state *state = tevent_req_data(
629 req, struct ads_dns_lookup_aaaa_state);
630 int ret;
631 struct dns_name_packet *reply = NULL;
632 uint16_t i;
634 ret = dns_lookup_recv(subreq, state, &reply);
635 TALLOC_FREE(subreq);
636 if (ret != 0) {
637 tevent_req_nterror(req, map_nt_error_from_unix_common(ret));
638 return;
641 state->rcode = (reply->operation & DNS_RCODE);
642 if (state->rcode != DNS_RCODE_OK) {
643 /* Don't bother looking for answers. */
644 tevent_req_done(req);
645 return;
649 * We don't care about CNAME answers here. We're
650 * just wanting an async name -> IPv6 lookup.
652 for (i = 0; i < reply->ancount; i++) {
653 if (reply->answers[i].rr_type == DNS_QTYPE_AAAA) {
654 state->num_names += 1;
658 state->hostnames = talloc_zero_array(state,
659 char *,
660 state->num_names);
661 if (tevent_req_nomem(state->hostnames, req)) {
662 return;
664 state->addrs = talloc_zero_array(state,
665 struct samba_sockaddr,
666 state->num_names);
667 if (tevent_req_nomem(state->addrs, req)) {
668 return;
671 state->num_names = 0;
673 for (i = 0; i < reply->ancount; i++) {
674 bool ok;
675 struct sockaddr_storage ss = {0};
676 struct dns_res_rec *an = &reply->answers[i];
678 if (an->rr_type != DNS_QTYPE_AAAA) {
679 continue;
681 if (an->name == NULL) {
682 /* Can this happen? */
683 continue;
685 if (an->rdata.ipv6_record == NULL) {
686 /* Can this happen? */
687 continue;
689 ok = dns_res_rec_get_sockaddr(an,
690 &ss);
691 if (!ok) {
692 continue;
694 if (is_zero_addr(&ss)) {
695 continue;
697 state->addrs[state->num_names].u.ss = ss;
698 state->addrs[state->num_names].sa_socklen =
699 sizeof(struct sockaddr_in6);
701 state->hostnames[state->num_names] = talloc_strdup(
702 state->hostnames,
703 an->name);
704 if (tevent_req_nomem(state->hostnames[state->num_names], req)) {
705 return;
707 state->num_names += 1;
710 tevent_req_done(req);
713 NTSTATUS ads_dns_lookup_aaaa_recv(struct tevent_req *req,
714 TALLOC_CTX *mem_ctx,
715 uint8_t *rcode_out,
716 size_t *num_names_out,
717 char ***hostnames_out,
718 struct samba_sockaddr **addrs_out)
720 struct ads_dns_lookup_aaaa_state *state = tevent_req_data(
721 req, struct ads_dns_lookup_aaaa_state);
722 NTSTATUS status;
724 if (tevent_req_is_nterror(req, &status)) {
725 return status;
727 if (rcode_out != NULL) {
729 * If we got no names, an upper layer may
730 * want to print a debug message.
732 *rcode_out = state->rcode;
734 if (hostnames_out != NULL) {
735 *hostnames_out = talloc_move(mem_ctx,
736 &state->hostnames);
738 if (addrs_out != NULL) {
739 *addrs_out = talloc_move(mem_ctx,
740 &state->addrs);
742 *num_names_out = state->num_names;
743 tevent_req_received(req);
744 return NT_STATUS_OK;
747 /*********************************************************************
748 Simple wrapper for a DNS AAAA query
749 *********************************************************************/
751 NTSTATUS ads_dns_lookup_aaaa(TALLOC_CTX *ctx,
752 const char *name_in,
753 size_t *num_names_out,
754 char ***hostnames_out,
755 struct samba_sockaddr **addrs_out)
757 struct tevent_context *ev = NULL;
758 struct tevent_req *req = NULL;
759 NTSTATUS status = NT_STATUS_NO_MEMORY;
761 ev = samba_tevent_context_init(ctx);
762 if (ev == NULL) {
763 goto fail;
765 req = ads_dns_lookup_aaaa_send(ev, ev, name_in);
766 if (req == NULL) {
767 goto fail;
769 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
770 goto fail;
773 * Synchronous doesn't need to care about the rcode or
774 * a copy of the name_in.
776 status = ads_dns_lookup_aaaa_recv(req,
777 ctx,
778 NULL,
779 num_names_out,
780 hostnames_out,
781 addrs_out);
782 fail:
783 TALLOC_FREE(ev);
784 return status;
786 #endif