1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-10 18:07:59 +00:00

Merge HTTP proxy fixes from 'pre-0.77'.

This commit is contained in:
Simon Tatham 2022-02-19 12:53:51 +00:00
commit 5886d610d8
3 changed files with 115 additions and 12 deletions

View File

@ -72,7 +72,8 @@ typedef struct HttpProxyNegotiator {
uint32_t nonce_count; uint32_t nonce_count;
prompts_t *prompts; prompts_t *prompts;
int username_prompt_index, password_prompt_index; int username_prompt_index, password_prompt_index;
size_t content_length; size_t content_length, chunk_length;
bool chunked_transfer;
ProxyNegotiator pn; ProxyNegotiator pn;
} HttpProxyNegotiator; } HttpProxyNegotiator;
@ -151,7 +152,9 @@ static void proxy_http_free(ProxyNegotiator *pn)
#define HTTP_HEADER_LIST(X) \ #define HTTP_HEADER_LIST(X) \
X(HDR_CONNECTION, "Connection") \ X(HDR_CONNECTION, "Connection") \
X(HDR_CONTENT_LENGTH, "Content-Length") \ X(HDR_CONTENT_LENGTH, "Content-Length") \
X(HDR_TRANSFER_ENCODING, "Transfer-Encoding") \
X(HDR_PROXY_AUTHENTICATE, "Proxy-Authenticate") \ X(HDR_PROXY_AUTHENTICATE, "Proxy-Authenticate") \
X(HDR_PROXY_CONNECTION, "Proxy-Connection") \
/* end of list */ /* end of list */
typedef enum HttpHeader { typedef enum HttpHeader {
@ -322,7 +325,7 @@ static HttpAuthDetails *parse_http_auth_header(HttpProxyNegotiator *s)
d->hash_username = !stricmp(s->token->s, "true"); d->hash_username = !stricmp(s->token->s, "true");
} else if (!stricmp(s->token->s, "algorithm")) { } else if (!stricmp(s->token->s, "algorithm")) {
if (!get_separator(s, '=') || if (!get_separator(s, '=') ||
!get_token(s)) (!get_token(s) && !get_quoted_string(s)))
return auth_error(d, "parse error in Digest algorithm " return auth_error(d, "parse error in Digest algorithm "
"field"); "field");
bool found = false; bool found = false;
@ -460,6 +463,7 @@ static void proxy_http_process_queue(ProxyNegotiator *pn)
crReturnV; crReturnV;
s->content_length = 0; s->content_length = 0;
s->chunked_transfer = false;
s->connection_close = false; s->connection_close = false;
/* /*
@ -530,7 +534,25 @@ static void proxy_http_process_queue(ProxyNegotiator *pn)
if (!get_token(s)) if (!get_token(s))
continue; continue;
s->content_length = strtoumax(s->token->s, NULL, 10); 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)) if (!get_token(s))
continue; continue;
if (!stricmp(s->token->s, "close")) if (!stricmp(s->token->s, "close"))
@ -584,8 +606,62 @@ static void proxy_http_process_queue(ProxyNegotiator *pn)
} while (s->header->len > 0); } while (s->header->len > 0);
/* Read and ignore the entire response document */ /* Read and ignore the entire response document */
if (!s->chunked_transfer) {
/* Simple approach: read exactly Content-Length bytes */
crMaybeWaitUntilV(bufchain_try_consume( crMaybeWaitUntilV(bufchain_try_consume(
pn->input, s->content_length)); 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) { if (200 <= s->http_status && s->http_status < 300) {
/* Any 2xx HTTP response means we're done */ /* 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 /* 407 is Proxy Authentication Required, which we may be
* able to do something about. */ * able to do something about. */
if (s->connection_close) { if (s->connection_close) {
pn->error = dupprintf("HTTP proxy closed connection after " /* If we got 407 + connection closed, reconnect before
"asking for authentication"); * sending our next request. */
crStopV; pn->reconnect = true;
} }
/* If the best we can do is report some kind of error from /* If the best we can do is report some kind of error from

View File

@ -99,6 +99,7 @@ static void sk_proxy_close (Socket *s)
ProxySocket *ps = container_of(s, ProxySocket, sock); ProxySocket *ps = container_of(s, ProxySocket, sock);
sk_close(ps->sub_socket); sk_close(ps->sub_socket);
sk_addr_free(ps->proxy_addr);
sk_addr_free(ps->remote_addr); sk_addr_free(ps->remote_addr);
proxy_negotiator_cleanup(ps); proxy_negotiator_cleanup(ps);
bufchain_clear(&ps->output_from_negotiator); bufchain_clear(&ps->output_from_negotiator);
@ -223,6 +224,16 @@ static void proxy_negotiate(ProxySocket *ps)
return; 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)) { while (bufchain_size(&ps->output_from_negotiator)) {
ptrlen data = bufchain_prefix(&ps->output_from_negotiator); ptrlen data = bufchain_prefix(&ps->output_from_negotiator);
sk_write(ps->sub_socket, data.ptr, data.len); 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, /* create the actual socket we will be using,
* connected to our proxy server and port. * connected to our proxy server and port.
*/ */
ps->sub_socket = sk_new(proxy_addr, ps->proxy_addr = sk_addr_dup(proxy_addr);
conf_get_int(conf, CONF_proxy_port), ps->proxy_port = conf_get_int(conf, CONF_proxy_port);
privport, oobinline, ps->proxy_privport = privport;
nodelay, keepalive, &ps->plugimpl); 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) if (sk_socket_error(ps->sub_socket) != NULL)
return &ps->sock; return &ps->sock;

View File

@ -22,6 +22,11 @@ struct ProxySocket {
SockAddr *remote_addr; SockAddr *remote_addr;
int remote_port; 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_output_data;
bufchain pending_oob_output_data; bufchain pending_oob_output_data;
bufchain pending_input_data; bufchain pending_input_data;
@ -68,6 +73,11 @@ struct ProxyNegotiator {
/* Set to report user abort during proxy negotiation. */ /* Set to report user abort during proxy negotiation. */
bool aborted; 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 { struct ProxyNegotiatorVT {