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

 

Introduction

Documentation

Support

Miscellaneous