diff --git a/CHANGES b/CHANGES index 4c40fd5a0..e65da208f 100644 --- a/CHANGES +++ b/CHANGES @@ -6,6 +6,12 @@ Changelog +Daniel Stenberg (26 Jan 2009) +- Craig A West brought CURLOPT_NOPROXY and the corresponding --noproxy option. + They basically offer the same thing the NO_PROXY environment variable only + offered previously: list a set of host names that shall not use the proxy + even if one is specified. + Daniel Fandrich (20 Jan 2009) - Call setlocale() for libtest tests to test the effects of locale-induced libc changes on libcurl. @@ -20,8 +26,8 @@ Daniel Fandrich (20 Jan 2009) locale. Daniel Stenberg (20 Jan 2009) -- Lisa Xu pointed out that the ssh.obj file was missing from the lib/Makefile.vc6 - file (and thus from the vc8 and vc9 ones too). +- Lisa Xu pointed out that the ssh.obj file was missing from the + lib/Makefile.vc6 file (and thus from the vc8 and vc9 ones too). Version 7.19.3 (19 January 2009) diff --git a/RELEASE-NOTES b/RELEASE-NOTES index 8f29683ec..bb76da697 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -1,15 +1,15 @@ Curl and libcurl 7.19.4 Public curl releases: 110 - Command line options: 128 - curl_easy_setopt() options: 158 + Command line options: 129 + curl_easy_setopt() options: 159 Public functions in libcurl: 58 Known libcurl bindings: 37 Contributors: 700 This release includes the following changes: - o + o Added CURLOPT_NOPROXY and the corresponding --noproxy This release includes the following bugfixes: @@ -23,6 +23,6 @@ This release includes the following known bugs: This release would not have looked like this without help, code, reports and advice from friends like these: - Lisa Xu + Lisa Xu, Daniel Fandrich, Craig A West Thanks! (and sorry if I forgot to mention someone) diff --git a/TODO-RELEASE b/TODO-RELEASE index 70fbf7b29..decf186ff 100644 --- a/TODO-RELEASE +++ b/TODO-RELEASE @@ -1,8 +1,6 @@ To be addressed in 7.19.4 (planned release: March 2009) ========================= -205 - A. Craig West's CURLOPT_NOPROXY option - 206 - A. Craig West's CURLOPT_HTTP_VERSION change for CONNECT 208 - Patch to allow GSSAPI authentication to a socks5 server diff --git a/docs/MANUAL b/docs/MANUAL index 3aea5d40f..de2bdd280 100644 --- a/docs/MANUAL +++ b/docs/MANUAL @@ -136,6 +136,11 @@ PROXY curl -U user:passwd -x my-proxy:888 http://www.get.this/ + A comma-separated list of hosts and domains which do not use the proxy can + be specified as: + + curl --noproxy localhost,get.this -x my-proxy:888 http://www.get.this/ + curl also supports SOCKS4 and SOCKS5 proxies with --socks4 and --socks5. See also the environment variables Curl support that offer further proxy @@ -793,8 +798,9 @@ ENVIRONMENT VARIABLES NO_PROXY - If a tail substring of the domain-path for a host matches one of these - strings, transactions with that node will not be proxied. + If the host name matches one of these strings, or the host is within the + domain of one of these strings, transactions with that node will not be + proxied. The usage of the -x/--proxy flag overrides the environment variables. diff --git a/docs/curl.1 b/docs/curl.1 index cd9dba9d2..5b7c111f5 100644 --- a/docs/curl.1 +++ b/docs/curl.1 @@ -5,7 +5,7 @@ .\" * | (__| |_| | _ <| |___ .\" * \___|\___/|_| \_\_____| .\" * -.\" * Copyright (C) 1998 - 2008, Daniel Stenberg, , et al. +.\" * Copyright (C) 1998 - 2009, Daniel Stenberg, , et al. .\" * .\" * This software is licensed as described in the file COPYING, which .\" * you should have received as part of this distribution. The terms @@ -815,6 +815,13 @@ you to succeed. (Added in 7.16.0) Note that this is the negated option name documented. You can thus use \fI--sessionid\fP to enforce session-ID caching. +.IP "--noproxy " +Comma-separated list of hosts which do not use a proxy, if one is specified. +The only wildcard is a single * character, which matches all hosts, and +effectively disables the proxy. Each name in this list is matched as either +a domain which contains the hostname, or the hostname itself. For example, +local.com would match local.com, local.com:80, and www.local.com, but not +www.notlocal.com. .IP "--ntlm" (HTTP) Enables NTLM authentication. The NTLM authentication method was designed by Microsoft and is used by IIS web servers. It is a proprietary diff --git a/docs/libcurl/curl_easy_setopt.3 b/docs/libcurl/curl_easy_setopt.3 index 5aa64f553..c9bc0365c 100644 --- a/docs/libcurl/curl_easy_setopt.3 +++ b/docs/libcurl/curl_easy_setopt.3 @@ -5,7 +5,7 @@ .\" * | (__| |_| | _ <| |___ .\" * \___|\___/|_| \_\_____| .\" * -.\" * Copyright (C) 1998 - 2008, Daniel Stenberg, , et al. +.\" * Copyright (C) 1998 - 2009, Daniel Stenberg, , et al. .\" * .\" * This software is licensed as described in the file COPYING, which .\" * you should have received as part of this distribution. The terms @@ -474,6 +474,14 @@ this are \fICURLPROXY_HTTP\fP, \fICURLPROXY_SOCKS4\fP (added in 7.15.2), \fICURLPROXY_SOCKS5\fP, \fICURLPROXY_SOCKS4A\fP (added in 7.18.0) and \fICURLPROXY_SOCKS5_HOSTNAME\fP (added in 7.18.0). The HTTP type is default. (Added in 7.10) +.IP CURLOPT_NOPROXY +Pass a pointer to a zero terminated string. The should be a comma- separated +list of hosts which do not use a proxy, if one is specified. The only +wildcard is a single * character, which matches all hosts, and effectively +disables the proxy. Each name in this list is matched as either a domain which +contains the hostname, or the hostname itself. For example, local.com would +match local.com, local.com:80, and www.local.com, but not www.notlocal.com. +(Added in 7.19.4) .IP CURLOPT_HTTPPROXYTUNNEL Set the parameter to 1 to make the library tunnel all operations through a given HTTP proxy. There is a big difference between using a proxy and to diff --git a/include/curl/curl.h b/include/curl/curl.h index e94ff5f9d..830e8a1d8 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2008, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2009, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -1150,6 +1150,15 @@ typedef enum { CINIT(PROXYUSERNAME, OBJECTPOINT, 175), CINIT(PROXYPASSWORD, OBJECTPOINT, 176), + /* Comma separated list of hostnames defining no-proxy zones. These should + match both hostnames directly, and hostnames within a domain. For + example, local.com will match local.com and www.local.com, but NOT + notlocal.com or www.notlocal.com. For compatibility with other + implementations of this, .local.com will be considered to be the same as + local.com. A single * is the only valid wildcard, and effectively + disables the use of proxy. */ + CINIT(NOPROXY, OBJECTPOINT, 177), + CURLOPT_LASTENTRY /* the last unused */ } CURLoption; diff --git a/include/curl/typecheck-gcc.h b/include/curl/typecheck-gcc.h index 41cc7598e..39fad7acb 100644 --- a/include/curl/typecheck-gcc.h +++ b/include/curl/typecheck-gcc.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2008, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2009, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -201,6 +201,7 @@ _CURL_WARNING(_curl_easy_getinfo_err_curl_slist, (option) == CURLOPT_PROXYUSERPWD || \ (option) == CURLOPT_PROXYUSERNAME || \ (option) == CURLOPT_PROXYPASSWORD || \ + (option) == CURLOPT_NOPROXY || \ (option) == CURLOPT_ENCODING || \ (option) == CURLOPT_REFERER || \ (option) == CURLOPT_USERAGENT || \ diff --git a/lib/url.c b/lib/url.c index 47d2af342..6cefda2c1 100644 --- a/lib/url.c +++ b/lib/url.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2008, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2009, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -1677,6 +1677,13 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, result = setstropt(&data->set.str[STRING_PROXYPASSWORD], va_arg(param, char *)); break; + case CURLOPT_NOPROXY: + /* + * proxy exception list + */ + result = setstropt(&data->set.str[STRING_NOPROXY], + va_arg(param, char *)); + break; #endif case CURLOPT_RANGE: @@ -3368,6 +3375,80 @@ static CURLcode setup_connection_internals(struct SessionHandle *data, } #ifndef CURL_DISABLE_PROXY +/**************************************************************** +* Checks if the host is in the noproxy list. returns true if it matches +* and therefore the proxy should NOT be used. +****************************************************************/ +static bool check_noproxy(const char* name, const char* no_proxy) +{ + /* no_proxy=domain1.dom,host.domain2.dom + * (a comma-separated list of hosts which should + * not be proxied, or an asterisk to override + * all proxy variables) + */ + size_t tok_start; + size_t tok_end; + const char* separator = ", "; + size_t no_proxy_len; + size_t namelen; + char *endptr; + + if(no_proxy && no_proxy[0]) { + if(Curl_raw_equal("*", no_proxy)) { + return TRUE; + } + + /* NO_PROXY was specified and it wasn't just an asterisk */ + + no_proxy_len = strlen(no_proxy); + endptr = strchr(name, ':'); + if(endptr) + namelen = endptr - name; + else + namelen = strlen(name); + + tok_start = 0; + for (tok_start = 0; tok_start < no_proxy_len; tok_start = tok_end + 1) { + while (tok_start < no_proxy_len && + strchr(separator, no_proxy[tok_start]) != NULL) { + /* Look for the beginning of the token. */ + ++tok_start; + } + + if(tok_start == no_proxy_len) + break; /* It was all trailing separator chars, no more tokens. */ + + for (tok_end = tok_start; tok_end < no_proxy_len && + strchr(separator, no_proxy[tok_end]) == NULL; ++tok_end) { + /* Look for the end of the token. */ + } + + /* To match previous behaviour, where it was necessary to specify + * ".local.com" to prevent matching "notlocal.com", we will leave + * the '.' off. + */ + if(no_proxy[tok_start] == '.') + ++tok_start; + + if((tok_end - tok_start) <= namelen) { + /* Match the last part of the name to the domain we are checking. */ + const char *checkn = name + namelen - (tok_end - tok_start); + if(Curl_raw_nequal(no_proxy + tok_start, checkn, tok_end - tok_start)) { + if((tok_end - tok_start) == namelen || *(checkn - 1) == '.') { + /* We either have an exact match, or the previous character is a . + * so it is within the same domain, so no proxy for this host. + */ + return TRUE; + } + } + } /* if((tok_end - tok_start) <= namelen) */ + } /* for (tok_start = 0; tok_start < no_proxy_len; + tok_start = tok_end + 1) */ + } /* NO_PROXY was specified and it wasn't just an asterisk */ + + return FALSE; +} + /**************************************************************** * Detect what (if any) proxy to use. Remember that this selects a host * name and is not limited to HTTP proxies only. @@ -3396,90 +3477,56 @@ static char *detect_proxy(struct connectdata *conn) * checked if the lowercase versions don't exist. */ char *no_proxy=NULL; - char *no_proxy_tok_buf; char proxy_env[128]; no_proxy=curl_getenv("no_proxy"); if(!no_proxy) no_proxy=curl_getenv("NO_PROXY"); - if(!no_proxy || !Curl_raw_equal("*", no_proxy)) { - /* NO_PROXY wasn't specified or it wasn't just an asterisk */ - char *nope; + if(!check_noproxy(conn->host.name, no_proxy)) { + /* It was not listed as without proxy */ + char *protop = conn->protostr; + char *envp = proxy_env; + char *prox; - nope=no_proxy?strtok_r(no_proxy, ", ", &no_proxy_tok_buf):NULL; - while(nope) { - size_t namelen; - char *endptr = strchr(conn->host.name, ':'); - if(endptr) - namelen=endptr-conn->host.name; - else - namelen=strlen(conn->host.name); + /* Now, build _proxy and check for such a one to use */ + while(*protop) + *envp++ = (char)tolower((int)*protop++); - if(strlen(nope) <= namelen) { - char *checkn= - conn->host.name + namelen - strlen(nope); - if(checkprefix(nope, checkn)) { - /* no proxy for this host! */ - break; - } - } - nope=strtok_r(NULL, ", ", &no_proxy_tok_buf); - } - if(!nope) { - /* It was not listed as without proxy */ - char *protop = conn->protostr; - char *envp = proxy_env; - char *prox; + /* append _proxy */ + strcpy(envp, "_proxy"); - /* Now, build _proxy and check for such a one to use */ - while(*protop) - *envp++ = (char)tolower((int)*protop++); + /* read the protocol proxy: */ + prox=curl_getenv(proxy_env); - /* append _proxy */ - strcpy(envp, "_proxy"); - - /* read the protocol proxy: */ + /* + * We don't try the uppercase version of HTTP_PROXY because of + * security reasons: + * + * When curl is used in a webserver application + * environment (cgi or php), this environment variable can + * be controlled by the web server user by setting the + * http header 'Proxy:' to some value. + * + * This can cause 'internal' http/ftp requests to be + * arbitrarily redirected by any external attacker. + */ + if(!prox && !Curl_raw_equal("http_proxy", proxy_env)) { + /* There was no lowercase variable, try the uppercase version: */ + Curl_strntoupper(proxy_env, proxy_env, sizeof(proxy_env)); prox=curl_getenv(proxy_env); + } - /* - * We don't try the uppercase version of HTTP_PROXY because of - * security reasons: - * - * When curl is used in a webserver application - * environment (cgi or php), this environment variable can - * be controlled by the web server user by setting the - * http header 'Proxy:' to some value. - * - * This can cause 'internal' http/ftp requests to be - * arbitrarily redirected by any external attacker. - */ - if(!prox && !Curl_raw_equal("http_proxy", proxy_env)) { - /* There was no lowercase variable, try the uppercase version: */ - Curl_strntoupper(proxy_env, proxy_env, sizeof(proxy_env)); - prox=curl_getenv(proxy_env); - } - - if(prox && *prox) { /* don't count "" strings */ - proxy = prox; /* use this */ - } - else { - proxy = curl_getenv("all_proxy"); /* default proxy to use */ - if(!proxy) - proxy=curl_getenv("ALL_PROXY"); - } - - if(proxy && *proxy) { - long bits = conn->protocol & (PROT_HTTPS|PROT_SSL|PROT_MISSING); - - if(conn->proxytype == CURLPROXY_HTTP) { - /* force this connection's protocol to become HTTP */ - conn->protocol = PROT_HTTP | bits; - conn->bits.proxy = conn->bits.httpproxy = TRUE; - } - } - } /* if(!nope) - it wasn't specified non-proxy */ - } /* NO_PROXY wasn't specified or '*' */ + if(prox && *prox) { /* don't count "" strings */ + proxy = prox; /* use this */ + } + else { + proxy = curl_getenv("all_proxy"); /* default proxy to use */ + if(!proxy) + proxy=curl_getenv("ALL_PROXY"); + } + } /* if(!check_noproxy(conn->host.name, no_proxy)) - it wasn't specified + non-proxy */ if(no_proxy) free(no_proxy); @@ -3629,7 +3676,8 @@ static CURLcode parse_proxy_auth(struct SessionHandle *data, char proxypasswd[MAX_CURL_PASSWORD_LENGTH]=""; if(data->set.str[STRING_PROXYUSERNAME] != NULL) { - strncpy(proxyuser, data->set.str[STRING_PROXYUSERNAME], MAX_CURL_USER_LENGTH); + strncpy(proxyuser, data->set.str[STRING_PROXYUSERNAME], + MAX_CURL_USER_LENGTH); proxyuser[MAX_CURL_USER_LENGTH-1] = '\0'; /*To be on safe side*/ } if(data->set.str[STRING_PROXYPASSWORD] != NULL) { @@ -4117,15 +4165,26 @@ static CURLcode create_conn(struct SessionHandle *data, and the SessionHandle */ conn->proxytype = data->set.proxytype; /* type */ + +#ifdef CURL_DISABLE_PROXY + + conn->bits.proxy = FALSE; + conn->bits.httpproxy = FALSE; + conn->bits.proxy_user_passwd = FALSE; + conn->bits.tunnel_proxy = FALSE; + +#else /* CURL_DISABLE_PROXY */ + conn->bits.proxy = (bool)(data->set.str[STRING_PROXY] && *data->set.str[STRING_PROXY]); conn->bits.httpproxy = (bool)(conn->bits.proxy && (conn->proxytype == CURLPROXY_HTTP)); - - - conn->bits.user_passwd = (bool)(NULL != data->set.str[STRING_USERNAME]); conn->bits.proxy_user_passwd = (bool)(NULL != data->set.str[STRING_PROXYUSERNAME]); conn->bits.tunnel_proxy = data->set.tunnel_thru_httpproxy; + +#endif /* CURL_DISABLE_PROXY */ + + conn->bits.user_passwd = (bool)(NULL != data->set.str[STRING_USERNAME]); conn->bits.ftp_use_epsv = data->set.ftp_use_epsv; conn->bits.ftp_use_eprt = data->set.ftp_use_eprt; @@ -4205,11 +4264,34 @@ static CURLcode create_conn(struct SessionHandle *data, if(!proxy) proxy = detect_proxy(conn); + else if(data->set.str[STRING_NOPROXY]) { + if(check_noproxy(conn->host.name, data->set.str[STRING_NOPROXY])) { + free(proxy); /* proxy is in exception list */ + proxy = NULL; + } + } if(proxy && !*proxy) { free(proxy); /* Don't bother with an empty proxy string */ proxy = NULL; } /* proxy must be freed later unless NULL */ + if(proxy && *proxy) { + long bits = conn->protocol & (PROT_HTTPS|PROT_SSL|PROT_MISSING); + + if(conn->proxytype == CURLPROXY_HTTP) { + /* force this connection's protocol to become HTTP */ + conn->protocol = PROT_HTTP | bits; + conn->bits.httpproxy = TRUE; + } + conn->bits.proxy = TRUE; + } + else { + /* we aren't using the proxy after all... */ + conn->bits.proxy = FALSE; + conn->bits.httpproxy = FALSE; + conn->bits.proxy_user_passwd = FALSE; + conn->bits.tunnel_proxy = FALSE; + } #endif /* CURL_DISABLE_PROXY */ /************************************************************* diff --git a/lib/urldata.h b/lib/urldata.h index aa3d19feb..39d217c65 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -1363,6 +1363,8 @@ enum dupstring { STRING_PASSWORD, /* , if used */ STRING_PROXYUSERNAME, /* Proxy , if used */ STRING_PROXYPASSWORD, /* Proxy , if used */ + STRING_NOPROXY, /* List of hosts which should not use the proxy, if + used */ /* -- end of strings -- */ STRING_LAST /* not used, just an end-of-list marker */ diff --git a/src/main.c b/src/main.c index f8e53384d..f99550dcf 100644 --- a/src/main.c +++ b/src/main.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2008, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2009, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -448,6 +448,7 @@ struct Configurable { char *userpwd; char *proxyuserpwd; char *proxy; + char *noproxy; bool proxytunnel; bool ftp_append; /* APPE on ftp */ bool mute; /* shutup */ @@ -772,6 +773,7 @@ static void help(void) " -N/--no-buffer Disable buffering of the output stream", " --no-keepalive Disable keepalive use on the connection", " --no-sessionid Disable SSL session-ID reusing (SSL)", + " --noproxy Comma-separated list of hosts which do not use proxy", " --ntlm Use HTTP NTLM authentication (H)", " -o/--output Write output to instead of stdout", " --pass Pass phrase for the private key (SSL/SSH)", @@ -1666,6 +1668,7 @@ static ParameterError getparameter(char *flag, /* f or -long-flag */ {"$2", "socks5-hostname", TRUE}, {"$3", "keepalive-time", TRUE}, {"$4", "post302", FALSE}, + {"$5", "noproxy", TRUE}, {"0", "http1.0", FALSE}, {"1", "tlsv1", FALSE}, @@ -2175,6 +2178,10 @@ static ParameterError getparameter(char *flag, /* f or -long-flag */ case '4': /* --post302 */ config->post302 = toggle; break; + case '5': /* --noproxy */ + /* This specifies the noproxy list */ + GetStr(&config->noproxy, nextarg); + break; } break; case '#': /* --progress-bar */ @@ -3641,6 +3648,8 @@ static void free_config_fields(struct Configurable *config) free(config->proxy); if(config->proxyuserpwd) free(config->proxyuserpwd); + if(config->noproxy) + free(config->noproxy); if(config->cookie) free(config->cookie); if(config->cookiefile) @@ -4559,6 +4568,7 @@ operate(struct Configurable *config, int argc, argv_item_t argv[]) my_setopt(curl, CURLOPT_TRANSFERTEXT, config->use_ascii); my_setopt(curl, CURLOPT_USERPWD, config->userpwd); my_setopt(curl, CURLOPT_PROXYUSERPWD, config->proxyuserpwd); + my_setopt(curl, CURLOPT_NOPROXY, config->noproxy); my_setopt(curl, CURLOPT_RANGE, config->range); my_setopt(curl, CURLOPT_ERRORBUFFER, errorbuffer); my_setopt(curl, CURLOPT_TIMEOUT, config->timeout);