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