Annotation of embedaddon/dnsmasq/src/pattern.c, revision 1.1.1.1
1.1 misho 1: /* dnsmasq is Copyright (c) 2000-2022 Simon Kelley
2:
3: This program is free software; you can redistribute it and/or modify
4: it under the terms of the GNU General Public License as published by
5: the Free Software Foundation; version 2 dated June, 1991, or
6: (at your option) version 3 dated 29 June, 2007.
7:
8: This program is distributed in the hope that it will be useful,
9: but WITHOUT ANY WARRANTY; without even the implied warranty of
10: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11: GNU General Public License for more details.
12:
13: You should have received a copy of the GNU General Public License
14: along with this program. If not, see <http://www.gnu.org/licenses/>.
15: */
16:
17: #include "dnsmasq.h"
18:
19: #ifdef HAVE_CONNTRACK
20:
21: #define LOG(...) \
22: do { \
23: my_syslog(LOG_DEBUG, __VA_ARGS__); \
24: } while (0)
25:
26: #define ASSERT(condition) \
27: do { \
28: if (!(condition)) \
29: my_syslog(LOG_ERR, _("[pattern.c:%d] Assertion failure: %s"), __LINE__, #condition); \
30: } while (0)
31:
32: /**
33: * Determines whether a given string value matches against a glob pattern
34: * which may contain zero-or-more-character wildcards denoted by '*'.
35: *
36: * Based on "Glob Matching Can Be Simple And Fast Too" by Russ Cox,
37: * See https://research.swtch.com/glob
38: *
39: * @param value A string value.
40: * @param num_value_bytes The number of bytes of the string value.
41: * @param pattern A glob pattern.
42: * @param num_pattern_bytes The number of bytes of the glob pattern.
43: *
44: * @return 1 If the provided value matches against the glob pattern.
45: * @return 0 Otherwise.
46: */
47: static int is_string_matching_glob_pattern(
48: const char *value,
49: size_t num_value_bytes,
50: const char *pattern,
51: size_t num_pattern_bytes)
52: {
53: ASSERT(value);
54: ASSERT(pattern);
55:
56: size_t value_index = 0;
57: size_t next_value_index = 0;
58: size_t pattern_index = 0;
59: size_t next_pattern_index = 0;
60: while (value_index < num_value_bytes || pattern_index < num_pattern_bytes)
61: {
62: if (pattern_index < num_pattern_bytes)
63: {
64: char pattern_character = pattern[pattern_index];
65: if ('a' <= pattern_character && pattern_character <= 'z')
66: pattern_character -= 'a' - 'A';
67: if (pattern_character == '*')
68: {
69: /* zero-or-more-character wildcard */
70: /* Try to match at value_index, otherwise restart at value_index + 1 next. */
71: next_pattern_index = pattern_index;
72: pattern_index++;
73: if (value_index < num_value_bytes)
74: next_value_index = value_index + 1;
75: else
76: next_value_index = 0;
77: continue;
78: }
79: else
80: {
81: /* ordinary character */
82: if (value_index < num_value_bytes)
83: {
84: char value_character = value[value_index];
85: if ('a' <= value_character && value_character <= 'z')
86: value_character -= 'a' - 'A';
87: if (value_character == pattern_character)
88: {
89: pattern_index++;
90: value_index++;
91: continue;
92: }
93: }
94: }
95: }
96: if (next_value_index)
97: {
98: pattern_index = next_pattern_index;
99: value_index = next_value_index;
100: continue;
101: }
102: return 0;
103: }
104: return 1;
105: }
106:
107: /**
108: * Determines whether a given string value represents a valid DNS name.
109: *
110: * - DNS names must adhere to RFC 1123: 1 to 253 characters in length, consisting of a sequence of labels
111: * delimited by dots ("."). Each label must be 1 to 63 characters in length, contain only
112: * ASCII letters ("a"-"Z"), digits ("0"-"9"), or hyphens ("-") and must not start or end with a hyphen.
113: *
114: * - A valid name must be fully qualified, i.e., consist of at least two labels.
115: * The final label must not be fully numeric, and must not be the "local" pseudo-TLD.
116: *
117: * - Examples:
118: * Valid: "example.com"
119: * Invalid: "ipcamera", "ipcamera.local", "8.8.8.8"
120: *
121: * @param value A string value.
122: *
123: * @return 1 If the provided string value is a valid DNS name.
124: * @return 0 Otherwise.
125: */
126: int is_valid_dns_name(const char *value)
127: {
128: ASSERT(value);
129:
130: size_t num_bytes = 0;
131: size_t num_labels = 0;
132: const char *c, *label = NULL;
133: int is_label_numeric = 1;
134: for (c = value;; c++)
135: {
136: if (*c &&
137: *c != '-' && *c != '.' &&
138: (*c < '0' || *c > '9') &&
139: (*c < 'A' || *c > 'Z') &&
140: (*c < 'a' || *c > 'z'))
141: {
142: LOG(_("Invalid DNS name: Invalid character %c."), *c);
143: return 0;
144: }
145: if (*c)
146: num_bytes++;
147: if (!label)
148: {
149: if (!*c || *c == '.')
150: {
151: LOG(_("Invalid DNS name: Empty label."));
152: return 0;
153: }
154: if (*c == '-')
155: {
156: LOG(_("Invalid DNS name: Label starts with hyphen."));
157: return 0;
158: }
159: label = c;
160: }
161: if (*c && *c != '.')
162: {
163: if (*c < '0' || *c > '9')
164: is_label_numeric = 0;
165: }
166: else
167: {
168: if (c[-1] == '-')
169: {
170: LOG(_("Invalid DNS name: Label ends with hyphen."));
171: return 0;
172: }
173: size_t num_label_bytes = (size_t) (c - label);
174: if (num_label_bytes > 63)
175: {
176: LOG(_("Invalid DNS name: Label is too long (%zu)."), num_label_bytes);
177: return 0;
178: }
179: num_labels++;
180: if (!*c)
181: {
182: if (num_labels < 2)
183: {
184: LOG(_("Invalid DNS name: Not enough labels (%zu)."), num_labels);
185: return 0;
186: }
187: if (is_label_numeric)
188: {
189: LOG(_("Invalid DNS name: Final label is fully numeric."));
190: return 0;
191: }
192: if (num_label_bytes == 5 &&
193: (label[0] == 'l' || label[0] == 'L') &&
194: (label[1] == 'o' || label[1] == 'O') &&
195: (label[2] == 'c' || label[2] == 'C') &&
196: (label[3] == 'a' || label[3] == 'A') &&
197: (label[4] == 'l' || label[4] == 'L'))
198: {
199: LOG(_("Invalid DNS name: \"local\" pseudo-TLD."));
200: return 0;
201: }
202: if (num_bytes < 1 || num_bytes > 253)
203: {
204: LOG(_("DNS name has invalid length (%zu)."), num_bytes);
205: return 0;
206: }
207: return 1;
208: }
209: label = NULL;
210: is_label_numeric = 1;
211: }
212: }
213: }
214:
215: /**
216: * Determines whether a given string value represents a valid DNS name pattern.
217: *
218: * - DNS names must adhere to RFC 1123: 1 to 253 characters in length, consisting of a sequence of labels
219: * delimited by dots ("."). Each label must be 1 to 63 characters in length, contain only
220: * ASCII letters ("a"-"Z"), digits ("0"-"9"), or hyphens ("-") and must not start or end with a hyphen.
221: *
222: * - Patterns follow the syntax of DNS names, but additionally allow the wildcard character "*" to be used up to
223: * twice per label to match 0 or more characters within that label. Note that the wildcard never matches a dot
224: * (e.g., "*.example.com" matches "api.example.com" but not "api.us.example.com").
225: *
226: * - A valid name or pattern must be fully qualified, i.e., consist of at least two labels.
227: * The final label must not be fully numeric, and must not be the "local" pseudo-TLD.
228: * A pattern must end with at least two literal (non-wildcard) labels.
229: *
230: * - Examples:
231: * Valid: "example.com", "*.example.com", "video*.example.com", "api*.*.example.com", "*-prod-*.example.com"
232: * Invalid: "ipcamera", "ipcamera.local", "*", "*.com", "8.8.8.8"
233: *
234: * @param value A string value.
235: *
236: * @return 1 If the provided string value is a valid DNS name pattern.
237: * @return 0 Otherwise.
238: */
239: int is_valid_dns_name_pattern(const char *value)
240: {
241: ASSERT(value);
242:
243: size_t num_bytes = 0;
244: size_t num_labels = 0;
245: const char *c, *label = NULL;
246: int is_label_numeric = 1;
247: size_t num_wildcards = 0;
248: int previous_label_has_wildcard = 1;
249: for (c = value;; c++)
250: {
251: if (*c &&
252: *c != '*' && /* Wildcard. */
253: *c != '-' && *c != '.' &&
254: (*c < '0' || *c > '9') &&
255: (*c < 'A' || *c > 'Z') &&
256: (*c < 'a' || *c > 'z'))
257: {
258: LOG(_("Invalid DNS name pattern: Invalid character %c."), *c);
259: return 0;
260: }
261: if (*c && *c != '*')
262: num_bytes++;
263: if (!label)
264: {
265: if (!*c || *c == '.')
266: {
267: LOG(_("Invalid DNS name pattern: Empty label."));
268: return 0;
269: }
270: if (*c == '-')
271: {
272: LOG(_("Invalid DNS name pattern: Label starts with hyphen."));
273: return 0;
274: }
275: label = c;
276: }
277: if (*c && *c != '.')
278: {
279: if (*c < '0' || *c > '9')
280: is_label_numeric = 0;
281: if (*c == '*')
282: {
283: if (num_wildcards >= 2)
284: {
285: LOG(_("Invalid DNS name pattern: Wildcard character used more than twice per label."));
286: return 0;
287: }
288: num_wildcards++;
289: }
290: }
291: else
292: {
293: if (c[-1] == '-')
294: {
295: LOG(_("Invalid DNS name pattern: Label ends with hyphen."));
296: return 0;
297: }
298: size_t num_label_bytes = (size_t) (c - label) - num_wildcards;
299: if (num_label_bytes > 63)
300: {
301: LOG(_("Invalid DNS name pattern: Label is too long (%zu)."), num_label_bytes);
302: return 0;
303: }
304: num_labels++;
305: if (!*c)
306: {
307: if (num_labels < 2)
308: {
309: LOG(_("Invalid DNS name pattern: Not enough labels (%zu)."), num_labels);
310: return 0;
311: }
312: if (num_wildcards != 0 || previous_label_has_wildcard)
313: {
314: LOG(_("Invalid DNS name pattern: Wildcard within final two labels."));
315: return 0;
316: }
317: if (is_label_numeric)
318: {
319: LOG(_("Invalid DNS name pattern: Final label is fully numeric."));
320: return 0;
321: }
322: if (num_label_bytes == 5 &&
323: (label[0] == 'l' || label[0] == 'L') &&
324: (label[1] == 'o' || label[1] == 'O') &&
325: (label[2] == 'c' || label[2] == 'C') &&
326: (label[3] == 'a' || label[3] == 'A') &&
327: (label[4] == 'l' || label[4] == 'L'))
328: {
329: LOG(_("Invalid DNS name pattern: \"local\" pseudo-TLD."));
330: return 0;
331: }
332: if (num_bytes < 1 || num_bytes > 253)
333: {
334: LOG(_("DNS name pattern has invalid length after removing wildcards (%zu)."), num_bytes);
335: return 0;
336: }
337: return 1;
338: }
339: label = NULL;
340: is_label_numeric = 1;
341: previous_label_has_wildcard = num_wildcards != 0;
342: num_wildcards = 0;
343: }
344: }
345: }
346:
347: /**
348: * Determines whether a given DNS name matches against a DNS name pattern.
349: *
350: * @param name A valid DNS name.
351: * @param pattern A valid DNS name pattern.
352: *
353: * @return 1 If the provided DNS name matches against the DNS name pattern.
354: * @return 0 Otherwise.
355: */
356: int is_dns_name_matching_pattern(const char *name, const char *pattern)
357: {
358: ASSERT(name);
359: ASSERT(is_valid_dns_name(name));
360: ASSERT(pattern);
361: ASSERT(is_valid_dns_name_pattern(pattern));
362:
363: const char *n = name;
364: const char *p = pattern;
365:
366: do {
367: const char *name_label = n;
368: while (*n && *n != '.')
369: n++;
370: const char *pattern_label = p;
371: while (*p && *p != '.')
372: p++;
373: if (!is_string_matching_glob_pattern(
374: name_label, (size_t) (n - name_label),
375: pattern_label, (size_t) (p - pattern_label)))
376: break;
377: if (*n)
378: n++;
379: if (*p)
380: p++;
381: } while (*n && *p);
382:
383: return !*n && !*p;
384: }
385:
386: #endif
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>