]>
Commit | Line | Data |
---|---|---|
e5fd101c PS |
1 | autofs-5.0.4 - use srv query for domain dn |
2 | ||
3 | From: Ian Kent <raven@themaw.net> | |
4 | ||
5 | Add the ability to use a domain dn in the LDAP_URI configuration | |
6 | entry. If a domain dn is encountered in the LDAP_URI the list of | |
7 | servers will be queried and used for the LDAP connection. The list | |
8 | won't be queried again until the minimum ttl found in the SRV RR | |
9 | records is reached or, if ttl isn't given in any SRV RR records, | |
10 | after 1 hour. | |
11 | --- | |
12 | ||
13 | CHANGELOG | 1 | |
14 | include/dclist.h | 14 + | |
15 | include/lookup_ldap.h | 3 | |
16 | man/auto.master.5.in | 8 | |
17 | modules/Makefile | 5 | |
18 | modules/dclist.c | 785 ++++++++++++++++++++++++++++++++++++++++ | |
19 | modules/lookup_ldap.c | 86 ++++ | |
20 | redhat/autofs.sysconfig.in | 11 + | |
21 | samples/autofs.conf.default.in | 11 + | |
22 | 9 files changed, 911 insertions(+), 13 deletions(-) | |
23 | create mode 100644 include/dclist.h | |
24 | create mode 100644 modules/dclist.c | |
25 | ||
26 | ||
27 | diff --git a/CHANGELOG b/CHANGELOG | |
28 | index 5000f0c..f49784a 100644 | |
29 | --- a/CHANGELOG | |
30 | +++ b/CHANGELOG | |
31 | @@ -49,6 +49,7 @@ | |
32 | - dont fail on ipv6 address when adding host. | |
33 | - always read file maps multi map fix. | |
34 | - always read file maps key lookup fixes. | |
35 | +- use srv query for domain dn. | |
36 | ||
37 | 4/11/2008 autofs-5.0.4 | |
38 | ----------------------- | |
39 | diff --git a/include/dclist.h b/include/dclist.h | |
40 | new file mode 100644 | |
41 | index 0000000..ed89f97 | |
42 | --- /dev/null | |
43 | +++ b/include/dclist.h | |
44 | @@ -0,0 +1,14 @@ | |
45 | +#ifndef __DCLIST_H | |
46 | +#define __DCLIST_H | |
47 | + | |
48 | +#include <sys/types.h> | |
49 | + | |
50 | +struct dclist { | |
51 | + time_t expire; | |
52 | + const char *uri; | |
53 | +}; | |
54 | + | |
55 | +struct dclist *get_dc_list(unsigned int logopt, const char *uri); | |
56 | +void free_dclist(struct dclist *dclist); | |
57 | + | |
58 | +#endif | |
59 | diff --git a/include/lookup_ldap.h b/include/lookup_ldap.h | |
60 | index b47bf5d..dcae220 100644 | |
61 | --- a/include/lookup_ldap.h | |
62 | +++ b/include/lookup_ldap.h | |
63 | @@ -10,6 +10,8 @@ | |
64 | #include <krb5.h> | |
65 | #endif | |
66 | ||
67 | +#include "dclist.h" | |
68 | + | |
69 | struct ldap_schema { | |
70 | char *map_class; | |
71 | char *map_attr; | |
72 | @@ -57,6 +59,7 @@ struct lookup_context { | |
73 | pthread_mutex_t uris_mutex; | |
74 | struct list_head *uris; | |
75 | struct ldap_uri *uri; | |
76 | + struct dclist *dclist; | |
77 | char *cur_host; | |
78 | struct ldap_searchdn *sdns; | |
79 | ||
80 | diff --git a/man/auto.master.5.in b/man/auto.master.5.in | |
81 | index 7b7004f..71c4402 100644 | |
82 | --- a/man/auto.master.5.in | |
83 | +++ b/man/auto.master.5.in | |
84 | @@ -271,6 +271,14 @@ Map entries that include a server name override this option and it is then | |
85 | not used. Default is an empty list in which case either the server given | |
86 | in a map entry or the LDAP configured default is used. This uri list is read at | |
87 | startup and whenever the daemon receives a HUP signal. | |
88 | +.P | |
89 | +This configuration option can also be used to request autofs lookup SRV RRs | |
90 | +for a domain of the form <proto>:///[<domain dn>]. Note that a trailing | |
91 | +"/" is not allowed when using this form. If the domain dn is not specified | |
92 | +the dns domain name (if any) is used to construct the domain dn for the | |
93 | +SRV RR lookup. The server list returned from an SRV RR lookup is refreshed | |
94 | +according to the minimum ttl found in the SRV RR records or after one hour, | |
95 | +whichever is less. | |
96 | .TP | |
97 | .B SEARCH_BASE | |
98 | The base dn to use when searching for amap base dn. This entry may be | |
99 | diff --git a/modules/Makefile b/modules/Makefile | |
100 | index 0d12f01..13b3bd8 100644 | |
101 | --- a/modules/Makefile | |
102 | +++ b/modules/Makefile | |
103 | @@ -86,9 +86,10 @@ lookup_hesiod.so: lookup_hesiod.c | |
104 | cyrus-sasl.o: cyrus-sasl.c | |
105 | $(CC) $(CFLAGS) $(LDAP_FLAGS) -c $< | |
106 | ||
107 | -lookup_ldap.so: lookup_ldap.c $(SASL_OBJ) | |
108 | +lookup_ldap.so: lookup_ldap.c dclist.o $(SASL_OBJ) | |
109 | $(CC) $(SOLDFLAGS) $(CFLAGS) $(LDAP_FLAGS) -o lookup_ldap.so \ | |
110 | - lookup_ldap.c $(SASL_OBJ) $(AUTOFS_LIB) $(LIBLDAP) | |
111 | + lookup_ldap.c dclist.o $(SASL_OBJ) \ | |
112 | + $(AUTOFS_LIB) $(LIBLDAP) $(LIBRESOLV) | |
113 | $(STRIP) lookup_ldap.so | |
114 | ||
115 | mount_nfs.so: mount_nfs.c replicated.o | |
116 | diff --git a/modules/dclist.c b/modules/dclist.c | |
117 | new file mode 100644 | |
118 | index 0000000..5b0e577 | |
119 | --- /dev/null | |
120 | +++ b/modules/dclist.c | |
121 | @@ -0,0 +1,785 @@ | |
122 | +/* | |
123 | + * Copyright 2009 Ian Kent <raven@themaw.net> | |
124 | + * Copyright 2009 Red Hat, Inc. | |
125 | + * | |
126 | + * This module was apapted from code contained in the Samba distribution | |
127 | + * file source/libads/dns.c which contained the following copyright | |
128 | + * information: | |
129 | + * | |
130 | + * Unix SMB/CIFS implementation. | |
131 | + * DNS utility library | |
132 | + * Copyright (C) Gerald (Jerry) Carter 2006. | |
133 | + * Copyright (C) Jeremy Allison 2007. | |
134 | + * | |
135 | + * This program is free software; you can redistribute it and/or modify | |
136 | + * it under the terms of the GNU General Public License as published by | |
137 | + * the Free Software Foundation; either version 3 of the License, or | |
138 | + * (at your option) any later version. | |
139 | + * | |
140 | + * This program is distributed in the hope that it will be useful, | |
141 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
142 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
143 | + * GNU General Public License for more details. | |
144 | + * | |
145 | + * You should have received a copy of the GNU General Public License | |
146 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
147 | +*/ | |
148 | + | |
149 | +#include <netinet/in.h> | |
150 | +#include <arpa/nameser.h> | |
151 | +#include <stdlib.h> | |
152 | +#include <string.h> | |
153 | +#include <resolv.h> | |
154 | +#include <netdb.h> | |
155 | +#include <ldap.h> | |
156 | +#include <sys/param.h> | |
157 | +#include <errno.h> | |
158 | + | |
159 | +#include "automount.h" | |
160 | +#include "dclist.h" | |
161 | + | |
162 | +#define MAX_DNS_PACKET_SIZE 0xffff | |
163 | +#define MAX_DNS_NAME_LENGTH MAXHOSTNAMELEN | |
164 | +/* The longest time we will cache dns srv records */ | |
165 | +#define MAX_TTL (60*60*1) /* 1 hours */ | |
166 | + | |
167 | +#ifdef NS_HFIXEDSZ /* Bind 8/9 interface */ | |
168 | +#if !defined(C_IN) /* AIX 5.3 already defines C_IN */ | |
169 | +# define C_IN ns_c_in | |
170 | +#endif | |
171 | +#if !defined(T_A) /* AIX 5.3 already defines T_A */ | |
172 | +# define T_A ns_t_a | |
173 | +#endif | |
174 | + | |
175 | +# define T_SRV ns_t_srv | |
176 | +#if !defined(T_NS) /* AIX 5.3 already defines T_NS */ | |
177 | +# define T_NS ns_t_ns | |
178 | +#endif | |
179 | +#else | |
180 | +# ifdef HFIXEDSZ | |
181 | +# define NS_HFIXEDSZ HFIXEDSZ | |
182 | +# else | |
183 | +# define NS_HFIXEDSZ sizeof(HEADER) | |
184 | +# endif /* HFIXEDSZ */ | |
185 | +# ifdef PACKETSZ | |
186 | +# define NS_PACKETSZ PACKETSZ | |
187 | +# else /* 512 is usually the default */ | |
188 | +# define NS_PACKETSZ 512 | |
189 | +# endif /* PACKETSZ */ | |
190 | +# define T_SRV 33 | |
191 | +#endif | |
192 | + | |
193 | +#define SVAL(buf, pos) (*(const uint16_t *)((const char *)(buf) + (pos))) | |
194 | +#define IVAL(buf, pos) (*(const uint32_t *)((const char *)(buf) + (pos))) | |
195 | + | |
196 | +#define SREV(x) ((((x)&0xFF)<<8) | (((x)>>8)&0xFF)) | |
197 | +#define IREV(x) ((SREV(x)<<16) | (SREV((x)>>16))) | |
198 | + | |
199 | +#define RSVAL(buf, pos) SREV(SVAL(buf, pos)) | |
200 | +#define RIVAL(buf, pos) IREV(IVAL(buf, pos)) | |
201 | + | |
202 | +#define QSORT_CAST (int (*)(const void *, const void *)) | |
203 | + | |
204 | +/* DNS query section in replies */ | |
205 | + | |
206 | +struct dns_query { | |
207 | + const char *hostname; | |
208 | + uint16_t type; | |
209 | + uint16_t in_class; | |
210 | +}; | |
211 | + | |
212 | +/* DNS RR record in reply */ | |
213 | + | |
214 | +struct dns_rr { | |
215 | + const char *hostname; | |
216 | + uint16_t type; | |
217 | + uint16_t in_class; | |
218 | + uint32_t ttl; | |
219 | + uint16_t rdatalen; | |
220 | + uint8_t *rdata; | |
221 | +}; | |
222 | + | |
223 | +/* SRV records */ | |
224 | + | |
225 | +struct dns_rr_srv { | |
226 | + const char *hostname; | |
227 | + uint16_t priority; | |
228 | + uint16_t weight; | |
229 | + uint16_t port; | |
230 | + uint32_t ttl; | |
231 | +}; | |
232 | + | |
233 | +static pthread_mutex_t dclist_mutex = PTHREAD_MUTEX_INITIALIZER; | |
234 | + | |
235 | +static void dclist_mutex_lock(void) | |
236 | +{ | |
237 | + int status = pthread_mutex_lock(&dclist_mutex); | |
238 | + if (status) | |
239 | + fatal(status); | |
240 | + return; | |
241 | +} | |
242 | + | |
243 | +static void dclist_mutex_unlock(void) | |
244 | +{ | |
245 | + int status = pthread_mutex_unlock(&dclist_mutex); | |
246 | + if (status) | |
247 | + fatal(status); | |
248 | + return; | |
249 | +} | |
250 | + | |
251 | +static int dns_parse_query(unsigned int logopt, | |
252 | + uint8_t *start, uint8_t *end, | |
253 | + uint8_t **ptr, struct dns_query *q) | |
254 | +{ | |
255 | + uint8_t *p = *ptr; | |
256 | + char hostname[MAX_DNS_NAME_LENGTH]; | |
257 | + char buf[MAX_ERR_BUF]; | |
258 | + int namelen; | |
259 | + | |
260 | + if (!start || !end || !q || !*ptr) | |
261 | + return 0; | |
262 | + | |
263 | + memset(q, 0, sizeof(*q)); | |
264 | + | |
265 | + /* See RFC 1035 for details. If this fails, then return. */ | |
266 | + | |
267 | + namelen = dn_expand(start, end, p, hostname, sizeof(hostname)); | |
268 | + if (namelen < 0) { | |
269 | + error(logopt, "failed to expand query hostname"); | |
270 | + return 0; | |
271 | + } | |
272 | + | |
273 | + p += namelen; | |
274 | + q->hostname = strdup(hostname); | |
275 | + if (!q) { | |
276 | + char *estr = strerror_r(errno, buf, MAX_ERR_BUF); | |
277 | + error(logopt, "strdup: %s", estr); | |
278 | + return 0; | |
279 | + } | |
280 | + | |
281 | + /* check that we have space remaining */ | |
282 | + | |
283 | + if (p + 4 > end) { | |
284 | + error(logopt, "insufficient buffer space for result"); | |
285 | + free((void *) q->hostname); | |
286 | + return 0; | |
287 | + } | |
288 | + | |
289 | + q->type = RSVAL(p, 0); | |
290 | + q->in_class = RSVAL(p, 2); | |
291 | + p += 4; | |
292 | + | |
293 | + *ptr = p; | |
294 | + | |
295 | + return 1; | |
296 | +} | |
297 | + | |
298 | +static int dns_parse_rr(unsigned int logopt, | |
299 | + uint8_t *start, uint8_t *end, | |
300 | + uint8_t **ptr, struct dns_rr *rr) | |
301 | +{ | |
302 | + uint8_t *p = *ptr; | |
303 | + char hostname[MAX_DNS_NAME_LENGTH]; | |
304 | + char buf[MAX_ERR_BUF]; | |
305 | + int namelen; | |
306 | + | |
307 | + if (!start || !end || !rr || !*ptr) | |
308 | + return 0; | |
309 | + | |
310 | + memset(rr, 0, sizeof(*rr)); | |
311 | + | |
312 | + /* pull the name from the answer */ | |
313 | + | |
314 | + namelen = dn_expand(start, end, p, hostname, sizeof(hostname)); | |
315 | + if (namelen < 0) { | |
316 | + error(logopt, "failed to expand query hostname"); | |
317 | + return 0; | |
318 | + } | |
319 | + p += namelen; | |
320 | + rr->hostname = strdup(hostname); | |
321 | + if (!rr->hostname) { | |
322 | + char *estr = strerror_r(errno, buf, MAX_ERR_BUF); | |
323 | + error(logopt, "strdup: %s", estr); | |
324 | + return 0; | |
325 | + } | |
326 | + | |
327 | + /* check that we have space remaining */ | |
328 | + | |
329 | + if (p + 10 > end) { | |
330 | + error(logopt, "insufficient buffer space for result"); | |
331 | + free((void *) rr->hostname); | |
332 | + return 0; | |
333 | + } | |
334 | + | |
335 | + /* pull some values and then skip onto the string */ | |
336 | + | |
337 | + rr->type = RSVAL(p, 0); | |
338 | + rr->in_class = RSVAL(p, 2); | |
339 | + rr->ttl = RIVAL(p, 4); | |
340 | + rr->rdatalen = RSVAL(p, 8); | |
341 | + | |
342 | + p += 10; | |
343 | + | |
344 | + /* sanity check the available space */ | |
345 | + | |
346 | + if (p + rr->rdatalen > end) { | |
347 | + error(logopt, "insufficient buffer space for data"); | |
348 | + free((void *) rr->hostname); | |
349 | + return 0; | |
350 | + } | |
351 | + | |
352 | + /* save a point to the rdata for this section */ | |
353 | + | |
354 | + rr->rdata = p; | |
355 | + p += rr->rdatalen; | |
356 | + | |
357 | + *ptr = p; | |
358 | + | |
359 | + return 1; | |
360 | +} | |
361 | + | |
362 | +static int dns_parse_rr_srv(unsigned int logopt, | |
363 | + uint8_t *start, uint8_t *end, | |
364 | + uint8_t **ptr, struct dns_rr_srv *srv) | |
365 | +{ | |
366 | + struct dns_rr rr; | |
367 | + uint8_t *p; | |
368 | + char dcname[MAX_DNS_NAME_LENGTH]; | |
369 | + char buf[MAX_ERR_BUF]; | |
370 | + int namelen; | |
371 | + | |
372 | + if (!start || !end || !srv || !*ptr) | |
373 | + return 0; | |
374 | + | |
375 | + /* Parse the RR entry. Coming out of the this, ptr is at the beginning | |
376 | + of the next record */ | |
377 | + | |
378 | + if (!dns_parse_rr(logopt, start, end, ptr, &rr)) { | |
379 | + error(logopt, "Failed to parse RR record"); | |
380 | + return 0; | |
381 | + } | |
382 | + | |
383 | + if (rr.type != T_SRV) { | |
384 | + error(logopt, "Bad answer type (%d)", rr.type); | |
385 | + return 0; | |
386 | + } | |
387 | + | |
388 | + p = rr.rdata; | |
389 | + | |
390 | + srv->priority = RSVAL(p, 0); | |
391 | + srv->weight = RSVAL(p, 2); | |
392 | + srv->port = RSVAL(p, 4); | |
393 | + srv->ttl = rr.ttl; | |
394 | + | |
395 | + p += 6; | |
396 | + | |
397 | + namelen = dn_expand(start, end, p, dcname, sizeof(dcname)); | |
398 | + if (namelen < 0) { | |
399 | + error(logopt, "Failed to expand dcname"); | |
400 | + return 0; | |
401 | + } | |
402 | + | |
403 | + srv->hostname = strdup(dcname); | |
404 | + if (!srv->hostname) { | |
405 | + char *estr = strerror_r(errno, buf, MAX_ERR_BUF); | |
406 | + error(logopt, "strdup: %s", estr); | |
407 | + return 0; | |
408 | + } | |
409 | + | |
410 | + debug(logopt, "Parsed %s [%u, %u, %u]", | |
411 | + srv->hostname, srv->priority, srv->weight, srv->port); | |
412 | + | |
413 | + return 1; | |
414 | +} | |
415 | + | |
416 | +/********************************************************************* | |
417 | + Sort SRV record list based on weight and priority. See RFC 2782. | |
418 | +*********************************************************************/ | |
419 | + | |
420 | +static int dnssrvcmp(struct dns_rr_srv *a, struct dns_rr_srv *b) | |
421 | +{ | |
422 | + if (a->priority == b->priority) { | |
423 | + /* randomize entries with an equal weight and priority */ | |
424 | + if (a->weight == b->weight) | |
425 | + return 0; | |
426 | + | |
427 | + /* higher weights should be sorted lower */ | |
428 | + if (a->weight > b->weight) | |
429 | + return -1; | |
430 | + else | |
431 | + return 1; | |
432 | + } | |
433 | + | |
434 | + if (a->priority < b->priority) | |
435 | + return -1; | |
436 | + | |
437 | + return 1; | |
438 | +} | |
439 | + | |
440 | +#define DNS_FAILED_WAITTIME 30 | |
441 | + | |
442 | +static int dns_send_req(unsigned int logopt, | |
443 | + const char *name, int q_type, uint8_t **rbuf, | |
444 | + int *resp_length) | |
445 | +{ | |
446 | + uint8_t *buffer = NULL; | |
447 | + size_t buf_len = 0; | |
448 | + int resp_len = NS_PACKETSZ; | |
449 | + static time_t last_dns_check = 0; | |
450 | + static unsigned int last_dns_status = 0; | |
451 | + time_t now = time(NULL); | |
452 | + char buf[MAX_ERR_BUF]; | |
453 | + | |
454 | + /* Try to prevent bursts of DNS lookups if the server is down */ | |
455 | + | |
456 | + /* Protect against large clock changes */ | |
457 | + | |
458 | + if (last_dns_check > now) | |
459 | + last_dns_check = 0; | |
460 | + | |
461 | + /* IF we had a DNS timeout or a bad server and we are still | |
462 | + in the 30 second cache window, just return the previous | |
463 | + status and save the network timeout. */ | |
464 | + | |
465 | + if ((last_dns_status == ETIMEDOUT || | |
466 | + last_dns_status == ECONNREFUSED) && | |
467 | + ((last_dns_check + DNS_FAILED_WAITTIME) > now)) { | |
468 | + char *estr = strerror_r(last_dns_status, buf, MAX_ERR_BUF); | |
469 | + debug(logopt, "Returning cached status (%s)", estr); | |
470 | + return last_dns_status; | |
471 | + } | |
472 | + | |
473 | + /* Send the Query */ | |
474 | + do { | |
475 | + if (buffer) | |
476 | + free(buffer); | |
477 | + | |
478 | + buf_len = resp_len * sizeof(uint8_t); | |
479 | + | |
480 | + if (buf_len) { | |
481 | + buffer = malloc(buf_len); | |
482 | + if (!buffer) { | |
483 | + char *estr = strerror_r(errno, buf, MAX_ERR_BUF); | |
484 | + error(logopt, "malloc: %s", estr); | |
485 | + last_dns_status = ENOMEM; | |
486 | + last_dns_check = time(NULL); | |
487 | + return last_dns_status; | |
488 | + } | |
489 | + } | |
490 | + | |
491 | + resp_len = res_query(name, C_IN, q_type, buffer, buf_len); | |
492 | + if (resp_len < 0) { | |
493 | + char *estr = strerror_r(errno, buf, MAX_ERR_BUF); | |
494 | + error(logopt, "Failed to resolve %s (%s)", name, estr); | |
495 | + free(buffer); | |
496 | + last_dns_status = ENOENT; | |
497 | + last_dns_check = time(NULL); | |
498 | + return last_dns_status; | |
499 | + } | |
500 | + | |
501 | + /* On AIX, Solaris, and possibly some older glibc systems (e.g. SLES8) | |
502 | + truncated replies never give back a resp_len > buflen | |
503 | + which ends up causing DNS resolve failures on large tcp DNS replies */ | |
504 | + | |
505 | + if (buf_len == resp_len) { | |
506 | + if (resp_len == MAX_DNS_PACKET_SIZE) { | |
507 | + error(logopt, | |
508 | + "DNS reply too large when resolving %s", | |
509 | + name); | |
510 | + free(buffer); | |
511 | + last_dns_status = EMSGSIZE; | |
512 | + last_dns_check = time(NULL); | |
513 | + return last_dns_status; | |
514 | + } | |
515 | + | |
516 | + resp_len = MIN(resp_len * 2, MAX_DNS_PACKET_SIZE); | |
517 | + } | |
518 | + } while (buf_len < resp_len && resp_len <= MAX_DNS_PACKET_SIZE); | |
519 | + | |
520 | + *rbuf = buffer; | |
521 | + *resp_length = resp_len; | |
522 | + | |
523 | + last_dns_check = time(NULL); | |
524 | + last_dns_status = 0; | |
525 | + | |
526 | + return 0; | |
527 | +} | |
528 | + | |
529 | +static int dns_lookup_srv(unsigned int logopt, const char *name, | |
530 | + struct dns_rr_srv **dclist, int *numdcs) | |
531 | +{ | |
532 | + uint8_t *buffer = NULL; | |
533 | + int resp_len = 0; | |
534 | + struct dns_rr_srv *dcs = NULL; | |
535 | + int query_count, answer_count; | |
536 | + uint8_t *p = buffer; | |
537 | + int rrnum; | |
538 | + int idx = 0; | |
539 | + char buf[MAX_ERR_BUF]; | |
540 | + int ret; | |
541 | + | |
542 | + if (!name || !dclist) | |
543 | + return -EINVAL; | |
544 | + | |
545 | + /* Send the request. May have to loop several times in case | |
546 | + of large replies */ | |
547 | + | |
548 | + ret = dns_send_req(logopt, name, T_SRV, &buffer, &resp_len); | |
549 | + if (ret) { | |
550 | + error(logopt, "Failed to send DNS query"); | |
551 | + return ret; | |
552 | + } | |
553 | + p = buffer; | |
554 | + | |
555 | + /* For some insane reason, the ns_initparse() et. al. routines are only | |
556 | + available in libresolv.a, and not the shared lib. Who knows why.... | |
557 | + So we have to parse the DNS reply ourselves */ | |
558 | + | |
559 | + /* Pull the answer RR's count from the header. | |
560 | + * Use the NMB ordering macros */ | |
561 | + | |
562 | + query_count = RSVAL(p, 4); | |
563 | + answer_count = RSVAL(p, 6); | |
564 | + | |
565 | + debug(logopt, | |
566 | + "%d records returned in the answer section.", | |
567 | + answer_count); | |
568 | + | |
569 | + if (answer_count) { | |
570 | + dcs = malloc(sizeof(struct dns_rr_srv) * answer_count); | |
571 | + if (!dcs) { | |
572 | + char *estr = strerror_r(errno, buf, MAX_ERR_BUF); | |
573 | + error(logopt, "malloc: %s", estr); | |
574 | + free(buffer); | |
575 | + return ENOMEM; | |
576 | + } | |
577 | + } | |
578 | + | |
579 | + /* now skip the header */ | |
580 | + | |
581 | + p += NS_HFIXEDSZ; | |
582 | + | |
583 | + /* parse the query section */ | |
584 | + | |
585 | + for (rrnum = 0; rrnum < query_count; rrnum++) { | |
586 | + struct dns_query q; | |
587 | + | |
588 | + ret = dns_parse_query(logopt, buffer, buffer+resp_len, &p, &q); | |
589 | + if (!ret) { | |
590 | + error(logopt, | |
591 | + "Failed to parse query record [%d]", rrnum); | |
592 | + free(buffer); | |
593 | + free(dcs); | |
594 | + return EBADMSG; | |
595 | + } | |
596 | + } | |
597 | + | |
598 | + /* now we are at the answer section */ | |
599 | + | |
600 | + for (rrnum = 0; rrnum < answer_count; rrnum++) { | |
601 | + ret = dns_parse_rr_srv(logopt, | |
602 | + buffer, buffer+resp_len, | |
603 | + &p, &dcs[rrnum]); | |
604 | + if (!ret) { | |
605 | + error(logopt, | |
606 | + "Failed to parse answer record [%d]", rrnum); | |
607 | + free(buffer); | |
608 | + free(dcs); | |
609 | + return EBADMSG; | |
610 | + } | |
611 | + } | |
612 | + idx = rrnum; | |
613 | + | |
614 | + qsort(dcs, idx, sizeof(struct dns_rr_srv), QSORT_CAST dnssrvcmp); | |
615 | + | |
616 | + *dclist = dcs; | |
617 | + *numdcs = idx; | |
618 | + | |
619 | + return 0; | |
620 | +} | |
621 | + | |
622 | +static char *escape_dn_commas(const char *uri) | |
623 | +{ | |
624 | + size_t len = strlen(uri); | |
625 | + char *new, *tmp, *ptr; | |
626 | + | |
627 | + ptr = (char *) uri; | |
628 | + while (*ptr) { | |
629 | + if (*ptr == '\\') | |
630 | + ptr += 2; | |
631 | + if (*ptr == ',') | |
632 | + len += 2; | |
633 | + ptr++; | |
634 | + } | |
635 | + | |
636 | + new = malloc(len + 1); | |
637 | + if (!new) | |
638 | + return NULL; | |
639 | + memset(new, 0, len + 1); | |
640 | + | |
641 | + ptr = (char *) uri; | |
642 | + tmp = new; | |
643 | + while (*ptr) { | |
644 | + if (*ptr == '\\') { | |
645 | + ptr++; | |
646 | + *tmp++ = *ptr++; | |
647 | + continue; | |
648 | + } | |
649 | + if (*ptr == ',') { | |
650 | + strcpy(tmp, "%2c"); | |
651 | + ptr++; | |
652 | + tmp += 3; | |
653 | + continue; | |
654 | + } | |
655 | + *tmp++ = *ptr++; | |
656 | + } | |
657 | + | |
658 | + return new; | |
659 | +} | |
660 | + | |
661 | +void free_dclist(struct dclist *dclist) | |
662 | +{ | |
663 | + if (dclist->uri) | |
664 | + free((void *) dclist->uri); | |
665 | + free(dclist); | |
666 | +} | |
667 | + | |
668 | +static char *getdnsdomainname(unsigned int logopt) | |
669 | +{ | |
670 | + struct addrinfo hints, *ni; | |
671 | + char name[MAX_DNS_NAME_LENGTH + 1]; | |
672 | + char buf[MAX_ERR_BUF]; | |
673 | + char *dnsdomain = NULL; | |
674 | + char *ptr; | |
675 | + int ret; | |
676 | + | |
677 | + memset(name, 0, sizeof(name)); | |
678 | + if (gethostname(name, MAX_DNS_NAME_LENGTH) == -1) { | |
679 | + char *estr = strerror_r(errno, buf, MAX_ERR_BUF); | |
680 | + error(logopt, "gethostname: %s", estr); | |
681 | + return NULL; | |
682 | + } | |
683 | + | |
684 | + memset(&hints, 0, sizeof(hints)); | |
685 | + hints.ai_flags = AI_CANONNAME; | |
686 | + hints.ai_family = AF_UNSPEC; | |
687 | + hints.ai_socktype = SOCK_DGRAM; | |
688 | + | |
689 | + ret = getaddrinfo(name, NULL, &hints, &ni); | |
690 | + if (ret) { | |
691 | + error(logopt, "hostname lookup failed: %s", gai_strerror(ret)); | |
692 | + return NULL; | |
693 | + } | |
694 | + | |
695 | + ptr = ni->ai_canonname; | |
696 | + while (*ptr && *ptr != '.') | |
697 | + ptr++; | |
698 | + | |
699 | + if (*++ptr) | |
700 | + dnsdomain = strdup(ptr); | |
701 | + | |
702 | + freeaddrinfo(ni); | |
703 | + | |
704 | + return dnsdomain; | |
705 | +} | |
706 | + | |
707 | +struct dclist *get_dc_list(unsigned int logopt, const char *uri) | |
708 | +{ | |
709 | + LDAPURLDesc *ludlist = NULL; | |
710 | + LDAPURLDesc **ludp; | |
711 | + struct dns_rr_srv *dcs; | |
712 | + unsigned int min_ttl = MAX_TTL; | |
713 | + struct dclist *dclist = NULL;; | |
714 | + char buf[MAX_ERR_BUF]; | |
715 | + char *dn_uri, *esc_uri; | |
716 | + char *domain; | |
717 | + char *list; | |
718 | + int numdcs; | |
719 | + int ret; | |
720 | + | |
721 | + if (strcmp(uri, "ldap:///") && strcmp(uri, "ldaps:///")) { | |
722 | + dn_uri = strdup(uri); | |
723 | + if (!dn_uri) { | |
724 | + char *estr = strerror_r(errno, buf, MAX_ERR_BUF); | |
725 | + error(logopt, "strdup: %s", estr); | |
726 | + return NULL; | |
727 | + } | |
728 | + } else { | |
729 | + char *dnsdomain; | |
730 | + char *hdn; | |
731 | + | |
732 | + dnsdomain = getdnsdomainname(logopt); | |
733 | + if (!dnsdomain) { | |
734 | + error(logopt, "failed to get dns domainname"); | |
735 | + return NULL; | |
736 | + } | |
737 | + | |
738 | + if (ldap_domain2dn(dnsdomain, &hdn) || hdn == NULL) { | |
739 | + error(logopt, | |
740 | + "Could not turn domain \"%s\" into a dn\n", | |
741 | + dnsdomain); | |
742 | + free(dnsdomain); | |
743 | + return NULL; | |
744 | + } | |
745 | + free(dnsdomain); | |
746 | + | |
747 | + dn_uri = malloc(strlen(uri) + strlen(hdn) + 1); | |
748 | + if (!dn_uri) { | |
749 | + char *estr = strerror_r(errno, buf, MAX_ERR_BUF); | |
750 | + error(logopt, "malloc: %s", estr); | |
751 | + ber_memfree(hdn); | |
752 | + return NULL; | |
753 | + } | |
754 | + | |
755 | + strcpy(dn_uri, uri); | |
756 | + strcat(dn_uri, hdn); | |
757 | + ber_memfree(hdn); | |
758 | + } | |
759 | + | |
760 | + esc_uri = escape_dn_commas(dn_uri); | |
761 | + if (!esc_uri) { | |
762 | + error(logopt, "Could not escape commas in uri %s", dn_uri); | |
763 | + free(dn_uri); | |
764 | + return NULL; | |
765 | + } | |
766 | + | |
767 | + ret = ldap_url_parse(esc_uri, &ludlist); | |
768 | + if (ret != LDAP_URL_SUCCESS) { | |
769 | + error(logopt, "Could not parse uri %s (%d)", dn_uri, ret); | |
770 | + free(esc_uri); | |
771 | + free(dn_uri); | |
772 | + return NULL; | |
773 | + } | |
774 | + | |
775 | + free(esc_uri); | |
776 | + | |
777 | + if (!ludlist) { | |
778 | + error(logopt, "No dn found in uri %s", dn_uri); | |
779 | + free(dn_uri); | |
780 | + return NULL; | |
781 | + } | |
782 | + | |
783 | + free(dn_uri); | |
784 | + | |
785 | + dclist = malloc(sizeof(struct dclist)); | |
786 | + if (!dclist) { | |
787 | + char *estr = strerror_r(errno, buf, MAX_ERR_BUF); | |
788 | + error(logopt, "malloc: %s", estr); | |
789 | + ldap_free_urldesc(ludlist); | |
790 | + return NULL; | |
791 | + } | |
792 | + memset(dclist, 0, sizeof(struct dclist)); | |
793 | + | |
794 | + list = NULL; | |
795 | + for (ludp = &ludlist; *ludp != NULL;) { | |
796 | + LDAPURLDesc *lud = *ludp; | |
797 | + size_t req_len, len; | |
798 | + char *request = NULL; | |
799 | + char *tmp; | |
800 | + int i; | |
801 | + | |
802 | + if (!lud->lud_dn && !lud->lud_dn[0] && | |
803 | + (!lud->lud_host || !lud->lud_host[0])) { | |
804 | + *ludp = lud->lud_next; | |
805 | + continue; | |
806 | + } | |
807 | + | |
808 | + domain = NULL; | |
809 | + if (ldap_dn2domain(lud->lud_dn, &domain) || domain == NULL) { | |
810 | + error(logopt, | |
811 | + "Could not turn dn \"%s\" into a domain", | |
812 | + lud->lud_dn); | |
813 | + *ludp = lud->lud_next; | |
814 | + continue; | |
815 | + } | |
816 | + | |
817 | + debug(logopt, "doing lookup of SRV RRs for domain %s", domain); | |
818 | + | |
819 | + req_len = sizeof("_ldap._tcp.") + strlen(domain); | |
820 | + request = malloc(req_len); | |
821 | + if (!request) { | |
822 | + char *estr = strerror_r(errno, buf, MAX_ERR_BUF); | |
823 | + error(logopt, "malloc: %s", estr); | |
824 | + goto out_error; | |
825 | + } | |
826 | + | |
827 | + ret = snprintf(request, req_len, "_ldap._tcp.%s", domain); | |
828 | + if (ret >= req_len) { | |
829 | + free(request); | |
830 | + goto out_error; | |
831 | + } | |
832 | + | |
833 | + dclist_mutex_lock(); | |
834 | + if (dns_lookup_srv(logopt, request, &dcs, &numdcs)) { | |
835 | + error(logopt, | |
836 | + "DNS SRV query failed for domain %s", domain); | |
837 | + dclist_mutex_unlock(); | |
838 | + free(request); | |
839 | + goto out_error; | |
840 | + } | |
841 | + dclist_mutex_unlock(); | |
842 | + free(request); | |
843 | + | |
844 | + len = strlen(lud->lud_scheme); | |
845 | + len += sizeof("://"); | |
846 | + len *= numdcs; | |
847 | + | |
848 | + for (i = 0; i < numdcs; i++) { | |
849 | + if (dcs[i].ttl > 0 && dcs[i].ttl < min_ttl) | |
850 | + min_ttl = dcs[i].ttl; | |
851 | + len += strlen(dcs[i].hostname); | |
852 | + if (dcs[i].port > 0) | |
853 | + len += sizeof(":65535"); | |
854 | + } | |
855 | + | |
856 | + tmp = realloc(list, len); | |
857 | + if (!tmp) { | |
858 | + char *estr = strerror_r(errno, buf, MAX_ERR_BUF); | |
859 | + error(logopt, "realloc: %s", estr); | |
860 | + goto out_error; | |
861 | + } | |
862 | + | |
863 | + if (!list) | |
864 | + memset(tmp, 0, len); | |
865 | + else | |
866 | + strcat(tmp, " "); | |
867 | + | |
868 | + for (i = 0; i < numdcs; i++) { | |
869 | + if (i > 0) | |
870 | + strcat(tmp, " "); | |
871 | + strcat(tmp, lud->lud_scheme); | |
872 | + strcat(tmp, "://"); | |
873 | + strcat(tmp, dcs[i].hostname); | |
874 | + if (dcs[i].port > 0) { | |
875 | + char port[7]; | |
876 | + ret = snprintf(port, 7, ":%d", dcs[i].port); | |
877 | + if (ret > 6) { | |
878 | + error(logopt, | |
879 | + "invalid port: %u", dcs[i].port); | |
880 | + goto out_error; | |
881 | + } | |
882 | + strcat(tmp, port); | |
883 | + } | |
884 | + } | |
885 | + list = tmp; | |
886 | + | |
887 | + *ludp = lud->lud_next; | |
888 | + ber_memfree(domain); | |
889 | + } | |
890 | + | |
891 | + ldap_free_urldesc(ludlist); | |
892 | + | |
893 | + dclist->expire = time(NULL) + min_ttl; | |
894 | + dclist->uri = list; | |
895 | + | |
896 | + return dclist; | |
897 | + | |
898 | +out_error: | |
899 | + if (list) | |
900 | + free(list); | |
901 | + if (domain) | |
902 | + ber_memfree(domain); | |
903 | + ldap_free_urldesc(ludlist); | |
904 | + free_dclist(dclist); | |
905 | + return NULL; | |
906 | +} | |
907 | diff --git a/modules/lookup_ldap.c b/modules/lookup_ldap.c | |
908 | index a847622..f6b3f42 100644 | |
909 | --- a/modules/lookup_ldap.c | |
910 | +++ b/modules/lookup_ldap.c | |
911 | @@ -643,14 +643,26 @@ static LDAP *find_server(unsigned logopt, struct lookup_context *ctxt) | |
912 | LDAP *ldap = NULL; | |
913 | struct ldap_uri *this; | |
914 | struct list_head *p, *first; | |
915 | + struct dclist *dclist = NULL; | |
916 | + char *uri = NULL; | |
917 | ||
918 | - /* Try each uri in list, add connect fails to tmp list */ | |
919 | uris_mutex_lock(ctxt); | |
920 | + if (ctxt->dclist) { | |
921 | + dclist = ctxt->dclist; | |
922 | + if (ctxt->dclist->expire < time(NULL)) { | |
923 | + free_dclist(ctxt->dclist); | |
924 | + ctxt->dclist = NULL; | |
925 | + dclist = NULL; | |
926 | + } | |
927 | + } | |
928 | if (!ctxt->uri) | |
929 | first = ctxt->uris; | |
930 | else | |
931 | first = &ctxt->uri->list; | |
932 | uris_mutex_unlock(ctxt); | |
933 | + | |
934 | + | |
935 | + /* Try each uri, save point in server list upon success */ | |
936 | p = first->next; | |
937 | while(p != first) { | |
938 | /* Skip list head */ | |
939 | @@ -659,25 +671,62 @@ static LDAP *find_server(unsigned logopt, struct lookup_context *ctxt) | |
940 | continue; | |
941 | } | |
942 | this = list_entry(p, struct ldap_uri, list); | |
943 | - debug(logopt, "trying server %s", this->uri); | |
944 | - ldap = connect_to_server(logopt, this->uri, ctxt); | |
945 | + if (!strstr(this->uri, ":///")) | |
946 | + uri = strdup(this->uri); | |
947 | + else { | |
948 | + if (dclist) | |
949 | + uri = strdup(dclist->uri); | |
950 | + else { | |
951 | + struct dclist *tmp; | |
952 | + tmp = get_dc_list(logopt, this->uri); | |
953 | + if (!tmp) { | |
954 | + p = p->next; | |
955 | + continue; | |
956 | + } | |
957 | + dclist = tmp; | |
958 | + uri = strdup(dclist->uri); | |
959 | + } | |
960 | + } | |
961 | + if (!uri) { | |
962 | + p = p->next; | |
963 | + continue; | |
964 | + } | |
965 | + debug(logopt, "trying server uri %s", uri); | |
966 | + ldap = connect_to_server(logopt, uri, ctxt); | |
967 | if (ldap) { | |
968 | - info(logopt, "connected to uri %s", this->uri); | |
969 | - uris_mutex_lock(ctxt); | |
970 | - ctxt->uri = this; | |
971 | - uris_mutex_unlock(ctxt); | |
972 | + info(logopt, "connected to uri %s", uri); | |
973 | + free(uri); | |
974 | break; | |
975 | } | |
976 | + free(uri); | |
977 | + uri = NULL; | |
978 | + free_dclist(dclist); | |
979 | + dclist = NULL; | |
980 | p = p->next; | |
981 | } | |
982 | ||
983 | + uris_mutex_lock(ctxt); | |
984 | + if (ldap) | |
985 | + ctxt->uri = this; | |
986 | + if (dclist) { | |
987 | + if (!ctxt->dclist) | |
988 | + ctxt->dclist = dclist; | |
989 | + else { | |
990 | + if (ctxt->dclist != dclist) { | |
991 | + free_dclist(ctxt->dclist); | |
992 | + ctxt->dclist = dclist; | |
993 | + } | |
994 | + } | |
995 | + } | |
996 | + uris_mutex_unlock(ctxt); | |
997 | + | |
998 | return ldap; | |
999 | } | |
1000 | ||
1001 | static LDAP *do_reconnect(unsigned logopt, struct lookup_context *ctxt) | |
1002 | { | |
1003 | - struct ldap_uri *this; | |
1004 | LDAP *ldap; | |
1005 | + char *uri; | |
1006 | ||
1007 | if (ctxt->server || !ctxt->uris) { | |
1008 | ldap = do_connect(logopt, ctxt->server, ctxt); | |
1009 | @@ -692,9 +741,20 @@ static LDAP *do_reconnect(unsigned logopt, struct lookup_context *ctxt) | |
1010 | } | |
1011 | ||
1012 | uris_mutex_lock(ctxt); | |
1013 | - this = ctxt->uri; | |
1014 | + if (ctxt->dclist) | |
1015 | + uri = strdup(ctxt->dclist->uri); | |
1016 | + else | |
1017 | + uri = strdup(ctxt->uri->uri); | |
1018 | uris_mutex_unlock(ctxt); | |
1019 | - ldap = do_connect(logopt, this->uri, ctxt); | |
1020 | + | |
1021 | + if (!uri) { | |
1022 | + char buf[MAX_ERR_BUF]; | |
1023 | + char *estr = strerror_r(errno, buf, sizeof(buf)); | |
1024 | + crit(logopt, MODPREFIX "strdup: %s", estr); | |
1025 | + return NULL; | |
1026 | + } | |
1027 | + | |
1028 | + ldap = do_connect(logopt, uri, ctxt); | |
1029 | #ifdef WITH_SASL | |
1030 | /* | |
1031 | * Dispose of the sasl authentication connection and try the | |
1032 | @@ -702,9 +762,11 @@ static LDAP *do_reconnect(unsigned logopt, struct lookup_context *ctxt) | |
1033 | */ | |
1034 | if (!ldap) { | |
1035 | autofs_sasl_dispose(ctxt); | |
1036 | - ldap = connect_to_server(logopt, this->uri, ctxt); | |
1037 | + ldap = connect_to_server(logopt, uri, ctxt); | |
1038 | } | |
1039 | #endif | |
1040 | + free(uri); | |
1041 | + | |
1042 | if (ldap) | |
1043 | return ldap; | |
1044 | ||
1045 | @@ -1296,6 +1358,8 @@ static void free_context(struct lookup_context *ctxt) | |
1046 | fatal(ret); | |
1047 | if (ctxt->sdns) | |
1048 | defaults_free_searchdns(ctxt->sdns); | |
1049 | + if (ctxt->dclist) | |
1050 | + free_dclist(ctxt->dclist); | |
1051 | free(ctxt); | |
1052 | ||
1053 | return; | |
1054 | diff --git a/redhat/autofs.sysconfig.in b/redhat/autofs.sysconfig.in | |
1055 | index 97e20fe..37448ea 100644 | |
1056 | --- a/redhat/autofs.sysconfig.in | |
1057 | +++ b/redhat/autofs.sysconfig.in | |
1058 | @@ -50,6 +50,17 @@ BROWSE_MODE="no" | |
1059 | # Map entries that include a server name override | |
1060 | # this option. | |
1061 | # | |
1062 | +# This configuration option can also be used to | |
1063 | +# request autofs lookup SRV RRs for a domain of | |
1064 | +# the form <proto>:///[<domain dn>]. Note that a | |
1065 | +# trailing "/" is not allowed when using this form. | |
1066 | +# If the domain dn is not specified the dns domain | |
1067 | +# name (if any) is used to construct the domain dn | |
1068 | +# for the SRV RR lookup. The server list returned | |
1069 | +# from an SRV RR lookup is refreshed according to | |
1070 | +# the minimum ttl found in the SRV RR records or | |
1071 | +# after one hour, whichever is less. | |
1072 | +# | |
1073 | #LDAP_URI="" | |
1074 | # | |
1075 | # LDAP__TIMEOUT - timeout value for the synchronous API calls | |
1076 | diff --git a/samples/autofs.conf.default.in b/samples/autofs.conf.default.in | |
1077 | index 62084c2..7dee5fd 100644 | |
1078 | --- a/samples/autofs.conf.default.in | |
1079 | +++ b/samples/autofs.conf.default.in | |
1080 | @@ -48,6 +48,17 @@ BROWSE_MODE="no" | |
1081 | # Map entries that include a server name override | |
1082 | # this option. | |
1083 | # | |
1084 | +# This configuration option can also be used to | |
1085 | +# request autofs lookup SRV RRs for a domain of | |
1086 | +# the form <proto>:///[<domain dn>]. Note that a | |
1087 | +# trailing "/" is not allowed when using this form. | |
1088 | +# If the domain dn is not specified the dns domain | |
1089 | +# name (if any) is used to construct the domain dn | |
1090 | +# for the SRV RR lookup. The server list returned | |
1091 | +# from an SRV RR lookup is refreshed according to | |
1092 | +# the minimum ttl found in the SRV RR records or | |
1093 | +# after one hour, whichever is less. | |
1094 | +# | |
1095 | #LDAP_URI="" | |
1096 | # | |
1097 | # LDAP__TIMEOUT - timeout value for the synchronous API calls |