mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-10 09:58:01 +00:00
Merge HTTP proxy fixes from 'pre-0.77'.
This commit is contained in:
commit
5886d610d8
88
proxy/http.c
88
proxy/http.c
@ -72,7 +72,8 @@ typedef struct HttpProxyNegotiator {
|
||||
uint32_t nonce_count;
|
||||
prompts_t *prompts;
|
||||
int username_prompt_index, password_prompt_index;
|
||||
size_t content_length;
|
||||
size_t content_length, chunk_length;
|
||||
bool chunked_transfer;
|
||||
ProxyNegotiator pn;
|
||||
} HttpProxyNegotiator;
|
||||
|
||||
@ -151,7 +152,9 @@ static void proxy_http_free(ProxyNegotiator *pn)
|
||||
#define HTTP_HEADER_LIST(X) \
|
||||
X(HDR_CONNECTION, "Connection") \
|
||||
X(HDR_CONTENT_LENGTH, "Content-Length") \
|
||||
X(HDR_TRANSFER_ENCODING, "Transfer-Encoding") \
|
||||
X(HDR_PROXY_AUTHENTICATE, "Proxy-Authenticate") \
|
||||
X(HDR_PROXY_CONNECTION, "Proxy-Connection") \
|
||||
/* end of list */
|
||||
|
||||
typedef enum HttpHeader {
|
||||
@ -322,7 +325,7 @@ static HttpAuthDetails *parse_http_auth_header(HttpProxyNegotiator *s)
|
||||
d->hash_username = !stricmp(s->token->s, "true");
|
||||
} else if (!stricmp(s->token->s, "algorithm")) {
|
||||
if (!get_separator(s, '=') ||
|
||||
!get_token(s))
|
||||
(!get_token(s) && !get_quoted_string(s)))
|
||||
return auth_error(d, "parse error in Digest algorithm "
|
||||
"field");
|
||||
bool found = false;
|
||||
@ -460,6 +463,7 @@ static void proxy_http_process_queue(ProxyNegotiator *pn)
|
||||
crReturnV;
|
||||
|
||||
s->content_length = 0;
|
||||
s->chunked_transfer = false;
|
||||
s->connection_close = false;
|
||||
|
||||
/*
|
||||
@ -530,7 +534,25 @@ static void proxy_http_process_queue(ProxyNegotiator *pn)
|
||||
if (!get_token(s))
|
||||
continue;
|
||||
s->content_length = strtoumax(s->token->s, NULL, 10);
|
||||
} else if (hdr == HDR_CONNECTION) {
|
||||
} else if (hdr == HDR_TRANSFER_ENCODING) {
|
||||
/*
|
||||
* The Transfer-Encoding header value should be a
|
||||
* comma-separated list of keywords including
|
||||
* "chunked", "deflate" and "gzip". We parse it in the
|
||||
* most superficial way, by just looking for "chunked"
|
||||
* and ignoring everything else.
|
||||
*
|
||||
* It's OK to do that because we're not actually
|
||||
* _using_ the error document - we only have to skip
|
||||
* over it to find the end of the HTTP response. So we
|
||||
* don't care if it's gzipped or not.
|
||||
*/
|
||||
while (get_token(s)) {
|
||||
if (!stricmp(s->token->s, "chunked"))
|
||||
s->chunked_transfer = true;
|
||||
}
|
||||
} else if (hdr == HDR_CONNECTION ||
|
||||
hdr == HDR_PROXY_CONNECTION) {
|
||||
if (!get_token(s))
|
||||
continue;
|
||||
if (!stricmp(s->token->s, "close"))
|
||||
@ -584,8 +606,62 @@ static void proxy_http_process_queue(ProxyNegotiator *pn)
|
||||
} while (s->header->len > 0);
|
||||
|
||||
/* Read and ignore the entire response document */
|
||||
if (!s->chunked_transfer) {
|
||||
/* Simple approach: read exactly Content-Length bytes */
|
||||
crMaybeWaitUntilV(bufchain_try_consume(
|
||||
pn->input, s->content_length));
|
||||
} else {
|
||||
/* Chunked transfer: read a sequence of
|
||||
* <hex length>\r\n<data>\r\n chunks, terminating in one with
|
||||
* zero length */
|
||||
do {
|
||||
/*
|
||||
* Expect a chunk length
|
||||
*/
|
||||
s->chunk_length = 0;
|
||||
while (true) {
|
||||
char c;
|
||||
crMaybeWaitUntilV(bufchain_try_fetch_consume(
|
||||
pn->input, &c, 1));
|
||||
if (c == '\r') {
|
||||
continue;
|
||||
} else if (c == '\n') {
|
||||
break;
|
||||
} else if ('0' <= c && c <= '9') {
|
||||
s->chunk_length = s->chunk_length*16 + (c-'0');
|
||||
} else if ('A' <= c && c <= 'F') {
|
||||
s->chunk_length = s->chunk_length*16 + (c-'A'+10);
|
||||
} else if ('a' <= c && c <= 'f') {
|
||||
s->chunk_length = s->chunk_length*16 + (c-'a'+10);
|
||||
} else {
|
||||
pn->error = dupprintf(
|
||||
"Received bad character 0x%02X in chunk length "
|
||||
"during HTTP chunked transfer encoding",
|
||||
(unsigned)(unsigned char)c);
|
||||
crStopV;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Expect that many bytes of chunked data
|
||||
*/
|
||||
crMaybeWaitUntilV(bufchain_try_consume(
|
||||
pn->input, s->chunk_length));
|
||||
|
||||
/* Now expect \r\n */
|
||||
{
|
||||
char buf[2];
|
||||
crMaybeWaitUntilV(bufchain_try_fetch_consume(
|
||||
pn->input, buf, 2));
|
||||
if (memcmp(buf, "\r\n", 2)) {
|
||||
pn->error = dupprintf(
|
||||
"Missing CRLF after chunk "
|
||||
"during HTTP chunked transfer encoding");
|
||||
crStopV;
|
||||
}
|
||||
}
|
||||
} while (s->chunk_length);
|
||||
}
|
||||
|
||||
if (200 <= s->http_status && s->http_status < 300) {
|
||||
/* Any 2xx HTTP response means we're done */
|
||||
@ -594,9 +670,9 @@ static void proxy_http_process_queue(ProxyNegotiator *pn)
|
||||
/* 407 is Proxy Authentication Required, which we may be
|
||||
* able to do something about. */
|
||||
if (s->connection_close) {
|
||||
pn->error = dupprintf("HTTP proxy closed connection after "
|
||||
"asking for authentication");
|
||||
crStopV;
|
||||
/* If we got 407 + connection closed, reconnect before
|
||||
* sending our next request. */
|
||||
pn->reconnect = true;
|
||||
}
|
||||
|
||||
/* If the best we can do is report some kind of error from
|
||||
|
@ -99,6 +99,7 @@ static void sk_proxy_close (Socket *s)
|
||||
ProxySocket *ps = container_of(s, ProxySocket, sock);
|
||||
|
||||
sk_close(ps->sub_socket);
|
||||
sk_addr_free(ps->proxy_addr);
|
||||
sk_addr_free(ps->remote_addr);
|
||||
proxy_negotiator_cleanup(ps);
|
||||
bufchain_clear(&ps->output_from_negotiator);
|
||||
@ -223,6 +224,16 @@ static void proxy_negotiate(ProxySocket *ps)
|
||||
return;
|
||||
}
|
||||
|
||||
if (ps->pn->reconnect) {
|
||||
sk_close(ps->sub_socket);
|
||||
SockAddr *proxy_addr = sk_addr_dup(ps->proxy_addr);
|
||||
ps->sub_socket = sk_new(proxy_addr, ps->proxy_port,
|
||||
ps->proxy_privport, ps->proxy_oobinline,
|
||||
ps->proxy_nodelay, ps->proxy_keepalive,
|
||||
&ps->plugimpl);
|
||||
ps->pn->reconnect = false;
|
||||
}
|
||||
|
||||
while (bufchain_size(&ps->output_from_negotiator)) {
|
||||
ptrlen data = bufchain_prefix(&ps->output_from_negotiator);
|
||||
sk_write(ps->sub_socket, data.ptr, data.len);
|
||||
@ -601,10 +612,16 @@ Socket *new_connection(SockAddr *addr, const char *hostname,
|
||||
/* create the actual socket we will be using,
|
||||
* connected to our proxy server and port.
|
||||
*/
|
||||
ps->sub_socket = sk_new(proxy_addr,
|
||||
conf_get_int(conf, CONF_proxy_port),
|
||||
privport, oobinline,
|
||||
nodelay, keepalive, &ps->plugimpl);
|
||||
ps->proxy_addr = sk_addr_dup(proxy_addr);
|
||||
ps->proxy_port = conf_get_int(conf, CONF_proxy_port);
|
||||
ps->proxy_privport = privport;
|
||||
ps->proxy_oobinline = oobinline;
|
||||
ps->proxy_nodelay = nodelay;
|
||||
ps->proxy_keepalive = keepalive;
|
||||
ps->sub_socket = sk_new(proxy_addr, ps->proxy_port,
|
||||
ps->proxy_privport, ps->proxy_oobinline,
|
||||
ps->proxy_nodelay, ps->proxy_keepalive,
|
||||
&ps->plugimpl);
|
||||
if (sk_socket_error(ps->sub_socket) != NULL)
|
||||
return &ps->sock;
|
||||
|
||||
|
@ -22,6 +22,11 @@ struct ProxySocket {
|
||||
SockAddr *remote_addr;
|
||||
int remote_port;
|
||||
|
||||
/* Parameters needed to make further connections to the proxy */
|
||||
SockAddr *proxy_addr;
|
||||
int proxy_port;
|
||||
bool proxy_privport, proxy_oobinline, proxy_nodelay, proxy_keepalive;
|
||||
|
||||
bufchain pending_output_data;
|
||||
bufchain pending_oob_output_data;
|
||||
bufchain pending_input_data;
|
||||
@ -68,6 +73,11 @@ struct ProxyNegotiator {
|
||||
|
||||
/* Set to report user abort during proxy negotiation. */
|
||||
bool aborted;
|
||||
|
||||
/* Set to request the centralised code to make a fresh connection
|
||||
* to the proxy server, e.g. because an HTTP proxy slammed the
|
||||
* connection shut after sending 407 Proxy Auth Required. */
|
||||
bool reconnect;
|
||||
};
|
||||
|
||||
struct ProxyNegotiatorVT {
|
||||
|
Loading…
Reference in New Issue
Block a user