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