+ /*
+ * XXX: does it make sense to compile the slash at the beginning?
+ */
+ jb_err err = compile_pattern(p, LEFT_ANCHORED, url, &url->pattern.url_spec.preg);
+
+ if (JB_ERR_OK != err)
+ {
+ return err;
+ }
+ }
+ *p = '\0';
+ }
+
+ /*
+ * IPv6 numeric hostnames can contain colons, thus we need
+ * to delimit the hostname before the real port separator.
+ * As brackets are already used in the hostname pattern,
+ * we use angle brackets ('<', '>') instead.
+ */
+ if ((buf[0] == '<') && (NULL != (p = strchr(buf + 1, '>'))))
+ {
+ *p++ = '\0';
+ buf++;
+
+ if (*p == '\0')
+ {
+ /* IPv6 address without port number */
+ p = NULL;
+ }
+ else if (*p != ':')
+ {
+ /* Garbage after address delimiter */
+ return JB_ERR_PARSE;
+ }
+ }
+ else
+ {
+ p = strchr(buf, ':');
+ }
+
+ if (NULL != p)
+ {
+ *p++ = '\0';
+ url->pattern.url_spec.port_list = strdup_or_die(p);
+ }
+ else
+ {
+ url->pattern.url_spec.port_list = NULL;
+ }
+
+ if (buf[0] != '\0')
+ {
+ return compile_host_pattern(url, buf);
+ }
+
+ return JB_ERR_OK;
+
+}
+
+
+#ifdef FEATURE_EXTENDED_HOST_PATTERNS
+/*********************************************************************
+ *
+ * Function : compile_host_pattern
+ *
+ * Description : Parses and compiles a host pattern.
+ *
+ * Parameters :
+ * 1 : url = Target pattern_spec to be filled in.
+ * 2 : host_pattern = Host pattern to compile.
+ *
+ * Returns : JB_ERR_OK - Success
+ * JB_ERR_MEMORY - Out of memory
+ * JB_ERR_PARSE - Cannot parse regex
+ *
+ *********************************************************************/
+static jb_err compile_host_pattern(struct pattern_spec *url, const char *host_pattern)
+{
+ return compile_pattern(host_pattern, RIGHT_ANCHORED_HOST, url, &url->pattern.url_spec.host_regex);
+}
+
+#else
+
+/*********************************************************************
+ *
+ * Function : compile_host_pattern
+ *
+ * Description : Parses and "compiles" an old-school host pattern.
+ *
+ * Parameters :
+ * 1 : url = Target pattern_spec to be filled in.
+ * 2 : host_pattern = Host pattern to parse.
+ *
+ * Returns : JB_ERR_OK - Success
+ * JB_ERR_PARSE - Cannot parse regex
+ *
+ *********************************************************************/
+static jb_err compile_host_pattern(struct pattern_spec *url, const char *host_pattern)
+{
+ char *v[150];
+ size_t size;
+ char *p;
+
+ /*
+ * Parse domain part
+ */
+ if (host_pattern[strlen(host_pattern) - 1] == '.')
+ {
+ url->pattern.url_spec.unanchored |= ANCHOR_RIGHT;
+ }
+ if (host_pattern[0] == '.')
+ {
+ url->pattern.url_spec.unanchored |= ANCHOR_LEFT;
+ }
+
+ /*
+ * Split domain into components
+ */
+ url->pattern.url_spec.dbuffer = strdup_or_die(host_pattern);
+
+ /*
+ * Map to lower case
+ */
+ for (p = url->pattern.url_spec.dbuffer; *p ; p++)
+ {
+ *p = (char)privoxy_tolower(*p);
+ }
+
+ /*
+ * Split the domain name into components
+ */
+ url->pattern.url_spec.dcount = ssplit(url->pattern.url_spec.dbuffer, ".", v, SZ(v));
+
+ if (url->pattern.url_spec.dcount < 0)
+ {
+ free_pattern_spec(url);
+ return JB_ERR_PARSE;
+ }
+ else if (url->pattern.url_spec.dcount != 0)
+ {
+ /*
+ * Save a copy of the pointers in dvec
+ */
+ size = (size_t)url->pattern.url_spec.dcount * sizeof(*url->pattern.url_spec.dvec);
+
+ url->pattern.url_spec.dvec = malloc_or_die(size);
+
+ memcpy(url->pattern.url_spec.dvec, v, size);
+ }
+ /*
+ * else dcount == 0 in which case we needn't do anything,
+ * since dvec will never be accessed and the pattern will
+ * match all domains.
+ */
+ return JB_ERR_OK;
+}
+
+
+/*********************************************************************
+ *
+ * Function : simplematch
+ *
+ * Description : String matching, with a (greedy) '*' wildcard that
+ * stands for zero or more arbitrary characters and
+ * character classes in [], which take both enumerations
+ * and ranges.
+ *
+ * Parameters :
+ * 1 : pattern = pattern for matching
+ * 2 : text = text to be matched
+ *
+ * Returns : 0 if match, else nonzero
+ *
+ *********************************************************************/
+static int simplematch(const char *pattern, const char *text)
+{
+ const unsigned char *pat = (const unsigned char *)pattern;
+ const unsigned char *txt = (const unsigned char *)text;
+ const unsigned char *fallback = pat;
+ int wildcard = 0;
+
+ unsigned char lastchar = 'a';
+ unsigned i;
+ unsigned char charmap[32];
+
+ while (*txt)
+ {
+
+ /* EOF pattern but !EOF text? */
+ if (*pat == '\0')
+ {
+ if (wildcard)
+ {
+ pat = fallback;
+ }
+ else
+ {
+ return 1;
+ }
+ }
+
+ /* '*' in the pattern? */
+ if (*pat == '*')
+ {
+
+ /* The pattern ends afterwards? Speed up the return. */
+ if (*++pat == '\0')
+ {
+ return 0;
+ }
+
+ /* Else, set wildcard mode and remember position after '*' */
+ wildcard = 1;
+ fallback = pat;
+ }
+
+ /* Character range specification? */
+ if (*pat == '[')
+ {
+ memset(charmap, '\0', sizeof(charmap));
+
+ while (*++pat != ']')
+ {
+ if (!*pat)
+ {
+ return 1;
+ }
+ else if (*pat == '-')
+ {
+ if ((*++pat == ']') || *pat == '\0')
+ {
+ return(1);
+ }
+ for (i = lastchar; i <= *pat; i++)
+ {
+ charmap[i / 8] |= (unsigned char)(1 << (i % 8));
+ }
+ }
+ else
+ {
+ charmap[*pat / 8] |= (unsigned char)(1 << (*pat % 8));
+ lastchar = *pat;
+ }
+ }
+ } /* -END- if Character range specification */
+
+
+ /*
+ * Char match, or char range match?
+ */
+ if ((*pat == *txt)
+ || (*pat == '?')
+ || ((*pat == ']') && (charmap[*txt / 8] & (1 << (*txt % 8)))))
+ {
+ /*
+ * Success: Go ahead
+ */
+ pat++;
+ }
+ else if (!wildcard)
+ {
+ /*
+ * No match && no wildcard: No luck
+ */
+ return 1;
+ }
+ else if (pat != fallback)
+ {
+ /*
+ * Increment text pointer if in char range matching
+ */
+ if (*pat == ']')
+ {
+ txt++;
+ }
+ /*
+ * Wildcard mode && nonmatch beyond fallback: Rewind pattern
+ */
+ pat = fallback;
+ /*
+ * Restart matching from current text pointer
+ */
+ continue;
+ }
+ txt++;
+ }
+
+ /* Cut off extra '*'s */
+ if (*pat == '*') pat++;
+
+ /* If this is the pattern's end, fine! */
+ return(*pat);
+
+}
+
+
+/*********************************************************************
+ *
+ * Function : simple_domaincmp
+ *
+ * Description : Domain-wise Compare fqdn's. The comparison is
+ * both left- and right-anchored. The individual
+ * domain names are compared with simplematch().
+ * This is only used by domain_match.
+ *
+ * Parameters :
+ * 1 : pv = array of patterns to compare
+ * 2 : fv = array of domain components to compare
+ * 3 : len = length of the arrays (both arrays are the
+ * same length - if they weren't, it couldn't
+ * possibly be a match).
+ *
+ * Returns : 0 => domains are equivalent, else no match.
+ *
+ *********************************************************************/
+static int simple_domaincmp(char **pv, char **fv, int len)
+{
+ int n;
+
+ for (n = 0; n < len; n++)
+ {
+ if (simplematch(pv[n], fv[n]))
+ {
+ return 1;
+ }
+ }
+
+ return 0;
+
+}
+
+
+/*********************************************************************
+ *
+ * Function : domain_match
+ *
+ * Description : Domain-wise Compare fqdn's. Governed by the bimap in
+ * p.pattern->unachored, the comparison is un-, left-,
+ * right-anchored, or both.
+ * The individual domain names are compared with
+ * simplematch().
+ *
+ * Parameters :
+ * 1 : p = a domain that may contain a '*' as a wildcard.
+ * 2 : fqdn = domain name against which the patterns are compared.
+ *
+ * Returns : 0 => domains are equivalent, else no match.
+ *
+ *********************************************************************/
+static int domain_match(const struct pattern_spec *p, const struct http_request *fqdn)
+{
+ char **pv, **fv; /* vectors */
+ int plen, flen;
+ int unanchored = p->pattern.url_spec.unanchored & (ANCHOR_RIGHT | ANCHOR_LEFT);
+
+ plen = p->pattern.url_spec.dcount;
+ flen = fqdn->dcount;
+
+ if (flen < plen)
+ {
+ /* fqdn is too short to match this pattern */
+ return 1;
+ }
+
+ pv = p->pattern.url_spec.dvec;
+ fv = fqdn->dvec;
+
+ if (unanchored == ANCHOR_LEFT)
+ {
+ /*
+ * Right anchored.
+ *
+ * Convert this into a fully anchored pattern with
+ * the fqdn and pattern the same length
+ */
+ fv += (flen - plen); /* flen - plen >= 0 due to check above */
+ return simple_domaincmp(pv, fv, plen);
+ }
+ else if (unanchored == 0)
+ {
+ /* Fully anchored, check length */
+ if (flen != plen)
+ {
+ return 1;
+ }
+ return simple_domaincmp(pv, fv, plen);
+ }
+ else if (unanchored == ANCHOR_RIGHT)