/* $OpenLDAP$ */ /* This work is part of OpenLDAP Software . * * Copyright 1998-2022 The OpenLDAP Foundation. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted only as authorized by the OpenLDAP * Public License. * * A copy of this license is available in the file LICENSE in the * top-level directory of the distribution or, alternatively, at * . */ /* * locate LDAP servers using DNS SRV records. * Location code based on MIT Kerberos KDC location code. */ #include "portable.h" #include #include #include #include #include #include #include "ldap-int.h" #ifdef HAVE_ARPA_NAMESER_H #include #endif #ifdef HAVE_RESOLV_H #include #endif int ldap_dn2domain( LDAP_CONST char *dn_in, char **domainp) { int i, j; char *ndomain; LDAPDN dn = NULL; LDAPRDN rdn = NULL; LDAPAVA *ava = NULL; struct berval domain = BER_BVNULL; static const struct berval DC = BER_BVC("DC"); static const struct berval DCOID = BER_BVC("0.9.2342.19200300.100.1.25"); assert( dn_in != NULL ); assert( domainp != NULL ); *domainp = NULL; if ( ldap_str2dn( dn_in, &dn, LDAP_DN_FORMAT_LDAP ) != LDAP_SUCCESS ) { return -2; } if( dn ) for( i=0; dn[i] != NULL; i++ ) { rdn = dn[i]; for( j=0; rdn[j] != NULL; j++ ) { ava = rdn[j]; if( rdn[j+1] == NULL && (ava->la_flags & LDAP_AVA_STRING) && ava->la_value.bv_len && ( ber_bvstrcasecmp( &ava->la_attr, &DC ) == 0 || ber_bvcmp( &ava->la_attr, &DCOID ) == 0 ) ) { if( domain.bv_len == 0 ) { ndomain = LDAP_REALLOC( domain.bv_val, ava->la_value.bv_len + 1); if( ndomain == NULL ) { goto return_error; } domain.bv_val = ndomain; AC_MEMCPY( domain.bv_val, ava->la_value.bv_val, ava->la_value.bv_len ); domain.bv_len = ava->la_value.bv_len; domain.bv_val[domain.bv_len] = '\0'; } else { ndomain = LDAP_REALLOC( domain.bv_val, ava->la_value.bv_len + sizeof(".") + domain.bv_len ); if( ndomain == NULL ) { goto return_error; } domain.bv_val = ndomain; domain.bv_val[domain.bv_len++] = '.'; AC_MEMCPY( &domain.bv_val[domain.bv_len], ava->la_value.bv_val, ava->la_value.bv_len ); domain.bv_len += ava->la_value.bv_len; domain.bv_val[domain.bv_len] = '\0'; } } else { domain.bv_len = 0; } } } if( domain.bv_len == 0 && domain.bv_val != NULL ) { LDAP_FREE( domain.bv_val ); domain.bv_val = NULL; } ldap_dnfree( dn ); *domainp = domain.bv_val; return 0; return_error: ldap_dnfree( dn ); LDAP_FREE( domain.bv_val ); return -1; } int ldap_domain2dn( LDAP_CONST char *domain_in, char **dnp) { char *domain, *s, *tok_r, *dn, *dntmp; size_t loc; assert( domain_in != NULL ); assert( dnp != NULL ); domain = LDAP_STRDUP(domain_in); if (domain == NULL) { return LDAP_NO_MEMORY; } dn = NULL; loc = 0; for (s = ldap_pvt_strtok(domain, ".", &tok_r); s != NULL; s = ldap_pvt_strtok(NULL, ".", &tok_r)) { size_t len = strlen(s); dntmp = (char *) LDAP_REALLOC(dn, loc + sizeof(",dc=") + len ); if (dntmp == NULL) { if (dn != NULL) LDAP_FREE(dn); LDAP_FREE(domain); return LDAP_NO_MEMORY; } dn = dntmp; if (loc > 0) { /* not first time. */ strcpy(dn + loc, ","); loc++; } strcpy(dn + loc, "dc="); loc += sizeof("dc=")-1; strcpy(dn + loc, s); loc += len; } LDAP_FREE(domain); *dnp = dn; return LDAP_SUCCESS; } #ifdef HAVE_RES_QUERY #define DNSBUFSIZ (64*1024) #define MAXHOST 254 /* RFC 1034, max length is 253 chars */ typedef struct srv_record { u_short priority; u_short weight; u_short port; char hostname[MAXHOST]; } srv_record; /* Linear Congruential Generator - we don't need * high quality randomness, and we don't want to * interfere with anyone else's use of srand(). * * The PRNG here cycles thru 941,955 numbers. */ static float srv_seed; static void srv_srand(int seed) { srv_seed = (float)seed / (float)RAND_MAX; } static float srv_rand() { float val = 9821.0 * srv_seed + .211327; srv_seed = val - (int)val; return srv_seed; } static int srv_cmp(const void *aa, const void *bb){ srv_record *a=(srv_record *)aa; srv_record *b=(srv_record *)bb; int i = a->priority - b->priority; if (i) return i; return b->weight - a->weight; } static void srv_shuffle(srv_record *a, int n) { int i, j, total = 0, r, p; for (i=0; i1; a++, p--) { if (!total) { /* all remaining weights are zero, do a straight Fisher-Yates shuffle */ j = srv_rand() * p; } else { r = srv_rand() * total; for (j=0; j= 0) { unsigned char *p; char host[DNSBUFSIZ]; int status; u_short port, priority, weight; /* Parse out query */ p = reply; #ifdef NS_HFIXEDSZ /* Bind 8/9 interface */ p += NS_HFIXEDSZ; #elif defined(HFIXEDSZ) /* Bind 4 interface w/ HFIXEDSZ */ p += HFIXEDSZ; #else /* Bind 4 interface w/o HFIXEDSZ */ p += sizeof(HEADER); #endif status = dn_expand(reply, reply + len, p, host, sizeof(host)); if (status < 0) { goto out; } p += status; p += 4; while (p < reply + len) { int type, class, ttl, size; status = dn_expand(reply, reply + len, p, host, sizeof(host)); if (status < 0) { goto out; } p += status; type = (p[0] << 8) | p[1]; p += 2; class = (p[0] << 8) | p[1]; p += 2; ttl = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; p += 4; size = (p[0] << 8) | p[1]; p += 2; if (type == T_SRV) { status = dn_expand(reply, reply + len, p + 6, host, sizeof(host)); if (status < 0) { goto out; } /* Get priority weight and port */ priority = (p[0] << 8) | p[1]; weight = (p[2] << 8) | p[3]; port = (p[4] << 8) | p[5]; if ( port == 0 || host[ 0 ] == '\0' ) { goto add_size; } hostent_head = (srv_record *) LDAP_REALLOC(hostent_head, (hostent_count+1)*(sizeof(srv_record))); if(hostent_head==NULL){ rc=LDAP_NO_MEMORY; goto out; } hostent_head[hostent_count].priority=priority; hostent_head[hostent_count].weight=weight; hostent_head[hostent_count].port=port; strncpy(hostent_head[hostent_count].hostname, host, MAXHOST-1); hostent_head[hostent_count].hostname[MAXHOST-1] = '\0'; hostent_count++; } add_size:; p += size; } if (!hostent_head) goto out; qsort(hostent_head, hostent_count, sizeof(srv_record), srv_cmp); if (!srv_seed) srv_srand(time(0L)); /* shuffle records of same priority */ j = 0; priority = hostent_head[0].priority; for (i=1; i 1) srv_shuffle(hostent_head+j, i-j); j = i; } } if (i-j > 1) srv_shuffle(hostent_head+j, i-j); for(i=0; i0){ hostlist[cur++]=' '; } cur += sprintf(&hostlist[cur], "%s:%hu", hostent_head[i].hostname, hostent_head[i].port); } } if (hostlist == NULL) { /* No LDAP servers found in DNS. */ rc = LDAP_UNAVAILABLE; goto out; } rc = LDAP_SUCCESS; *list = hostlist; out: LDAP_MUTEX_UNLOCK(&ldap_int_resolv_mutex); if (request != NULL) { LDAP_FREE(request); } if (hostent_head != NULL) { LDAP_FREE(hostent_head); } if (rc != LDAP_SUCCESS && hostlist != NULL) { LDAP_FREE(hostlist); } return rc; #else return LDAP_NOT_SUPPORTED; #endif /* HAVE_RES_QUERY */ }