Merge branch 'tfo'

This commit is contained in:
yrutschle 2019-03-10 10:12:33 +01:00
commit 67eb471c6f
7 changed files with 84 additions and 21 deletions

View File

@ -1,4 +1,9 @@
vNEXT:
vNEXT:
Added TCP_FASTOPEN support for client sockets (if
tfo_ok is specified in their configuration) and for
listenint socket, if all client protocols support it.
(Craig Andrews)
Added 'minlength' option to skip a probe if less
than that many bytes have been received (mostly for
regex)

View File

@ -86,6 +86,24 @@ int get_fd_sockets(int *sockfd[])
return sd;
}
/* Set TCP_FASTOPEN on listening socket if all client protocols support it */
int make_listen_tfo(int s)
{
int i, qlen = 5;
/* Don't do it if not supported */
if (!TCP_FASTOPEN)
return;
/* Don't do it if any protocol does not specify it */
for (i = 0; i < cfg.protocols_len; i++) {
if (! cfg.protocols[i].tfo_ok)
return;
}
return setsockopt(s, SOL_SOCKET, TCP_FASTOPEN, (char*)&qlen, sizeof(qlen));
}
/* Starts listening sockets on specified addresses.
* IN: addr[], num_addr
* OUT: *sockfd[] pointer to newly-allocated array of file descriptors
@ -134,6 +152,9 @@ int start_listen_sockets(int *sockfd[], struct addrinfo *addr_list)
res = setsockopt((*sockfd)[i], SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof(one));
check_res_dump(CR_DIE, res, addr, "setsockopt(SO_REUSEADDR)");
res = make_listen_tfo((*sockfd)[i]);
check_res_dump(CR_WARN, res, addr, "setsockopt(TCP_FASTOPEN)");
if (addr->ai_flags & SO_KEEPALIVE) {
res = setsockopt((*sockfd)[i], SOL_SOCKET, SO_KEEPALIVE, (char*)&one, sizeof(one));
check_res_dump(CR_DIE, res, addr, "setsockopt(SO_KEEPALIVE)");
@ -277,18 +298,28 @@ int connect_addr(struct connection *cnx, int fd_from)
log_message(LOG_ERR, "forward to %s failed:socket: %s\n",
cnx->proto->name, strerror(errno));
} else {
one = 1;
setsockopt(fd, IPPROTO_TCP, TCP_FASTOPEN_CONNECT, &one, sizeof(one));
/* no need to check return value; if it's not supported, that's okay */
if (cfg.transparent) {
res = bind_peer(fd, fd_from);
CHECK_RES_RETURN(res, "bind_peer");
}
res = connect(fd, a->ai_addr, a->ai_addrlen);
if (res == -1) {
log_message(LOG_ERR, "forward to %s failed:connect: %s\n",
cnx->proto->name, strerror(errno));
close(fd);
switch (errno) {
case EINPROGRESS:
/* Can't be done yet, or TFO already done */
break;
default:
log_message(LOG_ERR, "forward to %s failed:connect: %s\n",
cnx->proto->name, strerror(errno));
close(fd);
}
} else {
if (cnx->proto->keepalive) {
one = 1;
res = setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char*)&one, sizeof(one));
CHECK_RES_RETURN(res, "setsockopt(SO_KEEPALIVE)");
}

View File

@ -67,6 +67,14 @@
#define IP_FREEBIND 0
#endif
#ifndef TCP_FASTOPEN
#define TCP_FASTOPEN 0
#endif
#ifndef TCP_FASTOPEN_CONNECT
#define TCP_FASTOPEN_CONNECT 30 /* Attempt FastOpen with connect. */
#endif
enum connection_state {
ST_PROBING=1, /* Waiting for timeout to find where to forward */
ST_SHOVELING /* Connexion is established */

View File

@ -41,6 +41,7 @@ listen:
# connection (default is off)
# fork: Should a new process be forked for this protocol?
# (only useful for sslh-select)
# tfo_ok: Set to true if the server supports TCP FAST OPEN
#
# Probe-specific options:
# (sslh will try each probe in order they are declared, and
@ -66,28 +67,26 @@ listen:
protocols:
(
{ name: "ssh"; service: "ssh"; host: "localhost"; port: "22"; keepalive: true; fork: true;
listen: ( { host: "hello"; port: "xmpp" }, { host: "world";
port: "dns" } ),
},
{ name: "ssh"; service: "ssh"; host: "localhost"; port: "22";
keepalive: true; fork: true; tfo_ok: true },
{ name: "http"; host: "localhost"; port: "80"; },
# match BOTH ALPN/SNI
{ name: "tls"; host: "localhost"; port: "5223"; alpn_protocols: [ "xmpp-client" ]; sni_hostnames: [ "im.somethingelse.net" ]; log_level: 0;},
{ name: "tls"; host: "localhost"; port: "5223"; alpn_protocols: [ "xmpp-client" ]; sni_hostnames: [ "im.somethingelse.net" ]; log_level: 0; tfo_ok: true },
# just match ALPN
{ name: "tls"; host: "localhost"; port: "443"; alpn_protocols: [ "h2", "http/1.1", "spdy/1", "spdy/2", "spdy/3" ]; log_level: 0; },
{ name: "tls"; host: "localhost"; port: "xmpp-client"; alpn_protocols: [ "xmpp-client" ]; log_level: 0;},
{ name: "tls"; host: "localhost"; port: "443"; alpn_protocols: [ "h2", "http/1.1", "spdy/1", "spdy/2", "spdy/3" ]; log_level: 0; tfo_ok: true },
{ name: "tls"; host: "localhost"; port: "xmpp-client"; alpn_protocols: [ "xmpp-client" ]; log_level: 0; tfo_ok: true },
# just match SNI
{ name: "tls"; host: "localhost"; port: "993"; sni_hostnames: [ "mail.rutschle.net", "mail.englishintoulouse.com" ]; log_level: 0; },
{ name: "tls"; host: "localhost"; port: "xmpp-client"; sni_hostnames: [ "im.rutschle.net", "im.englishintoulouse.com" ]; log_level: 0;},
{ name: "tls"; host: "localhost"; port: "993"; sni_hostnames: [ "mail.rutschle.net", "mail.englishintoulouse.com" ]; log_level: 0; tfo_ok: true },
{ name: "tls"; host: "localhost"; port: "xmpp-client"; sni_hostnames: [ "im.rutschle.net", "im.englishintoulouse.com" ]; log_level: 0; tfo_ok: true },
# Let's Encrypt (tls-sni-* challenges)
{ name: "tls"; host: "localhost"; port: "letsencrypt-client"; sni_hostnames: [ "*.*.acme.invalid" ]; log_level: 0;},
# catch anything else TLS
{ name: "tls"; host: "localhost"; port: "443"; },
{ name: "tls"; host: "localhost"; port: "443"; tfo_ok: true },
# Regex examples -- better use the built-in probes for real-world use!
# OpenVPN

View File

@ -1,5 +1,5 @@
/* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README)
* on Sat Mar 9 12:35:49 2019. */
* on Sun Mar 10 09:52:17 2019. */
#define _GNU_SOURCE
#include <string.h>
@ -10,6 +10,7 @@
static void sslhcfg_protocols_init(struct sslhcfg_protocols_item* cfg) {
memset(cfg, 0, sizeof(*cfg));
cfg->fork = 0;
cfg->tfo_ok = 0;
cfg->log_level = 1;
cfg->keepalive = 0;
}
@ -94,6 +95,12 @@ static int sslhcfg_protocols_parser(
return 0;
} ;
}
if (config_setting_lookup(cfg, "tfo_ok")) {
if (config_setting_lookup_bool(cfg, "tfo_ok", &sslhcfg_protocols->tfo_ok) == CONFIG_FALSE) {
*errmsg = "Parsing of option \"tfo_ok\" failed";
return 0;
} ;
}
if (config_setting_lookup(cfg, "log_level")) {
if (config_setting_lookup_int(cfg, "log_level", &sslhcfg_protocols->log_level) == CONFIG_FALSE) {
*errmsg = "Parsing of option \"log_level\" failed";
@ -358,6 +365,8 @@ static void sslhcfg_protocols_fprint(
indent(out, depth);
fprintf(out, "fork: %d\n", sslhcfg_protocols->fork);
indent(out, depth);
fprintf(out, "tfo_ok: %d\n", sslhcfg_protocols->tfo_ok);
indent(out, depth);
fprintf(out, "log_level: %d\n", sslhcfg_protocols->log_level);
indent(out, depth);
fprintf(out, "keepalive: %d\n", sslhcfg_protocols->keepalive);
@ -646,6 +655,7 @@ int sslhcfg_cl_parse(int argc, char* argv[], struct sslhcfg_item* cfg) {
group->port = calloc(1, param_len + 1);
memcpy(group->port, sslhcfg_ssh->sval [cl_i]+pmatch[2].rm_so, param_len);
group->fork = *((char*)"1") - '0';
group->tfo_ok = *((char*)"1") - '0';
}
#define MAX_MATCH 10
for (cl_i = 0; cl_i < sslhcfg_tls->count; cl_i++) {
@ -695,6 +705,7 @@ int sslhcfg_cl_parse(int argc, char* argv[], struct sslhcfg_item* cfg) {
param_len = pmatch[2].rm_eo - pmatch[2].rm_so;
group->port = calloc(1, param_len + 1);
memcpy(group->port, sslhcfg_tls->sval [cl_i]+pmatch[2].rm_so, param_len);
group->tfo_ok = *((char*)"1") - '0';
}
#define MAX_MATCH 10
for (cl_i = 0; cl_i < sslhcfg_openvpn->count; cl_i++) {
@ -744,6 +755,7 @@ int sslhcfg_cl_parse(int argc, char* argv[], struct sslhcfg_item* cfg) {
param_len = pmatch[2].rm_eo - pmatch[2].rm_so;
group->port = calloc(1, param_len + 1);
memcpy(group->port, sslhcfg_openvpn->sval [cl_i]+pmatch[2].rm_so, param_len);
group->tfo_ok = *((char*)"1") - '0';
}
#define MAX_MATCH 10
for (cl_i = 0; cl_i < sslhcfg_tinc->count; cl_i++) {
@ -793,6 +805,7 @@ int sslhcfg_cl_parse(int argc, char* argv[], struct sslhcfg_item* cfg) {
param_len = pmatch[2].rm_eo - pmatch[2].rm_so;
group->port = calloc(1, param_len + 1);
memcpy(group->port, sslhcfg_tinc->sval [cl_i]+pmatch[2].rm_so, param_len);
group->tfo_ok = *((char*)"1") - '0';
}
#define MAX_MATCH 10
for (cl_i = 0; cl_i < sslhcfg_xmpp->count; cl_i++) {

View File

@ -1,5 +1,5 @@
/* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README)
* on Sat Mar 9 12:35:49 2019. */
* on Sun Mar 10 09:52:17 2019. */
#ifndef C2S_SSLHCFG_H
#define C2S_SSLHCFG_H
@ -23,6 +23,7 @@ struct sslhcfg_protocols_item {
int service_is_present;
const char* service;
int fork;
int tfo_ok;
int log_level;
int keepalive;
size_t sni_hostnames_len;

View File

@ -66,6 +66,8 @@ config: {
{ name: "port"; type: "string"; var: true; },
{ name: "service"; type: "string"; optional: true; },
{ name: "fork"; type: "boolean"; default: false },
{ name: "tfo_ok"; type: "boolean"; default: false;
description: "Set to true if this protocol supports TCP FAST OPEN" },
{ name: "log_level"; type: "int"; default: 1 },
{ name: "keepalive"; type: "boolean"; default: false },
{ name: "sni_hostnames",
@ -118,7 +120,8 @@ cl_groups: (
{ path: "name"; value: "ssh" },
{ path: "host"; value: "$1" },
{ path: "port"; value: "$2" },
{ path: "fork"; value: 1 }
{ path: "fork"; value: 1 },
{ path: "tfo_ok"; value: 1 }
);
},
{ name: "tls"; pattern: "(\w+):(\w+)"; description: "Set up TLS/SSL target";
@ -128,7 +131,8 @@ cl_groups: (
targets: (
{ path: "name"; value: "tls" },
{ path: "host"; value: "$1" },
{ path: "port"; value: "$2" }
{ path: "port"; value: "$2" },
{ path: "tfo_ok"; value: 1 }
);
},
{ name: "openvpn"; pattern: "(\w+):(\w+)"; description: "Set up OpenVPN target";
@ -138,7 +142,8 @@ cl_groups: (
targets: (
{ path: "name"; value: "openvpn" },
{ path: "host"; value: "$1" },
{ path: "port"; value: "$2" }
{ path: "port"; value: "$2" },
{ path: "tfo_ok"; value: 1 }
);
},
{ name: "tinc"; pattern: "(\w+):(\w+)"; description: "Set up tinc target";
@ -148,7 +153,8 @@ cl_groups: (
targets: (
{ path: "name"; value: "openvpn" },
{ path: "host"; value: "$1" },
{ path: "port"; value: "$2" }
{ path: "port"; value: "$2" },
{ path: "tfo_ok"; value: 1 }
);
},
{ name: "xmpp"; pattern: "(\w+):(\w+)"; description: "Set up XMPP target";