From 8971fcf991ea8faf1eed8f6e9aa8c7604a8f73ed Mon Sep 17 00:00:00 2001 From: Vladimir Dubrovin <3proxy@3proxy.ru> Date: Thu, 27 Aug 2026 17:27:50 +0300 Subject: [PATCH] Move patternmatch from common.c --- src/acl.c | 241 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/common.c | 238 -------------------------------------------------- 2 files changed, 241 insertions(+), 238 deletions(-) diff --git a/src/acl.c b/src/acl.c index fdf124c..719cb37 100644 --- a/src/acl.c +++ b/src/acl.c @@ -8,6 +8,247 @@ #include "proxy.h" +/* The pattern engine lives here rather than in common.c: common.c is linked + into the standalone binaries as well, and those carry neither the regular + expression code this calls nor a use for a host pattern. */ +/* Host lists in access rules have always accepted name, name*, *name and + *name*, with the leading and trailing star recorded as a match type rather + than kept in the string. The parser and the comparison are here so that + anything else matching a name against a pattern - the http command, and + whatever replaces the star with a regular expression later - behaves the same + way and gains the same syntax at the same time. + */ +/* A pattern written as a regular expression carries a prefix. Both spellings + are taken so a configuration reads the way its author thinks of it. */ +static unsigned char * regexprefix(unsigned char *arg) +{ + if(!strncmp((char *)arg, "pcre:", 5)) return arg + 5; + if(!strncmp((char *)arg, "regex:", 6)) return arg + 6; + return NULL; +} + +/* Shared by every pattern the configuration can carry. Returns 0 on success. */ +static int compileregex(struct hostname *h, unsigned char *pattern) +{ +#ifdef WITH_PCRE + char err[256]; + + h->re = pcre_pattern_compile(pattern, err, sizeof(err)); + if(!h->re){ + fprintf(stderr, "Bad regular expression '%s': %s\n", pattern, err); + return 1; + } + h->matchtype = MATCHREGEX; + h->name = (unsigned char *)strdup((char *)pattern); + return h->name? 0 : 1; +#else + fprintf(stderr, "Regular expression '%s' needs a build with PCRE\n", pattern); + return 1; +#endif +} + +int parsepattern(struct hostname *h, unsigned char *arg) +{ + int arglen; + unsigned char *pattern; + + h->re = NULL; + if((pattern = regexprefix(arg))) return compileregex(h, pattern); + + arglen = (int)strlen((char *)arg); + h->matchtype = 3; + pattern = arg; + + if(arglen && pattern[arglen-1] == '*'){ + arglen--; + pattern[arglen] = 0; + h->matchtype ^= MATCHEND; + } + if(arglen && pattern[0] == '*'){ + pattern++; + arglen--; + h->matchtype ^= MATCHBEGIN; + } + + h->name = (unsigned char *)strdup((char *)pattern); + return h->name? 0 : 1; +} + +/* Matches str against a pattern and reports the part a star stood for. Where a + pattern has a star at both ends the trailing one is reported, since that is + the part following the text that was matched. An exact pattern leaves an + empty span. */ +int patternmatchpos(const struct hostname *h, const unsigned char *str, int *start, int *len) +{ + int lname, lstr, pos = 0, match = 0; + char *found; + + if(!h->name || !str) return 0; + if(h->matchtype == MATCHREGEX || h->matchtype == MATCHGLOB){ + struct capture caps[MAXCAPTURES]; + + if(!patternmatchcaps(h, str, caps, NULL)) return 0; + if(start) *start = caps[1].start; + if(len) *len = caps[1].len; + return 1; + } + + lname = (int)strlen((char *)h->name); + lstr = (int)strlen((char *)str); + + switch(h->matchtype){ + case 0: +#ifndef _WIN32 + found = strcasestr((char *)str, (char *)h->name); +#else + found = strstr((char *)str, (char *)h->name); +#endif + if(found){ + match = 1; + pos = (int)(found - (char *)str) + lname; + } + break; + + case 1: + if(!strncasecmp((char *)str, (char *)h->name, lname)){ + match = 1; + pos = lname; + } + break; + + case 2: + if(lstr >= lname && + !strncasecmp((char *)str + (lstr - lname), (char *)h->name, lname)){ + match = 1; + pos = 0; + if(start) *start = 0; + if(len) *len = lstr - lname; + return 1; + } + break; + + default: + if(!strcasecmp((char *)str, (char *)h->name)){ + match = 1; + pos = lstr; + } + break; + } + + if(!match) return 0; + + if(start) *start = pos; + if(len) *len = lstr - pos; + return 1; +} + +int patternmatch(const struct hostname *h, const unsigned char *str) +{ + return patternmatchcaps(h, str, NULL, NULL); +} + +/* Match a glob, recording what each star stood for. + + A single star stands for any run of characters within one element of the + path, so it stops at a slash; a double star crosses them. Stars are + numbered in the order they appear, which is how a template refers to them. + */ +static int globmatch(const unsigned char *pat, const unsigned char *str, + const unsigned char *subject, struct capture *caps, int maxcaps, int star) +{ + while(*pat){ + if(*pat == '*'){ + int crosses = (pat[1] == '*'); + const unsigned char *rest = pat + (crosses? 2 : 1); + int len; + + for(len = 0; ; len++){ + if(star < maxcaps && caps){ + caps[star].start = (int)(str - subject); + caps[star].len = len; + } + if(globmatch(rest, str + len, subject, caps, maxcaps, star + 1)) return 1; + if(!str[len]) return 0; + if(!crosses && str[len] == '/') return 0; + } + } + if(*pat != *str) return 0; + pat++; + str++; + } + return *str == 0; +} + +/* Match a pattern of any kind and report what its stars or groups stood for. + caps may be NULL when only the yes or no answer is wanted. */ +int patternmatchcaps(const struct hostname *h, const unsigned char *str, + struct capture *caps, int *ncaps) +{ + int n = 0; + + if(ncaps) *ncaps = 0; + if(!h || !str) return 0; + + if(h->matchtype == MATCHREGEX){ +#ifdef WITH_PCRE + struct capture local[MAXCAPTURES]; + + n = pcre_pattern_match(h->re, str, caps? caps : local, MAXCAPTURES); + if(ncaps) *ncaps = n; + return n > 0; +#else + return 0; +#endif + } + + if(h->matchtype == MATCHGLOB){ + struct capture local[MAXCAPTURES]; + struct capture *use = caps? caps : local; + int i; + + for(i = 0; i < MAXCAPTURES; i++){ + use[i].start = 0; + use[i].len = 0; + } + use[0].start = 0; + use[0].len = (int)strlen((char *)str); + if(!h->name) return 0; + if(!globmatch(h->name, str, str, use, MAXCAPTURES, 1)) return 0; + if(ncaps){ + for(n = MAXCAPTURES - 1; n > 0 && !use[n].len && !use[n].start; n--); + *ncaps = n + 1; + } + return 1; + } + + /* the star at one end or both, as an access rule has always written it */ + if(caps){ + int start = 0, len = 0; + + if(!patternmatchpos(h, str, &start, &len)) return 0; + caps[0].start = 0; + caps[0].len = (int)strlen((char *)str); + caps[1].start = start; + caps[1].len = len; + if(ncaps) *ncaps = 2; + return 1; + } + return patternmatchpos(h, str, NULL, NULL); +} + +/* A URL in an http rule: stars anywhere, or a regular expression. */ +int parsepathpattern(struct hostname *h, unsigned char *arg) +{ + unsigned char *pattern; + + h->re = NULL; + if((pattern = regexprefix(arg))) return compileregex(h, pattern); + + h->matchtype = MATCHGLOB; + h->name = (unsigned char *)strdup((char *)arg); + return h->name? 0 : 1; +} + int IPInentry(struct sockaddr *sa, struct iplist *ipentry){ int addrlen; unsigned char *ip, *ipf, *ipt; diff --git a/src/common.c b/src/common.c index 6e28aef..fae4e62 100644 --- a/src/common.c +++ b/src/common.c @@ -810,244 +810,6 @@ int bindwithrange(struct clientparam *param, SOCKET sock, PROXYSOCKADDRTYPE *sa, return param->srv->so._bind(param->sostate, sock, (struct sockaddr *)sa, SASIZE(sa)); } -/* Host lists in access rules have always accepted name, name*, *name and - *name*, with the leading and trailing star recorded as a match type rather - than kept in the string. The parser and the comparison are here so that - anything else matching a name against a pattern - the http command, and - whatever replaces the star with a regular expression later - behaves the same - way and gains the same syntax at the same time. - */ -/* A pattern written as a regular expression carries a prefix. Both spellings - are taken so a configuration reads the way its author thinks of it. */ -static unsigned char * regexprefix(unsigned char *arg) -{ - if(!strncmp((char *)arg, "pcre:", 5)) return arg + 5; - if(!strncmp((char *)arg, "regex:", 6)) return arg + 6; - return NULL; -} - -/* Shared by every pattern the configuration can carry. Returns 0 on success. */ -static int compileregex(struct hostname *h, unsigned char *pattern) -{ -#ifdef WITH_PCRE - char err[256]; - - h->re = pcre_pattern_compile(pattern, err, sizeof(err)); - if(!h->re){ - fprintf(stderr, "Bad regular expression '%s': %s\n", pattern, err); - return 1; - } - h->matchtype = MATCHREGEX; - h->name = (unsigned char *)strdup((char *)pattern); - return h->name? 0 : 1; -#else - fprintf(stderr, "Regular expression '%s' needs a build with PCRE\n", pattern); - return 1; -#endif -} - -int parsepattern(struct hostname *h, unsigned char *arg) -{ - int arglen; - unsigned char *pattern; - - h->re = NULL; - if((pattern = regexprefix(arg))) return compileregex(h, pattern); - - arglen = (int)strlen((char *)arg); - h->matchtype = 3; - pattern = arg; - - if(arglen && pattern[arglen-1] == '*'){ - arglen--; - pattern[arglen] = 0; - h->matchtype ^= MATCHEND; - } - if(arglen && pattern[0] == '*'){ - pattern++; - arglen--; - h->matchtype ^= MATCHBEGIN; - } - - h->name = (unsigned char *)strdup((char *)pattern); - return h->name? 0 : 1; -} - -/* Matches str against a pattern and reports the part a star stood for. Where a - pattern has a star at both ends the trailing one is reported, since that is - the part following the text that was matched. An exact pattern leaves an - empty span. */ -int patternmatchpos(const struct hostname *h, const unsigned char *str, int *start, int *len) -{ - int lname, lstr, pos = 0, match = 0; - char *found; - - if(!h->name || !str) return 0; - if(h->matchtype == MATCHREGEX || h->matchtype == MATCHGLOB){ - struct capture caps[MAXCAPTURES]; - - if(!patternmatchcaps(h, str, caps, NULL)) return 0; - if(start) *start = caps[1].start; - if(len) *len = caps[1].len; - return 1; - } - - lname = (int)strlen((char *)h->name); - lstr = (int)strlen((char *)str); - - switch(h->matchtype){ - case 0: -#ifndef _WIN32 - found = strcasestr((char *)str, (char *)h->name); -#else - found = strstr((char *)str, (char *)h->name); -#endif - if(found){ - match = 1; - pos = (int)(found - (char *)str) + lname; - } - break; - - case 1: - if(!strncasecmp((char *)str, (char *)h->name, lname)){ - match = 1; - pos = lname; - } - break; - - case 2: - if(lstr >= lname && - !strncasecmp((char *)str + (lstr - lname), (char *)h->name, lname)){ - match = 1; - pos = 0; - if(start) *start = 0; - if(len) *len = lstr - lname; - return 1; - } - break; - - default: - if(!strcasecmp((char *)str, (char *)h->name)){ - match = 1; - pos = lstr; - } - break; - } - - if(!match) return 0; - - if(start) *start = pos; - if(len) *len = lstr - pos; - return 1; -} - -int patternmatch(const struct hostname *h, const unsigned char *str) -{ - return patternmatchcaps(h, str, NULL, NULL); -} - -/* Match a glob, recording what each star stood for. - - A single star stands for any run of characters within one element of the - path, so it stops at a slash; a double star crosses them. Stars are - numbered in the order they appear, which is how a template refers to them. - */ -static int globmatch(const unsigned char *pat, const unsigned char *str, - const unsigned char *subject, struct capture *caps, int maxcaps, int star) -{ - while(*pat){ - if(*pat == '*'){ - int crosses = (pat[1] == '*'); - const unsigned char *rest = pat + (crosses? 2 : 1); - int len; - - for(len = 0; ; len++){ - if(star < maxcaps && caps){ - caps[star].start = (int)(str - subject); - caps[star].len = len; - } - if(globmatch(rest, str + len, subject, caps, maxcaps, star + 1)) return 1; - if(!str[len]) return 0; - if(!crosses && str[len] == '/') return 0; - } - } - if(*pat != *str) return 0; - pat++; - str++; - } - return *str == 0; -} - -/* Match a pattern of any kind and report what its stars or groups stood for. - caps may be NULL when only the yes or no answer is wanted. */ -int patternmatchcaps(const struct hostname *h, const unsigned char *str, - struct capture *caps, int *ncaps) -{ - int n = 0; - - if(ncaps) *ncaps = 0; - if(!h || !str) return 0; - - if(h->matchtype == MATCHREGEX){ -#ifdef WITH_PCRE - struct capture local[MAXCAPTURES]; - - n = pcre_pattern_match(h->re, str, caps? caps : local, MAXCAPTURES); - if(ncaps) *ncaps = n; - return n > 0; -#else - return 0; -#endif - } - - if(h->matchtype == MATCHGLOB){ - struct capture local[MAXCAPTURES]; - struct capture *use = caps? caps : local; - int i; - - for(i = 0; i < MAXCAPTURES; i++){ - use[i].start = 0; - use[i].len = 0; - } - use[0].start = 0; - use[0].len = (int)strlen((char *)str); - if(!h->name) return 0; - if(!globmatch(h->name, str, str, use, MAXCAPTURES, 1)) return 0; - if(ncaps){ - for(n = MAXCAPTURES - 1; n > 0 && !use[n].len && !use[n].start; n--); - *ncaps = n + 1; - } - return 1; - } - - /* the star at one end or both, as an access rule has always written it */ - if(caps){ - int start = 0, len = 0; - - if(!patternmatchpos(h, str, &start, &len)) return 0; - caps[0].start = 0; - caps[0].len = (int)strlen((char *)str); - caps[1].start = start; - caps[1].len = len; - if(ncaps) *ncaps = 2; - return 1; - } - return patternmatchpos(h, str, NULL, NULL); -} - -/* A URL in an http rule: stars anywhere, or a regular expression. */ -int parsepathpattern(struct hostname *h, unsigned char *arg) -{ - unsigned char *pattern; - - h->re = NULL; - if((pattern = regexprefix(arg))) return compileregex(h, pattern); - - h->matchtype = MATCHGLOB; - h->name = (unsigned char *)strdup((char *)arg); - return h->name? 0 : 1; -} - int scanaddr(const unsigned char *s, uint32_t * ip, uint32_t * mask) { unsigned d1, d2, d3, d4, m; int res;