]> git.ipfire.org Git - thirdparty/squid.git/blob - helpers/external_acl/kerberos_ldap_group/support_resolv.cc
Sync with trunk rev.13542
[thirdparty/squid.git] / helpers / external_acl / kerberos_ldap_group / support_resolv.cc
1 /*
2 * -----------------------------------------------------------------------------
3 *
4 * Author: Markus Moeller (markus_moeller at compuserve.com)
5 *
6 * Copyright (C) 2007 Markus Moeller. All rights reserved.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
21 *
22 * -----------------------------------------------------------------------------
23 */
24
25 #include "squid.h"
26 #include "util.h"
27
28 #if HAVE_LDAP
29
30 #include "support.h"
31 #include <cerrno>
32 #if HAVE_NETDB_H
33 #include <netdb.h>
34 #endif
35 #if HAVE_NETINET_IN_H
36 #include <netinet/in.h>
37 #endif
38 #if HAVE_RESOLV_H
39 #include <resolv.h>
40 #endif
41 #if HAVE_ARPA_NAMESER_H
42 #include <arpa/nameser.h>
43 #endif
44
45 void nsError(int nserror, char *server);
46 static int compare_hosts(struct hstruct *h1, struct hstruct *h2);
47 static void swap(struct hstruct *a, struct hstruct *b);
48 static void sort(struct hstruct *array, int nitems, int (*cmp) (struct hstruct *, struct hstruct *), int begin, int end);
49 static void msort(struct hstruct *array, size_t nitems, int (*cmp) (struct hstruct *, struct hstruct *));
50
51 /*
52 * See http://www.ietf.org/rfc/rfc1035.txt
53 */
54 /*
55 * See http://www.ietf.org/rfc/rfc2782.txt
56 *
57 */
58 void
59 nsError(int nserror, char *service)
60 {
61 switch (nserror) {
62 case HOST_NOT_FOUND:
63 error((char *) "%s| %s: ERROR: res_search: Unknown service record: %s\n", LogTime(), PROGRAM, service);
64 break;
65 case NO_DATA:
66 error((char *) "%s| %s: ERROR: res_search: No SRV record for %s\n", LogTime(), PROGRAM, service);
67 break;
68 case TRY_AGAIN:
69 error((char *) "%s| %s: ERROR: res_search: No response for SRV query\n", LogTime(), PROGRAM);
70 break;
71 default:
72 error((char *) "%s| %s: ERROR: res_search: Unexpected error: %s\n", LogTime(), PROGRAM, strerror(nserror));
73 }
74 }
75
76 static void
77 swap(struct hstruct *a, struct hstruct *b)
78 {
79 struct hstruct c;
80
81 c.host = a->host;
82 c.priority = a->priority;
83 c.weight = a->weight;
84 a->host = b->host;
85 a->priority = b->priority;
86 a->weight = b->weight;
87 b->host = c.host;
88 b->priority = c.priority;
89 b->weight = c.weight;
90 }
91
92 static void
93 sort(struct hstruct *array, int nitems, int (*cmp) (struct hstruct *, struct hstruct *), int begin, int end)
94 {
95 if (end > begin) {
96 int l = begin + 1;
97 int r = end;
98 while (l < r) {
99 int pivot = begin;
100 if (cmp(&array[l], &array[pivot]) <= 0) {
101 l += 1;
102 } else {
103 r -= 1;
104 swap(&array[l], &array[r]);
105 }
106 }
107 l -= 1;
108 swap(&array[begin], &array[l]);
109 sort(array, nitems, cmp, begin, l);
110 sort(array, nitems, cmp, r, end);
111 }
112 }
113
114 static void
115 msort(struct hstruct *array, size_t nitems, int (*cmp) (struct hstruct *, struct hstruct *))
116 {
117 sort(array, (int)nitems, cmp, 0, (int)(nitems - 1));
118 }
119
120 static int
121 compare_hosts(struct hstruct *host1, struct hstruct *host2)
122 {
123 /*
124 *
125 * The comparison function must return an integer less than, equal to,
126 * or greater than zero if the first argument is considered to be
127 * respectively less than, equal to, or greater than the second.
128 */
129 if ((host1->priority < host2->priority) && (host1->priority != -1))
130 return -1;
131 if ((host1->priority < host2->priority) && (host1->priority == -1))
132 return 1;
133 if ((host1->priority > host2->priority) && (host2->priority != -1))
134 return 1;
135 if ((host1->priority > host2->priority) && (host2->priority == -1))
136 return -1;
137 if (host1->priority == host2->priority) {
138 if (host1->weight > host2->weight)
139 return -1;
140 if (host1->weight < host2->weight)
141 return 1;
142 }
143 return 0;
144 }
145
146 size_t
147 free_hostname_list(struct hstruct **hlist, size_t nhosts)
148 {
149 struct hstruct *hp = NULL;
150 size_t i;
151
152 hp = *hlist;
153 for (i = 0; i < nhosts; ++i) {
154 xfree(hp[i].host);
155 }
156
157 safe_free(hp);
158 *hlist = hp;
159 return 0;
160 }
161
162 size_t
163 get_hostname_list(struct hstruct **hlist, size_t nhosts, char *name)
164 {
165 struct addrinfo *hres = NULL, *hres_list;
166 int rc, count;
167 struct hstruct *hp = NULL;
168
169 if (!name)
170 return (nhosts);
171
172 hp = *hlist;
173 rc = getaddrinfo((const char *) name, NULL, NULL, &hres);
174 if (rc != 0) {
175 error((char *) "%s| %s: ERROR: Error while resolving hostname with getaddrinfo: %s\n", LogTime(), PROGRAM, gai_strerror(rc));
176 return (nhosts);
177 }
178 hres_list = hres;
179 count = 0;
180 while (hres_list) {
181 ++count;
182 hres_list = hres_list->ai_next;
183 }
184 hres_list = hres;
185 count = 0;
186 while (hres_list) {
187 /*
188 * char host[sysconf(_SC_HOST_NAME_MAX)];
189 */
190 char host[1024];
191 rc = getnameinfo(hres_list->ai_addr, hres_list->ai_addrlen, host, sizeof(host), NULL, 0, 0);
192 if (rc != 0) {
193 error((char *) "%s| %s: ERROR: Error while resolving ip address with getnameinfo: %s\n", LogTime(), PROGRAM, gai_strerror(rc));
194 freeaddrinfo(hres);
195 *hlist = hp;
196 return (nhosts);
197 }
198 ++count;
199 debug((char *) "%s| %s: DEBUG: Resolved address %d of %s to %s\n", LogTime(), PROGRAM, count, name, host);
200
201 hp = (struct hstruct *) xrealloc(hp, sizeof(struct hstruct) * (nhosts + 1));
202 hp[nhosts].host = xstrdup(host);
203 hp[nhosts].port = -1;
204 hp[nhosts].priority = -1;
205 hp[nhosts].weight = -1;
206 ++nhosts;
207
208 hres_list = hres_list->ai_next;
209 }
210
211 freeaddrinfo(hres);
212 *hlist = hp;
213 return (nhosts);
214 }
215
216 size_t
217 get_ldap_hostname_list(struct main_args *margs, struct hstruct **hlist, size_t nh, char *domain)
218 {
219
220 /*
221 * char name[sysconf(_SC_HOST_NAME_MAX)];
222 */
223 char name[1024];
224 char *service = NULL;
225 struct hstruct *hp = NULL;
226 struct lsstruct *ls = NULL;
227 size_t nhosts = 0;
228 int size;
229 int len, olen;
230 size_t i, j, k;
231 u_char *buffer = NULL;
232 u_char *p;
233
234 ls = margs->lservs;
235 while (ls) {
236 debug((char *) "%s| %s: DEBUG: Ldap server loop: lserver@domain %s@%s\n", LogTime(), PROGRAM, ls->lserver, ls->domain?ls->domain:"NULL");
237 if (ls->domain && !strcasecmp(ls->domain, domain)) {
238 debug((char *) "%s| %s: DEBUG: Found lserver@domain %s@%s\n", LogTime(), PROGRAM, ls->lserver, ls->domain);
239 hp = (struct hstruct *) xrealloc(hp, sizeof(struct hstruct) * (nhosts + 1));
240 hp[nhosts].host = xstrdup(ls->lserver);
241 hp[nhosts].port = -1;
242 hp[nhosts].priority = -2;
243 hp[nhosts].weight = -2;
244 ++nhosts;
245 } else if ( !ls->domain || !strcasecmp(ls->domain, "") ) {
246 debug((char *) "%s| %s: DEBUG: Found lserver@domain %s@%s\n", LogTime(), PROGRAM, ls->lserver, ls->domain?ls->domain:"NULL");
247 hp = (struct hstruct *) xrealloc(hp, sizeof(struct hstruct) * (nhosts + 1));
248 hp[nhosts].host = xstrdup(ls->lserver);
249 hp[nhosts].port = -1;
250 hp[nhosts].priority = -2;
251 hp[nhosts].weight = -2;
252 ++nhosts;
253
254 }
255 ls = ls->next;
256 }
257 /* found ldap servers in predefined list -> exit */
258 if (nhosts > 0)
259 goto cleanup;
260
261 if (margs->ssl) {
262 service = (char *) xmalloc(strlen("_ldaps._tcp.") + strlen(domain) + 1);
263 strcpy(service, "_ldaps._tcp.");
264 } else {
265 service = (char *) xmalloc(strlen("_ldap._tcp.") + strlen(domain) + 1);
266 strcpy(service, "_ldap._tcp.");
267 }
268 strcat(service, domain);
269
270 #ifndef PACKETSZ_MULT
271 /*
272 * It seems Solaris doesn't give back the real length back when res_search uses a to small buffer
273 * Set a bigger one here
274 */
275 #define PACKETSZ_MULT 10
276 #endif
277
278 hp = *hlist;
279 buffer = (u_char *) xmalloc(PACKETSZ_MULT * NS_PACKETSZ);
280 if ((len = res_search(service, ns_c_in, ns_t_srv, (u_char *) buffer, PACKETSZ_MULT * NS_PACKETSZ)) < 0) {
281 error((char *) "%s| %s: ERROR: Error while resolving service record %s with res_search\n", LogTime(), PROGRAM, service);
282 nsError(h_errno, service);
283 if (margs->ssl) {
284 xfree(service);
285 service = (char *) xmalloc(strlen("_ldap._tcp.") + strlen(domain) + 1);
286 strcpy(service, "_ldap._tcp.");
287 strcat(service, domain);
288 if ((len = res_search(service, ns_c_in, ns_t_srv, (u_char *) buffer, PACKETSZ_MULT * NS_PACKETSZ)) < 0) {
289 error((char *) "%s| %s: ERROR: Error while resolving service record %s with res_search\n", LogTime(), PROGRAM, service);
290 nsError(h_errno, service);
291 goto finalise;
292 }
293 } else {
294 goto finalise;
295 }
296 }
297 if (len > PACKETSZ_MULT * NS_PACKETSZ) {
298 olen = len;
299 buffer = (u_char *) xrealloc(buffer, (size_t)len);
300 if ((len = res_search(service, ns_c_in, ns_t_srv, (u_char *) buffer, len)) < 0) {
301 error((char *) "%s| %s: ERROR: Error while resolving service record %s with res_search\n", LogTime(), PROGRAM, service);
302 nsError(h_errno, service);
303 goto finalise;
304 }
305 if (len > olen) {
306 error((char *) "%s| %s: ERROR: Reply to big: buffer: %d reply length: %d\n", LogTime(), PROGRAM, olen, len);
307 goto finalise;
308 }
309 }
310 p = buffer;
311 p += 6 * NS_INT16SZ; /* Header(6*16bit) = id + flags + 4*section count */
312 if (p > buffer + len) {
313 error((char *) "%s| %s: ERROR: Message to small: %d < header size\n", LogTime(), PROGRAM, len);
314 goto finalise;
315 }
316 if ((size = dn_expand(buffer, buffer + len, p, name, sizeof(name))) < 0) {
317 error((char *) "%s| %s: ERROR: Error while expanding query name with dn_expand: %s\n", LogTime(), PROGRAM, strerror(errno));
318 goto finalise;
319 }
320 p += size; /* Query name */
321 p += 2 * NS_INT16SZ; /* Query type + class (2*16bit) */
322 if (p > buffer + len) {
323 error((char *) "%s| %s: ERROR: Message to small: %d < header + query name,type,class \n", LogTime(), PROGRAM, len);
324 goto finalise;
325 }
326 while (p < buffer + len) {
327 int type, rdlength;
328 if ((size = dn_expand(buffer, buffer + len, p, name, sizeof(name))) < 0) {
329 error((char *) "%s| %s: ERROR: Error while expanding answer name with dn_expand: %s\n", LogTime(), PROGRAM, strerror(errno));
330 goto finalise;
331 }
332 p += size; /* Resource Record name */
333 if (p > buffer + len) {
334 error((char *) "%s| %s: ERROR: Message to small: %d < header + query name,type,class + answer name\n", LogTime(), PROGRAM, len);
335 goto finalise;
336 }
337 NS_GET16(type, p); /* RR type (16bit) */
338 p += NS_INT16SZ + NS_INT32SZ; /* RR class + ttl (16bit+32bit) */
339 if (p > buffer + len) {
340 error((char *) "%s| %s: ERROR: Message to small: %d < header + query name,type,class + answer name + RR type,class,ttl\n", LogTime(), PROGRAM, len);
341 goto finalise;
342 }
343 NS_GET16(rdlength, p); /* RR data length (16bit) */
344
345 if (type == ns_t_srv) { /* SRV record */
346 int priority, weight, port;
347 char host[NS_MAXDNAME];
348 if (p > buffer + len) {
349 error((char *) "%s| %s: ERROR: Message to small: %d < header + query name,type,class + answer name + RR type,class,ttl + RR data length\n", LogTime(), PROGRAM, len);
350 goto finalise;
351 }
352 NS_GET16(priority, p); /* Priority (16bit) */
353 if (p > buffer + len) {
354 error((char *) "%s| %s: ERROR: Message to small: %d < SRV RR + priority\n", LogTime(), PROGRAM, len);
355 goto finalise;
356 }
357 NS_GET16(weight, p); /* Weight (16bit) */
358 if (p > buffer + len) {
359 error((char *) "%s| %s: ERROR: Message to small: %d < SRV RR + priority + weight\n", LogTime(), PROGRAM, len);
360 goto finalise;
361 }
362 NS_GET16(port, p); /* Port (16bit) */
363 if (p > buffer + len) {
364 error((char *) "%s| %s: ERROR: Message to small: %d < SRV RR + priority + weight + port\n", LogTime(), PROGRAM, len);
365 goto finalise;
366 }
367 if ((size = dn_expand(buffer, buffer + len, p, host, NS_MAXDNAME)) < 0) {
368 error((char *) "%s| %s: ERROR: Error while expanding SRV RR name with dn_expand: %s\n", LogTime(), PROGRAM, strerror(errno));
369 goto finalise;
370 }
371 debug((char *) "%s| %s: DEBUG: Resolved SRV %s record to %s\n", LogTime(), PROGRAM, service, host);
372 hp = (struct hstruct *) xrealloc(hp, sizeof(struct hstruct) * (nh + 1));
373 hp[nh].host = xstrdup(host);
374 hp[nh].port = port;
375 hp[nh].priority = priority;
376 hp[nh].weight = weight;
377 ++nh;
378 p += size;
379 } else {
380 p += rdlength;
381 }
382 if (p > buffer + len) {
383 error((char *) "%s| %s: ERROR: Message to small: %d < SRV RR + priority + weight + port + name\n", LogTime(), PROGRAM, len);
384 goto finalise;
385 }
386 }
387 if (p != buffer + len) {
388 #if (SIZEOF_LONG == 8)
389 error("%s| %s: ERROR: Inconsistence message length: %ld!=0\n", LogTime(), PROGRAM, buffer + len - p);
390 #else
391 error((char *) "%s| %s: ERROR: Inconsistence message length: %d!=0\n", LogTime(), PROGRAM, buffer + len - p);
392 #endif
393 goto finalise;
394 }
395
396 finalise:
397 nhosts = get_hostname_list(&hp, nh, domain);
398
399 debug("%s| %s: DEBUG: Adding %s to list\n", LogTime(), PROGRAM, domain);
400
401 hp = (struct hstruct *) xrealloc(hp, sizeof(struct hstruct) * (nhosts + 1));
402 hp[nhosts].host = xstrdup(domain);
403 hp[nhosts].port = -1;
404 hp[nhosts].priority = -2;
405 hp[nhosts].weight = -2;
406 ++nhosts;
407
408 cleanup:
409 /* Remove duplicates */
410 for (i = 0; i < nhosts; ++i) {
411 for (j = i + 1; j < nhosts; ++j) {
412 if (!strcasecmp(hp[i].host, hp[j].host)) {
413 if (hp[i].port == hp[j].port ||
414 (hp[i].port == -1 && hp[j].port == 389) ||
415 (hp[i].port == 389 && hp[j].port == -1)) {
416 xfree(hp[j].host);
417 for (k = j + 1; k < nhosts; ++k) {
418 hp[k - 1].host = hp[k].host;
419 hp[k - 1].port = hp[k].port;
420 hp[k - 1].priority = hp[k].priority;
421 hp[k - 1].weight = hp[k].weight;
422 }
423 --j;
424 --nhosts;
425 hp = (struct hstruct *) xrealloc(hp, sizeof(struct hstruct) * (nhosts + 1));
426 }
427 }
428 }
429 }
430
431 /* Sort by Priority / Weight */
432 msort(hp, (size_t)nhosts, compare_hosts);
433
434 if (debug_enabled) {
435 debug((char *) "%s| %s: DEBUG: Sorted ldap server names for domain %s:\n", LogTime(), PROGRAM, domain);
436 for (i = 0; i < nhosts; ++i) {
437 debug((char *) "%s| %s: DEBUG: Host: %s Port: %d Priority: %d Weight: %d\n", LogTime(), PROGRAM, hp[i].host, hp[i].port, hp[i].priority, hp[i].weight);
438 }
439 }
440 xfree(buffer);
441 xfree(service);
442 *hlist = hp;
443 return (nhosts);
444 }
445 #endif