123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422 |
- /* $OpenLDAP$ */
- /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
- *
- * 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
- * <http://www.OpenLDAP.org/license.html>.
- */
- /*
- * locate LDAP servers using DNS SRV records.
- * Location code based on MIT Kerberos KDC location code.
- */
- #include "portable.h"
- #include <stdio.h>
- #include <ac/stdlib.h>
- #include <ac/param.h>
- #include <ac/socket.h>
- #include <ac/string.h>
- #include <ac/time.h>
- #include "ldap-int.h"
- #ifdef HAVE_ARPA_NAMESER_H
- #include <arpa/nameser.h>
- #endif
- #ifdef HAVE_RESOLV_H
- #include <resolv.h>
- #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; i<n; i++)
- total += a[i].weight;
- /* Do a shuffle per RFC2782 Page 4 */
- for (p=n; p>1; 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<p; j++) {
- r -= a[j].weight;
- if (r < 0) {
- total -= a[j].weight;
- break;
- }
- }
- }
- if (j && j<p) {
- srv_record t = a[0];
- a[0] = a[j];
- a[j] = t;
- }
- }
- }
- #endif /* HAVE_RES_QUERY */
- /*
- * Lookup and return LDAP servers for domain (using the DNS
- * SRV record _ldap._tcp.domain).
- */
- int ldap_domain2hostlist(
- LDAP_CONST char *domain,
- char **list )
- {
- #ifdef HAVE_RES_QUERY
- char *request;
- char *hostlist = NULL;
- srv_record *hostent_head=NULL;
- int i, j;
- int rc, len, cur = 0;
- unsigned char reply[DNSBUFSIZ];
- int hostent_count=0;
- assert( domain != NULL );
- assert( list != NULL );
- if( *domain == '\0' ) {
- return LDAP_PARAM_ERROR;
- }
- request = LDAP_MALLOC(strlen(domain) + sizeof("_ldap._tcp."));
- if (request == NULL) {
- return LDAP_NO_MEMORY;
- }
- sprintf(request, "_ldap._tcp.%s", domain);
- LDAP_MUTEX_LOCK(&ldap_int_resolv_mutex);
- rc = LDAP_UNAVAILABLE;
- #ifdef NS_HFIXEDSZ
- /* Bind 8/9 interface */
- len = res_query(request, ns_c_in, ns_t_srv, reply, sizeof(reply));
- # ifndef T_SRV
- # define T_SRV ns_t_srv
- # endif
- #else
- /* Bind 4 interface */
- # ifndef T_SRV
- # define T_SRV 33
- # endif
- len = res_query(request, C_IN, T_SRV, reply, sizeof(reply));
- #endif
- if (len >= 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<hostent_count; i++) {
- if (hostent_head[i].priority != priority) {
- priority = hostent_head[i].priority;
- if (i-j > 1)
- srv_shuffle(hostent_head+j, i-j);
- j = i;
- }
- }
- if (i-j > 1)
- srv_shuffle(hostent_head+j, i-j);
- for(i=0; i<hostent_count; i++){
- int buflen;
- buflen = strlen(hostent_head[i].hostname) + STRLENOF(":65535 ");
- hostlist = (char *) LDAP_REALLOC(hostlist, cur+buflen+1);
- if (hostlist == NULL) {
- rc = LDAP_NO_MEMORY;
- goto out;
- }
- if(cur>0){
- 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 */
- }
|