4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
28 * DNS query helper functions for addisc.c
38 #include <sys/types.h>
39 #include <sys/socket.h>
40 #include <sys/sockio.h>
41 #include <netinet/in.h>
42 #include <arpa/inet.h>
43 #include <arpa/nameser.h>
49 #include <sasl/sasl.h>
50 #include <sys/u8_textprep.h>
52 #include <uuid/uuid.h>
53 #include <ads/dsgetdc.h>
54 #include "adutils_impl.h"
55 #include "addisc_impl.h"
57 static void save_addr(ad_disc_cds_t
*, sa_family_t
, uchar_t
*, size_t);
58 static struct addrinfo
*make_addrinfo(sa_family_t
, uchar_t
*, size_t);
60 static void do_getaddrinfo(ad_disc_cds_t
*);
61 static ad_disc_cds_t
*srv_parse(uchar_t
*, int, int *, int *);
62 static void add_preferred(ad_disc_cds_t
*, ad_disc_ds_t
*, int *, int);
63 static void get_addresses(ad_disc_cds_t
*, int);
66 * Simplified version of srv_query() for domain auto-discovery.
69 srv_getdom(res_state state
, const char *svc_name
, char **rrname
)
73 uchar_t buf
[NS_MAXMSG
];
75 int len
, qdcount
, ancount
;
77 char namebuf
[NS_MAXDNAME
];
79 /* query necessary resource records */
83 logger(LOG_DEBUG
, "Looking for SRV RRs '%s.*'", svc_name
);
85 len
= res_nsearch(state
, svc_name
, C_IN
, T_SRV
,
86 msg
.buf
, sizeof (msg
.buf
));
90 "DNS search for '%s' failed (%s)",
91 svc_name
, hstrerror(state
->res_h_errno
));
96 if (len
> sizeof (msg
.buf
)) {
98 "DNS query %ib message doesn't fit into %ib buffer",
99 len
, sizeof (msg
.buf
));
100 len
= sizeof (msg
.buf
);
103 /* parse the reply header */
105 ptr
= msg
.buf
+ sizeof (msg
.hdr
);
107 qdcount
= ntohs(msg
.hdr
.qdcount
);
108 ancount
= ntohs(msg
.hdr
.ancount
);
110 /* skip the question section */
112 len
= ns_skiprr(ptr
, eom
, ns_s_qd
, qdcount
);
114 logger(LOG_ERR
, "DNS query invalid message format");
119 /* parse the answer section */
121 logger(LOG_ERR
, "DNS query - no answers");
125 len
= dn_expand(msg
.buf
, eom
, ptr
, namebuf
, sizeof (namebuf
));
127 logger(LOG_ERR
, "DNS query invalid message format");
130 *rrname
= strdup(namebuf
);
131 if (*rrname
== NULL
) {
132 logger(LOG_ERR
, "Out of memory");
141 * Compare SRC RRs; used with qsort(). Sort order:
142 * "Earliest" (lowest number) priority first,
143 * then weight highest to lowest.
146 srvcmp(ad_disc_ds_t
*s1
, ad_disc_ds_t
*s2
)
148 if (s1
->priority
< s2
->priority
)
150 else if (s1
->priority
> s2
->priority
)
153 if (s1
->weight
< s2
->weight
)
155 else if (s1
->weight
> s2
->weight
)
162 * Query or search the SRV RRs for a given name.
164 * If dname == NULL then search (as in res_nsearch(3RESOLV), honoring any
165 * search list/option), else query (as in res_nquery(3RESOLV)).
167 * The output TTL will be the one of the SRV RR with the lowest TTL.
170 srv_query(res_state state
, const char *svc_name
, const char *dname
,
171 ad_disc_ds_t
*prefer
)
173 ad_disc_cds_t
*cds_res
= NULL
;
175 int len
, scnt
, maxcnt
;
177 msg
= malloc(NS_MAXMSG
);
179 logger(LOG_ERR
, "Out of memory");
183 /* query necessary resource records */
185 /* Search, querydomain or query */
189 logger(LOG_DEBUG
, "Looking for SRV RRs '%s.*'",
192 len
= res_nsearch(state
, svc_name
, C_IN
, T_SRV
,
197 "DNS search for '%s' failed (%s)",
198 svc_name
, hstrerror(state
->res_h_errno
));
202 } else { /* dname != NULL */
204 logger(LOG_DEBUG
, "Looking for SRV RRs '%s.%s' ",
208 len
= res_nquerydomain(state
, svc_name
, dname
, C_IN
, T_SRV
,
213 logger(LOG_DEBUG
, "DNS: %s.%s: %s",
215 hstrerror(state
->res_h_errno
));
221 if (len
> NS_MAXMSG
) {
223 "DNS query %ib message doesn't fit into %ib buffer",
229 /* parse the reply header */
231 cds_res
= srv_parse(msg
, len
, &scnt
, &maxcnt
);
236 add_preferred(cds_res
, prefer
, &scnt
, maxcnt
);
238 get_addresses(cds_res
, scnt
);
240 /* sort list of candidates */
242 qsort(cds_res
, scnt
, sizeof (*cds_res
),
243 (int (*)(const void *, const void *))srvcmp
);
253 static ad_disc_cds_t
*
254 srv_parse(uchar_t
*msg
, int len
, int *scnt
, int *maxcnt
)
257 ad_disc_cds_t
*cds_res
= NULL
;
259 int i
, qdcount
, ancount
, nscount
, arcount
;
263 /* LINTED E_FUNC_SET_NOT_USED */
267 char namebuf
[NS_MAXDNAME
];
271 ptr
= msg
+ sizeof (HEADER
);
273 qdcount
= ntohs(hdr
->qdcount
);
274 ancount
= ntohs(hdr
->ancount
);
275 nscount
= ntohs(hdr
->nscount
);
276 arcount
= ntohs(hdr
->arcount
);
278 /* skip the question section */
280 len
= ns_skiprr(ptr
, eom
, ns_s_qd
, qdcount
);
282 logger(LOG_ERR
, "DNS query invalid message format");
288 * Walk through the answer section, building the result array.
289 * The array size is +2 because we (possibly) add the preferred
290 * DC if it was not there, and an empty one (null termination).
293 *maxcnt
= ancount
+ 2;
294 cds_res
= calloc(*maxcnt
, sizeof (*cds_res
));
295 if (cds_res
== NULL
) {
296 logger(LOG_ERR
, "Out of memory");
301 for (i
= 0; i
< ancount
; i
++) {
303 len
= dn_expand(msg
, eom
, ptr
, namebuf
,
306 logger(LOG_ERR
, "DNS query invalid message format");
311 NS_GET16(class, ptr
);
314 if ((end
= ptr
+ size
) > eom
) {
315 logger(LOG_ERR
, "DNS query invalid message format");
324 NS_GET16(cds
->cds_ds
.priority
, ptr
);
325 NS_GET16(cds
->cds_ds
.weight
, ptr
);
326 NS_GET16(cds
->cds_ds
.port
, ptr
);
327 len
= dn_expand(msg
, eom
, ptr
, cds
->cds_ds
.host
,
328 sizeof (cds
->cds_ds
.host
));
330 logger(LOG_ERR
, "DNS query invalid SRV record");
334 cds
->cds_ds
.ttl
= rttl
;
337 logger(LOG_DEBUG
, " %s", namebuf
);
339 " ttl=%d pri=%d weight=%d %s:%d",
340 rttl
, cds
->cds_ds
.priority
, cds
->cds_ds
.weight
,
341 cds
->cds_ds
.host
, cds
->cds_ds
.port
);
345 /* move ptr to the end of current record */
348 *scnt
= (cds
- cds_res
);
350 /* skip the nameservers section (if any) */
352 len
= ns_skiprr(ptr
, eom
, ns_s_ns
, nscount
);
354 logger(LOG_ERR
, "DNS query invalid message format");
359 /* walk through the additional records */
360 for (i
= 0; i
< arcount
; i
++) {
363 len
= dn_expand(msg
, eom
, ptr
, namebuf
,
366 logger(LOG_ERR
, "DNS query invalid message format");
371 NS_GET16(class, ptr
);
374 if ((end
= ptr
+ size
) > eom
) {
375 logger(LOG_ERR
, "DNS query invalid message format");
390 char abuf
[INET6_ADDRSTRLEN
];
393 ap
= inet_ntop(af
, ptr
, abuf
, sizeof (abuf
));
394 logger(LOG_DEBUG
, " %s %s %s",
395 (af
== AF_INET
) ? "A " : "AAAA",
396 (ap
) ? ap
: "?", namebuf
);
399 /* Find the server, add to its address list. */
400 for (cds
= cds_res
; cds
->cds_ds
.host
[0] != '\0'; cds
++)
401 if (0 == strcmp(namebuf
, cds
->cds_ds
.host
))
402 save_addr(cds
, af
, ptr
, size
);
404 /* move ptr to the end of current record */
416 * Save this address on the server, if not already there.
419 save_addr(ad_disc_cds_t
*cds
, sa_family_t af
, uchar_t
*addr
, size_t alen
)
421 struct addrinfo
*ai
, *new_ai
, *last_ai
;
423 new_ai
= make_addrinfo(af
, addr
, alen
);
428 for (ai
= cds
->cds_ai
; ai
!= NULL
; ai
= ai
->ai_next
) {
431 if (new_ai
->ai_family
== ai
->ai_family
&&
432 new_ai
->ai_addrlen
== ai
->ai_addrlen
&&
433 0 == memcmp(new_ai
->ai_addr
, ai
->ai_addr
,
435 /* it's already there */
436 freeaddrinfo(new_ai
);
441 /* Not found. Append. */
442 if (last_ai
!= NULL
) {
443 last_ai
->ai_next
= new_ai
;
445 cds
->cds_ai
= new_ai
;
449 static struct addrinfo
*
450 make_addrinfo(sa_family_t af
, uchar_t
*addr
, size_t alen
)
454 struct sockaddr_in
*sin
;
455 struct sockaddr_in6
*sin6
;
458 ai
= calloc(1, sizeof (*ai
));
459 sa
= calloc(1, sizeof (struct sockaddr_in6
));
461 if (ai
== NULL
|| sa
== NULL
) {
462 logger(LOG_ERR
, "Out of memory");
469 if (alen
< sizeof (in_addr_t
)) {
470 logger(LOG_ERR
, "bad IPv4 addr len");
473 alen
= sizeof (in_addr_t
);
474 sin
->sin_family
= af
;
475 (void) memcpy(&sin
->sin_addr
, addr
, alen
);
476 slen
= sizeof (*sin
);
481 if (alen
< sizeof (in6_addr_t
)) {
482 logger(LOG_ERR
, "bad IPv6 addr len");
485 alen
= sizeof (in6_addr_t
);
486 sin6
->sin6_family
= af
;
487 (void) memcpy(&sin6
->sin6_addr
, addr
, alen
);
488 slen
= sizeof (*sin6
);
496 ai
->ai_addrlen
= slen
;
508 * Set a preferred candidate, which may already be in the list,
509 * in which case we just bump its priority, or else add it.
512 add_preferred(ad_disc_cds_t
*cds
, ad_disc_ds_t
*prefer
, int *nds
, int maxds
)
517 assert(*nds
< maxds
);
518 for (i
= 0; i
< *nds
; i
++) {
521 if (strcasecmp(ds
->host
, prefer
->host
) == 0) {
522 /* Force this element to be sorted first. */
530 * The preferred DC was not found in this DNS response,
531 * so add it. Again arrange for it to be sorted first.
532 * Address info. is added later.
535 (void) memcpy(ds
, prefer
, sizeof (*ds
));
542 * Do another pass over the array to check for missing addresses and
543 * try resolving the names. Normally, the DNS response from AD will
544 * have supplied additional address records for all the SRV records.
547 get_addresses(ad_disc_cds_t
*cds
, int cnt
)
551 for (i
= 0; i
< cnt
; i
++) {
552 if (cds
[i
].cds_ai
== NULL
) {
553 do_getaddrinfo(&cds
[i
]);
559 do_getaddrinfo(ad_disc_cds_t
*cds
)
561 struct addrinfo hints
;
567 (void) memset(&hints
, 0, sizeof (hints
));
568 hints
.ai_protocol
= IPPROTO_TCP
;
569 hints
.ai_socktype
= SOCK_STREAM
;
573 * This getaddrinfo call may take a LONG time, i.e. if our
574 * DNS servers are misconfigured or not responding.
575 * We need something like getaddrinfo_a(), with a timeout.
576 * For now, just log when this happens so we'll know
577 * if these calls are taking a long time.
580 logger(LOG_DEBUG
, "getaddrinfo %s ...", ds
->host
);
582 err
= getaddrinfo(cds
->cds_ds
.host
, NULL
, &hints
, &ai
);
585 logger(LOG_DEBUG
, "getaddrinfo %s rc=%d", ds
->host
, err
);
587 logger(LOG_WARNING
, "Lookup host (%s) took %u sec. "
588 "(Check DNS settings)", ds
->host
, (int)(t1
- t0
));
591 logger(LOG_ERR
, "No address for host: %s (%s)",
592 ds
->host
, gai_strerror(err
));
593 /* Make this sort at the end. */
594 ds
->priority
= 1 << 16;
602 srv_free(ad_disc_cds_t
*cds_vec
)
606 for (cds
= cds_vec
; cds
->cds_ds
.host
[0] != '\0'; cds
++) {
607 if (cds
->cds_ai
!= NULL
) {
608 freeaddrinfo(cds
->cds_ai
);