Import sslsplit-devel changes

Add stats logs, initial
Add SSLproxy_SrcAddr header field
Clean-up
pull/13/head
Soner Tari 7 years ago
parent 3958adce9e
commit 67ddee1585

@ -15,6 +15,7 @@ patches or pull requests, in chronological order of their first contribution:
- Richard Poole ([RichardPoole42](https://github.com/RichardPoole42))
- Maciej Kotowicz ([mak](https://github.com/mak))
- Eun Soo Park ([eunsoopark](https://github.com/eunsoopark))
- Christian Groschupp ([cgroschupp](https://github.com/cgroschupp))
Many more individuals have contributed by reporting bugs or feature requests.
See [issue tracker on Github][1], `NEWS.md` and `git log` for details.

@ -18,8 +18,8 @@
# PREFIX Prefix to install under (default /usr/local)
# DESTDIR Destination root under which prefix is located (default /)
# MANDIR Subdir of PREFIX that contains man section dirs
# INSTALLUID UID to use for installed files
# INSTALLGID GID to use for installed files
# INSTALLUID UID to use for installed files if installing as root
# INSTALLGID GID to use for installed files if installing as root
#
# Standard compiler variables are respected, e.g.:
#
@ -71,6 +71,9 @@ FEATURES+= -DDEBUG_PROXY
# Define to add thread debugging; dump thread state when choosing a thread.
FEATURES+= -DDEBUG_THREAD
# Define to add privilege separation server event loop debugging.
#FEATURES+= -DDEBUG_PRIVSEP_SERVER
# Define to add features specific to OpenBSD only, such as uuid_create().
FEATURES+= -DOPENBSD
@ -153,12 +156,20 @@ BINMODE?= 0755
MANUID?= $(INSTALLUID)
MANGID?= $(INSTALLGID)
MANMODE?= 0644
ifeq ($(shell id -u),0)
BINOWNERFLAGS?= -o $(BINUID) -g $(BINGID)
MANOWNERFLAGS?= -o $(MANUID) -g $(MANGID)
else
BINOWNERFLAGS?=
MANOWNERFLAGS?=
endif
OPENSSL?= openssl
PKGCONFIG?= pkg-config
BASENAME?= basename
CAT?= cat
CHECKNR?= checknr
CUT?= cut
GREP?= grep
INSTALL?= install
@ -350,6 +361,11 @@ CFLAGS+= -pthread
LDFLAGS+= -pthread
endif
# _FORTIFY_SOURCE requires -O on Linux
ifeq (,$(findstring -O,$(CFLAGS)))
CFLAGS+= -O
endif
export VERSION
export OPENSSL
export MKDIR
@ -421,9 +437,9 @@ install: $(TARGET)
test -d $(DESTDIR)$(PREFIX)/bin || $(MKDIR) -p $(DESTDIR)$(PREFIX)/bin
test -d $(DESTDIR)$(PREFIX)/$(MANDIR)/man1 || \
$(MKDIR) -p $(DESTDIR)$(PREFIX)/$(MANDIR)/man1
$(INSTALL) -o $(BINUID) -g $(BINGID) -m $(BINMODE) \
$(INSTALL) $(BINOWNERFLAGS) -m $(BINMODE) \
$(TARGET) $(DESTDIR)$(PREFIX)/bin/
$(INSTALL) -o $(MANUID) -g $(MANGID) -m $(MANMODE) \
$(INSTALL) $(MANOWNERFLAGS) -m $(MANMODE) \
$(TARGET).1 $(DESTDIR)$(PREFIX)/$(MANDIR)/man1/
deinstall:
@ -433,7 +449,10 @@ ifdef GITDIR
lint:
$(CPPCHECK) $(CPPCHECKFLAGS) --force --enable=all --error-exitcode=1 .
mantest:
manlint: $(TARGET).1
$(CHECKNR) $(TARGET).1
mantest: $(TARGET).1
$(RM) -f man1
$(LN) -sf . man1
$(MAN) -M . 1 $(TARGET)
@ -485,6 +504,6 @@ endif
FORCE:
.PHONY: all config clean test lint install deinstall \
.PHONY: all config clean test travis lint install deinstall manlint \
mantest man manclean fetchdeps dist disttest distclean realclean

@ -14,7 +14,7 @@ information will allow faster analysis of the problem:
- Output of `sslsplit -V`
- Output of `uname -a`
- Exact command line arguments used to run SSLsplit
- Relevant part of debug mode (`-d`) output, if applicable
- Relevant part of debug mode (`-D`) output, if applicable
- The NAT redirection rules you are using, if applicable
- For build problems, the full output of `make`

@ -1,4 +1,26 @@
### SSLsplit develop
- No longer assume an out of memory condition when a certificate contains
neither a CN nor a subjectAltName extension.
- Extend -L content logging with EOF message to allow log parsers to figure
out when a connection ends (issue #128 by @mattes). Note that log parsers
need to be adjusted to handle the new EOF message.
- Add missing authors Maciej Kotowicz and Eun Soo Park to manual page.
- Fix multiple signal handling issues in the privilege separation parent
which led to the parent process being killed ungracefully (SIGTERM) or
being stuck in wait() while still having signals (SIGQUIT etc) queued up
for forwarding to the child process (issue #137).
- Fix SSL connections that result from autossl to shutdown cleanly.
- Fix data processing when EOF is received before all incoming data has been
processed.
- Fix parallel make build (-j) for the test target (issue #140).
- Do not set owner and group if install target is called by unprivileged
user (pull req #141 by @cgroschupp).
- Add XNU headers for Mac OS X 10.11.3, 10.11.4, 10.11.5, 10.11.6 and 10.12.
- Minor bugfixes and improvements.
### SSLsplit 0.5.0 2016-03-27
- Generically support STARTTLS through the new autossl proxyspec type that
@ -34,9 +56,8 @@
after throttling one direction (issue #109).
- Fix build with LibreSSL that lacks recent OpenSSL API additions.
- Fix build with OpenSSL versions that had SSLv3 support removed.
- Fix a rare segmentation fault upon receiving EOF on the inbound connection
while the outbound connection has not been established yet (patch by
@eunsoopark, issue #124).
- Fix a rare segmentation fault upon receiving EOF on the outbound connection
while it has not been established yet (patch by @eunsoopark, issue #124).
- Fix SSL sessions to actually time out (patch by @eunsoopark, issue #115).
- Fix passthrough mode with -t and an empty directory (issue #92).
- Minor bugfixes and improvements.

@ -34,10 +34,10 @@ certificate verification vulnerabilities in SSL/TLS stacks.
SSLsplit implements a number of defences against mechanisms which would
normally prevent MitM attacks or make them more difficult. SSLsplit can deny
OCSP requests in a generic way. For HTTP and HTTPS connections, SSLsplit
removes response headers for HPKP in order to prevent public key pinning, for
HSTS to allow the user to accept untrusted certificates, and Alternate
Protocols to prevent switching to QUIC/SPDY. HTTP compression, encodings and
keep-alive are disabled to make the logs more readable.
removes response headers for HPKP in order to prevent server-instructed public
key pinning, for HSTS to avoid the strict transport security restrictions, and
Alternate Protocols to prevent switching to QUIC/SPDY. HTTP compression,
encodings and keep-alive are disabled to make the logs more readable.
As an experimental feature, SSLsplit supports STARTTLS and similar mechanisms,
where a protocol starts on a plain text TCP connection and is later upgraded to

@ -47,20 +47,22 @@ cache_new(cache_init_cb_t init_cb)
if (!(cache = malloc(sizeof(cache_t))))
return NULL;
init_cb(cache);
pthread_mutex_init(&cache->mutex, NULL);
if (pthread_mutex_init(&cache->mutex, NULL)) {
free(cache);
return NULL;
}
init_cb(cache);
return cache;
}
/*
* Reinitialize cache after fork().
* Reinitialize cache after fork(). Returns 0 on success, -1 on failure.
*/
void
int
cache_reinit(cache_t *cache)
{
pthread_mutex_init(&cache->mutex, NULL);
return pthread_mutex_init(&cache->mutex, NULL) ? -1 : 0;
}
/*

@ -71,7 +71,7 @@ typedef struct cache {
typedef void (*cache_init_cb_t)(struct cache *);
cache_t * cache_new(cache_init_cb_t) MALLOC;
void cache_reinit(cache_t *) NONNULL(1);
int cache_reinit(cache_t *) NONNULL(1) WUNRES;
void cache_free(cache_t *) NONNULL(1);
void cache_gc(cache_t *) NONNULL(1);
cache_val_t cache_get(cache_t *, cache_key_t) NONNULL(1) WUNRES;

@ -90,10 +90,14 @@ out4:
int
cachemgr_init(void)
{
cache_reinit(cachemgr_fkcrt);
cache_reinit(cachemgr_tgcrt);
cache_reinit(cachemgr_ssess);
cache_reinit(cachemgr_dsess);
if (cache_reinit(cachemgr_fkcrt))
return -1;
if (cache_reinit(cachemgr_tgcrt))
return -1;
if (cache_reinit(cachemgr_ssess))
return -1;
if (cache_reinit(cachemgr_dsess))
return -1;
return 0;
}

@ -43,8 +43,11 @@ cert_new(void)
if (!(c = malloc(sizeof(cert_t))))
return NULL;
memset(c, 0, sizeof(cert_t));
if (pthread_mutex_init(&c->mutex, NULL)) {
free(c);
return NULL;
}
c->references = 1;
pthread_mutex_init(&c->mutex, NULL);
return c;
}
@ -59,11 +62,14 @@ cert_new3(EVP_PKEY *key, X509 *crt, STACK_OF(X509) *chain)
if (!(c = malloc(sizeof(cert_t))))
return NULL;
if (pthread_mutex_init(&c->mutex, NULL)) {
free(c);
return NULL;
}
c->key = key;
c->crt = crt;
c->chain = chain;
c->references = 1;
pthread_mutex_init(&c->mutex, NULL);
return c;
}
@ -78,6 +84,10 @@ cert_new3_copy(EVP_PKEY *key, X509 *crt, STACK_OF(X509) *chain)
if (!(c = malloc(sizeof(cert_t))))
return NULL;
if (pthread_mutex_init(&c->mutex, NULL)) {
free(c);
return NULL;
}
c->key = key;
ssl_key_refcount_inc(c->key);
c->crt = crt;
@ -87,7 +97,6 @@ cert_new3_copy(EVP_PKEY *key, X509 *crt, STACK_OF(X509) *chain)
ssl_x509_refcount_inc(sk_X509_value(c->chain, i));
}
c->references = 1;
pthread_mutex_init(&c->mutex, NULL);
return c;
}
@ -102,6 +111,10 @@ cert_new_load(const char *filename)
if (!(c = malloc(sizeof(cert_t))))
return NULL;
memset(c, 0, sizeof(cert_t));
if (pthread_mutex_init(&c->mutex, NULL)) {
free(c);
return NULL;
}
if (ssl_x509chain_load(&c->crt, &c->chain, filename) == -1) {
free(c);
@ -117,7 +130,6 @@ cert_new_load(const char *filename)
return NULL;
}
c->references = 1;
pthread_mutex_init(&c->mutex, NULL);
return c;
}

@ -172,16 +172,21 @@ class NetworkStack():
tm = parse_timestamp(logentry['timestamp'])
conn5tuple = self._make5tuple(logentry)
if not conn5tuple in self.connstate:
self.connstate[conn5tuple] = NetworkStack.ConnState(logentry, tm,
self)
self.connstate[conn5tuple].syn()
if logentry['eof']:
if conn5tuple in self.connstate:
self.connstate[conn5tuple].fin()
del self.connstate[conn5tuple]
else:
self.connstate[conn5tuple].touch(tm)
self.connstate[conn5tuple].data(logentry)
if not conn5tuple in self.connstate:
self.connstate[conn5tuple] = NetworkStack.ConnState(logentry,
tm,
self)
self.connstate[conn5tuple].syn()
else:
self.connstate[conn5tuple].touch(tm)
self.connstate[conn5tuple].data(logentry)
# at most very 60 seconds, time out old connections (doesn't scale!)
# at most every 60s, time out old connections (should not happen)
if tm > self.last_timeout_tm + datetime.timedelta(0, 1, 0):
for conn in self.connstate:
if self.last_timeout_tm > self.connstate[conn5tuple].tm + \

@ -64,7 +64,7 @@ class LogSyntaxError(Exception):
def parse_header(line):
"""Parse the header line into a dict with useful fields"""
# 2015-09-27 14:55:41 UTC [192.0.2.1]:56721 -> [192.0.2.2]:443 (37):
m = re.match(r'(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} \S+) \[(.+?)\]:(\d+) -> \[(.+?)\]:(\d+) \((\d+)\):', line)
m = re.match(r'(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} \S+) \[(.+?)\]:(\d+) -> \[(.+?)\]:(\d+) \((\d+|EOF)\):?', line)
if not m:
raise LogSyntaxError(line)
res = {}
@ -73,7 +73,11 @@ def parse_header(line):
res['src_port'] = int(m.group(3))
res['dst_addr'] = m.group(4)
res['dst_port'] = int(m.group(5))
res['size'] = int(m.group(6))
if m.group(6) == 'EOF':
res['eof'] = True
else:
res['eof'] = False
res['size'] = int(m.group(6))
return res
def parse_log(f):
@ -83,7 +87,8 @@ def parse_log(f):
if not line:
break
res = parse_header(line)
res['data'] = read_count(f, res['size'])
if (not res['eof']):
res['data'] = read_count(f, res['size'])
yield res
if __name__ == '__main__':

@ -111,7 +111,7 @@ targets/wildcard.roe.ch.pem: rsa.crt
targets/wildcard.roe.ch.crt
# localhost network connectivity is required
session.pem:
session.pem: server.pem
openssl s_server -accept 46143 -cert server.pem -quiet -no_ssl2 & \
pid=$$! ; \
sleep 1 ; \

134
log.c

@ -57,11 +57,12 @@
*/
static proxy_ctx_t *proxy_ctx = NULL;
static void
void
log_exceptcb(void)
{
if (proxy_ctx) {
proxy_loopbreak(proxy_ctx);
proxy_ctx = NULL;
}
}
@ -269,6 +270,84 @@ log_connect_fini(void)
close(connect_fd);
}
/*
* Stats log. Logs to a file-based connection log.
* Uses a logger thread.
*/
logger_t *stats_log = NULL;
static int stats_fd = -1;
static char *stats_fn = NULL;
static int
log_stats_preinit(const char *logfile)
{
stats_fd = open(logfile, O_WRONLY|O_APPEND|O_CREAT, DFLT_FILEMODE);
if (stats_fd == -1) {
log_err_printf("Failed to open '%s' for writing: %s (%i)\n",
logfile, strerror(errno), errno);
return -1;
}
if (!(stats_fn = realpath(logfile, NULL))) {
log_err_printf("Failed to realpath '%s': %s (%i)\n",
logfile, strerror(errno), errno);
close(stats_fd);
stats_fd = -1;
return -1;
}
return 0;
}
static int
log_stats_reopencb(void)
{
close(stats_fd);
stats_fd = open(stats_fn, O_WRONLY|O_APPEND|O_CREAT, DFLT_FILEMODE);
if (stats_fd == -1) {
log_err_printf("Failed to open '%s' for writing: %s\n",
stats_fn, strerror(errno));
free(stats_fn);
stats_fn = NULL;
return -1;
}
return 0;
}
/*
* Do the actual write to the open connection log file descriptor.
* We prepend a timestamp here, which means that timestamps are slightly
* delayed from the time of actual logging. Since we only have second
* resolution that should not make any difference.
*/
static ssize_t
log_stats_writecb(UNUSED void *fh, const void *buf, size_t sz)
{
char timebuf[32];
time_t epoch;
struct tm *utc;
size_t n;
time(&epoch);
utc = gmtime(&epoch);
n = strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S UTC ", utc);
if (n == 0) {
log_err_printf("Error from strftime(): buffer too small\n");
return -1;
}
if ((write(stats_fd, timebuf, n) == -1) ||
(write(stats_fd, buf, sz) == -1)) {
log_err_printf("Warning: Failed to write to stats log: %s\n",
strerror(errno));
return -1;
}
return sz;
}
static void
log_stats_fini(void)
{
close(stats_fd);
}
/*
* Content log.
@ -281,6 +360,7 @@ log_connect_fini(void)
*/
#define PREPFLAG_REQUEST 1
#define PREPFLAG_EOF 2
struct log_content_ctx {
unsigned int open : 1;
@ -640,15 +720,23 @@ log_content_submit(log_content_ctx_t *ctx, logbuf_t *lb, int is_request)
}
int
log_content_close(log_content_ctx_t **pctx)
log_content_close(log_content_ctx_t **pctx, int by_requestor)
{
int rv = 0;
unsigned long prepflags = PREPFLAG_EOF;
if (!(*pctx) || !(*pctx)->open)
return -1;
if (by_requestor)
prepflags |= PREPFLAG_REQUEST;
if (logger_submit(content_log, (*pctx), prepflags, NULL) == -1) {
rv = -1;
goto out;
}
if (logger_close(content_log, *pctx) == -1) {
rv = -1;
}
out:
*pctx = NULL;
return rv;
}
@ -840,8 +928,13 @@ log_content_file_prepcb(void *fh, unsigned long prepflags, logbuf_t *lb)
: ctx->u.file.header_resp))
goto out;
/* prepend size tag and newline */
head = logbuf_new_printf(lb->fh, lb, " (%zu):\n", logbuf_size(lb));
/* prepend size tag or EOF, and newline */
if (prepflags & PREPFLAG_EOF) {
head = logbuf_new_printf(NULL, NULL, " (EOF)\n");
} else {
head = logbuf_new_printf(lb->fh, lb, " (%zu):\n",
logbuf_size(lb));
}
if (!head) {
log_err_printf("Failed to allocate memory\n");
logbuf_free(lb);
@ -987,6 +1080,17 @@ log_preinit(opts_t *opts)
goto out;
}
}
if (opts->statslog) {
if (log_stats_preinit(opts->statslog) == -1)
goto out;
if (!(stats_log = logger_new(log_stats_reopencb,
NULL, NULL,
log_stats_writecb, NULL,
log_exceptcb))) {
log_stats_fini();
goto out;
}
}
if (opts->certgendir) {
if (!(cert_log = logger_new(NULL, NULL, NULL, log_cert_writecb,
NULL, log_exceptcb)))
@ -1006,6 +1110,10 @@ out:
log_connect_fini();
logger_free(connect_log);
}
if (stats_log) {
log_stats_fini();
logger_free(stats_log);
}
if (cert_log) {
logger_free(cert_log);
}
@ -1027,6 +1135,10 @@ log_preinit_undo(void)
log_connect_fini();
logger_free(connect_log);
}
if (stats_log) {
log_stats_fini();
logger_free(stats_log);
}
}
/*
@ -1046,6 +1158,9 @@ log_init(opts_t *opts, proxy_ctx_t *ctx, int clisock1, int clisock2)
if (connect_log)
if (logger_start(connect_log) == -1)
return -1;
if (stats_log)
if (logger_start(stats_log) == -1)
return -1;
if (content_log) {
content_clisock = clisock1;
if (logger_start(content_log) == -1)
@ -1080,6 +1195,8 @@ log_fini(void)
logger_leave(content_log);
if (connect_log)
logger_leave(connect_log);
if (stats_log)
logger_leave(stats_log);
if (err_log)
logger_leave(err_log);
@ -1089,6 +1206,8 @@ log_fini(void)
logger_join(content_log);
if (connect_log)
logger_join(connect_log);
if (stats_log)
logger_join(stats_log);
if (err_log)
logger_join(err_log);
@ -1098,6 +1217,8 @@ log_fini(void)
logger_free(content_log);
if (connect_log)
logger_free(connect_log);
if (stats_log)
logger_free(stats_log);
if (err_log)
logger_free(err_log);
@ -1105,6 +1226,8 @@ log_fini(void)
log_content_file_fini();
if (connect_log)
log_connect_fini();
if (stats_log)
log_stats_fini();
if (cert_clisock != -1)
privsep_client_close(cert_clisock);
@ -1123,6 +1246,9 @@ log_reopen(void)
if (connect_log)
if (logger_reopen(connect_log) == -1)
rv = -1;
if (stats_log)
if (logger_reopen(stats_log) == -1)
rv = -1;
return rv;
}

@ -61,12 +61,16 @@ extern logger_t *connect_log;
#define log_connect_write_free(buf, sz) \
logger_write_freebuf(connect_log, NULL, 0, (buf), (sz))
extern logger_t *stats_log;
#define log_stats_print_free(s) \
logger_print_freebuf(stats_log, NULL, 0, (s))
typedef struct log_content_ctx log_content_ctx_t;
int log_content_open(log_content_ctx_t **, opts_t *, char *, char *, char *,
char *, char *, char *, char *) NONNULL(1,2,3) WUNRES;
int log_content_submit(log_content_ctx_t *, logbuf_t *, int)
NONNULL(1,2) WUNRES;
int log_content_close(log_content_ctx_t **) NONNULL(1) WUNRES;
int log_content_close(log_content_ctx_t **, int) NONNULL(1) WUNRES;
int log_content_split_pathspec(const char *, char **,
char **) NONNULL(1,2,3) WUNRES;
@ -77,6 +81,7 @@ void log_preinit_undo(void);
int log_init(opts_t *, proxy_ctx_t *, int, int) NONNULL(1,2) WUNRES;
void log_fini(void);
int log_reopen(void) WUNRES;
void log_exceptcb(void);
#endif /* !LOG_H */

@ -105,7 +105,8 @@ logger_free(logger_t *logger) {
* Submit a buffer to be logged by the logger thread.
* Calls the prep callback from within the calling tread before submission.
* Buffer guaranteed to be freed after logging completes or on failure.
* Returns -1 on error, 0 on success.
* Returns -1 on error, 0 on success (including logging a NULL logbuf, which
* is a no-op).
*/
int
logger_submit(logger_t *logger, void *fh, unsigned long prepflags,
@ -114,7 +115,7 @@ logger_submit(logger_t *logger, void *fh, unsigned long prepflags,
if (logger->prep)
lb = logger->prep(fh, prepflags, lb);
if (!lb)
return -1;
return 0;
lb->fh = fh;
logbuf_ctl_clear(lb);
if (thrqueue_enqueue(logger->queue, lb)) {
@ -313,7 +314,7 @@ logger_print(logger_t *logger, void *fh, unsigned long prepflags,
{
logbuf_t *lb;
if (!(lb = logbuf_new_copy(s, s ? strlen(s) : 0, fh, NULL)))
if (!(lb = logbuf_new_copy(s, strlen(s), fh, NULL)))
return -1;
return logger_submit(logger, fh, prepflags, lb);
}
@ -333,7 +334,7 @@ logger_print_freebuf(logger_t *logger, void *fh, unsigned long prepflags,
{
logbuf_t *lb;
if (!(lb = logbuf_new(s, s ? strlen(s) : 0, fh, NULL)))
if (!(lb = logbuf_new(s, strlen(s), fh, NULL)))
return -1;
return logger_submit(logger, fh, prepflags, lb);
}

@ -55,7 +55,7 @@ int logger_reopen(logger_t *) NONNULL(1) WUNRES;
int logger_open(logger_t *, void *) NONNULL(1,2) WUNRES;
int logger_close(logger_t *, void *) NONNULL(1,2) WUNRES;
int logger_submit(logger_t *, void *, unsigned long,
logbuf_t *) NONNULL(1,4) WUNRES;
logbuf_t *) NONNULL(1) WUNRES;
int logger_printf(logger_t *, void *, unsigned long,
const char *, ...) PRINTF(4,5) NONNULL(1,4) WUNRES;
int logger_print(logger_t *, void *, unsigned long,

@ -215,8 +215,9 @@ main_usage(void)
/*
* Callback to load a cert/chain/key combo from a single PEM file.
* A return value of -1 indicates a fatal error to the file walker.
*/
static void
static int
main_loadtgcrt(const char *filename, void *arg)
{
opts_t *opts = arg;
@ -227,15 +228,13 @@ main_loadtgcrt(const char *filename, void *arg)
if (!cert) {
log_err_printf("Failed to load cert and key from PEM file "
"'%s'\n", filename);
log_fini();
exit(EXIT_FAILURE);
return -1;
}
if (X509_check_private_key(cert->crt, cert->key) != 1) {
log_err_printf("Cert does not match key in PEM file "
"'%s':\n", filename);
ERR_print_errors_fp(stderr);
log_fini();
exit(EXIT_FAILURE);
return -1;
}
#ifdef DEBUG_CERTIFICATE
@ -265,6 +264,7 @@ main_loadtgcrt(const char *filename, void *arg)
}
free(names);
cert_free(cert);
return 0;
}
/*
@ -303,7 +303,7 @@ main(int argc, char *argv[])
}
while ((ch = getopt(argc, argv, OPT_g OPT_G OPT_Z OPT_i "k:c:C:K:t:"
"OPs:r:R:e:Eu:m:j:p:l:L:S:F:dD::VhW:w:")) != -1) {
"OPs:r:R:e:Eu:m:j:p:l:L:S:F:dD::VhW:w:I:")) != -1) {
switch (ch) {
case 'c':
if (opts->cacrt)
@ -544,6 +544,13 @@ main(int argc, char *argv[])
if (!opts->connectlog)
oom_die(argv0);
break;
case 'I':
if (opts->statslog)
free(opts->statslog);
opts->statslog = strdup(optarg);
if (!opts->statslog)
oom_die(argv0);
break;
case 'L':
if (opts->contentlog)
free(opts->contentlog);
@ -711,9 +718,7 @@ main(int argc, char *argv[])
fprintf(stderr, "%s: no proxyspec specified.\n", argv0);
exit(EXIT_FAILURE);
}
log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>>>> Enter spec for loop\n");
for (proxyspec_t *spec = opts->spec; spec; spec = spec->next) {
log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>>>> spec for loop: %s\n", spec->natengine);
if (spec->connect_addrlen || spec->sni_port)
continue;
if (!spec->natengine) {
@ -729,7 +734,6 @@ main(int argc, char *argv[])
argv0, spec->natengine);
exit(EXIT_FAILURE);
}
log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>>>> nat_getlookupcb: %s\n", spec->natengine);
spec->natlookup = nat_getlookupcb(spec->natengine);
spec->natsocket = nat_getsocketcb(spec->natengine);
}
@ -905,7 +909,12 @@ main(int argc, char *argv[])
/* Load certs before dropping privs but after cachemgr_preinit() */
if (opts->tgcrtdir) {
sys_dir_eachfile(opts->tgcrtdir, main_loadtgcrt, opts);
if (sys_dir_eachfile(opts->tgcrtdir,
main_loadtgcrt, opts) == -1) {
fprintf(stderr, "%s: failed to load certs from %s\n",
argv0, opts->tgcrtdir);
exit(EXIT_FAILURE);
}
}
/* Detach from tty; from this point on, only canonicalized absolute
@ -962,7 +971,10 @@ main(int argc, char *argv[])
strerror(errno), errno);
exit(EXIT_FAILURE);
}
ssl_reinit();
if (ssl_reinit() == -1) {
fprintf(stderr, "%s: failed to reinit SSL\n", argv0);
goto out_sslreinit_failed;
}
/* Post-privdrop/chroot/detach initialization, thread spawning */
if (log_init(opts, proxy, clisock[1], clisock[2]) == -1) {
@ -982,7 +994,6 @@ main(int argc, char *argv[])
log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>>>> Enter proxy_run\n");
proxy_run(proxy);
log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>>>> Exit proxy_run\n");
log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>>>> main: EXIT closing privsep clisock=%d\n", clisock[0]);
privsep_client_close(clisock[0]);
@ -993,6 +1004,7 @@ out_nat_failed:
cachemgr_fini();
out_cachemgr_failed:
log_fini();
out_sslreinit_failed:
out_log_failed:
out_parent:
opts_free(opts);

@ -26,7 +26,6 @@
*/
#include "nat.h"
#include "sys.h"
#include "log.h"
#include "attrib.h"
@ -43,7 +42,6 @@
#include <sys/fcntl.h>
#include <net/if.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#ifdef __APPLE__
#define PRIVATE
#endif /* __APPLE__ */

@ -101,6 +101,9 @@ opts_free(opts_t *opts)
if (opts->connectlog) {
free(opts->connectlog);
}
if (opts->statslog) {
free(opts->statslog);
}
if (opts->contentlog) {
free(opts->contentlog);
}
@ -295,7 +298,7 @@ proxyspec_parse(int *argc, char **argv[], const char *natengine)
switch (state) {
default:
case 0:
/* tcp | ssl | http | https */
/* tcp | ssl | http | https | autossl */
curspec = malloc(sizeof(proxyspec_t));
memset(curspec, 0, sizeof(proxyspec_t));
curspec->next = spec;
@ -333,7 +336,7 @@ proxyspec_parse(int *argc, char **argv[], const char *natengine)
break;
case 1:
/* listenaddr */
addr = **argv;
addr = **argv;
state++;
break;
case 2:

@ -97,6 +97,7 @@ typedef struct opts {
char *jaildir;
char *pidfile;
char *connectlog;
char *statslog;
char *contentlog;
char *contentlog_basedir; /* static part of logspec, for privsep srv */
CONST_SSL_METHOD *(*sslmethod)(void);

@ -71,11 +71,15 @@ static char *argv11[] = {
};
static char *argv12[] = {
"autossl", "127.0.0.1", "10025", "127.0.0.2", "25",
"https", "127.0.0.1", "10443", "127.0.0.2", "443",
"https", "127.0.0.1", "10443", "127.0.0.2", "443"
};
static char *argv13[] = {
"autossl", "127.0.0.1", "10025", "sni", "25"
};
static char *argv14[] = {
"https", "127.0.0.1", "10443",
"autossl", "127.0.0.1", "10025", "127.0.0.2", "25"
};
#define NATENGINE "pf"
@ -453,6 +457,40 @@ START_TEST(proxyspec_parse_17)
}
END_TEST
START_TEST(proxyspec_parse_18)
{
proxyspec_t *spec;
int argc = 8;
char **argv = argv14;
spec = proxyspec_parse(&argc, &argv, NATENGINE);
fail_unless(!!spec, "failed to parse spec");
fail_unless(!spec->ssl, "SSL");
fail_unless(!spec->http, "HTTP");
fail_unless(spec->upgrade, "not Upgrade");
fail_unless(spec->listen_addrlen == sizeof(struct sockaddr_in),
"not IPv4 listen addr");
fail_unless(spec->connect_addrlen == sizeof(struct sockaddr_in),
"not IPv4 connect addr");
fail_unless(!spec->sni_port, "SNI port is set");
fail_unless(!spec->natengine, "natengine is set");
fail_unless(!spec->natlookup, "natlookup() is set");
fail_unless(!spec->natsocket, "natsocket() is set");
fail_unless(!!spec->next, "next is not set");
fail_unless(spec->next->ssl, "not SSL");
fail_unless(spec->next->http, "not HTTP");
fail_unless(!spec->next->upgrade, "Upgrade");
fail_unless(spec->next->listen_addrlen == sizeof(struct sockaddr_in),
"not IPv4 listen addr");
fail_unless(!spec->next->connect_addrlen, "connect addr set");
fail_unless(!spec->next->sni_port, "SNI port is set");
fail_unless(!!spec->next->natengine, "natengine is not set");
fail_unless(!spec->next->natlookup, "natlookup() is set");
fail_unless(!spec->next->natsocket, "natsocket() is set");
proxyspec_free(spec);
}
END_TEST
START_TEST(opts_debug_01)
{
opts_t *opts;
@ -497,6 +535,7 @@ opts_suite(void)
tcase_add_test(tc, proxyspec_parse_15);
tcase_add_test(tc, proxyspec_parse_16);
tcase_add_exit_test(tc, proxyspec_parse_17, EXIT_FAILURE);
tcase_add_test(tc, proxyspec_parse_18);
suite_add_tcase(s, tc);
tc = tcase_create("opts_debug");

@ -73,18 +73,24 @@
#define PRIVSEP_ANS_SYS_ERR 4 /* system error; arg=errno */
/* communication with signal handler */
static int received_sighup;
static int received_sigint;
static int received_sigquit;
static int received_sigchld;
static int received_sigusr1;
static int selfpipe_wrfd; /* write end of pipe used for unblocking select */
static volatile sig_atomic_t received_sighup;
static volatile sig_atomic_t received_sigint;
static volatile sig_atomic_t received_sigquit;
static volatile sig_atomic_t received_sigterm;
static volatile sig_atomic_t received_sigchld;
static volatile sig_atomic_t received_sigusr1;
/* write end of pipe used for unblocking select */
static volatile sig_atomic_t selfpipe_wrfd;
static void
privsep_server_signal_handler(int sig)
{
int saved_errno;
#ifdef DEBUG_PRIVSEP_SERVER
log_dbg_printf("privsep_server_signal_handler\n");
#endif /* DEBUG_PRIVSEP_SERVER */
saved_errno = errno;
switch (sig) {
case SIGHUP:
@ -96,6 +102,9 @@ privsep_server_signal_handler(int sig)
case SIGQUIT:
received_sigquit = 1;
break;
case SIGTERM:
received_sigterm = 1;
break;
case SIGCHLD:
received_sigchld = 1;
break;
@ -106,6 +115,9 @@ privsep_server_signal_handler(int sig)
if (selfpipe_wrfd != -1) {
ssize_t n;
#ifdef DEBUG_PRIVSEP_SERVER
log_dbg_printf("writing to selfpipe_wrfd %i\n", selfpipe_wrfd);
#endif /* DEBUG_PRIVSEP_SERVER */
do {
n = write(selfpipe_wrfd, "!", 1);
} while (n == -1 && errno == EINTR);
@ -114,6 +126,10 @@ privsep_server_signal_handler(int sig)
"%s (%i)\n", strerror(errno), errno);
/* ignore error */
}
#ifdef DEBUG_PRIVSEP_SERVER
} else {
log_dbg_printf("selfpipe_wrfd is %i - not writing\n", selfpipe_wrfd);
#endif /* DEBUG_PRIVSEP_SERVER */
}
errno = saved_errno;
}
@ -601,6 +617,9 @@ privsep_server(opts_t *opts, int sigpipe, int srvsock[], size_t nsrvsock,
fd_set readfds;
int maxfd, rv;
#ifdef DEBUG_PRIVSEP_SERVER
log_dbg_printf("privsep_server select()\n");
#endif /* DEBUG_PRIVSEP_SERVER */
do {
FD_ZERO(&readfds);
FD_SET(sigpipe, &readfds);
@ -612,12 +631,18 @@ privsep_server(opts_t *opts, int sigpipe, int srvsock[], size_t nsrvsock,
}
}
rv = select(maxfd + 1, &readfds, NULL, NULL, NULL);
#ifdef DEBUG_PRIVSEP_SERVER
log_dbg_printf("privsep_server woke up (1)\n");
#endif /* DEBUG_PRIVSEP_SERVER */
} while (rv == -1 && errno == EINTR);
if (rv == -1) {
log_err_printf("Select failed: %s (%i)\n",
strerror(errno), errno);
return -1;
}
#ifdef DEBUG_PRIVSEP_SERVER
log_dbg_printf("privsep_server woke up (2)\n");
#endif /* DEBUG_PRIVSEP_SERVER */
if (FD_ISSET(sigpipe, &readfds)) {
char buf[16];
@ -625,22 +650,54 @@ privsep_server(opts_t *opts, int sigpipe, int srvsock[], size_t nsrvsock,
* all the individual signal flags */
read(sigpipe, buf, sizeof(buf));
if (received_sigquit) {
kill(childpid, SIGQUIT);
if (kill(childpid, SIGQUIT) == -1) {
log_err_printf("kill(%i,SIGQUIT) "
"failed: %s (%i)\n",
childpid,
strerror(errno), errno);
}
received_sigquit = 0;
}
if (received_sigterm) {
if (kill(childpid, SIGTERM) == -1) {
log_err_printf("kill(%i,SIGTERM) "
"failed: %s (%i)\n",
childpid,
strerror(errno), errno);
}
received_sigterm = 0;
}
if (received_sighup) {
kill(childpid, SIGHUP);
if (kill(childpid, SIGHUP) == -1) {
log_err_printf("kill(%i,SIGHUP) "
"failed: %s (%i)\n",
childpid,
strerror(errno), errno);
}
received_sighup = 0;
}
if (received_sigusr1) {
kill(childpid, SIGUSR1);
if (kill(childpid, SIGUSR1) == -1) {
log_err_printf("kill(%i,SIGUSR1) "
"failed: %s (%i)\n",
childpid,
strerror(errno), errno);
}
received_sigusr1 = 0;
}
if (received_sigint) {
/* if we don't detach from the TTY, the
* child process receives SIGINT directly */
if (opts->detach)
kill(childpid, SIGINT);
if (opts->detach) {
if (kill(childpid, SIGINT) == -1) {
log_err_printf("kill(%i,SIGINT"
") failed: "
"%s (%i)\n",
childpid,
strerror(errno),
errno);
}
}
received_sigint = 0;
}
if (received_sigchld) {
@ -665,17 +722,20 @@ privsep_server(opts_t *opts, int sigpipe, int srvsock[], size_t nsrvsock,
srvsock[i]);
return -1;
}
if (rv == 1)
if (rv == 1) {
#ifdef DEBUG_PRIVSEP_SERVER
log_dbg_printf("srveof[%zu]=1\n", i);
#endif /* DEBUG_PRIVSEP_SERVER */
srveof[i] = 1;
}
}
}
/* break if all server sockets received an EOF */
i = 0;
while (i < nsrvsock && srveof[i])
i++;
if (i == nsrvsock)
break;
/*
* We cannot exit as long as we need the signal handling,
* which is as long as the child process is running.
* The only way out of here is receiving SIGCHLD.
*/
}
return 0;
@ -992,6 +1052,11 @@ privsep_fork(opts_t *opts, int clisock[], size_t nclisock)
strerror(errno), errno);
return -1;
}
if (signal(SIGTERM, privsep_server_signal_handler) == SIG_ERR) {
log_err_printf("Failed to install SIGTERM handler: %s (%i)\n",
strerror(errno), errno);
return -1;
}
if (signal(SIGQUIT, privsep_server_signal_handler) == SIG_ERR) {
log_err_printf("Failed to install SIGQUIT handler: %s (%i)\n",
strerror(errno), errno);
@ -1020,6 +1085,9 @@ privsep_fork(opts_t *opts, int clisock[], size_t nclisock)
strerror(errno), errno);
/* fall through */
}
#ifdef DEBUG_PRIVSEP_SERVER
log_dbg_printf("privsep_server exited\n");
#endif /* DEBUG_PRIVSEP_SERVER */
for (size_t i = 0; i < nclisock; i++)
close(sockcliv[i][0]);

@ -34,7 +34,6 @@
#include "opts.h"
#include "log.h"
#include "attrib.h"
#include "sys.h"
#include <sys/types.h>
#include <sys/socket.h>
@ -52,11 +51,12 @@
#include <event2/buffer.h>
#include <event2/thread.h>
/*
* Proxy engine, built around libevent 2.x.
*/
static int signals[] = { SIGQUIT, SIGHUP, SIGINT, SIGPIPE, SIGUSR1 };
static int signals[] = { SIGTERM, SIGQUIT, SIGHUP, SIGINT, SIGPIPE, SIGUSR1 };
struct proxy_ctx {
pxy_thrmgr_ctx_t *thrmgr;
@ -98,23 +98,6 @@ proxy_listener_ctx_free(proxy_listener_ctx_t *ctx)
free(ctx);
}
/*
* Callback for error events on the socket listener bufferevent.
*/
// @todo Make this static?
//static void
void
proxy_listener_errorcb(struct evconnlistener *listener, UNUSED void *arg)
{
log_dbg_level_printf(LOG_DBG_MODE_FINE, ">############################# 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.
*/
@ -132,6 +115,21 @@ proxy_listener_acceptcb(UNUSED struct evconnlistener *listener,
log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>>>> !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! proxy_listener_acceptcb: fd=%d <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< EXIT\n", fd);
}
/*
* Callback for error events on the socket listener bufferevent.
*/
void
proxy_listener_errorcb(struct evconnlistener *listener, UNUSED void *arg)
{
log_dbg_level_printf(LOG_DBG_MODE_FINE, ">############################# 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);
}
/*
* Dump a description of an evbase to debugging code.
*/
@ -196,7 +194,7 @@ proxy_listener_setup(struct event_base *evbase, pxy_thrmgr_ctx_t *thrmgr,
}
/*
* Signal handler for SIGQUIT, SIGINT, SIGHUP, SIGPIPE and SIGUSR1.
* Signal handler for SIGTERM, SIGQUIT, SIGINT, SIGHUP, SIGPIPE and SIGUSR1.
*/
static void
proxy_signal_cb(evutil_socket_t fd, UNUSED short what, void *arg)
@ -208,6 +206,7 @@ proxy_signal_cb(evutil_socket_t fd, UNUSED short what, void *arg)
}
switch(fd) {
case SIGTERM:
case SIGQUIT:
case SIGINT:
case SIGHUP:

@ -31,7 +31,6 @@
#include "opts.h"
#include "attrib.h"
#include "pxythrmgr.h"
#include <pthread.h>
#include <uuid.h>
typedef struct proxy_ctx proxy_ctx_t;
@ -52,8 +51,7 @@ proxy_ctx_t * proxy_new(opts_t *, int) NONNULL(1) MALLOC;
void proxy_run(proxy_ctx_t *) NONNULL(1);
void proxy_loopbreak(proxy_ctx_t *) NONNULL(1);
void proxy_free(proxy_ctx_t *) NONNULL(1);
void
proxy_listener_errorcb(struct evconnlistener *listener, UNUSED void *ctx);
void proxy_listener_errorcb(struct evconnlistener *, UNUSED void *);
#endif /* !PROXY_H */

@ -111,6 +111,8 @@ typedef struct pxy_conn_lproc_desc {
#define SSLPROXY_ADDR_KEY "SSLproxy-Addr:"
#define SSLPROXY_ADDR_KEY_LEN strlen(SSLPROXY_ADDR_KEY)
#define SSLPROXY_SRCADDR_KEY "SSLproxy-SrcAddr:"
#define SSLPROXY_SRCADDR_KEY_LEN strlen(SSLPROXY_SRCADDR_KEY)
static pxy_conn_ctx_t * MALLOC NONNULL(2,3,4)
pxy_conn_ctx_new(evutil_socket_t fd,
@ -229,11 +231,6 @@ pxy_conn_ctx_free_child(pxy_conn_child_ctx_t *ctx)
if (ctx->usedcrtfpr) {
free(ctx->usedcrtfpr);
}
if (WANT_CONTENT_LOG(ctx->parent) && ctx->logctx) {
if (log_content_close(&ctx->logctx) == -1) {
log_err_printf("Warning: Content log close failed\n");
}
}
free(ctx);
}
@ -247,7 +244,7 @@ bufferevent_free_and_close_fd(struct bufferevent *bev, pxy_conn_ctx_t *ctx)
evutil_socket_t fd = bufferevent_getfd(bev);
SSL *ssl = NULL;
if (ctx->spec->ssl && !ctx->passthrough) {
if ((ctx->spec->ssl || ctx->clienthello_found) && !ctx->passthrough) {
ssl = bufferevent_openssl_get_ssl(bev); /* does not inc refc */
}
@ -353,7 +350,7 @@ pxy_conn_free_child(pxy_conn_child_ctx_t *ctx)
}
static void NONNULL(1)
pxy_conn_ctx_free(pxy_conn_ctx_t *ctx)
pxy_conn_ctx_free(pxy_conn_ctx_t *ctx, int by_requestor)
{
#ifdef DEBUG_PROXY
if (OPTS_DEBUG(ctx->opts)) {
@ -361,8 +358,12 @@ pxy_conn_ctx_free(pxy_conn_ctx_t *ctx)
(void*)ctx);
}
#endif /* DEBUG_PROXY */
if (WANT_CONTENT_LOG(ctx) && ctx->logctx) {
if (log_content_close(&ctx->logctx, by_requestor) == -1) {
log_err_printf("Warning: Content log close failed\n");
}
}
pxy_thrmgr_detach(ctx);
if (ctx->srchost_str) {
free(ctx->srchost_str);
}
@ -422,11 +423,6 @@ pxy_conn_ctx_free(pxy_conn_ctx_t *ctx)
if (ctx->ev) {
event_free(ctx->ev);
}
if (WANT_CONTENT_LOG(ctx) && ctx->logctx) {
if (log_content_close(&ctx->logctx) == -1) {
log_err_printf("Warning: Content log close failed\n");
}
}
if (ctx->uuid) {
free(ctx->uuid);
}
@ -436,6 +432,9 @@ pxy_conn_ctx_free(pxy_conn_ctx_t *ctx)
if (ctx->child_addr_str) {
free(ctx->child_addr_str);
}
if (ctx->src_addr_str) {
free(ctx->src_addr_str);
}
if (ctx->srv_dst_ssl_version) {
free(ctx->srv_dst_ssl_version);
}
@ -446,7 +445,7 @@ pxy_conn_ctx_free(pxy_conn_ctx_t *ctx)
}
void NONNULL(1)
pxy_conn_free(pxy_conn_ctx_t *ctx)
pxy_conn_free(pxy_conn_ctx_t *ctx, int by_requestor)
{
evutil_socket_t fd = ctx->fd;
evutil_socket_t child_fd = ctx->child_fd;
@ -496,7 +495,7 @@ pxy_conn_free(pxy_conn_ctx_t *ctx)
}
log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">############################# pxy_conn_free: FREEING ctx, fd=%d, child_fd=%d\n", fd, child_fd);
pxy_conn_ctx_free(ctx);
pxy_conn_ctx_free(ctx, by_requestor);
log_dbg_level_printf(LOG_DBG_MODE_FINER, ">############################# pxy_conn_free: EXIT, fd=%d, child_fd=%d\n", fd, child_fd);
}
@ -1345,7 +1344,7 @@ pxy_bufferevent_setup(pxy_conn_ctx_t *ctx, evutil_socket_t fd, SSL *ssl)
// @todo Use this functions amap
struct bufferevent *bev;
log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>>>> pxy_bufferevent_setup: ENTER fd=%d\n", (int)fd);
log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>>>> pxy_bufferevent_setup: ENTER fd=%d\n", fd);
if (ssl) {
log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>>>> pxy_bufferevent_setup: bufferevent_openssl_socket_new <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< SSL\n");
@ -1381,7 +1380,7 @@ pxy_bufferevent_setup(pxy_conn_ctx_t *ctx, evutil_socket_t fd, SSL *ssl)
(void*)bev);
}
#endif /* DEBUG_PROXY */
log_dbg_level_printf(LOG_DBG_MODE_FINER, ">>>>> pxy_bufferevent_setup: EXIT fd=%d, bev fd=%d\n", (int)fd, bufferevent_getfd(bev));
log_dbg_level_printf(LOG_DBG_MODE_FINER, ">>>>> pxy_bufferevent_setup: EXIT fd=%d, bev fd=%d\n", fd, bufferevent_getfd(bev));
return bev;
}
@ -1390,7 +1389,7 @@ pxy_bufferevent_setup_child(pxy_conn_child_ctx_t *ctx, evutil_socket_t fd, SSL *
{
struct bufferevent *bev;
log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>>>> pxy_bufferevent_setup_child: ENTER %d\n", (int)fd);
log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>>>> pxy_bufferevent_setup_child: ENTER, fd=%d\n", fd);
if (ssl) {
log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>>>> pxy_bufferevent_setup_child: bufferevent_openssl_socket_new <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< SSL child\n");
@ -1425,7 +1424,7 @@ pxy_bufferevent_setup_child(pxy_conn_child_ctx_t *ctx, evutil_socket_t fd, SSL *
(void*)bev);
}
#endif /* DEBUG_PROXY */
log_dbg_level_printf(LOG_DBG_MODE_FINER, ">>>>> pxy_bufferevent_setup_child: EXIT %d\n", (int)fd);
log_dbg_level_printf(LOG_DBG_MODE_FINER, ">>>>> pxy_bufferevent_setup_child: EXIT, fd=%d\n", fd);
return bev;
}
@ -1502,6 +1501,8 @@ pxy_http_reqhdr_filter_line(const char *line, pxy_conn_ctx_t *ctx, int child)
return NULL;
} else if (child && !strncasecmp(line, SSLPROXY_ADDR_KEY, SSLPROXY_ADDR_KEY_LEN)) {
return NULL;
} else if (child && !strncasecmp(line, SSLPROXY_SRCADDR_KEY, SSLPROXY_SRCADDR_KEY_LEN)) {
return NULL;
} else if (line[0] == '\0') {
ctx->seen_req_header = 1;
if (!ctx->sent_http_conn_close) {
@ -1760,16 +1761,17 @@ pxy_conn_autossl_peek_and_upgrade(pxy_conn_ctx_t *ctx)
}
ctx->srv_dst.bev = bufferevent_openssl_filter_new(
ctx->evbase, ctx->srv_dst.bev, ctx->srv_dst.ssl,
BUFFEREVENT_SSL_CONNECTING, 0);
BUFFEREVENT_SSL_CONNECTING,
BEV_OPT_DEFER_CALLBACKS);
bufferevent_setcb(ctx->srv_dst.bev, pxy_bev_readcb,
pxy_bev_writecb, pxy_bev_eventcb,
ctx);
log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>>>>----------------------- pxy_conn_autossl_peek_and_upgrade: bufferevent_enable\n");
bufferevent_enable(ctx->srv_dst.bev, EV_READ|EV_WRITE);
if(!ctx->srv_dst.bev) {
if (!ctx->srv_dst.bev) {
return 0;
}
if( OPTS_DEBUG(ctx->opts)) {
if (OPTS_DEBUG(ctx->opts)) {
log_err_printf("Replaced dst bufferevent, new "
"one is %p\n", (void *)ctx->srv_dst.bev);
}
@ -1866,6 +1868,8 @@ pxy_http_reqhdr_filter(struct evbuffer *inbuf, struct evbuffer *outbuf, struct b
inserted_sslproxy_addr = 1;
log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>>>> pxy_http_reqhdr_filter: src INSERT sslproxy_addr line, fd=%d: %s\n", ctx->fd, ctx->child_addr_str);
evbuffer_add_printf(outbuf, "%s\r\n", ctx->child_addr_str);
log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>>>> pxy_http_reqhdr_filter: src INSERT sslproxy_srcaddr line, fd=%d: %s\n", ctx->fd, ctx->src_addr_str);
evbuffer_add_printf(outbuf, "%s\r\n", ctx->src_addr_str);
}
if (ctx->seen_req_header) {
@ -1891,7 +1895,7 @@ pxy_http_reqhdr_filter(struct evbuffer *inbuf, struct evbuffer *outbuf, struct b
// @todo Fix this
/* out of memory condition? */
if (ctx->enomem) {
pxy_conn_free(parent);
pxy_conn_free(parent, (bev == ctx->src.bev));
goto leave;
}
@ -1972,7 +1976,7 @@ pxy_http_resphdr_filter(struct evbuffer *inbuf, struct evbuffer *outbuf, struct
// @todo Fix this
/* out of memory condition? */
if (ctx->enomem) {
pxy_conn_free(parent);
pxy_conn_free(parent, (bev == ctx->src.bev));
goto leave;
}
@ -2015,9 +2019,9 @@ pxy_process_response(struct evbuffer *inbuf, struct evbuffer *outbuf, struct buf
log_err_printf("ERROR: evbuffer_add failed\n");
}
// log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>>>>,,,,,,,,,,,,,,,,,,,,,,, pxy_response: dst packet (size = %d):\n%.*s\n",
// (int) packet_size, (int) packet_size, packet);
log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>>>>,,,,,,,,,,,,,,,,,,,,,,, pxy_response: dst packet (size = %d)\n", (int) packet_size);
// log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>>>>,,,,,,,,,,,,,,,,,,,,,,, pxy_response: dst packet (size=%lu):\n%.*s\n",
// packet_size, packet_size, packet);
log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>>>>,,,,,,,,,,,,,,,,,,,,,,, pxy_response: dst packet (size=%lu)\n", packet_size);
if (WANT_CONTENT_LOG(parent)) {
logbuf_t *lb;
@ -2043,17 +2047,24 @@ pxy_bev_readcb(struct bufferevent *bev, void *arg)
{
pxy_conn_ctx_t *ctx = arg;
#ifdef DEBUG_PROXY
if (OPTS_DEBUG(ctx->opts)) {
log_dbg_printf("%p %p %s readcb\n", arg, (void*)bev,
(bev == ctx->src.bev) ? "src" : "dst");
}
#endif /* DEBUG_PROXY */
ctx->atime = time(NULL);
char *event_name = pxy_get_event_name(bev, ctx);
log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>>>>,,,,,,,,,,,,,,,,,,,,,,, pxy_bev_readcb: ENTER %s, fd=%d, child_fd=%d\n", event_name, ctx->fd, ctx->child_fd);
log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>>>>,,,,,,,,,,,,,,,,,,,,,,, pxy_bev_readcb: ENTER %s, fd=%d, child_fd=%d, size=%lu\n",
event_name, ctx->fd, ctx->child_fd, evbuffer_get_length(bufferevent_get_input(bev)));
if (!ctx->connected) {
log_err_printf("readcb called when other end not connected - "
"aborting.\n");
/* XXX should signal main loop instead of calling exit() */
log_fini();
exit(EXIT_FAILURE);
log_exceptcb();
return;
}
// @attention srv_dst r cb is not set/enabled, so we only have src and dst at this point
@ -2062,6 +2073,8 @@ pxy_bev_readcb(struct bufferevent *bev, void *arg)
struct evbuffer *outbuf = bufferevent_get_output(other->bev);
if (other->closed) {
log_dbg_printf("Warning: Drained %zu bytes (conn closed)\n",
evbuffer_get_length(inbuf));
evbuffer_drain(inbuf, evbuffer_get_length(inbuf));
return;
}
@ -2087,13 +2100,14 @@ pxy_bev_readcb(struct bufferevent *bev, void *arg)
log_dbg_level_printf(LOG_DBG_MODE_FINER, ">>>>>,,,,,,,,,,,,,,,,,,,,,,, pxy_bev_readcb: custom_field= %s\n", ctx->child_addr_str);
size_t child_addr_len = strlen(ctx->child_addr_str);
size_t src_addr_len = strlen(ctx->src_addr_str);
size_t packet_size = evbuffer_get_length(inbuf);
// +2 is for \r\n
char *packet = malloc(packet_size + child_addr_len + 2);
char *packet = malloc(packet_size + child_addr_len + 2 + src_addr_len + 2);
if (!packet) {
// @todo Should we just set enomem?
ctx->enomem = 1;
pxy_conn_free(ctx);
pxy_conn_free(ctx, 1);
return;
}
@ -2101,11 +2115,11 @@ pxy_bev_readcb(struct bufferevent *bev, void *arg)
log_err_printf("ERROR: evbuffer_remove cannot drain the buffer\n");
}
log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>>>>,,,,,,,,,,,,,,,,,,,,,,, pxy_bev_readcb: src ORIG packet (size = %d), fd=%d:\n%.*s\n",
(int)packet_size, ctx->fd, (int)packet_size, packet);
log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>>>>,,,,,,,,,,,,,,,,,,,,,,, pxy_bev_readcb: src ORIG packet (size=%lu), fd=%d:\n%.*s\n",
packet_size, ctx->fd, (int)packet_size, packet);
// XXX: We insert our special header line to each packet we get, right after the first \r\n, hence the target may get multiple copies
// TODO: To insert our header line to the first packet only, should we look for GET/POST or Host header lines to detect the first packet?
// @todo To insert our header line to the first packet only, should we look for GET/POST or Host header lines to detect the first packet?
// But there is no guarantie that they will exist, due to fragmentation
// @attention We cannot append the ssl proxy address at the end of the packet or in between the header and the content,
@ -2115,9 +2129,11 @@ pxy_bev_readcb(struct bufferevent *bev, void *arg)
// @attention Cannot use string manipulation functions; we are dealing with binary arrays here, not NULL-terminated strings
char *pos = memmem(packet, packet_size, "\r\n", 2);
if (pos) {
memmove(pos + 2 + child_addr_len, pos, packet_size - (pos - packet));
memmove(pos + 2 + child_addr_len + 2 + src_addr_len, pos, packet_size - (pos - packet));
memcpy(pos + 2, ctx->child_addr_str, child_addr_len);
packet_size+= child_addr_len + 2;
memcpy(pos + 2 + child_addr_len, "\r\n", 2);
memcpy(pos + 2 + child_addr_len + 2, ctx->src_addr_str, src_addr_len);
packet_size+= child_addr_len + 2 + src_addr_len + 2;
} else {
log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>>>>,,,,,,,,,,,,,,,,,,,,,,, pxy_bev_readcb: No CRLF in packet\n");
}
@ -2126,9 +2142,9 @@ pxy_bev_readcb(struct bufferevent *bev, void *arg)
log_err_printf("ERROR: evbuffer_add failed\n");
}
log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>>>>,,,,,,,,,,,,,,,,,,,,,,, pxy_bev_readcb: src packet (size = %d), fd=%d:\n%.*s\n",
(int)packet_size, ctx->fd, (int)packet_size, packet);
// log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>>>>,,,,,,,,,,,,,,,,,,,,,,, pxy_bev_readcb: src packet (size = %d)\n", (int) packet_size);
log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>>>>,,,,,,,,,,,,,,,,,,,,,,, pxy_bev_readcb: src packet (size=%lu), fd=%d:\n%.*s\n",
packet_size, ctx->fd, (int)packet_size, packet);
// log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>>>>,,,,,,,,,,,,,,,,,,,,,,, pxy_bev_readcb: src packet (size=%lu)\n", packet_size);
if (WANT_CONTENT_LOG(ctx)) {
logbuf_t *lb;
@ -2179,12 +2195,12 @@ watermark:
if (evbuffer_get_length(outbuf) >= OUTBUF_LIMIT) {
/* temporarily disable data source;
* set an appropriate watermark. */
log_dbg_level_printf(LOG_DBG_MODE_FINE, ">>>>>,,,,,,,,,,,,,,,,,,,,,,, pxy_bev_readcb: setwatermark <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< WATERMARK\n");
log_dbg_level_printf(LOG_DBG_MODE_FINE, ">>>>>,,,,,,,,,,,,,,,,,,,,,,, pxy_bev_readcb: setwatermark <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< WATERMARK, fd=%d\n", ctx->fd);
bufferevent_setwatermark(other->bev, EV_WRITE, OUTBUF_LIMIT/2, OUTBUF_LIMIT);
bufferevent_disable(bev, EV_READ);
}
log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>>>>,,,,,,,,,,,,,,,,,,,,,,, pxy_bev_readcb: EXIT\n");
log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>>>>,,,,,,,,,,,,,,,,,,,,,,, pxy_bev_readcb: EXIT, size=%lu\n", evbuffer_get_length(bufferevent_get_input(bev)));
}
static void
@ -2199,14 +2215,14 @@ pxy_bev_readcb_child(struct bufferevent *bev, void *arg)
evutil_socket_t pfd = parent->fd;
char *event_name = pxy_get_event_name_child(bev, ctx);
log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>>>>....................... pxy_bev_readcb_child: ENTER %s fd=%d, pfd=%d\n", event_name, ctx->fd, pfd);
log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>>>>....................... pxy_bev_readcb_child: ENTER %s fd=%d, pfd=%d, size=%lu\n",
event_name, ctx->fd, pfd, evbuffer_get_length(bufferevent_get_input(bev)));
if (!ctx->connected) {
log_err_printf("readcb called when other end not connected - "
"aborting.\n");
/* XXX should signal main loop instead of calling exit() */
log_fini();
exit(EXIT_FAILURE);
log_exceptcb();
return;
}
pxy_conn_desc_t *other = (bev==ctx->src.bev) ? &ctx->dst : &ctx->src;
@ -2214,6 +2230,8 @@ pxy_bev_readcb_child(struct bufferevent *bev, void *arg)
struct evbuffer *outbuf = bufferevent_get_output(other->bev);
if (other->closed) {
log_dbg_printf("Warning: Drained %zu bytes (conn closed)\n",
evbuffer_get_length(inbuf));
evbuffer_drain(inbuf, evbuffer_get_length(inbuf));
return;
}
@ -2233,7 +2251,7 @@ pxy_bev_readcb_child(struct bufferevent *bev, void *arg)
char *packet = malloc(packet_size);
if (!packet) {
ctx->enomem = 1;
pxy_conn_free(parent);
pxy_conn_free(parent, 1);
return;
}
@ -2249,13 +2267,22 @@ pxy_bev_readcb_child(struct bufferevent *bev, void *arg)
log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>>>>....................... pxy_bev_readcb_child: <<<<<<<<<<<<<<<<<<<<<<<<<<<<< REMOVED SSLproxy-Addr\n");
}
// @todo Combine src_addr removal with child_addr removal?
size_t src_addr_len = strlen(parent->src_addr_str);
pos = memmem(packet, packet_size, parent->src_addr_str, src_addr_len);
if (pos) {
memmove(pos, pos + src_addr_len + 2, packet_size - (pos - packet) - (src_addr_len + 2));
packet_size-= src_addr_len + 2;
log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>>>>....................... pxy_bev_readcb_child: <<<<<<<<<<<<<<<<<<<<<<<<<<<<< REMOVED SSLproxy-SrcAddr\n");
}
if (evbuffer_add(outbuf, packet, packet_size) < 0) {
log_err_printf("ERROR: evbuffer_add failed\n");
}
log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>>>>....................... pxy_bev_readcb_child: src packet (size = %d), fd=%d, parent fd=%d:\n%.*s\n",
(int) packet_size, ctx->fd, pfd, (int) packet_size, packet);
// log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>>>>....................... pxy_bev_readcb_child: src packet (size = %d)\n", (int) packet_size);
log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>>>>....................... pxy_bev_readcb_child: src packet (size=%lu), fd=%d, parent fd=%d:\n%.*s\n",
packet_size, ctx->fd, pfd, (int)packet_size, packet);
// log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>>>>....................... pxy_bev_readcb_child: src packet (size=%lu)\n", packet_size);
if (WANT_CONTENT_LOG(parent)) {
logbuf_t *lb;
@ -2306,12 +2333,12 @@ watermark:
if (evbuffer_get_length(outbuf) >= OUTBUF_LIMIT) {
/* temporarily disable data source;
* set an appropriate watermark. */
log_dbg_level_printf(LOG_DBG_MODE_FINE, ">>>>>....................... pxy_bev_readcb_child: setwatermark <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< WATERMARK\n");
log_dbg_level_printf(LOG_DBG_MODE_FINE, ">>>>>....................... pxy_bev_readcb_child: setwatermark <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< WATERMARK, pfd=%d\n", ctx->parent->fd);
bufferevent_setwatermark(other->bev, EV_WRITE, OUTBUF_LIMIT/2, OUTBUF_LIMIT);
bufferevent_disable(bev, EV_READ);
}
log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>>>>....................... pxy_bev_readcb_child: EXIT\n");
log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>>>>....................... pxy_bev_readcb_child: EXIT, size=%lu\n", evbuffer_get_length(bufferevent_get_input(bev)));
}
static void
@ -2323,7 +2350,7 @@ pxy_conn_connect_child(pxy_conn_child_ctx_t *ctx)
if (!parent->addrlen) {
log_err_printf("Child no target address; aborting connection <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
evutil_closesocket(ctx->fd);
pxy_conn_free(parent);
pxy_conn_free(parent, 1);
return;
}
@ -2333,7 +2360,7 @@ pxy_conn_connect_child(pxy_conn_child_ctx_t *ctx)
if (!ctx->src.bev) {
log_err_printf("Error creating child src\n");
evutil_closesocket(ctx->fd);
pxy_conn_free(parent);
pxy_conn_free(parent, 1);
return;
}
@ -2353,7 +2380,7 @@ pxy_conn_connect_child(pxy_conn_child_ctx_t *ctx)
log_dbg_level_printf(LOG_DBG_MODE_FINE, ">>>>> pxy_conn_connect_child: Error creating SSL ctx->dst.ssl, fd=%d\n", ctx->fd);
log_err_printf("Error creating SSL\n");
// pxy_conn_free()>pxy_conn_free_child() will close the fd, since we have a non-NULL src.bev now
pxy_conn_free(parent);
pxy_conn_free(parent, 1);
return;
}
}
@ -2378,7 +2405,7 @@ pxy_conn_connect_child(pxy_conn_child_ctx_t *ctx)
SSL_free(ctx->dst.ssl);
ctx->dst.ssl = NULL;
}
pxy_conn_free(parent);
pxy_conn_free(parent, 1);
return;
}
@ -2417,7 +2444,7 @@ pxy_conn_setup_child(evutil_socket_t fd, pxy_conn_ctx_t *parent)
if (!ctx) {
log_err_printf("Error allocating memory\n");
evutil_closesocket(fd);
pxy_conn_free(parent);
pxy_conn_free(parent, 1);
return;
}
@ -2483,7 +2510,7 @@ pxy_connected_enable(struct bufferevent *bev, pxy_conn_ctx_t *ctx, char *event_n
ctx->spec->parent_dst_addrlen) == -1) {
log_dbg_level_printf(LOG_DBG_MODE_FINE, ">>>>>=================================== pxy_connected_enable: FAILED bufferevent_socket_connect for dst, fd=%d\n", fd);
evutil_closesocket(fd);
pxy_conn_free(ctx);
pxy_conn_free(ctx, 1);
return 0;
}
ctx->dst_fd = bufferevent_getfd(ctx->dst.bev);
@ -2514,7 +2541,7 @@ pxy_connected_enable(struct bufferevent *bev, pxy_conn_ctx_t *ctx, char *event_n
return 0;
}
evutil_closesocket(fd);
pxy_conn_free(ctx);
pxy_conn_free(ctx, 1);
return 0;
}
}
@ -2537,7 +2564,7 @@ pxy_connected_enable(struct bufferevent *bev, pxy_conn_ctx_t *ctx, char *event_n
ctx->src.ssl = NULL;
}
evutil_closesocket(fd);
pxy_conn_free(ctx);
pxy_conn_free(ctx, 1);
return 0;
}
bufferevent_setcb(ctx->src.bev, pxy_bev_readcb, pxy_bev_writecb, pxy_bev_eventcb, ctx);
@ -2549,7 +2576,7 @@ pxy_connected_enable(struct bufferevent *bev, pxy_conn_ctx_t *ctx, char *event_n
&ctx->dsthost_str,
&ctx->dstport_str) != 0) {
ctx->enomem = 1;
pxy_conn_free(ctx);
pxy_conn_free(ctx, 1);
return 0;
}
@ -2572,7 +2599,7 @@ pxy_connected_enable(struct bufferevent *bev, pxy_conn_ctx_t *ctx, char *event_n
if (!ctx->lproc.user ||
!ctx->lproc.group) {
ctx->enomem = 1;
pxy_conn_free(ctx);
pxy_conn_free(ctx, 1);
return 0;
}
}
@ -2593,7 +2620,7 @@ pxy_connected_enable(struct bufferevent *bev, pxy_conn_ctx_t *ctx, char *event_n
) == -1) {
if (errno == ENOMEM)
ctx->enomem = 1;
pxy_conn_free(ctx);
pxy_conn_free(ctx, 1);
return 0;
}
}
@ -2621,7 +2648,7 @@ pxy_connected_enable(struct bufferevent *bev, pxy_conn_ctx_t *ctx, char *event_n
evutil_socket_t cfd;
if ((cfd = privsep_client_opensock_child(ctx->clisock, ctx->spec)) == -1) {
log_err_printf("Error opening socket: %s (%i)\n", strerror(errno), errno);
pxy_conn_free(ctx);
pxy_conn_free(ctx, 1);
return 0;
}
ctx->child_fd = cfd;
@ -2634,7 +2661,7 @@ pxy_connected_enable(struct bufferevent *bev, pxy_conn_ctx_t *ctx, char *event_n
// @attention Cannot call proxy_listener_ctx_free() on child_evcl, child_evcl does not have any ctx with next listener
// @attention Close child fd separately, because child evcl does not exist yet, hence fd would not be closed by calling pxy_conn_free()
evutil_closesocket(ctx->child_fd);
pxy_conn_free(ctx);
pxy_conn_free(ctx, 1);
return 0;
}
ctx->child_evcl = child_evcl;
@ -2649,25 +2676,34 @@ pxy_connected_enable(struct bufferevent *bev, pxy_conn_ctx_t *ctx, char *event_n
perror("getsockname");
log_dbg_level_printf(LOG_DBG_MODE_FINE, ">>>>>=================================== pxy_connected_enable: %s, getsockname ERROR=%s, fd=%d, child_fd=%d <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n", event_name, strerror(errno), fd, ctx->child_fd);
// @todo If getsockname() fails, should we really terminate the connection?
pxy_conn_free(ctx);
pxy_conn_free(ctx, 1);
return 0;
}
// @attention Children are always listening on an IPv4 loopback address
char addr[INET_ADDRSTRLEN];
if (!inet_ntop(AF_INET, &child_listener_addr.sin_addr, addr, INET_ADDRSTRLEN)) {
pxy_conn_free(ctx);
pxy_conn_free(ctx, 1);
return 0;
}
// @todo Port may be less than 5 chars
int addr_len = SSLPROXY_ADDR_KEY_LEN + 1 + strlen(addr) + 5 + 3 + 1;
// @todo Always check malloc retvals. Should we close the conn if malloc fails?
ctx->child_addr_str = malloc(addr_len);
if (!ctx->child_addr_str) {
pxy_conn_free(ctx);
pxy_conn_free(ctx, 1);
return 0;
}
snprintf(ctx->child_addr_str, addr_len, "%s [%s]:%u", SSLPROXY_ADDR_KEY, addr, ntohs(child_listener_addr.sin_port));
int src_addr_len = SSLPROXY_SRCADDR_KEY_LEN + 1 + strlen(ctx->srchost_str) + strlen(ctx->srcport_str) + 3 + 1;
ctx->src_addr_str = malloc(src_addr_len);
if (!ctx->src_addr_str) {
pxy_conn_free(ctx, 1);
return 0;
}
snprintf(ctx->child_addr_str, addr_len, "%s [%s]:%d", SSLPROXY_ADDR_KEY, addr, (int) ntohs(child_listener_addr.sin_port));
snprintf(ctx->src_addr_str, src_addr_len, "%s [%s]:%s", SSLPROXY_SRCADDR_KEY, ctx->srchost_str, ctx->srcport_str);
log_dbg_level_printf(LOG_DBG_MODE_FINER, ">>>>>=================================== pxy_connected_enable: ENABLE src, child_addr= %s, fd=%d, child_fd=%d\n", ctx->child_addr_str, fd, ctx->child_fd);
@ -2744,7 +2780,8 @@ pxy_bev_writecb(struct bufferevent *bev, void *arg)
pxy_conn_ctx_t *ctx = arg;
char *event_name = pxy_get_event_name(bev, ctx);
log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>>>>+++++++++++++++++++++++++++++++++++ pxy_bev_writecb: ENTER %s fd=%d, child_fd=%d\n", event_name, ctx->fd, ctx->child_fd);
log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>>>>+++++++++++++++++++++++++++++++++++ pxy_bev_writecb: ENTER %s fd=%d, child_fd=%d, size=%lu\n",
event_name, ctx->fd, ctx->child_fd, evbuffer_get_length(bufferevent_get_output(bev)));
ctx->atime = time(NULL);
@ -2762,7 +2799,7 @@ pxy_bev_writecb(struct bufferevent *bev, void *arg)
this->closed = 1;
this_free_and_close_fd_func(bev, ctx);
this->bev = NULL;
pxy_conn_free(ctx);
pxy_conn_free(ctx, (bev == ctx->dst.bev));
}
goto leave;
}
@ -2770,7 +2807,7 @@ pxy_bev_writecb(struct bufferevent *bev, void *arg)
if (other->bev && !(bufferevent_get_enabled(other->bev) & EV_READ)) {
/* data source temporarily disabled;
* re-enable and reset watermark to 0. */
log_dbg_level_printf(LOG_DBG_MODE_FINE, ">>>>>+++++++++++++++++++++++++++++++++++ pxy_bev_writecb: remove watermark for w, enable r <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< WATERMARK\n");
log_dbg_level_printf(LOG_DBG_MODE_FINE, ">>>>>+++++++++++++++++++++++++++++++++++ pxy_bev_writecb: remove watermark for w, enable r <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< WATERMARK, fd=%d\n", ctx->fd);
bufferevent_setwatermark(bev, EV_WRITE, 0, 0);
bufferevent_enable(other->bev, EV_READ);
}
@ -2796,7 +2833,8 @@ pxy_bev_writecb_child(struct bufferevent *bev, void *arg)
pxy_conn_ctx_t *parent = ctx->parent;
char *event_name = pxy_get_event_name_child(bev, ctx);
log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>>>>??????????????????????????? pxy_bev_writecb_child: ENTER %s fd=%d, child_fd=%d, cfd=%d\n", event_name, parent->fd, parent->child_fd, ctx->fd);
log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>>>>??????????????????????????? pxy_bev_writecb_child: ENTER %s fd=%d, child_fd=%d, cfd=%d, size=%lu\n",
event_name, parent->fd, parent->child_fd, ctx->fd, evbuffer_get_length(bufferevent_get_output(bev)));
parent->atime = time(NULL);
@ -2821,7 +2859,7 @@ pxy_bev_writecb_child(struct bufferevent *bev, void *arg)
if (other->bev && !(bufferevent_get_enabled(other->bev) & EV_READ)) {
/* data source temporarily disabled;
* re-enable and reset watermark to 0. */
log_dbg_level_printf(LOG_DBG_MODE_FINE, ">>>>>??????????????????????????? pxy_bev_writecb_child: remove watermark for w, enable r <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< WATERMARK\n");
log_dbg_level_printf(LOG_DBG_MODE_FINE, ">>>>>??????????????????????????? pxy_bev_writecb_child: remove watermark for w, enable r <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< WATERMARK, pfd=%d\n", ctx->parent->fd);
bufferevent_setwatermark(bev, EV_WRITE, 0, 0);
bufferevent_enable(other->bev, EV_READ);
}
@ -2917,13 +2955,15 @@ static void
pxy_bev_eventcb(struct bufferevent *bev, short events, void *arg)
{
pxy_conn_ctx_t *ctx = arg;
int is_requestor = (bev == ctx->src.bev);
ctx->atime = time(NULL);
evutil_socket_t fd = ctx->fd;
char *event_name = pxy_get_event_name(bev, ctx);
log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>>>>=================================== pxy_bev_eventcb: ENTER %s fd=%d, child_fd=%d\n", event_name, ctx->fd, ctx->child_fd);
log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>>>>=================================== pxy_bev_eventcb: ENTER %s fd=%d, child_fd=%d, in=%lu, out=%lu\n",
event_name, ctx->fd, ctx->child_fd, evbuffer_get_length(bufferevent_get_input(bev)), evbuffer_get_length(bufferevent_get_output(bev)));
if (events & BEV_EVENT_CONNECTED) {
log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>>>>=================================== pxy_bev_eventcb: CONNECTED %s fd=%d\n", event_name, ctx->fd);
@ -2948,7 +2988,7 @@ pxy_bev_eventcb(struct bufferevent *bev, short events, void *arg)
* e.g. because it asked for client cert auth, so
* close the accepted socket and clean up */
log_dbg_level_printf(LOG_DBG_MODE_FINE, ">>>>>=================================== pxy_bev_eventcb: ERROR !ctx->connected %s, fd=%d\n", event_name, ctx->fd);
pxy_conn_free(ctx);
pxy_conn_free(ctx, 1);
return;
}
} else {
@ -2974,6 +3014,22 @@ pxy_bev_eventcb(struct bufferevent *bev, short events, void *arg)
if (events & BEV_EVENT_EOF) {
log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>>>>=================================== pxy_bev_eventcb: EOF %s fd=%d\n", event_name, ctx->fd);
#ifdef DEBUG_PROXY
if (OPTS_DEBUG(ctx->opts)) {
log_dbg_printf("evbuffer size at EOF: "
"i:%zu o:%zu i:%zu o:%zu\n",
evbuffer_get_length(
bufferevent_get_input(bev)),
evbuffer_get_length(
bufferevent_get_output(bev)),
evbuffer_get_length(
bufferevent_get_input(other->bev)),
evbuffer_get_length(
bufferevent_get_output(other->bev))
);
}
#endif /* DEBUG_PROXY */
if (bev == ctx->srv_dst.bev) {
bufferevent_free_and_close_fd(ctx->srv_dst.bev, ctx);
ctx->srv_dst.bev = NULL;
@ -2981,29 +3037,27 @@ pxy_bev_eventcb(struct bufferevent *bev, short events, void *arg)
return;
} else {
if (!ctx->connected) {
log_dbg_printf("EOF on inbound connection while "
"connecting to original destination\n");
log_dbg_printf("EOF on outbound connection before "
"connection establishment\n");
evutil_closesocket(ctx->fd);
other->closed = 1;
} else if (!other->closed) {
log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>>>>=================================== pxy_bev_eventcb: !other->closed <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< CONN TERM, fd=%d\n", ctx->fd);
struct evbuffer *inbuf, *outbuf;
inbuf = bufferevent_get_input(bev);
outbuf = bufferevent_get_output(other->bev);
if (evbuffer_get_length(inbuf) > 0) {
/* if there is data pending in the closed connection,
* handle it here, otherwise it will be lost. */
if (evbuffer_get_length(bufferevent_get_input(bev))) {
log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>>>>=================================== pxy_bev_eventcb: evbuffer_get_length(inbuf) > 0 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< CONN TERM, fd=%d\n", ctx->fd);
pxy_bev_readcb(bev, ctx);
} else {
/* if the other end is still open and doesn't
* have data to send, close it, otherwise its
* writecb will close it after writing what's
* left in the output buffer. */
if (evbuffer_get_length(outbuf) == 0) {
log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>>>>=================================== pxy_bev_eventcb: evbuffer_get_length(inbuf) == 0 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< CONN TERM, fd=%d\n", ctx->fd);
other->closed = 1;
other_free_and_close_fd_func(other->bev, ctx);
other->bev = NULL;
}
}
/* if the other end is still open and doesn't
* have data to send, close it, otherwise its
* writecb will close it after writing what's
* left in the output buffer. */
if (evbuffer_get_length(bufferevent_get_output(other->bev)) == 0) {
log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>>>>=================================== pxy_bev_eventcb: evbuffer_get_length(outbuf) == 0 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< CONN TERM, fd=%d\n", ctx->fd);
other->closed = 1;
other_free_and_close_fd_func(other->bev, ctx);
other->bev = NULL;
}
}
@ -3028,7 +3082,7 @@ leave:
this->bev = NULL;
if (other->closed) {
log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>>>>=================================== pxy_bev_eventcb: disconnect other->closed <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< CONN TERM, fd=%d\n", ctx->fd);
pxy_conn_free(ctx);
pxy_conn_free(ctx, is_requestor);
}
// @attention ctx may have been freed now, so cannot use ctx->fd here
@ -3047,7 +3101,8 @@ pxy_bev_eventcb_child(struct bufferevent *bev, short events, void *arg)
evutil_socket_t fd = ctx->fd;
char *event_name = pxy_get_event_name_child(bev, ctx);
log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>>>>--------------------- pxy_bev_eventcb_child: ENTER %s fd=%d, child_fd=%d\n", event_name, parent->fd, parent->child_fd);
log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>>>>--------------------- pxy_bev_eventcb_child: ENTER %s fd=%d, child_fd=%d, in=%lu, out=%lu\n",
event_name, parent->fd, parent->child_fd, evbuffer_get_length(bufferevent_get_input(bev)), evbuffer_get_length(bufferevent_get_output(bev)));
pxy_conn_desc_t *this = (bev==ctx->src.bev) ? &ctx->src : &ctx->dst;
pxy_conn_desc_t *other = (bev==ctx->src.bev) ? &ctx->dst : &ctx->src;
@ -3096,31 +3151,45 @@ pxy_bev_eventcb_child(struct bufferevent *bev, short events, void *arg)
if (events & BEV_EVENT_EOF) {
log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>>>>--------------------- pxy_bev_eventcb_child: EOF %s fd=%d\n", event_name, fd);
#ifdef DEBUG_PROXY
if (OPTS_DEBUG(parent->opts)) {
log_dbg_printf("evbuffer size at EOF: "
"i:%zu o:%zu i:%zu o:%zu\n",
evbuffer_get_length(
bufferevent_get_input(bev)),
evbuffer_get_length(
bufferevent_get_output(bev)),
evbuffer_get_length(
bufferevent_get_input(other->bev)),
evbuffer_get_length(
bufferevent_get_output(other->bev))
);
}
#endif /* DEBUG_PROXY */
// @todo How to handle the following case?
if (!ctx->connected) {
log_dbg_printf("EOF on inbound connection while "
"connecting to original destination\n");
log_dbg_printf("EOF on outbound connection before "
"connection establishment\n");
evutil_closesocket(ctx->fd);
other->closed = 1;
} else if (!other->closed) {
log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>>>>=================================== pxy_bev_eventcb_child: !other->closed <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< CHILD TERM, fd=%d, pfd=%d\n", ctx->fd, parent->fd);
struct evbuffer *inbuf, *outbuf;
inbuf = bufferevent_get_input(bev);
outbuf = bufferevent_get_output(other->bev);
if (evbuffer_get_length(inbuf) > 0) {
/* if there is data pending in the closed connection,
* handle it here, otherwise it will be lost. */
if (evbuffer_get_length(bufferevent_get_input(bev))) {
log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>>>>=================================== pxy_bev_eventcb_child: evbuffer_get_length(inbuf) > 0 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< CHILD TERM, fd=%d, pfd=%d\n", ctx->fd, parent->fd);
pxy_bev_readcb_child(bev, ctx);
} else {
/* if the other end is still open and doesn't
* have data to send, close it, otherwise its
* writecb will close it after writing what's
* left in the output buffer. */
if (evbuffer_get_length(outbuf) == 0) {
log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>>>>=================================== pxy_bev_eventcb_child: evbuffer_get_length(inbuf) == 0 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< CHILD TERM, fd=%d, pfd=%d\n", ctx->fd, parent->fd);
other->closed = 1;
other_free_and_close_fd_func(other->bev, ctx->parent);
other->bev = NULL;
}
pxy_bev_readcb(bev, ctx);
}
/* if the other end is still open and doesn't
* have data to send, close it, otherwise its
* writecb will close it after writing what's
* left in the output buffer. */
if (evbuffer_get_length(bufferevent_get_output(other->bev)) == 0) {
log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>>>>=================================== pxy_bev_eventcb_child: evbuffer_get_length(outbuf) == 0 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< CHILD TERM, fd=%d, pfd=%d\n", ctx->fd, parent->fd);
other->closed = 1;
other_free_and_close_fd_func(other->bev, ctx->parent);
other->bev = NULL;
}
}
goto leave;
@ -3162,7 +3231,7 @@ pxy_conn_connect(pxy_conn_ctx_t *ctx)
if (!ctx->addrlen) {
log_err_printf("No target address; aborting connection\n");
evutil_closesocket(fd);
pxy_conn_ctx_free(ctx);
pxy_conn_ctx_free(ctx, 1);
return;
}
@ -3172,7 +3241,7 @@ pxy_conn_connect(pxy_conn_ctx_t *ctx)
if (!ctx->dst.bev) {
log_err_printf("Error creating parent dst\n");
evutil_closesocket(fd);
pxy_conn_ctx_free(ctx);
pxy_conn_ctx_free(ctx, 1);
return;
}
@ -3185,7 +3254,7 @@ pxy_conn_connect(pxy_conn_ctx_t *ctx)
ctx->srv_dst.ssl = pxy_dstssl_create(ctx);
if (!ctx->srv_dst.ssl) {
log_err_printf("Error creating SSL for srv_dst\n");
pxy_conn_free(ctx);
pxy_conn_free(ctx, 1);
return;
}
}
@ -3197,7 +3266,7 @@ pxy_conn_connect(pxy_conn_ctx_t *ctx)
SSL_free(ctx->srv_dst.ssl);
ctx->srv_dst.ssl = NULL;
}
pxy_conn_free(ctx);
pxy_conn_free(ctx, 1);
return;
}
@ -3252,7 +3321,7 @@ pxy_sni_resolve_cb(int errcode, struct evutil_addrinfo *ai, void *arg)
log_err_printf("Cannot resolve SNI hostname '%s': %s\n",
ctx->sni, evutil_gai_strerror(errcode));
evutil_closesocket(ctx->fd);
pxy_conn_ctx_free(ctx);
pxy_conn_ctx_free(ctx, 1);
return;
}
@ -3297,14 +3366,14 @@ pxy_fd_readcb(MAYBE_UNUSED evutil_socket_t fd, UNUSED short what, void *arg)
log_err_printf("Error peeking on fd, aborting "
"connection, fd=%d\n", ctx->fd);
evutil_closesocket(fd);
pxy_conn_ctx_free(ctx);
pxy_conn_ctx_free(ctx, 1);
return;
}
if (n == 0) {
/* socket got closed while we were waiting */
log_err_printf("Socket got closed while waiting, fd=%d\n", ctx->fd);
evutil_closesocket(fd);
pxy_conn_ctx_free(ctx);
pxy_conn_ctx_free(ctx, 1);
return;
}
@ -3314,7 +3383,7 @@ pxy_fd_readcb(MAYBE_UNUSED evutil_socket_t fd, UNUSED short what, void *arg)
"ClientHello message, "
"aborting connection, fd=%d\n", ctx->fd);
evutil_closesocket(fd);
pxy_conn_ctx_free(ctx);
pxy_conn_ctx_free(ctx, 1);
return;
}
if (OPTS_DEBUG(ctx->opts)) {
@ -3342,7 +3411,7 @@ pxy_fd_readcb(MAYBE_UNUSED evutil_socket_t fd, UNUSED short what, void *arg)
"event, aborting "
"connection, fd=%d\n", ctx->fd);
evutil_closesocket(fd);
pxy_conn_ctx_free(ctx);
pxy_conn_ctx_free(ctx, 1);
return;
}
event_add(ctx->ev, &retry_delay);
@ -3405,6 +3474,8 @@ pxy_conn_setup(evutil_socket_t fd,
/* create per connection state and attach to thread */
pxy_conn_ctx_t *ctx = pxy_conn_ctx_new(fd, thrmgr, spec, opts, clisock);
if (!ctx) {
log_err_printf("Error allocating memory\n");
evutil_closesocket(fd);
return;
}
@ -3420,7 +3491,7 @@ pxy_conn_setup(evutil_socket_t fd,
log_err_printf("Connection not found in NAT "
"state table, aborting connection\n");
evutil_closesocket(fd);
pxy_conn_ctx_free(ctx);
pxy_conn_ctx_free(ctx, 1);
return;
}
} else if (spec->connect_addrlen > 0) {
@ -3434,7 +3505,7 @@ pxy_conn_setup(evutil_socket_t fd,
log_err_printf("SNI mode used for non-SSL connection; "
"aborting connection\n");
evutil_closesocket(fd);
pxy_conn_ctx_free(ctx);
pxy_conn_ctx_free(ctx, 1);
return;
}
}
@ -3470,7 +3541,7 @@ memout:
log_dbg_level_printf(LOG_DBG_MODE_FINE, ">>>>> pxy_conn_setup: FAIL EXIT fd=%d\n", fd);
log_err_printf("Aborting connection setup (out of memory)!\n");
evutil_closesocket(fd);
pxy_conn_ctx_free(ctx);
pxy_conn_ctx_free(ctx, 1);
}
/* vim: set noet ft=c: */

@ -27,8 +27,8 @@
#ifndef PXYCONN_H
#define PXYCONN_H
#include "proxy.h"
#include "proxy.h"
#include "opts.h"
#include "attrib.h"
#include "pxythrmgr.h"
@ -53,17 +53,19 @@ typedef struct pxy_conn_desc {
* connection-wide state and the specs and options */
struct pxy_conn_ctx {
// Common properties
// @attention The order of these common vars should match with the ones in children
/* per-connection state */
struct pxy_conn_desc src;
struct pxy_conn_desc dst;
/* status flags */
unsigned int connected : 1; /* 0 until both ends are connected */
unsigned int enomem : 1; /* 1 if out of memory */
/* http */
unsigned int seen_req_header : 1; /* 0 until request header complete */
unsigned int seen_resp_header : 1; /* 0 until response hdr complete */
unsigned int sent_http_conn_close : 1; /* 0 until Conn: close sent */
unsigned int ocsp_denied : 1; /* 1 if OCSP was denied */
unsigned int enomem : 1; /* 1 if out of memory */
/* log strings from socket */
char *srchost_str;
@ -89,17 +91,20 @@ struct pxy_conn_ctx {
/* store fd and fd event while connected is 0 */
evutil_socket_t fd;
// End of common properties
/* content log context */
log_content_ctx_t *logctx;
// End of common properties
unsigned int immutable_cert : 1; /* 1 if the cert cannot be changed */
unsigned int generated_cert : 1; /* 1 if we generated a new cert */
unsigned int srv_dst_connected : 1; /* 0 until server is connected */
unsigned int dst_connected : 1; /* 0 until dst is connected */
unsigned int passthrough : 1; /* 1 if SSL passthrough is active */
/* ssl */
unsigned int sni_peek_retries : 6; /* max 64 SNI parse retries */
unsigned int immutable_cert : 1; /* 1 if the cert cannot be changed */
unsigned int generated_cert : 1; /* 1 if we generated a new cert */
unsigned int passthrough : 1; /* 1 if SSL passthrough is active */
/* autossl */
unsigned int clienthello_search : 1; /* 1 if waiting for hello */
unsigned int clienthello_found : 1; /* 1 if conn upgrade to SSL */
@ -139,6 +144,7 @@ struct pxy_conn_ctx {
struct evconnlistener *child_evcl;
// SSL proxy return address: The IP:port address the children are listening to
char *child_addr_str;
char *src_addr_str;
// Child list of the conn
pxy_conn_child_ctx_t *children;
@ -175,17 +181,19 @@ struct pxy_conn_ctx {
* connection-wide state */
struct pxy_conn_child_ctx {
// Common properties
// @attention The order of these common vars should match with the ones in parent
/* per-connection state */
struct pxy_conn_desc src;
struct pxy_conn_desc dst;
/* status flags */
unsigned int connected : 1; /* 0 until both ends are connected */
unsigned int enomem : 1; /* 1 if out of memory */
/* http */
unsigned int seen_req_header : 1; /* 0 until request header complete */
unsigned int seen_resp_header : 1; /* 0 until response hdr complete */
unsigned int sent_http_conn_close : 1; /* 0 until Conn: close sent */
unsigned int ocsp_denied : 1; /* 1 if OCSP was denied */
unsigned int enomem : 1; /* 1 if out of memory */
/* log strings from socket */
char *srchost_str;
@ -211,9 +219,6 @@ struct pxy_conn_child_ctx {
/* store fd and fd event while connected is 0 */
evutil_socket_t fd;
/* content log context */
log_content_ctx_t *logctx;
// End of common properties
evutil_socket_t src_fd;
@ -232,7 +237,7 @@ void pxy_conn_setup(evutil_socket_t, struct sockaddr *, int,
pxy_thrmgr_ctx_t *, proxyspec_t *, opts_t *,
evutil_socket_t)
NONNULL(2,4,5,6);
void pxy_conn_free(pxy_conn_ctx_t *ctx) NONNULL(1);
void pxy_conn_free(pxy_conn_ctx_t *ctx, int) NONNULL(1);
#endif /* !PXYCONN_H */

@ -140,7 +140,7 @@ retry:
}
ctx->ev = event_new(ctx->evbase, fd, want, pxy_ssl_shutdown_cb, ctx);
if (ctx->ev) {
event_add(ctx->ev, want ? NULL : &retry_delay);
event_add(ctx->ev, &retry_delay);
return;
}
log_err_printf("Failed to shutdown SSL connection cleanly: "

@ -83,8 +83,23 @@ static void
pxy_thrmgr_print_child(pxy_conn_child_ctx_t *child_ctx, int count)
{
assert(child_ctx != NULL);
log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>> .......... pxy_thrmgr_print_child: thr=%d, cont=%d, src=%d, dst=%d, c=%d-%d, i=%d\n",
// log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>> .......... pxy_thrmgr_print_child: thr=%d, cont=%d, src=%d, dst=%d, c=%d-%d, i=%d\n",
// child_ctx->parent->thr->thridx, count, child_ctx->src_fd, child_ctx->dst_fd, child_ctx->src.closed, child_ctx->dst.closed, child_ctx->idx);
char *msg;
int rv;
rv = asprintf(&msg, "thr=%d, cont=%d, src=%d, dst=%d, c=%d-%d, i=%d\n",
child_ctx->parent->thr->thridx, count, child_ctx->src_fd, child_ctx->dst_fd, child_ctx->src.closed, child_ctx->dst.closed, child_ctx->idx);
log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>> .......... pxy_thrmgr_print_child: %s\n", msg);
if (child_ctx->parent->opts->statslog) {
if (log_stats_print_free(msg) == -1) {
free(msg);
log_err_printf("Warning: Stats logging failed\n");
}
} else {
free(msg);
}
if (child_ctx->next) {
pxy_thrmgr_print_child(child_ctx->next, count);
}
@ -102,19 +117,41 @@ pxy_thrmgr_print_thr_info(pxy_thr_ctx_t *tctx)
int count = 0;
while (ctx) {
char *host, *port;
char *msg;
int rv;
if (ctx->addrlen == 0 || (sys_sockaddr_str((struct sockaddr *)&ctx->addr, ctx->addrlen, &host, &port) != 0)) {
log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>> pxy_thrmgr_print_thr_info: Cannot get host:port: thr=%d, cont=%d, fd=%d, child_fd=%d\n", tctx->thridx, count, ctx->fd, ctx->child_fd);
log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>> pxy_thrmgr_print_thr_info: thr=%d, cont=%d, fd=%d, child_fd=%d, dst=%d, srv_dst=%d, child_src=%d, child_dst=%d, p=%d-%d-%d c=%d-%d, ce=%d cc=%d, at=%lld ct=%lld\n",
// log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>> pxy_thrmgr_print_thr_info: thr=%d, cont=%d, fd=%d, child_fd=%d, dst=%d, srv_dst=%d, child_src=%d, child_dst=%d, p=%d-%d-%d c=%d-%d, ce=%d cc=%d, at=%lld ct=%lld\n",
// tctx->thridx, count, ctx->fd, ctx->child_fd, ctx->dst_fd, ctx->srv_dst_fd, ctx->child_src_fd, ctx->child_dst_fd,
// ctx->src.closed, ctx->dst.closed, ctx->srv_dst.closed, ctx->children ? ctx->children->src.closed : 0, ctx->children ? ctx->children->dst.closed : 0, ctx->children ? 1:0, ctx->child_count,(long int) now - ctx->atime, (long int) now - ctx->ctime);
rv = asprintf(&msg, "thr=%d, cont=%d, fd=%d, child_fd=%d, dst=%d, srv_dst=%d, child_src=%d, child_dst=%d, p=%d-%d-%d c=%d-%d, ce=%d cc=%d, at=%lld ct=%lld\n",
tctx->thridx, count, ctx->fd, ctx->child_fd, ctx->dst_fd, ctx->srv_dst_fd, ctx->child_src_fd, ctx->child_dst_fd,
ctx->src.closed, ctx->dst.closed, ctx->srv_dst.closed, ctx->children ? ctx->children->src.closed : 0, ctx->children ? ctx->children->dst.closed : 0, ctx->children ? 1:0, ctx->child_count,(long int) now - ctx->atime, (long int) now - ctx->ctime);
} else {
log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>> pxy_thrmgr_print_thr_info: thr=%d, cont=%d, fd=%d, child_fd=%d, dst=%d, srv_dst=%d, child_src=%d, child_dst=%d, p=%d-%d-%d c=%d-%d, ce=%d cc=%d, at=%lld ct=%lld, addr=%s:%s\n",
// log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>> pxy_thrmgr_print_thr_info: thr=%d, cont=%d, fd=%d, child_fd=%d, dst=%d, srv_dst=%d, child_src=%d, child_dst=%d, p=%d-%d-%d c=%d-%d, ce=%d cc=%d, at=%lld ct=%lld, addr=%s:%s\n",
// tctx->thridx, count, ctx->fd, ctx->child_fd, ctx->dst_fd, ctx->srv_dst_fd, ctx->child_src_fd, ctx->child_dst_fd,
// ctx->src.closed, ctx->dst.closed, ctx->srv_dst.closed, ctx->children ? ctx->children->src.closed : 0, ctx->children ? ctx->children->dst.closed : 0, ctx->children ? 1:0, ctx->child_count, (long int) now - ctx->atime, (long int) now - ctx->ctime, host ? host : "?", port ? port : "?");
rv = asprintf(&msg, "thr=%d, cont=%d, fd=%d, child_fd=%d, dst=%d, srv_dst=%d, child_src=%d, child_dst=%d, p=%d-%d-%d c=%d-%d, ce=%d cc=%d, at=%lld ct=%lld, addr=%s:%s\n",
tctx->thridx, count, ctx->fd, ctx->child_fd, ctx->dst_fd, ctx->srv_dst_fd, ctx->child_src_fd, ctx->child_dst_fd,
ctx->src.closed, ctx->dst.closed, ctx->srv_dst.closed, ctx->children ? ctx->children->src.closed : 0, ctx->children ? ctx->children->dst.closed : 0, ctx->children ? 1:0, ctx->child_count, (long int) now - ctx->atime, (long int) now - ctx->ctime, host ? host : "?", port ? port : "?");
free(host);
free(port);
}
log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>> pxy_thrmgr_print_thr_info: %s\n", msg);
if (ctx->opts->statslog) {
if (log_stats_print_free(msg) == -1) {
free(msg);
log_err_printf("Warning: Stats logging failed\n");
}
} else {
free(msg);
}
if (ctx->children) {
pxy_thrmgr_print_child(ctx->children, count);
}
@ -137,11 +174,6 @@ pxy_thrmgr_timer_cb(UNUSED evutil_socket_t fd, UNUSED short what,
{
pxy_thr_ctx_t *ctx = arg;
if (ctx->timer_cb_running) {
return;
}
ctx->timer_cb_running = 1;
log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>>>> !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! pxy_thrmgr_timer_cb <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< THREAD TIMER thr=%d, load=%lu, to=%u\n", ctx->thridx, ctx->load, ctx->timeout_count);
pxy_conn_ctx_t *expired = NULL;
pxy_thrmgr_get_thr_expired_conns(ctx, &expired);
@ -153,7 +185,7 @@ pxy_thrmgr_timer_cb(UNUSED evutil_socket_t fd, UNUSED short what,
log_dbg_level_printf(LOG_DBG_MODE_FINE, ">>>>> !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! pxy_thrmgr_timer_cb: DELETE thr=%d, fd=%d, child_fd=%d, at=%lld ct=%lld <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< TIMED OUT\n",
expired->thr->thridx, expired->fd, expired->child_fd, (long int) now - expired->atime, (long int) now - expired->ctime);
pxy_conn_free(expired);
pxy_conn_free(expired, 1);
expired = next;
}
@ -164,8 +196,6 @@ pxy_thrmgr_timer_cb(UNUSED evutil_socket_t fd, UNUSED short what,
ctx->timeout_count = 0;
pxy_thrmgr_print_thr_info(ctx);
}
ctx->timer_cb_running = 0;
}
/*
@ -221,7 +251,10 @@ pxy_thrmgr_run(pxy_thrmgr_ctx_t *ctx)
dns = opts_has_dns_spec(ctx->opts);
pthread_mutex_init(&ctx->mutex, NULL);
if (pthread_mutex_init(&ctx->mutex, NULL)) {
log_dbg_printf("Failed to initialize mutex\n");
goto leave;
}
if (!(ctx->thr = malloc(ctx->num_thr * sizeof(pxy_thr_ctx_t*)))) {
log_dbg_printf("Failed to allocate memory\n");
@ -432,14 +465,14 @@ pxy_thrmgr_detach(pxy_conn_ctx_t *ctx)
pthread_mutex_lock(&ctx->thrmgr->mutex);
log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>>>> pxy_thrmgr_detach: BEFORE pxy_thrmgr_remove_conn\n");
pxy_thrmgr_print_thr_info(ctx->thr);
// log_dbg_level_printf(LOG_DBG_MODE_FINEST, ">>>>> pxy_thrmgr_detach: BEFORE pxy_thrmgr_remove_conn\n");
// pxy_thrmgr_print_thr_info(ctx->thr);
ctx->thr->load--;
pxy_thrmgr_remove_conn(ctx, &ctx->thr->conns);
log_dbg_level_printf(LOG_DBG_MODE_FINER, ">>>>> pxy_thrmgr_detach: AFTER pxy_thrmgr_remove_conn\n");
pxy_thrmgr_print_thr_info(ctx->thr);
// log_dbg_level_printf(LOG_DBG_MODE_FINER, ">>>>> pxy_thrmgr_detach: AFTER pxy_thrmgr_remove_conn\n");
// pxy_thrmgr_print_thr_info(ctx->thr);
pthread_mutex_unlock(&ctx->thrmgr->mutex);
}

@ -47,7 +47,6 @@ typedef struct pxy_thr_ctx {
struct event_base *evbase;
struct evdns_base *dnsbase;
int running;
int timer_cb_running;
unsigned int timeout_count;
pxy_conn_ctx_t *conns;
} pxy_thr_ctx_t;

31
ssl.c

@ -256,7 +256,10 @@ ssl_thr_dyn_create_cb(UNUSED const char *file, UNUSED int line)
struct CRYPTO_dynlock_value *dl;
if ((dl = malloc(sizeof(struct CRYPTO_dynlock_value)))) {
pthread_mutex_init(&dl->mutex, NULL);
if (pthread_mutex_init(&dl->mutex, NULL)) {
free(dl);
return NULL;
}
}
return dl;
}
@ -335,7 +338,10 @@ ssl_init(void)
ssl_mutex_num = CRYPTO_num_locks();
ssl_mutex = malloc(ssl_mutex_num * sizeof(*ssl_mutex));
for (int i = 0; i < ssl_mutex_num; i++) {
pthread_mutex_init(&ssl_mutex[i], NULL);
if (pthread_mutex_init(&ssl_mutex[i], NULL)) {
log_err_printf("Failed to initialize mutex\n");
return -1;
}
}
CRYPTO_set_locking_callback(ssl_thr_locking_cb);
CRYPTO_set_dynlock_create_callback(ssl_thr_dyn_create_cb);
@ -389,19 +395,23 @@ ssl_init(void)
}
/*
* Re-initialize OpenSSL after forking.
* Re-initialize OpenSSL after forking. Returns 0 on success, -1 on failure.
*/
void
int
ssl_reinit(void)
{
if (!ssl_initialized)
return;
return 0;
#ifdef OPENSSL_THREADS
for (int i = 0; i < ssl_mutex_num; i++) {
pthread_mutex_init(&ssl_mutex[i], NULL);
if (pthread_mutex_init(&ssl_mutex[i], NULL)) {
return -1;
}
}
#endif /* OPENSSL_THREADS */
return 0;
}
/*
@ -1325,8 +1335,10 @@ ssl_wildcardify(const char *hostname)
if (!(wildcarded = malloc(dotsz + 2)))
return NULL;
wildcarded[0] = '*';
strncpy(wildcarded + 1, dot, dotsz);
wildcarded[dotsz + 1] = '\0';
for (size_t i = 0; i < dotsz; i++) {
wildcarded[i+1] = dot[i];
}
wildcarded[dotsz+1] = '\0';
return wildcarded;
}
@ -1448,6 +1460,7 @@ ssl_x509_names(X509 *crt)
* in the Subject DN CN and subjectAltNames extension, separated by slashes.
* Caller must free returned buffer.
* Embedded NULL characters in hostnames are replaced with '!'.
* If no CN and no subjectAltNames are found, returns "-".
*/
char *
ssl_x509_names_to_str(X509 *crt)
@ -1458,7 +1471,7 @@ ssl_x509_names_to_str(X509 *crt)
names = ssl_x509_names(crt);
if (!names)
return NULL;
return strdup("-");
sz = 0;
for (char **p = names; *p; p++) {

@ -142,7 +142,7 @@ X509 * ssl_ssl_cert_get(SSL *);
void ssl_openssl_version(void);
int ssl_init(void) WUNRES;
void ssl_reinit(void);
int ssl_reinit(void) WUNRES;
void ssl_fini(void);
char * ssl_sha1_to_str(unsigned char *, int) NONNULL(1) MALLOC;

@ -82,8 +82,8 @@ SSLsplit implements a number of defences against mechanisms which would
normally prevent MitM attacks or make them more difficult.
SSLsplit can deny OCSP requests in a generic way.
For HTTP and HTTPS connections, SSLsplit removes response headers
for HPKP in order to prevent public key pinning,
for HSTS to allow the user to accept untrusted certificates,
for HPKP in order to prevent server-instructed public key pinning,
for HSTS to avoid the strict transport security restrictions,
and Alternate Protocols to prevent switching to QUIC/SPDY.
HTTP compression, encodings and keep-alive are disabled to make the logs more
readable.
@ -93,6 +93,12 @@ where a protocol starts on a plain text TCP connection and is later upgraded to
SSL/TLS through protocol-specific means, such as the STARTTLS command in SMTP.
SSLsplit supports generic upgrading of TCP connections to SSL.
.LP
In order to maximize the chances that a connection can be successfully split,
SSLsplit does not verify upstream server certificates. Instead, all
certificates including self-signed are accepted and if the expected hostname
signalled in SNI is missing from the server certificate, it will be added to
dynamically forged certificates.
.LP
SSLsplit does not automagically redirect any network traffic. To actually
implement an attack, you also need to redirect the traffic to the system
running \fBsslsplit\fP. Your options include running \fBsslsplit\fP on a
@ -608,13 +614,11 @@ connection data into separate files under \fB/tmp\fP (add \fB-e\fP
\fInat-engine\fP to select the appropriate engine if multiple engines are
available on your system):
.LP
.HS
.nf
\fBsslsplit -k ca.key -c ca.crt -l connect.log -L /tmp \\
https ::1 10443 https 127.0.0.1 10443 \\
http ::1 10080 http 127.0.0.1 10080\fP
.fi
.RE
.LP
If the Linux netfilter engine is used with the iptables REDIRECT target, it is
important to listen to the correct IP address (e.g. 192.0.2.1) or on all
@ -623,25 +627,21 @@ connections.
.LP
Intercepting IMAP/IMAPS using the same settings:
.LP
.HS
.nf
\fBsslsplit -k ca.key -c ca.crt -l connect.log -L /tmp \\
ssl ::1 10993 ssl 127.0.0.1 10993 \\
tcp ::1 10143 tcp 127.0.0.1 10143\fP
.fi
.RE
.LP
A more targetted setup, HTTPS only, using certificate/chain/key files from
\fB/path/to/cert.d\fP and statically redirecting to \fBwww.example.org\fP
instead of querying a NAT engine:
.LP
.HS
.nf
\fBsslsplit -t /path/to/cert.d -l connect.log -L /tmp \\
https ::1 10443 www.example.org 443 \\
https 127.0.0.1 10443 www.example.org 443\fP
.fi
.RE
.LP
The original example, but using SSL options optimized for speed by disabling
compression and selecting only fast cipher cipher suites and using a
@ -652,31 +652,26 @@ algorithm performance on your system. Note that clients may not support all
algorithms and key sizes. Also, some clients warn their users about cipher
suites they consider weak.
.LP
.HS
.nf
\fBsslsplit -Z -s NULL:RC4:AES128:-DHE -K leaf.key \\
-k ca.key -c ca.crt -l connect.log -L /tmp \\
https ::1 10443 https 127.0.0.1 10443 \\
http ::1 10080 http 127.0.0.1 10080\fP
.fi
.RE
.LP
The original example, but running as a daemon under user \fBsslsplit\fP and
writing a PID file:
.LP
.HS
.nf
\fBsslsplit -d -p /var/run/sslsplit.pid -u sslsplit \\
-k ca.key -c ca.crt -l connect.log -L /tmp \\
https ::1 10443 https 127.0.0.1 10443 \\
http ::1 10080 http 127.0.0.1 10080\fP
.fi
.RE
.LP
To generate a CA private key \fBca.key\fP and certificate \fBca.crt\fP using
OpenSSL:
.LP
.HS
.nf
\fBcat >x509v3ca.cnf <<'EOF'\fP
[ req ]
@ -724,7 +719,8 @@ SSLsplit was written by Daniel Roethlisberger <daniel@roe.ch>.
The following individuals have contributed code or documentation, in
chronological order of their first contribution:
Steve Wills, Landon Fuller, Wayne Jensen, Rory McNamara, Alexander Neumann,
Adam Jacob Muller and Richard Poole.
Adam Jacob Muller, Richard Poole, Maciej Kotowicz, Eun Soo Park and Christian
Groschupp.
.SH BUGS
Use Github for submission of bug reports or patches:
.LP

17
sys.c

@ -521,7 +521,6 @@ sys_mkpath(const char *path, mode_t mode)
* Iterate over all files in a directory hierarchy, calling the callback
* cb for each file, passing the filename and arg as arguments. Files and
* directories beginning with a dot are skipped, symlinks are followed.
* FIXME - there is no facility to return errors from the file handlers
*/
int
sys_dir_eachfile(const char *dirname, sys_dir_eachfile_cb_t cb, void *arg)
@ -529,6 +528,7 @@ sys_dir_eachfile(const char *dirname, sys_dir_eachfile_cb_t cb, void *arg)
FTS *tree;
FTSENT *node;
char * paths[2];
int rv = 0;
paths[1] = NULL;
paths[0] = strdup(dirname);
@ -539,25 +539,32 @@ sys_dir_eachfile(const char *dirname, sys_dir_eachfile_cb_t cb, void *arg)
if (!tree) {
log_err_printf("Cannot open directory '%s': %s\n",
dirname, strerror(errno));
return -1;
rv = -1;
goto out1;
}
while ((node = fts_read(tree))) {
if (node->fts_level > 0 && node->fts_name[0] == '.')
fts_set(tree, node, FTS_SKIP);
else if (node->fts_info & FTS_F) {
cb(node->fts_path, arg);
rv = cb(node->fts_path, arg);
if (rv == -1)
goto out2;
}
}
if (errno) {
log_err_printf("Error reading directory entry: %s\n",
strerror(errno));
return -1;
rv = -1;
goto out2;
}
out2:
fts_close(tree);
out1:
free(paths[0]);
return 0;
return rv;
}
/*

@ -54,8 +54,8 @@ char * sys_ip46str_sanitize(const char *) NONNULL(1) MALLOC;
int sys_isdir(const char *) NONNULL(1) WUNRES;
int sys_mkpath(const char *, mode_t) NONNULL(1) WUNRES;
typedef void (*sys_dir_eachfile_cb_t)(const char *, void *) NONNULL(1);
int sys_dir_eachfile(const char *, sys_dir_eachfile_cb_t, void *) NONNULL(1,2);
typedef int (*sys_dir_eachfile_cb_t)(const char *, void *) NONNULL(1) WUNRES;
int sys_dir_eachfile(const char *, sys_dir_eachfile_cb_t, void *) NONNULL(1,2) WUNRES;
uint32_t sys_get_cpu_cores(void) WUNRES;

@ -60,11 +60,11 @@ thrqueue_new(size_t sz)
goto out0;
if (!(queue->data = malloc(sz * sizeof(void*))))
goto out1;
if (0 != pthread_mutex_init(&queue->mutex, NULL))
if (pthread_mutex_init(&queue->mutex, NULL))
goto out2;
if (0 != pthread_cond_init(&queue->notempty, NULL))
if (pthread_cond_init(&queue->notempty, NULL))
goto out3;
if (0 != pthread_cond_init(&queue->notfull, NULL))
if (pthread_cond_init(&queue->notfull, NULL))
goto out4;
queue->sz = sz;
queue->n = 0;

Loading…
Cancel
Save