[mod_proxy] basic support for HTTP CONNECT method (#2060)

For security reasons, this supports only specific, pre-configured
target backends and not arbitrary CONNECT targets.

x-ref:
  "mod_connect"
  https://redmine.lighttpd.net/issues/2060
  "ssh over https tunnel"
  https://redmine.lighttpd.net/boards/2/topics/7805

  https://en.wikipedia.org/wiki/HTTP_tunnel
  https://nurdletech.com/linux-notes/ssh/via-http.html
personal/stbuehler/fix-fdevent
Glenn Strauss 6 years ago
parent d5d0258362
commit 3770df2387

@ -424,7 +424,10 @@ static int connection_handle_write_prepare(server *srv, connection *con) {
*/
if (!(con->parsed_response & (HTTP_CONTENT_LENGTH|HTTP_TRANSFER_ENCODING|HTTP_UPGRADE))) {
if (con->request.http_version == HTTP_VERSION_1_1) {
if (con->request.http_method == HTTP_METHOD_CONNECT
&& con->http_status == 200) {
/*(no transfer-encoding if successful CONNECT)*/
} else if (con->request.http_version == HTTP_VERSION_1_1) {
off_t qlen = chunkqueue_length(con->write_queue);
con->response.transfer_encoding = HTTP_TRANSFER_ENCODING_CHUNKED;
if (qlen) {

@ -28,6 +28,7 @@ typedef struct http_header_remap_opts {
const array *hosts_response;
int https_remap;
int upgrade;
int connect_method;
/*(not used in plugin_config, but used in handler_ctx)*/
const buffer *http_host;
const buffer *forwarded_host;
@ -241,6 +242,17 @@ SETDEFAULTS_FUNC(mod_proxy_set_defaults) {
&& !buffer_is_equal_string(ds->value, CONST_STR_LEN("0"));
continue;
}
else if (buffer_is_equal_string(da->key, CONST_STR_LEN("connect"))) {
data_string *ds = (data_string *)da;
if (ds->type != TYPE_STRING) {
log_error_write(srv, __FILE__, __LINE__, "s",
"unexpected value for proxy.header; expected \"connect\" => \"enable\" or \"disable\"");
return HANDLER_ERROR;
}
s->header.connect_method = !buffer_is_equal_string(ds->value, CONST_STR_LEN("disable"))
&& !buffer_is_equal_string(ds->value, CONST_STR_LEN("0"));
continue;
}
if (da->type != TYPE_ARRAY || !array_is_kvstring(da->value)) {
log_error_write(srv, __FILE__, __LINE__, "sb",
"unexpected value for proxy.header; expected ( \"param\" => ( \"key\" => \"value\" ) ) near key", da->key);
@ -850,6 +862,20 @@ static handler_t proxy_create_env(server *srv, gw_handler_ctx *gwhctx) {
return HANDLER_GO_ON;
}
static handler_t proxy_create_env_connect(server *srv, gw_handler_ctx *gwhctx) {
handler_ctx *hctx = (handler_ctx *)gwhctx;
connection *con = hctx->gw.remote_conn;
con->http_status = 200; /* OK */
con->file_started = 1;
gw_set_transparent(srv, &hctx->gw);
http_response_upgrade_read_body_unknown(srv, con);
status_counter_inc(srv, CONST_STR_LEN("proxy.requests"));
return HANDLER_GO_ON;
}
#define PATCH(x) \
p->conf.x = s->x;
#define PATCH_GW(x) \
@ -983,6 +1009,19 @@ static handler_t mod_proxy_check_extension(server *srv, connection *con, void *p
hctx->remap_hdrs.https_remap =
buffer_is_equal_string(con->uri.scheme, CONST_STR_LEN("https"));
}
if (con->request.http_method == HTTP_METHOD_CONNECT) {
/*(note: not requiring HTTP/1.1 due to too many non-compliant
* clients such as 'openssl s_client')*/
if (hctx->remap_hdrs.connect_method) {
hctx->gw.create_env = proxy_create_env_connect;
}
else {
con->http_status = 405; /* Method Not Allowed */
con->mode = DIRECT;
return HANDLER_FINISHED;
}
}
}
return HANDLER_GO_ON;

@ -218,6 +218,8 @@ handler_t http_response_prepare(server *srv, connection *con) {
con->uri.path_raw->ptr[0] == '*' && con->uri.path_raw->ptr[1] == '\0') {
/* OPTIONS * ... */
buffer_copy_buffer(con->uri.path, con->uri.path_raw);
} else if (con->request.http_method == HTTP_METHOD_CONNECT) {
buffer_copy_buffer(con->uri.path, con->uri.path_raw);
} else {
buffer_copy_buffer(srv->tmp_buf, con->uri.path_raw);
buffer_urldecode_path(srv->tmp_buf);

Loading…
Cancel
Save