SSLproxy/proxy.c
Soner Tari ad63380b07 Fix e2 port specs, otherwise we get:
"Error from socket() fd2: Protocol not supported (43)
Error opening socket: Bad file descriptor (9)"
2017-06-11 14:47:51 +03:00

602 lines
18 KiB
C

/*
* SSLsplit - transparent SSL/TLS interception
* Copyright (c) 2009-2016, Daniel Roethlisberger <daniel@roe.ch>
* All rights reserved.
* http://www.roe.ch/SSLsplit
*
* 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 AUTHOR ``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 AUTHOR 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 "proxy.h"
#include "privsep.h"
#include "pxythrmgr.h"
#include "pxyconn.h"
#include "cachemgr.h"
#include "opts.h"
#include "log.h"
#include "attrib.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <event2/event.h>
#include <event2/listener.h>
#include <event2/bufferevent.h>
#include <event2/bufferevent_ssl.h>
#include <event2/buffer.h>
#include <event2/thread.h>
/*
* Proxy engine, built around libevent 2.x.
*/
static int signals[] = { SIGQUIT, SIGHUP, SIGINT, SIGPIPE, SIGUSR1 };
struct proxy_ctx {
pxy_thrmgr_ctx_t *thrmgr;
struct event_base *evbase;
struct event *sev[sizeof(signals)/sizeof(int)];
struct event *gcev;
struct proxy_listener_ctx *lctx;
opts_t *opts;
};
///*
// * Listener context.
// */
//typedef struct proxy_listener_ctx {
// pxy_thrmgr_ctx_t *thrmgr;
// proxyspec_t *spec;
// opts_t *opts;
// struct evconnlistener *evcl;
// struct evconnlistener *evcl_e2;
// struct proxy_listener_ctx *next;
// pxy_conn_ctx_t * ctx;
// evutil_socket_t fd2;
// int clisock;
//} proxy_listener_ctx_t;
static proxy_listener_ctx_t *
proxy_listener_ctx_new(pxy_thrmgr_ctx_t *thrmgr, proxyspec_t *spec,
opts_t *opts) MALLOC;
static proxy_listener_ctx_t *
proxy_listener_ctx_new(pxy_thrmgr_ctx_t *thrmgr, proxyspec_t *spec,
opts_t *opts)
{
proxy_listener_ctx_t *ctx = malloc(sizeof(proxy_listener_ctx_t));
if (!ctx)
return NULL;
memset(ctx, 0, sizeof(proxy_listener_ctx_t));
ctx->thrmgr = thrmgr;
ctx->spec = spec;
ctx->opts = opts;
return ctx;
}
//static void
//proxy_listener_ctx_free(proxy_listener_ctx_t *ctx) NONNULL(1);
static void
proxy_listener_ctx_free(proxy_listener_ctx_t *ctx)
{
if (ctx->evcl) {
evconnlistener_free(ctx->evcl);
}
if (ctx->next) {
proxy_listener_ctx_free(ctx->next);
}
free(ctx);
}
/*
* Callback for error events on the socket listener bufferevent.
*/
static void
proxy_listener_errorcb(struct evconnlistener *listener, UNUSED void *ctx)
{
log_dbg_printf(">############################# proxy_listener_errorcb: ERROR\n");
struct event_base *evbase = evconnlistener_get_base(listener);
int err = EVUTIL_SOCKET_ERROR();
log_err_printf("Error %d on listener: %s\n", err,
evutil_socket_error_to_string(err));
event_base_loopbreak(evbase);
}
/*
* Callback for accept events on the socket listener bufferevent.
*/
static void
proxy_listener_acceptcb_e2(UNUSED struct evconnlistener *listener,
evutil_socket_t fd,
struct sockaddr *peeraddr, int peeraddrlen,
void *arg)
{
// pxy_conn_ctx_t *parent_ctx = arg;
proxy_conn_meta_ctx_t *mctx = arg;
if (!mctx) {
log_dbg_printf(">>>>>------------------------------------------------------------------------------------ proxy_listener_acceptcb_e2: NULL mctx <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< GONE\n");
return;
}
pthread_mutex_t *cmutex = &mctx->mutex;
int err = pthread_mutex_lock(cmutex);
log_dbg_printf(">>>>>------------------------------------------------------------------------------------ proxy_listener_acceptcb_e2() lock err=%d\n", err);
pxy_conn_ctx_t *parent_ctx = mctx->parent_ctx;
// pthread_mutex_t *cmutex = &parent_ctx->thrmgr->mutex2;
if (!parent_ctx) {
log_dbg_printf(">>>>>------------------------------------------------------------------------------------ proxy_listener_acceptcb_e2: NULL parent_ctx <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< GONE\n");
goto leave;
}
log_dbg_printf(">>>>>------------------------------------------------------------------------------------ proxy_listener_acceptcb_e2(): child fd=%d, parent fd=%d\n", fd, parent_ctx->fd);
char *host, *port;
if (sys_sockaddr_str(peeraddr, peeraddrlen, &host, &port) != 0) {
log_dbg_printf(">>>>>------------------------------------------------------------------------------------ proxy_listener_acceptcb_e2(): PEER failed\n");
} else {
log_dbg_printf(">>>>>------------------------------------------------------------------------------------ proxy_listener_acceptcb_e2(): PEER [%s]:%s <<<<< child fd=%d, parent fd=%d\n", host, port, fd, parent_ctx->fd);
free(host);
free(port);
}
// pxy_conn_setup_e2(fd, peeraddr, peeraddrlen, parent_ctx->lctx->thrmgr, parent_ctx->lctx->spec, parent_ctx->lctx->opts, mctx);
// pxy_conn_setup_e2(fd, peeraddr, peeraddrlen, mctx->lctx->thrmgr, mctx->lctx->spec, mctx->lctx->opts, mctx);
// pxy_conn_setup_e2(fd, peeraddr, peeraddrlen, mctx);
pxy_conn_setup_e2(fd, mctx);
leave:
log_dbg_printf(">>>>>------------------------------------------------------------------------------------ proxy_listener_acceptcb_e2(): EXIT\n");
pthread_mutex_unlock(cmutex);
}
static proxy_conn_meta_ctx_t *
pxy_conn_meta_ctx_new()
{
log_dbg_printf(">>>>>................... pxy_conn_meta_ctx_new(): ENTER <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
proxy_conn_meta_ctx_t *ctx = malloc(sizeof(proxy_conn_meta_ctx_t));
// proxy_conn_meta_ctx_t *ctx = malloc(100);
if (!ctx)
return NULL;
memset(ctx, 0, sizeof(proxy_conn_meta_ctx_t));
log_dbg_printf(">>>>> !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! pxy_conn_meta_ctx_new: sizeof(proxy_conn_meta_ctx_t)=%d <<<<<<\n", sizeof(proxy_conn_meta_ctx_t));
ctx->next = NULL;
pthread_mutex_init(&ctx->mutex, NULL);
log_dbg_printf(">>>>>................... pxy_conn_meta_ctx_new(): EXIT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
return ctx;
}
int remove_ctx(proxy_conn_meta_ctx_t **current) {
// if ((*current)->released) {
proxy_conn_meta_ctx_t *tmp = *current;
if (tmp->parent_ctx) {
log_dbg_printf(">>>>> !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! remove_ctx: PARENT NOT NULL !!!!!!!!!!!!!!!!!!! <<<<<<\n");
return 0;
}
if (tmp->child_ctx) {
log_dbg_printf(">>>>> !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! remove_ctx: CHILD NOT NULL !!!!!!!!!!!!!!!!!!! <<<<<<\n");
return 0;
}
*current = (*current)->next;
log_dbg_printf(">>>>> !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! remove_ctx: FREEING META CTX, size=%d <<<<<<\n", sizeof(*tmp));
pthread_mutex_destroy(&tmp->mutex);
free(tmp);
tmp = NULL;
log_dbg_printf(">>>>> !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! remove_ctx: EXIT <<<<<<\n");
return 1;
// }
// log_dbg_printf(">>>>> !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! remove_ctx: NOT FREEING UNRELEASED META CTX <<<<<<\n");
// return 0;
}
static void
proxy_listener_acceptcb(UNUSED struct evconnlistener *listener,
evutil_socket_t fd,
struct sockaddr *peeraddr, int peeraddrlen,
void *arg)
{
proxy_listener_ctx_t *lctx = arg;
log_dbg_printf(">>>>> proxy_listener_acceptcb: <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< LOCK\n");
// pthread_mutex_t *tmutex = &lctx->thrmgr->mutex2;
// pthread_mutex_t *lmutex = &lctx->mutex;
// pthread_mutex_lock(lmutex);
// int total = sizeof(pxy_conn_ctx_t *) + sizeof(pxy_conn_ctx_t *) + sizeof(pthread_mutex_t) + sizeof(unsigned int) + sizeof(proxy_conn_meta_ctx_t *);
// log_dbg_printf(">>>>> !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! proxy_listener_acceptcb: TOTAL SIZE=%d <<<<<<\n", total);
// log_dbg_printf(">>>>> !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! proxy_listener_acceptcb: sizeof(pthread_mutex_t)=%d <<<<<<\n", sizeof(pthread_mutex_t));
proxy_conn_meta_ctx_t *mctx = pxy_conn_meta_ctx_new();
pthread_mutex_lock(&mctx->mutex);
mctx->lctx = lctx;
log_dbg_printf(">>>>>------------------------------------------------------------------------------------ proxy_listener_acceptcb(): fd=%d, previous fd2=%d\n", fd, lctx->fd2);
log_dbg_printf(">>>>> proxy_listener_acceptcb: SETTING UP E2, lctx->clisock=%d\n", lctx->clisock);
evutil_socket_t fd2;
if ((fd2 = privsep_client_opensock_e2(lctx->clisock, lctx->spec)) == -1) {
log_err_printf("Error opening socket: %s (%i)\n",
strerror(errno), errno);
return;
}
log_dbg_printf(">>>>> !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! proxy_listener_acceptcb: fd=%d, prev fd2=%d, NEW fd2=%d <<<<<<\n", fd, lctx->fd2, fd2);
lctx->fd2 = fd2;
mctx->fd2 = fd2;
// pxy_conn_ctx_t *parent_ctx = pxy_conn_setup(fd, peeraddr, peeraddrlen, lctx->thrmgr, lctx->spec, lctx->opts, fd2);
pxy_conn_ctx_t *parent_ctx = pxy_conn_setup(fd, peeraddr, peeraddrlen, mctx, fd2);
mctx->parent_ctx = parent_ctx;
struct evconnlistener *evcl2 = evconnlistener_new(evconnlistener_get_base(lctx->evcl), proxy_listener_acceptcb_e2,
mctx, LEV_OPT_CLOSE_ON_FREE, 1024, fd2);
// parent_ctx, LEV_OPT_CLOSE_ON_FREE, 1024, fd2);
// parent_ctx, LEV_OPT_CLOSE_ON_FREE|LEV_OPT_THREADSAFE, 1024, fd2);
// lctx->evcl_e2 = evcl_e2;
if (!evcl2) {
log_err_printf("Error creating evconnlistener e2: %s\n",
strerror(errno));
proxy_listener_ctx_free(evcl2);
evutil_closesocket(fd2);
// pthread_mutex_unlock(lmutex);
pthread_mutex_unlock(&mctx->mutex);
return;
}
mctx->evcl2 = evcl2;
evconnlistener_set_error_cb(evcl2, proxy_listener_errorcb);
// log_dbg_printf(">>>>> proxy_listener_acceptcb: <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< bufferevent_enable(parent_ctx->dst.bev)\n");
// bufferevent_enable(parent_ctx->dst.bev, EV_READ|EV_WRITE);
log_dbg_printf(">>>>> proxy_listener_acceptcb: FINISHED SETTING UP E2 SUCCESS, parent fd=%d, NEW fd2=%d\n", fd, fd2);
// pthread_mutex_unlock(lmutex);
pthread_mutex_unlock(&mctx->mutex);
}
/*
* Dump a description of an evbase to debugging code.
*/
static void
proxy_debug_base(const struct event_base *ev_base)
{
log_dbg_printf("Using libevent backend '%s'\n",
event_base_get_method(ev_base));
enum event_method_feature f;
f = event_base_get_features(ev_base);
log_dbg_printf("Event base supports: edge %s, O(1) %s, anyfd %s\n",
((f & EV_FEATURE_ET) ? "yes" : "no"),
((f & EV_FEATURE_O1) ? "yes" : "no"),
((f & EV_FEATURE_FDS) ? "yes" : "no"));
}
/*
* Set up the listener for a single proxyspec and add it to evbase.
* Returns the proxy_listener_ctx_t pointer if successful, NULL otherwise.
*/
static proxy_listener_ctx_t *
proxy_listener_setup(struct event_base *evbase, pxy_thrmgr_ctx_t *thrmgr,
proxyspec_t *spec, opts_t *opts, int clisock)
{
log_dbg_printf(">>>>> proxy_listener_setup\n");
proxy_listener_ctx_t *lctx;
int fd;
if ((fd = privsep_client_opensock(clisock, spec)) == -1) {
log_err_printf("Error opening socket: %s (%i)\n",
strerror(errno), errno);
return NULL;
}
lctx = proxy_listener_ctx_new(thrmgr, spec, opts);
if (!lctx) {
log_err_printf("Error creating listener context\n");
evutil_closesocket(fd);
return NULL;
}
pthread_mutex_init(&lctx->mutex, NULL);
lctx->clisock = clisock;
lctx->evcl = evconnlistener_new(evbase, proxy_listener_acceptcb,
lctx, LEV_OPT_CLOSE_ON_FREE, 1024, fd);
// lctx, LEV_OPT_CLOSE_ON_FREE|LEV_OPT_THREADSAFE, 1024, fd);
if (!lctx->evcl) {
log_err_printf("Error creating evconnlistener: %s\n",
strerror(errno));
proxy_listener_ctx_free(lctx);
evutil_closesocket(fd);
return NULL;
}
evconnlistener_set_error_cb(lctx->evcl, proxy_listener_errorcb);
return lctx;
}
/*
* Signal handler for SIGQUIT, SIGINT, SIGHUP, SIGPIPE and SIGUSR1.
*/
static void
proxy_signal_cb(evutil_socket_t fd, UNUSED short what, void *arg)
{
proxy_ctx_t *ctx = arg;
if (OPTS_DEBUG(ctx->opts)) {
log_dbg_printf("Received signal %i\n", fd);
}
switch(fd) {
case SIGQUIT:
case SIGINT:
case SIGHUP:
proxy_loopbreak(ctx);
break;
case SIGUSR1:
if (log_reopen() == -1) {
log_err_printf("Warning: Failed to reopen logs\n");
} else {
log_dbg_printf("Reopened log files\n");
}
break;
case SIGPIPE:
log_err_printf("Warning: Received SIGPIPE; ignoring.\n");
break;
default:
log_err_printf("Warning: Received unexpected signal %i\n", fd);
break;
}
}
/*
* Garbage collection handler.
*/
static void
proxy_gc_cb(UNUSED evutil_socket_t fd, UNUSED short what, void *arg)
{
proxy_ctx_t *ctx = arg;
if (OPTS_DEBUG(ctx->opts))
log_dbg_printf("Garbage collecting caches started.\n");
cachemgr_gc();
if (OPTS_DEBUG(ctx->opts))
log_dbg_printf("Garbage collecting caches done.\n");
}
/*
* Set up the core event loop.
* Socket clisock is the privsep client socket used for binding to ports.
* Returns ctx on success, or NULL on error.
*/
proxy_ctx_t *
proxy_new(opts_t *opts, int clisock)
{
proxy_listener_ctx_t *head;
proxy_ctx_t *ctx;
struct evdns_base *dnsbase;
int rc;
/* adds locking, only required if accessed from separate threads */
evthread_use_pthreads();
#ifndef PURIFY
if (OPTS_DEBUG(opts)) {
event_enable_debug_mode();
}
#endif /* PURIFY */
ctx = malloc(sizeof(proxy_ctx_t));
if (!ctx) {
log_err_printf("Error allocating memory\n");
goto leave0;
}
memset(ctx, 0, sizeof(proxy_ctx_t));
ctx->opts = opts;
ctx->evbase = event_base_new();
if (!ctx->evbase) {
log_err_printf("Error getting event base\n");
goto leave1;
}
if (opts_has_dns_spec(opts)) {
/* create a dnsbase here purely for being able to test parsing
* resolv.conf while we can still alert the user about it. */
dnsbase = evdns_base_new(ctx->evbase, 0);
if (!dnsbase) {
log_err_printf("Error creating dns event base\n");
goto leave1b;
}
rc = evdns_base_resolv_conf_parse(dnsbase, DNS_OPTIONS_ALL,
"/etc/resolv.conf");
evdns_base_free(dnsbase, 0);
if (rc != 0) {
log_err_printf("evdns cannot parse resolv.conf: "
"%s (%d)\n",
rc == 1 ? "failed to open file" :
rc == 2 ? "failed to stat file" :
rc == 3 ? "file too large" :
rc == 4 ? "out of memory" :
rc == 5 ? "short read from file" :
rc == 6 ? "no nameservers in file" :
"unknown error", rc);
goto leave1b;
}
}
if (OPTS_DEBUG(opts)) {
proxy_debug_base(ctx->evbase);
}
ctx->thrmgr = pxy_thrmgr_new(opts);
if (!ctx->thrmgr) {
log_err_printf("Error creating thread manager\n");
goto leave1b;
}
head = ctx->lctx = NULL;
for (proxyspec_t *spec = opts->spec; spec; spec = spec->next) {
head = proxy_listener_setup(ctx->evbase, ctx->thrmgr,
spec, opts, clisock);
if (!head)
goto leave2;
head->next = ctx->lctx;
ctx->lctx = head;
char *specstr = proxyspec_str(spec);
if (!specstr) {
fprintf(stderr, "out of memory\n");
exit(EXIT_FAILURE);
}
log_dbg_printf(">>>>> proxy_listener_setup - %s\n", specstr);
free(specstr);
}
for (size_t i = 0; i < (sizeof(signals) / sizeof(int)); i++) {
ctx->sev[i] = evsignal_new(ctx->evbase, signals[i],
proxy_signal_cb, ctx);
if (!ctx->sev[i])
goto leave3;
evsignal_add(ctx->sev[i], NULL);
}
struct timeval gc_delay = {60, 0};
ctx->gcev = event_new(ctx->evbase, -1, EV_PERSIST, proxy_gc_cb, ctx);
if (!ctx->gcev)
goto leave4;
evtimer_add(ctx->gcev, &gc_delay);
// @attention Do not close privsep sock, the client binds to new sockets on the egress path
//privsep_client_close(clisock);
return ctx;
leave4:
if (ctx->gcev) {
event_free(ctx->gcev);
}
leave3:
for (size_t i = 0; i < (sizeof(ctx->sev) / sizeof(ctx->sev[0])); i++) {
if (ctx->sev[i]) {
event_free(ctx->sev[i]);
}
}
leave2:
if (ctx->lctx) {
proxy_listener_ctx_free(ctx->lctx);
}
pxy_thrmgr_free(ctx->thrmgr);
leave1b:
event_base_free(ctx->evbase);
leave1:
free(ctx);
leave0:
return NULL;
}
/*
* Run the event loop. Returns when the event loop is cancelled by a signal
* or on failure.
*/
void
proxy_run(proxy_ctx_t *ctx)
{
if (ctx->opts->detach) {
event_reinit(ctx->evbase);
}
#ifndef PURIFY
if (OPTS_DEBUG(ctx->opts)) {
event_base_dump_events(ctx->evbase, stderr);
}
#endif /* PURIFY */
if (pxy_thrmgr_run(ctx->thrmgr) == -1) {
log_err_printf("Failed to start thread manager\n");
return;
}
if (OPTS_DEBUG(ctx->opts)) {
log_dbg_printf("Starting main event loop.\n");
}
event_base_dispatch(ctx->evbase);
if (OPTS_DEBUG(ctx->opts)) {
log_dbg_printf("Main event loop stopped.\n");
}
}
/*
* Break the loop of the proxy, causing the proxy_run to return.
*/
void
proxy_loopbreak(proxy_ctx_t *ctx)
{
event_base_loopbreak(ctx->evbase);
}
/*
* Free the proxy data structures.
*/
void
proxy_free(proxy_ctx_t *ctx)
{
if (ctx->gcev) {
event_free(ctx->gcev);
}
if (ctx->lctx) {
proxy_listener_ctx_free(ctx->lctx);
}
for (size_t i = 0; i < (sizeof(ctx->sev) / sizeof(ctx->sev[0])); i++) {
if (ctx->sev[i]) {
event_free(ctx->sev[i]);
}
}
if (ctx->thrmgr) {
pxy_thrmgr_free(ctx->thrmgr);
}
if (ctx->evbase) {
event_base_free(ctx->evbase);
}
free(ctx);
}
/* vim: set noet ft=c: */