Annotation of embedaddon/dnsmasq/src/pattern.c, revision 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>