mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-03-19 05:15:26 -05:00
159 lines
4.3 KiB
C
159 lines
4.3 KiB
C
![]() |
/*
|
||
|
* HTTP CONNECT proxy negotiation.
|
||
|
*/
|
||
|
|
||
|
#include "putty.h"
|
||
|
#include "network.h"
|
||
|
#include "proxy.h"
|
||
|
#include "sshcr.h"
|
||
|
|
||
|
static bool read_line(bufchain *input, strbuf *output, bool is_header)
|
||
|
{
|
||
|
char c;
|
||
|
|
||
|
while (bufchain_try_fetch(input, &c, 1)) {
|
||
|
if (is_header && output->len > 0 &&
|
||
|
output->s[output->len - 1] == '\n') {
|
||
|
/*
|
||
|
* A newline terminates the header, provided we're sure it
|
||
|
* is _not_ followed by a space or a tab.
|
||
|
*/
|
||
|
if (c != ' ' && c != '\t')
|
||
|
goto done; /* we have a complete header line */
|
||
|
} else {
|
||
|
put_byte(output, c);
|
||
|
bufchain_consume(input, 1);
|
||
|
|
||
|
if (!is_header && output->len > 0 &&
|
||
|
output->s[output->len - 1] == '\n') {
|
||
|
/* If we're looking for just a line, not an HTTP
|
||
|
* header, then any newline terminates it. */
|
||
|
goto done;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
|
||
|
done:
|
||
|
strbuf_chomp(output, '\n');
|
||
|
strbuf_chomp(output, '\r');
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
typedef struct HttpProxyNegotiator {
|
||
|
int crLine;
|
||
|
strbuf *line;
|
||
|
ProxyNegotiator pn;
|
||
|
} HttpProxyNegotiator;
|
||
|
|
||
|
static ProxyNegotiator *proxy_http_new(const ProxyNegotiatorVT *vt)
|
||
|
{
|
||
|
HttpProxyNegotiator *s = snew(HttpProxyNegotiator);
|
||
|
s->pn.vt = vt;
|
||
|
s->crLine = 0;
|
||
|
s->line = strbuf_new();
|
||
|
return &s->pn;
|
||
|
}
|
||
|
|
||
|
static void proxy_http_free(ProxyNegotiator *pn)
|
||
|
{
|
||
|
HttpProxyNegotiator *s = container_of(pn, HttpProxyNegotiator, pn);
|
||
|
strbuf_free(s->line);
|
||
|
sfree(s);
|
||
|
}
|
||
|
|
||
|
static void proxy_http_process_queue(ProxyNegotiator *pn)
|
||
|
{
|
||
|
HttpProxyNegotiator *s = container_of(pn, HttpProxyNegotiator, pn);
|
||
|
|
||
|
crBegin(s->crLine);
|
||
|
|
||
|
/*
|
||
|
* Standard prefix for the HTTP CONNECT request.
|
||
|
*/
|
||
|
{
|
||
|
char dest[512];
|
||
|
sk_getaddr(pn->ps->remote_addr, dest, lenof(dest));
|
||
|
put_fmt(pn->output,
|
||
|
"CONNECT %s:%d HTTP/1.1\r\n"
|
||
|
"Host: %s:%d\r\n",
|
||
|
dest, pn->ps->remote_port, dest, pn->ps->remote_port);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Optionally send an HTTP Basic auth header with the username and
|
||
|
* password.
|
||
|
*/
|
||
|
{
|
||
|
const char *username = conf_get_str(pn->ps->conf, CONF_proxy_username);
|
||
|
const char *password = conf_get_str(pn->ps->conf, CONF_proxy_password);
|
||
|
if (username[0] || password[0]) {
|
||
|
put_datalit(pn->output, "Proxy-Authorization: Basic ");
|
||
|
|
||
|
char *base64_input = dupcat(username, ":", password);
|
||
|
char base64_output[4];
|
||
|
for (size_t i = 0, e = strlen(base64_input); i < e; i += 3) {
|
||
|
base64_encode_atom((const unsigned char *)base64_input + i,
|
||
|
e-i > 3 ? 3 : e-i, base64_output);
|
||
|
put_data(pn->output, base64_output, 4);
|
||
|
}
|
||
|
burnstr(base64_input);
|
||
|
smemclr(base64_output, sizeof(base64_output));
|
||
|
put_datalit(pn->output, "\r\n");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Blank line to terminate the HTTP request.
|
||
|
*/
|
||
|
put_datalit(pn->output, "\r\n");
|
||
|
crReturnV;
|
||
|
|
||
|
/*
|
||
|
* Read and parse the HTTP status line, and check if it's a 2xx
|
||
|
* for success.
|
||
|
*/
|
||
|
strbuf_clear(s->line);
|
||
|
crMaybeWaitUntilV(read_line(pn->input, s->line, false));
|
||
|
{
|
||
|
int maj_ver, min_ver, status_pos = -1;
|
||
|
sscanf(s->line->s, "HTTP/%d.%d %n", &maj_ver, &min_ver, &status_pos);
|
||
|
|
||
|
/* If status_pos is still -1 then the sscanf didn't get right
|
||
|
* to the end of the string */
|
||
|
if (status_pos == -1) {
|
||
|
pn->error = dupstr("HTTP response was absent or malformed");
|
||
|
crStopV;
|
||
|
}
|
||
|
|
||
|
if (s->line->s[status_pos] != '2') {
|
||
|
pn->error = dupprintf("HTTP response %s", s->line->s + status_pos);
|
||
|
crStopV;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Read and skip the rest of the HTTP response headers, terminated
|
||
|
* by a blank line.
|
||
|
*/
|
||
|
do {
|
||
|
strbuf_clear(s->line);
|
||
|
crMaybeWaitUntilV(read_line(pn->input, s->line, true));
|
||
|
} while (s->line->len > 0);
|
||
|
|
||
|
/*
|
||
|
* Success! Hand over to the main connection.
|
||
|
*/
|
||
|
pn->done = true;
|
||
|
|
||
|
crFinishV;
|
||
|
}
|
||
|
|
||
|
const struct ProxyNegotiatorVT http_proxy_negotiator_vt = {
|
||
|
.new = proxy_http_new,
|
||
|
.free = proxy_http_free,
|
||
|
.process_queue = proxy_http_process_queue,
|
||
|
.type = "HTTP",
|
||
|
};
|