master
c 459 lines 14.2 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "../libnetdata.h"
4
5 struct simple_pattern {
6 const char *match;
7 uint32_t len;
8
9 SIMPLE_PREFIX_MODE mode;
10 bool negative;
11 bool case_sensitive;
12
13 struct simple_pattern *child;
14 struct simple_pattern *next;
15 };
16
17 static struct simple_pattern *parse_pattern(char *str, SIMPLE_PREFIX_MODE default_mode, size_t count) {
18 if(unlikely(count >= 1000))
19 return NULL;
20
21 // fprintf(stderr, "PARSING PATTERN: '%s'\n", str);
22
23 SIMPLE_PREFIX_MODE mode;
24 struct simple_pattern *child = NULL;
25
26 char *s = str, *c = str;
27
28 // skip asterisks in front
29 while(*c == '*') c++;
30
31 // find the next asterisk
32 while(*c && *c != '*') c++;
33
34 // do we have an asterisk in the middle?
35 if(*c == '*' && c[1] != '\0') {
36 // yes, we have
37 child = parse_pattern(c, default_mode, count + 1);
38 c[1] = '\0';
39 }
40
41 // check what this one matches
42
43 size_t len = strlen(s);
44 if(len >= 2 && *s == '*' && s[len - 1] == '*') {
45 s[len - 1] = '\0';
46 s++;
47 mode = SIMPLE_PATTERN_SUBSTRING;
48 }
49 else if(len >= 1 && *s == '*') {
50 s++;
51 mode = SIMPLE_PATTERN_SUFFIX;
52 }
53 else if(len >= 1 && s[len - 1] == '*') {
54 s[len - 1] = '\0';
55 mode = SIMPLE_PATTERN_PREFIX;
56 }
57 else
58 mode = default_mode;
59
60 // allocate the structure
61 struct simple_pattern *m = callocz(1, sizeof(struct simple_pattern));
62 if(*s) {
63 m->match = strdupz(s);
64 m->len = strlen(m->match);
65 m->mode = mode;
66 }
67 else {
68 m->mode = SIMPLE_PATTERN_SUBSTRING;
69 }
70
71 m->child = child;
72
73 return m;
74 }
75
76 SIMPLE_PATTERN *simple_pattern_create(const char *list, const char *separators, SIMPLE_PREFIX_MODE default_mode, bool case_sensitive) {
77 struct simple_pattern *root = NULL, *last = NULL;
78
79 if(unlikely(!list || !*list)) return root;
80
81 bool isseparator[256] = {
82 [' '] = true // space
83 , ['\t'] = true // tab
84 , ['\r'] = true // carriage return
85 , ['\n'] = true // new line
86 , ['\f'] = true // form feed
87 , ['\v'] = true // vertical tab
88 };
89
90 if (unlikely(separators == SIMPLE_PATTERN_NO_SEPARATORS))
91 memset(isseparator, false, sizeof(isseparator));
92
93 else if (unlikely(separators && *separators)) {
94 memset(isseparator, false, sizeof(isseparator));
95 while(*separators) isseparator[(unsigned char)*separators++] = true;
96 }
97
98 char *buf = mallocz(strlen(list) + 1);
99 const char *s = list;
100
101 while(s && *s) {
102 buf[0] = '\0';
103 char *c = buf;
104
105 bool negative = false;
106
107 // skip all spaces
108 while(isseparator[(unsigned char)*s])
109 s++;
110
111 if(*s == '!') {
112 negative = true;
113 s++;
114 }
115
116 // empty string
117 if(unlikely(!*s))
118 break;
119
120 // find the next space
121 char escape = 0;
122 while(*s) {
123 if(*s == '\\' && !escape) {
124 escape = 1;
125 s++;
126 }
127 else {
128 if (isseparator[(unsigned char)*s] && !escape) {
129 s++;
130 break;
131 }
132
133 *c++ = *s++;
134 escape = 0;
135 }
136 }
137
138 // terminate our string
139 *c = '\0';
140
141 // if we matched the empty string, skip it
142 if(unlikely(!*buf))
143 continue;
144
145 // fprintf(stderr, "FOUND PATTERN: '%s'\n", buf);
146 struct simple_pattern *m = parse_pattern(buf, default_mode, 0);
147 m->negative = negative;
148 m->case_sensitive = case_sensitive;
149
150 if(default_mode == SIMPLE_PATTERN_SUBSTRING) {
151 m->mode = SIMPLE_PATTERN_SUBSTRING;
152
153 struct simple_pattern *tm;
154 for(tm = m; tm->child ; tm = tm->child) ;
155 tm->mode = SIMPLE_PATTERN_SUBSTRING;
156 }
157
158 // link it at the end
159 if(unlikely(!root))
160 root = last = m;
161 else {
162 last->next = m;
163 last = m;
164 }
165 }
166
167 freez(buf);
168 return (SIMPLE_PATTERN *)root;
169 }
170
171 ALWAYS_INLINE
172 static char *add_wildcarded(const char *matched, size_t matched_size, char *wildcarded, size_t *wildcarded_size) {
173 //if(matched_size) {
174 // char buf[matched_size + 1];
175 // strncpyz(buf, matched, matched_size);
176 // fprintf(stderr, "ADD WILDCARDED '%s' of length %zu\n", buf, matched_size);
177 //}
178
179 if(unlikely(wildcarded && *wildcarded_size && matched && *matched && matched_size)) {
180 size_t wss = *wildcarded_size - 1;
181 size_t len = (matched_size < wss)?matched_size:wss;
182 if(likely(len)) {
183 strncpyz(wildcarded, matched, len);
184
185 *wildcarded_size -= len;
186 return &wildcarded[len];
187 }
188 }
189
190 return wildcarded;
191 }
192
193 ALWAYS_INLINE
194 static int sp_strcmp(const char *s1, const char *s2, bool case_sensitive) {
195 if(case_sensitive)
196 return strcmp(s1, s2);
197
198 return strcasecmp(s1, s2);
199 }
200
201 ALWAYS_INLINE
202 static int sp_strncmp(const char *s1, const char *s2, size_t n, bool case_sensitive) {
203 if(case_sensitive)
204 return strncmp(s1, s2, n);
205
206 return strncasecmp(s1, s2, n);
207 }
208
209 ALWAYS_INLINE
210 static char *sp_strstr(const char *haystack, const char *needle, bool case_sensitive) {
211 if(case_sensitive)
212 return strstr(haystack, needle);
213
214 return strcasestr(haystack, needle);
215 }
216
217 ALWAYS_INLINE
218 static bool match_pattern(struct simple_pattern *m, const char *str, size_t len, char *wildcarded, size_t *wildcarded_size) {
219 char *s;
220
221 bool loop = true;
222 while(loop && m->len <= len) {
223 loop = false;
224
225 switch(m->mode) {
226 default:
227 case SIMPLE_PATTERN_EXACT:
228 if(unlikely(sp_strcmp(str, m->match, m->case_sensitive) == 0)) {
229 if(!m->child) return true;
230 return false;
231 }
232 break;
233
234 case SIMPLE_PATTERN_SUBSTRING:
235 if(!m->len) return true;
236 if((s = sp_strstr(str, m->match, m->case_sensitive))) {
237 wildcarded = add_wildcarded(str, s - str, wildcarded, wildcarded_size);
238 if(!m->child) {
239 add_wildcarded(&s[m->len], len - (&s[m->len] - str), wildcarded, wildcarded_size);
240 return true;
241 }
242
243 // instead of recursion
244 {
245 len = len - (s - str) - m->len;
246 str = &s[m->len];
247 m = m->child;
248 loop = true;
249 // return match_pattern(m->child, &s[m->len], len - (s - str) - m->len, wildcarded, wildcarded_size);
250 }
251 }
252 break;
253
254 case SIMPLE_PATTERN_PREFIX:
255 if(unlikely(sp_strncmp(str, m->match, m->len, m->case_sensitive) == 0)) {
256 if(!m->child) {
257 add_wildcarded(&str[m->len], len - m->len, wildcarded, wildcarded_size);
258 return true;
259 }
260 // instead of recursion
261 {
262 len = len - m->len;
263 str = &str[m->len];
264 m = m->child;
265 loop = true;
266 // return match_pattern(m->child, &str[m->len], len - m->len, wildcarded, wildcarded_size);
267 }
268 }
269 break;
270
271 case SIMPLE_PATTERN_SUFFIX:
272 if(unlikely(sp_strcmp(&str[len - m->len], m->match, m->case_sensitive) == 0)) {
273 add_wildcarded(str, len - m->len, wildcarded, wildcarded_size);
274 if(!m->child) return true;
275 return false;
276 }
277 break;
278 }
279 }
280
281 return false;
282 }
283
284 ALWAYS_INLINE
285 static SIMPLE_PATTERN_RESULT simple_pattern_matches_extract_with_length(SIMPLE_PATTERN *list, const char *str, size_t len, char *wildcarded, size_t wildcarded_size) {
286 struct simple_pattern *m, *root = (struct simple_pattern *)list;
287
288 for(m = root; m ; m = m->next) {
289 char *ws = wildcarded;
290 size_t wss = wildcarded_size;
291 if(unlikely(ws)) *ws = '\0';
292
293 if (match_pattern(m, str, len, ws, &wss)) {
294 if (m->negative) return SP_MATCHED_NEGATIVE;
295 return SP_MATCHED_POSITIVE;
296 }
297 }
298
299 return SP_NOT_MATCHED;
300 }
301
302 SIMPLE_PATTERN_RESULT simple_pattern_matches_buffer_extract(SIMPLE_PATTERN *list, BUFFER *str, char *wildcarded, size_t wildcarded_size) {
303 if(!list || !str || buffer_strlen(str)) return SP_NOT_MATCHED;
304 return simple_pattern_matches_extract_with_length(list, buffer_tostring(str), buffer_strlen(str), wildcarded, wildcarded_size);
305 }
306
307 SIMPLE_PATTERN_RESULT simple_pattern_matches_string_extract(SIMPLE_PATTERN *list, STRING *str, char *wildcarded, size_t wildcarded_size) {
308 if(!list || !str) return SP_NOT_MATCHED;
309 return simple_pattern_matches_extract_with_length(list, string2str(str), string_strlen(str), wildcarded, wildcarded_size);
310 }
311
312 SIMPLE_PATTERN_RESULT simple_pattern_matches_extract(SIMPLE_PATTERN *list, const char *str, char *wildcarded, size_t wildcarded_size) {
313 if(!list || !str || !*str) return SP_NOT_MATCHED;
314 return simple_pattern_matches_extract_with_length(list, str, strlen(str), wildcarded, wildcarded_size);
315 }
316
317 SIMPLE_PATTERN_RESULT simple_pattern_matches_length_extract(SIMPLE_PATTERN *list, const char *str, size_t len, char *wildcarded, size_t wildcarded_size) {
318 if(!list || !str || !*str || !len) return SP_NOT_MATCHED;
319 return simple_pattern_matches_extract_with_length(list, str, len, wildcarded, wildcarded_size);
320 }
321
322 static inline void free_pattern(struct simple_pattern *m) {
323 if(!m) return;
324
325 free_pattern(m->child);
326 free_pattern(m->next);
327 freez((void *)m->match);
328 freez(m);
329 }
330
331 void simple_pattern_free(SIMPLE_PATTERN *list) {
332 if(!list) return;
333
334 free_pattern(((struct simple_pattern *)list));
335 }
336
337 /* Debugging patterns
338
339 This code should be dead - it is useful for debugging but should not be called by production code.
340 Feel free to comment it out, but please leave it in the file.
341 */
342 extern void simple_pattern_dump(uint64_t debug_type, SIMPLE_PATTERN *p)
343 {
344 struct simple_pattern *root = (struct simple_pattern *)p;
345 if(root==NULL) {
346 netdata_log_debug(debug_type,"dump_pattern(NULL)");
347 return;
348 }
349 netdata_log_debug(debug_type,"dump_pattern(%p) child=%p next=%p mode=%u match=%s", root, root->child, root->next, root->mode,
350 root->match);
351 if(root->child!=NULL)
352 simple_pattern_dump(debug_type, (SIMPLE_PATTERN*)root->child);
353 if(root->next!=NULL)
354 simple_pattern_dump(debug_type, (SIMPLE_PATTERN*)root->next);
355 }
356
357 /* Heuristic: decide if the pattern could match a DNS name.
358
359 Although this functionality is used directly by socket.c:connection_allowed() it must be in this file
360 because of the SIMPLE_PATTERN/simple_pattern structure hiding.
361 Based on RFC952 / RFC1123. We need to decide if the pattern may match a DNS name, or not. For the negative
362 cases we need to be sure that it can only match an ipv4 or ipv6 address:
363 * IPv6 addresses contain ':', which are illegal characters in DNS.
364 * IPv4 addresses cannot contain alpha- characters.
365 * DNS TLDs must be alphanumeric to distinguish from IPv4.
366 Some patterns (e.g. "*a*" ) could match multiple cases (i.e. DNS or IPv6).
367 Some patterns will be awkward (e.g. "192.168.*") as they look like they are intended to match IPv4-only
368 but could match DNS (i.e. "192.168.com" is a valid name).
369 */
370 static void scan_is_potential_name(struct simple_pattern *p, int *alpha, int *colon, int *wildcards)
371 {
372 while (p) {
373 if (p->match) {
374 if(p->mode == SIMPLE_PATTERN_EXACT && !strcmp("localhost", p->match)) {
375 p = p->child;
376 continue;
377 }
378 char const *scan = p->match;
379 while (*scan != 0) {
380 if ((*scan >= 'a' && *scan <= 'z') || (*scan >= 'A' && *scan <= 'Z'))
381 *alpha = 1;
382 if (*scan == ':')
383 *colon = 1;
384 scan++;
385 }
386 if (p->mode != SIMPLE_PATTERN_EXACT)
387 *wildcards = 1;
388 p = p->child;
389 }
390 }
391 }
392
393 extern int simple_pattern_is_potential_name(SIMPLE_PATTERN *p)
394 {
395 int alpha=0, colon=0, wildcards=0;
396 struct simple_pattern *root = (struct simple_pattern*)p;
397 while (root != NULL) {
398 if (root->match != NULL) {
399 scan_is_potential_name(root, &alpha, &colon, &wildcards);
400 }
401 if (root->mode != SIMPLE_PATTERN_EXACT)
402 wildcards = 1;
403 root = root->next;
404 }
405 return (alpha || wildcards) && !colon;
406 }
407
408 char *simple_pattern_iterate(SIMPLE_PATTERN **p)
409 {
410 struct simple_pattern *root = (struct simple_pattern *) *p;
411 struct simple_pattern **Proot = (struct simple_pattern **)p;
412
413 (*Proot) = (*Proot)->next;
414 return (char *) root->match;
415 }
416
417 bool simple_pattern_contains_wildcards(const char *str, const char *separators) {
418 if(unlikely(!str || !*str)) return false;
419
420 // Check if it starts with exclamation mark (and not escaped)
421 if(*str == '!')
422 return true;
423
424 bool isseparator[256] = {
425 [' '] = true // space
426 , ['\t'] = true // tab
427 , ['\r'] = true // carriage return
428 , ['\n'] = true // new line
429 , ['\f'] = true // form feed
430 , ['\v'] = true // vertical tab
431 };
432
433 if (unlikely(separators == SIMPLE_PATTERN_NO_SEPARATORS))
434 memset(isseparator, false, sizeof(isseparator));
435
436 else if (unlikely(separators && *separators)) {
437 memset(isseparator, false, sizeof(isseparator));
438 while(*separators) isseparator[(unsigned char)*separators++] = true;
439 }
440
441 // Check for separators or asterisks in the string, respecting escaping
442 const char *s = str;
443 char escape = 0;
444 while(*s) {
445 if(*s == '\\' && !escape) {
446 escape = 1;
447 s++;
448 }
449 else {
450 if (!escape && (isseparator[(unsigned char)*s] || *s == '*'))
451 return true;
452
453 s++;
454 escape = 0;
455 }
456 }
457
458 return false;
459 }