2 Unix SMB/CIFS implementation.
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/>.
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"
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
)
41 /* higher weights should be sorted lower */
42 if ( a
->weight
> b
->weight
)
48 if ( a
->priority
< b
->priority
)
54 struct ads_dns_lookup_srv_state
{
55 struct dns_rr_srv
*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
,
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
);
74 subreq
= dns_lookup_send(
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
);
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
);
96 struct dns_name_packet
*reply
;
99 ret
= dns_lookup_recv(subreq
, state
, &reply
);
102 tevent_req_nterror(req
, map_nt_error_from_unix_common(ret
));
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
)) {
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
) {
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
,
139 for (i
=0; i
<reply
->arcount
; i
++) {
140 struct dns_res_rec
*ar
= &reply
->additional
[i
];
141 struct sockaddr_storage addr
;
145 ok
= dns_res_rec_get_sockaddr(ar
, &addr
);
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
)) {
161 /* uint16_t can't wrap here. */
162 tmp
= talloc_realloc(
165 struct sockaddr_storage
,
168 if (tevent_req_nomem(tmp
, req
)) {
173 srv
->ss_s
[srv
->num_ips
] = addr
;
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
,
185 struct dns_rr_srv
**srvs
,
188 struct ads_dns_lookup_srv_state
*state
= tevent_req_data(
189 req
, struct ads_dns_lookup_srv_state
);
192 if (tevent_req_is_nterror(req
, &status
)) {
195 *srvs
= talloc_move(mem_ctx
, &state
->srvs
);
196 *num_srvs
= state
->num_srvs
;
197 tevent_req_received(req
);
201 /*********************************************************************
202 Simple wrapper for a DNS SRV query
203 *********************************************************************/
205 NTSTATUS
ads_dns_lookup_srv(TALLOC_CTX
*ctx
,
207 struct dns_rr_srv
**dclist
,
210 struct tevent_context
*ev
;
211 struct tevent_req
*req
;
212 NTSTATUS status
= NT_STATUS_NO_MEMORY
;
215 ev
= samba_tevent_context_init(ctx
);
219 req
= ads_dns_lookup_srv_send(ev
, ev
, name
);
223 if (!tevent_req_poll_ntstatus(req
, ev
, &status
)) {
226 status
= ads_dns_lookup_srv_recv(req
, ctx
, dclist
, &num_srvs
);
227 if (NT_STATUS_IS_OK(status
)) {
235 struct ads_dns_lookup_ns_state
{
236 struct dns_rr_ns
*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
,
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
);
255 subreq
= dns_lookup_send(state
, ev
, NULL
, name
, DNS_QCLASS_IN
,
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
);
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
);
271 struct dns_name_packet
*reply
;
274 ret
= dns_lookup_recv(subreq
, state
, &reply
);
277 tevent_req_nterror(req
, map_nt_error_from_unix_common(ret
));
281 for (i
=0; i
<reply
->ancount
; i
++) {
282 if (reply
->answers
[i
].rr_type
== DNS_QTYPE_NS
) {
287 state
->nss
= talloc_array(state
, struct dns_rr_ns
, state
->num_nss
);
288 if (tevent_req_nomem(state
->nss
, req
)) {
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
) {
301 state
->nss
[idx
].hostname
= talloc_move(state
->nss
,
302 &an
->rdata
.ns_record
);
306 for (i
=0; i
<reply
->arcount
; i
++) {
307 struct dns_res_rec
*ar
= &reply
->additional
[i
];
308 struct sockaddr_storage addr
;
312 ok
= dns_res_rec_get_sockaddr(ar
, &addr
);
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) {
326 tevent_req_done(req
);
329 NTSTATUS
ads_dns_lookup_ns_recv(struct tevent_req
*req
,
331 struct dns_rr_ns
**nss
,
334 struct ads_dns_lookup_ns_state
*state
= tevent_req_data(
335 req
, struct ads_dns_lookup_ns_state
);
338 if (tevent_req_is_nterror(req
, &status
)) {
341 *nss
= talloc_move(mem_ctx
, &state
->nss
);
342 *num_nss
= state
->num_nss
;
343 tevent_req_received(req
);
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
,
356 struct tevent_context
*ev
;
357 struct tevent_req
*req
;
358 NTSTATUS status
= NT_STATUS_NO_MEMORY
;
361 ev
= samba_tevent_context_init(ctx
);
365 req
= ads_dns_lookup_ns_send(ev
, ev
, dnsdomain
);
369 if (!tevent_req_poll_ntstatus(req
, ev
, &status
)) {
372 status
= ads_dns_lookup_ns_recv(req
, ctx
, nslist
, &num_ns
);
379 /*********************************************************************
380 Async A record lookup.
381 *********************************************************************/
383 struct ads_dns_lookup_a_state
{
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
,
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
);
405 subreq
= dns_lookup_send(
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
);
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
);
427 struct dns_name_packet
*reply
= NULL
;
430 ret
= dns_lookup_recv(subreq
, state
, &reply
);
433 tevent_req_nterror(req
, map_nt_error_from_unix_common(ret
));
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
);
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
,
457 if (tevent_req_nomem(state
->hostnames
, req
)) {
460 state
->addrs
= talloc_zero_array(state
,
461 struct samba_sockaddr
,
463 if (tevent_req_nomem(state
->addrs
, req
)) {
467 state
->num_names
= 0;
469 for (i
= 0; i
< reply
->ancount
; i
++) {
471 struct sockaddr_storage ss
= {0};
472 struct dns_res_rec
*an
= &reply
->answers
[i
];
474 if (an
->rr_type
!= DNS_QTYPE_A
) {
477 if (an
->name
== NULL
) {
478 /* Can this happen? */
481 if (an
->rdata
.ipv4_record
== NULL
) {
482 /* Can this happen? */
485 ok
= dns_res_rec_get_sockaddr(an
,
490 if (is_zero_addr(&ss
)) {
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(
499 if (tevent_req_nomem(state
->hostnames
[state
->num_names
], req
)) {
502 state
->num_names
+= 1;
505 tevent_req_done(req
);
508 NTSTATUS
ads_dns_lookup_a_recv(struct tevent_req
*req
,
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
);
519 if (tevent_req_is_nterror(req
, &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
,
533 if (addrs_out
!= NULL
) {
534 *addrs_out
= talloc_move(mem_ctx
,
537 *num_names_out
= state
->num_names
;
538 tevent_req_received(req
);
542 /*********************************************************************
543 Simple wrapper for a DNS A query
544 *********************************************************************/
546 NTSTATUS
ads_dns_lookup_a(TALLOC_CTX
*ctx
,
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
);
560 req
= ads_dns_lookup_a_send(ev
, ev
, name_in
);
564 if (!tevent_req_poll_ntstatus(req
, ev
, &status
)) {
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
,
582 #if defined(HAVE_IPV6)
583 /*********************************************************************
584 Async AAAA record lookup.
585 *********************************************************************/
587 struct ads_dns_lookup_aaaa_state
{
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
,
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
);
609 subreq
= dns_lookup_send(
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
);
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
);
631 struct dns_name_packet
*reply
= NULL
;
634 ret
= dns_lookup_recv(subreq
, state
, &reply
);
637 tevent_req_nterror(req
, map_nt_error_from_unix_common(ret
));
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
);
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
,
661 if (tevent_req_nomem(state
->hostnames
, req
)) {
664 state
->addrs
= talloc_zero_array(state
,
665 struct samba_sockaddr
,
667 if (tevent_req_nomem(state
->addrs
, req
)) {
671 state
->num_names
= 0;
673 for (i
= 0; i
< reply
->ancount
; i
++) {
675 struct sockaddr_storage ss
= {0};
676 struct dns_res_rec
*an
= &reply
->answers
[i
];
678 if (an
->rr_type
!= DNS_QTYPE_AAAA
) {
681 if (an
->name
== NULL
) {
682 /* Can this happen? */
685 if (an
->rdata
.ipv6_record
== NULL
) {
686 /* Can this happen? */
689 ok
= dns_res_rec_get_sockaddr(an
,
694 if (is_zero_addr(&ss
)) {
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(
704 if (tevent_req_nomem(state
->hostnames
[state
->num_names
], req
)) {
707 state
->num_names
+= 1;
710 tevent_req_done(req
);
713 NTSTATUS
ads_dns_lookup_aaaa_recv(struct tevent_req
*req
,
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
);
724 if (tevent_req_is_nterror(req
, &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
,
738 if (addrs_out
!= NULL
) {
739 *addrs_out
= talloc_move(mem_ctx
,
742 *num_names_out
= state
->num_names
;
743 tevent_req_received(req
);
747 /*********************************************************************
748 Simple wrapper for a DNS AAAA query
749 *********************************************************************/
751 NTSTATUS
ads_dns_lookup_aaaa(TALLOC_CTX
*ctx
,
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
);
765 req
= ads_dns_lookup_aaaa_send(ev
, ev
, name_in
);
769 if (!tevent_req_poll_ntstatus(req
, ev
, &status
)) {
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
,