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
;
154 if (strcmp(srv
->hostname
, ar
->name
) != 0) {
157 /* uint16_t can't wrap here. */
158 tmp
= talloc_realloc(
161 struct sockaddr_storage
,
164 if (tevent_req_nomem(tmp
, req
)) {
169 srv
->ss_s
[srv
->num_ips
] = addr
;
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
,
181 struct dns_rr_srv
**srvs
,
184 struct ads_dns_lookup_srv_state
*state
= tevent_req_data(
185 req
, struct ads_dns_lookup_srv_state
);
188 if (tevent_req_is_nterror(req
, &status
)) {
191 *srvs
= talloc_move(mem_ctx
, &state
->srvs
);
192 *num_srvs
= state
->num_srvs
;
193 tevent_req_received(req
);
197 /*********************************************************************
198 Simple wrapper for a DNS SRV query
199 *********************************************************************/
201 NTSTATUS
ads_dns_lookup_srv(TALLOC_CTX
*ctx
,
203 struct dns_rr_srv
**dclist
,
206 struct tevent_context
*ev
;
207 struct tevent_req
*req
;
208 NTSTATUS status
= NT_STATUS_NO_MEMORY
;
211 ev
= samba_tevent_context_init(ctx
);
215 req
= ads_dns_lookup_srv_send(ev
, ev
, name
);
219 if (!tevent_req_poll_ntstatus(req
, ev
, &status
)) {
222 status
= ads_dns_lookup_srv_recv(req
, ctx
, dclist
, &num_srvs
);
223 if (NT_STATUS_IS_OK(status
)) {
231 struct ads_dns_lookup_ns_state
{
232 struct dns_rr_ns
*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
,
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
);
251 subreq
= dns_lookup_send(state
, ev
, NULL
, name
, DNS_QCLASS_IN
,
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
);
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
);
267 struct dns_name_packet
*reply
;
270 ret
= dns_lookup_recv(subreq
, state
, &reply
);
273 tevent_req_nterror(req
, map_nt_error_from_unix_common(ret
));
277 for (i
=0; i
<reply
->ancount
; i
++) {
278 if (reply
->answers
[i
].rr_type
== DNS_QTYPE_NS
) {
283 state
->nss
= talloc_array(state
, struct dns_rr_ns
, state
->num_nss
);
284 if (tevent_req_nomem(state
->nss
, req
)) {
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
) {
297 state
->nss
[idx
].hostname
= talloc_move(state
->nss
,
298 &an
->rdata
.ns_record
);
302 for (i
=0; i
<reply
->arcount
; i
++) {
303 struct dns_res_rec
*ar
= &reply
->additional
[i
];
304 struct sockaddr_storage addr
;
308 ok
= dns_res_rec_get_sockaddr(ar
, &addr
);
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) {
322 tevent_req_done(req
);
325 NTSTATUS
ads_dns_lookup_ns_recv(struct tevent_req
*req
,
327 struct dns_rr_ns
**nss
,
330 struct ads_dns_lookup_ns_state
*state
= tevent_req_data(
331 req
, struct ads_dns_lookup_ns_state
);
334 if (tevent_req_is_nterror(req
, &status
)) {
337 *nss
= talloc_move(mem_ctx
, &state
->nss
);
338 *num_nss
= state
->num_nss
;
339 tevent_req_received(req
);
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
,
352 struct tevent_context
*ev
;
353 struct tevent_req
*req
;
354 NTSTATUS status
= NT_STATUS_NO_MEMORY
;
357 ev
= samba_tevent_context_init(ctx
);
361 req
= ads_dns_lookup_ns_send(ev
, ev
, dnsdomain
);
365 if (!tevent_req_poll_ntstatus(req
, ev
, &status
)) {
368 status
= ads_dns_lookup_ns_recv(req
, ctx
, nslist
, &num_ns
);
375 /*********************************************************************
376 Async A record lookup.
377 *********************************************************************/
379 struct ads_dns_lookup_a_state
{
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
,
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
);
401 subreq
= dns_lookup_send(
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
);
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
);
423 struct dns_name_packet
*reply
= NULL
;
426 ret
= dns_lookup_recv(subreq
, state
, &reply
);
429 tevent_req_nterror(req
, map_nt_error_from_unix_common(ret
));
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
);
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
,
453 if (tevent_req_nomem(state
->hostnames
, req
)) {
456 state
->addrs
= talloc_zero_array(state
,
457 struct samba_sockaddr
,
459 if (tevent_req_nomem(state
->addrs
, req
)) {
463 state
->num_names
= 0;
465 for (i
= 0; i
< reply
->ancount
; i
++) {
467 struct sockaddr_storage ss
= {0};
468 struct dns_res_rec
*an
= &reply
->answers
[i
];
470 if (an
->rr_type
!= DNS_QTYPE_A
) {
473 if (an
->name
== NULL
) {
474 /* Can this happen? */
477 if (an
->rdata
.ipv4_record
== NULL
) {
478 /* Can this happen? */
481 ok
= dns_res_rec_get_sockaddr(an
,
486 if (is_zero_addr(&ss
)) {
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(
495 if (tevent_req_nomem(state
->hostnames
[state
->num_names
], req
)) {
498 state
->num_names
+= 1;
501 tevent_req_done(req
);
504 NTSTATUS
ads_dns_lookup_a_recv(struct tevent_req
*req
,
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
);
515 if (tevent_req_is_nterror(req
, &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
,
529 if (addrs_out
!= NULL
) {
530 *addrs_out
= talloc_move(mem_ctx
,
533 *num_names_out
= state
->num_names
;
534 tevent_req_received(req
);
538 /*********************************************************************
539 Simple wrapper for a DNS A query
540 *********************************************************************/
542 NTSTATUS
ads_dns_lookup_a(TALLOC_CTX
*ctx
,
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
);
556 req
= ads_dns_lookup_a_send(ev
, ev
, name_in
);
560 if (!tevent_req_poll_ntstatus(req
, ev
, &status
)) {
564 * Synchronous doesn't need to care about the rcode or
565 * a copy of the name_in.
567 status
= ads_dns_lookup_a_recv(req
,
578 #if defined(HAVE_IPV6)
579 /*********************************************************************
580 Async AAAA record lookup.
581 *********************************************************************/
583 struct ads_dns_lookup_aaaa_state
{
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
,
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
);
605 subreq
= dns_lookup_send(
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
);
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
);
627 struct dns_name_packet
*reply
= NULL
;
630 ret
= dns_lookup_recv(subreq
, state
, &reply
);
633 tevent_req_nterror(req
, map_nt_error_from_unix_common(ret
));
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
);
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
,
657 if (tevent_req_nomem(state
->hostnames
, req
)) {
660 state
->addrs
= talloc_zero_array(state
,
661 struct samba_sockaddr
,
663 if (tevent_req_nomem(state
->addrs
, req
)) {
667 state
->num_names
= 0;
669 for (i
= 0; i
< reply
->ancount
; i
++) {
671 struct sockaddr_storage ss
= {0};
672 struct dns_res_rec
*an
= &reply
->answers
[i
];
674 if (an
->rr_type
!= DNS_QTYPE_AAAA
) {
677 if (an
->name
== NULL
) {
678 /* Can this happen? */
681 if (an
->rdata
.ipv6_record
== NULL
) {
682 /* Can this happen? */
685 ok
= dns_res_rec_get_sockaddr(an
,
690 if (is_zero_addr(&ss
)) {
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(
700 if (tevent_req_nomem(state
->hostnames
[state
->num_names
], req
)) {
703 state
->num_names
+= 1;
706 tevent_req_done(req
);
709 NTSTATUS
ads_dns_lookup_aaaa_recv(struct tevent_req
*req
,
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
);
720 if (tevent_req_is_nterror(req
, &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
,
734 if (addrs_out
!= NULL
) {
735 *addrs_out
= talloc_move(mem_ctx
,
738 *num_names_out
= state
->num_names
;
739 tevent_req_received(req
);
743 /*********************************************************************
744 Simple wrapper for a DNS AAAA query
745 *********************************************************************/
747 NTSTATUS
ads_dns_lookup_aaaa(TALLOC_CTX
*ctx
,
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
);
761 req
= ads_dns_lookup_aaaa_send(ev
, ev
, name_in
);
765 if (!tevent_req_poll_ntstatus(req
, ev
, &status
)) {
769 * Synchronous doesn't need to care about the rcode or
770 * a copy of the name_in.
772 status
= ads_dns_lookup_aaaa_recv(req
,