dnssrv.c 7.97 KB
Newer Older
Luke Howard's avatar
 
Luke Howard committed
1
/* $OpenLDAP$ */
2
3
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
 *
Kurt Zeilenga's avatar
Kurt Zeilenga committed
4
 * Copyright 1998-2014 The OpenLDAP Foundation.
5
6
7
8
9
10
11
12
13
 * 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>.
Luke Howard's avatar
 
Luke Howard committed
14
15
16
 */

/*
17
18
 * locate LDAP servers using DNS SRV records.
 * Location code based on MIT Kerberos KDC location code.
Luke Howard's avatar
 
Luke Howard committed
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
 */
#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

40
41
42
43
int ldap_dn2domain(
	LDAP_CONST char *dn_in,
	char **domainp)
{
44
45
	int i, j;
	char *ndomain;
Howard Chu's avatar
Howard Chu committed
46
47
	LDAPDN dn = NULL;
	LDAPRDN rdn = NULL;
48
	LDAPAVA *ava = NULL;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
49
	struct berval domain = BER_BVNULL;
50
51
	static const struct berval DC = BER_BVC("DC");
	static const struct berval DCOID = BER_BVC("0.9.2342.19200300.100.1.25");
52

Kurt Zeilenga's avatar
Kurt Zeilenga committed
53
54
	assert( dn_in != NULL );
	assert( domainp != NULL );
55

Howard Chu's avatar
Howard Chu committed
56
57
	*domainp = NULL;

58
	if ( ldap_str2dn( dn_in, &dn, LDAP_DN_FORMAT_LDAP ) != LDAP_SUCCESS ) {
59
60
61
		return -2;
	}

Howard Chu's avatar
Howard Chu committed
62
63
	if( dn ) for( i=0; dn[i] != NULL; i++ ) {
		rdn = dn[i];
64

Howard Chu's avatar
Howard Chu committed
65
66
		for( j=0; rdn[j] != NULL; j++ ) {
			ava = rdn[j];
Kurt Zeilenga's avatar
Kurt Zeilenga committed
67

Howard Chu's avatar
Howard Chu committed
68
69
70
			if( rdn[j+1] == NULL &&
				(ava->la_flags & LDAP_AVA_STRING) &&
				ava->la_value.bv_len &&
71
				( ber_bvstrcasecmp( &ava->la_attr, &DC ) == 0
Pierangelo Masarati's avatar
Pierangelo Masarati committed
72
				|| ber_bvcmp( &ava->la_attr, &DCOID ) == 0 ) )
73
			{
74
75
76
				if( domain.bv_len == 0 ) {
					ndomain = LDAP_REALLOC( domain.bv_val,
						ava->la_value.bv_len + 1);
77

78
79
80
					if( ndomain == NULL ) {
						goto return_error;
					}
81

82
					domain.bv_val = ndomain;
83

84
85
					AC_MEMCPY( domain.bv_val, ava->la_value.bv_val,
						ava->la_value.bv_len );
86

87
88
					domain.bv_len = ava->la_value.bv_len;
					domain.bv_val[domain.bv_len] = '\0';
89

90
				} else {
91
92
93
94
95
96
97
98
99
100
101
102
103
					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';
104
				}
105
106
			} else {
				domain.bv_len = 0;
107
			}
108
109
		} 
	}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
110

111

112
113
114
	if( domain.bv_len == 0 && domain.bv_val != NULL ) {
		LDAP_FREE( domain.bv_val );
		domain.bv_val = NULL;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
115
116
	}

117
118
	ldap_dnfree( dn );
	*domainp = domain.bv_val;
119
	return 0;
120
121
122
123
124

return_error:
	ldap_dnfree( dn );
	LDAP_FREE( domain.bv_val );
	return -1;
125
126
127
128
129
}

int ldap_domain2dn(
	LDAP_CONST char *domain_in,
	char **dnp)
Luke Howard's avatar
 
Luke Howard committed
130
{
Kurt Zeilenga's avatar
Kurt Zeilenga committed
131
132
	char *domain, *s, *tok_r, *dn, *dntmp;
	size_t loc;
Luke Howard's avatar
 
Luke Howard committed
133

Kurt Zeilenga's avatar
Kurt Zeilenga committed
134
135
136
	assert( domain_in != NULL );
	assert( dnp != NULL );

Kurt Zeilenga's avatar
Kurt Zeilenga committed
137
138
	domain = LDAP_STRDUP(domain_in);
	if (domain == NULL) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
139
		return LDAP_NO_MEMORY;
Luke Howard's avatar
 
Luke Howard committed
140
	}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
141
142
143
144
145
146
147
148
149
150
	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 );
Luke Howard's avatar
Luke Howard committed
151
152
		if (dntmp == NULL) {
		    if (dn != NULL)
Kurt Zeilenga's avatar
Kurt Zeilenga committed
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
			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;
Luke Howard's avatar
 
Luke Howard committed
170
171
    }

Kurt Zeilenga's avatar
Kurt Zeilenga committed
172
173
174
	LDAP_FREE(domain);
	*dnp = dn;
	return LDAP_SUCCESS;
Luke Howard's avatar
 
Luke Howard committed
175
176
}

177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
#ifdef HAVE_RES_QUERY
#define DNSBUFSIZ (64*1024)
typedef struct srv_record {
    u_short priority;
    u_short weight;
    u_short port;
    char hostname[DNSBUFSIZ];
} srv_record;


static int srv_cmp(const void *aa, const void *bb){
    srv_record *a=(srv_record *)aa;
    srv_record *b=(srv_record *)bb;
    u_long total;

    if(a->priority < b->priority) {
	return -1;
    }
    if(a->priority > b->priority) {
	return 1;
    }
    if(a->priority == b->priority){
	/* targets with same priority are in psudeo random order */
	if (a->weight == 0 && b->weight == 0) {
	    if (rand() % 2) {
		return -1;
	    } else {
		return 1;
	    }
	}
	total = a->weight + b->weight;
	if (rand() % total < a->weight) {
	    return -1;
	} else {
	    return 1;
	}
    }
}
#endif /* HAVE_RES_QUERY */

Luke Howard's avatar
 
Luke Howard committed
217
/*
218
219
 * Lookup and return LDAP servers for domain (using the DNS
 * SRV record _ldap._tcp.domain).
Luke Howard's avatar
 
Luke Howard committed
220
 */
221
222
223
int ldap_domain2hostlist(
	LDAP_CONST char *domain,
	char **list )
Luke Howard's avatar
 
Luke Howard committed
224
{
225
#ifdef HAVE_RES_QUERY
Luke Howard's avatar
 
Luke Howard committed
226
227
    char *request;
    char *hostlist = NULL;
228
229
    srv_record *hostent_head=NULL;
    int i;
Luke Howard's avatar
 
Luke Howard committed
230
    int rc, len, cur = 0;
231
    unsigned char reply[DNSBUFSIZ];
232
    int hostent_count=0;
Luke Howard's avatar
 
Luke Howard committed
233

234
235
236
	assert( domain != NULL );
	assert( list != NULL );
	if( *domain == '\0' ) {
237
238
239
		return LDAP_PARAM_ERROR;
	}

Luke Howard's avatar
 
Luke Howard committed
240
241
    request = LDAP_MALLOC(strlen(domain) + sizeof("_ldap._tcp."));
    if (request == NULL) {
242
		return LDAP_NO_MEMORY;
Luke Howard's avatar
 
Luke Howard committed
243
244
245
    }
    sprintf(request, "_ldap._tcp.%s", domain);

Howard Chu's avatar
Howard Chu committed
246
    LDAP_MUTEX_LOCK(&ldap_int_resolv_mutex);
Luke Howard's avatar
 
Luke Howard committed
247

Kurt Zeilenga's avatar
Kurt Zeilenga committed
248
    rc = LDAP_UNAVAILABLE;
249
250
#ifdef NS_HFIXEDSZ
	/* Bind 8/9 interface */
251
    len = res_query(request, ns_c_in, ns_t_srv, reply, sizeof(reply));
Kurt Zeilenga's avatar
Kurt Zeilenga committed
252
253
254
#	ifndef T_SRV
#		define T_SRV ns_t_srv
#	endif
255
#else
Kurt Zeilenga's avatar
Kurt Zeilenga committed
256
257
258
259
260
	/* Bind 4 interface */
#	ifndef T_SRV
#		define T_SRV 33
#	endif

261
262
    len = res_query(request, C_IN, T_SRV, reply, sizeof(reply));
#endif
Luke Howard's avatar
 
Luke Howard committed
263
264
    if (len >= 0) {
	unsigned char *p;
265
	char host[DNSBUFSIZ];
Luke Howard's avatar
 
Luke Howard committed
266
	int status;
267
	u_short port, priority, weight;
Luke Howard's avatar
 
Luke Howard committed
268
269
270

	/* Parse out query */
	p = reply;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
271

272
273
#ifdef NS_HFIXEDSZ
	/* Bind 8/9 interface */
Kurt Zeilenga's avatar
Kurt Zeilenga committed
274
	p += NS_HFIXEDSZ;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
275
276
#elif defined(HFIXEDSZ)
	/* Bind 4 interface w/ HFIXEDSZ */
277
	p += HFIXEDSZ;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
278
279
280
#else
	/* Bind 4 interface w/o HFIXEDSZ */
	p += sizeof(HEADER);
281
#endif
Kurt Zeilenga's avatar
Kurt Zeilenga committed
282

Luke Howard's avatar
 
Luke Howard committed
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
	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;
		}
310
311
312
313

		/* Get priority weight and port */
		priority = (p[0] << 8) | p[1];
		weight = (p[2] << 8) | p[3];
Luke Howard's avatar
 
Luke Howard committed
314
315
		port = (p[4] << 8) | p[5];

316
317
318
319
		if ( port == 0 || host[ 0 ] == '\0' ) {
		    goto add_size;
		}

320
321
322
		hostent_head = (srv_record *) LDAP_REALLOC(hostent_head, (hostent_count+1)*(sizeof(srv_record)));
		if(hostent_head==NULL){
		    rc=LDAP_NO_MEMORY;
Luke Howard's avatar
 
Luke Howard committed
323
324
		    goto out;
		}
325
326
327
328
329
		hostent_head[hostent_count].priority=priority;
		hostent_head[hostent_count].weight=priority;
		hostent_head[hostent_count].port=port;
		strncpy(hostent_head[hostent_count].hostname, host,255);
		hostent_count=hostent_count+1;
Luke Howard's avatar
 
Luke Howard committed
330
	    }
331
add_size:;
Luke Howard's avatar
 
Luke Howard committed
332
333
334
	    p += size;
	}
    }
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
    qsort(hostent_head, hostent_count, sizeof(srv_record), srv_cmp);

    for(i=0; i<hostent_count; i++){
	int buflen;
        buflen = strlen(hostent_head[i].hostname) + STRLENOF(":65355" );
        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:%hd", hostent_head[i].hostname, hostent_head[i].port);
    }

Luke Howard's avatar
 
Luke Howard committed
351
352
353
354
355
356
357
    if (hostlist == NULL) {
	/* No LDAP servers found in DNS. */
	rc = LDAP_UNAVAILABLE;
	goto out;
    }

    rc = LDAP_SUCCESS;
358
	*list = hostlist;
Luke Howard's avatar
 
Luke Howard committed
359
360

  out:
Howard Chu's avatar
Howard Chu committed
361
    LDAP_MUTEX_UNLOCK(&ldap_int_resolv_mutex);
Luke Howard's avatar
 
Luke Howard committed
362
363
364
365

    if (request != NULL) {
	LDAP_FREE(request);
    }
366
367
368
    if (hostent_head != NULL) {
	LDAP_FREE(hostent_head);
    }
369
    if (rc != LDAP_SUCCESS && hostlist != NULL) {
Luke Howard's avatar
 
Luke Howard committed
370
371
372
373
374
	LDAP_FREE(hostlist);
    }
    return rc;
#else
    return LDAP_NOT_SUPPORTED;
375
#endif /* HAVE_RES_QUERY */
Luke Howard's avatar
 
Luke Howard committed
376
}