static const char * classend(struct match_state *ms, const char *p) { switch (*p++) { case L_ESC: if (p == ms->p_end) match_error(ms, "malformed pattern (ends with '%')"); return p + 1; case '[': if (*p == '^') p++; do { /* look for a ']' */ if (p == ms->p_end) { match_error(ms, "malformed pattern (missing ']')"); break; } if (*(p++) == L_ESC && p < ms->p_end) { /* skip escapes (e.g. '%]') */ p++; } } while (*p != ']'); return p + 1; default: return p; } }
std::string Pattern::group (int groupNumber) { const char * stringPtr; int rc = pcre_get_substring ( _subject.substr (_offset[0]).c_str(), _ovector, _count, groupNumber, &stringPtr); if (rc < 0) { switch (rc) { case PCRE_ERROR_NOSUBSTRING: throw std::out_of_range ("Invalid group reference."); case PCRE_ERROR_NOMEMORY: throw match_error ("Memory exhausted."); default: throw match_error ("Failed to get named substring."); } } std::string matchedStr (stringPtr); pcre_free_substring (stringPtr); return matchedStr; }
static int str_find_aux(struct match_state *ms, const char *pattern, const char *string, struct str_find *sm, size_t nsm, off_t init) { size_t ls = strlen(string); size_t lp = strlen(pattern); const char *s = string; const char *p = pattern; const char *s1, *s2; int anchor, i; if (init < 0) init = 0; else if (init > (off_t)ls) return match_error(ms, "starting after string's end"); s1 = s + init; if (nospecials(p, lp)) { /* do a plain search */ s2 = lmemfind(s1, ls - (size_t)init, p, lp); if (s2 == NULL) return (0); i = 0; sm[i].sm_so = 0; sm[i].sm_eo = (off_t)ls; if (nsm > 1) { i++; sm[i].sm_so = s2 - s; sm[i].sm_eo = (off_t)((s2 - s) + (off_t)lp); } return (i + 1); } anchor = (*p == '^'); if (anchor) { p++; lp--; /* skip anchor character */ } ms->maxcaptures = (int)((nsm > MAXCAPTURES ? MAXCAPTURES : nsm) - 1); ms->matchdepth = MAXCCALLS; ms->repetitioncounter = MAXREPETITION; ms->src_init = s; ms->src_end = s + ls; ms->p_end = p + lp; do { const char *res; ms->level = 0; if ((res = match(ms, s1, p)) != NULL) { sm->sm_so = 0; sm->sm_eo = (off_t)ls; return push_captures(ms, s1, res, sm + 1, nsm - 1) + 1; } else if (ms->error != NULL) { return 0; } } while (s1++ < ms->src_end && !anchor); return 0; }
static const char * matchbalance(struct match_state *ms, const char *s, const char *p) { if (p >= ms->p_end - 1) { match_error(ms, "malformed pattern (missing arguments to '%b')"); return (NULL); } if (*s != *p) return (NULL); else { int b = *p; int e = *(p + 1); int cont = 1; while (++s < ms->src_end) { if (*s == e) { if (--cont == 0) return s + 1; } else if (*s == b) cont++; } } /* string ends out of balance */ return (NULL); }
int match_string(MATCH_LIST *list, const char *string, const char *pattern) { const char *myname = "match_string"; DICT *dict; if (msg_verbose) msg_info("%s: %s ~? %s", myname, string, pattern); /* * Try dictionary lookup: exact match. */ if (MATCH_DICTIONARY(pattern)) { if ((dict = dict_handle(pattern)) == 0) msg_panic("%s: unknown dictionary: %s", myname, pattern); if (dict_get(dict, string) != 0) return (1); if ((list->error = dict->error) != 0) return (match_error(list, "%s:%s: table lookup problem", dict->type, dict->name)); return (0); } /* * Try an exact string match. */ if (strcasecmp(string, pattern) == 0) { return (1); } /* * No match found. */ return (0); }
static int check_capture(struct match_state *ms, int l) { l -= '1'; if (l < 0 || l >= ms->level || ms->capture[l].len == CAP_UNFINISHED) return match_error(ms, "invalid capture index"); return (l); }
static int capture_to_close(struct match_state *ms) { int level = ms->level; for (level--; level >= 0; level--) if (ms->capture[level].len == CAP_UNFINISHED) return (level); return match_error(ms, "invalid pattern capture"); }
std::string Pattern::group (const std::string& groupName) { const char * stringPtr = NULL; int rc = pcre_get_named_substring ( _re, _subject.substr (_offset[0]).c_str(), _ovector, _count, groupName.c_str(), &stringPtr); if (rc < 0) { switch (rc) { case PCRE_ERROR_NOSUBSTRING: break; case PCRE_ERROR_NOMEMORY: throw match_error ("Memory exhausted."); default: throw match_error ("Failed to get named substring."); } } std::string matchedStr; if (stringPtr) { matchedStr = stringPtr; pcre_free_substring (stringPtr); } else { matchedStr = ""; } return matchedStr; }
static int push_onecapture(struct match_state *ms, int i, const char *s, const char *e, struct str_find *sm) { if (i >= ms->level) { if (i == 0 || ms->level == 0) { /* add whole match */ sm->sm_so = (off_t)(s - ms->src_init); sm->sm_eo = (off_t)(e - s) + sm->sm_so; } else return match_error(ms, "invalid capture index"); } else { ptrdiff_t l = ms->capture[i].len; if (l == CAP_UNFINISHED) return match_error(ms, "unfinished capture"); sm->sm_so = ms->capture[i].init - ms->src_init; sm->sm_eo = sm->sm_so + l; } sm->sm_eo = sm->sm_eo < sm->sm_so ? sm->sm_so : sm->sm_eo; return (0); }
static const char * start_capture(struct match_state *ms, const char *s, const char *p, int what) { const char *res; int level = ms->level; if (level >= ms->maxcaptures) { match_error(ms, "too many captures"); return (NULL); } ms->capture[level].init = s; ms->capture[level].len = what; ms->level = level + 1; /* undo capture if match failed */ if ((res = match(ms, s, p)) == NULL) ms->level--; return res; }
bool Pattern::matches (const std::string& subject) throw (match_error) { // Try to find a match for this pattern int rc = pcre_exec ( _re, NULL, subject.substr (_offset[1]).c_str(), subject.length() - _offset[1], 0, _options, _ovector, _ovectorSize); // Matching failed. if (rc < 0) { _offset[0] = _offset[1] = 0; return false; } // Handle the case if matching should be done globally if (_optionsDescription.find ("g") != std::string::npos) { _offset[0] = _offset[1]; // New offset is old offset + end of relative offset _offset[1] = _ovector[1] + _offset[0]; } // Matching succeded but not enough space. if (rc == 0) { throw match_error ("No space to store all substrings."); // @TODO figure out something more clever to do in that case. } // Matching succeeded. Keep the number of substrings for // subsequent calls to group(). _count = rc; return true; }
static const char * match(struct match_state *ms, const char *s, const char *p) { const char *ep, *res; char previous; if (ms->matchdepth-- == 0) { match_error(ms, "pattern too complex"); return (NULL); } /* using goto's to optimize tail recursion */ init: /* end of pattern? */ if (p != ms->p_end) { switch (*p) { case '(': /* start capture */ if (*(p + 1) == ')') /* position capture? */ s = start_capture(ms, s, p + 2, CAP_POSITION); else s = start_capture(ms, s, p + 1, CAP_UNFINISHED); break; case ')': /* end capture */ s = end_capture(ms, s, p + 1); break; case '$': /* is the '$' the last char in pattern? */ if ((p + 1) != ms->p_end) { /* no; go to default */ goto dflt; } /* check end of string */ s = (s == ms->src_end) ? s : NULL; break; case L_ESC: /* escaped sequences not in the format class[*+?-]? */ switch (*(p + 1)) { case 'b': /* balanced string? */ s = matchbalance(ms, s, p + 2); if (s != NULL) { p += 4; /* return match(ms, s, p + 4); */ goto init; } /* else fail (s == NULL) */ break; case 'f': /* frontier? */ p += 2; if (*p != '[') { match_error(ms, "missing '['" " after '%f' in pattern"); break; } /* points to what is next */ ep = classend(ms, p); if (ms->error != NULL) break; previous = (s == ms->src_init) ? '\0' : *(s - 1); if (!matchbracketclass(uchar(previous), p, ep - 1) && matchbracketclass(uchar(*s), p, ep - 1)) { p = ep; /* return match(ms, s, ep); */ goto init; } /* match failed */ s = NULL; break; case '0' ... '9': /* capture results (%0-%9)? */ s = match_capture(ms, s, uchar(*(p + 1))); if (s != NULL) { p += 2; /* return match(ms, s, p + 2) */ goto init; } break; default: goto dflt; } break; default: /* pattern class plus optional suffix */ dflt: /* points to optional suffix */ ep = classend(ms, p); if (ms->error != NULL) break; /* does not match at least once? */ if (!singlematch(ms, s, p, ep)) { if (ms->repetitioncounter-- == 0) { match_error(ms, "max repetition items"); s = NULL; /* fail */ /* accept empty? */ } else if (*ep == '*' || *ep == '?' || *ep == '-') { p = ep + 1; /* return match(ms, s, ep + 1); */ goto init; } else { /* '+' or no suffix */ s = NULL; /* fail */ } } else { /* matched once */ /* handle optional suffix */ switch (*ep) { case '?': /* optional */ if ((res = match(ms, s + 1, ep + 1)) != NULL) s = res; else { /* * else return * match(ms, s, ep + 1); */ p = ep + 1; goto init; } break; case '+': /* 1 or more repetitions */ s++; /* 1 match already done */ /* FALLTHROUGH */ case '*': /* 0 or more repetitions */ s = max_expand(ms, s, p, ep); break; case '-': /* 0 or more repetitions (minimum) */ s = min_expand(ms, s, p, ep); break; default: /* no suffix */ s++; p = ep; /* return match(ms, s + 1, ep); */ goto init; } } break; } } ms->matchdepth++; return s; }
int match_hostaddr(MATCH_LIST *list, const char *addr, const char *pattern) { const char *myname = "match_hostaddr"; char *saved_patt; CIDR_MATCH match_info; DICT *dict; VSTRING *err; int rc; if (msg_verbose) msg_info("%s: %s ~? %s", myname, addr, pattern); #define V4_ADDR_STRING_CHARS "01234567890." #define V6_ADDR_STRING_CHARS V4_ADDR_STRING_CHARS "abcdefABCDEF:" if (addr[strspn(addr, V6_ADDR_STRING_CHARS)] != 0) return (0); /* * Try dictionary lookup. This can be case insensitive. */ if (MATCH_DICTIONARY(pattern)) { if ((dict = dict_handle(pattern)) == 0) msg_panic("%s: unknown dictionary: %s", myname, pattern); if (dict_get(dict, addr) != 0) return (1); if ((list->error = dict->error) != 0) return (match_error(list, "%s:%s: table lookup problem", dict->type, dict->name)); return (0); } /* * Try an exact match with the host address. */ if (pattern[0] != '[') { if (strcasecmp(addr, pattern) == 0) return (1); } else { size_t addr_len = strlen(addr); if (strncasecmp(addr, pattern + 1, addr_len) == 0 && strcmp(pattern + 1 + addr_len, "]") == 0) return (1); } /* * Light-weight tests before we get into expensive operations. * * - Don't bother matching IPv4 against IPv6. Postfix transforms * IPv4-in-IPv6 to native IPv4 form when IPv4 support is enabled in * Postfix; if not, then Postfix has no business dealing with IPv4 * addresses anyway. * * - Don't bother unless the pattern is either an IPv6 address or net/mask. * * We can safely skip IPv4 address patterns because their form is * unambiguous and they did not match in the strcasecmp() calls above. * * XXX We MUST skip (parent) domain names, which may appear in NAMADR_LIST * input, to avoid triggering false cidr_match_parse() errors. * * The last two conditions below are for backwards compatibility with * earlier Postfix versions: don't abort with fatal errors on junk that * was silently ignored (principle of least astonishment). */ if (!strchr(addr, ':') != !strchr(pattern, ':') || pattern[strcspn(pattern, ":/")] == 0 || pattern[strspn(pattern, V4_ADDR_STRING_CHARS)] == 0 || pattern[strspn(pattern, V6_ADDR_STRING_CHARS "[]/")] != 0) return (0); /* * No escape from expensive operations: either we have a net/mask * pattern, or we have an address that can have multiple valid * representations (e.g., 0:0:0:0:0:0:0:1 versus ::1, etc.). The only way * to find out if the address matches the pattern is to transform * everything into to binary form, and to do the comparison there. */ saved_patt = mystrdup(pattern); err = cidr_match_parse(&match_info, saved_patt, (VSTRING *) 0); myfree(saved_patt); if (err != 0) { list->error = DICT_ERR_RETRY; rc = match_error(list, "%s", vstring_str(err)); vstring_free(err); return (rc); } return (cidr_match_execute(&match_info, addr) != 0); }
int match_hostname(MATCH_LIST *list, const char *name, const char *pattern) { const char *myname = "match_hostname"; const char *pd; const char *entry; const char *next; int match; DICT *dict; if (msg_verbose) msg_info("%s: %s ~? %s", myname, name, pattern); /* * Try dictionary lookup: exact match and parent domains. * * Don't look up parent domain substrings with regexp maps etc. */ if (MATCH_DICTIONARY(pattern)) { if ((dict = dict_handle(pattern)) == 0) msg_panic("%s: unknown dictionary: %s", myname, pattern); match = 0; for (entry = name; *entry != 0; entry = next) { if (entry == name || (dict->flags & DICT_FLAG_FIXED)) { match = (dict_get(dict, entry) != 0); if (msg_verbose > 1) msg_info("%s: lookup %s:%s %s: %s", myname, dict->type, dict->name, entry, match ? "found" : "notfound"); if (match != 0) break; if ((list->error = dict->error) != 0) return (match_error(list, "%s:%s: table lookup problem", dict->type, dict->name)); } if ((next = strchr(entry + 1, '.')) == 0) break; if (list->flags & MATCH_FLAG_PARENT) next += 1; } return (match); } /* * Try an exact match with the host name. */ if (strcasecmp(name, pattern) == 0) { return (1); } /* * See if the pattern is a parent domain of the hostname. */ else { if (list->flags & MATCH_FLAG_PARENT) { pd = name + strlen(name) - strlen(pattern); if (pd > name && pd[-1] == '.' && strcasecmp(pd, pattern) == 0) return (1); } else if (pattern[0] == '.') { pd = name + strlen(name) - strlen(pattern); if (pd > name && strcasecmp(pd, pattern) == 0) return (1); } } return (0); }