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