2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4 * Copyright 1998-2016 The OpenLDAP Foundation.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted only as authorized by the OpenLDAP
11 * A copy of this license is available in the file LICENSE in the
12 * top-level directory of the distribution or, alternatively, at
13 * <http://www.OpenLDAP.org/license.html>.
17 * locate LDAP servers using DNS SRV records.
18 * Location code based on MIT Kerberos KDC location code.
24 #include <ac/stdlib.h>
27 #include <ac/socket.h>
28 #include <ac/string.h>
33 #ifdef HAVE_ARPA_NAMESER_H
34 #include <arpa/nameser.h>
41 LDAP_CONST char *dn_in,
49 struct berval domain = BER_BVNULL;
50 static const struct berval DC = BER_BVC("DC");
51 static const struct berval DCOID = BER_BVC("0.9.2342.19200300.100.1.25");
53 assert( dn_in != NULL );
54 assert( domainp != NULL );
58 if ( ldap_str2dn( dn_in, &dn, LDAP_DN_FORMAT_LDAP ) != LDAP_SUCCESS ) {
62 if( dn ) for( i=0; dn[i] != NULL; i++ ) {
65 for( j=0; rdn[j] != NULL; j++ ) {
68 if( rdn[j+1] == NULL &&
69 (ava->la_flags & LDAP_AVA_STRING) &&
70 ava->la_value.bv_len &&
71 ( ber_bvstrcasecmp( &ava->la_attr, &DC ) == 0
72 || ber_bvcmp( &ava->la_attr, &DCOID ) == 0 ) )
74 if( domain.bv_len == 0 ) {
75 ndomain = LDAP_REALLOC( domain.bv_val,
76 ava->la_value.bv_len + 1);
78 if( ndomain == NULL ) {
82 domain.bv_val = ndomain;
84 AC_MEMCPY( domain.bv_val, ava->la_value.bv_val,
85 ava->la_value.bv_len );
87 domain.bv_len = ava->la_value.bv_len;
88 domain.bv_val[domain.bv_len] = '\0';
91 ndomain = LDAP_REALLOC( domain.bv_val,
92 ava->la_value.bv_len + sizeof(".") + domain.bv_len );
94 if( ndomain == NULL ) {
98 domain.bv_val = ndomain;
99 domain.bv_val[domain.bv_len++] = '.';
100 AC_MEMCPY( &domain.bv_val[domain.bv_len],
101 ava->la_value.bv_val, ava->la_value.bv_len );
102 domain.bv_len += ava->la_value.bv_len;
103 domain.bv_val[domain.bv_len] = '\0';
112 if( domain.bv_len == 0 && domain.bv_val != NULL ) {
113 LDAP_FREE( domain.bv_val );
114 domain.bv_val = NULL;
118 *domainp = domain.bv_val;
123 LDAP_FREE( domain.bv_val );
128 LDAP_CONST char *domain_in,
131 char *domain, *s, *tok_r, *dn, *dntmp;
134 assert( domain_in != NULL );
135 assert( dnp != NULL );
137 domain = LDAP_STRDUP(domain_in);
138 if (domain == NULL) {
139 return LDAP_NO_MEMORY;
144 for (s = ldap_pvt_strtok(domain, ".", &tok_r);
146 s = ldap_pvt_strtok(NULL, ".", &tok_r))
148 size_t len = strlen(s);
150 dntmp = (char *) LDAP_REALLOC(dn, loc + sizeof(",dc=") + len );
155 return LDAP_NO_MEMORY;
161 /* not first time. */
162 strcpy(dn + loc, ",");
165 strcpy(dn + loc, "dc=");
166 loc += sizeof("dc=")-1;
177 #ifdef HAVE_RES_QUERY
178 #define DNSBUFSIZ (64*1024)
179 #define MAXHOST 254 /* RFC 1034, max length is 253 chars */
180 typedef struct srv_record {
184 char hostname[MAXHOST];
187 /* Linear Congruential Generator - we don't need
188 * high quality randomness, and we don't want to
189 * interfere with anyone else's use of srand().
191 * The PRNG here cycles thru 941,955 numbers.
193 static float srv_seed;
195 static void srv_srand(int seed) {
196 srv_seed = (float)seed / (float)RAND_MAX;
199 static float srv_rand() {
200 float val = 9821.0 * srv_seed + .211327;
201 srv_seed = val - (int)val;
205 static int srv_cmp(const void *aa, const void *bb){
206 srv_record *a=(srv_record *)aa;
207 srv_record *b=(srv_record *)bb;
208 int i = a->priority - b->priority;
210 return b->weight - a->weight;
213 static void srv_shuffle(srv_record *a, int n) {
214 int i, j, total = 0, r, p;
217 total += a[i].weight;
219 /* all weights are zero, do a straight Fisher-Yates shuffle */
223 i = srv_rand() * n--;
231 /* Do a shuffle per RFC2782 Page 4 */
233 for (i=0; i<n-1; i++) {
234 r = srv_rand() * total;
235 for (j=0; j<p; j++) {
243 total -= a[0].weight;
251 #endif /* HAVE_RES_QUERY */
254 * Lookup and return LDAP servers for domain (using the DNS
255 * SRV record _ldap._tcp.domain).
257 int ldap_domain2hostlist(
258 LDAP_CONST char *domain,
261 #ifdef HAVE_RES_QUERY
263 char *hostlist = NULL;
264 srv_record *hostent_head=NULL;
266 int rc, len, cur = 0;
267 unsigned char reply[DNSBUFSIZ];
270 assert( domain != NULL );
271 assert( list != NULL );
272 if( *domain == '\0' ) {
273 return LDAP_PARAM_ERROR;
276 request = LDAP_MALLOC(strlen(domain) + sizeof("_ldap._tcp."));
277 if (request == NULL) {
278 return LDAP_NO_MEMORY;
280 sprintf(request, "_ldap._tcp.%s", domain);
282 LDAP_MUTEX_LOCK(&ldap_int_resolv_mutex);
284 rc = LDAP_UNAVAILABLE;
286 /* Bind 8/9 interface */
287 len = res_query(request, ns_c_in, ns_t_srv, reply, sizeof(reply));
289 # define T_SRV ns_t_srv
292 /* Bind 4 interface */
297 len = res_query(request, C_IN, T_SRV, reply, sizeof(reply));
301 char host[DNSBUFSIZ];
303 u_short port, priority, weight;
305 /* Parse out query */
309 /* Bind 8/9 interface */
311 #elif defined(HFIXEDSZ)
312 /* Bind 4 interface w/ HFIXEDSZ */
315 /* Bind 4 interface w/o HFIXEDSZ */
319 status = dn_expand(reply, reply + len, p, host, sizeof(host));
326 while (p < reply + len) {
327 int type, class, ttl, size;
328 status = dn_expand(reply, reply + len, p, host, sizeof(host));
333 type = (p[0] << 8) | p[1];
335 class = (p[0] << 8) | p[1];
337 ttl = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
339 size = (p[0] << 8) | p[1];
342 status = dn_expand(reply, reply + len, p + 6, host, sizeof(host));
347 /* Get priority weight and port */
348 priority = (p[0] << 8) | p[1];
349 weight = (p[2] << 8) | p[3];
350 port = (p[4] << 8) | p[5];
352 if ( port == 0 || host[ 0 ] == '\0' ) {
356 hostent_head = (srv_record *) LDAP_REALLOC(hostent_head, (hostent_count+1)*(sizeof(srv_record)));
357 if(hostent_head==NULL){
361 hostent_head[hostent_count].priority=priority;
362 hostent_head[hostent_count].weight=weight;
363 hostent_head[hostent_count].port=port;
364 strncpy(hostent_head[hostent_count].hostname, host, MAXHOST-1);
365 hostent_head[hostent_count].hostname[MAXHOST-1] = '\0';
371 if (!hostent_head) goto out;
372 qsort(hostent_head, hostent_count, sizeof(srv_record), srv_cmp);
377 /* shuffle records of same priority */
379 priority = hostent_head[0].priority;
380 for (i=1; i<hostent_count; i++) {
381 if (hostent_head[i].priority != priority) {
382 priority = hostent_head[i].priority;
384 srv_shuffle(hostent_head+j, i-j);
389 srv_shuffle(hostent_head+j, i-j);
391 for(i=0; i<hostent_count; i++){
393 buflen = strlen(hostent_head[i].hostname) + STRLENOF(":65535 ");
394 hostlist = (char *) LDAP_REALLOC(hostlist, cur+buflen+1);
395 if (hostlist == NULL) {
402 cur += sprintf(&hostlist[cur], "%s:%hu", hostent_head[i].hostname, hostent_head[i].port);
406 if (hostlist == NULL) {
407 /* No LDAP servers found in DNS. */
408 rc = LDAP_UNAVAILABLE;
416 LDAP_MUTEX_UNLOCK(&ldap_int_resolv_mutex);
418 if (request != NULL) {
421 if (hostent_head != NULL) {
422 LDAP_FREE(hostent_head);
424 if (rc != LDAP_SUCCESS && hostlist != NULL) {
429 return LDAP_NOT_SUPPORTED;
430 #endif /* HAVE_RES_QUERY */