1 /*-
2  * SSLsplit - transparent SSL/TLS interception
3  * https://www.roe.ch/SSLsplit
4  *
5  * Copyright (c) 2009-2019, Daniel Roethlisberger <daniel@roe.ch>.
6  * Copyright (c) 2017-2021, Soner Tari <sonertari@gmail.com>.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions are met:
11  * 1. Redistributions of source code must retain the above copyright notice,
12  *    this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright notice,
14  *    this list of conditions and the following disclaimer in the documentation
15  *    and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS''
18  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28  */
29 
30 #include "protohttp.h"
31 #include "prototcp.h"
32 #include "protossl.h"
33 #include "protopassthrough.h"
34 
35 #include "util.h"
36 #include "base64.h"
37 #include "url.h"
38 
39 #include <string.h>
40 #include <event2/bufferevent.h>
41 
42 static void NONNULL(1)
protohttp_log_connect(pxy_conn_ctx_t * ctx)43 protohttp_log_connect(pxy_conn_ctx_t *ctx)
44 {
45 	if (!ctx->log_connect)
46 		return;
47 
48 	protohttp_ctx_t *http_ctx = ctx->protoctx->arg;
49 
50 	char *msg;
51 #ifdef HAVE_LOCAL_PROCINFO
52 	char *lpi = NULL;
53 #endif /* HAVE_LOCAL_PROCINFO */
54 	int rv;
55 
56 #ifdef HAVE_LOCAL_PROCINFO
57 	if (ctx->global->lprocinfo) {
58 		rv = asprintf(&lpi, "lproc:%i:%s:%s:%s",
59 		              ctx->lproc.pid,
60 		              STRORDASH(ctx->lproc.user),
61 		              STRORDASH(ctx->lproc.group),
62 		              STRORDASH(ctx->lproc.exec_path));
63 		if ((rv < 0) || !lpi) {
64 			ctx->enomem = 1;
65 			goto out;
66 		}
67 	}
68 #endif /* HAVE_LOCAL_PROCINFO */
69 
70 	/*
71 	 * The following ifdef's within asprintf arguments list generates
72 	 * warnings with -Wembedded-directive on some compilers.
73 	 * Not fixing the code in order to avoid more code duplication.
74 	 */
75 
76 	if (!ctx->spec->ssl) {
77 		rv = asprintf(&msg, "CONN: http %s %s %s %s %s %s %s %s %s"
78 #ifdef HAVE_LOCAL_PROCINFO
79 		              " %s"
80 #endif /* HAVE_LOCAL_PROCINFO */
81 		              "%s"
82 #ifndef WITHOUT_USERAUTH
83 		              " user:%s"
84 #endif /* !WITHOUT_USERAUTH */
85 		              "\n",
86 		              STRORDASH(ctx->srchost_str),
87 		              STRORDASH(ctx->srcport_str),
88 		              STRORDASH(ctx->dsthost_str),
89 		              STRORDASH(ctx->dstport_str),
90 		              STRORDASH(http_ctx->http_host),
91 		              STRORDASH(http_ctx->http_method),
92 		              STRORDASH(http_ctx->http_uri),
93 		              STRORDASH(http_ctx->http_status_code),
94 		              STRORDASH(http_ctx->http_content_length),
95 #ifdef HAVE_LOCAL_PROCINFO
96 		              lpi,
97 #endif /* HAVE_LOCAL_PROCINFO */
98 		              http_ctx->ocsp_denied ? " ocsp:denied" : ""
99 #ifndef WITHOUT_USERAUTH
100 		              , STRORDASH(ctx->user)
101 #endif /* !WITHOUT_USERAUTH */
102 		              );
103 	} else {
104 		rv = asprintf(&msg, "CONN: https %s %s %s %s %s %s %s %s %s "
105 		              "sni:%s names:%s "
106 		              "sproto:%s:%s dproto:%s:%s "
107 		              "origcrt:%s usedcrt:%s"
108 #ifdef HAVE_LOCAL_PROCINFO
109 		              " %s"
110 #endif /* HAVE_LOCAL_PROCINFO */
111 		              "%s"
112 #ifndef WITHOUT_USERAUTH
113 		              " user:%s"
114 #endif /* !WITHOUT_USERAUTH */
115 		              "\n",
116 		              STRORDASH(ctx->srchost_str),
117 		              STRORDASH(ctx->srcport_str),
118 		              STRORDASH(ctx->dsthost_str),
119 		              STRORDASH(ctx->dstport_str),
120 		              STRORDASH(http_ctx->http_host),
121 		              STRORDASH(http_ctx->http_method),
122 		              STRORDASH(http_ctx->http_uri),
123 		              STRORDASH(http_ctx->http_status_code),
124 		              STRORDASH(http_ctx->http_content_length),
125 		              STRORDASH(ctx->sslctx->sni),
126 		              STRORDASH(ctx->sslctx->ssl_names),
127 		              SSL_get_version(ctx->src.ssl),
128 		              SSL_get_cipher(ctx->src.ssl),
129 		              STRORDASH(ctx->sslctx->srvdst_ssl_version),
130 		              STRORDASH(ctx->sslctx->srvdst_ssl_cipher),
131 		              STRORDASH(ctx->sslctx->origcrtfpr),
132 		              STRORDASH(ctx->sslctx->usedcrtfpr),
133 #ifdef HAVE_LOCAL_PROCINFO
134 		              lpi,
135 #endif /* HAVE_LOCAL_PROCINFO */
136 		              http_ctx->ocsp_denied ? " ocsp:denied" : ""
137 #ifndef WITHOUT_USERAUTH
138 		              , STRORDASH(ctx->user)
139 #endif /* !WITHOUT_USERAUTH */
140 		              );
141 	}
142 	if ((rv < 0 ) || !msg) {
143 		ctx->enomem = 1;
144 		goto out;
145 	}
146 	if (!ctx->global->detach) {
147 		log_err_printf("%s", msg);
148 	} else if (ctx->global->statslog) {
149 		if (log_conn(msg) == -1) {
150 			log_err_level_printf(LOG_WARNING, "Conn logging failed\n");
151 		}
152 	}
153 	if (ctx->global->connectlog) {
154 		if (log_connect_print_free(msg) == -1) {
155 			free(msg);
156 			log_err_level_printf(LOG_WARNING, "Connection logging failed\n");
157 		}
158 	} else {
159 		free(msg);
160 	}
161 out:
162 #ifdef HAVE_LOCAL_PROCINFO
163 	if (lpi) {
164 		free(lpi);
165 	}
166 #endif /* HAVE_LOCAL_PROCINFO */
167 	return;
168 }
169 
170 /*
171  * Return 1 if uri is an OCSP GET URI, 0 if not.
172  */
173 static int NONNULL(1,2)
protohttp_ocsp_is_valid_uri(const char * uri,pxy_conn_ctx_t * ctx)174 protohttp_ocsp_is_valid_uri(const char *uri, pxy_conn_ctx_t *ctx)
175 {
176 	char *buf_url;
177 	size_t sz_url;
178 	char *buf_b64;
179 	size_t sz_b64;
180 	unsigned char *buf_asn1;
181 	size_t sz_asn1;
182 	int ret;
183 
184 	buf_url = strrchr(uri, '/');
185 	if (!buf_url)
186 		return 0;
187 	buf_url++;
188 
189 	/*
190 	 * Do some quick checks to avoid unnecessary buffer allocations and
191 	 * decoding URL, Base64 and ASN.1:
192 	 * -   OCSP requests begin with a SEQUENCE (0x30), so the first Base64
193 	 *     byte is 'M' or, unlikely but legal, the URL encoding thereof.
194 	 * -   There should be no query string in OCSP GET requests.
195 	 * -   Encoded OCSP request ASN.1 blobs are longer than 32 bytes.
196 	 */
197 	if (buf_url[0] != 'M' && buf_url[0] != '%')
198 		return 0;
199 	if (strchr(uri, '?'))
200 		return 0;
201 	sz_url = strlen(buf_url);
202 	if (sz_url < 32)
203 		return 0;
204 	buf_b64 = url_dec(buf_url, sz_url, &sz_b64);
205 	if (!buf_b64) {
206 		ctx->enomem = 1;
207 		return 0;
208 	}
209 	buf_asn1 = base64_dec(buf_b64, sz_b64, &sz_asn1);
210 	if (!buf_asn1) {
211 		ctx->enomem = 1;
212 		free(buf_b64);
213 		return 0;
214 	}
215 	ret = ssl_is_ocspreq(buf_asn1, sz_asn1);
216 	free(buf_asn1);
217 	free(buf_b64);
218 	return ret;
219 }
220 
221 /*
222  * Called after a request header was completely read.
223  * If the request is an OCSP request, deny the request by sending an
224  * OCSP response of type tryLater and close the connection to the server.
225  *
226  * Reference:
227  * RFC 2560: X.509 Internet PKI Online Certificate Status Protocol (OCSP)
228  */
229 static void NONNULL(1,2)
protohttp_ocsp_deny(pxy_conn_ctx_t * ctx,protohttp_ctx_t * http_ctx)230 protohttp_ocsp_deny(pxy_conn_ctx_t *ctx, protohttp_ctx_t *http_ctx)
231 {
232 	struct evbuffer *inbuf, *outbuf;
233 	static const char ocspresp[] =
234 		"HTTP/1.0 200 OK\r\n"
235 		"Content-Type: application/ocsp-response\r\n"
236 		"Content-Length: 5\r\n"
237 		"Connection: close\r\n"
238 		"\r\n"
239 		"\x30\x03"      /* OCSPResponse: SEQUENCE */
240 		"\x0a\x01"      /* OCSPResponseStatus: ENUMERATED */
241 		"\x03";         /* tryLater (3) */
242 
243 	if (!http_ctx->http_method)
244 		return;
245 	if (!strncasecmp(http_ctx->http_method, "GET", 3) &&
246 	    protohttp_ocsp_is_valid_uri(http_ctx->http_uri, ctx))
247 		goto deny;
248 	if (!strncasecmp(http_ctx->http_method, "POST", 4) &&
249 	    http_ctx->http_content_type &&
250 	    !strncasecmp(http_ctx->http_content_type,
251 	                 "application/ocsp-request", 24))
252 		goto deny;
253 	return;
254 
255 deny:
256 	inbuf = bufferevent_get_input(ctx->src.bev);
257 	outbuf = bufferevent_get_output(ctx->src.bev);
258 
259 	if (evbuffer_get_length(inbuf) > 0) {
260 		evbuffer_drain(inbuf, evbuffer_get_length(inbuf));
261 	}
262 
263 	// Do not send anything to the child conns
264 	struct evbuffer *dst_outbuf = bufferevent_get_output(ctx->dst.bev);
265 	if (evbuffer_get_length(dst_outbuf) > 0) {
266 		evbuffer_drain(dst_outbuf, evbuffer_get_length(dst_outbuf));
267 	}
268 
269 	// Do not send duplicate OCSP denied responses
270 	if (http_ctx->ocsp_denied)
271 		return;
272 
273 	log_finer("Sending OCSP denied response");
274 	evbuffer_add_printf(outbuf, ocspresp);
275 	http_ctx->ocsp_denied = 1;
276 }
277 
278 /*
279  * Filter a single line of HTTP request headers.
280  * Also fills in some context fields for logging.
281  *
282  * Returns NULL if the current line should be deleted from the request.
283  * Returns a newly allocated string if the current line should be replaced.
284  * Returns 'line' if the line should be kept.
285  */
286 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)287 protohttp_filter_request_header_line(const char *line, protohttp_ctx_t *http_ctx, enum conn_type type, pxy_conn_ctx_t *ctx)
288 {
289 	/* parse information for connect log */
290 	if (!http_ctx->http_method) {
291 		/* first line */
292 		char *space1, *space2;
293 
294 		space1 = strchr(line, ' ');
295 		space2 = space1 ? strchr(space1 + 1, ' ') : NULL;
296 		if (!space1) {
297 			/* not HTTP */
298 			http_ctx->seen_req_header = 1;
299 			http_ctx->not_valid = 1;
300 		} else {
301 			http_ctx->http_method = malloc(space1 - line + 1);
302 			if (http_ctx->http_method) {
303 				memcpy(http_ctx->http_method, line, space1 - line);
304 				http_ctx->http_method[space1 - line] = '\0';
305 			} else {
306 				ctx->enomem = 1;
307 				return NULL;
308 			}
309 			space1++;
310 			if (!space2) {
311 				/* HTTP/0.9 */
312 				http_ctx->seen_req_header = 1;
313 				space2 = space1 + strlen(space1);
314 			}
315 			http_ctx->http_uri = malloc(space2 - space1 + 1);
316 			if (http_ctx->http_uri) {
317 				memcpy(http_ctx->http_uri, space1, space2 - space1);
318 				http_ctx->http_uri[space2 - space1] = '\0';
319 			} else {
320 				ctx->enomem = 1;
321 				return NULL;
322 			}
323 		}
324 	} else {
325 		/* not first line */
326 		char *newhdr;
327 
328 		if (!http_ctx->http_host && !strncasecmp(line, "Host:", 5)) {
329 			http_ctx->http_host = strdup(util_skipws(line + 5));
330 			if (!http_ctx->http_host) {
331 				ctx->enomem = 1;
332 				return NULL;
333 			}
334 			http_ctx->seen_keyword_count++;
335 		} else if (!strncasecmp(line, "Content-Type:", 13)) {
336 			http_ctx->http_content_type = strdup(util_skipws(line + 13));
337 			if (!http_ctx->http_content_type) {
338 				ctx->enomem = 1;
339 				return NULL;
340 			}
341 			http_ctx->seen_keyword_count++;
342 		/* Override Connection: keepalive and Connection: upgrade */
343 		} else if (!strncasecmp(line, "Connection:", 11)) {
344 			http_ctx->sent_http_conn_close = 1;
345 			if (!(newhdr = strdup("Connection: close"))) {
346 				ctx->enomem = 1;
347 				return NULL;
348 			}
349 			http_ctx->seen_keyword_count++;
350 			return newhdr;
351 		// @attention Always use conn ctx for opts, child ctx does not have opts, see the comments in pxy_conn_child_ctx
352 		} else if (ctx->conn_opts->remove_http_accept_encoding && !strncasecmp(line, "Accept-Encoding:", 16)) {
353 			http_ctx->seen_keyword_count++;
354 			return NULL;
355 		} else if (ctx->conn_opts->remove_http_referer && !strncasecmp(line, "Referer:", 8)) {
356 			http_ctx->seen_keyword_count++;
357 			return NULL;
358 		/* Suppress upgrading to SSL/TLS, WebSockets or HTTP/2 and keep-alive */
359 		} else if (!strncasecmp(line, "Upgrade:", 8) || !strncasecmp(line, "Keep-Alive:", 11)) {
360 			http_ctx->seen_keyword_count++;
361 			return NULL;
362 		} else if ((type == CONN_TYPE_CHILD) && (
363 				   // @attention flickr keeps redirecting to https with 301 unless we remove the Via line of squid
364 				   // Apparently flickr assumes the existence of Via header field or squid keyword a sign of plain http, even if we are using https
365 		           !strncasecmp(line, "Via:", 4) ||
366 				   // Also do not send the loopback address to the Internet
367 		           !strncasecmp(line, "X-Forwarded-For:", 16))) {
368 			http_ctx->seen_keyword_count++;
369 			return NULL;
370 		} else if (!strncasecmp(line, SSLPROXY_KEY, SSLPROXY_KEY_LEN)) {
371 			// Remove any SSLproxy line, parent or child
372 			return NULL;
373 		} else if (line[0] == '\0') {
374 			http_ctx->seen_req_header = 1;
375 			if (!http_ctx->sent_http_conn_close) {
376 				newhdr = strdup("Connection: close\r\n");
377 				if (!newhdr) {
378 					ctx->enomem = 1;
379 					return NULL;
380 				}
381 				return newhdr;
382 			}
383 		}
384 	}
385 
386 	return (char*)line;
387 }
388 
389 static filter_action_t * NONNULL(1,2)
protohttp_filter_match_host(pxy_conn_ctx_t * ctx,filter_list_t * list)390 protohttp_filter_match_host(pxy_conn_ctx_t *ctx, filter_list_t *list)
391 {
392 	protohttp_ctx_t *http_ctx = ctx->protoctx->arg;
393 
394 	filter_site_t *site = filter_site_find(list->host_btree, list->host_acm, list->host_all, http_ctx->http_host);
395 	if (!site)
396 		return NULL;
397 
398 #ifndef WITHOUT_USERAUTH
399 	log_fine_va("Found site (line=%d): %s for %s:%s, %s:%s, %s, %s, %s", site->action.line_num, site->site,
400 		STRORDASH(ctx->srchost_str), STRORDASH(ctx->srcport_str), STRORDASH(ctx->dsthost_str), STRORDASH(ctx->dstport_str),
401 		STRORDASH(ctx->user), STRORDASH(ctx->desc), STRORDASH(http_ctx->http_host));
402 #else /* WITHOUT_USERAUTH */
403 	log_fine_va("Found site (line=%d): %s for %s:%s, %s:%s, %s", site->action.line_num, site->site,
404 		STRORDASH(ctx->srchost_str), STRORDASH(ctx->srcport_str), STRORDASH(ctx->dsthost_str), STRORDASH(ctx->dstport_str),
405 		STRORDASH(http_ctx->http_host));
406 #endif /* WITHOUT_USERAUTH */
407 
408 	if (!site->port_btree && !site->port_acm && (site->action.precedence < ctx->filter_precedence)) {
409 		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);
410 		return NULL;
411 	}
412 
413 #ifdef DEBUG_PROXY
414 	if (site->all_sites)
415 		log_finest_va("Match all host (line=%d): %s, %s", site->action.line_num, site->site, http_ctx->http_host);
416 	else if (site->exact)
417 		log_finest_va("Match exact with host (line=%d): %s, %s", site->action.line_num, site->site, http_ctx->http_host);
418 	else
419 		log_finest_va("Match substring in host (line=%d): %s, %s", site->action.line_num, site->site, http_ctx->http_host);
420 #endif /* DEBUG_PROXY */
421 
422 	filter_action_t *port_action = pxy_conn_filter_port(ctx, site);
423 	if (port_action)
424 		return port_action;
425 
426 	return &site->action;
427 }
428 
429 static filter_action_t * NONNULL(1,2)
protohttp_filter_match_uri(pxy_conn_ctx_t * ctx,filter_list_t * list)430 protohttp_filter_match_uri(pxy_conn_ctx_t *ctx, filter_list_t *list)
431 {
432 	protohttp_ctx_t *http_ctx = ctx->protoctx->arg;
433 
434 	filter_site_t *site = filter_site_find(list->uri_btree, list->uri_acm, list->uri_all, http_ctx->http_uri);
435 	if (!site)
436 		return NULL;
437 
438 #ifndef WITHOUT_USERAUTH
439 	log_fine_va("Found site (line=%d): %s for %s:%s, %s:%s, %s, %s, %s", site->action.line_num, site->site,
440 		STRORDASH(ctx->srchost_str), STRORDASH(ctx->srcport_str), STRORDASH(ctx->dsthost_str), STRORDASH(ctx->dstport_str),
441 		STRORDASH(ctx->user), STRORDASH(ctx->desc), STRORDASH(http_ctx->http_uri));
442 #else /* WITHOUT_USERAUTH */
443 	log_fine_va("Found site (line=%d): %s for %s:%s, %s:%s, %s", site->action.line_num, site->site,
444 		STRORDASH(ctx->srchost_str), STRORDASH(ctx->srcport_str), STRORDASH(ctx->dsthost_str), STRORDASH(ctx->dstport_str),
445 		STRORDASH(http_ctx->http_uri));
446 #endif /* WITHOUT_USERAUTH */
447 
448 	if (!site->port_btree && !site->port_acm && (site->action.precedence < ctx->filter_precedence)) {
449 		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);
450 		return NULL;
451 	}
452 
453 #ifdef DEBUG_PROXY
454 	if (site->all_sites)
455 		log_finest_va("Match all uri (line=%d): %s, %s", site->action.line_num, site->site, http_ctx->http_uri);
456 	else if (site->exact)
457 		log_finest_va("Match exact with uri (line=%d): %s, %s", site->action.line_num, site->site, http_ctx->http_uri);
458 	else
459 		log_finest_va("Match substring in uri (line=%d): %s, %s", site->action.line_num, site->site, http_ctx->http_uri);
460 #endif /* DEBUG_PROXY */
461 
462 	filter_action_t *port_action = pxy_conn_filter_port(ctx, site);
463 	if (port_action)
464 		return port_action;
465 
466 	return &site->action;
467 }
468 
469 static filter_action_t * NONNULL(1,2)
protohttp_filter(pxy_conn_ctx_t * ctx,filter_list_t * list)470 protohttp_filter(pxy_conn_ctx_t *ctx, filter_list_t *list)
471 {
472 	protohttp_ctx_t *http_ctx = ctx->protoctx->arg;
473 
474 	filter_action_t *action_host = NULL;
475 	filter_action_t *action_uri = NULL;
476 
477 	if (http_ctx->http_host) {
478 		if (!(action_host = protohttp_filter_match_host(ctx, list))) {
479 #ifndef WITHOUT_USERAUTH
480 			log_finest_va("No filter match with host: %s:%s, %s:%s, %s, %s, %s, %s",
481 				STRORDASH(ctx->srchost_str), STRORDASH(ctx->srcport_str), STRORDASH(ctx->dsthost_str), STRORDASH(ctx->dstport_str),
482 				STRORDASH(ctx->user), STRORDASH(ctx->desc), STRORDASH(http_ctx->http_host), STRORDASH(http_ctx->http_uri));
483 #else /* WITHOUT_USERAUTH */
484 			log_finest_va("No filter match with host: %s:%s, %s:%s, %s, %s",
485 				STRORDASH(ctx->srchost_str), STRORDASH(ctx->srcport_str), STRORDASH(ctx->dsthost_str), STRORDASH(ctx->dstport_str),
486 				STRORDASH(http_ctx->http_host), STRORDASH(http_ctx->http_uri));
487 #endif /* !WITHOUT_USERAUTH */
488 		}
489 	}
490 
491 	if (http_ctx->http_uri) {
492 		if (!(action_uri = protohttp_filter_match_uri(ctx, list))) {
493 #ifndef WITHOUT_USERAUTH
494 			log_finest_va("No filter match with uri: %s:%s, %s:%s, %s, %s, %s, %s",
495 				STRORDASH(ctx->srchost_str), STRORDASH(ctx->srcport_str), STRORDASH(ctx->dsthost_str), STRORDASH(ctx->dstport_str),
496 				STRORDASH(ctx->user), STRORDASH(ctx->desc), STRORDASH(http_ctx->http_host), STRORDASH(http_ctx->http_uri));
497 #else /* WITHOUT_USERAUTH */
498 			log_finest_va("No filter match with uri: %s:%s, %s:%s, %s, %s",
499 				STRORDASH(ctx->srchost_str), STRORDASH(ctx->srcport_str), STRORDASH(ctx->dsthost_str), STRORDASH(ctx->dstport_str),
500 				STRORDASH(http_ctx->http_host), STRORDASH(http_ctx->http_uri));
501 #endif /* !WITHOUT_USERAUTH */
502 		}
503 	}
504 
505 	if (action_host ||  action_uri)
506 		return pxy_conn_set_filter_action(action_host, action_uri
507 #ifdef DEBUG_PROXY
508 				, ctx, http_ctx->http_host, http_ctx->http_uri
509 #endif /* DEBUG_PROXY */
510 				);
511 
512 	return NULL;
513 }
514 
515 static int
protohttp_apply_filter(pxy_conn_ctx_t * ctx)516 protohttp_apply_filter(pxy_conn_ctx_t *ctx)
517 {
518 	int rv = 0;
519 	filter_action_t *a;
520 	if ((a = pxy_conn_filter(ctx, protohttp_filter))) {
521 		unsigned int action = pxy_conn_translate_filter_action(ctx, a);
522 
523 		ctx->filter_precedence = action & FILTER_PRECEDENCE;
524 
525 		if (action & FILTER_ACTION_DIVERT) {
526 			if (ctx->divert) {
527 				// Override any deferred block action, if already in divert mode (keep divert mode)
528 				ctx->deferred_action = FILTER_ACTION_NONE;
529 			} else {
530 				log_fine("HTTP filter cannot enable divert mode");
531 			}
532 		}
533 		else if (action & FILTER_ACTION_SPLIT) {
534 			if (!ctx->divert) {
535 				// Override any deferred block action, if already in split mode (keep split mode)
536 				ctx->deferred_action = FILTER_ACTION_NONE;
537 			} else {
538 				log_fine("HTTP filter cannot enable split mode");
539 			}
540 		}
541 		else if (action & FILTER_ACTION_PASS) {
542 			log_fine("HTTP filter cannot take pass action");
543 		}
544 		else if (action & FILTER_ACTION_BLOCK) {
545 			ctx->deferred_action = FILTER_ACTION_NONE;
546 			pxy_conn_term(ctx, 1);
547 			rv = 1;
548 		}
549 		//else { /* FILTER_ACTION_MATCH */ }
550 
551 		if (action & (FILTER_LOG_CONTENT | FILTER_LOG_PCAP
552 #ifndef WITHOUT_MIRROR
553 				| FILTER_LOG_MIRROR
554 #endif /* !WITHOUT_MIRROR */
555 				)) {
556 #ifndef WITHOUT_MIRROR
557 			log_fine("HTTP filter cannot enable content, pcap, or mirror logging");
558 #else /* !WITHOUT_MIRROR */
559 			log_fine("HTTP filter cannot enable content or pcap logging");
560 #endif /* WITHOUT_MIRROR */
561 		}
562 
563 		// Note that connect, master, and cert logs have already been written by now
564 		// so enabling or disabling those logs here will not have any effect
565 		if (action & FILTER_LOG_CONNECT)
566 			ctx->log_connect = 1;
567 		else if (action & FILTER_LOG_NOCONNECT)
568 			ctx->log_connect = 0;
569 		if (action & FILTER_LOG_MASTER)
570 			ctx->log_master = 1;
571 		else if (action & FILTER_LOG_NOMASTER)
572 			ctx->log_master = 0;
573 		if (action & FILTER_LOG_CERT)
574 			ctx->log_cert = 1;
575 		else if (action & FILTER_LOG_NOCERT)
576 			ctx->log_cert = 0;
577 
578 		// content, pcap, and mirror logging can be disabled only
579 		// loggers will stop writing further contents
580 		if (action & FILTER_LOG_NOCONTENT)
581 			ctx->log_content = 0;
582 		if (action & FILTER_LOG_NOPCAP)
583 			ctx->log_pcap = 0;
584 #ifndef WITHOUT_MIRROR
585 		if (action & FILTER_LOG_NOMIRROR)
586 			ctx->log_mirror = 0;
587 #endif /* !WITHOUT_MIRROR */
588 
589 		if (a->conn_opts)
590 			ctx->conn_opts = a->conn_opts;
591 	}
592 
593 	// Cannot defer block action any longer
594 	// Match action should not override any deferred action, hence no 'else if'
595 	if (pxy_conn_apply_deferred_block_action(ctx))
596 		rv = 1;
597 
598 	return rv;
599 }
600 
601 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)602 protohttp_filter_request_header(struct evbuffer *inbuf, struct evbuffer *outbuf, protohttp_ctx_t *http_ctx, enum conn_type type, pxy_conn_ctx_t *ctx)
603 {
604 	char *line;
605 
606 	while (!http_ctx->seen_req_header && (line = evbuffer_readln(inbuf, NULL, EVBUFFER_EOL_CRLF))) {
607 		log_finest_va("%s", line);
608 
609 		char *replace = protohttp_filter_request_header_line(line, http_ctx, type, ctx);
610 		if (replace == line) {
611 			evbuffer_add_printf(outbuf, "%s\r\n", line);
612 		} else if (replace) {
613 			log_finer_va("REPLACE= %s", replace);
614 			evbuffer_add_printf(outbuf, "%s\r\n", replace);
615 			free(replace);
616 		} else {
617 			log_finer_va("REMOVE= %s", line);
618 			if (ctx->enomem) {
619 				return -1;
620 			}
621 		}
622 		free(line);
623 
624 		if ((type == CONN_TYPE_PARENT) && ctx->divert && !ctx->sent_sslproxy_header) {
625 			ctx->sent_sslproxy_header = 1;
626 			log_finer_va("INSERT= %s", ctx->sslproxy_header);
627 			evbuffer_add_printf(outbuf, "%s\r\n", ctx->sslproxy_header);
628 		}
629 	}
630 
631 	if (http_ctx->seen_req_header) {
632 		if (type == CONN_TYPE_PARENT) {
633 			if (protohttp_apply_filter(ctx)) {
634 				return -1;
635 			}
636 
637 			/* request header complete */
638 			if (ctx->conn_opts->deny_ocsp) {
639 				protohttp_ocsp_deny(ctx, http_ctx);
640 			}
641 		}
642 
643 		if (ctx->enomem) {
644 			return -1;
645 		}
646 
647 		/* no data left after parsing headers? */
648 		if (evbuffer_get_length(inbuf) == 0) {
649 			return 0;
650 		}
651 		evbuffer_add_buffer(outbuf, inbuf);
652 	}
653 	return 0;
654 }
655 
656 #ifndef WITHOUT_USERAUTH
657 static char * NONNULL(1,2)
protohttp_get_url(struct evbuffer * inbuf,pxy_conn_ctx_t * ctx)658 protohttp_get_url(struct evbuffer *inbuf, pxy_conn_ctx_t *ctx)
659 {
660 	char *line;
661 	char *path = NULL;
662 	char *host = NULL;
663 	char *url = NULL;
664 
665 	while ((!host || !path) && (line = evbuffer_readln(inbuf, NULL, EVBUFFER_EOL_CRLF))) {
666 		log_finest_va("%s", line);
667 
668 		//GET / HTTP/1.1
669 		if (!path && !strncasecmp(line, "GET ", 4)) {
670 			path = strdup(util_skipws(line + 4));
671 			if (!path) {
672 				ctx->enomem = 1;
673 				free(line);
674 				goto memout;
675 			}
676 			path = strsep(&path, " \t");
677 			log_finest_va("path=%s", path);
678 		//Host: example.com
679 		} else if (!host && !strncasecmp(line, "Host:", 5)) {
680 			host = strdup(util_skipws(line + 5));
681 			if (!host) {
682 				ctx->enomem = 1;
683 				free(line);
684 				goto memout;
685 			}
686 			log_finest_va("host=%s", host);
687 		}
688 		free(line);
689 	}
690 
691 	if (host && path) {
692 		// Assume that path will always have a leading /, so do not insert an extra / in between host and path
693 		// Don't care about computing the exact url size for plain or secure http (http or https)
694 		// http  s   ://  example.com  + /            + NULL
695 		// 4  +  1 + 3  + strlen(host) + strlen(path) + 1
696 		size_t url_size = strlen(host) + strlen(path) + 9;
697 		url = malloc(url_size);
698 		if (!url) {
699 			ctx->enomem = 1;
700 			goto memout;
701 		}
702 
703 		if (snprintf(url, url_size, "http%s://%s%s", ctx->spec->ssl ? "s": "", host, path) < 0) {
704 			ctx->enomem = 1;
705 			free(url);
706 			url = NULL;
707 			goto memout;
708 		}
709 		log_finest_va("url=%s", url);
710 	}
711 memout:
712 	if (host)
713 		free(host);
714 	if (path)
715 		free(path);
716 	return url;
717 }
718 #endif /* !WITHOUT_USERAUTH */
719 
720 // Size = 39
721 static char *http_methods[] = { "GET", "PUT", "ICY", "COPY", "HEAD", "LOCK", "MOVE", "POLL", "POST", "BCOPY", "BMOVE", "MKCOL", "TRACE", "LABEL", "MERGE", "DELETE",
722 	"SEARCH", "UNLOCK", "REPORT", "UPDATE", "NOTIFY", "BDELETE", "CONNECT", "OPTIONS", "CHECKIN", "PROPFIND", "CHECKOUT", "CCM_POST", "SUBSCRIBE",
723 	"PROPPATCH", "BPROPFIND", "BPROPPATCH", "UNCHECKOUT", "MKACTIVITY", "MKWORKSPACE", "UNSUBSCRIBE", "RPC_CONNECT", "VERSION-CONTROL", "BASELINE-CONTROL" };
724 
725 static int NONNULL(1)
protohttp_validate_method(char * method,pxy_conn_ctx_t * ctx)726 protohttp_validate_method(char *method
727 #ifdef DEBUG_PROXY
728 	, pxy_conn_ctx_t *ctx
729 #endif /* DEBUG_PROXY */
730 	)
731 {
732 	size_t method_len = strlen(method);
733 
734 	unsigned int i;
735 	for (i = 0; i < sizeof(http_methods)/sizeof(char *); i++) {
736 		char *m = http_methods[i];
737 		if (strlen(m) == method_len && !strncasecmp(method, m, method_len)) {
738 			log_finest_va("Passed method validation: %s", method);
739 			return 0;
740 		}
741 	}
742 	return -1;
743 }
744 
745 int
protohttp_validate(pxy_conn_ctx_t * ctx)746 protohttp_validate(pxy_conn_ctx_t *ctx)
747 {
748 	protohttp_ctx_t *http_ctx = ctx->protoctx->arg;
749 
750 	if (http_ctx->not_valid) {
751 		log_finest("Not http, validation failed previously");
752 		return -1;
753 	}
754 	if (http_ctx->http_method) {
755 		if (protohttp_validate_method(http_ctx->http_method
756 #ifdef DEBUG_PROXY
757 				, ctx
758 #endif /* DEBUG_PROXY */
759 				) == -1) {
760 			http_ctx->not_valid = 1;
761 			log_finest_va("Failed method validation: %s", http_ctx->http_method);
762 			return -1;
763 		}
764 	}
765 	if (http_ctx->seen_keyword_count) {
766 		// The first line has been processed successfully
767 		// Pass validation if we have seen at least one http keyword
768 		ctx->protoctx->is_valid = 1;
769 		log_finest("Passed validation");
770 		return 0;
771 	}
772 	if (http_ctx->seen_bytes > ctx->conn_opts->max_http_header_size) {
773 		// Fail validation if still cannot pass as http after reaching max header size
774 		http_ctx->not_valid = 1;
775 		log_finest_va("Reached max header size, size=%llu", http_ctx->seen_bytes);
776 		return -1;
777 	}
778 	return 0;
779 }
780 
781 static void NONNULL(1,2)
protohttp_bev_readcb_src(struct bufferevent * bev,pxy_conn_ctx_t * ctx)782 protohttp_bev_readcb_src(struct bufferevent *bev, pxy_conn_ctx_t *ctx)
783 {
784 #ifndef WITHOUT_USERAUTH
785 	static const char redirect[] =
786 		"HTTP/1.1 302 Found\r\n"
787 		"Location: %s\r\n"
788 		"\r\n";
789 	static const char redirect_url[] =
790 		"HTTP/1.1 302 Found\r\n"
791 		"Location: %s?SSLproxy=%s\r\n"
792 		"\r\n";
793 #endif /* !WITHOUT_USERAUTH */
794 	static const char proto_error[] =
795 		"HTTP/1.1 400 Bad request\r\n"
796 		"Cache-Control: no-cache\r\n"
797 		"Connection: close\r\n"
798 		"Content-Type: text/html\r\n"
799 		"\r\n";
800 
801 	log_finest_va("ENTER, size=%zu", evbuffer_get_length(bufferevent_get_input(bev)));
802 
803 	if (ctx->dst.closed) {
804 		pxy_discard_inbuf(bev);
805 		return;
806 	}
807 
808 	protohttp_ctx_t *http_ctx = ctx->protoctx->arg;
809 	struct evbuffer *inbuf = bufferevent_get_input(bev);
810 	struct evbuffer *outbuf = bufferevent_get_output(ctx->dst.bev);
811 
812 #ifndef WITHOUT_USERAUTH
813 	if (ctx->conn_opts->user_auth && !ctx->user) {
814 		log_finest("Redirecting conn");
815 		char *url = protohttp_get_url(inbuf, ctx);
816 		pxy_discard_inbuf(bev);
817 		if (url) {
818 			evbuffer_add_printf(bufferevent_get_output(bev), redirect_url, ctx->conn_opts->user_auth_url, url);
819 			free(url);
820 		} else {
821 			evbuffer_add_printf(bufferevent_get_output(bev), redirect, ctx->conn_opts->user_auth_url);
822 		}
823 		ctx->sent_userauth_msg = 1;
824 		return;
825 	}
826 #endif /* !WITHOUT_USERAUTH */
827 
828 	if (ctx->conn_opts->validate_proto && !ctx->protoctx->is_valid) {
829 		http_ctx->seen_bytes += evbuffer_get_length(inbuf);
830 	}
831 
832 	// 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
833 	// @todo Should we look for GET/POST or Host header lines to detect the first packet?
834 	// But there is no guarantee that they will exist, due to fragmentation.
835 	// @attention We cannot append the ssl proxy address at the end of the packet or in between the header and the content,
836 	// because (1) the packet may be just the first fragment split somewhere not appropriate for appending a header,
837 	// and (2) there may not be any content.
838 	// And we are dealing with pop3 and smtp also, not just http.
839 
840 	/* request header munging */
841 	if (!http_ctx->seen_req_header) {
842 		log_finest_va("HTTP Request Header, size=%zu", evbuffer_get_length(inbuf));
843 		if (protohttp_filter_request_header(inbuf, outbuf, http_ctx, ctx->type, ctx) == -1) {
844 			return;
845 		}
846 	} else {
847 		log_finest_va("HTTP Request Body, size=%zu", evbuffer_get_length(inbuf));
848 		evbuffer_add_buffer(outbuf, inbuf);
849 	}
850 
851 	if (ctx->conn_opts->validate_proto && !ctx->protoctx->is_valid) {
852 		if (protohttp_validate(ctx) == -1) {
853 			evbuffer_add(bufferevent_get_output(bev), proto_error, strlen(proto_error));
854 			ctx->sent_protoerror_msg = 1;
855 			pxy_discard_inbuf(bev);
856 			evbuffer_drain(outbuf, evbuffer_get_length(outbuf));
857 			return;
858 		}
859 	}
860 
861 	pxy_try_set_watermark(bev, ctx, ctx->dst.bev);
862 }
863 
864 /*
865  * Filter a single line of HTTP response headers.
866  *
867  * Returns NULL if the current line should be deleted from the response.
868  * Returns a newly allocated string if the current line should be replaced.
869  * Returns `line' if the line should be kept.
870  */
871 static char * NONNULL(1,2,3)
protohttp_filter_response_header_line(const char * line,protohttp_ctx_t * http_ctx,pxy_conn_ctx_t * ctx)872 protohttp_filter_response_header_line(const char *line, protohttp_ctx_t *http_ctx, pxy_conn_ctx_t *ctx)
873 {
874 	/* parse information for connect log */
875 	if (!http_ctx->http_status_code) {
876 		/* first line */
877 		char *space1, *space2;
878 
879 		space1 = strchr(line, ' ');
880 		space2 = space1 ? strchr(space1 + 1, ' ') : NULL;
881 		if (!space1 || !!strncmp(line, "HTTP", 4)) {
882 			/* not HTTP or HTTP/0.9 */
883 			http_ctx->seen_resp_header = 1;
884 		} else {
885 			size_t len_code, len_text;
886 
887 			if (space2) {
888 				len_code = space2 - space1 - 1;
889 				len_text = strlen(space2 + 1);
890 			} else {
891 				len_code = strlen(space1 + 1);
892 				len_text = 0;
893 			}
894 			http_ctx->http_status_code = malloc(len_code + 1);
895 			http_ctx->http_status_text = malloc(len_text + 1);
896 			if (!http_ctx->http_status_code || !http_ctx->http_status_text) {
897 				ctx->enomem = 1;
898 				return NULL;
899 			}
900 			memcpy(http_ctx->http_status_code, space1 + 1, len_code);
901 			http_ctx->http_status_code[len_code] = '\0';
902 			if (space2) {
903 				memcpy(http_ctx->http_status_text,
904 				       space2 + 1, len_text);
905 			}
906 			http_ctx->http_status_text[len_text] = '\0';
907 		}
908 	} else {
909 		/* not first line */
910 		if (!http_ctx->http_content_length &&
911 		    !strncasecmp(line, "Content-Length:", 15)) {
912 			http_ctx->http_content_length =
913 				strdup(util_skipws(line + 15));
914 			if (!http_ctx->http_content_length) {
915 				ctx->enomem = 1;
916 				return NULL;
917 			}
918 		} else if (
919 		    /* HPKP: Public Key Pinning Extension for HTTP
920 		     * (draft-ietf-websec-key-pinning)
921 		     * remove to prevent public key pinning */
922 		    !strncasecmp(line, "Public-Key-Pins:", 16) ||
923 		    !strncasecmp(line, "Public-Key-Pins-Report-Only:", 28) ||
924 		    /* HSTS: HTTP Strict Transport Security (RFC 6797)
925 		     * remove to allow users to accept bad certs */
926 		    !strncasecmp(line, "Strict-Transport-Security:", 26) ||
927 		    /* Expect-CT: Expect Certificate Transparency
928 		     * (draft-ietf-httpbis-expect-ct-latest)
929 		     * remove to prevent failed CT log lookups */
930 		    !strncasecmp(line, "Expect-CT:", 10) ||
931 		    /* Alternate Protocol
932 		     * remove to prevent switching to QUIC, SPDY et al */
933 		    !strncasecmp(line, "Alternate-Protocol:", 19) ||
934 		    /* Upgrade header
935 		     * remove to prevent upgrading to HTTPS in unhandled ways,
936 		     * and more importantly, WebSockets and HTTP/2 */
937 		    !strncasecmp(line, "Upgrade:", 8)) {
938 			return NULL;
939 		} else if (line[0] == '\0') {
940 			http_ctx->seen_resp_header = 1;
941 		}
942 	}
943 
944 	return (char*)line;
945 }
946 
947 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)948 protohttp_filter_response_header(struct evbuffer *inbuf, struct evbuffer *outbuf, protohttp_ctx_t *http_ctx, pxy_conn_ctx_t *ctx)
949 {
950 	char *line;
951 
952 	while (!http_ctx->seen_resp_header && (line = evbuffer_readln(inbuf, NULL, EVBUFFER_EOL_CRLF))) {
953 		log_finest_va("%s", line);
954 
955 		char *replace = protohttp_filter_response_header_line(line, http_ctx, ctx);
956 		if (replace == line) {
957 			evbuffer_add_printf(outbuf, "%s\r\n", line);
958 		} else if (replace) {
959 			log_finer_va("REPLACE= %s", replace);
960 			evbuffer_add_printf(outbuf, "%s\r\n", replace);
961 			free(replace);
962 		} else {
963 			log_finer_va("REMOVE= %s", line);
964 			if (ctx->enomem) {
965 				return;
966 			}
967 		}
968 		free(line);
969 	}
970 
971 	if (http_ctx->seen_resp_header) {
972 		/* no data left after parsing headers? */
973 		if (evbuffer_get_length(inbuf) == 0) {
974 			return;
975 		}
976 		evbuffer_add_buffer(outbuf, inbuf);
977 	}
978 }
979 
980 static void NONNULL(1)
protohttp_bev_readcb_dst(struct bufferevent * bev,pxy_conn_ctx_t * ctx)981 protohttp_bev_readcb_dst(struct bufferevent *bev, pxy_conn_ctx_t *ctx)
982 {
983 	log_finest_va("ENTER, size=%zu", evbuffer_get_length(bufferevent_get_input(bev)));
984 
985 	if (ctx->src.closed) {
986 		pxy_discard_inbuf(bev);
987 		return;
988 	}
989 
990 	protohttp_ctx_t *http_ctx = ctx->protoctx->arg;
991 	struct evbuffer *inbuf = bufferevent_get_input(bev);
992 	struct evbuffer *outbuf = bufferevent_get_output(ctx->src.bev);
993 
994 	if (!http_ctx->seen_resp_header) {
995 		log_finest_va("HTTP Response Header, size=%zu", evbuffer_get_length(inbuf));
996 		protohttp_filter_response_header(inbuf, outbuf, http_ctx, ctx);
997 		if (ctx->enomem) {
998 			return;
999 		}
1000 	} else {
1001 		log_finest_va("HTTP Response Body, size=%zu", evbuffer_get_length(inbuf));
1002 		evbuffer_add_buffer(outbuf, inbuf);
1003 	}
1004 	pxy_try_set_watermark(bev, ctx, ctx->src.bev);
1005 }
1006 
1007 static void NONNULL(1)
protohttp_bev_readcb_srvdst(UNUSED struct bufferevent * bev,UNUSED pxy_conn_ctx_t * ctx)1008 protohttp_bev_readcb_srvdst(UNUSED struct bufferevent *bev, UNUSED pxy_conn_ctx_t *ctx)
1009 {
1010 	log_err_level(LOG_ERR, "readcb called on srvdst");
1011 }
1012 
1013 static void NONNULL(1)
protohttp_bev_readcb_src_child(struct bufferevent * bev,pxy_conn_child_ctx_t * ctx)1014 protohttp_bev_readcb_src_child(struct bufferevent *bev, pxy_conn_child_ctx_t *ctx)
1015 {
1016 	log_finest_va("ENTER, size=%zu", evbuffer_get_length(bufferevent_get_input(bev)));
1017 
1018 	if (ctx->dst.closed) {
1019 		pxy_discard_inbuf(bev);
1020 		return;
1021 	}
1022 
1023 	protohttp_ctx_t *http_ctx = ctx->protoctx->arg;
1024 	struct evbuffer *inbuf = bufferevent_get_input(bev);
1025 	struct evbuffer *outbuf = bufferevent_get_output(ctx->dst.bev);
1026 
1027 	if (!http_ctx->seen_req_header) {
1028 		log_finest_va("HTTP Request Header, size=%zu", evbuffer_get_length(inbuf));
1029 		// @todo Just remove SSLproxy line, do not filter request on the server side?
1030 		if (protohttp_filter_request_header(inbuf, outbuf, http_ctx, ctx->type, ctx->conn) == -1) {
1031 			return;
1032 		}
1033 	} else {
1034 		log_finest_va("HTTP Request Body, size=%zu", evbuffer_get_length(inbuf));
1035 		evbuffer_add_buffer(outbuf, inbuf);
1036 	}
1037 	pxy_try_set_watermark(bev, ctx->conn, ctx->dst.bev);
1038 }
1039 
1040 static void NONNULL(1)
protohttp_bev_readcb_dst_child(struct bufferevent * bev,pxy_conn_child_ctx_t * ctx)1041 protohttp_bev_readcb_dst_child(struct bufferevent *bev, pxy_conn_child_ctx_t *ctx)
1042 {
1043 	log_finest_va("ENTER, size=%zu", evbuffer_get_length(bufferevent_get_input(bev)));
1044 
1045 	if (ctx->src.closed) {
1046 		pxy_discard_inbuf(bev);
1047 		return;
1048 	}
1049 
1050 	protohttp_ctx_t *http_ctx = ctx->protoctx->arg;
1051 	struct evbuffer *inbuf = bufferevent_get_input(bev);
1052 	struct evbuffer *outbuf = bufferevent_get_output(ctx->src.bev);
1053 
1054 	if (!http_ctx->seen_resp_header) {
1055 		log_finest_va("HTTP Response Header, size=%zu", evbuffer_get_length(inbuf));
1056 		// @todo Do not filter response on the server side?
1057 		protohttp_filter_response_header(inbuf, outbuf, http_ctx, ctx->conn);
1058 		if (ctx->conn->enomem) {
1059 			return;
1060 		}
1061 	} else {
1062 		log_finest_va("HTTP Response Body, size=%zu", evbuffer_get_length(inbuf));
1063 		evbuffer_add_buffer(outbuf, inbuf);
1064 	}
1065 	pxy_try_set_watermark(bev, ctx->conn, ctx->src.bev);
1066 }
1067 
1068 static void NONNULL(1)
protohttp_bev_readcb(struct bufferevent * bev,void * arg)1069 protohttp_bev_readcb(struct bufferevent *bev, void *arg)
1070 {
1071 	pxy_conn_ctx_t *ctx = arg;
1072 	protohttp_ctx_t *http_ctx = ctx->protoctx->arg;
1073 
1074 	int seen_resp_header_on_entry = http_ctx->seen_resp_header;
1075 
1076 	if (bev == ctx->src.bev) {
1077 		protohttp_bev_readcb_src(bev, ctx);
1078 	} else if (bev == ctx->dst.bev) {
1079 		protohttp_bev_readcb_dst(bev, ctx);
1080 	} else if (bev == ctx->srvdst.bev) {
1081 		protohttp_bev_readcb_srvdst(bev, ctx);
1082 	} else {
1083 		log_err_printf("protohttp_bev_readcb: UNKWN conn end\n");
1084 		return;
1085 	}
1086 
1087 	if (ctx->enomem) {
1088 		return;
1089 	}
1090 
1091 	if (!seen_resp_header_on_entry && http_ctx->seen_resp_header) {
1092 		/* response header complete: log connection */
1093 		if (WANT_CONNECT_LOG(ctx->conn)) {
1094 			protohttp_log_connect(ctx);
1095 		}
1096 	}
1097 }
1098 
1099 static void NONNULL(1)
protohttp_bev_readcb_child(struct bufferevent * bev,void * arg)1100 protohttp_bev_readcb_child(struct bufferevent *bev, void *arg)
1101 {
1102 	pxy_conn_child_ctx_t *ctx = arg;
1103 
1104 	if (bev == ctx->src.bev) {
1105 		protohttp_bev_readcb_src_child(bev, ctx);
1106 	} else if (bev == ctx->dst.bev) {
1107 		protohttp_bev_readcb_dst_child(bev, ctx);
1108 	} else {
1109 		log_err_printf("protohttp_bev_readcb_child: UNKWN conn end\n");
1110 	}
1111 }
1112 
1113 static void NONNULL(1)
protohttp_bev_writecb_src(struct bufferevent * bev,pxy_conn_ctx_t * ctx)1114 protohttp_bev_writecb_src(struct bufferevent *bev, pxy_conn_ctx_t *ctx)
1115 {
1116 	log_finest("ENTER");
1117 
1118 #ifndef WITHOUT_USERAUTH
1119 	if (prototcp_try_close_unauth_conn(bev, ctx)) {
1120 		return;
1121 	}
1122 #endif /* !WITHOUT_USERAUTH */
1123 
1124 	if (prototcp_try_close_protoerror_conn(bev, ctx)) {
1125 		return;
1126 	}
1127 
1128 	protohttp_ctx_t *http_ctx = ctx->protoctx->arg;
1129 	if (ctx->dst.closed || http_ctx->ocsp_denied) {
1130 		if (pxy_try_close_conn_end(&ctx->src, ctx) == 1) {
1131 			log_finest("dst.closed or ocsp_denied, terminate conn");
1132 			pxy_conn_term(ctx, 1);
1133 		}
1134 		return;
1135 	}
1136 	pxy_try_unset_watermark(bev, ctx, &ctx->dst);
1137 }
1138 
1139 static void NONNULL(1)
protohttp_bev_writecb(struct bufferevent * bev,void * arg)1140 protohttp_bev_writecb(struct bufferevent *bev, void *arg)
1141 {
1142 	pxy_conn_ctx_t *ctx = arg;
1143 
1144 	if (bev == ctx->src.bev) {
1145 		protohttp_bev_writecb_src(bev, ctx);
1146 	} else if (bev == ctx->dst.bev) {
1147 		prototcp_bev_writecb_dst(bev, ctx);
1148 	} else {
1149 		log_err_printf("protohttp_bev_writecb: UNKWN conn end\n");
1150 	}
1151 }
1152 
1153 static void NONNULL(1)
protohttp_free_ctx(protohttp_ctx_t * http_ctx)1154 protohttp_free_ctx(protohttp_ctx_t *http_ctx)
1155 {
1156 	if (http_ctx->http_method) {
1157 		free(http_ctx->http_method);
1158 	}
1159 	if (http_ctx->http_uri) {
1160 		free(http_ctx->http_uri);
1161 	}
1162 	if (http_ctx->http_host) {
1163 		free(http_ctx->http_host);
1164 	}
1165 	if (http_ctx->http_content_type) {
1166 		free(http_ctx->http_content_type);
1167 	}
1168 	if (http_ctx->http_status_code) {
1169 		free(http_ctx->http_status_code);
1170 	}
1171 	if (http_ctx->http_status_text) {
1172 		free(http_ctx->http_status_text);
1173 	}
1174 	if (http_ctx->http_content_length) {
1175 		free(http_ctx->http_content_length);
1176 	}
1177 	free(http_ctx);
1178 }
1179 
1180 static void NONNULL(1)
protohttp_free(pxy_conn_ctx_t * ctx)1181 protohttp_free(pxy_conn_ctx_t *ctx)
1182 {
1183 	protohttp_ctx_t *http_ctx = ctx->protoctx->arg;
1184 	protohttp_free_ctx(http_ctx);
1185 }
1186 
1187 static void NONNULL(1)
protohttps_free(pxy_conn_ctx_t * ctx)1188 protohttps_free(pxy_conn_ctx_t *ctx)
1189 {
1190 	protohttp_free(ctx);
1191 	protossl_free(ctx);
1192 }
1193 
1194 static void NONNULL(1)
protohttp_free_child(pxy_conn_child_ctx_t * ctx)1195 protohttp_free_child(pxy_conn_child_ctx_t *ctx)
1196 {
1197 	protohttp_ctx_t *http_ctx = ctx->protoctx->arg;
1198 	protohttp_free_ctx(http_ctx);
1199 }
1200 
1201 // @attention Called by thrmgr thread
1202 protocol_t
protohttp_setup(pxy_conn_ctx_t * ctx)1203 protohttp_setup(pxy_conn_ctx_t *ctx)
1204 {
1205 	ctx->protoctx->proto = PROTO_HTTP;
1206 
1207 	ctx->protoctx->bev_readcb = protohttp_bev_readcb;
1208 	ctx->protoctx->bev_writecb = protohttp_bev_writecb;
1209 	ctx->protoctx->proto_free = protohttp_free;
1210 
1211 	ctx->protoctx->arg = malloc(sizeof(protohttp_ctx_t));
1212 	if (!ctx->protoctx->arg) {
1213 		return PROTO_ERROR;
1214 	}
1215 	memset(ctx->protoctx->arg, 0, sizeof(protohttp_ctx_t));
1216 
1217 	return PROTO_HTTP;
1218 }
1219 
1220 // @attention Called by thrmgr thread
1221 protocol_t
protohttps_setup(pxy_conn_ctx_t * ctx)1222 protohttps_setup(pxy_conn_ctx_t *ctx)
1223 {
1224 	ctx->protoctx->proto = PROTO_HTTPS;
1225 	ctx->protoctx->connectcb = protossl_conn_connect;
1226 	ctx->protoctx->init_conn = protossl_init_conn;
1227 
1228 	ctx->protoctx->bev_readcb = protohttp_bev_readcb;
1229 	ctx->protoctx->bev_writecb = protohttp_bev_writecb;
1230 	ctx->protoctx->bev_eventcb = protossl_bev_eventcb;
1231 
1232 	ctx->protoctx->proto_free = protohttps_free;
1233 
1234 	ctx->protoctx->arg = malloc(sizeof(protohttp_ctx_t));
1235 	if (!ctx->protoctx->arg) {
1236 		return PROTO_ERROR;
1237 	}
1238 	memset(ctx->protoctx->arg, 0, sizeof(protohttp_ctx_t));
1239 
1240 	ctx->sslctx = malloc(sizeof(ssl_ctx_t));
1241 	if (!ctx->sslctx) {
1242 		free(ctx->protoctx->arg);
1243 		return PROTO_ERROR;
1244 	}
1245 	memset(ctx->sslctx, 0, sizeof(ssl_ctx_t));
1246 
1247 	return PROTO_HTTPS;
1248 }
1249 
1250 protocol_t
protohttp_setup_child(pxy_conn_child_ctx_t * ctx)1251 protohttp_setup_child(pxy_conn_child_ctx_t *ctx)
1252 {
1253 	ctx->protoctx->proto = PROTO_HTTP;
1254 
1255 	// @todo Should HTTP child conns do any http related processing, so use tcp defaults instead?
1256 	ctx->protoctx->bev_readcb = protohttp_bev_readcb_child;
1257 	ctx->protoctx->proto_free = protohttp_free_child;
1258 
1259 	ctx->protoctx->arg = malloc(sizeof(protohttp_ctx_t));
1260 	if (!ctx->protoctx->arg) {
1261 		return PROTO_ERROR;
1262 	}
1263 	memset(ctx->protoctx->arg, 0, sizeof(protohttp_ctx_t));
1264 
1265 	return PROTO_HTTP;
1266 }
1267 
1268 protocol_t
protohttps_setup_child(pxy_conn_child_ctx_t * ctx)1269 protohttps_setup_child(pxy_conn_child_ctx_t *ctx)
1270 {
1271 	ctx->protoctx->proto = PROTO_HTTPS;
1272 	ctx->protoctx->connectcb = protossl_connect_child;
1273 
1274 	ctx->protoctx->bev_readcb = protohttp_bev_readcb_child;
1275 	ctx->protoctx->bev_eventcb = protossl_bev_eventcb_child;
1276 
1277 	ctx->protoctx->proto_free = protohttp_free_child;
1278 
1279 	ctx->protoctx->arg = malloc(sizeof(protohttp_ctx_t));
1280 	if (!ctx->protoctx->arg) {
1281 		return PROTO_ERROR;
1282 	}
1283 	memset(ctx->protoctx->arg, 0, sizeof(protohttp_ctx_t));
1284 
1285 	return PROTO_HTTPS;
1286 }
1287 
1288 /* vim: set noet ft=c: */
1289