xref: /titanic_51/usr/src/lib/libadutils/common/srv_query.c (revision b3700b074e637f8c6991b70754c88a2cfffb246b)
1*b3700b07SGordon Ross /*
2*b3700b07SGordon Ross  * CDDL HEADER START
3*b3700b07SGordon Ross  *
4*b3700b07SGordon Ross  * The contents of this file are subject to the terms of the
5*b3700b07SGordon Ross  * Common Development and Distribution License (the "License").
6*b3700b07SGordon Ross  * You may not use this file except in compliance with the License.
7*b3700b07SGordon Ross  *
8*b3700b07SGordon Ross  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*b3700b07SGordon Ross  * or http://www.opensolaris.org/os/licensing.
10*b3700b07SGordon Ross  * See the License for the specific language governing permissions
11*b3700b07SGordon Ross  * and limitations under the License.
12*b3700b07SGordon Ross  *
13*b3700b07SGordon Ross  * When distributing Covered Code, include this CDDL HEADER in each
14*b3700b07SGordon Ross  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*b3700b07SGordon Ross  * If applicable, add the following below this CDDL HEADER, with the
16*b3700b07SGordon Ross  * fields enclosed by brackets "[]" replaced with your own identifying
17*b3700b07SGordon Ross  * information: Portions Copyright [yyyy] [name of copyright owner]
18*b3700b07SGordon Ross  *
19*b3700b07SGordon Ross  * CDDL HEADER END
20*b3700b07SGordon Ross  */
21*b3700b07SGordon Ross 
22*b3700b07SGordon Ross /*
23*b3700b07SGordon Ross  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
24*b3700b07SGordon Ross  * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
25*b3700b07SGordon Ross  */
26*b3700b07SGordon Ross 
27*b3700b07SGordon Ross /*
28*b3700b07SGordon Ross  * DNS query helper functions for addisc.c
29*b3700b07SGordon Ross  */
30*b3700b07SGordon Ross 
31*b3700b07SGordon Ross #include <stdio.h>
32*b3700b07SGordon Ross #include <string.h>
33*b3700b07SGordon Ross #include <strings.h>
34*b3700b07SGordon Ross #include <unistd.h>
35*b3700b07SGordon Ross #include <assert.h>
36*b3700b07SGordon Ross #include <stdlib.h>
37*b3700b07SGordon Ross #include <net/if.h>
38*b3700b07SGordon Ross #include <sys/types.h>
39*b3700b07SGordon Ross #include <sys/socket.h>
40*b3700b07SGordon Ross #include <sys/sockio.h>
41*b3700b07SGordon Ross #include <netinet/in.h>
42*b3700b07SGordon Ross #include <arpa/inet.h>
43*b3700b07SGordon Ross #include <arpa/nameser.h>
44*b3700b07SGordon Ross #include <resolv.h>
45*b3700b07SGordon Ross #include <netdb.h>
46*b3700b07SGordon Ross #include <ctype.h>
47*b3700b07SGordon Ross #include <errno.h>
48*b3700b07SGordon Ross #include <ldap.h>
49*b3700b07SGordon Ross #include <sasl/sasl.h>
50*b3700b07SGordon Ross #include <sys/u8_textprep.h>
51*b3700b07SGordon Ross #include <syslog.h>
52*b3700b07SGordon Ross #include <uuid/uuid.h>
53*b3700b07SGordon Ross #include <ads/dsgetdc.h>
54*b3700b07SGordon Ross #include "adutils_impl.h"
55*b3700b07SGordon Ross #include "addisc_impl.h"
56*b3700b07SGordon Ross 
57*b3700b07SGordon Ross static void save_addr(ad_disc_cds_t *, sa_family_t, uchar_t *, size_t);
58*b3700b07SGordon Ross static struct addrinfo *make_addrinfo(sa_family_t, uchar_t *, size_t);
59*b3700b07SGordon Ross 
60*b3700b07SGordon Ross static void do_getaddrinfo(ad_disc_cds_t *);
61*b3700b07SGordon Ross static ad_disc_cds_t *srv_parse(uchar_t *, int, int *, int *);
62*b3700b07SGordon Ross static void add_preferred(ad_disc_cds_t *, ad_disc_ds_t *, int *, int);
63*b3700b07SGordon Ross static void get_addresses(ad_disc_cds_t *, int);
64*b3700b07SGordon Ross 
65*b3700b07SGordon Ross /*
66*b3700b07SGordon Ross  * Simplified version of srv_query() for domain auto-discovery.
67*b3700b07SGordon Ross  */
68*b3700b07SGordon Ross int
69*b3700b07SGordon Ross srv_getdom(res_state state, const char *svc_name, char **rrname)
70*b3700b07SGordon Ross {
71*b3700b07SGordon Ross 	union {
72*b3700b07SGordon Ross 		HEADER hdr;
73*b3700b07SGordon Ross 		uchar_t buf[NS_MAXMSG];
74*b3700b07SGordon Ross 	} msg;
75*b3700b07SGordon Ross 	int len, qdcount, ancount;
76*b3700b07SGordon Ross 	uchar_t *ptr, *eom;
77*b3700b07SGordon Ross 	char namebuf[NS_MAXDNAME];
78*b3700b07SGordon Ross 
79*b3700b07SGordon Ross 	/* query necessary resource records */
80*b3700b07SGordon Ross 
81*b3700b07SGordon Ross 	*rrname = NULL;
82*b3700b07SGordon Ross 	if (DBG(DNS, 1))  {
83*b3700b07SGordon Ross 		logger(LOG_DEBUG, "Looking for SRV RRs '%s.*'", svc_name);
84*b3700b07SGordon Ross 	}
85*b3700b07SGordon Ross 	len = res_nsearch(state, svc_name, C_IN, T_SRV,
86*b3700b07SGordon Ross 	    msg.buf, sizeof (msg.buf));
87*b3700b07SGordon Ross 	if (len < 0) {
88*b3700b07SGordon Ross 		if (DBG(DNS, 0)) {
89*b3700b07SGordon Ross 			logger(LOG_DEBUG,
90*b3700b07SGordon Ross 			    "DNS search for '%s' failed (%s)",
91*b3700b07SGordon Ross 			    svc_name, hstrerror(state->res_h_errno));
92*b3700b07SGordon Ross 		}
93*b3700b07SGordon Ross 		return (-1);
94*b3700b07SGordon Ross 	}
95*b3700b07SGordon Ross 
96*b3700b07SGordon Ross 	if (len > sizeof (msg.buf)) {
97*b3700b07SGordon Ross 		logger(LOG_WARNING,
98*b3700b07SGordon Ross 		    "DNS query %ib message doesn't fit into %ib buffer",
99*b3700b07SGordon Ross 		    len, sizeof (msg.buf));
100*b3700b07SGordon Ross 		len = sizeof (msg.buf);
101*b3700b07SGordon Ross 	}
102*b3700b07SGordon Ross 
103*b3700b07SGordon Ross 	/* parse the reply header */
104*b3700b07SGordon Ross 
105*b3700b07SGordon Ross 	ptr = msg.buf + sizeof (msg.hdr);
106*b3700b07SGordon Ross 	eom = msg.buf + len;
107*b3700b07SGordon Ross 	qdcount = ntohs(msg.hdr.qdcount);
108*b3700b07SGordon Ross 	ancount = ntohs(msg.hdr.ancount);
109*b3700b07SGordon Ross 
110*b3700b07SGordon Ross 	/* skip the question section */
111*b3700b07SGordon Ross 
112*b3700b07SGordon Ross 	len = ns_skiprr(ptr, eom, ns_s_qd, qdcount);
113*b3700b07SGordon Ross 	if (len < 0) {
114*b3700b07SGordon Ross 		logger(LOG_ERR, "DNS query invalid message format");
115*b3700b07SGordon Ross 		return (-1);
116*b3700b07SGordon Ross 	}
117*b3700b07SGordon Ross 	ptr += len;
118*b3700b07SGordon Ross 
119*b3700b07SGordon Ross 	/* parse the answer section */
120*b3700b07SGordon Ross 	if (ancount < 1) {
121*b3700b07SGordon Ross 		logger(LOG_ERR, "DNS query - no answers");
122*b3700b07SGordon Ross 		return (-1);
123*b3700b07SGordon Ross 	}
124*b3700b07SGordon Ross 
125*b3700b07SGordon Ross 	len = dn_expand(msg.buf, eom, ptr, namebuf, sizeof (namebuf));
126*b3700b07SGordon Ross 	if (len < 0) {
127*b3700b07SGordon Ross 		logger(LOG_ERR, "DNS query invalid message format");
128*b3700b07SGordon Ross 		return (-1);
129*b3700b07SGordon Ross 	}
130*b3700b07SGordon Ross 	*rrname = strdup(namebuf);
131*b3700b07SGordon Ross 	if (*rrname == NULL) {
132*b3700b07SGordon Ross 		logger(LOG_ERR, "Out of memory");
133*b3700b07SGordon Ross 		return (-1);
134*b3700b07SGordon Ross 	}
135*b3700b07SGordon Ross 
136*b3700b07SGordon Ross 	return (0);
137*b3700b07SGordon Ross }
138*b3700b07SGordon Ross 
139*b3700b07SGordon Ross 
140*b3700b07SGordon Ross /*
141*b3700b07SGordon Ross  * Compare SRC RRs; used with qsort().  Sort order:
142*b3700b07SGordon Ross  * "Earliest" (lowest number) priority first,
143*b3700b07SGordon Ross  * then weight highest to lowest.
144*b3700b07SGordon Ross  */
145*b3700b07SGordon Ross static int
146*b3700b07SGordon Ross srvcmp(ad_disc_ds_t *s1, ad_disc_ds_t *s2)
147*b3700b07SGordon Ross {
148*b3700b07SGordon Ross 	if (s1->priority < s2->priority)
149*b3700b07SGordon Ross 		return (-1);
150*b3700b07SGordon Ross 	else if (s1->priority > s2->priority)
151*b3700b07SGordon Ross 		return (1);
152*b3700b07SGordon Ross 
153*b3700b07SGordon Ross 	if (s1->weight < s2->weight)
154*b3700b07SGordon Ross 		return (1);
155*b3700b07SGordon Ross 	else if (s1->weight > s2->weight)
156*b3700b07SGordon Ross 		return (-1);
157*b3700b07SGordon Ross 
158*b3700b07SGordon Ross 	return (0);
159*b3700b07SGordon Ross }
160*b3700b07SGordon Ross 
161*b3700b07SGordon Ross /*
162*b3700b07SGordon Ross  * Query or search the SRV RRs for a given name.
163*b3700b07SGordon Ross  *
164*b3700b07SGordon Ross  * If dname == NULL then search (as in res_nsearch(3RESOLV), honoring any
165*b3700b07SGordon Ross  * search list/option), else query (as in res_nquery(3RESOLV)).
166*b3700b07SGordon Ross  *
167*b3700b07SGordon Ross  * The output TTL will be the one of the SRV RR with the lowest TTL.
168*b3700b07SGordon Ross  */
169*b3700b07SGordon Ross ad_disc_cds_t *
170*b3700b07SGordon Ross srv_query(res_state state, const char *svc_name, const char *dname,
171*b3700b07SGordon Ross     ad_disc_ds_t *prefer)
172*b3700b07SGordon Ross {
173*b3700b07SGordon Ross 	ad_disc_cds_t *cds_res = NULL;
174*b3700b07SGordon Ross 	uchar_t *msg = NULL;
175*b3700b07SGordon Ross 	int len, scnt, maxcnt;
176*b3700b07SGordon Ross 
177*b3700b07SGordon Ross 	msg = malloc(NS_MAXMSG);
178*b3700b07SGordon Ross 	if (msg == NULL) {
179*b3700b07SGordon Ross 		logger(LOG_ERR, "Out of memory");
180*b3700b07SGordon Ross 		return (NULL);
181*b3700b07SGordon Ross 	}
182*b3700b07SGordon Ross 
183*b3700b07SGordon Ross 	/* query necessary resource records */
184*b3700b07SGordon Ross 
185*b3700b07SGordon Ross 	/* Search, querydomain or query */
186*b3700b07SGordon Ross 	if (dname == NULL) {
187*b3700b07SGordon Ross 		dname = "*";
188*b3700b07SGordon Ross 		if (DBG(DNS, 1))  {
189*b3700b07SGordon Ross 			logger(LOG_DEBUG, "Looking for SRV RRs '%s.*'",
190*b3700b07SGordon Ross 			    svc_name);
191*b3700b07SGordon Ross 		}
192*b3700b07SGordon Ross 		len = res_nsearch(state, svc_name, C_IN, T_SRV,
193*b3700b07SGordon Ross 		    msg, NS_MAXMSG);
194*b3700b07SGordon Ross 		if (len < 0) {
195*b3700b07SGordon Ross 			if (DBG(DNS, 0)) {
196*b3700b07SGordon Ross 				logger(LOG_DEBUG,
197*b3700b07SGordon Ross 				    "DNS search for '%s' failed (%s)",
198*b3700b07SGordon Ross 				    svc_name, hstrerror(state->res_h_errno));
199*b3700b07SGordon Ross 			}
200*b3700b07SGordon Ross 			goto errout;
201*b3700b07SGordon Ross 		}
202*b3700b07SGordon Ross 	} else { /* dname != NULL */
203*b3700b07SGordon Ross 		if (DBG(DNS, 1)) {
204*b3700b07SGordon Ross 			logger(LOG_DEBUG, "Looking for SRV RRs '%s.%s' ",
205*b3700b07SGordon Ross 			    svc_name, dname);
206*b3700b07SGordon Ross 		}
207*b3700b07SGordon Ross 
208*b3700b07SGordon Ross 		len = res_nquerydomain(state, svc_name, dname, C_IN, T_SRV,
209*b3700b07SGordon Ross 		    msg, NS_MAXMSG);
210*b3700b07SGordon Ross 
211*b3700b07SGordon Ross 		if (len < 0) {
212*b3700b07SGordon Ross 			if (DBG(DNS, 0)) {
213*b3700b07SGordon Ross 				logger(LOG_DEBUG, "DNS: %s.%s: %s",
214*b3700b07SGordon Ross 				    svc_name, dname,
215*b3700b07SGordon Ross 				    hstrerror(state->res_h_errno));
216*b3700b07SGordon Ross 			}
217*b3700b07SGordon Ross 			goto errout;
218*b3700b07SGordon Ross 		}
219*b3700b07SGordon Ross 	}
220*b3700b07SGordon Ross 
221*b3700b07SGordon Ross 	if (len > NS_MAXMSG) {
222*b3700b07SGordon Ross 		logger(LOG_WARNING,
223*b3700b07SGordon Ross 		    "DNS query %ib message doesn't fit into %ib buffer",
224*b3700b07SGordon Ross 		    len, NS_MAXMSG);
225*b3700b07SGordon Ross 		len = NS_MAXMSG;
226*b3700b07SGordon Ross 	}
227*b3700b07SGordon Ross 
228*b3700b07SGordon Ross 
229*b3700b07SGordon Ross 	/* parse the reply header */
230*b3700b07SGordon Ross 
231*b3700b07SGordon Ross 	cds_res = srv_parse(msg, len, &scnt, &maxcnt);
232*b3700b07SGordon Ross 	if (cds_res == NULL)
233*b3700b07SGordon Ross 		goto errout;
234*b3700b07SGordon Ross 
235*b3700b07SGordon Ross 	if (prefer != NULL)
236*b3700b07SGordon Ross 		add_preferred(cds_res, prefer, &scnt, maxcnt);
237*b3700b07SGordon Ross 
238*b3700b07SGordon Ross 	get_addresses(cds_res, scnt);
239*b3700b07SGordon Ross 
240*b3700b07SGordon Ross 	/* sort list of candidates */
241*b3700b07SGordon Ross 	if (scnt > 1)
242*b3700b07SGordon Ross 		qsort(cds_res, scnt, sizeof (*cds_res),
243*b3700b07SGordon Ross 		    (int (*)(const void *, const void *))srvcmp);
244*b3700b07SGordon Ross 
245*b3700b07SGordon Ross 	free(msg);
246*b3700b07SGordon Ross 	return (cds_res);
247*b3700b07SGordon Ross 
248*b3700b07SGordon Ross errout:
249*b3700b07SGordon Ross 	free(msg);
250*b3700b07SGordon Ross 	return (NULL);
251*b3700b07SGordon Ross }
252*b3700b07SGordon Ross 
253*b3700b07SGordon Ross static ad_disc_cds_t *
254*b3700b07SGordon Ross srv_parse(uchar_t *msg, int len, int *scnt, int *maxcnt)
255*b3700b07SGordon Ross {
256*b3700b07SGordon Ross 	ad_disc_cds_t *cds;
257*b3700b07SGordon Ross 	ad_disc_cds_t *cds_res = NULL;
258*b3700b07SGordon Ross 	HEADER *hdr;
259*b3700b07SGordon Ross 	int i, qdcount, ancount, nscount, arcount;
260*b3700b07SGordon Ross 	uchar_t *ptr, *eom;
261*b3700b07SGordon Ross 	uchar_t *end;
262*b3700b07SGordon Ross 	uint16_t type;
263*b3700b07SGordon Ross 	/* LINTED  E_FUNC_SET_NOT_USED */
264*b3700b07SGordon Ross 	uint16_t class;
265*b3700b07SGordon Ross 	uint32_t rttl;
266*b3700b07SGordon Ross 	uint16_t size;
267*b3700b07SGordon Ross 	char namebuf[NS_MAXDNAME];
268*b3700b07SGordon Ross 
269*b3700b07SGordon Ross 	eom = msg + len;
270*b3700b07SGordon Ross 	hdr = (void *)msg;
271*b3700b07SGordon Ross 	ptr = msg + sizeof (HEADER);
272*b3700b07SGordon Ross 
273*b3700b07SGordon Ross 	qdcount = ntohs(hdr->qdcount);
274*b3700b07SGordon Ross 	ancount = ntohs(hdr->ancount);
275*b3700b07SGordon Ross 	nscount = ntohs(hdr->nscount);
276*b3700b07SGordon Ross 	arcount = ntohs(hdr->arcount);
277*b3700b07SGordon Ross 
278*b3700b07SGordon Ross 	/* skip the question section */
279*b3700b07SGordon Ross 
280*b3700b07SGordon Ross 	len = ns_skiprr(ptr, eom, ns_s_qd, qdcount);
281*b3700b07SGordon Ross 	if (len < 0) {
282*b3700b07SGordon Ross 		logger(LOG_ERR, "DNS query invalid message format");
283*b3700b07SGordon Ross 		return (NULL);
284*b3700b07SGordon Ross 	}
285*b3700b07SGordon Ross 	ptr += len;
286*b3700b07SGordon Ross 
287*b3700b07SGordon Ross 	/*
288*b3700b07SGordon Ross 	 * Walk through the answer section, building the result array.
289*b3700b07SGordon Ross 	 * The array size is +2 because we (possibly) add the preferred
290*b3700b07SGordon Ross 	 * DC if it was not there, and an empty one (null termination).
291*b3700b07SGordon Ross 	 */
292*b3700b07SGordon Ross 
293*b3700b07SGordon Ross 	*maxcnt = ancount + 2;
294*b3700b07SGordon Ross 	cds_res = calloc(*maxcnt, sizeof (*cds_res));
295*b3700b07SGordon Ross 	if (cds_res == NULL) {
296*b3700b07SGordon Ross 		logger(LOG_ERR, "Out of memory");
297*b3700b07SGordon Ross 		return (NULL);
298*b3700b07SGordon Ross 	}
299*b3700b07SGordon Ross 
300*b3700b07SGordon Ross 	cds = cds_res;
301*b3700b07SGordon Ross 	for (i = 0; i < ancount; i++) {
302*b3700b07SGordon Ross 
303*b3700b07SGordon Ross 		len = dn_expand(msg, eom, ptr, namebuf,
304*b3700b07SGordon Ross 		    sizeof (namebuf));
305*b3700b07SGordon Ross 		if (len < 0) {
306*b3700b07SGordon Ross 			logger(LOG_ERR, "DNS query invalid message format");
307*b3700b07SGordon Ross 			goto err;
308*b3700b07SGordon Ross 		}
309*b3700b07SGordon Ross 		ptr += len;
310*b3700b07SGordon Ross 		NS_GET16(type, ptr);
311*b3700b07SGordon Ross 		NS_GET16(class, ptr);
312*b3700b07SGordon Ross 		NS_GET32(rttl, ptr);
313*b3700b07SGordon Ross 		NS_GET16(size, ptr);
314*b3700b07SGordon Ross 		if ((end = ptr + size) > eom) {
315*b3700b07SGordon Ross 			logger(LOG_ERR, "DNS query invalid message format");
316*b3700b07SGordon Ross 			goto err;
317*b3700b07SGordon Ross 		}
318*b3700b07SGordon Ross 
319*b3700b07SGordon Ross 		if (type != T_SRV) {
320*b3700b07SGordon Ross 			ptr = end;
321*b3700b07SGordon Ross 			continue;
322*b3700b07SGordon Ross 		}
323*b3700b07SGordon Ross 
324*b3700b07SGordon Ross 		NS_GET16(cds->cds_ds.priority, ptr);
325*b3700b07SGordon Ross 		NS_GET16(cds->cds_ds.weight, ptr);
326*b3700b07SGordon Ross 		NS_GET16(cds->cds_ds.port, ptr);
327*b3700b07SGordon Ross 		len = dn_expand(msg, eom, ptr, cds->cds_ds.host,
328*b3700b07SGordon Ross 		    sizeof (cds->cds_ds.host));
329*b3700b07SGordon Ross 		if (len < 0) {
330*b3700b07SGordon Ross 			logger(LOG_ERR, "DNS query invalid SRV record");
331*b3700b07SGordon Ross 			goto err;
332*b3700b07SGordon Ross 		}
333*b3700b07SGordon Ross 
334*b3700b07SGordon Ross 		cds->cds_ds.ttl = rttl;
335*b3700b07SGordon Ross 
336*b3700b07SGordon Ross 		if (DBG(DNS, 2)) {
337*b3700b07SGordon Ross 			logger(LOG_DEBUG, "    %s", namebuf);
338*b3700b07SGordon Ross 			logger(LOG_DEBUG,
339*b3700b07SGordon Ross 			    "        ttl=%d pri=%d weight=%d %s:%d",
340*b3700b07SGordon Ross 			    rttl, cds->cds_ds.priority, cds->cds_ds.weight,
341*b3700b07SGordon Ross 			    cds->cds_ds.host, cds->cds_ds.port);
342*b3700b07SGordon Ross 		}
343*b3700b07SGordon Ross 		cds++;
344*b3700b07SGordon Ross 
345*b3700b07SGordon Ross 		/* move ptr to the end of current record */
346*b3700b07SGordon Ross 		ptr = end;
347*b3700b07SGordon Ross 	}
348*b3700b07SGordon Ross 	*scnt = (cds - cds_res);
349*b3700b07SGordon Ross 
350*b3700b07SGordon Ross 	/* skip the nameservers section (if any) */
351*b3700b07SGordon Ross 
352*b3700b07SGordon Ross 	len = ns_skiprr(ptr, eom, ns_s_ns, nscount);
353*b3700b07SGordon Ross 	if (len < 0) {
354*b3700b07SGordon Ross 		logger(LOG_ERR, "DNS query invalid message format");
355*b3700b07SGordon Ross 		goto err;
356*b3700b07SGordon Ross 	}
357*b3700b07SGordon Ross 	ptr += len;
358*b3700b07SGordon Ross 
359*b3700b07SGordon Ross 	/* walk through the additional records */
360*b3700b07SGordon Ross 	for (i = 0; i < arcount; i++) {
361*b3700b07SGordon Ross 		sa_family_t af;
362*b3700b07SGordon Ross 
363*b3700b07SGordon Ross 		len = dn_expand(msg, eom, ptr, namebuf,
364*b3700b07SGordon Ross 		    sizeof (namebuf));
365*b3700b07SGordon Ross 		if (len < 0) {
366*b3700b07SGordon Ross 			logger(LOG_ERR, "DNS query invalid message format");
367*b3700b07SGordon Ross 			goto err;
368*b3700b07SGordon Ross 		}
369*b3700b07SGordon Ross 		ptr += len;
370*b3700b07SGordon Ross 		NS_GET16(type, ptr);
371*b3700b07SGordon Ross 		NS_GET16(class, ptr);
372*b3700b07SGordon Ross 		NS_GET32(rttl, ptr);
373*b3700b07SGordon Ross 		NS_GET16(size, ptr);
374*b3700b07SGordon Ross 		if ((end = ptr + size) > eom) {
375*b3700b07SGordon Ross 			logger(LOG_ERR, "DNS query invalid message format");
376*b3700b07SGordon Ross 			goto err;
377*b3700b07SGordon Ross 		}
378*b3700b07SGordon Ross 		switch (type) {
379*b3700b07SGordon Ross 		case ns_t_a:
380*b3700b07SGordon Ross 			af = AF_INET;
381*b3700b07SGordon Ross 			break;
382*b3700b07SGordon Ross 		case ns_t_aaaa:
383*b3700b07SGordon Ross 			af = AF_INET6;
384*b3700b07SGordon Ross 			break;
385*b3700b07SGordon Ross 		default:
386*b3700b07SGordon Ross 			continue;
387*b3700b07SGordon Ross 		}
388*b3700b07SGordon Ross 
389*b3700b07SGordon Ross 		if (DBG(DNS, 2)) {
390*b3700b07SGordon Ross 			char abuf[INET6_ADDRSTRLEN];
391*b3700b07SGordon Ross 			const char *ap;
392*b3700b07SGordon Ross 
393*b3700b07SGordon Ross 			ap = inet_ntop(af, ptr, abuf, sizeof (abuf));
394*b3700b07SGordon Ross 			logger(LOG_DEBUG, "    %s    %s    %s",
395*b3700b07SGordon Ross 			    (af == AF_INET) ? "A   " : "AAAA",
396*b3700b07SGordon Ross 			    (ap) ? ap : "?", namebuf);
397*b3700b07SGordon Ross 		}
398*b3700b07SGordon Ross 
399*b3700b07SGordon Ross 		/* Find the server, add to its address list. */
400*b3700b07SGordon Ross 		for (cds = cds_res; cds->cds_ds.host[0] != '\0'; cds++)
401*b3700b07SGordon Ross 			if (0 == strcmp(namebuf, cds->cds_ds.host))
402*b3700b07SGordon Ross 				save_addr(cds, af, ptr, size);
403*b3700b07SGordon Ross 
404*b3700b07SGordon Ross 		/* move ptr to the end of current record */
405*b3700b07SGordon Ross 		ptr = end;
406*b3700b07SGordon Ross 	}
407*b3700b07SGordon Ross 
408*b3700b07SGordon Ross 	return (cds_res);
409*b3700b07SGordon Ross 
410*b3700b07SGordon Ross err:
411*b3700b07SGordon Ross 	free(cds_res);
412*b3700b07SGordon Ross 	return (NULL);
413*b3700b07SGordon Ross }
414*b3700b07SGordon Ross 
415*b3700b07SGordon Ross /*
416*b3700b07SGordon Ross  * Save this address on the server, if not already there.
417*b3700b07SGordon Ross  */
418*b3700b07SGordon Ross static void
419*b3700b07SGordon Ross save_addr(ad_disc_cds_t *cds, sa_family_t af, uchar_t *addr, size_t alen)
420*b3700b07SGordon Ross {
421*b3700b07SGordon Ross 	struct addrinfo *ai, *new_ai, *last_ai;
422*b3700b07SGordon Ross 
423*b3700b07SGordon Ross 	new_ai = make_addrinfo(af, addr, alen);
424*b3700b07SGordon Ross 	if (new_ai == NULL)
425*b3700b07SGordon Ross 		return;
426*b3700b07SGordon Ross 
427*b3700b07SGordon Ross 	last_ai = NULL;
428*b3700b07SGordon Ross 	for (ai = cds->cds_ai; ai != NULL; ai = ai->ai_next) {
429*b3700b07SGordon Ross 		last_ai = ai;
430*b3700b07SGordon Ross 
431*b3700b07SGordon Ross 		if (new_ai->ai_family == ai->ai_family &&
432*b3700b07SGordon Ross 		    new_ai->ai_addrlen == ai->ai_addrlen &&
433*b3700b07SGordon Ross 		    0 == memcmp(new_ai->ai_addr, ai->ai_addr,
434*b3700b07SGordon Ross 		    ai->ai_addrlen)) {
435*b3700b07SGordon Ross 			/* it's already there */
436*b3700b07SGordon Ross 			freeaddrinfo(new_ai);
437*b3700b07SGordon Ross 			return;
438*b3700b07SGordon Ross 		}
439*b3700b07SGordon Ross 	}
440*b3700b07SGordon Ross 
441*b3700b07SGordon Ross 	/* Not found.  Append. */
442*b3700b07SGordon Ross 	if (last_ai != NULL) {
443*b3700b07SGordon Ross 		last_ai->ai_next = new_ai;
444*b3700b07SGordon Ross 	} else {
445*b3700b07SGordon Ross 		cds->cds_ai = new_ai;
446*b3700b07SGordon Ross 	}
447*b3700b07SGordon Ross }
448*b3700b07SGordon Ross 
449*b3700b07SGordon Ross static struct addrinfo *
450*b3700b07SGordon Ross make_addrinfo(sa_family_t af, uchar_t *addr, size_t alen)
451*b3700b07SGordon Ross {
452*b3700b07SGordon Ross 	struct addrinfo *ai;
453*b3700b07SGordon Ross 	struct sockaddr *sa;
454*b3700b07SGordon Ross 	struct sockaddr_in *sin;
455*b3700b07SGordon Ross 	struct sockaddr_in6 *sin6;
456*b3700b07SGordon Ross 	int slen;
457*b3700b07SGordon Ross 
458*b3700b07SGordon Ross 	ai = calloc(1, sizeof (*ai));
459*b3700b07SGordon Ross 	sa = calloc(1, sizeof (struct sockaddr_in6));
460*b3700b07SGordon Ross 
461*b3700b07SGordon Ross 	if (ai == NULL || sa == NULL) {
462*b3700b07SGordon Ross 		logger(LOG_ERR, "Out of memory");
463*b3700b07SGordon Ross 		goto errout;
464*b3700b07SGordon Ross 	}
465*b3700b07SGordon Ross 
466*b3700b07SGordon Ross 	switch (af) {
467*b3700b07SGordon Ross 	case AF_INET:
468*b3700b07SGordon Ross 		sin = (void *)sa;
469*b3700b07SGordon Ross 		if (alen < sizeof (in_addr_t)) {
470*b3700b07SGordon Ross 			logger(LOG_ERR, "bad IPv4 addr len");
471*b3700b07SGordon Ross 			goto errout;
472*b3700b07SGordon Ross 		}
473*b3700b07SGordon Ross 		alen = sizeof (in_addr_t);
474*b3700b07SGordon Ross 		sin->sin_family = af;
475*b3700b07SGordon Ross 		(void) memcpy(&sin->sin_addr, addr, alen);
476*b3700b07SGordon Ross 		slen = sizeof (*sin);
477*b3700b07SGordon Ross 		break;
478*b3700b07SGordon Ross 
479*b3700b07SGordon Ross 	case AF_INET6:
480*b3700b07SGordon Ross 		sin6 = (void *)sa;
481*b3700b07SGordon Ross 		if (alen < sizeof (in6_addr_t)) {
482*b3700b07SGordon Ross 			logger(LOG_ERR, "bad IPv6 addr len");
483*b3700b07SGordon Ross 			goto errout;
484*b3700b07SGordon Ross 		}
485*b3700b07SGordon Ross 		alen = sizeof (in6_addr_t);
486*b3700b07SGordon Ross 		sin6->sin6_family = af;
487*b3700b07SGordon Ross 		(void) memcpy(&sin6->sin6_addr, addr, alen);
488*b3700b07SGordon Ross 		slen = sizeof (*sin6);
489*b3700b07SGordon Ross 		break;
490*b3700b07SGordon Ross 
491*b3700b07SGordon Ross 	default:
492*b3700b07SGordon Ross 		goto errout;
493*b3700b07SGordon Ross 	}
494*b3700b07SGordon Ross 
495*b3700b07SGordon Ross 	ai->ai_family = af;
496*b3700b07SGordon Ross 	ai->ai_addrlen = slen;
497*b3700b07SGordon Ross 	ai->ai_addr = sa;
498*b3700b07SGordon Ross 	sa->sa_family = af;
499*b3700b07SGordon Ross 	return (ai);
500*b3700b07SGordon Ross 
501*b3700b07SGordon Ross errout:
502*b3700b07SGordon Ross 	free(ai);
503*b3700b07SGordon Ross 	free(sa);
504*b3700b07SGordon Ross 	return (NULL);
505*b3700b07SGordon Ross }
506*b3700b07SGordon Ross 
507*b3700b07SGordon Ross /*
508*b3700b07SGordon Ross  * Set a preferred candidate, which may already be in the list,
509*b3700b07SGordon Ross  * in which case we just bump its priority, or else add it.
510*b3700b07SGordon Ross  */
511*b3700b07SGordon Ross static void
512*b3700b07SGordon Ross add_preferred(ad_disc_cds_t *cds, ad_disc_ds_t *prefer, int *nds, int maxds)
513*b3700b07SGordon Ross {
514*b3700b07SGordon Ross 	ad_disc_ds_t *ds;
515*b3700b07SGordon Ross 	int i;
516*b3700b07SGordon Ross 
517*b3700b07SGordon Ross 	assert(*nds < maxds);
518*b3700b07SGordon Ross 	for (i = 0; i < *nds; i++) {
519*b3700b07SGordon Ross 		ds = &cds[i].cds_ds;
520*b3700b07SGordon Ross 
521*b3700b07SGordon Ross 		if (strcasecmp(ds->host, prefer->host) == 0) {
522*b3700b07SGordon Ross 			/* Force this element to be sorted first. */
523*b3700b07SGordon Ross 			ds->priority = 0;
524*b3700b07SGordon Ross 			ds->weight = 200;
525*b3700b07SGordon Ross 			return;
526*b3700b07SGordon Ross 		}
527*b3700b07SGordon Ross 	}
528*b3700b07SGordon Ross 
529*b3700b07SGordon Ross 	/*
530*b3700b07SGordon Ross 	 * The preferred DC was not found in this DNS response,
531*b3700b07SGordon Ross 	 * so add it.  Again arrange for it to be sorted first.
532*b3700b07SGordon Ross 	 * Address info. is added later.
533*b3700b07SGordon Ross 	 */
534*b3700b07SGordon Ross 	ds = &cds[i].cds_ds;
535*b3700b07SGordon Ross 	(void) memcpy(ds, prefer, sizeof (*ds));
536*b3700b07SGordon Ross 	ds->priority = 0;
537*b3700b07SGordon Ross 	ds->weight = 200;
538*b3700b07SGordon Ross 	*nds = i + 1;
539*b3700b07SGordon Ross }
540*b3700b07SGordon Ross 
541*b3700b07SGordon Ross /*
542*b3700b07SGordon Ross  * Do another pass over the array to check for missing addresses and
543*b3700b07SGordon Ross  * try resolving the names.  Normally, the DNS response from AD will
544*b3700b07SGordon Ross  * have supplied additional address records for all the SRV records.
545*b3700b07SGordon Ross  */
546*b3700b07SGordon Ross static void
547*b3700b07SGordon Ross get_addresses(ad_disc_cds_t *cds, int cnt)
548*b3700b07SGordon Ross {
549*b3700b07SGordon Ross 	int i;
550*b3700b07SGordon Ross 
551*b3700b07SGordon Ross 	for (i = 0; i < cnt; i++) {
552*b3700b07SGordon Ross 		if (cds[i].cds_ai == NULL) {
553*b3700b07SGordon Ross 			do_getaddrinfo(&cds[i]);
554*b3700b07SGordon Ross 		}
555*b3700b07SGordon Ross 	}
556*b3700b07SGordon Ross }
557*b3700b07SGordon Ross 
558*b3700b07SGordon Ross static void
559*b3700b07SGordon Ross do_getaddrinfo(ad_disc_cds_t *cds)
560*b3700b07SGordon Ross {
561*b3700b07SGordon Ross 	struct addrinfo hints;
562*b3700b07SGordon Ross 	struct addrinfo *ai;
563*b3700b07SGordon Ross 	ad_disc_ds_t *ds;
564*b3700b07SGordon Ross 	time_t t0, t1;
565*b3700b07SGordon Ross 	int err;
566*b3700b07SGordon Ross 
567*b3700b07SGordon Ross 	(void) memset(&hints, 0, sizeof (hints));
568*b3700b07SGordon Ross 	hints.ai_protocol = IPPROTO_TCP;
569*b3700b07SGordon Ross 	hints.ai_socktype = SOCK_STREAM;
570*b3700b07SGordon Ross 	ds = &cds->cds_ds;
571*b3700b07SGordon Ross 
572*b3700b07SGordon Ross 	/*
573*b3700b07SGordon Ross 	 * This getaddrinfo call may take a LONG time, i.e. if our
574*b3700b07SGordon Ross 	 * DNS servers are misconfigured or not responding.
575*b3700b07SGordon Ross 	 * We need something like getaddrinfo_a(), with a timeout.
576*b3700b07SGordon Ross 	 * For now, just log when this happens so we'll know
577*b3700b07SGordon Ross 	 * if these calls are taking a long time.
578*b3700b07SGordon Ross 	 */
579*b3700b07SGordon Ross 	if (DBG(DNS, 2))
580*b3700b07SGordon Ross 		logger(LOG_DEBUG, "getaddrinfo %s ...", ds->host);
581*b3700b07SGordon Ross 	t0 = time(NULL);
582*b3700b07SGordon Ross 	err = getaddrinfo(cds->cds_ds.host, NULL, &hints, &ai);
583*b3700b07SGordon Ross 	t1 = time(NULL);
584*b3700b07SGordon Ross 	if (DBG(DNS, 2))
585*b3700b07SGordon Ross 		logger(LOG_DEBUG, "getaddrinfo %s rc=%d", ds->host, err);
586*b3700b07SGordon Ross 	if (t1 > (t0 + 5)) {
587*b3700b07SGordon Ross 		logger(LOG_WARNING, "Lookup host (%s) took %u sec. "
588*b3700b07SGordon Ross 		    "(Check DNS settings)", ds->host, (int)(t1 - t0));
589*b3700b07SGordon Ross 	}
590*b3700b07SGordon Ross 	if (err != 0) {
591*b3700b07SGordon Ross 		logger(LOG_ERR, "No address for host: %s (%s)",
592*b3700b07SGordon Ross 		    ds->host, gai_strerror(err));
593*b3700b07SGordon Ross 		/* Make this sort at the end. */
594*b3700b07SGordon Ross 		ds->priority = 1 << 16;
595*b3700b07SGordon Ross 		return;
596*b3700b07SGordon Ross 	}
597*b3700b07SGordon Ross 
598*b3700b07SGordon Ross 	cds->cds_ai = ai;
599*b3700b07SGordon Ross }
600*b3700b07SGordon Ross 
601*b3700b07SGordon Ross void
602*b3700b07SGordon Ross srv_free(ad_disc_cds_t *cds_vec)
603*b3700b07SGordon Ross {
604*b3700b07SGordon Ross 	ad_disc_cds_t *cds;
605*b3700b07SGordon Ross 
606*b3700b07SGordon Ross 	for (cds = cds_vec; cds->cds_ds.host[0] != '\0'; cds++) {
607*b3700b07SGordon Ross 		if (cds->cds_ai != NULL) {
608*b3700b07SGordon Ross 			freeaddrinfo(cds->cds_ai);
609*b3700b07SGordon Ross 		}
610*b3700b07SGordon Ross 	}
611*b3700b07SGordon Ross 	free(cds_vec);
612*b3700b07SGordon Ross }
613