mirror of
https://github.com/moparisthebest/curl
synced 2024-12-21 23:58:49 -05:00
news: CURLOPT_CONNECT_TO and --connect-to
Makes curl connect to the given host+port instead of the host+port found in the URL.
This commit is contained in:
parent
f86f50f05a
commit
cd8d236245
13
docs/curl.1
13
docs/curl.1
@ -1176,6 +1176,19 @@ 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 "--connect-to <host:port:connect-to-host:connect-to-port>"
|
||||
For a request to the given "host:port" pair, connect to
|
||||
"connect-to-host:connect-to-port" instead.
|
||||
This is suitable to direct the request at a specific server, e.g. at a specific
|
||||
cluster node in a cluster of servers.
|
||||
This option is only used to establish the network connection. It does NOT
|
||||
affect the hostname/port that is used for TLS/SSL (e.g. SNI, certificate
|
||||
verification) or for the application protocols.
|
||||
"host" and "port" may be the empty string, meaning "any host/port".
|
||||
"connect-to-host" and "connect-to-port" may also be the empty string,
|
||||
meaning "use the request's original host/port".
|
||||
This option can be used many times to add many connect rules.
|
||||
(Added in 7.49.0).
|
||||
.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
|
||||
|
@ -165,6 +165,8 @@ Proxy type. See \fICURLOPT_PROXYTYPE(3)\fP
|
||||
Filter out hosts from proxy use. \fICURLOPT_NOPROXY(3)\fP
|
||||
.IP CURLOPT_HTTPPROXYTUNNEL
|
||||
Tunnel through the HTTP proxy. \fICURLOPT_HTTPPROXYTUNNEL(3)\fP
|
||||
.IP CURLOPT_CONNECT_TO
|
||||
Connect to a specific host and port. See \fICURLOPT_CONNECT_TO(3)\fP
|
||||
.IP CURLOPT_SOCKS5_GSSAPI_SERVICE
|
||||
Socks5 GSSAPI service name. \fICURLOPT_SOCKS5_GSSAPI_SERVICE(3)\fP
|
||||
.IP CURLOPT_SOCKS5_GSSAPI_NEC
|
||||
|
105
docs/libcurl/opts/CURLOPT_CONNECT_TO.3
Normal file
105
docs/libcurl/opts/CURLOPT_CONNECT_TO.3
Normal file
@ -0,0 +1,105 @@
|
||||
.\" **************************************************************************
|
||||
.\" * _ _ ____ _
|
||||
.\" * Project ___| | | | _ \| |
|
||||
.\" * / __| | | | |_) | |
|
||||
.\" * | (__| |_| | _ <| |___
|
||||
.\" * \___|\___/|_| \_\_____|
|
||||
.\" *
|
||||
.\" * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
.\" *
|
||||
.\" * This software is licensed as described in the file COPYING, which
|
||||
.\" * you should have received as part of this distribution. The terms
|
||||
.\" * are also available at http://curl.haxx.se/docs/copyright.html.
|
||||
.\" *
|
||||
.\" * You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||
.\" * copies of the Software, and permit persons to whom the Software is
|
||||
.\" * furnished to do so, under the terms of the COPYING file.
|
||||
.\" *
|
||||
.\" * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
.\" * KIND, either express or implied.
|
||||
.\" *
|
||||
.\" **************************************************************************
|
||||
.\"
|
||||
.TH CURLOPT_CONNECT_TO 3 "10 April 2016" "libcurl 7.49.0" "curl_easy_setopt options"
|
||||
.SH NAME
|
||||
CURLOPT_CONNECT_TO \- Connect to a specific host and port instead of the URL's host and port
|
||||
.SH SYNOPSIS
|
||||
.nf
|
||||
#include <curl/curl.h>
|
||||
|
||||
CURLcode curl_easy_setopt(CURL *handle, CURLOPT_CONNECT_TO,
|
||||
struct curl_slist *connect_to);
|
||||
.fi
|
||||
.SH DESCRIPTION
|
||||
Pass a pointer to a linked list of strings with "connect to" information to
|
||||
use for establishing network connections with this handle. The linked list
|
||||
should be a fully valid list of \fBstruct curl_slist\fP structs properly
|
||||
filled in. Use \fIcurl_slist_append(3)\fP to create the list and
|
||||
\fIcurl_slist_free_all(3)\fP to clean up an entire list.
|
||||
|
||||
Each single string should be written using the format
|
||||
HOST:PORT:CONNECT-TO-HOST:CONNECT-TO-PORT where HOST is the host of the
|
||||
request, PORT is the port of the request, CONNECT-TO-HOST is the host name to
|
||||
connect to, and CONNECT-TO-PORT is the port to connect to.
|
||||
|
||||
The first string that matches the request's host and port is used.
|
||||
|
||||
Dotted numerical IP addresses are supported for HOST and CONNECT-TO-HOST.
|
||||
A numerical IPv6 address must be written within [brackets].
|
||||
|
||||
Any of the four values may be empty. When the HOST or PORT is empty, the host
|
||||
or port will always match (the request's host or port is ignored).
|
||||
When CONNECT-TO-HOST or CONNECT-TO-PORT is empty, the "connect to" feature
|
||||
will be disabled for the host or port, and the request's host or port will be
|
||||
used to establish the network connection.
|
||||
|
||||
This option is suitable to direct the request at a specific server, e.g. at a
|
||||
specific cluster node in a cluster of servers.
|
||||
|
||||
The "connect to" host and port are only used to establish the network
|
||||
connection. They do NOT affect the host and port that are used for TLS/SSL
|
||||
(e.g. SNI, certificate verification) or for the application protocols.
|
||||
|
||||
In contrast to \fICURLOPT_RESOLVE(3)\fP, the option
|
||||
\fICURLOPT_CONNECT_TO(3)\fP does not pre-populate the DNS cache and therefore
|
||||
it does not affect future transfers of other easy handles that have been added
|
||||
to the same multi handle.
|
||||
|
||||
The "connect to" host and port are ignored if they are equal to the host and
|
||||
the port in the request URL, because connecting to the host and the port in
|
||||
the request URL is the default behavior.
|
||||
|
||||
If an HTTP proxy is used for a request having a special "connect to" host or
|
||||
port, and the "connect to" host or port differs from the requests's host and
|
||||
port, the HTTP proxy is automatically switched to tunnel mode for this
|
||||
specific request. This is necessary because it is not possible to connect to a
|
||||
specific host or port in normal (non-tunnel) mode.
|
||||
|
||||
.SH DEFAULT
|
||||
NULL
|
||||
.SH PROTOCOLS
|
||||
All
|
||||
.SH EXAMPLE
|
||||
.nf
|
||||
CURL *curl;
|
||||
struct curl_slist *connect_to = NULL;
|
||||
host = curl_slist_append(NULL, "example.com::server1.example.com:");
|
||||
|
||||
curl = curl_easy_init();
|
||||
if(curl) {
|
||||
curl_easy_setopt(curl, CURLOPT_CONNECT_TO, connect_to);
|
||||
curl_easy_setopt(curl, CURLOPT_URL, "http://example.com");
|
||||
res = curl_easy_perform(curl);
|
||||
|
||||
/* always cleanup */
|
||||
curl_easy_cleanup(curl);
|
||||
}
|
||||
|
||||
curl_slist_free_all(connect_to);
|
||||
.fi
|
||||
.SH AVAILABILITY
|
||||
Added in 7.49.0
|
||||
.SH RETURN VALUE
|
||||
Returns CURLE_OK if the option is supported, and CURLE_UNKNOWN_OPTION if not.
|
||||
.SH "SEE ALSO"
|
||||
.BR CURLOPT_URL "(3), " CURLOPT_RESOLVE "(3), " CURLOPT_FOLLOWLOCATION "(3), " CURLOPT_HTTPPROXYTUNNEL "(3), "
|
@ -45,7 +45,7 @@ ADDRESS can of course be either IPv4 or IPv6 style addressing.
|
||||
This option effectively pre-populates the DNS cache with entries for the
|
||||
host+port pair so redirects and everything that operations against the
|
||||
HOST+PORT will instead use your provided ADDRESS. Addresses set with
|
||||
\fICURL_RESOLVE\fP will not time-out from the DNS cache like ordinary entries.
|
||||
\fICURLOPT_RESOLVE\fP will not time-out from the DNS cache like ordinary entries.
|
||||
|
||||
Remove names from the DNS cache again, to stop providing these fake resolves,
|
||||
by including a string in the linked list that uses the format
|
||||
@ -79,4 +79,4 @@ Added in 7.21.3. Removal support added in 7.42.0.
|
||||
.SH RETURN VALUE
|
||||
Returns CURLE_OK if the option is supported, and CURLE_UNKNOWN_OPTION if not.
|
||||
.SH "SEE ALSO"
|
||||
.BR CURLOPT_IPRESOLVE "(3), " CURLOPT_DNS_CACHE_TIMEOUT "(3), "
|
||||
.BR CURLOPT_IPRESOLVE "(3), " CURLOPT_DNS_CACHE_TIMEOUT "(3), " CURLOPT_CONNECT_TO "(3), "
|
||||
|
@ -28,7 +28,7 @@ man_MANS = CURLOPT_ACCEPT_ENCODING.3 CURLOPT_ACCEPTTIMEOUT_MS.3 \
|
||||
CURLOPT_CERTINFO.3 CURLOPT_CHUNK_BGN_FUNCTION.3 CURLOPT_CHUNK_DATA.3 \
|
||||
CURLOPT_CHUNK_END_FUNCTION.3 CURLOPT_CLOSESOCKETDATA.3 \
|
||||
CURLOPT_CLOSESOCKETFUNCTION.3 CURLOPT_CONNECT_ONLY.3 \
|
||||
CURLOPT_CONNECTTIMEOUT.3 CURLOPT_CONNECTTIMEOUT_MS.3 \
|
||||
CURLOPT_CONNECTTIMEOUT.3 CURLOPT_CONNECTTIMEOUT_MS.3 CURLOPT_CONNECT_TO.3 \
|
||||
CURLOPT_CONV_FROM_NETWORK_FUNCTION.3 CURLOPT_CONV_FROM_UTF8_FUNCTION.3 \
|
||||
CURLOPT_CONV_TO_NETWORK_FUNCTION.3 CURLOPT_COOKIE.3 \
|
||||
CURLOPT_COOKIEFILE.3 CURLOPT_COOKIEJAR.3 CURLOPT_COOKIELIST.3 \
|
||||
@ -147,7 +147,7 @@ HTMLPAGES = CURLOPT_ACCEPT_ENCODING.html CURLOPT_ACCEPTTIMEOUT_MS.html \
|
||||
CURLOPT_CHUNK_END_FUNCTION.html CURLOPT_CLOSESOCKETDATA.html \
|
||||
CURLOPT_CLOSESOCKETFUNCTION.html CURLOPT_CONNECT_ONLY.html \
|
||||
CURLOPT_CONNECTTIMEOUT.html CURLOPT_CONNECTTIMEOUT_MS.html \
|
||||
CURLOPT_CONV_FROM_NETWORK_FUNCTION.html \
|
||||
CURLOPT_CONNECT_TO.html CURLOPT_CONV_FROM_NETWORK_FUNCTION.html \
|
||||
CURLOPT_CONV_FROM_UTF8_FUNCTION.html \
|
||||
CURLOPT_CONV_TO_NETWORK_FUNCTION.html CURLOPT_COOKIE.html \
|
||||
CURLOPT_COOKIEFILE.html CURLOPT_COOKIEJAR.html CURLOPT_COOKIELIST.html \
|
||||
@ -281,7 +281,7 @@ PDFPAGES = CURLOPT_ACCEPT_ENCODING.pdf CURLOPT_ACCEPTTIMEOUT_MS.pdf \
|
||||
CURLOPT_CLOSESOCKETDATA.pdf CURLOPT_CLOSESOCKETFUNCTION.pdf \
|
||||
CURLOPT_CONNECT_ONLY.pdf CURLOPT_CONNECTTIMEOUT.pdf \
|
||||
CURLOPT_CONNECTTIMEOUT_MS.pdf CURLOPT_CONV_FROM_NETWORK_FUNCTION.pdf \
|
||||
CURLOPT_CONV_FROM_UTF8_FUNCTION.pdf \
|
||||
CURLOPT_CONNECT_TO.pdf CURLOPT_CONV_FROM_UTF8_FUNCTION.pdf \ \
|
||||
CURLOPT_CONV_TO_NETWORK_FUNCTION.pdf CURLOPT_COOKIE.pdf \
|
||||
CURLOPT_COOKIEFILE.pdf CURLOPT_COOKIEJAR.pdf CURLOPT_COOKIELIST.pdf \
|
||||
CURLOPT_COOKIESESSION.pdf CURLOPT_COPYPOSTFIELDS.pdf CURLOPT_CRLF.pdf \
|
||||
|
@ -331,6 +331,7 @@ CURLOPT_CLOSESOCKETFUNCTION 7.21.7
|
||||
CURLOPT_CONNECTTIMEOUT 7.7
|
||||
CURLOPT_CONNECTTIMEOUT_MS 7.16.2
|
||||
CURLOPT_CONNECT_ONLY 7.15.2
|
||||
CURLOPT_CONNECT_TO 7.49.0
|
||||
CURLOPT_CONV_FROM_NETWORK_FUNCTION 7.15.4
|
||||
CURLOPT_CONV_FROM_UTF8_FUNCTION 7.15.4
|
||||
CURLOPT_CONV_TO_NETWORK_FUNCTION 7.15.4
|
||||
|
@ -1680,6 +1680,10 @@ typedef enum {
|
||||
/* Do not send any tftp option requests to the server */
|
||||
CINIT(TFTP_NO_OPTIONS, LONG, 242),
|
||||
|
||||
/* Linked-list of host:port:connect-to-host:connect-to-port,
|
||||
overrides the URL's host:port (only for the network layer) */
|
||||
CINIT(CONNECT_TO, STRINGPOINT, 243),
|
||||
|
||||
CURLOPT_LASTENTRY /* the last unused */
|
||||
} CURLoption;
|
||||
|
||||
|
@ -132,9 +132,16 @@ void Curl_conncache_destroy(struct conncache *connc)
|
||||
/* returns an allocated key to find a bundle for this connection */
|
||||
static char *hashkey(struct connectdata *conn)
|
||||
{
|
||||
return aprintf("%s:%d",
|
||||
conn->bits.proxy?conn->proxy.name:conn->host.name,
|
||||
conn->localport);
|
||||
const char *hostname;
|
||||
|
||||
if(conn->bits.proxy)
|
||||
hostname = conn->proxy.name;
|
||||
else if(conn->bits.conn_to_host)
|
||||
hostname = conn->conn_to_host.name;
|
||||
else
|
||||
hostname = conn->host.name;
|
||||
|
||||
return aprintf("%s:%d", hostname, conn->localport);
|
||||
}
|
||||
|
||||
/* Look up the bundle with all the connections to the same host this
|
||||
|
@ -841,6 +841,8 @@ CURLcode Curl_is_connected(struct connectdata *conn,
|
||||
if(result) {
|
||||
/* no more addresses to try */
|
||||
|
||||
const char* hostname;
|
||||
|
||||
/* if the first address family runs out of addresses to try before
|
||||
the happy eyeball timeout, go ahead and try the next family now */
|
||||
if(conn->tempaddr[1] == NULL) {
|
||||
@ -849,9 +851,15 @@ CURLcode Curl_is_connected(struct connectdata *conn,
|
||||
return result;
|
||||
}
|
||||
|
||||
if(conn->bits.proxy)
|
||||
hostname = conn->proxy.name;
|
||||
else if(conn->bits.conn_to_host)
|
||||
hostname = conn->conn_to_host.name;
|
||||
else
|
||||
hostname = conn->host.name;
|
||||
|
||||
failf(data, "Failed to connect to %s port %ld: %s",
|
||||
conn->bits.proxy?conn->proxy.name:conn->host.name,
|
||||
conn->port, Curl_strerror(conn, error));
|
||||
hostname, conn->port, Curl_strerror(conn, error));
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -1832,6 +1832,8 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
|
||||
data->state.first_host = strdup(conn->host.name);
|
||||
if(!data->state.first_host)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
|
||||
data->state.first_remote_port = conn->remote_port;
|
||||
}
|
||||
http->writebytecount = http->readbytecount = 0;
|
||||
|
||||
|
@ -49,6 +49,8 @@ CURLcode Curl_proxy_connect(struct connectdata *conn)
|
||||
/* for [protocol] tunneled through HTTP proxy */
|
||||
struct HTTP http_proxy;
|
||||
void *prot_save;
|
||||
const char *hostname;
|
||||
int remote_port;
|
||||
CURLcode result;
|
||||
|
||||
/* BLOCKING */
|
||||
@ -67,8 +69,16 @@ CURLcode Curl_proxy_connect(struct connectdata *conn)
|
||||
memset(&http_proxy, 0, sizeof(http_proxy));
|
||||
conn->data->req.protop = &http_proxy;
|
||||
connkeep(conn, "HTTP proxy CONNECT");
|
||||
result = Curl_proxyCONNECT(conn, FIRSTSOCKET,
|
||||
conn->host.name, conn->remote_port, FALSE);
|
||||
if(conn->bits.conn_to_host)
|
||||
hostname = conn->conn_to_host.name;
|
||||
else
|
||||
hostname = conn->host.name;
|
||||
if(conn->bits.conn_to_port)
|
||||
remote_port = conn->conn_to_port;
|
||||
else
|
||||
remote_port = conn->remote_port;
|
||||
result = Curl_proxyCONNECT(conn, FIRSTSOCKET, hostname,
|
||||
remote_port, FALSE);
|
||||
conn->data->req.protop = prot_save;
|
||||
if(CURLE_OK != result)
|
||||
return result;
|
||||
@ -153,9 +163,14 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn,
|
||||
const char *useragent="";
|
||||
const char *http = (conn->proxytype == CURLPROXY_HTTP_1_0) ?
|
||||
"1.0" : "1.1";
|
||||
char *hostheader= /* host:port with IPv6 support */
|
||||
aprintf("%s%s%s:%hu", conn->bits.ipv6_ip?"[":"",
|
||||
hostname, conn->bits.ipv6_ip?"]":"",
|
||||
bool ipv6_ip = conn->bits.ipv6_ip;
|
||||
char *hostheader;
|
||||
|
||||
/* the hostname may be different */
|
||||
if(hostname != conn->host.name)
|
||||
ipv6_ip = (strchr(hostname, ':') != NULL);
|
||||
hostheader= /* host:port with IPv6 support */
|
||||
aprintf("%s%s%s:%hu", ipv6_ip?"[":"", hostname, ipv6_ip?"]":"",
|
||||
remote_port);
|
||||
if(!hostheader) {
|
||||
Curl_add_buffer_free(req_buffer);
|
||||
|
@ -1464,9 +1464,15 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
|
||||
{
|
||||
struct Curl_dns_entry *dns = NULL;
|
||||
struct connectdata *conn = data->easy_conn;
|
||||
const char *hostname;
|
||||
|
||||
if(conn->bits.conn_to_host)
|
||||
hostname = conn->conn_to_host.name;
|
||||
else
|
||||
hostname = conn->host.name;
|
||||
|
||||
/* check if we have the name resolved by now */
|
||||
dns = Curl_fetch_addr(conn, conn->host.name, (int)conn->port);
|
||||
dns = Curl_fetch_addr(conn, hostname, (int)conn->port);
|
||||
|
||||
if(dns) {
|
||||
#ifdef CURLRES_ASYNCH
|
||||
|
329
lib/url.c
329
lib/url.c
@ -2674,6 +2674,9 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option,
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
case CURLOPT_CONNECT_TO:
|
||||
data->set.connect_to = va_arg(param, struct curl_slist *);
|
||||
break;
|
||||
default:
|
||||
/* unknown tag and its companion, just ignore: */
|
||||
result = CURLE_UNKNOWN_OPTION;
|
||||
@ -2729,6 +2732,7 @@ static void conn_free(struct connectdata *conn)
|
||||
Curl_safefree(conn->allocptr.rtsp_transport);
|
||||
Curl_safefree(conn->trailer);
|
||||
Curl_safefree(conn->host.rawalloc); /* host name buffer */
|
||||
Curl_safefree(conn->conn_to_host.rawalloc); /* host name buffer */
|
||||
Curl_safefree(conn->proxy.rawalloc); /* proxy name buffer */
|
||||
Curl_safefree(conn->master_buffer);
|
||||
|
||||
@ -2787,6 +2791,7 @@ CURLcode Curl_disconnect(struct connectdata *conn, bool dead_connection)
|
||||
Curl_conncache_remove_conn(data->state.conn_cache, conn);
|
||||
|
||||
free_fixed_hostname(&conn->host);
|
||||
free_fixed_hostname(&conn->conn_to_host);
|
||||
free_fixed_hostname(&conn->proxy);
|
||||
|
||||
Curl_ssl_close(conn, FIRSTSOCKET);
|
||||
@ -3146,9 +3151,15 @@ ConnectionExists(struct SessionHandle *data,
|
||||
max_pipeline_length(data->multi):0;
|
||||
size_t best_pipe_len = max_pipe_len;
|
||||
struct curl_llist_element *curr;
|
||||
const char *hostname;
|
||||
|
||||
if(needle->bits.conn_to_host)
|
||||
hostname = needle->conn_to_host.name;
|
||||
else
|
||||
hostname = needle->host.name;
|
||||
|
||||
infof(data, "Found bundle for host %s: %p [%s]\n",
|
||||
needle->host.name, (void *)bundle,
|
||||
hostname, (void *)bundle,
|
||||
(bundle->multiuse== BUNDLE_PIPELINING?
|
||||
"can pipeline":
|
||||
(bundle->multiuse== BUNDLE_MULTIPLEX?
|
||||
@ -3267,6 +3278,16 @@ ConnectionExists(struct SessionHandle *data,
|
||||
/* don't do mixed proxy and non-proxy connections */
|
||||
continue;
|
||||
|
||||
if(needle->bits.conn_to_host != check->bits.conn_to_host)
|
||||
/* don't mix connections that use the "connect to host" feature and
|
||||
* connections that don't use this feature */
|
||||
continue;
|
||||
|
||||
if(needle->bits.conn_to_port != check->bits.conn_to_port)
|
||||
/* don't mix connections that use the "connect to port" feature and
|
||||
* connections that don't use this feature */
|
||||
continue;
|
||||
|
||||
if(!canPipeline && check->inuse)
|
||||
/* this request can't be pipelined but the checked connection is
|
||||
already in use so we skip it */
|
||||
@ -3302,7 +3323,7 @@ ConnectionExists(struct SessionHandle *data,
|
||||
}
|
||||
}
|
||||
|
||||
if(!needle->bits.httpproxy || needle->handler->flags&PROTOPT_SSL ||
|
||||
if(!needle->bits.httpproxy || (needle->handler->flags&PROTOPT_SSL) ||
|
||||
(needle->bits.httpproxy && check->bits.httpproxy &&
|
||||
needle->bits.tunnel_proxy && check->bits.tunnel_proxy &&
|
||||
Curl_raw_equal(needle->proxy.name, check->proxy.name) &&
|
||||
@ -3313,6 +3334,10 @@ ConnectionExists(struct SessionHandle *data,
|
||||
if((Curl_raw_equal(needle->handler->scheme, check->handler->scheme) ||
|
||||
(get_protocol_family(check->handler->protocol) ==
|
||||
needle->handler->protocol && check->tls_upgraded)) &&
|
||||
(!needle->bits.conn_to_host || Curl_raw_equal(
|
||||
needle->conn_to_host.name, check->conn_to_host.name)) &&
|
||||
(!needle->bits.conn_to_port ||
|
||||
needle->conn_to_port == check->conn_to_port) &&
|
||||
Curl_raw_equal(needle->host.name, check->host.name) &&
|
||||
needle->remote_port == check->remote_port) {
|
||||
/* The schemes match or the the protocol family is the same and the
|
||||
@ -3490,16 +3515,27 @@ CURLcode Curl_connected_proxy(struct connectdata *conn,
|
||||
case CURLPROXY_SOCKS5:
|
||||
case CURLPROXY_SOCKS5_HOSTNAME:
|
||||
return Curl_SOCKS5(conn->proxyuser, conn->proxypasswd,
|
||||
conn->host.name, conn->remote_port,
|
||||
conn->bits.conn_to_host ? conn->conn_to_host.name :
|
||||
conn->host.name,
|
||||
conn->bits.conn_to_port ? conn->conn_to_port :
|
||||
conn->remote_port,
|
||||
FIRSTSOCKET, conn);
|
||||
|
||||
case CURLPROXY_SOCKS4:
|
||||
return Curl_SOCKS4(conn->proxyuser, conn->host.name,
|
||||
conn->remote_port, FIRSTSOCKET, conn, FALSE);
|
||||
return Curl_SOCKS4(conn->proxyuser,
|
||||
conn->bits.conn_to_host ? conn->conn_to_host.name :
|
||||
conn->host.name,
|
||||
conn->bits.conn_to_port ? conn->conn_to_port :
|
||||
conn->remote_port,
|
||||
FIRSTSOCKET, conn, FALSE);
|
||||
|
||||
case CURLPROXY_SOCKS4A:
|
||||
return Curl_SOCKS4(conn->proxyuser, conn->host.name,
|
||||
conn->remote_port, FIRSTSOCKET, conn, TRUE);
|
||||
return Curl_SOCKS4(conn->proxyuser,
|
||||
conn->bits.conn_to_host ? conn->conn_to_host.name :
|
||||
conn->host.name,
|
||||
conn->bits.conn_to_port ? conn->conn_to_port :
|
||||
conn->remote_port,
|
||||
FIRSTSOCKET, conn, TRUE);
|
||||
|
||||
#endif /* CURL_DISABLE_PROXY */
|
||||
case CURLPROXY_HTTP:
|
||||
@ -4373,11 +4409,6 @@ static CURLcode setup_connection_internals(struct connectdata *conn)
|
||||
was very likely already set to the proxy port */
|
||||
conn->port = p->defport;
|
||||
|
||||
/* only if remote_port was not already parsed off the URL we use the
|
||||
default port number */
|
||||
if(conn->remote_port < 0)
|
||||
conn->remote_port = (unsigned short)conn->given->defport;
|
||||
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
@ -4652,7 +4683,7 @@ static CURLcode parse_proxy(struct SessionHandle *data,
|
||||
if(strncmp("%25", ptr, 3))
|
||||
infof(data, "Please URL encode %% as %%25, see RFC 6874.\n");
|
||||
ptr++;
|
||||
/* Allow unresered characters as defined in RFC 3986 */
|
||||
/* Allow unreserved characters as defined in RFC 3986 */
|
||||
while(*ptr && (ISALPHA(*ptr) || ISXDIGIT(*ptr) || (*ptr == '-') ||
|
||||
(*ptr == '.') || (*ptr == '_') || (*ptr == '~')))
|
||||
ptr++;
|
||||
@ -4679,7 +4710,7 @@ static CURLcode parse_proxy(struct SessionHandle *data,
|
||||
/* now set the local port number */
|
||||
port = strtol(prox_portno, &endp, 10);
|
||||
if((endp && *endp && (*endp != '/') && (*endp != ' ')) ||
|
||||
(port >= 65536) ) {
|
||||
(port < 0) || (port > 65535)) {
|
||||
/* meant to detect for example invalid IPv6 numerical addresses without
|
||||
brackets: "2a00:fac0:a000::7:13". Accept a trailing slash only
|
||||
because we then allow "URL style" with the number followed by a
|
||||
@ -4702,7 +4733,7 @@ static CURLcode parse_proxy(struct SessionHandle *data,
|
||||
a slash so we strip everything from the first slash */
|
||||
atsign = strchr(proxyptr, '/');
|
||||
if(atsign)
|
||||
*atsign = 0x0; /* cut off path part from host name */
|
||||
*atsign = '\0'; /* cut off path part from host name */
|
||||
|
||||
if(data->set.proxyport)
|
||||
/* None given in the proxy string, then get the default one if it is
|
||||
@ -5106,6 +5137,12 @@ static CURLcode parse_remote_port(struct SessionHandle *data,
|
||||
use the default port. Firefox and Chrome both do that. */
|
||||
*portptr = '\0';
|
||||
}
|
||||
|
||||
/* only if remote_port was not already parsed off the URL we use the
|
||||
default port number */
|
||||
if(conn->remote_port < 0)
|
||||
conn->remote_port = (unsigned short)conn->given->defport;
|
||||
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
@ -5211,6 +5248,214 @@ static CURLcode set_login(struct connectdata *conn,
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parses a "host:port" string to connect to.
|
||||
* The hostname and the port may be empty; in this case, NULL is returned for
|
||||
* the hostname and -1 for the port.
|
||||
*/
|
||||
static CURLcode parse_connect_to_host_port(struct SessionHandle *data,
|
||||
const char *host,
|
||||
char **hostname_result,
|
||||
int *port_result)
|
||||
{
|
||||
char *host_dup;
|
||||
char *hostptr;
|
||||
char *host_portno;
|
||||
char *portptr;
|
||||
int port = -1;
|
||||
|
||||
*hostname_result = NULL;
|
||||
*port_result = -1;
|
||||
|
||||
if(!host || !*host)
|
||||
return CURLE_OK;
|
||||
|
||||
host_dup = strdup(host);
|
||||
if(!host_dup)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
|
||||
hostptr = host_dup;
|
||||
|
||||
/* start scanning for port number at this point */
|
||||
portptr = hostptr;
|
||||
|
||||
/* detect and extract RFC6874-style IPv6-addresses */
|
||||
if(*hostptr == '[') {
|
||||
char *ptr = ++hostptr; /* advance beyond the initial bracket */
|
||||
while(*ptr && (ISXDIGIT(*ptr) || (*ptr == ':') || (*ptr == '.')))
|
||||
ptr++;
|
||||
if(*ptr == '%') {
|
||||
/* There might be a zone identifier */
|
||||
if(strncmp("%25", ptr, 3))
|
||||
infof(data, "Please URL encode %% as %%25, see RFC 6874.\n");
|
||||
ptr++;
|
||||
/* Allow unreserved characters as defined in RFC 3986 */
|
||||
while(*ptr && (ISALPHA(*ptr) || ISXDIGIT(*ptr) || (*ptr == '-') ||
|
||||
(*ptr == '.') || (*ptr == '_') || (*ptr == '~')))
|
||||
ptr++;
|
||||
}
|
||||
if(*ptr == ']')
|
||||
/* yeps, it ended nicely with a bracket as well */
|
||||
*ptr++ = '\0';
|
||||
else
|
||||
infof(data, "Invalid IPv6 address format\n");
|
||||
portptr = ptr;
|
||||
/* Note that if this didn't end with a bracket, we still advanced the
|
||||
* hostptr first, but I can't see anything wrong with that as no host
|
||||
* name nor a numeric can legally start with a bracket.
|
||||
*/
|
||||
}
|
||||
|
||||
/* Get port number off server.com:1080 */
|
||||
host_portno = strchr(portptr, ':');
|
||||
if(host_portno) {
|
||||
char *endp = NULL;
|
||||
*host_portno = '\0'; /* cut off number from host name */
|
||||
host_portno++;
|
||||
if(*host_portno) {
|
||||
long portparse = strtol(host_portno, &endp, 10);
|
||||
if((endp && *endp) || (portparse < 0) || (portparse > 65535)) {
|
||||
infof(data, "No valid port number in connect to host string (%s)\n",
|
||||
host_portno);
|
||||
hostptr = NULL;
|
||||
port = -1;
|
||||
}
|
||||
else
|
||||
port = (int)portparse; /* we know it will fit */
|
||||
}
|
||||
}
|
||||
|
||||
/* now, clone the cleaned host name */
|
||||
if(hostptr) {
|
||||
*hostname_result = strdup(hostptr);
|
||||
if(!*hostname_result) {
|
||||
free(host_dup);
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
*port_result = port;
|
||||
|
||||
free(host_dup);
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parses one "connect to" string in the form:
|
||||
* "HOST:PORT:CONNECT-TO-HOST:CONNECT-TO-PORT".
|
||||
*/
|
||||
static CURLcode parse_connect_to_string(struct SessionHandle *data,
|
||||
struct connectdata *conn,
|
||||
const char *conn_to_host,
|
||||
char **host_result,
|
||||
int *port_result)
|
||||
{
|
||||
CURLcode result = CURLE_OK;
|
||||
const char *ptr = conn_to_host;
|
||||
int host_match = FALSE;
|
||||
int port_match = FALSE;
|
||||
|
||||
if(*ptr == ':') {
|
||||
/* an empty hostname always matches */
|
||||
host_match = TRUE;
|
||||
ptr++;
|
||||
}
|
||||
else {
|
||||
/* check whether the URL's hostname matches */
|
||||
size_t hostname_to_match_len;
|
||||
char *hostname_to_match = aprintf("%s%s%s",
|
||||
conn->bits.ipv6_ip ? "[" : "",
|
||||
conn->host.name,
|
||||
conn->bits.ipv6_ip ? "]" : "");
|
||||
if(!hostname_to_match)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
hostname_to_match_len = strlen(hostname_to_match);
|
||||
host_match = curl_strnequal(ptr, hostname_to_match, hostname_to_match_len);
|
||||
free(hostname_to_match);
|
||||
ptr += hostname_to_match_len;
|
||||
|
||||
host_match = host_match && *ptr == ':';
|
||||
ptr++;
|
||||
}
|
||||
|
||||
if(host_match) {
|
||||
if(*ptr == ':') {
|
||||
/* an empty port always matches */
|
||||
port_match = TRUE;
|
||||
ptr++;
|
||||
}
|
||||
else {
|
||||
/* check whether the URL's port matches */
|
||||
char *ptr_next = strchr(ptr, ':');
|
||||
if(ptr_next) {
|
||||
char *endp = NULL;
|
||||
long port_to_match = strtol(ptr, &endp, 10);
|
||||
if((endp == ptr_next) && (port_to_match == conn->remote_port)) {
|
||||
port_match = TRUE;
|
||||
ptr = ptr_next + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(host_match && port_match) {
|
||||
/* parse the hostname and port to connect to */
|
||||
result = parse_connect_to_host_port(data, ptr, host_result, port_result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Processes all strings in the "connect to" slist, and uses the "connect
|
||||
* to host" and "connect to port" of the first string that matches.
|
||||
*/
|
||||
static CURLcode parse_connect_to_slist(struct SessionHandle *data,
|
||||
struct connectdata *conn,
|
||||
struct curl_slist *conn_to_host)
|
||||
{
|
||||
CURLcode result = CURLE_OK;
|
||||
char *host = NULL;
|
||||
int port = 0;
|
||||
|
||||
while(conn_to_host && !host) {
|
||||
result = parse_connect_to_string(data, conn, conn_to_host->data,
|
||||
&host, &port);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
if(host && *host) {
|
||||
bool ipv6host;
|
||||
conn->conn_to_host.rawalloc = host;
|
||||
conn->conn_to_host.name = host;
|
||||
conn->bits.conn_to_host = TRUE;
|
||||
|
||||
ipv6host = strchr(host, ':') != NULL;
|
||||
infof(data, "Connecting to hostname: %s%s%s\n",
|
||||
ipv6host ? "[" : "", host, ipv6host ? "]" : "");
|
||||
}
|
||||
else {
|
||||
/* no "connect to host" */
|
||||
conn->bits.conn_to_host = FALSE;
|
||||
free(host);
|
||||
}
|
||||
|
||||
if(port >= 0) {
|
||||
conn->conn_to_port = port;
|
||||
conn->bits.conn_to_port = TRUE;
|
||||
infof(data, "Connecting to port: %d\n", port);
|
||||
}
|
||||
else {
|
||||
/* no "connect to port" */
|
||||
conn->bits.conn_to_port = FALSE;
|
||||
}
|
||||
|
||||
conn_to_host = conn_to_host->next;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*************************************************************
|
||||
* Resolve the address of the server or proxy
|
||||
*************************************************************/
|
||||
@ -5262,12 +5507,21 @@ static CURLcode resolve_server(struct SessionHandle *data,
|
||||
else
|
||||
#endif
|
||||
if(!conn->proxy.name || !*conn->proxy.name) {
|
||||
struct hostname *connhost;
|
||||
if(conn->bits.conn_to_host)
|
||||
connhost = &conn->conn_to_host;
|
||||
else
|
||||
connhost = &conn->host;
|
||||
|
||||
/* If not connecting via a proxy, extract the port from the URL, if it is
|
||||
* there, thus overriding any defaults that might have been set above. */
|
||||
if(conn->bits.conn_to_port)
|
||||
conn->port = conn->conn_to_port;
|
||||
else
|
||||
conn->port = conn->remote_port; /* it is the same port */
|
||||
|
||||
/* Resolve target host right on */
|
||||
rc = Curl_resolv_timeout(conn, conn->host.name, (int)conn->port,
|
||||
rc = Curl_resolv_timeout(conn, connhost->name, (int)conn->port,
|
||||
&hostaddr, timeout_ms);
|
||||
if(rc == CURLRESOLV_PENDING)
|
||||
*async = TRUE;
|
||||
@ -5276,7 +5530,7 @@ static CURLcode resolve_server(struct SessionHandle *data,
|
||||
result = CURLE_OPERATION_TIMEDOUT;
|
||||
|
||||
else if(!hostaddr) {
|
||||
failf(data, "Couldn't resolve host '%s'", conn->host.dispname);
|
||||
failf(data, "Couldn't resolve host '%s'", connhost->dispname);
|
||||
result = CURLE_COULDNT_RESOLVE_HOST;
|
||||
/* don't return yet, we need to clean up the timeout first */
|
||||
}
|
||||
@ -5351,8 +5605,14 @@ static void reuse_conn(struct connectdata *old_conn,
|
||||
/* host can change, when doing keepalive with a proxy or if the case is
|
||||
different this time etc */
|
||||
free_fixed_hostname(&conn->host);
|
||||
free_fixed_hostname(&conn->conn_to_host);
|
||||
Curl_safefree(conn->host.rawalloc);
|
||||
Curl_safefree(conn->conn_to_host.rawalloc);
|
||||
conn->host=old_conn->host;
|
||||
conn->bits.conn_to_host = old_conn->bits.conn_to_host;
|
||||
conn->conn_to_host = old_conn->conn_to_host;
|
||||
conn->bits.conn_to_port = old_conn->bits.conn_to_port;
|
||||
conn->conn_to_port = old_conn->conn_to_port;
|
||||
|
||||
/* persist connection info in session handle */
|
||||
Curl_persistconninfo(conn);
|
||||
@ -5660,13 +5920,48 @@ static CURLcode create_conn(struct SessionHandle *data,
|
||||
if(result)
|
||||
goto out;
|
||||
|
||||
/*************************************************************
|
||||
* Process the "connect to" linked list of hostname/port mappings.
|
||||
* Do this after the remote port number has been fixed in the URL.
|
||||
*************************************************************/
|
||||
result = parse_connect_to_slist(data, conn, data->set.connect_to);
|
||||
if(result)
|
||||
goto out;
|
||||
|
||||
/*************************************************************
|
||||
* IDN-fix the hostnames
|
||||
*************************************************************/
|
||||
fix_hostname(data, conn, &conn->host);
|
||||
if(conn->bits.conn_to_host)
|
||||
fix_hostname(data, conn, &conn->conn_to_host);
|
||||
if(conn->proxy.name && *conn->proxy.name)
|
||||
fix_hostname(data, conn, &conn->proxy);
|
||||
|
||||
/*************************************************************
|
||||
* Check whether the host and the "connect to host" are equal.
|
||||
* Do this after the hostnames have been IDN-fixed .
|
||||
*************************************************************/
|
||||
if(conn->bits.conn_to_host &&
|
||||
Curl_raw_equal(conn->conn_to_host.name, conn->host.name)) {
|
||||
conn->bits.conn_to_host = FALSE;
|
||||
}
|
||||
|
||||
/*************************************************************
|
||||
* Check whether the port and the "connect to port" are equal.
|
||||
* Do this after the remote port number has been fixed in the URL.
|
||||
*************************************************************/
|
||||
if(conn->bits.conn_to_port && conn->conn_to_port == conn->remote_port) {
|
||||
conn->bits.conn_to_port = FALSE;
|
||||
}
|
||||
|
||||
/*************************************************************
|
||||
* If the "connect to" feature is used with an HTTP proxy,
|
||||
* we set the tunnel_proxy bit.
|
||||
*************************************************************/
|
||||
if((conn->bits.conn_to_host || conn->bits.conn_to_port) &&
|
||||
conn->bits.httpproxy)
|
||||
conn->bits.tunnel_proxy = TRUE;
|
||||
|
||||
/*************************************************************
|
||||
* Setup internals depending on protocol. Needs to be done after
|
||||
* we figured out what/if proxy to use.
|
||||
|
@ -375,10 +375,12 @@ struct ssl_config_data {
|
||||
/* information stored about one single SSL session */
|
||||
struct curl_ssl_session {
|
||||
char *name; /* host name for which this ID was used */
|
||||
char *conn_to_host; /* host name for the connection (may be NULL) */
|
||||
void *sessionid; /* as returned from the SSL layer */
|
||||
size_t idsize; /* if known, otherwise 0 */
|
||||
long age; /* just a number, the higher the more recent */
|
||||
int remote_port; /* remote port to connect to */
|
||||
int remote_port; /* remote port */
|
||||
int conn_to_port; /* remote port for the connection (may be -1) */
|
||||
struct ssl_config_data ssl_config; /* setup for this session */
|
||||
};
|
||||
|
||||
@ -490,6 +492,10 @@ struct ConnectBits {
|
||||
/* always modify bits.close with the connclose() and connkeep() macros! */
|
||||
bool close; /* if set, we close the connection after this request */
|
||||
bool reuse; /* if set, this is a re-used connection */
|
||||
bool conn_to_host; /* if set, this connection has a "connect to host"
|
||||
that overrides the host in the URL */
|
||||
bool conn_to_port; /* if set, this connection has a "connect to port"
|
||||
that overrides the port in the URL (remote port) */
|
||||
bool proxy; /* if set, this transfer is done through a proxy - any type */
|
||||
bool httpproxy; /* if set, this transfer is done through a http proxy */
|
||||
bool user_passwd; /* do we use user+password for this connection? */
|
||||
@ -874,10 +880,14 @@ struct connectdata {
|
||||
int socktype; /* SOCK_STREAM or SOCK_DGRAM */
|
||||
|
||||
struct hostname host;
|
||||
struct hostname conn_to_host; /* the host to connect to. valid only if
|
||||
bits.conn_to_host is set */
|
||||
struct hostname proxy;
|
||||
|
||||
long port; /* which port to use locally */
|
||||
int remote_port; /* what remote port to connect to, not the proxy port! */
|
||||
int remote_port; /* the remote port, not the proxy port! */
|
||||
int conn_to_port; /* the remote port to connect to. valid only if
|
||||
bits.conn_to_port is set */
|
||||
|
||||
/* 'primary_ip' and 'primary_port' get filled with peer's numerical
|
||||
ip address and port number whenever an outgoing connection is
|
||||
@ -1226,11 +1236,13 @@ struct UrlState {
|
||||
bytes / second */
|
||||
bool this_is_a_follow; /* this is a followed Location: request */
|
||||
|
||||
char *first_host; /* if set, this should be the host name that we will
|
||||
char *first_host; /* host name of the first (not followed) request.
|
||||
if set, this should be the host name that we will
|
||||
sent authorization to, no else. Used to make Location:
|
||||
following not keep sending user+password... This is
|
||||
strdup() data.
|
||||
*/
|
||||
int first_remote_port; /* remote port of the first (not followed) request */
|
||||
struct curl_ssl_session *session; /* array of 'max_ssl_sessions' size */
|
||||
long sessionage; /* number of the most recent session */
|
||||
char *tempwrite; /* allocated buffer to keep data in when a write
|
||||
@ -1528,6 +1540,8 @@ struct UserDefined {
|
||||
struct curl_slist *telnet_options; /* linked list of telnet options */
|
||||
struct curl_slist *resolve; /* list of names to add/remove from
|
||||
DNS cache */
|
||||
struct curl_slist *connect_to; /* list of host:port mappings to override
|
||||
the hostname and port to connect to */
|
||||
curl_TimeCond timecondition; /* kind of time/date comparison */
|
||||
time_t timevalue; /* what time to compare with */
|
||||
Curl_HttpReq httpreq; /* what kind of HTTP request (if any) is this */
|
||||
@ -1575,7 +1589,6 @@ struct UserDefined {
|
||||
bool http_set_referer; /* is a custom referer used */
|
||||
bool http_auto_referer; /* set "correct" referer when following location: */
|
||||
bool opt_no_body; /* as set with CURLOPT_NOBODY */
|
||||
bool set_port; /* custom port number used */
|
||||
bool upload; /* upload request */
|
||||
enum CURL_NETRC_OPTION
|
||||
use_netrc; /* defined in include/curl.h */
|
||||
|
@ -363,6 +363,12 @@ bool Curl_ssl_getsessionid(struct connectdata *conn,
|
||||
/* not session ID means blank entry */
|
||||
continue;
|
||||
if(Curl_raw_equal(conn->host.name, check->name) &&
|
||||
((!conn->bits.conn_to_host && !check->conn_to_host) ||
|
||||
(conn->bits.conn_to_host && check->conn_to_host &&
|
||||
Curl_raw_equal(conn->conn_to_host.name, check->conn_to_host))) &&
|
||||
((!conn->bits.conn_to_port && check->conn_to_port == -1) ||
|
||||
(conn->bits.conn_to_port && check->conn_to_port != -1 &&
|
||||
conn->conn_to_port == check->conn_to_port)) &&
|
||||
(conn->remote_port == check->remote_port) &&
|
||||
Curl_ssl_config_matches(&conn->ssl_config, &check->ssl_config)) {
|
||||
/* yes, we have a session ID! */
|
||||
@ -400,6 +406,7 @@ void Curl_ssl_kill_session(struct curl_ssl_session *session)
|
||||
Curl_free_ssl_config(&session->ssl_config);
|
||||
|
||||
Curl_safefree(session->name);
|
||||
Curl_safefree(session->conn_to_host);
|
||||
}
|
||||
}
|
||||
|
||||
@ -442,6 +449,8 @@ CURLcode Curl_ssl_addsessionid(struct connectdata *conn,
|
||||
struct curl_ssl_session *store = &data->state.session[0];
|
||||
long oldest_age=data->state.session[0].age; /* zero if unused */
|
||||
char *clone_host;
|
||||
char *clone_conn_to_host;
|
||||
int conn_to_port;
|
||||
long *general_age;
|
||||
|
||||
/* Even though session ID re-use might be disabled, that only disables USING
|
||||
@ -452,6 +461,21 @@ CURLcode Curl_ssl_addsessionid(struct connectdata *conn,
|
||||
if(!clone_host)
|
||||
return CURLE_OUT_OF_MEMORY; /* bail out */
|
||||
|
||||
if(conn->bits.conn_to_host) {
|
||||
clone_conn_to_host = strdup(conn->conn_to_host.name);
|
||||
if(!clone_conn_to_host) {
|
||||
free(clone_host);
|
||||
return CURLE_OUT_OF_MEMORY; /* bail out */
|
||||
}
|
||||
}
|
||||
else
|
||||
clone_conn_to_host = NULL;
|
||||
|
||||
if(conn->bits.conn_to_port)
|
||||
conn_to_port = conn->conn_to_port;
|
||||
else
|
||||
conn_to_port = -1;
|
||||
|
||||
/* Now we should add the session ID and the host name to the cache, (remove
|
||||
the oldest if necessary) */
|
||||
|
||||
@ -484,10 +508,12 @@ CURLcode Curl_ssl_addsessionid(struct connectdata *conn,
|
||||
store->age = *general_age; /* set current age */
|
||||
/* free it if there's one already present */
|
||||
free(store->name);
|
||||
free(store->conn_to_host);
|
||||
store->name = clone_host; /* clone host name */
|
||||
store->conn_to_host = clone_conn_to_host; /* clone connect to host name */
|
||||
store->conn_to_port = conn_to_port; /* connect to port number */
|
||||
store->remote_port = conn->remote_port; /* port number */
|
||||
|
||||
|
||||
/* Unlock */
|
||||
if(SSLSESSION_SHARED(data))
|
||||
Curl_share_unlock(data, CURL_LOCK_DATA_SSL_SESSION);
|
||||
@ -495,6 +521,7 @@ CURLcode Curl_ssl_addsessionid(struct connectdata *conn,
|
||||
if(!Curl_clone_ssl_config(&conn->ssl_config, &store->ssl_config)) {
|
||||
store->sessionid = NULL; /* let caller free sessionid */
|
||||
free(clone_host);
|
||||
free(clone_conn_to_host);
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
|
@ -135,6 +135,7 @@ static void free_config_fields(struct OperationConfig *config)
|
||||
|
||||
curl_slist_free_all(config->telnet_options);
|
||||
curl_slist_free_all(config->resolve);
|
||||
curl_slist_free_all(config->connect_to);
|
||||
|
||||
Curl_safefree(config->socksproxy);
|
||||
Curl_safefree(config->proxy_service_name);
|
||||
|
@ -151,6 +151,7 @@ struct OperationConfig {
|
||||
struct curl_httppost *last_post;
|
||||
struct curl_slist *telnet_options;
|
||||
struct curl_slist *resolve;
|
||||
struct curl_slist *connect_to;
|
||||
HttpReq httpreq;
|
||||
|
||||
/* for bandwidth limiting features: */
|
||||
|
@ -183,6 +183,7 @@ static const struct LongShort aliases[]= {
|
||||
{"$Q", "proto-default", TRUE},
|
||||
{"$R", "expect100-timeout", TRUE},
|
||||
{"$S", "tftp-no-options", FALSE},
|
||||
{"$U", "connect-to", TRUE},
|
||||
{"0", "http1.0", FALSE},
|
||||
{"01", "http1.1", FALSE},
|
||||
{"02", "http2", FALSE},
|
||||
@ -1009,6 +1010,11 @@ ParameterError getparameter(char *flag, /* f or -long-flag */
|
||||
case 'S': /* --tftp-no-options */
|
||||
config->tftp_no_options = toggle;
|
||||
break;
|
||||
case 'U': /* --connect-to */
|
||||
err = add2list(&config->connect_to, nextarg);
|
||||
if(err)
|
||||
return err;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case '#': /* --progress-bar */
|
||||
|
@ -58,6 +58,7 @@ static const char *const helptext[] = {
|
||||
" --compressed Request compressed response (using deflate or gzip)",
|
||||
" -K, --config FILE Read config from FILE",
|
||||
" --connect-timeout SECONDS Maximum time allowed for connection",
|
||||
" --connect-to HOST1:PORT1:HOST2:PORT2 Connect to host (network level)",
|
||||
" -C, --continue-at OFFSET Resumed transfer OFFSET",
|
||||
" -b, --cookie STRING/FILE Read cookies from STRING/FILE (H)",
|
||||
" -c, --cookie-jar FILE Write cookies to FILE after operation (H)",
|
||||
|
@ -1305,6 +1305,10 @@ static CURLcode operate_do(struct GlobalConfig *global,
|
||||
/* new in 7.21.3 */
|
||||
my_setopt_slist(curl, CURLOPT_RESOLVE, config->resolve);
|
||||
|
||||
if(config->connect_to)
|
||||
/* new in 7.49.0 */
|
||||
my_setopt_slist(curl, CURLOPT_CONNECT_TO, config->connect_to);
|
||||
|
||||
/* new in 7.21.4 */
|
||||
if(curlinfo->features & CURL_VERSION_TLSAUTH_SRP) {
|
||||
if(config->tls_username)
|
||||
|
@ -169,4 +169,4 @@ test2016 test2017 test2018 test2019 test2020 test2021 test2022 test2023 \
|
||||
test2024 test2025 test2026 test2027 test2028 test2029 test2030 test2031 \
|
||||
test2032 test2033 test2034 test2035 test2036 test2037 test2038 test2039 \
|
||||
test2040 test2041 test2042 test2043 test2044 test2045 test2046 test2047 \
|
||||
test2048
|
||||
test2048 test2049 test2050 test2051 test2052
|
||||
|
64
tests/data/test2049
Normal file
64
tests/data/test2049
Normal file
@ -0,0 +1,64 @@
|
||||
<testcase>
|
||||
<info>
|
||||
<keywords>
|
||||
HTTP
|
||||
HTTP GET
|
||||
CURLOPT_CONNECT_TO
|
||||
</keywords>
|
||||
</info>
|
||||
|
||||
#
|
||||
# Server-side
|
||||
<reply>
|
||||
<data>
|
||||
HTTP/1.1 200 OK
|
||||
Date: Thu, 09 Nov 2010 14:49:00 GMT
|
||||
Content-Length: 3
|
||||
Content-Type: text/plain
|
||||
|
||||
OK
|
||||
</data>
|
||||
</reply>
|
||||
|
||||
#
|
||||
# Client-side
|
||||
<client>
|
||||
<server>
|
||||
http
|
||||
</server>
|
||||
<name>
|
||||
Connect to specific host
|
||||
</name>
|
||||
|
||||
<command>
|
||||
http://www1.example.com:8081/2049 --connect-to ::%HOSTIP:%HTTPPORT --next http://www2.example.com:8082/2049 --connect-to :8082:%HOSTIP:%HTTPPORT --next http://www3.example.com:8083/2049 --connect-to www3.example.com::%HOSTIP:%HTTPPORT --next http://www4.example.com:8084/2049 --connect-to www4.example.com:8084:%HOSTIP:%HTTPPORT
|
||||
</command>
|
||||
</client>
|
||||
|
||||
#
|
||||
# Verify data after the test has been "shot"
|
||||
<verify>
|
||||
<strip>
|
||||
^User-Agent:.*
|
||||
</strip>
|
||||
<protocol>
|
||||
GET /2049 HTTP/1.1
|
||||
Host: www1.example.com:8081
|
||||
Accept: */*
|
||||
|
||||
GET /2049 HTTP/1.1
|
||||
Host: www2.example.com:8082
|
||||
Accept: */*
|
||||
|
||||
GET /2049 HTTP/1.1
|
||||
Host: www3.example.com:8083
|
||||
Accept: */*
|
||||
|
||||
GET /2049 HTTP/1.1
|
||||
Host: www4.example.com:8084
|
||||
Accept: */*
|
||||
|
||||
</protocol>
|
||||
|
||||
</verify>
|
||||
</testcase>
|
77
tests/data/test2050
Normal file
77
tests/data/test2050
Normal file
@ -0,0 +1,77 @@
|
||||
<testcase>
|
||||
<info>
|
||||
<keywords>
|
||||
HTTP
|
||||
HTTP GET
|
||||
HTTP CONNECT
|
||||
HTTP proxy
|
||||
proxytunnel
|
||||
CURLOPT_CONNECT_TO
|
||||
</keywords>
|
||||
</info>
|
||||
|
||||
#
|
||||
# Server-side
|
||||
<reply>
|
||||
<connect>
|
||||
HTTP/1.1 200 Connection established
|
||||
|
||||
</connect>
|
||||
|
||||
<data>
|
||||
HTTP/1.1 200 OK
|
||||
Date: Thu, 09 Nov 2010 14:49:00 GMT
|
||||
Content-Length: 3
|
||||
Content-Type: text/plain
|
||||
|
||||
OK
|
||||
</data>
|
||||
|
||||
<datacheck>
|
||||
HTTP/1.1 200 Connection established
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Date: Thu, 09 Nov 2010 14:49:00 GMT
|
||||
Content-Length: 3
|
||||
Content-Type: text/plain
|
||||
|
||||
OK
|
||||
</datacheck>
|
||||
</reply>
|
||||
|
||||
#
|
||||
# Client-side
|
||||
<client>
|
||||
<server>
|
||||
http
|
||||
http-proxy
|
||||
</server>
|
||||
<name>
|
||||
Connect to specific host via HTTP proxy (switch to tunnel mode automatically)
|
||||
</name>
|
||||
|
||||
<command>
|
||||
http://www.example.com.2050/2050 --connect-to ::connect.example.com.2050:%HTTPPORT -x %HOSTIP:%PROXYPORT
|
||||
</command>
|
||||
</client>
|
||||
|
||||
#
|
||||
# Verify data after the test has been "shot"
|
||||
<verify>
|
||||
<strip>
|
||||
^User-Agent:.*
|
||||
</strip>
|
||||
<proxy>
|
||||
CONNECT connect.example.com.2050:%HTTPPORT HTTP/1.1
|
||||
Host: connect.example.com.2050:%HTTPPORT
|
||||
|
||||
</proxy>
|
||||
<protocol>
|
||||
GET /2050 HTTP/1.1
|
||||
Host: www.example.com.2050
|
||||
Accept: */*
|
||||
|
||||
</protocol>
|
||||
|
||||
</verify>
|
||||
</testcase>
|
74
tests/data/test2051
Normal file
74
tests/data/test2051
Normal file
@ -0,0 +1,74 @@
|
||||
<testcase>
|
||||
<info>
|
||||
<keywords>
|
||||
HTTP
|
||||
HTTP GET
|
||||
CURLOPT_CONNECT_TO
|
||||
</keywords>
|
||||
</info>
|
||||
|
||||
#
|
||||
# Server-side
|
||||
<reply>
|
||||
<data nocheck="yes">
|
||||
HTTP/1.1 200 OK
|
||||
Date: Thu, 09 Nov 2010 14:49:00 GMT
|
||||
Content-Length: 3
|
||||
Content-Type: text/plain
|
||||
|
||||
OK
|
||||
</data>
|
||||
</reply>
|
||||
|
||||
#
|
||||
# Client-side
|
||||
<client>
|
||||
<server>
|
||||
http
|
||||
</server>
|
||||
<name>
|
||||
Connect to specific host: Re-use existing connections if possible
|
||||
</name>
|
||||
|
||||
<command>
|
||||
http://%HOSTIP:%HTTPPORT/2051 -w "%{num_connects}\n" --next --connect-to ::%HOSTIP:%HTTPPORT http://%HOSTIP:%HTTPPORT/2051 -w "%{num_connects}\n" --next http://%HOSTIP:%HTTPPORT/2051 -w "%{num_connects}\n"
|
||||
</command>
|
||||
</client>
|
||||
|
||||
#
|
||||
# Verify data after the test has been "shot"
|
||||
<verify>
|
||||
<strip>
|
||||
^User-Agent:.*
|
||||
</strip>
|
||||
<protocol>
|
||||
GET /2051 HTTP/1.1
|
||||
Host: %HOSTIP:%HTTPPORT
|
||||
Accept: */*
|
||||
|
||||
GET /2051 HTTP/1.1
|
||||
Host: %HOSTIP:%HTTPPORT
|
||||
Accept: */*
|
||||
|
||||
GET /2051 HTTP/1.1
|
||||
Host: %HOSTIP:%HTTPPORT
|
||||
Accept: */*
|
||||
|
||||
</protocol>
|
||||
|
||||
<stdout>
|
||||
HTTP/1.1 200 OK
|
||||
Date: Thu, 09 Nov 2010 14:49:00 GMT
|
||||
Content-Length: 3
|
||||
Content-Type: text/plain
|
||||
|
||||
OK
|
||||
1
|
||||
OK
|
||||
0
|
||||
OK
|
||||
0
|
||||
</stdout>
|
||||
|
||||
</verify>
|
||||
</testcase>
|
68
tests/data/test2052
Normal file
68
tests/data/test2052
Normal file
@ -0,0 +1,68 @@
|
||||
<testcase>
|
||||
<info>
|
||||
<keywords>
|
||||
HTTP
|
||||
HTTP GET
|
||||
CURLOPT_CONNECT_TO
|
||||
</keywords>
|
||||
</info>
|
||||
|
||||
#
|
||||
# Server-side
|
||||
<reply>
|
||||
<data nocheck="yes">
|
||||
HTTP/1.1 200 OK
|
||||
Date: Thu, 09 Nov 2010 14:49:00 GMT
|
||||
Content-Length: 3
|
||||
Content-Type: text/plain
|
||||
|
||||
OK
|
||||
</data>
|
||||
</reply>
|
||||
|
||||
#
|
||||
# Client-side
|
||||
<client>
|
||||
<server>
|
||||
http
|
||||
</server>
|
||||
<name>
|
||||
Connect to specific host: Do not mix connections with and without a "connect to host"
|
||||
</name>
|
||||
|
||||
<command>
|
||||
http://www.example.com:%HTTPPORT/2052 --resolve www.example.com:%HTTPPORT:%HOSTIP -w "%{num_connects}\n" --next --resolve -www.example.com:%HTTPPORT --connect-to ::%HOSTIP:%HTTPPORT http://www.example.com:%HTTPPORT/2052 -w "%{num_connects}\n"
|
||||
</command>
|
||||
</client>
|
||||
|
||||
#
|
||||
# Verify data after the test has been "shot"
|
||||
<verify>
|
||||
<strip>
|
||||
^User-Agent:.*
|
||||
</strip>
|
||||
<protocol>
|
||||
GET /2052 HTTP/1.1
|
||||
Host: www.example.com:%HTTPPORT
|
||||
Accept: */*
|
||||
|
||||
GET /2052 HTTP/1.1
|
||||
Host: www.example.com:%HTTPPORT
|
||||
Accept: */*
|
||||
|
||||
</protocol>
|
||||
|
||||
<stdout>
|
||||
HTTP/1.1 200 OK
|
||||
Date: Thu, 09 Nov 2010 14:49:00 GMT
|
||||
Content-Length: 3
|
||||
Content-Type: text/plain
|
||||
|
||||
OK
|
||||
1
|
||||
OK
|
||||
1
|
||||
</stdout>
|
||||
|
||||
</verify>
|
||||
</testcase>
|
Loading…
Reference in New Issue
Block a user