/src/trafficserver/include/tsutil/Regex.h
Line | Count | Source |
1 | | /** @file |
2 | | |
3 | | A brief file description |
4 | | |
5 | | @section license License |
6 | | |
7 | | Licensed to the Apache Software Foundation (ASF) under one |
8 | | or more contributor license agreements. See the NOTICE file |
9 | | distributed with this work for additional information |
10 | | regarding copyright ownership. The ASF licenses this file |
11 | | to you under the Apache License, Version 2.0 (the |
12 | | "License"); you may not use this file except in compliance |
13 | | with the License. You may obtain a copy of the License at |
14 | | |
15 | | http://www.apache.org/licenses/LICENSE-2.0 |
16 | | |
17 | | Unless required by applicable law or agreed to in writing, software |
18 | | distributed under the License is distributed on an "AS IS" BASIS, |
19 | | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
20 | | See the License for the specific language governing permissions and |
21 | | limitations under the License. |
22 | | */ |
23 | | |
24 | | #pragma once |
25 | | |
26 | | #include <cstdint> |
27 | | #include <memory> |
28 | | #include <string_view> |
29 | | #include <string> |
30 | | #include <vector> |
31 | | |
32 | | /// @brief Match flags for regular expression evaluation. |
33 | | /// |
34 | | /// @internal These values are copied from pcre2.h, to avoid having to include it. The values are checked (with |
35 | | /// static_assert) in Regex.cc against PCRE2 named constants, in case they change in future PCRE2 releases. |
36 | | enum REFlags { |
37 | | RE_CASE_INSENSITIVE = 0x00000008u, ///< Ignore case (by default, matches are case sensitive). |
38 | | RE_UNANCHORED = 0x00000400u, ///< Unanchored (@a DFA defaults to anchored). |
39 | | RE_ANCHORED = 0x80000000u, ///< Anchored (@a Regex defaults to unanchored). |
40 | | RE_ENDANCHORED = 0x20000000u, ///< Anchored at the subject end; with RE_ANCHORED, requires a whole-subject match. |
41 | | RE_NOTEMPTY = 0x00000004u, ///< Not empty (by default, matches may match empty string). |
42 | | /// Require the match to consume the entire subject. When set, a successful pcre2 match |
43 | | /// that does not span [0, subject.size()) is reported as @c RE_ERROR_NOMATCH. Implemented |
44 | | /// as a post-match length check so JIT remains eligible on all supported PCRE2 versions. |
45 | | RE_FULL_MATCH = 0x10000000u, |
46 | | }; |
47 | | |
48 | | /// @brief Error codes returned by regular expression operations. |
49 | | /// |
50 | | /// @internal As with REFlags, these values are copied from pcre2.h, to avoid having to include it. |
51 | | enum REErrors { |
52 | | RE_ERROR_NOMATCH = -1, ///< No match found. |
53 | | RE_ERROR_NULL = -51 ///< NULL code or subject was passed. |
54 | | }; |
55 | | |
56 | | /// @brief Wrapper for PCRE2 match data. |
57 | | class RegexMatches |
58 | | { |
59 | | friend class Regex; |
60 | | |
61 | | public: |
62 | | /** Construct a new RegexMatches object. |
63 | | * |
64 | | * @param size The number of matches to allocate space for. |
65 | | */ |
66 | | RegexMatches(uint32_t size = DEFAULT_MATCHES); |
67 | | ~RegexMatches(); |
68 | | |
69 | | /** Get the match at the given index. |
70 | | * |
71 | | * @return The match at the given index, or an empty view. |
72 | | */ |
73 | | std::string_view operator[](size_t index) const; |
74 | | /** Get the ovector pointer for the capture groups. Don't use this unless you know what you are doing. |
75 | | * |
76 | | * @return ovector pointer. |
77 | | */ |
78 | | size_t *get_ovector_pointer(); |
79 | | int32_t size() const; |
80 | | |
81 | | private: |
82 | | constexpr static uint32_t DEFAULT_MATCHES = 10; |
83 | | static void *malloc(size_t size, void *caller); |
84 | | static void free(void *p, void *caller); |
85 | | std::string_view _subject; |
86 | | char _buffer[24 + 96 + 28 * DEFAULT_MATCHES]; // 24 bytes for the general context, 96 bytes overhead, 28 bytes per match. |
87 | | size_t _buffer_bytes_used = 0; |
88 | | int32_t _size = 0; |
89 | | |
90 | | /// @internal This effectively wraps a void* so that we can avoid requiring the pcre2.h include for the user of the Regex |
91 | | /// API (see Regex.cc). |
92 | | struct _MatchData; |
93 | | class _MatchDataPtr |
94 | | { |
95 | | friend struct _MatchData; |
96 | | |
97 | | private: |
98 | | void *_ptr = nullptr; |
99 | | }; |
100 | | _MatchDataPtr _match_data; |
101 | | }; |
102 | | |
103 | | /// @brief Wrapper for PCRE2 match context |
104 | | /// |
105 | | /// @internal This instance is not tied to any Regex and can be used with one of the Regex::exec overloads. |
106 | | class RegexMatchContext |
107 | | { |
108 | | friend class Regex; |
109 | | |
110 | | public: |
111 | | /** Construct a new RegexMatchContext object. |
112 | | */ |
113 | | RegexMatchContext(); |
114 | | ~RegexMatchContext(); |
115 | | |
116 | | /// uses pcre2_match_context_copy for a deep copy. |
117 | | RegexMatchContext(RegexMatchContext const &orig); |
118 | | RegexMatchContext &operator=(RegexMatchContext const &orig); |
119 | | |
120 | | RegexMatchContext(RegexMatchContext &&) = default; |
121 | | RegexMatchContext &operator=(RegexMatchContext &&) = default; |
122 | | |
123 | | /** Limits the amount of backtracking that can take place. |
124 | | * Any regex exec call that fails will return PCRE2_ERROR_MATCHLIMIT(-47) |
125 | | */ |
126 | | void set_match_limit(uint32_t limit); |
127 | | |
128 | | private: |
129 | | /// @internal This wraps a void* so to avoid requiring a pcre2 include. |
130 | | struct _MatchContext; |
131 | | struct _MatchContextPtr { |
132 | | void *_ptr = nullptr; |
133 | | }; |
134 | | |
135 | | _MatchContextPtr _match_context; |
136 | | }; |
137 | | |
138 | | /// @brief Wrapper for PCRE2 regular expression. |
139 | | class Regex |
140 | | { |
141 | | public: |
142 | 0 | Regex() = default; |
143 | | /** Deep copy constructor. |
144 | | * |
145 | | * Creates a new Regex object with a deep copy of the compiled pattern. |
146 | | * Uses pcre2_code_copy() to duplicate the compiled pattern without |
147 | | * requiring the original pattern string, then compiles the copy for the |
148 | | * just-in-time engine, which pcre2_code_copy() cannot carry over. |
149 | | * |
150 | | * @param other The Regex object to copy from. |
151 | | */ |
152 | | Regex(Regex const &other); |
153 | | /** Deep copy assignment operator. |
154 | | * |
155 | | * Replaces the current compiled pattern with a deep copy of the other's pattern. |
156 | | * |
157 | | * @param other The Regex object to copy from. |
158 | | * @return Reference to this object. |
159 | | */ |
160 | | Regex &operator=(Regex const &other); |
161 | | Regex(Regex &&that) noexcept; |
162 | | Regex &operator=(Regex &&other); |
163 | | ~Regex(); |
164 | | |
165 | | /** Compile the @a pattern into a regular expression. |
166 | | * |
167 | | * @param pattern Source pattern for regular expression. |
168 | | * @param flags Compilation flags. |
169 | | * @return @a true if compiled successfully, @a false otherwise. |
170 | | * |
171 | | * @a flags should be the bitwise @c or of @c REFlags values. |
172 | | * |
173 | | * On failure any previously compiled pattern is left in place and remains usable. |
174 | | */ |
175 | | bool compile(std::string_view pattern, uint32_t flags = 0); |
176 | | |
177 | | /** Compile the @a pattern into a regular expression that drives a policy decision. |
178 | | * |
179 | | * @param pattern Source pattern for regular expression. |
180 | | * @param flags Compilation flags. |
181 | | * @param policy_gate Operator-facing label identifying this pattern as a policy gate. |
182 | | * @return @a true if compiled successfully, @a false otherwise. |
183 | | * |
184 | | * @a flags should be the bitwise @c or of @c REFlags values. |
185 | | * |
186 | | * A non-empty @a policy_gate marks @a pattern as operator-authored input that |
187 | | * drives a policy decision, subjecting it to @c policy_gate_anchoring. The label |
188 | | * names the site in diagnostics, e.g. @c "url_sig excl_regex". |
189 | | * |
190 | | * @internal This is a distinct overload rather than a defaulted third parameter |
191 | | * on the two-argument form. A default argument is supplied at the call site, so |
192 | | * appending one changes the mangled name and every plugin already built against |
193 | | * the old header fails to load with an undefined symbol for @c Regex::compile. |
194 | | * |
195 | | * @internal Dropping @a policy_gate at a call site selects the two-argument |
196 | | * overload and compiles clean, leaving that site neither anchored nor reported. |
197 | | * The two-argument form has to stay reachable for the plugins above, so this is |
198 | | * covered by tests at the gate sites rather than by the signature. |
199 | | */ |
200 | | bool compile(std::string_view pattern, uint32_t flags, std::string_view policy_gate); |
201 | | |
202 | | /** Compile the @a pattern into a regular expression. |
203 | | * |
204 | | * @param pattern Source pattern for regular expression. |
205 | | * @param error String to receive error message. |
206 | | * @param erroffset Pointer to integer to receive error offset. |
207 | | * @param flags Compilation flags. |
208 | | * @return @a true if compiled successfully, @a false otherwise. |
209 | | * |
210 | | * @a flags should be the bitwise @c or of @c REFlags values. |
211 | | * |
212 | | * On failure any previously compiled pattern is left in place and remains usable. |
213 | | */ |
214 | | bool compile(std::string_view pattern, std::string &error, int &erroffset, unsigned flags = 0); |
215 | | |
216 | | /** Compile the @a pattern into a regular expression that drives a policy decision. |
217 | | * |
218 | | * @param pattern Source pattern for regular expression. |
219 | | * @param error String to receive error message. |
220 | | * @param erroffset Pointer to integer to receive error offset. |
221 | | * @param flags Compilation flags. |
222 | | * @param policy_gate Operator-facing label identifying this pattern as a policy gate. |
223 | | * @return @a true if compiled successfully, @a false otherwise. |
224 | | * |
225 | | * @a flags should be the bitwise @c or of @c REFlags values. |
226 | | * |
227 | | * @internal A distinct overload for the same ABI reason as the two-argument form. |
228 | | */ |
229 | | bool compile(std::string_view pattern, std::string &error, int &erroffset, unsigned flags, std::string_view policy_gate); |
230 | | |
231 | | /** Process-wide anchoring policy for policy-gate patterns. |
232 | | * |
233 | | * Non-zero anchors gate patterns to the whole subject. Zero (the default) leaves |
234 | | * matching unchanged and warns instead. Read at compile time, so a change takes |
235 | | * effect as configurations are reloaded. |
236 | | * |
237 | | * @internal tsutil cannot depend on librecords, so the owning process links this |
238 | | * with @c RecEstablishStaticConfigInt32. Plugins share the variable, so they need |
239 | | * no plumbing of their own. |
240 | | */ |
241 | | static int32_t policy_gate_anchoring; |
242 | | |
243 | | /** What a policy-gate report is telling the operator. */ |
244 | | enum class PolicyGateReport { |
245 | | UNENFORCED, ///< The policy is off and this pattern is not required to match the whole subject. |
246 | | ENFORCED, ///< The policy anchored this pattern to the whole subject. |
247 | | }; |
248 | | |
249 | | /** Handler invoked when a policy-gate pattern is compiled. |
250 | | * |
251 | | * @a message is a complete sentence. @a kind selects the severity: @c UNENFORCED is |
252 | | * a warning, @c ENFORCED is informational, since under enforcement the pattern is |
253 | | * safe and the operator only needs a record that the policy touched it. Null (the |
254 | | * default) stays silent. |
255 | | * |
256 | | * @internal A function pointer because tsutil must not reference the diagnostics |
257 | | * implementation: a @c Regex.cc to @c DbgCtl.cc edge pulls that object into every |
258 | | * target linking @c libtsutil.a, colliding with the stubs some plugin tests |
259 | | * provide and with the ODR constraint in @c plugins/esi/lib/CMakeLists.txt. |
260 | | */ |
261 | | using PolicyGateReportHandler = void (*)(PolicyGateReport kind, std::string_view message); |
262 | | |
263 | | static PolicyGateReportHandler policy_gate_report_handler; |
264 | | |
265 | | /** Execute the regular expression. |
266 | | * |
267 | | * @param subject String to match against. |
268 | | * @return @c true if the pattern matched, @a false if not. |
269 | | * |
270 | | * It is safe to call this method concurrently on the same instance of @a this. |
271 | | */ |
272 | | bool exec(std::string_view subject) const; |
273 | | |
274 | | /** Execute the regular expression. |
275 | | * |
276 | | * @param subject String to match against. |
277 | | * @param flags Match flags (e.g., RE_NOTEMPTY). |
278 | | * @return @c true if the pattern matched, @a false if not. |
279 | | * |
280 | | * It is safe to call this method concurrently on the same instance of @a this. |
281 | | */ |
282 | | bool exec(std::string_view subject, uint32_t flags) const; |
283 | | |
284 | | /** Execute the regular expression. |
285 | | * |
286 | | * @param subject String to match against. |
287 | | * @param matches Place to store the capture groups. |
288 | | * @return @c The number of capture groups. < 0 if an error occurred. 0 if the number of Matches is too small. |
289 | | * |
290 | | * It is safe to call this method concurrently on the same instance of @a this. |
291 | | * |
292 | | * Each capture group takes 3 elements of @a ovector, therefore @a ovecsize must |
293 | | * be a multiple of 3 and at least three times the number of desired capture groups. |
294 | | */ |
295 | | int exec(std::string_view subject, RegexMatches &matches) const; |
296 | | |
297 | | /** Execute the regular expression. |
298 | | * |
299 | | * @param subject String to match against. |
300 | | * @param matches Place to store the capture groups. |
301 | | * @param flags Match flags (e.g., RE_NOTEMPTY). |
302 | | * @param optional context Match context (set matching limits). |
303 | | * @return @c The number of capture groups. < 0 if an error occurred. 0 if the number of Matches is too small. |
304 | | * |
305 | | * It is safe to call this method concurrently on the same instance of @a this. |
306 | | * |
307 | | * Each capture group takes 3 elements of @a ovector, therefore @a ovecsize must |
308 | | * be a multiple of 3 and at least three times the number of desired capture groups. |
309 | | */ |
310 | | int exec(std::string_view subject, RegexMatches &matches, uint32_t flags, |
311 | | RegexMatchContext const *const matchContext = nullptr) const; |
312 | | |
313 | | /** Error string for exec failure. |
314 | | * |
315 | | * @param int return code from exec call. |
316 | | */ |
317 | | static std::string get_error_string(int rc); |
318 | | |
319 | | /// @return The number of capture groups in the compiled pattern, -1 for fail. |
320 | | int32_t get_capture_count() const; |
321 | | |
322 | | /// @return number of highest back references, -1 for fail. |
323 | | int32_t get_backref_max() const; |
324 | | |
325 | | /// @return Is the compiled pattern empty? |
326 | | bool empty() const; |
327 | | |
328 | | private: |
329 | | /// @internal This effectively wraps a void* so that we can avoid requiring the pcre2.h include for the user of the Regex |
330 | | /// API (see Regex.cc). |
331 | | struct _Code; |
332 | | class _CodePtr |
333 | | { |
334 | | friend struct _Code; |
335 | | |
336 | | private: |
337 | | void *_ptr = nullptr; |
338 | | }; |
339 | | _CodePtr _code; |
340 | | }; |
341 | | |
342 | | /** Deterministic Finite state Automata container. |
343 | | * |
344 | | * This contains a set of patterns (which may be of size 1) and matches if any of the patterns |
345 | | * match. |
346 | | */ |
347 | | class DFA |
348 | | { |
349 | | public: |
350 | | DFA() = default; |
351 | | ~DFA(); |
352 | | |
353 | | /// @return The number of patterns successfully compiled. |
354 | | int32_t compile(const std::string_view pattern, unsigned flags = 0); |
355 | | /// @return The number of patterns successfully compiled. |
356 | | int32_t compile(const std::string_view *const patterns, int npatterns, unsigned flags = 0); |
357 | | /// @return The number of patterns successfully compiled. |
358 | | int32_t compile(const char *const *patterns, int npatterns, unsigned flags = 0); |
359 | | |
360 | | /** Match @a str against the internal patterns. |
361 | | * |
362 | | * @param str String to match. |
363 | | * @return Index of the matched pattern, -1 if no match. |
364 | | */ |
365 | | int32_t match(std::string_view str) const; |
366 | | |
367 | | private: |
368 | | struct Pattern { |
369 | 0 | Pattern(Regex &&rxp, std::string &&s) : _re(std::move(rxp)), _p(std::move(s)) {} |
370 | | Regex _re; ///< The compile pattern. |
371 | | std::string _p; ///< The original pattern. |
372 | | }; |
373 | | |
374 | | /** Compile @a pattern and add it to the pattern set. |
375 | | * |
376 | | * @param pattern Regular expression to compile. |
377 | | * @param flags Regular expression compilation flags. |
378 | | * @return @c true if @a pattern was successfully compiled, @c false if not. |
379 | | */ |
380 | | bool build(std::string_view pattern, unsigned flags = 0); |
381 | | |
382 | | std::vector<Pattern> _patterns; |
383 | | bool _full_match = false; ///< Apply RE_FULL_MATCH on every match() call. |
384 | | }; |