From b9885401050ad27d9fa13ffa67d5e43441f495c0 Mon Sep 17 00:00:00 2001 From: moparisthebest Date: Sun, 12 Jul 2015 23:10:53 -0400 Subject: [PATCH 1/4] Add SNI hostname based probe --- Makefile | 4 +- example.cfg | 1 + probe.c | 31 +++++++ probe.h | 3 +- sslh-main.c | 39 ++++++++- tls.c | 238 ++++++++++++++++++++++++++++++++++++++++++++++++++++ tls.h | 33 ++++++++ 7 files changed, 345 insertions(+), 4 deletions(-) create mode 100644 tls.c create mode 100644 tls.h diff --git a/Makefile b/Makefile index 001e7f5..146d29d 100644 --- a/Makefile +++ b/Makefile @@ -23,7 +23,7 @@ CC ?= gcc CFLAGS ?=-Wall -g $(CFLAGS_COV) LIBS= -OBJS=common.o sslh-main.o probe.o +OBJS=common.o sslh-main.o probe.o tls.o ifneq ($(strip $(USELIBWRAP)),) LIBS:=$(LIBS) -lwrap @@ -63,7 +63,7 @@ sslh-select: version.h $(OBJS) sslh-select.o Makefile common.h #strip sslh-select echosrv: $(OBJS) echosrv.o - $(CC) $(CFLAGS) $(LDFLAGS) -o echosrv echosrv.o probe.o common.o $(LIBS) + $(CC) $(CFLAGS) $(LDFLAGS) -o echosrv echosrv.o probe.o common.o tls.o $(LIBS) $(MAN): sslh.pod Makefile pod2man --section=8 --release=$(VERSION) --center=" " sslh.pod | gzip -9 - > $(MAN) diff --git a/example.cfg b/example.cfg index 20299c9..f9d76fa 100644 --- a/example.cfg +++ b/example.cfg @@ -36,6 +36,7 @@ listen: protocols: ( { name: "ssh"; service: "ssh"; host: "localhost"; port: "22"; probe: "builtin"; }, + { name: "sni"; host: "localhost"; port: "993"; probe: "builtin"; sni_hostnames: [ "imap.example.org", "imap.example.com" ]; }, { name: "openvpn"; host: "localhost"; port: "1194"; probe: [ "^\x00[\x0D-\xFF]$", "^\x00[\x0D-\xFF]\x38" ]; }, { name: "xmpp"; host: "localhost"; port: "5222"; probe: [ "jabber" ]; }, { name: "http"; host: "localhost"; port: "80"; probe: "builtin"; }, diff --git a/probe.c b/probe.c index c5500b5..1a5f194 100644 --- a/probe.c +++ b/probe.c @@ -216,6 +216,33 @@ static int is_http_protocol(const char *p, int len, struct proto *proto) return PROBE_NEXT; } +static int is_sni_protocol(const char *p, int len, struct proto *proto) +{ + int valid_tls; + char *hostname; + + valid_tls = parse_tls_header(p, len, &hostname); + + if(valid_tls < 0) + return -1 == valid_tls ? PROBE_AGAIN : PROBE_NEXT; + + if (verbose) fprintf(stderr, "sni hostname: %s\n", hostname); + + /* Assume does not match */ + valid_tls = PROBE_NEXT; + + char **sni_hostname = proto->data; + + for (; *sni_hostname; sni_hostname++) + if(!strcmp(hostname, *sni_hostname)) { + valid_tls = PROBE_MATCH; + break; + } + + free(hostname); + return valid_tls; +} + static int is_tls_protocol(const char *p, int len, struct proto *proto) { if (len < 3) @@ -334,6 +361,10 @@ T_PROBE* get_probe(const char* description) { if (!strcmp(description, "regex")) return regex_probe; + /* Special case of "sni" probe for same reason as above*/ + if (!strcmp(description, "sni")) + return is_sni_protocol; + return NULL; } diff --git a/probe.h b/probe.h index d79b795..81ac684 100644 --- a/probe.h +++ b/probe.h @@ -4,6 +4,7 @@ #define __PROBE_H_ #include "common.h" +#include "tls.h" typedef enum { PROBE_NEXT, /* Enough data, probe failed -- it's some other protocol */ @@ -23,7 +24,7 @@ struct proto { /* function to probe that protocol; parameters are buffer and length * containing the data to probe, and a pointer to the protocol structure */ T_PROBE* probe; - void* data; /* opaque pointer ; used to pass list of regex to regex probe */ + void* data; /* opaque pointer ; used to pass list of regex to regex probe, or sni hostnames to sni probe */ struct proto *next; /* pointer to next protocol in list, NULL if last */ }; diff --git a/sslh-main.c b/sslh-main.c index 2029856..60fe6ce 100644 --- a/sslh-main.c +++ b/sslh-main.c @@ -211,13 +211,45 @@ static void setup_regex_probe(struct proto *p, config_setting_t* probes) } #endif +#ifdef LIBCONFIG +static void setup_sni_hostnames(struct proto *p, config_setting_t* sni_hostnames) +{ + int num_probes, i, max_server_name_len, server_name_len; + const char * sni_hostname; + char** sni_hostname_list; + + num_probes = config_setting_length(sni_hostnames); + if (!num_probes) { + fprintf(stderr, "%s: no sni_hostnames specified\n", p->description); + exit(1); + } + + max_server_name_len = 0; + for (i = 0; i < num_probes; i++) { + server_name_len = strlen(config_setting_get_string_elem(sni_hostnames, i)); + if(server_name_len > max_server_name_len) + max_server_name_len = server_name_len; + } + + sni_hostname_list = calloc(num_probes + 1, ++max_server_name_len); + p->data = (void*)sni_hostname_list; + + for (i = 0; i < num_probes; i++) { + sni_hostname = config_setting_get_string_elem(sni_hostnames, i); + sni_hostname_list[i] = malloc(max_server_name_len); + strcpy (sni_hostname_list[i], sni_hostname); + if(verbose) fprintf(stderr, "sni_hostnames[%d]: %s\n", i, sni_hostname_list[i]); + } +} +#endif + /* Extract configuration for protocols to connect to. * out: newly-allocated list of protocols */ #ifdef LIBCONFIG static int config_protocols(config_t *config, struct proto **prots) { - config_setting_t *setting, *prot, *probes; + config_setting_t *setting, *prot, *probes, *sni_hostnames; const char *hostname, *port, *name; int i, num_prots; struct proto *p, *prev = NULL; @@ -265,6 +297,11 @@ static int config_protocols(config_t *config, struct proto **prots) } } } + + sni_hostnames = config_setting_get_member(prot, "sni_hostnames"); + if (sni_hostnames && config_setting_is_array(sni_hostnames)) { + setup_sni_hostnames(p, sni_hostnames); + } } } } diff --git a/tls.c b/tls.c new file mode 100644 index 0000000..16c072c --- /dev/null +++ b/tls.c @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2011 and 2012, Dustin Lundquist + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This is a minimal TLS implementation intended only to parse the server name + * extension. This was created based primarily on Wireshark dissection of a + * TLS handshake and RFC4366. + */ +#include +#include /* malloc() */ +#include /* strncpy() */ +#include +#include "tls.h" + +#define TLS_HEADER_LEN 5 +#define TLS_HANDSHAKE_CONTENT_TYPE 0x16 +#define TLS_HANDSHAKE_TYPE_CLIENT_HELLO 0x01 + +#ifndef MIN +#define MIN(X, Y) ((X) < (Y) ? (X) : (Y)) +#endif + +static int parse_extensions(const char *, size_t, char **); +static int parse_server_name_extension(const char *, size_t, char **); + +const char tls_alert[] = { + 0x15, /* TLS Alert */ + 0x03, 0x01, /* TLS version */ + 0x00, 0x02, /* Payload length */ + 0x02, 0x28, /* Fatal, handshake failure */ +}; + +/* Parse a TLS packet for the Server Name Indication extension in the client + * hello handshake, returning the first servername found (pointer to static + * array) + * + * Returns: + * >=0 - length of the hostname and updates *hostname + * caller is responsible for freeing *hostname + * -1 - Incomplete request + * -2 - No Host header included in this request + * -3 - Invalid hostname pointer + * -4 - malloc failure + * < -4 - Invalid TLS client hello + */ +int +parse_tls_header(const char *data, size_t data_len, char **hostname) { + char tls_content_type; + char tls_version_major; + char tls_version_minor; + size_t pos = TLS_HEADER_LEN; + size_t len; + + if (hostname == NULL) + return -3; + + /* Check that our TCP payload is at least large enough for a TLS header */ + if (data_len < TLS_HEADER_LEN) + return -1; + + /* SSL 2.0 compatible Client Hello + * + * High bit of first byte (length) and content type is Client Hello + * + * See RFC5246 Appendix E.2 + */ + if (data[0] & 0x80 && data[2] == 1) { + if (verbose) fprintf(stderr, "Received SSL 2.0 Client Hello which can not support SNI.\n"); + return -2; + } + + tls_content_type = data[0]; + if (tls_content_type != TLS_HANDSHAKE_CONTENT_TYPE) { + if (verbose) fprintf(stderr, "Request did not begin with TLS handshake.\n"); + return -5; + } + + tls_version_major = data[1]; + tls_version_minor = data[2]; + if (tls_version_major < 3) { + if (verbose) fprintf(stderr, "Received SSL %d.%d handshake which which can not support SNI.\n", + tls_version_major, tls_version_minor); + + return -2; + } + + /* TLS record length */ + len = ((unsigned char)data[3] << 8) + + (unsigned char)data[4] + TLS_HEADER_LEN; + data_len = MIN(data_len, len); + + /* Check we received entire TLS record length */ + if (data_len < len) + return -1; + + /* + * Handshake + */ + if (pos + 1 > data_len) { + return -5; + } + if (data[pos] != TLS_HANDSHAKE_TYPE_CLIENT_HELLO) { + if (verbose) fprintf(stderr, "Not a client hello\n"); + + return -5; + } + + /* Skip past fixed length records: + 1 Handshake Type + 3 Length + 2 Version (again) + 32 Random + to Session ID Length + */ + pos += 38; + + /* Session ID */ + if (pos + 1 > data_len) + return -5; + len = (unsigned char)data[pos]; + pos += 1 + len; + + /* Cipher Suites */ + if (pos + 2 > data_len) + return -5; + len = ((unsigned char)data[pos] << 8) + (unsigned char)data[pos + 1]; + pos += 2 + len; + + /* Compression Methods */ + if (pos + 1 > data_len) + return -5; + len = (unsigned char)data[pos]; + pos += 1 + len; + + if (pos == data_len && tls_version_major == 3 && tls_version_minor == 0) { + if (verbose) fprintf(stderr, "Received SSL 3.0 handshake without extensions\n"); + return -2; + } + + /* Extensions */ + if (pos + 2 > data_len) + return -5; + len = ((unsigned char)data[pos] << 8) + (unsigned char)data[pos + 1]; + pos += 2; + + if (pos + len > data_len) + return -5; + return parse_extensions(data + pos, len, hostname); +} + +int +parse_extensions(const char *data, size_t data_len, char **hostname) { + size_t pos = 0; + size_t len; + + /* Parse each 4 bytes for the extension header */ + while (pos + 4 <= data_len) { + /* Extension Length */ + len = ((unsigned char)data[pos + 2] << 8) + + (unsigned char)data[pos + 3]; + + /* Check if it's a server name extension */ + if (data[pos] == 0x00 && data[pos + 1] == 0x00) { + /* There can be only one extension of each type, so we break + our state and move p to beinnging of the extension here */ + if (pos + 4 + len > data_len) + return -5; + return parse_server_name_extension(data + pos + 4, len, hostname); + } + pos += 4 + len; /* Advance to the next extension header */ + } + /* Check we ended where we expected to */ + if (pos != data_len) + return -5; + + return -2; +} + +int +parse_server_name_extension(const char *data, size_t data_len, + char **hostname) { + size_t pos = 2; /* skip server name list length */ + size_t len; + + while (pos + 3 < data_len) { + len = ((unsigned char)data[pos + 1] << 8) + + (unsigned char)data[pos + 2]; + + if (pos + 3 + len > data_len) + return -5; + + switch (data[pos]) { /* name type */ + case 0x00: /* host_name */ + *hostname = malloc(len + 1); + if (*hostname == NULL) { + if (verbose) fprintf(stderr, "malloc() failure\n"); + return -4; + } + + strncpy(*hostname, data + pos + 3, len); + + (*hostname)[len] = '\0'; + + return len; + default: + if (verbose) fprintf(stderr, "Unknown server name extension name type: %d\n", + data[pos]); + } + pos += 3 + len; + } + /* Check we ended where we expected to */ + if (pos != data_len) + return -5; + + return -2; +} diff --git a/tls.h b/tls.h new file mode 100644 index 0000000..ce7a8c6 --- /dev/null +++ b/tls.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2011 and 2012, Dustin Lundquist + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef TLS_H +#define TLS_H + +#include "common.h" + +int parse_tls_header(const char *data, size_t data_len, char **hostname); + +#endif From 9475d9689b3eb1c6357c4532151ac5aca18e20b7 Mon Sep 17 00:00:00 2001 From: Yves Rutschle Date: Wed, 15 Jul 2015 15:02:37 +0200 Subject: [PATCH 2/4] Comment for SNI inclusion --- ChangeLog | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ChangeLog b/ChangeLog index 0ff2914..413ebb4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,6 +2,9 @@ vNEXT: Added USELIBPCRE to make use of regex engine optional. + Added support for RFC4366 SNI + (Travis Burtrum) + v1.17: 09MAR2015 Support RFC5952-style IPv6 addresses, e.g. [::]:443. From 77ef29358d7cfb5962cab175995e9e48399a991d Mon Sep 17 00:00:00 2001 From: Yves Rutschle Date: Wed, 15 Jul 2015 15:09:39 +0200 Subject: [PATCH 3/4] make code C-compliant --- probe.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/probe.c b/probe.c index 4b76ecf..5626983 100644 --- a/probe.c +++ b/probe.c @@ -1,7 +1,7 @@ /* # probe.c: Code for probing protocols # -# Copyright (C) 2007-2012 Yves Rutschle +# Copyright (C) 2007-2015 Yves Rutschle # # This program is free software; you can redistribute it # and/or modify it under the terms of the GNU General Public @@ -221,6 +221,7 @@ static int is_sni_protocol(const char *p, int len, struct proto *proto) { int valid_tls; char *hostname; + char **sni_hostname; valid_tls = parse_tls_header(p, len, &hostname); @@ -232,9 +233,7 @@ static int is_sni_protocol(const char *p, int len, struct proto *proto) /* Assume does not match */ valid_tls = PROBE_NEXT; - char **sni_hostname = proto->data; - - for (; *sni_hostname; sni_hostname++) + for (sni_hostname = proto->data; *sni_hostname; sni_hostname++) if(!strcmp(hostname, *sni_hostname)) { valid_tls = PROBE_MATCH; break; From 8fdaf6eb08555e01c9993ffbf52f7e4ac40c3d15 Mon Sep 17 00:00:00 2001 From: Yves Rutschle Date: Fri, 17 Jul 2015 15:04:04 +0200 Subject: [PATCH 4/4] changed configuration file to accomodate SNI in a cleaner way --- ChangeLog | 5 +++++ basic.cfg | 12 ++++++------ example.cfg | 45 +++++++++++++++++++++++++++++++-------------- probe.c | 9 ++++++++- sslh-main.c | 42 +++++++++++++++++------------------------- sslh.pod | 6 +----- 6 files changed, 68 insertions(+), 51 deletions(-) diff --git a/ChangeLog b/ChangeLog index 413ebb4..05ccaa5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -5,6 +5,11 @@ vNEXT: Added support for RFC4366 SNI (Travis Burtrum) + Changed configuration file format: 'probe' field is + no longer required, 'name' field can now contain + 'sni' or 'regex', with corresponding options (see + example.org) + v1.17: 09MAR2015 Support RFC5952-style IPv6 addresses, e.g. [::]:443. diff --git a/basic.cfg b/basic.cfg index 526ffbf..64942e5 100644 --- a/basic.cfg +++ b/basic.cfg @@ -19,11 +19,11 @@ listen: protocols: ( - { name: "ssh"; service: "ssh"; host: "localhost"; port: "22"; probe: "builtin"; }, - { name: "openvpn"; host: "localhost"; port: "1194"; probe: "builtin"; }, - { name: "xmpp"; host: "localhost"; port: "5222"; probe: "builtin"; }, - { name: "http"; host: "localhost"; port: "80"; probe: "builtin"; }, - { name: "ssl"; host: "localhost"; port: "443"; probe: "builtin"; }, - { name: "anyprot"; host: "localhost"; port: "443"; probe: "builtin"; } + { name: "ssh"; service: "ssh"; host: "localhost"; port: "22"; }, + { name: "openvpn"; host: "localhost"; port: "1194"; }, + { name: "xmpp"; host: "localhost"; port: "5222"; }, + { name: "http"; host: "localhost"; port: "80"; }, + { name: "ssl"; host: "localhost"; port: "443"; }, + { name: "anyprot"; host: "localhost"; port: "443"; } ); diff --git a/example.cfg b/example.cfg index f9d76fa..5ca9af6 100644 --- a/example.cfg +++ b/example.cfg @@ -23,31 +23,48 @@ listen: # List of protocols # # Each protocol entry consists of: -# name: name of the protocol +# name: name of the probe. These are listed on the command +# line (ssh -?), plus 'regex', 'sni' and 'timeout'. + # service: (optional) libwrap service name (see hosts_access(5)) -# host: host name to connect that protocol -# port: port number to connect that protocol -# probe: "builtin" or a list of regular expressions -# (can be left out, e.g. to use with on-timeout) +# host, port: where to connect when this probe succeeds +# +# Probe-specific options: +# sni: +# sni_hotnames: list of FQDN for that target +# regex: +# regex_patterns: list of patterns to match for +# that target. # # sslh will try each probe in order they are declared, and # connect to the first that matches. - +# +# You can specify several of 'regex' and 'sni'. + protocols: ( - { name: "ssh"; service: "ssh"; host: "localhost"; port: "22"; probe: "builtin"; }, - { name: "sni"; host: "localhost"; port: "993"; probe: "builtin"; sni_hostnames: [ "imap.example.org", "imap.example.com" ]; }, - { name: "openvpn"; host: "localhost"; port: "1194"; probe: [ "^\x00[\x0D-\xFF]$", "^\x00[\x0D-\xFF]\x38" ]; }, - { name: "xmpp"; host: "localhost"; port: "5222"; probe: [ "jabber" ]; }, - { name: "http"; host: "localhost"; port: "80"; probe: "builtin"; }, - { name: "ssl"; host: "localhost"; port: "443"; probe: [ "" ]; }, + { name: "ssh"; service: "ssh"; host: "localhost"; port: "22"; }, + { name: "http"; host: "localhost"; port: "80"; }, + + { name: "sni"; host: "localhost"; port: "993"; sni_hostnames: [ "mail.rutschle.net", "mail.englishintoulouse.com" ]; }, + { name: "sni"; host: "localhost"; port: "xmpp-client"; sni_hostnames: [ "im.rutschle.net", "im.englishintoulouse.com" ]; }, + +# OpenVPN + { name: "regex"; host: "localhost"; port: "1194"; regex_patterns: [ "^\x00[\x0D-\xFF]$", "^\x00[\x0D-\xFF]\x38" ]; }, +# Jabber + { name: "regex"; host: "localhost"; port: "5222"; regex_patterns: [ "jabber" ]; }, + +# Catch-all + { name: "regex"; host: "localhost"; port: "443"; regex_patterns: [ "" ]; }, + +# Where to connect in case of timeout (defaults to ssh) { name: "timeout"; service: "daytime"; host: "localhost"; port: "daytime"; } ); # Optionally, specify to which protocol to connect in case # of timeout (defaults to "ssh"). -# You can timeout to any arbitrary address by setting a -# protocol with no probe, as is the case with this example. +# You can timeout to any arbitrary address by setting an +# entry in 'protocols' named "timeout". # This enables you to set a tcpd service name for this # protocol too. on-timeout: "timeout"; diff --git a/probe.c b/probe.c index 5626983..27120c4 100644 --- a/probe.c +++ b/probe.c @@ -233,11 +233,13 @@ static int is_sni_protocol(const char *p, int len, struct proto *proto) /* Assume does not match */ valid_tls = PROBE_NEXT; - for (sni_hostname = proto->data; *sni_hostname; sni_hostname++) + for (sni_hostname = proto->data; *sni_hostname; sni_hostname++) { + fprintf(stderr, "matching [%s] with [%s]\n", hostname, *sni_hostname); if(!strcmp(hostname, *sni_hostname)) { valid_tls = PROBE_MATCH; break; } + } free(hostname); return valid_tls; @@ -365,6 +367,11 @@ T_PROBE* get_probe(const char* description) { if (!strcmp(description, "sni")) return is_sni_protocol; + /* Special case of "timeout" is allowed as a probe name in the + * configuration file even though it's not really a probe */ + if (!strcmp(description, "timeout")) + return is_true; + return NULL; } diff --git a/sslh-main.c b/sslh-main.c index 60fe6ce..4cd6def 100644 --- a/sslh-main.c +++ b/sslh-main.c @@ -249,7 +249,7 @@ static void setup_sni_hostnames(struct proto *p, config_setting_t* sni_hostnames #ifdef LIBCONFIG static int config_protocols(config_t *config, struct proto **prots) { - config_setting_t *setting, *prot, *probes, *sni_hostnames; + config_setting_t *setting, *prot, *patterns, *sni_hostnames; const char *hostname, *port, *name; int i, num_prots; struct proto *p, *prev = NULL; @@ -273,34 +273,26 @@ static int config_protocols(config_t *config, struct proto **prots) resolve_split_name(&(p->saddr), hostname, port); + p->probe = get_probe(name); + if (!p->probe) { + fprintf(stderr, "line %d: %s: probe unknown\n", config_setting_source_line(prot), name); + exit(1); + } - probes = config_setting_get_member(prot, "probe"); - if (probes) { - if (config_setting_is_array(probes)) { - /* If 'probe' is an array, setup a regex probe using the - * array of strings as pattern */ - - setup_regex_probe(p, probes); - - } else { - /* if 'probe' is 'builtin', set the probe to the - * appropriate builtin protocol */ - if (!strcmp(config_setting_get_string(probes), "builtin")) { - p->probe = get_probe(name); - if (!p->probe) { - fprintf(stderr, "%s: no builtin probe for this protocol\n", name); - exit(1); - } - } else { - fprintf(stderr, "%s: illegal probe name\n", name); - exit(1); - } + /* Probe-specific options: regex patterns */ + if (!strcmp(name, "regex")) { + patterns = config_setting_get_member(prot, "regex_patterns"); + if (patterns && config_setting_is_array(patterns)) { + setup_regex_probe(p, patterns); } } - sni_hostnames = config_setting_get_member(prot, "sni_hostnames"); - if (sni_hostnames && config_setting_is_array(sni_hostnames)) { - setup_sni_hostnames(p, sni_hostnames); + /* Probe-specific options: SNI hostnames */ + if (!strcmp(name, "sni")) { + sni_hostnames = config_setting_get_member(prot, "sni_hostnames"); + if (sni_hostnames && config_setting_is_array(sni_hostnames)) { + setup_sni_hostnames(p, sni_hostnames); + } } } } diff --git a/sslh.pod b/sslh.pod index c0ce606..a90d4bd 100644 --- a/sslh.pod +++ b/sslh.pod @@ -51,14 +51,10 @@ and the list of protocols). The configuration file makes it possible to specify protocols using regular expressions: a list of regular -expressions is given as the I parameter, and if the +expressions is given as the I parameter, and if the first packet received from the client matches any of these expressions, B connects to that protocol. -Alternatively, the I parameter can be set to -"builtin", to use the compiled probes which are much faster -than regular expressions. - =head2 Probing protocols When receiving an incoming connection, B will read the