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>