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