]>
git.ipfire.org Git - thirdparty/squid.git/blob - src/acl/RegexData.cc
2 * DEBUG: section 28 Access Control
3 * AUTHOR: Duane Wessels
6 * SQUID Web Proxy Cache http://www.squid-cache.org/
7 * ----------------------------------------------------------
9 * Squid is the result of efforts by numerous individuals from
10 * the Internet community; see the CONTRIBUTORS file for full
11 * details. Many organizations have provided support for Squid's
12 * development; see the SPONSORS file for full details. Squid is
13 * Copyrighted (C) 2001 by the Regents of the University of
14 * California; see the COPYRIGHT file for full details. Squid
15 * incorporates software developed and/or copyrighted by other
16 * sources; see the CREDITS file for full details.
18 * This program is free software; you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation; either version 2 of the License, or
21 * (at your option) any later version.
23 * This program is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU General Public License for more details.
28 * You should have received a copy of the GNU General Public License
29 * along with this program; if not, write to the Free Software
30 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
33 * Copyright (c) 2003, Robert Collins <robertc@squid-cache.org>
34 * Copyright (c) 2011, Marcus Kool
38 #include "acl/RegexData.h"
39 #include "acl/Checklist.h"
41 #include "ConfigParser.h"
47 aclDestroyRegexList(relist
* data
)
51 for (; data
; data
= next
) {
53 regfree(&data
->regex
);
54 safe_free(data
->pattern
);
55 memFree(data
, MEM_RELIST
);
59 ACLRegexData::~ACLRegexData()
61 aclDestroyRegexList(data
);
65 ACLRegexData::match(char const *word
)
70 debugs(28, 3, "aclRegexData::match: checking '" << word
<< "'");
78 relist
*current
= first
;
81 debugs(28, 3, "aclRegexData::match: looking for '" << current
->pattern
<< "'");
83 if (regexec(¤t
->regex
, word
, 0, 0, 0) == 0) {
85 /* shift the element just found to the second position
87 prev
->next
= current
->next
;
88 current
->next
= first
->next
;
89 first
->next
= current
;
92 debugs(28, 2, "aclRegexData::match: match '" << current
->pattern
<< "' found in '" << word
<< "'");
97 current
= current
->next
;
108 int flags
= REG_EXTENDED
| REG_NOSUB
;
110 while (temp
!= NULL
) {
111 if (temp
->flags
!= flags
) {
112 if ((temp
->flags
®_ICASE
) != 0) {
113 wordlistAdd(&W
, "-i");
115 wordlistAdd(&W
, "+i");
120 wordlistAdd(&W
, temp
->pattern
);
128 removeUnnecessaryWildcards(char * t
)
132 if (strncmp(t
, "^.*", 3) == 0)
135 /* NOTE: an initial '.' might seem unnessary but is not;
136 * it can be a valid requirement that cannot be optimised
138 while (*t
== '.' && *(t
+1) == '*') {
143 debugs(28, DBG_IMPORTANT
, "" << cfg_filename
<< " line " << config_lineno
<< ": " << config_input_line
);
144 debugs(28, DBG_IMPORTANT
, "WARNING: regular expression '" << orig
<< "' has only wildcards and matches all strings. Using '.*' instead.");
148 debugs(28, DBG_IMPORTANT
, "" << cfg_filename
<< " line " << config_lineno
<< ": " << config_input_line
);
149 debugs(28, DBG_IMPORTANT
, "WARNING: regular expression '" << orig
<< "' has unnecessary wildcard(s). Using '" << t
<< "' instead.");
156 compileRE(relist
**Tail
, char * RE
, int flags
)
162 if (RE
== NULL
|| *RE
== '\0')
165 if ((errcode
= regcomp(&comp
, RE
, flags
)) != 0) {
167 regerror(errcode
, &comp
, errbuf
, sizeof errbuf
);
168 debugs(28, DBG_CRITICAL
, "" << cfg_filename
<< " line " << config_lineno
<< ": " << config_input_line
);
169 debugs(28, DBG_CRITICAL
, "ERROR: invalid regular expression: '" << RE
<< "': " << errbuf
);
172 debugs(28, 2, "compileRE: compiled '" << RE
<< "' with flags " << flags
);
174 q
= (relist
*) memAllocate(MEM_RELIST
);
175 q
->pattern
= xstrdup(RE
);
184 /** Compose and compile one large RE from a set of (small) REs.
185 * The ultimate goal is to have only one RE per ACL so that regexec() is
186 * called only once per ACL.
189 compileOptimisedREs(relist
**curlist
, wordlist
* wl
)
195 int flags
= REG_EXTENDED
| REG_NOSUB
;
196 int largeREindex
= 0;
197 char largeRE
[BUFSIZ
];
206 RElen
= strlen( wl
->key
);
208 if (strcmp(wl
->key
, "-i") == 0) {
209 if (flags
& REG_ICASE
) {
210 /* optimisation of -i ... -i */
211 debugs(28, 2, "compileOptimisedREs: optimisation of -i ... -i" );
213 debugs(28, 2, "compileOptimisedREs: -i" );
214 newlistp
= compileRE( newlistp
, largeRE
, flags
);
215 if (newlistp
== NULL
) {
216 aclDestroyRegexList( newlist
);
220 largeRE
[largeREindex
=0] = '\0';
222 } else if (strcmp(wl
->key
, "+i") == 0) {
223 if ((flags
& REG_ICASE
) == 0) {
224 /* optimisation of +i ... +i */
225 debugs(28, 2, "compileOptimisedREs: optimisation of +i ... +i");
227 debugs(28, 2, "compileOptimisedREs: +i");
228 newlistp
= compileRE( newlistp
, largeRE
, flags
);
229 if (newlistp
== NULL
) {
230 aclDestroyRegexList( newlist
);
234 largeRE
[largeREindex
=0] = '\0';
236 } else if (RElen
+ largeREindex
+ 3 < BUFSIZ
-1) {
237 debugs(28, 2, "compileOptimisedREs: adding RE '" << wl
->key
<< "'");
238 if (largeREindex
> 0) {
239 largeRE
[largeREindex
] = '|';
242 largeRE
[largeREindex
] = '(';
244 for (char * t
= wl
->key
; *t
!= '\0'; ++t
) {
245 largeRE
[largeREindex
] = *t
;
248 largeRE
[largeREindex
] = ')';
250 largeRE
[largeREindex
] = '\0';
253 debugs(28, 2, "compileOptimisedREs: buffer full, generating new optimised RE..." );
254 newlistp
= compileRE( newlistp
, largeRE
, flags
);
255 if (newlistp
== NULL
) {
256 aclDestroyRegexList( newlist
);
259 largeRE
[largeREindex
=0] = '\0';
260 continue; /* do the loop again to add the RE to largeRE */
265 newlistp
= compileRE( newlistp
, largeRE
, flags
);
266 if (newlistp
== NULL
) {
267 aclDestroyRegexList( newlist
);
271 /* all was successful, so put the new list at the tail */
272 if (*curlist
== NULL
) {
275 for (Tail
= curlist
; *Tail
!= NULL
; Tail
= &((*Tail
)->next
))
280 debugs(28, 2, "compileOptimisedREs: " << numREs
<< " REs are optimised into one RE.");
282 debugs(28, (opt_parse_cfg_only
?DBG_IMPORTANT
:2), "" << cfg_filename
<< " line " << config_lineno
<< ": " << config_input_line
);
283 debugs(28, (opt_parse_cfg_only
?DBG_IMPORTANT
:2), "WARNING: there are more than 100 regular expressions. " <<
284 "Consider using less REs or use rules without expressions like 'dstdomain'.");
291 compileUnoptimisedREs(relist
**curlist
, wordlist
* wl
)
295 int flags
= REG_EXTENDED
| REG_NOSUB
;
297 for (Tail
= curlist
; *Tail
!= NULL
; Tail
= &((*Tail
)->next
))
301 if (strcmp(wl
->key
, "-i") == 0) {
303 } else if (strcmp(wl
->key
, "+i") == 0) {
306 newTail
= compileRE( Tail
, wl
->key
, flags
);
308 debugs(28, DBG_CRITICAL
, "ERROR: Skipping regular expression. Compile failed: '" << wl
->key
<< "'");
317 aclParseRegexList(relist
**curlist
)
322 debugs(28, 2, HERE
<< "aclParseRegexList: new Regex line or file");
324 while ((t
= ConfigParser::strtokFile()) != NULL
) {
325 const char *clean
= removeUnnecessaryWildcards(t
);
326 if (strlen(clean
) > BUFSIZ
-1) {
327 debugs(28, DBG_CRITICAL
, "" << cfg_filename
<< " line " << config_lineno
<< ": " << config_input_line
);
328 debugs(28, DBG_CRITICAL
, "ERROR: Skipping regular expression. Larger than " << BUFSIZ
-1 << " characters: '" << clean
<< "'");
330 debugs(28, 3, "aclParseRegexList: buffering RE '" << clean
<< "'");
331 wordlistAdd(&wl
, clean
);
335 if (!compileOptimisedREs(curlist
, wl
)) {
336 debugs(28, DBG_IMPORTANT
, "WARNING: optimisation of regular expressions failed; using fallback method without optimisation");
337 compileUnoptimisedREs(curlist
, wl
);
340 wordlistDestroy(&wl
);
344 ACLRegexData::parse()
346 aclParseRegexList(&data
);
350 ACLRegexData::empty() const
355 ACLData
<char const *> *
356 ACLRegexData::clone() const
358 /* Regex's don't clone yet. */
360 return new ACLRegexData
;