]> git.sur5r.net Git - openldap/blob - libraries/libldap/dnssrv.c
Happy New Year
[openldap] / libraries / libldap / dnssrv.c
1 /* $OpenLDAP$ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 1998-2018 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
40 int 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
121 return_error:
122         ldap_dnfree( dn );
123         LDAP_FREE( domain.bv_val );
124         return -1;
125 }
126
127 int 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 #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 {
181     u_short priority;
182     u_short weight;
183     u_short port;
184     char hostname[MAXHOST];
185 } srv_record;
186
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().
190  *
191  * The PRNG here cycles thru 941,955 numbers.
192  */
193 static float srv_seed;
194
195 static void srv_srand(int seed) {
196         srv_seed = (float)seed / (float)RAND_MAX;
197 }
198
199 static float srv_rand() {
200         float val = 9821.0 * srv_seed + .211327;
201         srv_seed = val - (int)val;
202         return srv_seed;
203 }
204
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;
209         if (i) return i;
210         return b->weight - a->weight;
211 }
212
213 static void srv_shuffle(srv_record *a, int n) {
214         int i, j, total = 0, r, p;
215
216         for (i=0; i<n; i++)
217                 total += a[i].weight;
218
219         /* all weights are zero, do a straight Fisher-Yates shuffle */
220         if (!total) {
221                 while (n) {
222                         srv_record t;
223                         i = srv_rand() * n--;
224                         t = a[n];
225                         a[n] = a[i];
226                         a[i] = t;
227                 }
228                 return;
229         }
230
231         /* Do a shuffle per RFC2782 Page 4 */
232         p = n;
233         for (i=0; i<n-1; i++) {
234                 r = srv_rand() * total;
235                 for (j=0; j<p; j++) {
236                         r -= a[j].weight;
237                         if (r <= 0) {
238                                 if (j) {
239                                         srv_record t = a[0];
240                                         a[0] = a[j];
241                                         a[j] = t;
242                                 }
243                                 total -= a[0].weight;
244                                 a++;
245                                 p--;
246                                 break;
247                         }
248                 }
249         }
250 }
251 #endif /* HAVE_RES_QUERY */
252
253 /*
254  * Lookup and return LDAP servers for domain (using the DNS
255  * SRV record _ldap._tcp.domain).
256  */
257 int ldap_domain2hostlist(
258         LDAP_CONST char *domain,
259         char **list )
260 {
261 #ifdef HAVE_RES_QUERY
262     char *request;
263     char *hostlist = NULL;
264     srv_record *hostent_head=NULL;
265     int i, j;
266     int rc, len, cur = 0;
267     unsigned char reply[DNSBUFSIZ];
268     int hostent_count=0;
269
270         assert( domain != NULL );
271         assert( list != NULL );
272         if( *domain == '\0' ) {
273                 return LDAP_PARAM_ERROR;
274         }
275
276     request = LDAP_MALLOC(strlen(domain) + sizeof("_ldap._tcp."));
277     if (request == NULL) {
278                 return LDAP_NO_MEMORY;
279     }
280     sprintf(request, "_ldap._tcp.%s", domain);
281
282     LDAP_MUTEX_LOCK(&ldap_int_resolv_mutex);
283
284     rc = LDAP_UNAVAILABLE;
285 #ifdef NS_HFIXEDSZ
286         /* Bind 8/9 interface */
287     len = res_query(request, ns_c_in, ns_t_srv, reply, sizeof(reply));
288 #       ifndef T_SRV
289 #               define T_SRV ns_t_srv
290 #       endif
291 #else
292         /* Bind 4 interface */
293 #       ifndef T_SRV
294 #               define T_SRV 33
295 #       endif
296
297     len = res_query(request, C_IN, T_SRV, reply, sizeof(reply));
298 #endif
299     if (len >= 0) {
300         unsigned char *p;
301         char host[DNSBUFSIZ];
302         int status;
303         u_short port, priority, weight;
304
305         /* Parse out query */
306         p = reply;
307
308 #ifdef NS_HFIXEDSZ
309         /* Bind 8/9 interface */
310         p += NS_HFIXEDSZ;
311 #elif defined(HFIXEDSZ)
312         /* Bind 4 interface w/ HFIXEDSZ */
313         p += HFIXEDSZ;
314 #else
315         /* Bind 4 interface w/o HFIXEDSZ */
316         p += sizeof(HEADER);
317 #endif
318
319         status = dn_expand(reply, reply + len, p, host, sizeof(host));
320         if (status < 0) {
321             goto out;
322         }
323         p += status;
324         p += 4;
325
326         while (p < reply + len) {
327             int type, class, ttl, size;
328             status = dn_expand(reply, reply + len, p, host, sizeof(host));
329             if (status < 0) {
330                 goto out;
331             }
332             p += status;
333             type = (p[0] << 8) | p[1];
334             p += 2;
335             class = (p[0] << 8) | p[1];
336             p += 2;
337             ttl = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
338             p += 4;
339             size = (p[0] << 8) | p[1];
340             p += 2;
341             if (type == T_SRV) {
342                 status = dn_expand(reply, reply + len, p + 6, host, sizeof(host));
343                 if (status < 0) {
344                     goto out;
345                 }
346
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];
351
352                 if ( port == 0 || host[ 0 ] == '\0' ) {
353                     goto add_size;
354                 }
355
356                 hostent_head = (srv_record *) LDAP_REALLOC(hostent_head, (hostent_count+1)*(sizeof(srv_record)));
357                 if(hostent_head==NULL){
358                     rc=LDAP_NO_MEMORY;
359                     goto out;
360                 }
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';
366                 hostent_count++;
367             }
368 add_size:;
369             p += size;
370         }
371         if (!hostent_head) goto out;
372     qsort(hostent_head, hostent_count, sizeof(srv_record), srv_cmp);
373
374         if (!srv_seed)
375                 srv_srand(time(0L));
376
377         /* shuffle records of same priority */
378         j = 0;
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;
383                         if (i-j > 1)
384                                 srv_shuffle(hostent_head+j, i-j);
385                         j = i;
386                 }
387         }
388         if (i-j > 1)
389                 srv_shuffle(hostent_head+j, i-j);
390
391     for(i=0; i<hostent_count; i++){
392         int buflen;
393         buflen = strlen(hostent_head[i].hostname) + STRLENOF(":65535 ");
394         hostlist = (char *) LDAP_REALLOC(hostlist, cur+buflen+1);
395         if (hostlist == NULL) {
396             rc = LDAP_NO_MEMORY;
397             goto out;
398         }
399         if(cur>0){
400             hostlist[cur++]=' ';
401         }
402         cur += sprintf(&hostlist[cur], "%s:%hu", hostent_head[i].hostname, hostent_head[i].port);
403     }
404     }
405
406     if (hostlist == NULL) {
407         /* No LDAP servers found in DNS. */
408         rc = LDAP_UNAVAILABLE;
409         goto out;
410     }
411
412     rc = LDAP_SUCCESS;
413         *list = hostlist;
414
415   out:
416     LDAP_MUTEX_UNLOCK(&ldap_int_resolv_mutex);
417
418     if (request != NULL) {
419         LDAP_FREE(request);
420     }
421     if (hostent_head != NULL) {
422         LDAP_FREE(hostent_head);
423     }
424     if (rc != LDAP_SUCCESS && hostlist != NULL) {
425         LDAP_FREE(hostlist);
426     }
427     return rc;
428 #else
429     return LDAP_NOT_SUPPORTED;
430 #endif /* HAVE_RES_QUERY */
431 }