|
|
|
/*-
|
|
|
|
* SSLsplit - transparent SSL/TLS interception
|
|
|
|
* https://www.roe.ch/SSLsplit
|
|
|
|
*
|
|
|
|
* Copyright (c) 2009-2019, Daniel Roethlisberger <daniel@roe.ch>.
|
|
|
|
* Copyright (c) 2017-2024, Soner Tari <sonertari@gmail.com>.
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright notice,
|
|
|
|
* this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
|
|
* and/or other materials provided with the distribution.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS''
|
|
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "protohttp.h"
|
|
|
|
#include "prototcp.h"
|
|
|
|
#include "protossl.h"
|
|
|
|
#include "protopassthrough.h"
|
|
|
|
|
|
|
|
#include "util.h"
|
|
|
|
#include "base64.h"
|
|
|
|
#include "url.h"
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
#include <event2/bufferevent.h>
|
|
|
|
|
|
|
|
static void NONNULL(1)
|
|
|
|
protohttp_log_connect(pxy_conn_ctx_t *ctx)
|
|
|
|
{
|
|
|
|
if (!ctx->log_connect)
|
|
|
|
return;
|
|
|
|
|
|
|
|
protohttp_ctx_t *http_ctx = ctx->protoctx->arg;
|
|
|
|
|
|
|
|
char *msg;
|
|
|
|
#ifdef HAVE_LOCAL_PROCINFO
|
|
|
|
char *lpi = NULL;
|
|
|
|
#endif /* HAVE_LOCAL_PROCINFO */
|
|
|
|
int rv;
|
|
|
|
|
|
|
|
#ifdef HAVE_LOCAL_PROCINFO
|
|
|
|
if (ctx->global->lprocinfo) {
|
|
|
|
rv = asprintf(&lpi, "lproc:%i:%s:%s:%s",
|
|
|
|
ctx->lproc.pid,
|
|
|
|
STRORDASH(ctx->lproc.user),
|
|
|
|
STRORDASH(ctx->lproc.group),
|
|
|
|
STRORDASH(ctx->lproc.exec_path));
|
|
|
|
if ((rv < 0) || !lpi) {
|
|
|
|
ctx->enomem = 1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif /* HAVE_LOCAL_PROCINFO */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The following ifdef's within asprintf arguments list generates
|
|
|
|
* warnings with -Wembedded-directive on some compilers.
|
|
|
|
* Not fixing the code in order to avoid more code duplication.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (!ctx->spec->ssl) {
|
|
|
|
rv = asprintf(&msg, "CONN: http %s %s %s %s %s %s %s %s %s"
|
|
|
|
#ifdef HAVE_LOCAL_PROCINFO
|
|
|
|
" %s"
|
|
|
|
#endif /* HAVE_LOCAL_PROCINFO */
|
|
|
|
"%s"
|
|
|
|
#ifndef WITHOUT_USERAUTH
|
|
|
|
" user:%s"
|
|
|
|
#endif /* !WITHOUT_USERAUTH */
|
|
|
|
"\n",
|
|
|
|
STRORDASH(ctx->srchost_str),
|
|
|
|
STRORDASH(ctx->srcport_str),
|
|
|
|
STRORDASH(ctx->dsthost_str),
|
|
|
|
STRORDASH(ctx->dstport_str),
|
|
|
|
STRORDASH(http_ctx->http_host),
|
|
|
|
STRORDASH(http_ctx->http_method),
|
|
|
|
STRORDASH(http_ctx->http_uri),
|
|
|
|
STRORDASH(http_ctx->http_status_code),
|
|
|
|
STRORDASH(http_ctx->http_content_length),
|
|
|
|
#ifdef HAVE_LOCAL_PROCINFO
|
|
|
|
lpi,
|
|
|
|
#endif /* HAVE_LOCAL_PROCINFO */
|
|
|
|
http_ctx->ocsp_denied ? " ocsp:denied" : ""
|
|
|
|
#ifndef WITHOUT_USERAUTH
|
|
|
|
, STRORDASH(ctx->user)
|
|
|
|
#endif /* !WITHOUT_USERAUTH */
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
rv = asprintf(&msg, "CONN: https %s %s %s %s %s %s %s %s %s "
|
|
|
|
"sni:%s names:%s "
|
|
|
|
"sproto:%s:%s dproto:%s:%s "
|
|
|
|
"origcrt:%s usedcrt:%s"
|
|
|
|
#ifdef HAVE_LOCAL_PROCINFO
|
|
|
|
" %s"
|
|
|
|
#endif /* HAVE_LOCAL_PROCINFO */
|
|
|
|
"%s"
|
|
|
|
#ifndef WITHOUT_USERAUTH
|
|
|
|
" user:%s"
|
|
|
|
#endif /* !WITHOUT_USERAUTH */
|
|
|
|
"\n",
|
|
|
|
STRORDASH(ctx->srchost_str),
|
|
|
|
STRORDASH(ctx->srcport_str),
|
|
|
|
STRORDASH(ctx->dsthost_str),
|
|
|
|
STRORDASH(ctx->dstport_str),
|
|
|
|
STRORDASH(http_ctx->http_host),
|
|
|
|
STRORDASH(http_ctx->http_method),
|
|
|
|
STRORDASH(http_ctx->http_uri),
|
|
|
|
STRORDASH(http_ctx->http_status_code),
|
|
|
|
STRORDASH(http_ctx->http_content_length),
|
|
|
|
STRORDASH(ctx->sslctx->sni),
|
|
|
|
STRORDASH(ctx->sslctx->ssl_names),
|
|
|
|
SSL_get_version(ctx->src.ssl),
|
|
|
|
SSL_get_cipher(ctx->src.ssl),
|
|
|
|
STRORDASH(ctx->sslctx->srvdst_ssl_version),
|
|
|
|
STRORDASH(ctx->sslctx->srvdst_ssl_cipher),
|
|
|
|
STRORDASH(ctx->sslctx->origcrtfpr),
|
|
|
|
STRORDASH(ctx->sslctx->usedcrtfpr),
|
|
|
|
#ifdef HAVE_LOCAL_PROCINFO
|
|
|
|
lpi,
|
|
|
|
#endif /* HAVE_LOCAL_PROCINFO */
|
|
|
|
http_ctx->ocsp_denied ? " ocsp:denied" : ""
|
|
|
|
#ifndef WITHOUT_USERAUTH
|
|
|
|
, STRORDASH(ctx->user)
|
|
|
|
#endif /* !WITHOUT_USERAUTH */
|
|
|
|
);
|
|
|
|
}
|
|
|
|
if ((rv < 0 ) || !msg) {
|
|
|
|
ctx->enomem = 1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (!ctx->global->detach) {
|
|
|
|
log_err_printf("%s", msg);
|
|
|
|
} else if (ctx->global->statslog) {
|
|
|
|
if (log_conn(msg) == -1) {
|
|
|
|
log_err_level_printf(LOG_WARNING, "Conn logging failed\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (ctx->global->connectlog) {
|
|
|
|
if (log_connect_print_free(msg) == -1) {
|
|
|
|
log_err_level_printf(LOG_WARNING, "Connection logging failed\n");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
free(msg);
|
|
|
|
}
|
|
|
|
out:
|
|
|
|
#ifdef HAVE_LOCAL_PROCINFO
|
|
|
|
if (lpi) {
|
|
|
|
free(lpi);
|
|
|
|
}
|
|
|
|
#endif /* HAVE_LOCAL_PROCINFO */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return 1 if uri is an OCSP GET URI, 0 if not.
|
|
|
|
*/
|
|
|
|
static int NONNULL(1,2)
|
|
|
|
protohttp_ocsp_is_valid_uri(const char *uri, pxy_conn_ctx_t *ctx)
|
|
|
|
{
|
|
|
|
char *buf_url;
|
|
|
|
size_t sz_url;
|
|
|
|
char *buf_b64;
|
|
|
|
size_t sz_b64;
|
|
|
|
unsigned char *buf_asn1;
|
|
|
|
size_t sz_asn1;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
buf_url = strrchr(uri, '/');
|
|
|
|
if (!buf_url)
|
|
|
|
return 0;
|
|
|
|
buf_url++;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Do some quick checks to avoid unnecessary buffer allocations and
|
|
|
|
* decoding URL, Base64 and ASN.1:
|
|
|
|
* - OCSP requests begin with a SEQUENCE (0x30), so the first Base64
|
|
|
|
* byte is 'M' or, unlikely but legal, the URL encoding thereof.
|
|
|
|
* - There should be no query string in OCSP GET requests.
|
|
|
|
* - Encoded OCSP request ASN.1 blobs are longer than 32 bytes.
|
|
|
|
*/
|
|
|
|
if (buf_url[0] != 'M' && buf_url[0] != '%')
|
|
|
|
return 0;
|
|
|
|
if (strchr(uri, '?'))
|
|
|
|
return 0;
|
|
|
|
sz_url = strlen(buf_url);
|
|
|
|
if (sz_url < 32)
|
|
|
|
return 0;
|
|
|
|
buf_b64 = url_dec(buf_url, sz_url, &sz_b64);
|
|
|
|
if (!buf_b64) {
|
|
|
|
ctx->enomem = 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
buf_asn1 = base64_dec(buf_b64, sz_b64, &sz_asn1);
|
|
|
|
if (!buf_asn1) {
|
|
|
|
ctx->enomem = 1;
|
|
|
|
free(buf_b64);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
ret = ssl_is_ocspreq(buf_asn1, sz_asn1);
|
|
|
|
free(buf_asn1);
|
|
|
|
free(buf_b64);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Called after a request header was completely read.
|
|
|
|
* If the request is an OCSP request, deny the request by sending an
|
|
|
|
* OCSP response of type tryLater and close the connection to the server.
|
|
|
|
*
|
|
|
|
* Reference:
|
|
|
|
* RFC 2560: X.509 Internet PKI Online Certificate Status Protocol (OCSP)
|
|
|
|
*/
|
|
|
|
static void NONNULL(1,2)
|
|
|
|
protohttp_ocsp_deny(pxy_conn_ctx_t *ctx, protohttp_ctx_t *http_ctx)
|
|
|
|
{
|
|
|
|
static const char ocspresp[] =
|
|
|
|
"HTTP/1.0 200 OK\r\n"
|
|
|
|
"Content-Type: application/ocsp-response\r\n"
|
|
|
|
"Content-Length: 5\r\n"
|
|
|
|
"Connection: close\r\n"
|
|
|
|
"\r\n"
|
|
|
|
"\x30\x03" /* OCSPResponse: SEQUENCE */
|
|
|
|
"\x0a\x01" /* OCSPResponseStatus: ENUMERATED */
|
|
|
|
"\x03"; /* tryLater (3) */
|
|
|
|
|
|
|
|
if (!http_ctx->http_method)
|
|
|
|
return;
|
|
|
|
if (!strncasecmp(http_ctx->http_method, "GET", 3) &&
|
|
|
|
protohttp_ocsp_is_valid_uri(http_ctx->http_uri, ctx))
|
|
|
|
goto deny;
|
|
|
|
if (!strncasecmp(http_ctx->http_method, "POST", 4) &&
|
|
|
|
http_ctx->http_content_type &&
|
|
|
|
!strncasecmp(http_ctx->http_content_type,
|
|
|
|
"application/ocsp-request", 24))
|
|
|
|
goto deny;
|
|
|
|
return;
|
|
|
|
|
|
|
|
deny:
|
|
|
|
ctx->protoctx->discard_inbufcb(ctx->src.bev);
|
|
|
|
|
|
|
|
// Do not send anything to the child conns
|
|
|
|
ctx->protoctx->discard_outbufcb(ctx->dst.bev);
|
|
|
|
|
|
|
|
// Do not send duplicate OCSP denied responses
|
|
|
|
if (http_ctx->ocsp_denied)
|
|
|
|
return;
|
|
|
|
|
|
|
|
log_finer("Sending OCSP denied response");
|
|
|
|
evbuffer_add_printf(bufferevent_get_output(ctx->src.bev), ocspresp);
|
|
|
|
http_ctx->ocsp_denied = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Filter a single line of HTTP request headers.
|
|
|
|
* Also fills in some context fields for logging.
|
|
|
|
*
|
|
|
|
* Returns NULL if the current line should be deleted from the request.
|
|
|
|
* Returns a newly allocated string if the current line should be replaced.
|
|
|
|
* Returns 'line' if the line should be kept.
|
|
|
|
*/
|
|
|
|
static char * NONNULL(1,2,4)
|
|
|
|
protohttp_filter_request_header_line(const char *line, protohttp_ctx_t *http_ctx, enum conn_type type, pxy_conn_ctx_t *ctx)
|
|
|
|
{
|
|
|
|
/* parse information for connect log */
|
|
|
|
if (!http_ctx->http_method) {
|
|
|
|
/* first line */
|
|
|
|
char *space1, *space2;
|
|
|
|
|
|
|
|
space1 = strchr(line, ' ');
|
|
|
|
space2 = space1 ? strchr(space1 + 1, ' ') : NULL;
|
|
|
|
if (!space1) {
|
|
|
|
/* not HTTP */
|
|
|
|
http_ctx->seen_req_header = 1;
|
|
|
|
http_ctx->not_valid = 1;
|
|
|
|
} else {
|
|
|
|
http_ctx->http_method = malloc(space1 - line + 1);
|
|
|
|
if (http_ctx->http_method) {
|
|
|
|
memcpy(http_ctx->http_method, line, space1 - line);
|
|
|
|
http_ctx->http_method[space1 - line] = '\0';
|
|
|
|
} else {
|
|
|
|
ctx->enomem = 1;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
space1++;
|
|
|
|
if (!space2) {
|
|
|
|
/* HTTP/0.9 */
|
|
|
|
http_ctx->seen_req_header = 1;
|
|
|
|
space2 = space1 + strlen(space1);
|
|
|
|
}
|
|
|
|
http_ctx->http_uri = malloc(space2 - space1 + 1);
|
|
|
|
if (http_ctx->http_uri) {
|
|
|
|
memcpy(http_ctx->http_uri, space1, space2 - space1);
|
|
|
|
http_ctx->http_uri[space2 - space1] = '\0';
|
|
|
|
} else {
|
|
|
|
ctx->enomem = 1;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* not first line */
|
|
|
|
char *newhdr;
|
|
|
|
|
|
|
|
if (!http_ctx->http_host && !strncasecmp(line, "Host:", 5)) {
|
|
|
|
http_ctx->http_host = strdup(util_skipws(line + 5));
|
|
|
|
if (!http_ctx->http_host) {
|
|
|
|
ctx->enomem = 1;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
http_ctx->seen_keyword_count++;
|
|
|
|
} else if (!strncasecmp(line, "Content-Type:", 13)) {
|
|
|
|
http_ctx->http_content_type = strdup(util_skipws(line + 13));
|
|
|
|
if (!http_ctx->http_content_type) {
|
|
|
|
ctx->enomem = 1;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
http_ctx->seen_keyword_count++;
|
|
|
|
/* Override Connection: keepalive and Connection: upgrade */
|
|
|
|
} else if (!strncasecmp(line, "Connection:", 11)) {
|
|
|
|
http_ctx->sent_http_conn_close = 1;
|
|
|
|
if (!(newhdr = strdup("Connection: close"))) {
|
|
|
|
ctx->enomem = 1;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
http_ctx->seen_keyword_count++;
|
|
|
|
return newhdr;
|
|
|
|
// @attention Always use conn ctx for opts, child ctx does not have opts, see the comments in pxy_conn_child_ctx
|
|
|
|
} else if (ctx->conn_opts->remove_http_accept_encoding && !strncasecmp(line, "Accept-Encoding:", 16)) {
|
|
|
|
http_ctx->seen_keyword_count++;
|
|
|
|
return NULL;
|
|
|
|
} else if (ctx->conn_opts->remove_http_referer && !strncasecmp(line, "Referer:", 8)) {
|
|
|
|
http_ctx->seen_keyword_count++;
|
|
|
|
return NULL;
|
|
|
|
/* Suppress upgrading to SSL/TLS, WebSockets or HTTP/2 and keep-alive */
|
|
|
|
} else if (!strncasecmp(line, "Upgrade:", 8) || !strncasecmp(line, "Keep-Alive:", 11)) {
|
|
|
|
http_ctx->seen_keyword_count++;
|
|
|
|
return NULL;
|
|
|
|
} else if ((type == CONN_TYPE_CHILD) && (
|
|
|
|
// @attention flickr keeps redirecting to https with 301 unless we remove the Via line of squid
|
|
|
|
// Apparently flickr assumes the existence of Via header field or squid keyword a sign of plain http, even if we are using https
|
|
|
|
!strncasecmp(line, "Via:", 4) ||
|
|
|
|
// Also do not send the loopback address to the Internet
|
|
|
|
!strncasecmp(line, "X-Forwarded-For:", 16))) {
|
|
|
|
http_ctx->seen_keyword_count++;
|
|
|
|
return NULL;
|
|
|
|
} else if (!strncasecmp(line, SSLPROXY_KEY, SSLPROXY_KEY_LEN)) {
|
|
|
|
// Remove any SSLproxy line, parent or child
|
|
|
|
return NULL;
|
|
|
|
} else if (line[0] == '\0') {
|
|
|
|
http_ctx->seen_req_header = 1;
|
|
|
|
if (!http_ctx->sent_http_conn_close) {
|
|
|
|
newhdr = strdup("Connection: close\r\n");
|
|
|
|
if (!newhdr) {
|
|
|
|
ctx->enomem = 1;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return newhdr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return (char*)line;
|
|
|
|
}
|
|
|
|
|
|
|
|
static filter_action_t * NONNULL(1,2)
|
|
|
|
protohttp_filter_match_host(pxy_conn_ctx_t *ctx, filter_list_t *list)
|
|
|
|
{
|
|
|
|
protohttp_ctx_t *http_ctx = ctx->protoctx->arg;
|
|
|
|
|
|
|
|
filter_site_t *site = filter_site_find(list->host_btree, list->host_acm, list->host_all, http_ctx->http_host);
|
|
|
|
if (!site)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
#ifndef WITHOUT_USERAUTH
|
|
|
|
log_fine_va("Found site (line=%d): %s for %s:%s, %s:%s, %s, %s, %s", site->action.line_num, site->site,
|
|
|
|
STRORDASH(ctx->srchost_str), STRORDASH(ctx->srcport_str), STRORDASH(ctx->dsthost_str), STRORDASH(ctx->dstport_str),
|
|
|
|
STRORDASH(ctx->user), STRORDASH(ctx->desc), STRORDASH(http_ctx->http_host));
|
|
|
|
#else /* WITHOUT_USERAUTH */
|
|
|
|
log_fine_va("Found site (line=%d): %s for %s:%s, %s:%s, %s", site->action.line_num, site->site,
|
|
|
|
STRORDASH(ctx->srchost_str), STRORDASH(ctx->srcport_str), STRORDASH(ctx->dsthost_str), STRORDASH(ctx->dstport_str),
|
|
|
|
STRORDASH(http_ctx->http_host));
|
|
|
|
#endif /* WITHOUT_USERAUTH */
|
|
|
|
|
|
|
|
if (!site->port_btree && !site->port_acm && (site->action.precedence < ctx->filter_precedence)) {
|
|
|
|
log_finest_va("Rule precedence lower than conn filter precedence %d < %d (line=%d): %s, %s", site->action.precedence, ctx->filter_precedence, site->action.line_num, site->site, http_ctx->http_host);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef DEBUG_PROXY
|
|
|
|
if (site->all_sites)
|
|
|
|
log_finest_va("Match all host (line=%d): %s, %s", site->action.line_num, site->site, http_ctx->http_host);
|
|
|
|
else if (site->exact)
|
|
|
|
log_finest_va("Match exact with host (line=%d): %s, %s", site->action.line_num, site->site, http_ctx->http_host);
|
|
|
|
else
|
|
|
|
log_finest_va("Match substring in host (line=%d): %s, %s", site->action.line_num, site->site, http_ctx->http_host);
|
|
|
|
#endif /* DEBUG_PROXY */
|
|
|
|
|
|
|
|
filter_action_t *port_action = pxy_conn_filter_port(ctx, site);
|
|
|
|
if (port_action)
|
|
|
|
return port_action;
|
|
|
|
|
|
|
|
return &site->action;
|
|
|
|
}
|
|
|
|
|
|
|
|
static filter_action_t * NONNULL(1,2)
|
|
|
|
protohttp_filter_match_uri(pxy_conn_ctx_t *ctx, filter_list_t *list)
|
|
|
|
{
|
|
|
|
protohttp_ctx_t *http_ctx = ctx->protoctx->arg;
|
|
|
|
|
|
|
|
filter_site_t *site = filter_site_find(list->uri_btree, list->uri_acm, list->uri_all, http_ctx->http_uri);
|
|
|
|
if (!site)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
#ifndef WITHOUT_USERAUTH
|
|
|
|
log_fine_va("Found site (line=%d): %s for %s:%s, %s:%s, %s, %s, %s", site->action.line_num, site->site,
|
|
|
|
STRORDASH(ctx->srchost_str), STRORDASH(ctx->srcport_str), STRORDASH(ctx->dsthost_str), STRORDASH(ctx->dstport_str),
|
|
|
|
STRORDASH(ctx->user), STRORDASH(ctx->desc), STRORDASH(http_ctx->http_uri));
|
|
|
|
#else /* WITHOUT_USERAUTH */
|
|
|
|
log_fine_va("Found site (line=%d): %s for %s:%s, %s:%s, %s", site->action.line_num, site->site,
|
|
|
|
STRORDASH(ctx->srchost_str), STRORDASH(ctx->srcport_str), STRORDASH(ctx->dsthost_str), STRORDASH(ctx->dstport_str),
|
|
|
|
STRORDASH(http_ctx->http_uri));
|
|
|
|
#endif /* WITHOUT_USERAUTH */
|
|
|
|
|
|
|
|
if (!site->port_btree && !site->port_acm && (site->action.precedence < ctx->filter_precedence)) {
|
|
|
|
log_finest_va("Rule precedence lower than conn filter precedence %d < %d (line=%d): %s, %s", site->action.precedence, ctx->filter_precedence, site->action.line_num, site->site, http_ctx->http_uri);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef DEBUG_PROXY
|
|
|
|
if (site->all_sites)
|
|
|
|
log_finest_va("Match all uri (line=%d): %s, %s", site->action.line_num, site->site, http_ctx->http_uri);
|
|
|
|
else if (site->exact)
|
|
|
|
log_finest_va("Match exact with uri (line=%d): %s, %s", site->action.line_num, site->site, http_ctx->http_uri);
|
|
|
|
else
|
|
|
|
log_finest_va("Match substring in uri (line=%d): %s, %s", site->action.line_num, site->site, http_ctx->http_uri);
|
|
|
|
#endif /* DEBUG_PROXY */
|
|
|
|
|
|
|
|
filter_action_t *port_action = pxy_conn_filter_port(ctx, site);
|
|
|
|
if (port_action)
|
|
|
|
return port_action;
|
|
|
|
|
|
|
|
return &site->action;
|
|
|
|
}
|
|
|
|
|
|
|
|
static filter_action_t * NONNULL(1,2)
|
|
|
|
protohttp_filter(pxy_conn_ctx_t *ctx, filter_list_t *list)
|
|
|
|
{
|
|
|
|
protohttp_ctx_t *http_ctx = ctx->protoctx->arg;
|
|
|
|
|
|
|
|
filter_action_t *action_host = NULL;
|
|
|
|
filter_action_t *action_uri = NULL;
|
|
|
|
|
|
|
|
if (http_ctx->http_host) {
|
|
|
|
if (!(action_host = protohttp_filter_match_host(ctx, list))) {
|
Implement filter actions
- SSL and Dst Host filters can take all of the actions.
- HTTP filter can only take block action, not divert, split, or pass.
Because, we cannot tear a conn down and reconnect its src, after the
processing of HTTP request header is complete, e.g. SSLproxy line has
already been added to its dst buffer. Also, any change in child conns
would affect listening programs too.
- The precedence of filters is as Dst Host > SSL > HTTP.
- The precedence of actions is as Divert > Split > Pass > Block. This is
only for the same type of filter.
- The precedence of match sites is as sni > cn for ssl filter and host >
uri for http filter.
For example, pass action of dst host filter is taken before split action
of ssl filter, due to the precedence order of filters.
For example, pass action of sni site is taken before split action of cn,
due to the precedence order of sites.
We now create src ssl before enabling src to be able to take divert or
split actions of SSL filter. Otherwise, we wouldn't be able to switch
between divert and split while enabling src, only pass or block action
could be taken at that stage.
Also, refactor and clean up.
3 years ago
|
|
|
#ifndef WITHOUT_USERAUTH
|
|
|
|
log_finest_va("No filter match with host: %s:%s, %s:%s, %s, %s, %s, %s",
|
|
|
|
STRORDASH(ctx->srchost_str), STRORDASH(ctx->srcport_str), STRORDASH(ctx->dsthost_str), STRORDASH(ctx->dstport_str),
|
|
|
|
STRORDASH(ctx->user), STRORDASH(ctx->desc), STRORDASH(http_ctx->http_host), STRORDASH(http_ctx->http_uri));
|
Implement filter actions
- SSL and Dst Host filters can take all of the actions.
- HTTP filter can only take block action, not divert, split, or pass.
Because, we cannot tear a conn down and reconnect its src, after the
processing of HTTP request header is complete, e.g. SSLproxy line has
already been added to its dst buffer. Also, any change in child conns
would affect listening programs too.
- The precedence of filters is as Dst Host > SSL > HTTP.
- The precedence of actions is as Divert > Split > Pass > Block. This is
only for the same type of filter.
- The precedence of match sites is as sni > cn for ssl filter and host >
uri for http filter.
For example, pass action of dst host filter is taken before split action
of ssl filter, due to the precedence order of filters.
For example, pass action of sni site is taken before split action of cn,
due to the precedence order of sites.
We now create src ssl before enabling src to be able to take divert or
split actions of SSL filter. Otherwise, we wouldn't be able to switch
between divert and split while enabling src, only pass or block action
could be taken at that stage.
Also, refactor and clean up.
3 years ago
|
|
|
#else /* WITHOUT_USERAUTH */
|
|
|
|
log_finest_va("No filter match with host: %s:%s, %s:%s, %s, %s",
|
|
|
|
STRORDASH(ctx->srchost_str), STRORDASH(ctx->srcport_str), STRORDASH(ctx->dsthost_str), STRORDASH(ctx->dstport_str),
|
|
|
|
STRORDASH(http_ctx->http_host), STRORDASH(http_ctx->http_uri));
|
Implement filter actions
- SSL and Dst Host filters can take all of the actions.
- HTTP filter can only take block action, not divert, split, or pass.
Because, we cannot tear a conn down and reconnect its src, after the
processing of HTTP request header is complete, e.g. SSLproxy line has
already been added to its dst buffer. Also, any change in child conns
would affect listening programs too.
- The precedence of filters is as Dst Host > SSL > HTTP.
- The precedence of actions is as Divert > Split > Pass > Block. This is
only for the same type of filter.
- The precedence of match sites is as sni > cn for ssl filter and host >
uri for http filter.
For example, pass action of dst host filter is taken before split action
of ssl filter, due to the precedence order of filters.
For example, pass action of sni site is taken before split action of cn,
due to the precedence order of sites.
We now create src ssl before enabling src to be able to take divert or
split actions of SSL filter. Otherwise, we wouldn't be able to switch
between divert and split while enabling src, only pass or block action
could be taken at that stage.
Also, refactor and clean up.
3 years ago
|
|
|
#endif /* !WITHOUT_USERAUTH */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (http_ctx->http_uri) {
|
|
|
|
if (!(action_uri = protohttp_filter_match_uri(ctx, list))) {
|
|
|
|
#ifndef WITHOUT_USERAUTH
|
|
|
|
log_finest_va("No filter match with uri: %s:%s, %s:%s, %s, %s, %s, %s",
|
|
|
|
STRORDASH(ctx->srchost_str), STRORDASH(ctx->srcport_str), STRORDASH(ctx->dsthost_str), STRORDASH(ctx->dstport_str),
|
|
|
|
STRORDASH(ctx->user), STRORDASH(ctx->desc), STRORDASH(http_ctx->http_host), STRORDASH(http_ctx->http_uri));
|
|
|
|
#else /* WITHOUT_USERAUTH */
|
|
|
|
log_finest_va("No filter match with uri: %s:%s, %s:%s, %s, %s",
|
|
|
|
STRORDASH(ctx->srchost_str), STRORDASH(ctx->srcport_str), STRORDASH(ctx->dsthost_str), STRORDASH(ctx->dstport_str),
|
|
|
|
STRORDASH(http_ctx->http_host), STRORDASH(http_ctx->http_uri));
|
|
|
|
#endif /* !WITHOUT_USERAUTH */
|
|
|
|
}
|
Implement filter actions
- SSL and Dst Host filters can take all of the actions.
- HTTP filter can only take block action, not divert, split, or pass.
Because, we cannot tear a conn down and reconnect its src, after the
processing of HTTP request header is complete, e.g. SSLproxy line has
already been added to its dst buffer. Also, any change in child conns
would affect listening programs too.
- The precedence of filters is as Dst Host > SSL > HTTP.
- The precedence of actions is as Divert > Split > Pass > Block. This is
only for the same type of filter.
- The precedence of match sites is as sni > cn for ssl filter and host >
uri for http filter.
For example, pass action of dst host filter is taken before split action
of ssl filter, due to the precedence order of filters.
For example, pass action of sni site is taken before split action of cn,
due to the precedence order of sites.
We now create src ssl before enabling src to be able to take divert or
split actions of SSL filter. Otherwise, we wouldn't be able to switch
between divert and split while enabling src, only pass or block action
could be taken at that stage.
Also, refactor and clean up.
3 years ago
|
|
|
}
|
|
|
|
|
|
|
|
if (action_host || action_uri)
|
|
|
|
return pxy_conn_set_filter_action(action_host, action_uri
|
|
|
|
#ifdef DEBUG_PROXY
|
|
|
|
, ctx, http_ctx->http_host, http_ctx->http_uri
|
|
|
|
#endif /* DEBUG_PROXY */
|
|
|
|
);
|
|
|
|
|
|
|
|
return NULL;
|
Implement filter actions
- SSL and Dst Host filters can take all of the actions.
- HTTP filter can only take block action, not divert, split, or pass.
Because, we cannot tear a conn down and reconnect its src, after the
processing of HTTP request header is complete, e.g. SSLproxy line has
already been added to its dst buffer. Also, any change in child conns
would affect listening programs too.
- The precedence of filters is as Dst Host > SSL > HTTP.
- The precedence of actions is as Divert > Split > Pass > Block. This is
only for the same type of filter.
- The precedence of match sites is as sni > cn for ssl filter and host >
uri for http filter.
For example, pass action of dst host filter is taken before split action
of ssl filter, due to the precedence order of filters.
For example, pass action of sni site is taken before split action of cn,
due to the precedence order of sites.
We now create src ssl before enabling src to be able to take divert or
split actions of SSL filter. Otherwise, we wouldn't be able to switch
between divert and split while enabling src, only pass or block action
could be taken at that stage.
Also, refactor and clean up.
3 years ago
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
protohttp_apply_filter(pxy_conn_ctx_t *ctx)
|
|
|
|
{
|
|
|
|
int rv = 0;
|
|
|
|
filter_action_t *a;
|
|
|
|
if ((a = pxy_conn_filter(ctx, protohttp_filter))) {
|
|
|
|
unsigned int action = pxy_conn_translate_filter_action(ctx, a);
|
|
|
|
|
|
|
|
ctx->filter_precedence = action & FILTER_PRECEDENCE;
|
|
|
|
|
|
|
|
if (action & FILTER_ACTION_DIVERT) {
|
|
|
|
if (ctx->divert) {
|
|
|
|
// Override any deferred block action, if already in divert mode (keep divert mode)
|
|
|
|
ctx->deferred_action = FILTER_ACTION_NONE;
|
|
|
|
} else {
|
|
|
|
log_fine("HTTP filter cannot enable divert mode");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (action & FILTER_ACTION_SPLIT) {
|
|
|
|
if (!ctx->divert) {
|
|
|
|
// Override any deferred block action, if already in split mode (keep split mode)
|
|
|
|
ctx->deferred_action = FILTER_ACTION_NONE;
|
|
|
|
} else {
|
|
|
|
log_fine("HTTP filter cannot enable split mode");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (action & FILTER_ACTION_PASS) {
|
|
|
|
log_fine("HTTP filter cannot take pass action");
|
|
|
|
}
|
|
|
|
else if (action & FILTER_ACTION_BLOCK) {
|
|
|
|
ctx->deferred_action = FILTER_ACTION_NONE;
|
Implement filter actions
- SSL and Dst Host filters can take all of the actions.
- HTTP filter can only take block action, not divert, split, or pass.
Because, we cannot tear a conn down and reconnect its src, after the
processing of HTTP request header is complete, e.g. SSLproxy line has
already been added to its dst buffer. Also, any change in child conns
would affect listening programs too.
- The precedence of filters is as Dst Host > SSL > HTTP.
- The precedence of actions is as Divert > Split > Pass > Block. This is
only for the same type of filter.
- The precedence of match sites is as sni > cn for ssl filter and host >
uri for http filter.
For example, pass action of dst host filter is taken before split action
of ssl filter, due to the precedence order of filters.
For example, pass action of sni site is taken before split action of cn,
due to the precedence order of sites.
We now create src ssl before enabling src to be able to take divert or
split actions of SSL filter. Otherwise, we wouldn't be able to switch
between divert and split while enabling src, only pass or block action
could be taken at that stage.
Also, refactor and clean up.
3 years ago
|
|
|
pxy_conn_term(ctx, 1);
|
|
|
|
rv = 1;
|
Implement filter actions
- SSL and Dst Host filters can take all of the actions.
- HTTP filter can only take block action, not divert, split, or pass.
Because, we cannot tear a conn down and reconnect its src, after the
processing of HTTP request header is complete, e.g. SSLproxy line has
already been added to its dst buffer. Also, any change in child conns
would affect listening programs too.
- The precedence of filters is as Dst Host > SSL > HTTP.
- The precedence of actions is as Divert > Split > Pass > Block. This is
only for the same type of filter.
- The precedence of match sites is as sni > cn for ssl filter and host >
uri for http filter.
For example, pass action of dst host filter is taken before split action
of ssl filter, due to the precedence order of filters.
For example, pass action of sni site is taken before split action of cn,
due to the precedence order of sites.
We now create src ssl before enabling src to be able to take divert or
split actions of SSL filter. Otherwise, we wouldn't be able to switch
between divert and split while enabling src, only pass or block action
could be taken at that stage.
Also, refactor and clean up.
3 years ago
|
|
|
}
|
|
|
|
//else { /* FILTER_ACTION_MATCH */ }
|
|
|
|
|
|
|
|
if (action & (FILTER_LOG_CONTENT | FILTER_LOG_PCAP
|
|
|
|
#ifndef WITHOUT_MIRROR
|
|
|
|
| FILTER_LOG_MIRROR
|
|
|
|
#endif /* !WITHOUT_MIRROR */
|
|
|
|
)) {
|
|
|
|
#ifndef WITHOUT_MIRROR
|
|
|
|
log_fine("HTTP filter cannot enable content, pcap, or mirror logging");
|
|
|
|
#else /* !WITHOUT_MIRROR */
|
|
|
|
log_fine("HTTP filter cannot enable content or pcap logging");
|
|
|
|
#endif /* WITHOUT_MIRROR */
|
|
|
|
}
|
|
|
|
|
|
|
|
// Note that connect, master, and cert logs have already been written by now
|
|
|
|
// so enabling or disabling those logs here will not have any effect
|
|
|
|
if (action & FILTER_LOG_CONNECT)
|
|
|
|
ctx->log_connect = 1;
|
|
|
|
else if (action & FILTER_LOG_NOCONNECT)
|
|
|
|
ctx->log_connect = 0;
|
|
|
|
if (action & FILTER_LOG_MASTER)
|
|
|
|
ctx->log_master = 1;
|
|
|
|
else if (action & FILTER_LOG_NOMASTER)
|
|
|
|
ctx->log_master = 0;
|
|
|
|
if (action & FILTER_LOG_CERT)
|
|
|
|
ctx->log_cert = 1;
|
|
|
|
else if (action & FILTER_LOG_NOCERT)
|
|
|
|
ctx->log_cert = 0;
|
|
|
|
|
|
|
|
// content, pcap, and mirror logging can be disabled only
|
|
|
|
// loggers will stop writing further contents
|
|
|
|
if (action & FILTER_LOG_NOCONTENT)
|
|
|
|
ctx->log_content = 0;
|
|
|
|
if (action & FILTER_LOG_NOPCAP)
|
|
|
|
ctx->log_pcap = 0;
|
|
|
|
#ifndef WITHOUT_MIRROR
|
|
|
|
if (action & FILTER_LOG_NOMIRROR)
|
|
|
|
ctx->log_mirror = 0;
|
|
|
|
#endif /* !WITHOUT_MIRROR */
|
|
|
|
|
|
|
|
if (a->conn_opts)
|
|
|
|
ctx->conn_opts = a->conn_opts;
|
Implement filter actions
- SSL and Dst Host filters can take all of the actions.
- HTTP filter can only take block action, not divert, split, or pass.
Because, we cannot tear a conn down and reconnect its src, after the
processing of HTTP request header is complete, e.g. SSLproxy line has
already been added to its dst buffer. Also, any change in child conns
would affect listening programs too.
- The precedence of filters is as Dst Host > SSL > HTTP.
- The precedence of actions is as Divert > Split > Pass > Block. This is
only for the same type of filter.
- The precedence of match sites is as sni > cn for ssl filter and host >
uri for http filter.
For example, pass action of dst host filter is taken before split action
of ssl filter, due to the precedence order of filters.
For example, pass action of sni site is taken before split action of cn,
due to the precedence order of sites.
We now create src ssl before enabling src to be able to take divert or
split actions of SSL filter. Otherwise, we wouldn't be able to switch
between divert and split while enabling src, only pass or block action
could be taken at that stage.
Also, refactor and clean up.
3 years ago
|
|
|
}
|
|
|
|
|
|
|
|
// Cannot defer block action any longer
|
|
|
|
// Match action should not override any deferred action, hence no 'else if'
|
|
|
|
if (pxy_conn_apply_deferred_block_action(ctx))
|
|
|
|
rv = 1;
|
|
|
|
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int WUNRES NONNULL(1,2,3,5)
|
|
|
|
protohttp_filter_request_header(struct evbuffer *inbuf, struct evbuffer *outbuf, protohttp_ctx_t *http_ctx, enum conn_type type, pxy_conn_ctx_t *ctx)
|
|
|
|
{
|
|
|
|
char *line;
|
|
|
|
|
|
|
|
while (!http_ctx->seen_req_header && (line = evbuffer_readln(inbuf, NULL, EVBUFFER_EOL_CRLF))) {
|
|
|
|
log_finest_va("%s", line);
|
|
|
|
|
|
|
|
char *replace = protohttp_filter_request_header_line(line, http_ctx, type, ctx);
|
|
|
|
if (replace == line) {
|
|
|
|
evbuffer_add_printf(outbuf, "%s\r\n", line);
|
|
|
|
} else if (replace) {
|
|
|
|
log_finer_va("REPLACE= %s", replace);
|
|
|
|
evbuffer_add_printf(outbuf, "%s\r\n", replace);
|
|
|
|
free(replace);
|
|
|
|
} else {
|
|
|
|
log_finer_va("REMOVE= %s", line);
|
|
|
|
if (ctx->enomem) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
free(line);
|
|
|
|
|
Implement filter actions
- SSL and Dst Host filters can take all of the actions.
- HTTP filter can only take block action, not divert, split, or pass.
Because, we cannot tear a conn down and reconnect its src, after the
processing of HTTP request header is complete, e.g. SSLproxy line has
already been added to its dst buffer. Also, any change in child conns
would affect listening programs too.
- The precedence of filters is as Dst Host > SSL > HTTP.
- The precedence of actions is as Divert > Split > Pass > Block. This is
only for the same type of filter.
- The precedence of match sites is as sni > cn for ssl filter and host >
uri for http filter.
For example, pass action of dst host filter is taken before split action
of ssl filter, due to the precedence order of filters.
For example, pass action of sni site is taken before split action of cn,
due to the precedence order of sites.
We now create src ssl before enabling src to be able to take divert or
split actions of SSL filter. Otherwise, we wouldn't be able to switch
between divert and split while enabling src, only pass or block action
could be taken at that stage.
Also, refactor and clean up.
3 years ago
|
|
|
if ((type == CONN_TYPE_PARENT) && ctx->divert && !ctx->sent_sslproxy_header) {
|
|
|
|
ctx->sent_sslproxy_header = 1;
|
|
|
|
log_finer_va("INSERT= %s", ctx->sslproxy_header);
|
|
|
|
evbuffer_add_printf(outbuf, "%s\r\n", ctx->sslproxy_header);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (http_ctx->seen_req_header) {
|
|
|
|
if (type == CONN_TYPE_PARENT) {
|
|
|
|
if (protohttp_apply_filter(ctx)) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* request header complete */
|
|
|
|
if (ctx->conn_opts->deny_ocsp) {
|
|
|
|
protohttp_ocsp_deny(ctx, http_ctx);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ctx->enomem) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* no data left after parsing headers? */
|
|
|
|
if (evbuffer_get_length(inbuf) == 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
evbuffer_add_buffer(outbuf, inbuf);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef WITHOUT_USERAUTH
|
|
|
|
static char * NONNULL(1,2)
|
|
|
|
protohttp_get_url(struct evbuffer *inbuf, pxy_conn_ctx_t *ctx)
|
|
|
|
{
|
|
|
|
char *line;
|
|
|
|
char *path = NULL;
|
|
|
|
char *host = NULL;
|
|
|
|
char *url = NULL;
|
|
|
|
|
|
|
|
while ((!host || !path) && (line = evbuffer_readln(inbuf, NULL, EVBUFFER_EOL_CRLF))) {
|
|
|
|
log_finest_va("%s", line);
|
|
|
|
|
|
|
|
//GET / HTTP/1.1
|
|
|
|
if (!path && !strncasecmp(line, "GET ", 4)) {
|
|
|
|
path = strdup(util_skipws(line + 4));
|
|
|
|
if (!path) {
|
|
|
|
ctx->enomem = 1;
|
|
|
|
free(line);
|
|
|
|
goto memout;
|
|
|
|
}
|
|
|
|
path = strsep(&path, " \t");
|
|
|
|
log_finest_va("path=%s", path);
|
|
|
|
//Host: example.com
|
|
|
|
} else if (!host && !strncasecmp(line, "Host:", 5)) {
|
|
|
|
host = strdup(util_skipws(line + 5));
|
|
|
|
if (!host) {
|
|
|
|
ctx->enomem = 1;
|
|
|
|
free(line);
|
|
|
|
goto memout;
|
|
|
|
}
|
|
|
|
log_finest_va("host=%s", host);
|
|
|
|
}
|
|
|
|
free(line);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (host && path) {
|
|
|
|
// Assume that path will always have a leading /, so do not insert an extra / in between host and path
|
|
|
|
// Don't care about computing the exact url size for plain or secure http (http or https)
|
|
|
|
// http s :// example.com + / + NULL
|
|
|
|
// 4 + 1 + 3 + strlen(host) + strlen(path) + 1
|
|
|
|
size_t url_size = strlen(host) + strlen(path) + 9;
|
|
|
|
url = malloc(url_size);
|
|
|
|
if (!url) {
|
|
|
|
ctx->enomem = 1;
|
|
|
|
goto memout;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (snprintf(url, url_size, "http%s://%s%s", ctx->spec->ssl ? "s": "", host, path) < 0) {
|
|
|
|
ctx->enomem = 1;
|
|
|
|
free(url);
|
|
|
|
url = NULL;
|
|
|
|
goto memout;
|
|
|
|
}
|
|
|
|
log_finest_va("url=%s", url);
|
|
|
|
}
|
|
|
|
memout:
|
|
|
|
if (host)
|
|
|
|
free(host);
|
|
|
|
if (path)
|
|
|
|
free(path);
|
|
|
|
return url;
|
|
|
|
}
|
|
|
|
#endif /* !WITHOUT_USERAUTH */
|
|
|
|
|
|
|
|
// Size = 39
|
|
|
|
static char *http_methods[] = { "GET", "PUT", "ICY", "COPY", "HEAD", "LOCK", "MOVE", "POLL", "POST", "BCOPY", "BMOVE", "MKCOL", "TRACE", "LABEL", "MERGE", "DELETE",
|
|
|
|
"SEARCH", "UNLOCK", "REPORT", "UPDATE", "NOTIFY", "BDELETE", "CONNECT", "OPTIONS", "CHECKIN", "PROPFIND", "CHECKOUT", "CCM_POST", "SUBSCRIBE",
|
|
|
|
"PROPPATCH", "BPROPFIND", "BPROPPATCH", "UNCHECKOUT", "MKACTIVITY", "MKWORKSPACE", "UNSUBSCRIBE", "RPC_CONNECT", "VERSION-CONTROL", "BASELINE-CONTROL" };
|
|
|
|
|
|
|
|
static int NONNULL(1)
|
|
|
|
protohttp_validate_method(char *method
|
|
|
|
#ifdef DEBUG_PROXY
|
|
|
|
, pxy_conn_ctx_t *ctx
|
|
|
|
#endif /* DEBUG_PROXY */
|
|
|
|
)
|
|
|
|
{
|
|
|
|
size_t method_len = strlen(method);
|
|
|
|
|
|
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < sizeof(http_methods)/sizeof(char *); i++) {
|
|
|
|
char *m = http_methods[i];
|
|
|
|
if (strlen(m) == method_len && !strncasecmp(method, m, method_len)) {
|
|
|
|
log_finest_va("Passed method validation: %s", method);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
protohttp_validate(pxy_conn_ctx_t *ctx)
|
|
|
|
{
|
|
|
|
protohttp_ctx_t *http_ctx = ctx->protoctx->arg;
|
|
|
|
|
|
|
|
if (http_ctx->not_valid) {
|
|
|
|
log_finest("Not http, validation failed previously");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (http_ctx->http_method) {
|
|
|
|
if (protohttp_validate_method(http_ctx->http_method
|
|
|
|
#ifdef DEBUG_PROXY
|
|
|
|
, ctx
|
|
|
|
#endif /* DEBUG_PROXY */
|
|
|
|
) == -1) {
|
|
|
|
http_ctx->not_valid = 1;
|
|
|
|
log_finest_va("Failed method validation: %s", http_ctx->http_method);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (http_ctx->seen_keyword_count) {
|
|
|
|
// The first line has been processed successfully
|
|
|
|
// Pass validation if we have seen at least one http keyword
|
|
|
|
ctx->protoctx->is_valid = 1;
|
|
|
|
log_finest("Passed validation");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (http_ctx->seen_bytes > ctx->conn_opts->max_http_header_size) {
|
|
|
|
// Fail validation if still cannot pass as http after reaching max header size
|
|
|
|
http_ctx->not_valid = 1;
|
|
|
|
log_finest_va("Reached max header size, size=%llu", http_ctx->seen_bytes);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void NONNULL(1,2)
|
|
|
|
protohttp_bev_readcb_src(struct bufferevent *bev, pxy_conn_ctx_t *ctx)
|
|
|
|
{
|
|
|
|
#ifndef WITHOUT_USERAUTH
|
|
|
|
static const char redirect[] =
|
|
|
|
"HTTP/1.1 302 Found\r\n"
|
|
|
|
"Location: %s\r\n"
|
|
|
|
"\r\n";
|
|
|
|
static const char redirect_url[] =
|
|
|
|
"HTTP/1.1 302 Found\r\n"
|
|
|
|
"Location: %s?SSLproxy=%s\r\n"
|
|
|
|
"\r\n";
|
|
|
|
#endif /* !WITHOUT_USERAUTH */
|
|
|
|
static const char proto_error[] =
|
|
|
|
"HTTP/1.1 400 Bad request\r\n"
|
|
|
|
"Cache-Control: no-cache\r\n"
|
|
|
|
"Connection: close\r\n"
|
|
|
|
"Content-Type: text/html\r\n"
|
|
|
|
"\r\n";
|
|
|
|
|
|
|
|
log_finest_va("ENTER, size=%zu", evbuffer_get_length(bufferevent_get_input(bev)));
|
|
|
|
|
|
|
|
if (ctx->dst.closed) {
|
|
|
|
ctx->protoctx->discard_inbufcb(bev);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
protohttp_ctx_t *http_ctx = ctx->protoctx->arg;
|
|
|
|
struct evbuffer *inbuf = bufferevent_get_input(bev);
|
|
|
|
struct evbuffer *outbuf = bufferevent_get_output(ctx->dst.bev);
|
|
|
|
|
|
|
|
#ifndef WITHOUT_USERAUTH
|
|
|
|
if (ctx->conn_opts->user_auth && !ctx->user) {
|
|
|
|
log_finest("Redirecting conn");
|
|
|
|
char *url = protohttp_get_url(inbuf, ctx);
|
|
|
|
ctx->protoctx->discard_inbufcb(bev);
|
|
|
|
if (url) {
|
|
|
|
evbuffer_add_printf(bufferevent_get_output(bev), redirect_url, ctx->conn_opts->user_auth_url, url);
|
|
|
|
free(url);
|
|
|
|
} else {
|
|
|
|
evbuffer_add_printf(bufferevent_get_output(bev), redirect, ctx->conn_opts->user_auth_url);
|
|
|
|
}
|
|
|
|
ctx->sent_userauth_msg = 1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#endif /* !WITHOUT_USERAUTH */
|
|
|
|
|
|
|
|
if (ctx->conn_opts->validate_proto && !ctx->protoctx->is_valid) {
|
|
|
|
http_ctx->seen_bytes += evbuffer_get_length(inbuf);
|
|
|
|
}
|
|
|
|
|
|
|
|
// We insert our special header line to the first packet we get, e.g. right after the first \r\n in the case of http
|
|
|
|
// @todo Should we look for GET/POST or Host header lines to detect the first packet?
|
|
|
|
// But there is no guarantee that they will exist, due to fragmentation.
|
|
|
|
// @attention We cannot append the ssl proxy address at the end of the packet or in between the header and the content,
|
|
|
|
// because (1) the packet may be just the first fragment split somewhere not appropriate for appending a header,
|
|
|
|
// and (2) there may not be any content.
|
|
|
|
// And we are dealing with pop3 and smtp also, not just http.
|
|
|
|
|
|
|
|
/* request header munging */
|
|
|
|
if (!http_ctx->seen_req_header) {
|
|
|
|
log_finest_va("HTTP Request Header, size=%zu", evbuffer_get_length(inbuf));
|
|
|
|
if (protohttp_filter_request_header(inbuf, outbuf, http_ctx, ctx->type, ctx) == -1) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
log_finest_va("HTTP Request Body, size=%zu", evbuffer_get_length(inbuf));
|
|
|
|
evbuffer_add_buffer(outbuf, inbuf);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ctx->conn_opts->validate_proto && !ctx->protoctx->is_valid) {
|
|
|
|
if (protohttp_validate(ctx) == -1) {
|
|
|
|
evbuffer_add(bufferevent_get_output(bev), proto_error, strlen(proto_error));
|
|
|
|
ctx->sent_protoerror_msg = 1;
|
|
|
|
ctx->protoctx->discard_inbufcb(bev);
|
|
|
|
ctx->protoctx->discard_outbufcb(ctx->dst.bev);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx->protoctx->set_watermarkcb(bev, ctx, ctx->dst.bev);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Filter a single line of HTTP response headers.
|
|
|
|
*
|
|
|
|
* Returns NULL if the current line should be deleted from the response.
|
|
|
|
* Returns a newly allocated string if the current line should be replaced.
|
|
|
|
* Returns `line' if the line should be kept.
|
|
|
|
*/
|
|
|
|
static char * NONNULL(1,2,3)
|
|
|
|
protohttp_filter_response_header_line(const char *line, protohttp_ctx_t *http_ctx, pxy_conn_ctx_t *ctx)
|
|
|
|
{
|
|
|
|
/* parse information for connect log */
|
|
|
|
if (!http_ctx->http_status_code) {
|
|
|
|
/* first line */
|
|
|
|
char *space1, *space2;
|
|
|
|
|
|
|
|
space1 = strchr(line, ' ');
|
|
|
|
space2 = space1 ? strchr(space1 + 1, ' ') : NULL;
|
|
|
|
if (!space1 || !!strncmp(line, "HTTP", 4)) {
|
|
|
|
/* not HTTP or HTTP/0.9 */
|
|
|
|
http_ctx->seen_resp_header = 1;
|
|
|
|
} else {
|
|
|
|
size_t len_code, len_text;
|
|
|
|
|
|
|
|
if (space2) {
|
|
|
|
len_code = space2 - space1 - 1;
|
|
|
|
len_text = strlen(space2 + 1);
|
|
|
|
} else {
|
|
|
|
len_code = strlen(space1 + 1);
|
|
|
|
len_text = 0;
|
|
|
|
}
|
|
|
|
http_ctx->http_status_code = malloc(len_code + 1);
|
|
|
|
http_ctx->http_status_text = malloc(len_text + 1);
|
|
|
|
if (!http_ctx->http_status_code || !http_ctx->http_status_text) {
|
|
|
|
ctx->enomem = 1;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
memcpy(http_ctx->http_status_code, space1 + 1, len_code);
|
|
|
|
http_ctx->http_status_code[len_code] = '\0';
|
|
|
|
if (space2) {
|
|
|
|
memcpy(http_ctx->http_status_text,
|
|
|
|
space2 + 1, len_text);
|
|
|
|
}
|
|
|
|
http_ctx->http_status_text[len_text] = '\0';
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* not first line */
|
|
|
|
if (!http_ctx->http_content_length &&
|
|
|
|
!strncasecmp(line, "Content-Length:", 15)) {
|
|
|
|
http_ctx->http_content_length =
|
|
|
|
strdup(util_skipws(line + 15));
|
|
|
|
if (!http_ctx->http_content_length) {
|
|
|
|
ctx->enomem = 1;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
} else if (
|
|
|
|
/* HPKP: Public Key Pinning Extension for HTTP
|
|
|
|
* (draft-ietf-websec-key-pinning)
|
|
|
|
* remove to prevent public key pinning */
|
|
|
|
!strncasecmp(line, "Public-Key-Pins:", 16) ||
|
|
|
|
!strncasecmp(line, "Public-Key-Pins-Report-Only:", 28) ||
|
|
|
|
/* HSTS: HTTP Strict Transport Security (RFC 6797)
|
|
|
|
* remove to allow users to accept bad certs */
|
|
|
|
!strncasecmp(line, "Strict-Transport-Security:", 26) ||
|
|
|
|
/* Expect-CT: Expect Certificate Transparency
|
|
|
|
* (draft-ietf-httpbis-expect-ct-latest)
|
|
|
|
* remove to prevent failed CT log lookups */
|
|
|
|
!strncasecmp(line, "Expect-CT:", 10) ||
|
|
|
|
/* Alternate Protocol
|
|
|
|
* remove to prevent switching to QUIC, SPDY et al */
|
|
|
|
!strncasecmp(line, "Alternate-Protocol:", 19) ||
|
|
|
|
/* Upgrade header
|
|
|
|
* remove to prevent upgrading to HTTPS in unhandled ways,
|
|
|
|
* and more importantly, WebSockets and HTTP/2 */
|
|
|
|
!strncasecmp(line, "Upgrade:", 8)) {
|
|
|
|
return NULL;
|
|
|
|
} else if (line[0] == '\0') {
|
|
|
|
http_ctx->seen_resp_header = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return (char*)line;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void NONNULL(1,2,3,4)
|
|
|
|
protohttp_filter_response_header(struct evbuffer *inbuf, struct evbuffer *outbuf, protohttp_ctx_t *http_ctx, pxy_conn_ctx_t *ctx)
|
|
|
|
{
|
|
|
|
char *line;
|
|
|
|
|
|
|
|
while (!http_ctx->seen_resp_header && (line = evbuffer_readln(inbuf, NULL, EVBUFFER_EOL_CRLF))) {
|
|
|
|
log_finest_va("%s", line);
|
|
|
|
|
|
|
|
char *replace = protohttp_filter_response_header_line(line, http_ctx, ctx);
|
|
|
|
if (replace == line) {
|
|
|
|
evbuffer_add_printf(outbuf, "%s\r\n", line);
|
|
|
|
} else if (replace) {
|
|
|
|
log_finer_va("REPLACE= %s", replace);
|
|
|
|
evbuffer_add_printf(outbuf, "%s\r\n", replace);
|
|
|
|
free(replace);
|
|
|
|
} else {
|
|
|
|
log_finer_va("REMOVE= %s", line);
|
|
|
|
if (ctx->enomem) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
free(line);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (http_ctx->seen_resp_header) {
|
|
|
|
/* no data left after parsing headers? */
|
|
|
|
if (evbuffer_get_length(inbuf) == 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
evbuffer_add_buffer(outbuf, inbuf);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void NONNULL(1)
|
|
|
|
protohttp_bev_readcb_dst(struct bufferevent *bev, pxy_conn_ctx_t *ctx)
|
|
|
|
{
|
|
|
|
log_finest_va("ENTER, size=%zu", evbuffer_get_length(bufferevent_get_input(bev)));
|
|
|
|
|
|
|
|
if (ctx->src.closed) {
|
|
|
|
ctx->protoctx->discard_inbufcb(bev);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
protohttp_ctx_t *http_ctx = ctx->protoctx->arg;
|
|
|
|
struct evbuffer *inbuf = bufferevent_get_input(bev);
|
|
|
|
struct evbuffer *outbuf = bufferevent_get_output(ctx->src.bev);
|
|
|
|
|
|
|
|
if (!http_ctx->seen_resp_header) {
|
|
|
|
log_finest_va("HTTP Response Header, size=%zu", evbuffer_get_length(inbuf));
|
|
|
|
protohttp_filter_response_header(inbuf, outbuf, http_ctx, ctx);
|
|
|
|
if (ctx->enomem) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
log_finest_va("HTTP Response Body, size=%zu", evbuffer_get_length(inbuf));
|
|
|
|
evbuffer_add_buffer(outbuf, inbuf);
|
|
|
|
}
|
|
|
|
ctx->protoctx->set_watermarkcb(bev, ctx, ctx->src.bev);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void NONNULL(1)
|
|
|
|
protohttp_bev_readcb_srvdst(UNUSED struct bufferevent *bev, UNUSED pxy_conn_ctx_t *ctx)
|
|
|
|
{
|
|
|
|
log_err_level(LOG_ERR, "readcb called on srvdst");
|
|
|
|
}
|
|
|
|
|
|
|
|
static void NONNULL(1)
|
|
|
|
protohttp_bev_readcb_src_child(struct bufferevent *bev, pxy_conn_child_ctx_t *ctx)
|
|
|
|
{
|
|
|
|
log_finest_va("ENTER, size=%zu", evbuffer_get_length(bufferevent_get_input(bev)));
|
|
|
|
|
|
|
|
if (ctx->dst.closed) {
|
|
|
|
ctx->conn->protoctx->discard_inbufcb(bev);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
protohttp_ctx_t *http_ctx = ctx->protoctx->arg;
|
|
|
|
struct evbuffer *inbuf = bufferevent_get_input(bev);
|
|
|
|
struct evbuffer *outbuf = bufferevent_get_output(ctx->dst.bev);
|
|
|
|
|
|
|
|
if (!http_ctx->seen_req_header) {
|
|
|
|
log_finest_va("HTTP Request Header, size=%zu", evbuffer_get_length(inbuf));
|
|
|
|
// @todo Just remove SSLproxy line, do not filter request on the server side?
|
|
|
|
if (protohttp_filter_request_header(inbuf, outbuf, http_ctx, ctx->type, ctx->conn) == -1) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
log_finest_va("HTTP Request Body, size=%zu", evbuffer_get_length(inbuf));
|
|
|
|
evbuffer_add_buffer(outbuf, inbuf);
|
|
|
|
}
|
|
|
|
ctx->conn->protoctx->set_watermarkcb(bev, ctx->conn, ctx->dst.bev);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void NONNULL(1)
|
|
|
|
protohttp_bev_readcb_dst_child(struct bufferevent *bev, pxy_conn_child_ctx_t *ctx)
|
|
|
|
{
|
|
|
|
log_finest_va("ENTER, size=%zu", evbuffer_get_length(bufferevent_get_input(bev)));
|
|
|
|
|
|
|
|
if (ctx->src.closed) {
|
|
|
|
ctx->conn->protoctx->discard_inbufcb(bev);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
protohttp_ctx_t *http_ctx = ctx->protoctx->arg;
|
|
|
|
struct evbuffer *inbuf = bufferevent_get_input(bev);
|
|
|
|
struct evbuffer *outbuf = bufferevent_get_output(ctx->src.bev);
|
|
|
|
|
|
|
|
if (!http_ctx->seen_resp_header) {
|
|
|
|
log_finest_va("HTTP Response Header, size=%zu", evbuffer_get_length(inbuf));
|
|
|
|
// @todo Do not filter response on the server side?
|
|
|
|
protohttp_filter_response_header(inbuf, outbuf, http_ctx, ctx->conn);
|
|
|
|
if (ctx->conn->enomem) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
log_finest_va("HTTP Response Body, size=%zu", evbuffer_get_length(inbuf));
|
|
|
|
evbuffer_add_buffer(outbuf, inbuf);
|
|
|
|
}
|
|
|
|
ctx->conn->protoctx->set_watermarkcb(bev, ctx->conn, ctx->src.bev);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void NONNULL(1)
|
|
|
|
protohttp_bev_readcb(struct bufferevent *bev, void *arg)
|
|
|
|
{
|
|
|
|
pxy_conn_ctx_t *ctx = arg;
|
|
|
|
protohttp_ctx_t *http_ctx = ctx->protoctx->arg;
|
|
|
|
|
|
|
|
int seen_resp_header_on_entry = http_ctx->seen_resp_header;
|
|
|
|
|
|
|
|
if (bev == ctx->src.bev) {
|
|
|
|
protohttp_bev_readcb_src(bev, ctx);
|
|
|
|
} else if (bev == ctx->dst.bev) {
|
|
|
|
protohttp_bev_readcb_dst(bev, ctx);
|
|
|
|
} else if (bev == ctx->srvdst.bev) {
|
|
|
|
protohttp_bev_readcb_srvdst(bev, ctx);
|
|
|
|
} else {
|
|
|
|
log_err_printf("protohttp_bev_readcb: UNKWN conn end\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ctx->enomem) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!seen_resp_header_on_entry && http_ctx->seen_resp_header) {
|
|
|
|
/* response header complete: log connection */
|
|
|
|
if (WANT_CONNECT_LOG(ctx->conn)) {
|
|
|
|
protohttp_log_connect(ctx);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void NONNULL(1)
|
|
|
|
protohttp_bev_readcb_child(struct bufferevent *bev, void *arg)
|
|
|
|
{
|
|
|
|
pxy_conn_child_ctx_t *ctx = arg;
|
|
|
|
|
|
|
|
if (bev == ctx->src.bev) {
|
|
|
|
protohttp_bev_readcb_src_child(bev, ctx);
|
|
|
|
} else if (bev == ctx->dst.bev) {
|
|
|
|
protohttp_bev_readcb_dst_child(bev, ctx);
|
|
|
|
} else {
|
|
|
|
log_err_printf("protohttp_bev_readcb_child: UNKWN conn end\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void NONNULL(1)
|
|
|
|
protohttp_bev_writecb_src(struct bufferevent *bev, pxy_conn_ctx_t *ctx)
|
|
|
|
{
|
|
|
|
log_finest("ENTER");
|
|
|
|
|
|
|
|
#ifndef WITHOUT_USERAUTH
|
|
|
|
if (prototcp_try_close_unauth_conn(bev, ctx)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#endif /* !WITHOUT_USERAUTH */
|
|
|
|
|
|
|
|
if (prototcp_try_close_protoerror_conn(bev, ctx)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
protohttp_ctx_t *http_ctx = ctx->protoctx->arg;
|
|
|
|
if (ctx->dst.closed || http_ctx->ocsp_denied) {
|
|
|
|
if (pxy_try_close_conn_end(&ctx->src, ctx) == 1) {
|
|
|
|
log_finest("dst.closed or ocsp_denied, terminate conn");
|
|
|
|
pxy_conn_term(ctx, 1);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
ctx->protoctx->unset_watermarkcb(bev, ctx, &ctx->dst);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void NONNULL(1)
|
|
|
|
protohttp_bev_writecb(struct bufferevent *bev, void *arg)
|
|
|
|
{
|
|
|
|
pxy_conn_ctx_t *ctx = arg;
|
|
|
|
|
|
|
|
if (bev == ctx->src.bev) {
|
|
|
|
protohttp_bev_writecb_src(bev, ctx);
|
|
|
|
} else if (bev == ctx->dst.bev) {
|
|
|
|
prototcp_bev_writecb_dst(bev, ctx);
|
|
|
|
} else {
|
|
|
|
log_err_printf("protohttp_bev_writecb: UNKWN conn end\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void NONNULL(1)
|
|
|
|
protohttp_free_ctx(protohttp_ctx_t *http_ctx)
|
|
|
|
{
|
|
|
|
if (http_ctx->http_method) {
|
|
|
|
free(http_ctx->http_method);
|
|
|
|
}
|
|
|
|
if (http_ctx->http_uri) {
|
|
|
|
free(http_ctx->http_uri);
|
|
|
|
}
|
|
|
|
if (http_ctx->http_host) {
|
|
|
|
free(http_ctx->http_host);
|
|
|
|
}
|
|
|
|
if (http_ctx->http_content_type) {
|
|
|
|
free(http_ctx->http_content_type);
|
|
|
|
}
|
|
|
|
if (http_ctx->http_status_code) {
|
|
|
|
free(http_ctx->http_status_code);
|
|
|
|
}
|
|
|
|
if (http_ctx->http_status_text) {
|
|
|
|
free(http_ctx->http_status_text);
|
|
|
|
}
|
|
|
|
if (http_ctx->http_content_length) {
|
|
|
|
free(http_ctx->http_content_length);
|
|
|
|
}
|
|
|
|
free(http_ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void NONNULL(1)
|
|
|
|
protohttp_free(pxy_conn_ctx_t *ctx)
|
|
|
|
{
|
|
|
|
protohttp_ctx_t *http_ctx = ctx->protoctx->arg;
|
|
|
|
protohttp_free_ctx(http_ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void NONNULL(1)
|
|
|
|
protohttps_free(pxy_conn_ctx_t *ctx)
|
|
|
|
{
|
|
|
|
protohttp_free(ctx);
|
|
|
|
protossl_free(ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void NONNULL(1)
|
|
|
|
protohttp_free_child(pxy_conn_child_ctx_t *ctx)
|
|
|
|
{
|
|
|
|
protohttp_ctx_t *http_ctx = ctx->protoctx->arg;
|
|
|
|
protohttp_free_ctx(http_ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
// @attention Called by thrmgr thread
|
|
|
|
protocol_t
|
|
|
|
protohttp_setup(pxy_conn_ctx_t *ctx)
|
|
|
|
{
|
|
|
|
ctx->protoctx->proto = PROTO_HTTP;
|
|
|
|
|
|
|
|
ctx->protoctx->bev_readcb = protohttp_bev_readcb;
|
|
|
|
ctx->protoctx->bev_writecb = protohttp_bev_writecb;
|
|
|
|
ctx->protoctx->proto_free = protohttp_free;
|
|
|
|
|
|
|
|
ctx->protoctx->arg = malloc(sizeof(protohttp_ctx_t));
|
|
|
|
if (!ctx->protoctx->arg) {
|
|
|
|
return PROTO_ERROR;
|
|
|
|
}
|
|
|
|
memset(ctx->protoctx->arg, 0, sizeof(protohttp_ctx_t));
|
|
|
|
|
|
|
|
return PROTO_HTTP;
|
|
|
|
}
|
|
|
|
|
|
|
|
// @attention Called by thrmgr thread
|
|
|
|
protocol_t
|
|
|
|
protohttps_setup(pxy_conn_ctx_t *ctx)
|
|
|
|
{
|
|
|
|
ctx->protoctx->proto = PROTO_HTTPS;
|
|
|
|
ctx->protoctx->connectcb = protossl_conn_connect;
|
|
|
|
ctx->protoctx->init_conn = protossl_init_conn;
|
|
|
|
|
|
|
|
ctx->protoctx->bev_readcb = protohttp_bev_readcb;
|
|
|
|
ctx->protoctx->bev_writecb = protohttp_bev_writecb;
|
|
|
|
ctx->protoctx->bev_eventcb = protossl_bev_eventcb;
|
|
|
|
|
|
|
|
ctx->protoctx->proto_free = protohttps_free;
|
|
|
|
|
|
|
|
ctx->protoctx->arg = malloc(sizeof(protohttp_ctx_t));
|
|
|
|
if (!ctx->protoctx->arg) {
|
|
|
|
return PROTO_ERROR;
|
|
|
|
}
|
|
|
|
memset(ctx->protoctx->arg, 0, sizeof(protohttp_ctx_t));
|
|
|
|
|
|
|
|
ctx->sslctx = malloc(sizeof(ssl_ctx_t));
|
|
|
|
if (!ctx->sslctx) {
|
|
|
|
free(ctx->protoctx->arg);
|
|
|
|
return PROTO_ERROR;
|
|
|
|
}
|
|
|
|
memset(ctx->sslctx, 0, sizeof(ssl_ctx_t));
|
|
|
|
|
|
|
|
return PROTO_HTTPS;
|
|
|
|
}
|
|
|
|
|
|
|
|
protocol_t
|
|
|
|
protohttp_setup_child(pxy_conn_child_ctx_t *ctx)
|
|
|
|
{
|
|
|
|
ctx->protoctx->proto = PROTO_HTTP;
|
|
|
|
|
|
|
|
// @todo Should HTTP child conns do any http related processing, so use tcp defaults instead?
|
|
|
|
ctx->protoctx->bev_readcb = protohttp_bev_readcb_child;
|
|
|
|
ctx->protoctx->proto_free = protohttp_free_child;
|
|
|
|
|
|
|
|
ctx->protoctx->arg = malloc(sizeof(protohttp_ctx_t));
|
|
|
|
if (!ctx->protoctx->arg) {
|
|
|
|
return PROTO_ERROR;
|
|
|
|
}
|
|
|
|
memset(ctx->protoctx->arg, 0, sizeof(protohttp_ctx_t));
|
|
|
|
|
|
|
|
return PROTO_HTTP;
|
|
|
|
}
|
|
|
|
|
|
|
|
protocol_t
|
|
|
|
protohttps_setup_child(pxy_conn_child_ctx_t *ctx)
|
|
|
|
{
|
|
|
|
ctx->protoctx->proto = PROTO_HTTPS;
|
|
|
|
ctx->protoctx->connectcb = protossl_connect_child;
|
|
|
|
|
|
|
|
ctx->protoctx->bev_readcb = protohttp_bev_readcb_child;
|
|
|
|
ctx->protoctx->bev_eventcb = protossl_bev_eventcb_child;
|
|
|
|
|
|
|
|
ctx->protoctx->proto_free = protohttp_free_child;
|
|
|
|
|
|
|
|
ctx->protoctx->arg = malloc(sizeof(protohttp_ctx_t));
|
|
|
|
if (!ctx->protoctx->arg) {
|
|
|
|
return PROTO_ERROR;
|
|
|
|
}
|
|
|
|
memset(ctx->protoctx->arg, 0, sizeof(protohttp_ctx_t));
|
|
|
|
|
|
|
|
return PROTO_HTTPS;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* vim: set noet ft=c: */
|