X-Git-Url: http://www.privoxy.org/gitweb/?p=privoxy.git;a=blobdiff_plain;f=urlmatch.c;h=1e10c077440b95c31559835f96531e23c20c9afe;hp=4839c2b1941f5f2c7a53afddce67873dafbd67f4;hb=HEAD;hpb=d6f6d40eb51a7a7f491c5c027f83bbd7f44c009f diff --git a/urlmatch.c b/urlmatch.c index 4839c2b1..494a69a9 100644 --- a/urlmatch.c +++ b/urlmatch.c @@ -1,4 +1,3 @@ -const char urlmatch_rcs[] = "$Id: urlmatch.c,v 1.87 2016/02/26 12:29:39 fabiankeil Exp $"; /********************************************************************* * * File : $Source: /cvsroot/ijbswa/current/urlmatch.c,v $ @@ -6,8 +5,8 @@ const char urlmatch_rcs[] = "$Id: urlmatch.c,v 1.87 2016/02/26 12:29:39 fabianke * Purpose : Declares functions to match URLs against URL * patterns. * - * Copyright : Written by and Copyright (C) 2001-2014 - * the Privoxy team. http://www.privoxy.org/ + * Copyright : Written by and Copyright (C) 2001-2020 + * the Privoxy team. https://www.privoxy.org/ * * Based on the Internet Junkbuster originally written * by and Copyright (C) 1997 Anonymous Coders and @@ -46,7 +45,7 @@ const char urlmatch_rcs[] = "$Id: urlmatch.c,v 1.87 2016/02/26 12:29:39 fabianke #include #include -#if !defined(_WIN32) && !defined(__OS2__) +#if !defined(_WIN32) #include #endif @@ -56,8 +55,6 @@ const char urlmatch_rcs[] = "$Id: urlmatch.c,v 1.87 2016/02/26 12:29:39 fabianke #include "miscutil.h" #include "errlog.h" -const char urlmatch_h_rcs[] = URLMATCH_H_VERSION; - enum regex_anchoring { NO_ANCHORING, @@ -65,7 +62,10 @@ enum regex_anchoring RIGHT_ANCHORED, RIGHT_ANCHORED_HOST }; -static jb_err compile_host_pattern(struct pattern_spec *url, const char *host_pattern); +static jb_err compile_vanilla_host_pattern(struct pattern_spec *url, const char *host_pattern); +#ifdef FEATURE_PCRE_HOST_PATTERNS +static jb_err compile_pcre_host_pattern(struct pattern_spec *url, const char *host_pattern); +#endif /********************************************************************* * @@ -90,17 +90,14 @@ void free_http_request(struct http_request *http) freez(http->url); freez(http->hostport); freez(http->path); - freez(http->ver); + freez(http->version); freez(http->host_ip_addr_str); -#ifndef FEATURE_EXTENDED_HOST_PATTERNS freez(http->dbuffer); freez(http->dvec); http->dcount = 0; -#endif } -#ifndef FEATURE_EXTENDED_HOST_PATTERNS /********************************************************************* * * Function : init_domain_components @@ -108,7 +105,7 @@ void free_http_request(struct http_request *http) * Description : Splits the domain name so we can compare it * against wildcards. It used to be part of * parse_http_url, but was separated because the - * same code is required in chat in case of + * same code is required in chat() in case of * intercepted requests. * * Parameters : @@ -155,7 +152,6 @@ jb_err init_domain_components(struct http_request *http) return JB_ERR_OK; } -#endif /* ndef FEATURE_EXTENDED_HOST_PATTERNS */ /********************************************************************* @@ -249,7 +245,7 @@ jb_err parse_http_url(const char *url, struct http_request *http, int require_pr /* - * Split URL into protocol,hostport,path. + * Split URL into protocol, hostport, path. */ { char *buf; @@ -267,7 +263,9 @@ jb_err parse_http_url(const char *url, struct http_request *http, int require_pr else if (strncmpic(url_noproto, "https://", 8) == 0) { /* - * Should only happen when called from cgi_show_url_info(). + * Should only happen when called from cgi_show_url_info() + * or when the request was https-inspected and the request + * line got rewritten. */ url_noproto += 8; http->ssl = 1; @@ -294,14 +292,19 @@ jb_err parse_http_url(const char *url, struct http_request *http, int require_pr /* * Got a path. * - * NOTE: The following line ignores the path for HTTPS URLS. - * This means that you get consistent behaviour if you type a - * https URL in and it's parsed by the function. (When the - * URL is actually retrieved, SSL hides the path part). + * If FEATURE_HTTPS_INSPECTION isn't available, ignore the + * path for https URLs so that we get consistent behaviour + * if a https URL is parsed. When the URL is actually + * retrieved, https hides the path part. */ - http->path = strdup_or_die(http->ssl ? "/" : url_path); + http->path = strdup_or_die( +#ifndef FEATURE_HTTPS_INSPECTION + http->ssl ? "/" : +#endif + url_path + ); *url_path = '\0'; - http->hostport = strdup_or_die(url_noproto); + http->hostport = string_tolower(url_noproto); } else { @@ -310,10 +313,15 @@ jb_err parse_http_url(const char *url, struct http_request *http, int require_pr * or CONNECT requests */ http->path = strdup_or_die("/"); - http->hostport = strdup_or_die(url_noproto); + http->hostport = string_tolower(url_noproto); } freez(buf); + + if (http->hostport == NULL) + { + return JB_ERR_PARSE; + } } if (!host_available) @@ -406,12 +414,8 @@ jb_err parse_http_url(const char *url, struct http_request *http, int require_pr freez(buf); } -#ifdef FEATURE_EXTENDED_HOST_PATTERNS - return JB_ERR_OK; -#else /* Split domain name so we can compare it against wildcards */ return init_domain_components(http); -#endif /* def FEATURE_EXTENDED_HOST_PATTERNS */ } @@ -494,7 +498,7 @@ static int unknown_method(const char *method) * JB_ERR_PARSE if the HTTP version is unsupported * *********************************************************************/ -jb_err static normalize_http_version(char *http_version) +static jb_err normalize_http_version(char *http_version) { unsigned int major_version; unsigned int minor_version; @@ -590,7 +594,7 @@ jb_err parse_http_request(const char *req, struct http_request *http) */ http->cmd = strdup_or_die(req); http->gpc = strdup_or_die(v[0]); - http->ver = strdup_or_die(v[2]); + http->version = strdup_or_die(v[2]); http->ocmd = strdup_or_die(http->cmd); freez(buf); @@ -600,6 +604,7 @@ jb_err parse_http_request(const char *req, struct http_request *http) } +#ifdef HAVE_PCRE2 /********************************************************************* * * Function : compile_pattern @@ -621,14 +626,16 @@ jb_err parse_http_request(const char *req, struct http_request *http) * *********************************************************************/ static jb_err compile_pattern(const char *pattern, enum regex_anchoring anchoring, - struct pattern_spec *url, regex_t **regex) + struct pattern_spec *url, pcre2_code **regex) { int errcode; - char rebuf[BUFFER_SIZE]; const char *fmt = NULL; + char *rebuf; + size_t rebuf_size; + PCRE2_SIZE error_offset; + int ret; assert(pattern); - assert(strlen(pattern) < sizeof(rebuf) - 2); if (pattern[0] == '\0') { @@ -654,31 +661,126 @@ static jb_err compile_pattern(const char *pattern, enum regex_anchoring anchorin log_error(LOG_LEVEL_FATAL, "Invalid anchoring in compile_pattern %d", anchoring); } + rebuf_size = strlen(pattern) + strlen(fmt); + rebuf = malloc_or_die(rebuf_size); + + snprintf(rebuf, rebuf_size, fmt, pattern); + + *regex = pcre2_compile((const unsigned char *)rebuf, + PCRE2_ZERO_TERMINATED, PCRE2_CASELESS, &errcode, + &error_offset, NULL); + if (*regex == NULL) + { + log_error(LOG_LEVEL_ERROR, "error compiling %s from %s: %s", + pattern, url->spec, rebuf); + freez(rebuf); + + return JB_ERR_PARSE; + } + +#ifndef DISABLE_PCRE_JIT_COMPILATION + /* Try to enable JIT compilation but continue if it's unsupported. */ + if ((ret = pcre2_jit_compile(*regex, PCRE2_JIT_COMPLETE)) && + (ret != PCRE2_ERROR_JIT_BADOPTION)) + { + log_error(LOG_LEVEL_ERROR, + "Unexpected error enabling JIT compilation for %s from %s: %s", + pattern, url->spec, rebuf); + freez(rebuf); + + return JB_ERR_PARSE; + } +#endif + + freez(rebuf); + + return JB_ERR_OK; + +} +#else +/********************************************************************* + * + * Function : compile_pattern + * + * Description : Compiles a host, domain or TAG pattern. + * + * Parameters : + * 1 : pattern = The pattern to compile. + * 2 : anchoring = How the regex should be modified + * before compilation. Can be either + * one of NO_ANCHORING, LEFT_ANCHORED, + * RIGHT_ANCHORED or RIGHT_ANCHORED_HOST. + * 3 : url = In case of failures, the spec member is + * logged and the structure freed. + * 4 : regex = Where the compiled regex should be stored. + * + * Returns : JB_ERR_OK - Success + * JB_ERR_PARSE - Cannot parse regex + * + *********************************************************************/ +static jb_err compile_pattern(const char *pattern, enum regex_anchoring anchoring, + struct pattern_spec *url, regex_t **regex) +{ + int errcode; + const char *fmt = NULL; + char *rebuf; + size_t rebuf_size; + + assert(pattern); + + if (pattern[0] == '\0') + { + *regex = NULL; + return JB_ERR_OK; + } + switch (anchoring) + { + case NO_ANCHORING: + fmt = "%s"; + break; + case RIGHT_ANCHORED: + fmt = "%s$"; + break; + case RIGHT_ANCHORED_HOST: + fmt = "%s\\.?$"; + break; + case LEFT_ANCHORED: + fmt = "^%s"; + break; + default: + log_error(LOG_LEVEL_FATAL, + "Invalid anchoring in compile_pattern %d", anchoring); + } + rebuf_size = strlen(pattern) + strlen(fmt); + rebuf = malloc_or_die(rebuf_size); *regex = zalloc_or_die(sizeof(**regex)); - snprintf(rebuf, sizeof(rebuf), fmt, pattern); + snprintf(rebuf, rebuf_size, fmt, pattern); errcode = regcomp(*regex, rebuf, (REG_EXTENDED|REG_NOSUB|REG_ICASE)); if (errcode) { - size_t errlen = regerror(errcode, *regex, rebuf, sizeof(rebuf)); - if (errlen > (sizeof(rebuf) - (size_t)1)) + size_t errlen = regerror(errcode, *regex, rebuf, rebuf_size); + if (errlen > (rebuf_size - (size_t)1)) { - errlen = sizeof(rebuf) - (size_t)1; + errlen = rebuf_size - (size_t)1; } rebuf[errlen] = '\0'; log_error(LOG_LEVEL_ERROR, "error compiling %s from %s: %s", pattern, url->spec, rebuf); free_pattern_spec(url); + freez(rebuf); return JB_ERR_PARSE; } + freez(rebuf); return JB_ERR_OK; } +#endif /********************************************************************* @@ -699,6 +801,36 @@ static jb_err compile_pattern(const char *pattern, enum regex_anchoring anchorin static jb_err compile_url_pattern(struct pattern_spec *url, char *buf) { char *p; + const size_t prefix_length = 18; + +#ifdef FEATURE_PCRE_HOST_PATTERNS + if (strncmpic(buf, "PCRE-HOST-PATTERN:", prefix_length) == 0) + { + url->pattern.url_spec.host_regex_type = PCRE_HOST_PATTERN; + /* Overwrite the "PCRE-HOST-PATTERN:" prefix */ + memmove(buf, buf+prefix_length, strlen(buf+prefix_length)+1); + } + else + { + url->pattern.url_spec.host_regex_type = VANILLA_HOST_PATTERN; + } +#else + if (strncmpic(buf, "PCRE-HOST-PATTERN:", prefix_length) == 0) + { + log_error(LOG_LEVEL_ERROR, + "PCRE-HOST-PATTERN detected while Privoxy has been compiled " + "without FEATURE_PCRE_HOST_PATTERNS: %s", + buf); + /* Overwrite the "PCRE-HOST-PATTERN:" prefix */ + memmove(buf, buf+prefix_length, strlen(buf+prefix_length)+1); + /* + * The pattern will probably not work as expected. + * We don't simply return JB_ERR_PARSE here so the + * regression tests can be loaded with and without + * FEATURE_PCRE_HOST_PATTERNS. + */ + } +#endif p = strchr(buf, '/'); if (NULL != p) @@ -761,7 +893,16 @@ static jb_err compile_url_pattern(struct pattern_spec *url, char *buf) if (buf[0] != '\0') { - return compile_host_pattern(url, buf); +#ifdef FEATURE_PCRE_HOST_PATTERNS + if (url->pattern.url_spec.host_regex_type == PCRE_HOST_PATTERN) + { + return compile_pcre_host_pattern(url, buf); + } + else +#endif + { + return compile_vanilla_host_pattern(url, buf); + } } return JB_ERR_OK; @@ -769,12 +910,12 @@ static jb_err compile_url_pattern(struct pattern_spec *url, char *buf) } -#ifdef FEATURE_EXTENDED_HOST_PATTERNS +#ifdef FEATURE_PCRE_HOST_PATTERNS /********************************************************************* * - * Function : compile_host_pattern + * Function : compile_pcre_host_pattern * - * Description : Parses and compiles a host pattern. + * Description : Parses and compiles a pcre host pattern. * * Parameters : * 1 : url = Target pattern_spec to be filled in. @@ -785,16 +926,16 @@ static jb_err compile_url_pattern(struct pattern_spec *url, char *buf) * JB_ERR_PARSE - Cannot parse regex * *********************************************************************/ -static jb_err compile_host_pattern(struct pattern_spec *url, const char *host_pattern) +static jb_err compile_pcre_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); } +#endif /* def FEATURE_PCRE_HOST_PATTERNS */ -#else /********************************************************************* * - * Function : compile_host_pattern + * Function : compile_vanilla_host_pattern * * Description : Parses and "compiles" an old-school host pattern. * @@ -806,7 +947,7 @@ static jb_err compile_host_pattern(struct pattern_spec *url, const char *host_pa * JB_ERR_PARSE - Cannot parse regex * *********************************************************************/ -static jb_err compile_host_pattern(struct pattern_spec *url, const char *host_pattern) +static jb_err compile_vanilla_host_pattern(struct pattern_spec *url, const char *host_pattern) { char *v[150]; size_t size; @@ -1005,6 +1146,72 @@ static int simplematch(const char *pattern, const char *text) } +#ifdef HAVE_PCRE2 +/********************************************************************* + * + * Function : pcre2_pattern_matches + * + * Description : Checks if a compiled pcre2 pattern matches a string. + * + * Parameters : + * 1 : pattern = The compiled pattern + * 2 : string = The string to check + * + * Returns : TRUE for yes, FALSE otherwise. + * + *********************************************************************/ +static int pcre2_pattern_matches(const pcre2_code *pattern, const char *string) +{ + PCRE2_SIZE offset; + int ret; + pcre2_match_data *pcre2_matches; + + assert(pattern != NULL); + assert(string != NULL); + + offset = 0; + + pcre2_matches = pcre2_match_data_create_from_pattern(pattern, NULL); + if (NULL == pcre2_matches) + { + log_error(LOG_LEVEL_ERROR, + "Out of memory while matching pattern against %s", string); + return FALSE; + } + + ret = pcre2_match(pattern, (const unsigned char *)string, strlen(string), + offset, 0, pcre2_matches, NULL); + + pcre2_match_data_free(pcre2_matches); + + return (ret >= 0); +} +#endif + + +/********************************************************************* + * + * Function : regex_matches + * + * Description : Checks if a compiled regex pattern matches a string + * using either pcre2 or pcre1 code. + * + * Parameters : + * 1 : pattern = The compiled pattern + * 2 : string = The string to check + * + * Returns : TRUE for yes, FALSE otherwise. + * + *********************************************************************/ +int regex_matches(const REGEX_TYPE *pattern, const char *string) +{ +#ifdef HAVE_PCRE2 + return pcre2_pattern_matches(pattern, string); +#else + return (0 == regexec(pattern, string, 0, NULL, 0)); +#endif +} + /********************************************************************* * * Function : simple_domaincmp @@ -1122,7 +1329,6 @@ static int domain_match(const struct pattern_spec *p, const struct http_request } } -#endif /* def FEATURE_EXTENDED_HOST_PATTERNS */ /********************************************************************* @@ -1161,9 +1367,9 @@ jb_err create_pattern_spec(struct pattern_spec *pattern, char *buf) const unsigned flag; } tag_pattern[] = { { "TAG:", 4, PATTERN_SPEC_TAG_PATTERN}, - #ifdef FEATURE_CLIENT_TAGS +#ifdef FEATURE_CLIENT_TAGS { "CLIENT-TAG:", 11, PATTERN_SPEC_CLIENT_TAG_PATTERN}, - #endif +#endif { "NO-REQUEST-TAG:", 15, PATTERN_SPEC_NO_REQUEST_TAG_PATTERN}, { "NO-RESPONSE-TAG:", 16, PATTERN_SPEC_NO_RESPONSE_TAG_PATTERN} }; @@ -1218,27 +1424,44 @@ void free_pattern_spec(struct pattern_spec *pattern) if (pattern == NULL) return; freez(pattern->spec); -#ifdef FEATURE_EXTENDED_HOST_PATTERNS + + if (!(pattern->flags & PATTERN_SPEC_URL_PATTERN)) + { + if (pattern->pattern.tag_regex) + { +#ifdef HAVE_PCRE2 + pcre2_code_free(pattern->pattern.tag_regex); +#else + regfree(pattern->pattern.tag_regex); + freez(pattern->pattern.tag_regex); +#endif + } + return; + } + +#ifdef FEATURE_PCRE_HOST_PATTERNS if (pattern->pattern.url_spec.host_regex) { +#ifdef HAVE_PCRE2 + pcre2_code_free(pattern->pattern.url_spec.host_regex); +#else regfree(pattern->pattern.url_spec.host_regex); freez(pattern->pattern.url_spec.host_regex); +#endif } -#else +#endif /* def FEATURE_PCRE_HOST_PATTERNS */ freez(pattern->pattern.url_spec.dbuffer); freez(pattern->pattern.url_spec.dvec); pattern->pattern.url_spec.dcount = 0; -#endif /* ndef FEATURE_EXTENDED_HOST_PATTERNS */ freez(pattern->pattern.url_spec.port_list); if (pattern->pattern.url_spec.preg) { +#ifdef HAVE_PCRE2 + pcre2_code_free(pattern->pattern.url_spec.preg); +#else regfree(pattern->pattern.url_spec.preg); freez(pattern->pattern.url_spec.preg); - } - if (pattern->pattern.tag_regex) - { - regfree(pattern->pattern.tag_regex); - freez(pattern->pattern.tag_regex); +#endif } } @@ -1279,12 +1502,14 @@ static int host_matches(const struct http_request *http, const struct pattern_spec *pattern) { assert(http->host != NULL); -#ifdef FEATURE_EXTENDED_HOST_PATTERNS - return ((NULL == pattern->pattern.url_spec.host_regex) - || (0 == regexec(pattern->pattern.url_spec.host_regex, http->host, 0, NULL, 0))); -#else - return ((NULL == pattern->pattern.url_spec.dbuffer) || (0 == domain_match(pattern, http))); +#ifdef FEATURE_PCRE_HOST_PATTERNS + if (pattern->pattern.url_spec.host_regex_type == PCRE_HOST_PATTERN) + { + return ((NULL == pattern->pattern.url_spec.host_regex) + || regex_matches(pattern->pattern.url_spec.host_regex, http->host)); + } #endif + return ((NULL == pattern->pattern.url_spec.dbuffer) || (0 == domain_match(pattern, http))); } @@ -1304,7 +1529,7 @@ static int host_matches(const struct http_request *http, static int path_matches(const char *path, const struct pattern_spec *pattern) { return ((NULL == pattern->pattern.url_spec.preg) - || (0 == regexec(pattern->pattern.url_spec.preg, path, 0, NULL, 0))); + || regex_matches(pattern->pattern.url_spec.preg, path)); } @@ -1420,31 +1645,53 @@ int match_portlist(const char *portlist, int port) * * Function : parse_forwarder_address * - * Description : Parse out the host and port from a forwarder address. + * Description : Parse out the username, password, host and port from + * a forwarder address. * * Parameters : * 1 : address = The forwarder address to parse. * 2 : hostname = Used to return the hostname. NULL on error. * 3 : port = Used to return the port. Untouched if no port * is specified. + * 4 : username = Used to return the username if any. + * 5 : password = Used to return the password if any. * * Returns : JB_ERR_OK on success * JB_ERR_MEMORY on out of memory * JB_ERR_PARSE on malformed address. * *********************************************************************/ -jb_err parse_forwarder_address(char *address, char **hostname, int *port) +jb_err parse_forwarder_address(char *address, char **hostname, int *port, + char **username, char **password) { - char *p = address; + char *p; + char *tmp; + + tmp = *hostname = strdup_or_die(address); + + /* Parse username and password */ + if (username && password && (NULL != (p = strchr(*hostname, '@')))) + { + *p++ = '\0'; + *username = strdup_or_die(*hostname); + *hostname = strdup_or_die(p); - if ((*address == '[') && (NULL == strchr(address, ']'))) + if (NULL != (p = strchr(*username, ':'))) + { + *p++ = '\0'; + *password = strdup_or_die(p); + } + freez(tmp); + } + + /* Parse hostname and port */ + p = *hostname; + if ((*p == '[') && (NULL == strchr(p, ']'))) { /* XXX: Should do some more validity checks here. */ return JB_ERR_PARSE; } - *hostname = strdup_or_die(address); - if ((**hostname == '[') && (NULL != (p = strchr(*hostname, ']')))) { *p++ = '\0';