1/* $OpenLDAP$ */
2/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3 *
4 * Copyright 1998-2011 The OpenLDAP Foundation.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted only as authorized by the OpenLDAP
9 * Public License.
10 *
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>.
14 */
15
16/*
17 * locate LDAP servers using DNS SRV records.
18 * Location code based on MIT Kerberos KDC location code.
19 */
20#include "portable.h"
21
22#include <stdio.h>
23
24#include <ac/stdlib.h>
25
26#include <ac/param.h>
27#include <ac/socket.h>
28#include <ac/string.h>
29#include <ac/time.h>
30
31#include "ldap-int.h"
32
33#ifdef HAVE_ARPA_NAMESER_H
34#include <arpa/nameser.h>
35#endif
36#ifdef HAVE_RESOLV_H
37#include <resolv.h>
38#endif
39
40int ldap_dn2domain(
41	LDAP_CONST char *dn_in,
42	char **domainp)
43{
44	int i, j;
45	char *ndomain;
46	LDAPDN dn = NULL;
47	LDAPRDN rdn = NULL;
48	LDAPAVA *ava = NULL;
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");
52
53	assert( dn_in != NULL );
54	assert( domainp != NULL );
55
56	*domainp = NULL;
57
58	if ( ldap_str2dn( dn_in, &dn, LDAP_DN_FORMAT_LDAP ) != LDAP_SUCCESS ) {
59		return -2;
60	}
61
62	if( dn ) for( i=0; dn[i] != NULL; i++ ) {
63		rdn = dn[i];
64
65		for( j=0; rdn[j] != NULL; j++ ) {
66			ava = rdn[j];
67
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 ) )
73			{
74				if( domain.bv_len == 0 ) {
75					ndomain = LDAP_REALLOC( domain.bv_val,
76						ava->la_value.bv_len + 1);
77
78					if( ndomain == NULL ) {
79						goto return_error;
80					}
81
82					domain.bv_val = ndomain;
83
84					AC_MEMCPY( domain.bv_val, ava->la_value.bv_val,
85						ava->la_value.bv_len );
86
87					domain.bv_len = ava->la_value.bv_len;
88					domain.bv_val[domain.bv_len] = '\0';
89
90				} else {
91					ndomain = LDAP_REALLOC( domain.bv_val,
92						ava->la_value.bv_len + sizeof(".") + domain.bv_len );
93
94					if( ndomain == NULL ) {
95						goto return_error;
96					}
97
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';
104				}
105			} else {
106				domain.bv_len = 0;
107			}
108		}
109	}
110
111
112	if( domain.bv_len == 0 && domain.bv_val != NULL ) {
113		LDAP_FREE( domain.bv_val );
114		domain.bv_val = NULL;
115	}
116
117	ldap_dnfree( dn );
118	*domainp = domain.bv_val;
119	return 0;
120
121return_error:
122	ldap_dnfree( dn );
123	LDAP_FREE( domain.bv_val );
124	return -1;
125}
126
127int ldap_domain2dn(
128	LDAP_CONST char *domain_in,
129	char **dnp)
130{
131	char *domain, *s, *tok_r, *dn, *dntmp;
132	size_t loc;
133
134	assert( domain_in != NULL );
135	assert( dnp != NULL );
136
137	domain = LDAP_STRDUP(domain_in);
138	if (domain == NULL) {
139		return LDAP_NO_MEMORY;
140	}
141	dn = NULL;
142	loc = 0;
143
144	for (s = ldap_pvt_strtok(domain, ".", &tok_r);
145		s != NULL;
146		s = ldap_pvt_strtok(NULL, ".", &tok_r))
147	{
148		size_t len = strlen(s);
149
150		dntmp = (char *) LDAP_REALLOC(dn, loc + sizeof(",dc=") + len );
151		if (dntmp == NULL) {
152		    if (dn != NULL)
153			LDAP_FREE(dn);
154		    LDAP_FREE(domain);
155		    return LDAP_NO_MEMORY;
156		}
157
158		dn = dntmp;
159
160		if (loc > 0) {
161		    /* not first time. */
162		    strcpy(dn + loc, ",");
163		    loc++;
164		}
165		strcpy(dn + loc, "dc=");
166		loc += sizeof("dc=")-1;
167
168		strcpy(dn + loc, s);
169		loc += len;
170    }
171
172	LDAP_FREE(domain);
173	*dnp = dn;
174	return LDAP_SUCCESS;
175}
176
177/*
178 * Lookup and return LDAP servers for domain (using the DNS
179 * SRV record _ldap._tcp.domain).
180 */
181int ldap_domain2hostlist(
182	LDAP_CONST char *domain,
183	char **list )
184{
185#ifdef HAVE_RES_QUERY
186#define DNSBUFSIZ (64*1024)
187    char *request;
188    char *hostlist = NULL;
189    int rc, len, cur = 0;
190    unsigned char reply[DNSBUFSIZ];
191
192	assert( domain != NULL );
193	assert( list != NULL );
194
195	if( *domain == '\0' ) {
196		return LDAP_PARAM_ERROR;
197	}
198
199    request = LDAP_MALLOC(strlen(domain) + sizeof("_ldap._tcp."));
200    if (request == NULL) {
201		return LDAP_NO_MEMORY;
202    }
203    sprintf(request, "_ldap._tcp.%s", domain);
204
205    LDAP_MUTEX_LOCK(&ldap_int_resolv_mutex);
206
207    rc = LDAP_UNAVAILABLE;
208#ifdef NS_HFIXEDSZ
209	/* Bind 8/9 interface */
210    len = res_query(request, ns_c_in, ns_t_srv, reply, sizeof(reply));
211#	ifndef T_SRV
212#		define T_SRV ns_t_srv
213#	endif
214#else
215	/* Bind 4 interface */
216#	ifndef T_SRV
217#		define T_SRV 33
218#	endif
219
220    len = res_query(request, C_IN, T_SRV, reply, sizeof(reply));
221#endif
222    if (len >= 0) {
223	unsigned char *p;
224	char host[DNSBUFSIZ];
225	int status;
226	u_short port;
227	/* int priority, weight; */
228
229	/* Parse out query */
230	p = reply;
231
232#ifdef NS_HFIXEDSZ
233	/* Bind 8/9 interface */
234	p += NS_HFIXEDSZ;
235#elif defined(HFIXEDSZ)
236	/* Bind 4 interface w/ HFIXEDSZ */
237	p += HFIXEDSZ;
238#else
239	/* Bind 4 interface w/o HFIXEDSZ */
240	p += sizeof(HEADER);
241#endif
242
243	status = dn_expand(reply, reply + len, p, host, sizeof(host));
244	if (status < 0) {
245	    goto out;
246	}
247	p += status;
248	p += 4;
249
250	while (p < reply + len) {
251	    int type, class, ttl, size;
252	    status = dn_expand(reply, reply + len, p, host, sizeof(host));
253	    if (status < 0) {
254		goto out;
255	    }
256	    p += status;
257	    type = (p[0] << 8) | p[1];
258	    p += 2;
259	    class = (p[0] << 8) | p[1];
260	    p += 2;
261	    ttl = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
262	    p += 4;
263	    size = (p[0] << 8) | p[1];
264	    p += 2;
265	    if (type == T_SRV) {
266		int buflen;
267		status = dn_expand(reply, reply + len, p + 6, host, sizeof(host));
268		if (status < 0) {
269		    goto out;
270		}
271		/* ignore priority and weight for now */
272		/* priority = (p[0] << 8) | p[1]; */
273		/* weight = (p[2] << 8) | p[3]; */
274		port = (p[4] << 8) | p[5];
275
276		if ( port == 0 || host[ 0 ] == '\0' ) {
277		    goto add_size;
278		}
279
280		buflen = strlen(host) + STRLENOF(":65355 ");
281		hostlist = (char *) LDAP_REALLOC(hostlist, cur + buflen + 1);
282		if (hostlist == NULL) {
283		    rc = LDAP_NO_MEMORY;
284		    goto out;
285		}
286		if (cur > 0) {
287		    /* not first time around */
288		    hostlist[cur++] = ' ';
289		}
290		cur += sprintf(&hostlist[cur], "%s:%hu", host, port);
291	    }
292add_size:;
293	    p += size;
294	}
295    }
296    if (hostlist == NULL) {
297	/* No LDAP servers found in DNS. */
298	rc = LDAP_UNAVAILABLE;
299	goto out;
300    }
301
302    rc = LDAP_SUCCESS;
303	*list = hostlist;
304
305  out:
306    LDAP_MUTEX_UNLOCK(&ldap_int_resolv_mutex);
307
308    if (request != NULL) {
309	LDAP_FREE(request);
310    }
311    if (rc != LDAP_SUCCESS && hostlist != NULL) {
312	LDAP_FREE(hostlist);
313    }
314    return rc;
315#else
316    return LDAP_NOT_SUPPORTED;
317#endif /* HAVE_RES_QUERY */
318}
319