]> git.ipfire.org Git - thirdparty/squid.git/blob - src/external_acl.cc
SMP Cache Manager, Phase2 implementation.
[thirdparty/squid.git] / src / external_acl.cc
1
2 /*
3 * $Id$
4 *
5 * DEBUG: section 82 External ACL
6 * AUTHOR: Henrik Nordstrom, MARA Systems AB
7 *
8 * SQUID Web Proxy Cache http://www.squid-cache.org/
9 * ----------------------------------------------------------
10 *
11 * The contents of this file is Copyright (C) 2002 by MARA Systems AB,
12 * Sweden, unless otherwise is indicated in the specific function. The
13 * author gives his full permission to include this file into the Squid
14 * software product under the terms of the GNU General Public License as
15 * published by the Free Software Foundation; either version 2 of the
16 * License, or (at your option) any later version.
17 *
18 * Squid is the result of efforts by numerous individuals from
19 * the Internet community; see the CONTRIBUTORS file for full
20 * details. Many organizations have provided support for Squid's
21 * development; see the SPONSORS file for full details. Squid is
22 * Copyrighted (C) 2001 by the Regents of the University of
23 * California; see the COPYRIGHT file for full details. Squid
24 * incorporates software developed and/or copyrighted by other
25 * sources; see the CREDITS file for full details.
26 *
27 * This program is free software; you can redistribute it and/or modify
28 * it under the terms of the GNU General Public License as published by
29 * the Free Software Foundation; either version 2 of the License, or
30 * (at your option) any later version.
31 *
32 * This program is distributed in the hope that it will be useful,
33 * but WITHOUT ANY WARRANTY; without even the implied warranty of
34 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
35 * GNU General Public License for more details.
36 *
37 * You should have received a copy of the GNU General Public License
38 * along with this program; if not, write to the Free Software
39 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
40 *
41 */
42
43 #include "squid.h"
44 #include "mgr/Registration.h"
45 #include "ExternalACL.h"
46 #include "ExternalACLEntry.h"
47 #include "auth/UserRequest.h"
48 #include "SquidTime.h"
49 #include "Store.h"
50 #include "fde.h"
51 #include "acl/FilledChecklist.h"
52 #include "acl/Acl.h"
53 #if USE_IDENT
54 #include "ident/AclIdent.h"
55 #endif
56 #include "ip/tools.h"
57 #include "client_side.h"
58 #include "HttpRequest.h"
59 #include "HttpReply.h"
60 #include "auth/Acl.h"
61 #include "auth/Gadgets.h"
62 #include "helper.h"
63 #include "MemBuf.h"
64 #include "rfc1738.h"
65 #include "URLScheme.h"
66 #include "wordlist.h"
67
68 #ifndef DEFAULT_EXTERNAL_ACL_TTL
69 #define DEFAULT_EXTERNAL_ACL_TTL 1 * 60 * 60
70 #endif
71 #ifndef DEFAULT_EXTERNAL_ACL_CHILDREN
72 #define DEFAULT_EXTERNAL_ACL_CHILDREN 5
73 #endif
74
75 typedef struct _external_acl_format external_acl_format;
76
77 static char *makeExternalAclKey(ACLFilledChecklist * ch, external_acl_data * acl_data);
78 static void external_acl_cache_delete(external_acl * def, external_acl_entry * entry);
79 static int external_acl_entry_expired(external_acl * def, external_acl_entry * entry);
80 static int external_acl_grace_expired(external_acl * def, external_acl_entry * entry);
81 static void external_acl_cache_touch(external_acl * def, external_acl_entry * entry);
82 static external_acl_entry *external_acl_cache_add(external_acl * def, const char *key, ExternalACLEntryData const &data);
83
84 /******************************************************************
85 * external_acl directive
86 */
87
88 class external_acl
89 {
90
91 public:
92 external_acl *next;
93
94 void add
95 (ExternalACLEntry *);
96
97 void trimCache();
98
99 int ttl;
100
101 int negative_ttl;
102
103 int grace;
104
105 char *name;
106
107 external_acl_format *format;
108
109 wordlist *cmdline;
110
111 HelperChildConfig children;
112
113 helper *theHelper;
114
115 hash_table *cache;
116
117 dlink_list lru_list;
118
119 int cache_size;
120
121 int cache_entries;
122
123 dlink_list queue;
124
125 /**
126 * Configuration flag. May only be altered by the configuration parser.
127 *
128 * Indicates that all uses of this external_acl_type helper require authentication
129 * details to be processed. If none are available its a fail match.
130 */
131 bool require_auth;
132
133 enum {
134 QUOTE_METHOD_SHELL = 1,
135 QUOTE_METHOD_URL
136 } quote;
137
138 Ip::Address local_addr;
139 };
140
141 struct _external_acl_format {
142 enum format_type {
143 EXT_ACL_UNKNOWN,
144 EXT_ACL_LOGIN,
145 #if USE_IDENT
146 EXT_ACL_IDENT,
147 #endif
148 EXT_ACL_SRC,
149 EXT_ACL_SRCPORT,
150 #if USE_SQUID_EUI
151 EXT_ACL_SRCEUI48,
152 EXT_ACL_SRCEUI64,
153 #endif
154 EXT_ACL_MYADDR,
155 EXT_ACL_MYPORT,
156 EXT_ACL_URI,
157 EXT_ACL_DST,
158 EXT_ACL_PROTO,
159 EXT_ACL_PORT,
160 EXT_ACL_PATH,
161 EXT_ACL_METHOD,
162
163 EXT_ACL_HEADER_REQUEST,
164 EXT_ACL_HEADER_REQUEST_MEMBER,
165 EXT_ACL_HEADER_REQUEST_ID,
166 EXT_ACL_HEADER_REQUEST_ID_MEMBER,
167
168 EXT_ACL_HEADER_REPLY,
169 EXT_ACL_HEADER_REPLY_MEMBER,
170 EXT_ACL_HEADER_REPLY_ID,
171 EXT_ACL_HEADER_REPLY_ID_MEMBER,
172
173 #if USE_SSL
174 EXT_ACL_USER_CERT,
175 EXT_ACL_CA_CERT,
176 EXT_ACL_USER_CERT_RAW,
177 EXT_ACL_USER_CERTCHAIN_RAW,
178 #endif
179 EXT_ACL_EXT_USER,
180 EXT_ACL_END
181 } type;
182 external_acl_format *next;
183 char *header;
184 char *member;
185 char separator;
186 http_hdr_type header_id;
187 };
188
189 /* FIXME: These are not really cbdata, but it is an easy way
190 * to get them pooled, refcounted, accounted and freed properly...
191 */
192 CBDATA_TYPE(external_acl);
193 CBDATA_TYPE(external_acl_format);
194
195 static void
196 free_external_acl_format(void *data)
197 {
198 external_acl_format *p = static_cast<external_acl_format *>(data);
199 safe_free(p->header);
200 }
201
202 static void
203 free_external_acl(void *data)
204 {
205 external_acl *p = static_cast<external_acl *>(data);
206 safe_free(p->name);
207
208 while (p->format) {
209 external_acl_format *f = p->format;
210 p->format = f->next;
211 cbdataFree(f);
212 }
213
214 wordlistDestroy(&p->cmdline);
215
216 if (p->theHelper) {
217 helperShutdown(p->theHelper);
218 delete p->theHelper;
219 p->theHelper = NULL;
220 }
221
222 while (p->lru_list.tail)
223 external_acl_cache_delete(p, static_cast<external_acl_entry *>(p->lru_list.tail->data));
224 if (p->cache)
225 hashFreeMemory(p->cache);
226 }
227
228 /**
229 * Parse the External ACL format %<{.*} and %>{.*} token(s) to pass a specific
230 * request or reply header to external helper.
231 *
232 \param header - the token being parsed (without the identifying prefix)
233 \param type - format enum identifier for this element, pulled from identifying prefix
234 \param format - structure to contain all the info about this format element.
235 */
236 void
237 parse_header_token(external_acl_format *format, char *header, const _external_acl_format::format_type type)
238 {
239 /* header format */
240 char *member, *end;
241
242 /** Cut away the closing brace */
243 end = strchr(header, '}');
244 if (end && strlen(end) == 1)
245 *end = '\0';
246 else
247 self_destruct();
248
249 member = strchr(header, ':');
250
251 if (member) {
252 /* Split in header and member */
253 *member++ = '\0';
254
255 if (!xisalnum(*member))
256 format->separator = *member++;
257 else
258 format->separator = ',';
259
260 format->member = xstrdup(member);
261
262 if (type == _external_acl_format::EXT_ACL_HEADER_REQUEST)
263 format->type = _external_acl_format::EXT_ACL_HEADER_REQUEST_MEMBER;
264 else
265 format->type = _external_acl_format::EXT_ACL_HEADER_REQUEST_MEMBER;
266 } else {
267 format->type = type;
268 }
269
270 format->header = xstrdup(header);
271 format->header_id = httpHeaderIdByNameDef(header, strlen(header));
272
273 if (format->header_id != -1) {
274 if (member) {
275 if (type == _external_acl_format::EXT_ACL_HEADER_REQUEST)
276 format->type = _external_acl_format::EXT_ACL_HEADER_REQUEST_ID_MEMBER;
277 else
278 format->type = _external_acl_format::EXT_ACL_HEADER_REPLY_ID_MEMBER;
279 } else {
280 if (type == _external_acl_format::EXT_ACL_HEADER_REQUEST)
281 format->type = _external_acl_format::EXT_ACL_HEADER_REQUEST_ID;
282 else
283 format->type = _external_acl_format::EXT_ACL_HEADER_REPLY_ID;
284 }
285 }
286 }
287
288 void
289 parse_externalAclHelper(external_acl ** list)
290 {
291 external_acl *a;
292 char *token;
293 external_acl_format **p;
294
295 CBDATA_INIT_TYPE_FREECB(external_acl, free_external_acl);
296 CBDATA_INIT_TYPE_FREECB(external_acl_format, free_external_acl_format);
297
298 a = cbdataAlloc(external_acl);
299
300 /* set defaults */
301 a->ttl = DEFAULT_EXTERNAL_ACL_TTL;
302 a->negative_ttl = -1;
303 a->cache_size = 256*1024;
304 a->children.n_max = DEFAULT_EXTERNAL_ACL_CHILDREN;
305 a->children.n_startup = a->children.n_max;
306 a->children.n_idle = 1;
307 a->local_addr.SetLocalhost();
308 a->quote = external_acl::QUOTE_METHOD_URL;
309
310 token = strtok(NULL, w_space);
311
312 if (!token)
313 self_destruct();
314
315 a->name = xstrdup(token);
316
317 token = strtok(NULL, w_space);
318
319 /* Parse options */
320 while (token) {
321 if (strncmp(token, "ttl=", 4) == 0) {
322 a->ttl = atoi(token + 4);
323 } else if (strncmp(token, "negative_ttl=", 13) == 0) {
324 a->negative_ttl = atoi(token + 13);
325 } else if (strncmp(token, "children=", 9) == 0) {
326 a->children.n_max = atoi(token + 9);
327 debugs(0, 0, "WARNING: external_acl_type option children=N has been deprecated in favor of children-max=N and children-startup=N");
328 } else if (strncmp(token, "children-max=", 13) == 0) {
329 a->children.n_max = atoi(token + 13);
330 } else if (strncmp(token, "children-startup=", 17) == 0) {
331 a->children.n_startup = atoi(token + 17);
332 } else if (strncmp(token, "children-idle=", 14) == 0) {
333 a->children.n_idle = atoi(token + 14);
334 } else if (strncmp(token, "concurrency=", 12) == 0) {
335 a->children.concurrency = atoi(token + 12);
336 } else if (strncmp(token, "cache=", 6) == 0) {
337 a->cache_size = atoi(token + 6);
338 } else if (strncmp(token, "grace=", 6) == 0) {
339 a->grace = atoi(token + 6);
340 } else if (strcmp(token, "protocol=2.5") == 0) {
341 a->quote = external_acl::QUOTE_METHOD_SHELL;
342 } else if (strcmp(token, "protocol=3.0") == 0) {
343 a->quote = external_acl::QUOTE_METHOD_URL;
344 } else if (strcmp(token, "quote=url") == 0) {
345 a->quote = external_acl::QUOTE_METHOD_URL;
346 } else if (strcmp(token, "quote=shell") == 0) {
347 a->quote = external_acl::QUOTE_METHOD_SHELL;
348
349 /* INET6: allow admin to configure some helpers explicitly to
350 bind to IPv4/v6 localhost port. */
351 } else if (strcmp(token, "ipv4") == 0) {
352 if ( !a->local_addr.SetIPv4() ) {
353 debugs(3, 0, "WARNING: Error converting " << a->local_addr << " to IPv4 in " << a->name );
354 }
355 } else if (strcmp(token, "ipv6") == 0) {
356 if (!Ip::EnableIpv6)
357 debugs(3, 0, "WARNING: --enable-ipv6 required for external ACL helpers to use IPv6: " << a->name );
358 // else nothing to do.
359 } else {
360 break;
361 }
362
363 token = strtok(NULL, w_space);
364 }
365
366 /* check that child startup value is sane. */
367 if (a->children.n_startup > a->children.n_max)
368 a->children.n_startup = a->children.n_max;
369
370 /* check that child idle value is sane. */
371 if (a->children.n_idle > a->children.n_max)
372 a->children.n_idle = a->children.n_max;
373 if (a->children.n_idle < 1)
374 a->children.n_idle = 1;
375
376 if (a->negative_ttl == -1)
377 a->negative_ttl = a->ttl;
378
379 /* Parse format */
380 p = &a->format;
381
382 while (token) {
383 external_acl_format *format;
384
385 /* stop on first non-format token found */
386
387 if (*token != '%')
388 break;
389
390 format = cbdataAlloc(external_acl_format);
391
392 if (strncmp(token, "%{", 2) == 0) {
393 // deprecated. but assume the old configs all referred to request headers.
394 debugs(82, DBG_IMPORTANT, "WARNING: external_acl_type format %{...} is being replaced by %>{...} for : " << token);
395 parse_header_token(format, (token+2), _external_acl_format::EXT_ACL_HEADER_REQUEST);
396 } else if (strncmp(token, "%>{", 3) == 0) {
397 parse_header_token(format, (token+3), _external_acl_format::EXT_ACL_HEADER_REQUEST);
398 } else if (strncmp(token, "%<{", 3) == 0) {
399 parse_header_token(format, (token+3), _external_acl_format::EXT_ACL_HEADER_REPLY);
400 } else if (strcmp(token, "%LOGIN") == 0) {
401 format->type = _external_acl_format::EXT_ACL_LOGIN;
402 a->require_auth = true;
403 }
404
405 #if USE_IDENT
406 else if (strcmp(token, "%IDENT") == 0)
407 format->type = _external_acl_format::EXT_ACL_IDENT;
408
409 #endif
410
411 else if (strcmp(token, "%SRC") == 0)
412 format->type = _external_acl_format::EXT_ACL_SRC;
413 else if (strcmp(token, "%SRCPORT") == 0)
414 format->type = _external_acl_format::EXT_ACL_SRCPORT;
415 #if USE_SQUID_EUI
416 else if (strcmp(token, "%SRCEUI48") == 0)
417 format->type = _external_acl_format::EXT_ACL_SRCEUI48;
418 else if (strcmp(token, "%SRCEUI64") == 0)
419 format->type = _external_acl_format::EXT_ACL_SRCEUI64;
420 #endif
421 else if (strcmp(token, "%MYADDR") == 0)
422 format->type = _external_acl_format::EXT_ACL_MYADDR;
423 else if (strcmp(token, "%MYPORT") == 0)
424 format->type = _external_acl_format::EXT_ACL_MYPORT;
425 else if (strcmp(token, "%URI") == 0)
426 format->type = _external_acl_format::EXT_ACL_URI;
427 else if (strcmp(token, "%DST") == 0)
428 format->type = _external_acl_format::EXT_ACL_DST;
429 else if (strcmp(token, "%PROTO") == 0)
430 format->type = _external_acl_format::EXT_ACL_PROTO;
431 else if (strcmp(token, "%PORT") == 0)
432 format->type = _external_acl_format::EXT_ACL_PORT;
433 else if (strcmp(token, "%PATH") == 0)
434 format->type = _external_acl_format::EXT_ACL_PATH;
435 else if (strcmp(token, "%METHOD") == 0)
436 format->type = _external_acl_format::EXT_ACL_METHOD;
437
438 #if USE_SSL
439 else if (strcmp(token, "%USER_CERT") == 0)
440 format->type = _external_acl_format::EXT_ACL_USER_CERT_RAW;
441 else if (strcmp(token, "%USER_CERTCHAIN") == 0)
442 format->type = _external_acl_format::EXT_ACL_USER_CERTCHAIN_RAW;
443 else if (strncmp(token, "%USER_CERT_", 11) == 0) {
444 format->type = _external_acl_format::EXT_ACL_USER_CERT;
445 format->header = xstrdup(token + 11);
446 } else if (strncmp(token, "%CA_CERT_", 11) == 0) {
447 format->type = _external_acl_format::EXT_ACL_USER_CERT;
448 format->header = xstrdup(token + 11);
449 }
450 #endif
451 else if (strcmp(token, "%EXT_USER") == 0)
452 format->type = _external_acl_format::EXT_ACL_EXT_USER;
453 else {
454 debugs(0,0, "ERROR: Unknown Format token " << token);
455 self_destruct();
456 }
457
458 *p = format;
459 p = &format->next;
460 token = strtok(NULL, w_space);
461 }
462
463 /* There must be at least one format token */
464 if (!a->format)
465 self_destruct();
466
467 /* helper */
468 if (!token)
469 self_destruct();
470
471 wordlistAdd(&a->cmdline, token);
472
473 /* arguments */
474 parse_wordlist(&a->cmdline);
475
476 while (*list)
477 list = &(*list)->next;
478
479 *list = a;
480 }
481
482 void
483 dump_externalAclHelper(StoreEntry * sentry, const char *name, const external_acl * list)
484 {
485 const external_acl *node;
486 const external_acl_format *format;
487 const wordlist *word;
488
489 for (node = list; node; node = node->next) {
490 storeAppendPrintf(sentry, "%s %s", name, node->name);
491
492 if (!node->local_addr.IsIPv6())
493 storeAppendPrintf(sentry, " ipv4");
494 else
495 storeAppendPrintf(sentry, " ipv6");
496
497 if (node->ttl != DEFAULT_EXTERNAL_ACL_TTL)
498 storeAppendPrintf(sentry, " ttl=%d", node->ttl);
499
500 if (node->negative_ttl != node->ttl)
501 storeAppendPrintf(sentry, " negative_ttl=%d", node->negative_ttl);
502
503 if (node->grace)
504 storeAppendPrintf(sentry, " grace=%d", node->grace);
505
506 if (node->children.n_max != DEFAULT_EXTERNAL_ACL_CHILDREN)
507 storeAppendPrintf(sentry, " children-max=%d", node->children.n_max);
508
509 if (node->children.n_startup != 1)
510 storeAppendPrintf(sentry, " children-startup=%d", node->children.n_startup);
511
512 if (node->children.n_idle != (node->children.n_max + node->children.n_startup) )
513 storeAppendPrintf(sentry, " children-idle=%d", node->children.n_idle);
514
515 if (node->children.concurrency)
516 storeAppendPrintf(sentry, " concurrency=%d", node->children.concurrency);
517
518 if (node->cache)
519 storeAppendPrintf(sentry, " cache=%d", node->cache_size);
520
521 for (format = node->format; format; format = format->next) {
522 switch (format->type) {
523
524 case _external_acl_format::EXT_ACL_HEADER_REQUEST:
525 case _external_acl_format::EXT_ACL_HEADER_REQUEST_ID:
526 storeAppendPrintf(sentry, " %%>{%s}", format->header);
527 break;
528
529 case _external_acl_format::EXT_ACL_HEADER_REQUEST_MEMBER:
530 case _external_acl_format::EXT_ACL_HEADER_REQUEST_ID_MEMBER:
531 storeAppendPrintf(sentry, " %%>{%s:%s}", format->header, format->member);
532 break;
533
534 case _external_acl_format::EXT_ACL_HEADER_REPLY:
535 case _external_acl_format::EXT_ACL_HEADER_REPLY_ID:
536 storeAppendPrintf(sentry, " %%<{%s}", format->header);
537 break;
538
539 case _external_acl_format::EXT_ACL_HEADER_REPLY_MEMBER:
540 case _external_acl_format::EXT_ACL_HEADER_REPLY_ID_MEMBER:
541 storeAppendPrintf(sentry, " %%<{%s:%s}", format->header, format->member);
542 break;
543 #define DUMP_EXT_ACL_TYPE(a) \
544 case _external_acl_format::EXT_ACL_##a: \
545 storeAppendPrintf(sentry, " %%%s", #a); \
546 break
547
548 DUMP_EXT_ACL_TYPE(LOGIN);
549 #if USE_IDENT
550
551 DUMP_EXT_ACL_TYPE(IDENT);
552 #endif
553
554 DUMP_EXT_ACL_TYPE(SRC);
555 DUMP_EXT_ACL_TYPE(SRCPORT);
556 #if USE_SQUID_EUI
557 DUMP_EXT_ACL_TYPE(SRCEUI48);
558 DUMP_EXT_ACL_TYPE(SRCEUI64);
559 #endif
560
561 DUMP_EXT_ACL_TYPE(MYADDR);
562 DUMP_EXT_ACL_TYPE(MYPORT);
563 DUMP_EXT_ACL_TYPE(URI);
564 DUMP_EXT_ACL_TYPE(DST);
565 DUMP_EXT_ACL_TYPE(PROTO);
566 DUMP_EXT_ACL_TYPE(PORT);
567 DUMP_EXT_ACL_TYPE(PATH);
568 DUMP_EXT_ACL_TYPE(METHOD);
569 #if USE_SSL
570
571 case _external_acl_format::EXT_ACL_USER_CERT_RAW:
572 storeAppendPrintf(sentry, " %%USER_CERT");
573 break;
574
575 case _external_acl_format::EXT_ACL_USER_CERTCHAIN_RAW:
576 storeAppendPrintf(sentry, " %%USER_CERTCHAIN");
577 break;
578
579 case _external_acl_format::EXT_ACL_USER_CERT:
580 storeAppendPrintf(sentry, " %%USER_CERT_%s", format->header);
581 break;
582
583 case _external_acl_format::EXT_ACL_CA_CERT:
584 storeAppendPrintf(sentry, " %%USER_CERT_%s", format->header);
585 break;
586 #endif
587
588 DUMP_EXT_ACL_TYPE(EXT_USER);
589
590 default:
591 fatal("unknown external_acl format error");
592 break;
593 }
594 }
595
596 for (word = node->cmdline; word; word = word->next)
597 storeAppendPrintf(sentry, " %s", word->key);
598
599 storeAppendPrintf(sentry, "\n");
600 }
601 }
602
603 void
604 free_externalAclHelper(external_acl ** list)
605 {
606 while (*list) {
607 external_acl *node = *list;
608 *list = node->next;
609 node->next = NULL;
610 cbdataFree(node);
611 }
612 }
613
614 static external_acl *
615 find_externalAclHelper(const char *name)
616 {
617 external_acl *node;
618
619 for (node = Config.externalAclHelperList; node; node = node->next) {
620 if (strcmp(node->name, name) == 0)
621 return node;
622 }
623
624 return NULL;
625 }
626
627 void
628
629 external_acl::add
630 (ExternalACLEntry *anEntry)
631 {
632 trimCache();
633 assert (anEntry->def == NULL);
634 anEntry->def = this;
635 hash_join(cache, anEntry);
636 dlinkAdd(anEntry, &anEntry->lru, &lru_list);
637 cache_entries++;
638 }
639
640 void
641 external_acl::trimCache()
642 {
643 if (cache_size && cache_entries >= cache_size)
644 external_acl_cache_delete(this, static_cast<external_acl_entry *>(lru_list.tail->data));
645 }
646
647
648 /******************************************************************
649 * external acl type
650 */
651
652 struct _external_acl_data {
653 external_acl *def;
654 wordlist *arguments;
655 };
656
657 CBDATA_TYPE(external_acl_data);
658 static void
659 free_external_acl_data(void *data)
660 {
661 external_acl_data *p = static_cast<external_acl_data *>(data);
662 wordlistDestroy(&p->arguments);
663 cbdataReferenceDone(p->def);
664 }
665
666 void
667 ACLExternal::parse()
668 {
669 char *token;
670
671 if (data)
672 self_destruct();
673
674 CBDATA_INIT_TYPE_FREECB(external_acl_data, free_external_acl_data);
675
676 data = cbdataAlloc(external_acl_data);
677
678 token = strtok(NULL, w_space);
679
680 if (!token)
681 self_destruct();
682
683 data->def = cbdataReference(find_externalAclHelper(token));
684
685 if (!data->def)
686 self_destruct();
687
688 while ((token = strtokFile())) {
689 wordlistAdd(&data->arguments, token);
690 }
691 }
692
693 bool
694 ACLExternal::valid () const
695 {
696 if (data->def->require_auth) {
697 if (authenticateSchemeCount() == 0) {
698 debugs(28, 0, "Can't use proxy auth because no authentication schemes were compiled.");
699 return false;
700 }
701
702 if (authenticateActiveSchemeCount() == 0) {
703 debugs(28, 0, "Can't use proxy auth because no authentication schemes are fully configured.");
704 return false;
705 }
706 }
707
708 return true;
709 }
710
711 bool
712 ACLExternal::empty () const
713 {
714 return false;
715 }
716
717 ACLExternal::~ACLExternal()
718 {
719 cbdataFree(data);
720 safe_free (class_);
721 }
722
723 static int
724 aclMatchExternal(external_acl_data *acl, ACLFilledChecklist *ch)
725 {
726 int result;
727 external_acl_entry *entry;
728 const char *key = "";
729 debugs(82, 9, "aclMatchExternal: acl=\"" << acl->def->name << "\"");
730 entry = ch->extacl_entry;
731
732 if (entry) {
733 if (cbdataReferenceValid(entry) && entry->def == acl->def &&
734 strcmp((char *)entry->key, key) == 0) {
735 /* Ours, use it.. */
736 } else {
737 /* Not valid, or not ours.. get rid of it */
738 cbdataReferenceDone(ch->extacl_entry);
739 entry = NULL;
740 }
741 }
742
743 external_acl_message = "MISSING REQUIRED INFORMATION";
744
745 if (!entry) {
746 if (acl->def->require_auth) {
747 int ti;
748 /* Make sure the user is authenticated */
749 debugs(82, 3, "aclMatchExternal: " << acl->def->name << " check user authenticated.");
750
751 if ((ti = AuthenticateAcl(ch)) != 1) {
752 debugs(82, 2, "aclMatchExternal: " << acl->def->name << " user not authenticated (" << ti << ")");
753 return ti;
754 }
755 debugs(82, 3, "aclMatchExternal: " << acl->def->name << " user is authenticated.");
756 }
757
758 key = makeExternalAclKey(ch, acl);
759
760 if (!key) {
761 /* Not sufficient data to process */
762 return -1;
763 }
764
765 entry = static_cast<external_acl_entry *>(hash_lookup(acl->def->cache, key));
766
767 if (!entry || external_acl_grace_expired(acl->def, entry)) {
768 debugs(82, 2, "aclMatchExternal: " << acl->def->name << "(\"" << key << "\") = lookup needed");
769 debugs(82, 2, "aclMatchExternal: \"" << key << "\": entry=@" <<
770 entry << ", age=" << (entry ? (long int) squid_curtime - entry->date : 0));
771
772 if (acl->def->theHelper->stats.queue_size <= (int)acl->def->theHelper->childs.n_active) {
773 debugs(82, 2, "aclMatchExternal: \"" << key << "\": queueing a call.");
774 ch->changeState (ExternalACLLookup::Instance());
775
776 if (entry == NULL) {
777 debugs(82, 2, "aclMatchExternal: \"" << key << "\": return -1.");
778 return -1;
779 }
780 } else {
781 if (!entry) {
782 debugs(82, 1, "aclMatchExternal: '" << acl->def->name <<
783 "' queue overload. Request rejected '" << key << "'.");
784 external_acl_message = "SYSTEM TOO BUSY, TRY AGAIN LATER";
785 return -1;
786 } else {
787 debugs(82, 1, "aclMatchExternal: '" << acl->def->name <<
788 "' queue overload. Using stale result. '" << key << "'.");
789 /* Fall thru to processing below */
790 }
791 }
792 }
793 }
794
795 external_acl_cache_touch(acl->def, entry);
796 result = entry->result;
797 external_acl_message = entry->message.termedBuf();
798
799 debugs(82, 2, "aclMatchExternal: " << acl->def->name << " = " << result);
800
801 if (ch->request) {
802 if (entry->user.size())
803 ch->request->extacl_user = entry->user;
804
805 if (entry->password.size())
806 ch->request->extacl_passwd = entry->password;
807
808 if (!ch->request->tag.size())
809 ch->request->tag = entry->tag;
810
811 if (entry->log.size())
812 ch->request->extacl_log = entry->log;
813
814 if (entry->message.size())
815 ch->request->extacl_message = entry->message;
816 }
817
818 return result;
819 }
820
821 int
822 ACLExternal::match(ACLChecklist *checklist)
823 {
824 return aclMatchExternal (data, Filled(checklist));
825 }
826
827 wordlist *
828 ACLExternal::dump() const
829 {
830 external_acl_data const *acl = data;
831 wordlist *result = NULL;
832 wordlist *arg;
833 MemBuf mb;
834 mb.init();
835 mb.Printf("%s", acl->def->name);
836
837 for (arg = acl->arguments; arg; arg = arg->next) {
838 mb.Printf(" %s", arg->key);
839 }
840
841 wordlistAdd(&result, mb.buf);
842 mb.clean();
843 return result;
844 }
845
846 /******************************************************************
847 * external_acl cache
848 */
849
850 static void
851 external_acl_cache_touch(external_acl * def, external_acl_entry * entry)
852 {
853 dlinkDelete(&entry->lru, &def->lru_list);
854 dlinkAdd(entry, &entry->lru, &def->lru_list);
855 }
856
857 static char *
858 makeExternalAclKey(ACLFilledChecklist * ch, external_acl_data * acl_data)
859 {
860 static MemBuf mb;
861 char buf[256];
862 int first = 1;
863 wordlist *arg;
864 external_acl_format *format;
865 HttpRequest *request = ch->request;
866 HttpReply *reply = ch->reply;
867 mb.reset();
868
869 for (format = acl_data->def->format; format; format = format->next) {
870 const char *str = NULL;
871 String sb;
872
873 switch (format->type) {
874
875 case _external_acl_format::EXT_ACL_LOGIN:
876 assert (ch->auth_user_request != NULL);
877 str = ch->auth_user_request->username();
878 break;
879 #if USE_IDENT
880
881 case _external_acl_format::EXT_ACL_IDENT:
882 str = ch->rfc931;
883
884 if (!str || !*str) {
885 ch->changeState(IdentLookup::Instance());
886 return NULL;
887 }
888
889 break;
890 #endif
891
892 case _external_acl_format::EXT_ACL_SRC:
893 str = ch->src_addr.NtoA(buf,sizeof(buf));
894 break;
895
896 case _external_acl_format::EXT_ACL_SRCPORT:
897 snprintf(buf, sizeof(buf), "%d", request->client_addr.GetPort());
898 str = buf;
899 break;
900
901 #if USE_SQUID_EUI
902 case _external_acl_format::EXT_ACL_SRCEUI48:
903 if (request->client_eui48.encode(buf, sizeof(buf)))
904 str = buf;
905 break;
906
907 case _external_acl_format::EXT_ACL_SRCEUI64:
908 if (request->client_eui64.encode(buf, sizeof(buf)))
909 str = buf;
910 break;
911 #endif
912
913 case _external_acl_format::EXT_ACL_MYADDR:
914 str = request->my_addr.NtoA(buf, sizeof(buf));
915 break;
916
917 case _external_acl_format::EXT_ACL_MYPORT:
918 snprintf(buf, sizeof(buf), "%d", request->my_addr.GetPort());
919 str = buf;
920 break;
921
922 case _external_acl_format::EXT_ACL_URI:
923 str = urlCanonical(request);
924 break;
925
926 case _external_acl_format::EXT_ACL_DST:
927 str = request->GetHost();
928 break;
929
930 case _external_acl_format::EXT_ACL_PROTO:
931 str = ProtocolStr[request->protocol];
932 break;
933
934 case _external_acl_format::EXT_ACL_PORT:
935 snprintf(buf, sizeof(buf), "%d", request->port);
936 str = buf;
937 break;
938
939 case _external_acl_format::EXT_ACL_PATH:
940 str = request->urlpath.termedBuf();
941 break;
942
943 case _external_acl_format::EXT_ACL_METHOD:
944 str = RequestMethodStr(request->method);
945 break;
946
947 case _external_acl_format::EXT_ACL_HEADER_REQUEST:
948 sb = request->header.getByName(format->header);
949 str = sb.termedBuf();
950 break;
951
952 case _external_acl_format::EXT_ACL_HEADER_REQUEST_ID:
953 sb = request->header.getStrOrList(format->header_id);
954 str = sb.termedBuf();
955 break;
956
957 case _external_acl_format::EXT_ACL_HEADER_REQUEST_MEMBER:
958 sb = request->header.getByNameListMember(format->header, format->member, format->separator);
959 str = sb.termedBuf();
960 break;
961
962 case _external_acl_format::EXT_ACL_HEADER_REQUEST_ID_MEMBER:
963 sb = request->header.getListMember(format->header_id, format->member, format->separator);
964 str = sb.termedBuf();
965 break;
966
967 case _external_acl_format::EXT_ACL_HEADER_REPLY:
968 if (reply) {
969 sb = reply->header.getByName(format->header);
970 str = sb.termedBuf();
971 }
972 break;
973
974 case _external_acl_format::EXT_ACL_HEADER_REPLY_ID:
975 if (reply) {
976 sb = reply->header.getStrOrList(format->header_id);
977 str = sb.termedBuf();
978 }
979 break;
980
981 case _external_acl_format::EXT_ACL_HEADER_REPLY_MEMBER:
982 if (reply) {
983 sb = reply->header.getByNameListMember(format->header, format->member, format->separator);
984 str = sb.termedBuf();
985 }
986 break;
987
988 case _external_acl_format::EXT_ACL_HEADER_REPLY_ID_MEMBER:
989 if (reply) {
990 sb = reply->header.getListMember(format->header_id, format->member, format->separator);
991 str = sb.termedBuf();
992 }
993 break;
994 #if USE_SSL
995
996 case _external_acl_format::EXT_ACL_USER_CERT_RAW:
997
998 if (ch->conn() != NULL) {
999 SSL *ssl = fd_table[ch->conn()->fd].ssl;
1000
1001 if (ssl)
1002 str = sslGetUserCertificatePEM(ssl);
1003 }
1004
1005 break;
1006
1007 case _external_acl_format::EXT_ACL_USER_CERTCHAIN_RAW:
1008
1009 if (ch->conn() != NULL) {
1010 SSL *ssl = fd_table[ch->conn()->fd].ssl;
1011
1012 if (ssl)
1013 str = sslGetUserCertificateChainPEM(ssl);
1014 }
1015
1016 break;
1017
1018 case _external_acl_format::EXT_ACL_USER_CERT:
1019
1020 if (ch->conn() != NULL) {
1021 SSL *ssl = fd_table[ch->conn()->fd].ssl;
1022
1023 if (ssl)
1024 str = sslGetUserAttribute(ssl, format->header);
1025 }
1026
1027 break;
1028
1029 case _external_acl_format::EXT_ACL_CA_CERT:
1030
1031 if (ch->conn() != NULL) {
1032 SSL *ssl = fd_table[ch->conn()->fd].ssl;
1033
1034 if (ssl)
1035 str = sslGetCAAttribute(ssl, format->header);
1036 }
1037
1038 break;
1039 #endif
1040
1041 case _external_acl_format::EXT_ACL_EXT_USER:
1042 str = request->extacl_user.termedBuf();
1043 break;
1044
1045 case _external_acl_format::EXT_ACL_UNKNOWN:
1046
1047 case _external_acl_format::EXT_ACL_END:
1048 fatal("unknown external_acl format error");
1049 break;
1050 }
1051
1052 if (str)
1053 if (!*str)
1054 str = NULL;
1055
1056 if (!str)
1057 str = "-";
1058
1059 if (!first)
1060 mb.append(" ", 1);
1061
1062 if (acl_data->def->quote == external_acl::QUOTE_METHOD_URL) {
1063 const char *quoted = rfc1738_escape(str);
1064 mb.append(quoted, strlen(quoted));
1065 } else {
1066 strwordquote(&mb, str);
1067 }
1068
1069 sb.clean();
1070
1071 first = 0;
1072 }
1073
1074 for (arg = acl_data->arguments; arg; arg = arg->next) {
1075 if (!first)
1076 mb.append(" ", 1);
1077
1078 if (acl_data->def->quote == external_acl::QUOTE_METHOD_URL) {
1079 const char *quoted = rfc1738_escape(arg->key);
1080 mb.append(quoted, strlen(quoted));
1081 } else {
1082 strwordquote(&mb, arg->key);
1083 }
1084
1085 first = 0;
1086 }
1087
1088 return mb.buf;
1089 }
1090
1091 static int
1092 external_acl_entry_expired(external_acl * def, external_acl_entry * entry)
1093 {
1094 if (entry->date + (entry->result == 1 ? def->ttl : def->negative_ttl) < squid_curtime)
1095 return 1;
1096 else
1097 return 0;
1098 }
1099
1100 static int
1101 external_acl_grace_expired(external_acl * def, external_acl_entry * entry)
1102 {
1103 int ttl;
1104 ttl = entry->result == 1 ? def->ttl : def->negative_ttl;
1105 ttl = (ttl * (100 - def->grace)) / 100;
1106
1107 if (entry->date + ttl < squid_curtime)
1108 return 1;
1109 else
1110 return 0;
1111 }
1112
1113 static external_acl_entry *
1114 external_acl_cache_add(external_acl * def, const char *key, ExternalACLEntryData const & data)
1115 {
1116 ExternalACLEntry *entry = static_cast<ExternalACLEntry *>(hash_lookup(def->cache, key));
1117 debugs(82, 2, "external_acl_cache_add: Adding '" << key << "' = " << data.result);
1118
1119 if (entry) {
1120 debugs(82, 3, "ExternalACLEntry::update: updating existing entry");
1121 entry->update (data);
1122 external_acl_cache_touch(def, entry);
1123
1124 return entry;
1125 }
1126
1127 entry = new ExternalACLEntry;
1128 entry->key = xstrdup(key);
1129 entry->update (data);
1130
1131 def->add
1132 (entry);
1133
1134 return entry;
1135 }
1136
1137 static void
1138 external_acl_cache_delete(external_acl * def, external_acl_entry * entry)
1139 {
1140 assert (entry->def == def);
1141 hash_remove_link(def->cache, entry);
1142 dlinkDelete(&entry->lru, &def->lru_list);
1143 def->cache_entries -= 1;
1144 delete entry;
1145 }
1146
1147 /******************************************************************
1148 * external_acl helpers
1149 */
1150
1151 typedef struct _externalAclState externalAclState;
1152
1153 struct _externalAclState {
1154 EAH *callback;
1155 void *callback_data;
1156 char *key;
1157 external_acl *def;
1158 dlink_node list;
1159 externalAclState *queue;
1160 };
1161
1162 CBDATA_TYPE(externalAclState);
1163 static void
1164 free_externalAclState(void *data)
1165 {
1166 externalAclState *state = static_cast<externalAclState *>(data);
1167 safe_free(state->key);
1168 cbdataReferenceDone(state->callback_data);
1169 cbdataReferenceDone(state->def);
1170 }
1171
1172 /*
1173 * The helper program receives queries on stdin, one
1174 * per line, and must return the result on on stdout
1175 *
1176 * General result syntax:
1177 *
1178 * OK/ERR keyword=value ...
1179 *
1180 * Keywords:
1181 *
1182 * user= The users name (login)
1183 * message= Message describing the reason
1184 * tag= A string tag to be applied to the request that triggered the acl match.
1185 * applies to both OK and ERR responses.
1186 * Won't override existing request tags.
1187 * log= A string to be used in access logging
1188 *
1189 * Other keywords may be added to the protocol later
1190 *
1191 * value needs to be enclosed in quotes if it may contain whitespace, or
1192 * the whitespace escaped using \ (\ escaping obviously also applies to
1193 * any " characters)
1194 */
1195
1196 static void
1197 externalAclHandleReply(void *data, char *reply)
1198 {
1199 externalAclState *state = static_cast<externalAclState *>(data);
1200 externalAclState *next;
1201 char *status;
1202 char *token;
1203 char *value;
1204 char *t;
1205 ExternalACLEntryData entryData;
1206 entryData.result = 0;
1207 external_acl_entry *entry = NULL;
1208
1209 debugs(82, 2, "externalAclHandleReply: reply=\"" << reply << "\"");
1210
1211 if (reply) {
1212 status = strwordtok(reply, &t);
1213
1214 if (status && strcmp(status, "OK") == 0)
1215 entryData.result = 1;
1216
1217 while ((token = strwordtok(NULL, &t))) {
1218 value = strchr(token, '=');
1219
1220 if (value) {
1221 *value++ = '\0'; /* terminate the token, and move up to the value */
1222
1223 if (state->def->quote == external_acl::QUOTE_METHOD_URL)
1224 rfc1738_unescape(value);
1225
1226 if (strcmp(token, "user") == 0)
1227 entryData.user = value;
1228 else if (strcmp(token, "message") == 0)
1229 entryData.message = value;
1230 else if (strcmp(token, "error") == 0)
1231 entryData.message = value;
1232 else if (strcmp(token, "tag") == 0)
1233 entryData.tag = value;
1234 else if (strcmp(token, "log") == 0)
1235 entryData.log = value;
1236 else if (strcmp(token, "password") == 0)
1237 entryData.password = value;
1238 else if (strcmp(token, "passwd") == 0)
1239 entryData.password = value;
1240 else if (strcmp(token, "login") == 0)
1241 entryData.user = value;
1242 }
1243 }
1244 }
1245
1246 dlinkDelete(&state->list, &state->def->queue);
1247
1248 if (cbdataReferenceValid(state->def)) {
1249 if (reply)
1250 entry = external_acl_cache_add(state->def, state->key, entryData);
1251 else {
1252 external_acl_entry *oldentry = (external_acl_entry *)hash_lookup(state->def->cache, state->key);
1253
1254 if (oldentry)
1255 external_acl_cache_delete(state->def, oldentry);
1256 }
1257 }
1258
1259 do {
1260 void *cbdata;
1261 cbdataReferenceDone(state->def);
1262
1263 if (state->callback && cbdataReferenceValidDone(state->callback_data, &cbdata))
1264 state->callback(cbdata, entry);
1265
1266 next = state->queue;
1267
1268 cbdataFree(state);
1269
1270 state = next;
1271 } while (state);
1272 }
1273
1274 void
1275 ACLExternal::ExternalAclLookup(ACLChecklist *checklist, ACLExternal * me, EAH * callback, void *callback_data)
1276 {
1277 MemBuf buf;
1278 external_acl_data *acl = me->data;
1279 external_acl *def = acl->def;
1280 externalAclState *state;
1281 dlink_node *node;
1282 externalAclState *oldstate = NULL;
1283 bool graceful = 0;
1284
1285 ACLFilledChecklist *ch = Filled(checklist);
1286 if (acl->def->require_auth) {
1287 int ti;
1288 /* Make sure the user is authenticated */
1289 debugs(82, 3, "aclMatchExternal: " << acl->def->name << " check user authenticated.");
1290
1291 if ((ti = AuthenticateAcl(ch)) != 1) {
1292 debugs(82, 1, "externalAclLookup: " << acl->def->name <<
1293 " user authentication failure (" << ti << ", ch=" << ch << ")");
1294 callback(callback_data, NULL);
1295 return;
1296 }
1297 debugs(82, 3, "aclMatchExternal: " << acl->def->name << " user is authenticated.");
1298 }
1299
1300 const char *key = makeExternalAclKey(ch, acl);
1301
1302 if (!key) {
1303 debugs(82, 1, "externalAclLookup: lookup in '" << def->name <<
1304 "', prerequisit failure (ch=" << ch << ")");
1305 callback(callback_data, NULL);
1306 return;
1307 }
1308
1309 debugs(82, 2, "externalAclLookup: lookup in '" << def->name << "' for '" << key << "'");
1310
1311 external_acl_entry *entry = static_cast<external_acl_entry *>(hash_lookup(def->cache, key));
1312
1313 if (entry && external_acl_entry_expired(def, entry))
1314 entry = NULL;
1315
1316 /* Check for a pending lookup to hook into */
1317 for (node = def->queue.head; node; node = node->next) {
1318 externalAclState *oldstatetmp = static_cast<externalAclState *>(node->data);
1319
1320 if (strcmp(key, oldstatetmp->key) == 0) {
1321 oldstate = oldstatetmp;
1322 break;
1323 }
1324 }
1325
1326 if (entry && external_acl_grace_expired(def, entry)) {
1327 if (oldstate) {
1328 debugs(82, 4, "externalAclLookup: in grace period, but already pending lookup ('" << key << "', ch=" << ch << ")");
1329 callback(callback_data, entry);
1330 return;
1331 } else {
1332 graceful = 1; // grace expired, (neg)ttl did not, and we must start a new lookup.
1333 }
1334 }
1335
1336 // The entry is in the cache, grace_ttl did not expired.
1337 if (!graceful && entry && !external_acl_grace_expired(def, entry)) {
1338 /* Should not really happen, but why not.. */
1339 callback(callback_data, entry);
1340 debugs(82, 4, "externalAclLookup: no lookup pending for '" << key << "', and grace not expired");
1341 debugs(82, 4, "externalAclLookup: (what tha' hell?)");
1342 return;
1343 }
1344
1345 /* No pending lookup found. Sumbit to helper */
1346 state = cbdataAlloc(externalAclState);
1347
1348 state->def = cbdataReference(def);
1349
1350 state->key = xstrdup(key);
1351
1352 if (!graceful) {
1353 state->callback = callback;
1354 state->callback_data = cbdataReference(callback_data);
1355 }
1356
1357 if (oldstate) {
1358 /* Hook into pending lookup */
1359 state->queue = oldstate->queue;
1360 oldstate->queue = state;
1361 } else {
1362 /* Check for queue overload */
1363
1364 if (def->theHelper->stats.queue_size >= (int)def->theHelper->childs.n_running) {
1365 debugs(82, 1, "externalAclLookup: '" << def->name << "' queue overload (ch=" << ch << ")");
1366 cbdataFree(state);
1367 callback(callback_data, entry);
1368 return;
1369 }
1370
1371 /* Send it off to the helper */
1372 buf.init();
1373
1374 buf.Printf("%s\n", key);
1375
1376 debugs(82, 4, "externalAclLookup: looking up for '" << key << "' in '" << def->name << "'.");
1377
1378 helperSubmit(def->theHelper, buf.buf, externalAclHandleReply, state);
1379
1380 dlinkAdd(state, &state->list, &def->queue);
1381
1382 buf.clean();
1383 }
1384
1385 if (graceful) {
1386 /* No need to wait during grace period */
1387 debugs(82, 4, "externalAclLookup: no need to wait for the result of '" <<
1388 key << "' in '" << def->name << "' (ch=" << ch << ").");
1389 debugs(82, 4, "externalAclLookup: using cached entry " << entry);
1390
1391 if (entry != NULL) {
1392 debugs(82, 4, "externalAclLookup: entry = { date=" <<
1393 (long unsigned int) entry->date << ", result=" <<
1394 entry->result << ", user=" << entry->user << " tag=" <<
1395 entry->tag << " log=" << entry->log << " }");
1396
1397 }
1398
1399 callback(callback_data, entry);
1400 return;
1401 }
1402
1403 debugs(82, 4, "externalAclLookup: will wait for the result of '" << key <<
1404 "' in '" << def->name << "' (ch=" << ch << ").");
1405 }
1406
1407 static void
1408 externalAclStats(StoreEntry * sentry)
1409 {
1410 external_acl *p;
1411
1412 for (p = Config.externalAclHelperList; p; p = p->next) {
1413 storeAppendPrintf(sentry, "External ACL Statistics: %s\n", p->name);
1414 storeAppendPrintf(sentry, "Cache size: %d\n", p->cache->count);
1415 helperStats(sentry, p->theHelper);
1416 storeAppendPrintf(sentry, "\n");
1417 }
1418 }
1419
1420 static void
1421 externalAclRegisterWithCacheManager(void)
1422 {
1423 Mgr::RegisterAction("external_acl",
1424 "External ACL stats",
1425 externalAclStats, 0, 1);
1426 }
1427
1428 void
1429 externalAclInit(void)
1430 {
1431 static int firstTimeInit = 1;
1432 external_acl *p;
1433
1434 for (p = Config.externalAclHelperList; p; p = p->next) {
1435 if (!p->cache)
1436 p->cache = hash_create((HASHCMP *) strcmp, hashPrime(1024), hash4);
1437
1438 if (!p->theHelper)
1439 p->theHelper = new helper(p->name);
1440
1441 p->theHelper->cmdline = p->cmdline;
1442
1443 p->theHelper->childs = p->children;
1444
1445 p->theHelper->ipc_type = IPC_TCP_SOCKET;
1446
1447 p->theHelper->addr = p->local_addr;
1448
1449 helperOpenServers(p->theHelper);
1450 }
1451
1452 if (firstTimeInit) {
1453 firstTimeInit = 0;
1454 CBDATA_INIT_TYPE_FREECB(externalAclState, free_externalAclState);
1455 }
1456
1457 externalAclRegisterWithCacheManager();
1458 }
1459
1460 void
1461 externalAclShutdown(void)
1462 {
1463 external_acl *p;
1464
1465 for (p = Config.externalAclHelperList; p; p = p->next) {
1466 helperShutdown(p->theHelper);
1467 }
1468 }
1469
1470 ExternalACLLookup ExternalACLLookup::instance_;
1471 ExternalACLLookup *
1472 ExternalACLLookup::Instance()
1473 {
1474 return &instance_;
1475 }
1476
1477 void
1478 ExternalACLLookup::checkForAsync(ACLChecklist *checklist)const
1479 {
1480 /* TODO: optimise this - we probably have a pointer to this
1481 * around somewhere */
1482 ACL *acl = ACL::FindByName(AclMatchedName);
1483 assert(acl);
1484 ACLExternal *me = dynamic_cast<ACLExternal *> (acl);
1485 assert (me);
1486 checklist->asyncInProgress(true);
1487 ACLExternal::ExternalAclLookup(checklist, me, LookupDone, checklist);
1488 }
1489
1490 void
1491 ExternalACLLookup::LookupDone(void *data, void *result)
1492 {
1493 ACLFilledChecklist *checklist = Filled(static_cast<ACLChecklist*>(data));
1494 checklist->extacl_entry = cbdataReference((external_acl_entry *)result);
1495 checklist->asyncInProgress(false);
1496 checklist->changeState (ACLChecklist::NullState::Instance());
1497 checklist->check();
1498 }
1499
1500 /* This registers "external" in the registry. To do dynamic definitions
1501 * of external ACL's, rather than a static prototype, have a Prototype instance
1502 * prototype in the class that defines each external acl 'class'.
1503 * Then, then the external acl instance is created, it self registers under
1504 * it's name.
1505 * Be sure that clone is fully functional for that acl class though!
1506 */
1507 ACL::Prototype ACLExternal::RegistryProtoype(&ACLExternal::RegistryEntry_, "external");
1508
1509 ACLExternal ACLExternal::RegistryEntry_("external");
1510
1511 ACL *
1512 ACLExternal::clone() const
1513 {
1514 return new ACLExternal(*this);
1515 }
1516
1517 ACLExternal::ACLExternal (char const *theClass) : data (NULL), class_ (xstrdup (theClass))
1518 {}
1519
1520 ACLExternal::ACLExternal (ACLExternal const & old) : data (NULL), class_ (old.class_ ? xstrdup (old.class_) : NULL)
1521 {
1522 /* we don't have copy constructors for the data yet */
1523 assert (!old.data);
1524 }
1525
1526 char const *
1527 ACLExternal::typeString() const
1528 {
1529 return class_;
1530 }
1531
1532 bool
1533 ACLExternal::isProxyAuth() const
1534 {
1535 return data->def->require_auth;
1536 }