diff --git a/Makefile b/Makefile
index aeafb7f..2fbeaa2 100644
--- a/Makefile
+++ b/Makefile
@@ -25,7 +25,7 @@ CC ?= gcc
 CFLAGS ?=-Wall -g $(CFLAGS_COV)
 
 LIBS=
-OBJS=common.o sslh-main.o probe.o tls.o
+OBJS=sslh-conf.o common.o sslh-main.o probe.o tls.o 
 
 CONDITIONAL_TARGETS=
 
@@ -72,6 +72,9 @@ sslh: sslh-fork sslh-select
 
 $(OBJS): version.h
 
+sslh-conf.c: sslhconf.cfg
+	conf2struct sslhconf.cfg
+
 sslh-fork: version.h $(OBJS) sslh-fork.o Makefile common.h
 	$(CC) $(CFLAGS) $(LDFLAGS) -o sslh-fork sslh-fork.o $(OBJS) $(LIBS)
 	#strip sslh-fork
@@ -112,7 +115,7 @@ uninstall:
 	update-rc.d sslh remove
 
 distclean: clean
-	rm -f tags cscope.*
+	rm -f tags sslh-conf.c sslh-conf.h cscope.*
 
 clean:
 	rm -f sslh-fork sslh-select echosrv version.h $(MAN) systemd-sslh-generator *.o *.gcov *.gcno *.gcda *.png *.html *.css *.info
diff --git a/common.c b/common.c
index 3409fc8..bc405b6 100644
--- a/common.c
+++ b/common.c
@@ -16,6 +16,7 @@
 
 #include "common.h"
 #include "probe.h"
+#include "sslh-conf.h"
 
 /* Added to make the code compilable under CYGWIN
  * */
@@ -30,18 +31,9 @@
 #endif
 
 /*
- * Settings that depend on the command line.  They're set in main(), but also
- * used in other places in common.c, and it'd be heavy-handed to pass it all as
- * parameters
+ * Settings that depend on the command line or the config file
  */
-int verbose = 0;
-int probing_timeout = 2;
-int inetd = 0;
-int foreground = 0;
-int background = 0;
-int transparent = 0;
-int numeric = 0;
-const char *user_name, *pid_file, *chroot_path, *facility = "auth";
+struct sslhcfg_item cfg;
 
 struct addrinfo *addr_listen = NULL; /* what addresses do we listen to? */
 
@@ -122,7 +114,7 @@ int start_listen_sockets(int *sockfd[], struct addrinfo *addr_list)
        exit(1);
    }
 
-   if (verbose)
+   if (cfg.verbose)
        fprintf(stderr, "listening to %d addresses\n", num_addr);
 
    *sockfd = malloc(num_addr * sizeof(*sockfd[0]));
@@ -272,9 +264,9 @@ int connect_addr(struct connection *cnx, int fd_from)
 
     for (a = cnx->proto->saddr; a; a = a->ai_next) {
         /* When transparent, make sure both connections use the same address family */
-        if (transparent && a->ai_family != from.ai_addr->sa_family)
+        if (cfg.transparent && a->ai_family != from.ai_addr->sa_family)
             continue;
-        if (verbose)
+        if (cfg.verbose)
             fprintf(stderr, "connecting to %s family %d len %d\n",
                     sprintaddr(buf, sizeof(buf), a),
                     a->ai_addr->sa_family, a->ai_addrlen);
@@ -283,16 +275,16 @@ int connect_addr(struct connection *cnx, int fd_from)
         fd = socket(a->ai_family, SOCK_STREAM, 0);
         if (fd == -1) {
             log_message(LOG_ERR, "forward to %s failed:socket: %s\n",
-                        cnx->proto->description, strerror(errno));
+                        cnx->proto->name, strerror(errno));
         } else {
-            if (transparent) {
+            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->description, strerror(errno));
+                            cnx->proto->name, strerror(errno));
                 close(fd);
             } else {
                 if (cnx->proto->keepalive) {
@@ -312,7 +304,7 @@ int defer_write(struct queue *q, void* data, int data_size)
 {
     char *p;
     ptrdiff_t data_offset = q->deferred_data - q->begin_deferred_data;
-    if (verbose)
+    if (cfg.verbose)
         fprintf(stderr, "**** writing deferred on fd %d\n", q->fd);
 
     p = realloc(q->begin_deferred_data, data_offset + q->deferred_data_size + data_size);
@@ -335,7 +327,7 @@ int flush_deferred(struct queue *q)
 {
     int n;
 
-    if (verbose)
+    if (cfg.verbose)
         fprintf(stderr, "flushing deferred data to fd %d\n", q->fd);
 
     n = write(q->fd, q->deferred_data, q->deferred_data_size);
@@ -363,7 +355,7 @@ void init_cnx(struct connection *cnx)
     memset(cnx, 0, sizeof(*cnx));
     cnx->q[0].fd = -1;
     cnx->q[1].fd = -1;
-    cnx->proto = get_first_protocol();
+    cnx->proto = NULL;
 }
 
 void dump_connection(struct connection *cnx)
@@ -395,7 +387,7 @@ int fd2fd(struct queue *target_q, struct queue *from_q)
    if (size_r == -1) {
        switch (errno) {
        case EAGAIN:
-           if (verbose)
+           if (cfg.verbose)
                fprintf(stderr, "reading 0 from %d\n", from);
            return FD_NODATA;
 
@@ -444,7 +436,7 @@ char* sprintaddr(char* buf, size_t size, struct addrinfo *a)
    res = getnameinfo(a->ai_addr, a->ai_addrlen,
                host, sizeof(host),
                serv, sizeof(serv),
-               numeric ? NI_NUMERICHOST | NI_NUMERICSERV : 0 );
+               cfg.numeric ? NI_NUMERICHOST | NI_NUMERICSERV : 0 );
 
    if (res) {
        log_message(LOG_ERR, "sprintaddr:getnameinfo: %s\n", gai_strerror(res));
@@ -469,26 +461,16 @@ char* sprintaddr(char* buf, size_t size, struct addrinfo *a)
 /* Turns a hostname and port (or service) into a list of struct addrinfo
  * returns 0 on success, -1 otherwise and logs error
  */
-int resolve_split_name(struct addrinfo **out, const char* ct_host, const char* serv)
+int resolve_split_name(struct addrinfo **out, char* host, char* serv)
 {
    struct addrinfo hint;
    char *end;
    int res;
-   char* host, *host_base;
 
    memset(&hint, 0, sizeof(hint));
    hint.ai_family = PF_UNSPEC;
    hint.ai_socktype = SOCK_STREAM;
 
-   /* Copy parameter so not to clobber data in libconfig */
-   res = asprintf(&host_base, "%s", ct_host);
-   if (res == -1) {
-       log_message(LOG_ERR, "asprintf: cannot allocate memory");
-       return -1;
-   }
-
-   host = host_base;
-
    /* If it is a RFC-Compliant IPv6 address ("[1234::12]:443"), remove brackets
     * around IP address */
    if (host[0] == '[') {
@@ -504,7 +486,6 @@ int resolve_split_name(struct addrinfo **out, const char* ct_host, const char* s
    res = getaddrinfo(host, serv, &hint, out);
    if (res)
       log_message(LOG_ERR, "%s `%s:%s'\n", gai_strerror(res), host, serv);
-   free(host_base);
    return res;
 }
 
@@ -543,7 +524,7 @@ void log_message(int type, char* msg, ...)
     va_list ap;
 
     va_start(ap, msg);
-    if (foreground)
+    if (cfg.foreground)
         vfprintf(stderr, msg, ap);
     else
         vsyslog(type, msg, ap);
@@ -587,7 +568,7 @@ void log_connection(struct connection *cnx)
     sprintaddr(local, sizeof(local), &addr);
 
     log_message(LOG_INFO, "%s:connection from %s to %s forwarded from %s to %s\n",
-                cnx->proto->description,
+                cnx->proto->name,
                 peer,
                 service,
                 local,
@@ -618,22 +599,22 @@ int check_access_rights(int in_socket, const char* service)
     /* extract peer address */
     res = getnameinfo(&peer.saddr, size, addr_str, sizeof(addr_str), NULL, 0, NI_NUMERICHOST);
     if (res) {
-        if (verbose)
+        if (cfg.verbose)
             fprintf(stderr, "getnameinfo(NI_NUMERICHOST):%s\n", gai_strerror(res));
         strcpy(addr_str, STRING_UNKNOWN);
     }
     /* extract peer name */
     strcpy(host, STRING_UNKNOWN);
-    if (!numeric) {
+    if (!cfg.numeric) {
         res = getnameinfo(&peer.saddr, size, host, sizeof(host), NULL, 0, NI_NAMEREQD);
         if (res) {
-            if (verbose)
+            if (cfg.verbose)
                 fprintf(stderr, "getnameinfo(NI_NAMEREQD):%s\n", gai_strerror(res));
         }
     }
 
     if (!hosts_ctl(service, host, addr_str, STRING_UNKNOWN)) {
-        if (verbose)
+        if (cfg.verbose)
             fprintf(stderr, "access denied\n");
         log_message(LOG_INFO, "connection from %s(%s): access denied", host, addr_str);
         close(in_socket);
@@ -681,10 +662,10 @@ void setup_syslog(const char* bin_name) {
     CHECK_RES_DIE(res, "asprintf");
 
     for (fn = 0; facilitynames[fn].c_val != -1; fn++)
-        if (strcmp(facilitynames[fn].c_name, facility) == 0)
+        if (strcmp(facilitynames[fn].c_name, cfg.syslog_facility) == 0)
             break;
     if (facilitynames[fn].c_val == -1) {
-        fprintf(stderr, "Unknown facility %s\n", facility);
+        fprintf(stderr, "Unknown facility %s\n", cfg.syslog_facility);
         exit(1);
     }
 
@@ -715,7 +696,7 @@ void set_capabilities(void) {
     cap_value_t cap_list[10];
     int ncap = 0;
 
-    if (transparent)
+    if (cfg.transparent)
         cap_list[ncap++] = CAP_NET_ADMIN;
 
     caps = cap_init();
@@ -757,12 +738,12 @@ void drop_privileges(const char* user_name, const char* chroot_path)
             fprintf(stderr, "%s: not found\n", user_name);
             exit(2);
         }
-        if (verbose)
+        if (cfg.verbose)
             fprintf(stderr, "turning into %s\n", user_name);
     }
 
     if (chroot_path) {
-        if (verbose)
+        if (cfg.verbose)
             fprintf(stderr, "chrooting into %s\n", chroot_path);
 
         res = chroot(chroot_path);
diff --git a/common.h b/common.h
index e8fad3e..97beb47 100644
--- a/common.h
+++ b/common.h
@@ -87,7 +87,7 @@ struct queue {
 struct connection {
     enum connection_state state;
     time_t probe_timeout;
-    struct proto *proto;
+    struct sslhcfg_protocols_item* proto;
 
     /* q[0]: queue for external connection (client);
      * q[1]: queue for internal connection (httpd or sshd);
@@ -114,7 +114,7 @@ void drop_privileges(const char* user_name, const char* chroot_path);
 void write_pid_file(const char* pidfile);
 void log_message(int type, char* msg, ...);
 void dump_connection(struct connection *cnx);
-int resolve_split_name(struct addrinfo **out, const char* hostname, const char* port);
+int resolve_split_name(struct addrinfo **out, char* hostname, char* port);
 
 int start_listen_sockets(int *sockfd[], struct addrinfo *addr_list);
 
@@ -123,6 +123,7 @@ int flush_deferred(struct queue *q);
 
 extern int probing_timeout, verbose, inetd, foreground, 
        background, transparent, numeric;
+extern struct sslhcfg_item cfg;
 extern struct sockaddr_storage addr_ssl, addr_ssh, addr_openvpn;
 extern struct addrinfo *addr_listen;
 extern const char* USAGE_STRING;
diff --git a/echosrv.c b/echosrv.c
index 65a85c3..e5838d2 100644
--- a/echosrv.c
+++ b/echosrv.c
@@ -53,6 +53,8 @@ const char* server_type = "echsrv"; /* keep setup_syslog happy */
 char* prefix = "";
 int port;
 
+int verbose, numeric;
+
 void parse_cmdline(int argc, char* argv[])
 {
     int c;
diff --git a/example.cfg b/example.cfg
index 182f508..dc9e650 100644
--- a/example.cfg
+++ b/example.cfg
@@ -66,7 +66,10 @@ listen:
  
 protocols:
 (
-     { name: "ssh"; service: "ssh"; host: "localhost"; port: "22"; keepalive: true; fork: true; },
+     { name: "ssh"; service: "ssh"; host: "localhost"; port: "22"; keepalive: true; fork: true;
+listen: ( { host: "hello"; port: "xmpp" }, { host: "world";
+port: "dns" } ), 
+     },
      { name: "http"; host: "localhost"; port: "80"; },
 
 # match BOTH ALPN/SNI
diff --git a/probe.c b/probe.c
index 15ff8b9..aaf4ac8 100644
--- a/probe.c
+++ b/probe.c
@@ -33,35 +33,36 @@
 
 
 
-static int is_ssh_protocol(const char *p, int len, struct proto*);
-static int is_openvpn_protocol(const char *p, int len, struct proto*);
-static int is_tinc_protocol(const char *p, int len, struct proto*);
-static int is_xmpp_protocol(const char *p, int len, struct proto*);
-static int is_http_protocol(const char *p, int len, struct proto*);
-static int is_tls_protocol(const char *p, int len, struct proto*);
-static int is_adb_protocol(const char *p, int len, struct proto*);
-static int is_socks5_protocol(const char *p, int len, struct proto*);
-static int is_true(const char *p, int len, struct proto* proto) { return 1; }
+static int is_ssh_protocol(const char *p, int len, struct sslhcfg_protocols_item*);
+static int is_openvpn_protocol(const char *p, int len, struct sslhcfg_protocols_item*);
+static int is_tinc_protocol(const char *p, int len, struct sslhcfg_protocols_item*);
+static int is_xmpp_protocol(const char *p, int len, struct sslhcfg_protocols_item*);
+static int is_http_protocol(const char *p, int len, struct sslhcfg_protocols_item*);
+static int is_tls_protocol(const char *p, int len, struct sslhcfg_protocols_item*);
+static int is_adb_protocol(const char *p, int len, struct sslhcfg_protocols_item*);
+static int is_socks5_protocol(const char *p, int len, struct sslhcfg_protocols_item*);
+static int is_true(const char *p, int len, struct sslhcfg_protocols_item* proto) { return 1; }
 
 /* Table of protocols that have a built-in probe
  */
-static struct proto builtins[] = {
-    /* description   service  saddr  log_level  keepalive  fork  probe  */
-    { "ssh",         "sshd",   NULL,  1,        0,         1,    is_ssh_protocol},
-    { "openvpn",     NULL,     NULL,  1,        0,         1,    is_openvpn_protocol },
-    { "tinc",        NULL,     NULL,  1,        0,         1,    is_tinc_protocol },
-    { "xmpp",        NULL,     NULL,  1,        0,         0,    is_xmpp_protocol },
-    { "http",        NULL,     NULL,  1,        0,         0,    is_http_protocol },
-    { "tls",         NULL,     NULL,  1,        0,         0,    is_tls_protocol },
-    { "adb",         NULL,     NULL,  1,        0,         0,    is_adb_protocol },
-    { "socks5",      NULL,     NULL,  1,        0,         0,    is_socks5_protocol },
-    { "anyprot",     NULL,     NULL,  1,        0,         0,    is_true }
+static struct protocol_probe_desc builtins[] = {
+    /* description  probe  */
+    { "ssh",        is_ssh_protocol},
+    { "openvpn",    is_openvpn_protocol },
+    { "tinc",       is_tinc_protocol },
+    { "xmpp",       is_xmpp_protocol },
+    { "http",       is_http_protocol },
+    { "tls",        is_tls_protocol },
+    { "ssl",        is_tls_protocol },
+    { "adb",        is_adb_protocol },
+    { "socks5",     is_socks5_protocol },
+    { "anyprot",    is_true }
 };
 
-static struct proto *protocols;
 static char* on_timeout = "ssh";
 
-struct proto*  get_builtins(void) {
+/* TODO I think this has to go */
+struct protocol_probe_desc*  get_builtins(void) {
     return builtins;
 }
 
@@ -79,24 +80,15 @@ void set_ontimeout(const char* name)
 /* Returns the protocol to connect to in case of timeout; 
  * if not found, return the first protocol specified 
  */
-struct proto* timeout_protocol(void) 
+struct sslhcfg_protocols_item* timeout_protocol(void) 
 {
-    struct proto* p = get_first_protocol();
-    for (; p && strcmp(p->description, on_timeout); p = p->next);
-    if (p) return p;
-    return get_first_protocol();
+    int i;
+    for (i = 0; i < cfg.protocols_len; i++) {
+        if (!strcmp(cfg.protocols[i].name, on_timeout)) return &cfg.protocols[i];
+    }
+    return &cfg.protocols[0];
 }
 
-/* returns the first protocol (caller can then follow the *next pointers) */
-struct proto* get_first_protocol(void)
-{
-    return protocols;
-}
-
-void set_protocol_list(struct proto* prots)
-{
-    protocols = prots;
-}
 
 /* From http://grapsus.net/blog/post/Hexadecimal-dump-in-C */
 #define HEXDUMP_COLS 16
@@ -132,7 +124,7 @@ void hexdump(const char *mem, unsigned int len)
 }
 
 /* Is the buffer the beginning of an SSH connection? */
-static int is_ssh_protocol(const char *p, int len, struct proto *proto)
+static int is_ssh_protocol(const char *p, int len, struct sslhcfg_protocols_item* proto)
 {
     if (len < 4)
         return PROBE_AGAIN;
@@ -150,7 +142,7 @@ static int is_ssh_protocol(const char *p, int len, struct proto *proto)
  * http://www.fengnet.com/book/vpns%20illustrated%20tunnels%20%20vpnsand%20ipsec/ch08lev1sec5.html
  * and OpenVPN ssl.c, ssl.h and options.c
  */
-static int is_openvpn_protocol (const char*p,int len, struct proto *proto)
+static int is_openvpn_protocol (const char*p,int len, struct sslhcfg_protocols_item* proto)
 {
     int packet_len;
 
@@ -165,7 +157,7 @@ static int is_openvpn_protocol (const char*p,int len, struct proto *proto)
  * Protocol is documented here: http://www.tinc-vpn.org/documentation/tinc.pdf
  * First connection starts with "0 " in 1.0.15)
  * */
-static int is_tinc_protocol( const char *p, int len, struct proto *proto)
+static int is_tinc_protocol( const char *p, int len, struct sslhcfg_protocols_item* proto)
 {
     if (len < 2)
         return PROBE_AGAIN;
@@ -177,7 +169,7 @@ static int is_tinc_protocol( const char *p, int len, struct proto *proto)
  * (Protocol is documented (http://tools.ietf.org/html/rfc6120) but for lazy
  * clients, just checking first frame containing "jabber" in xml entity)
  * */
-static int is_xmpp_protocol( const char *p, int len, struct proto *proto)
+static int is_xmpp_protocol( const char *p, int len, struct sslhcfg_protocols_item* proto)
 {
     if (memmem(p, len, "jabber", 6))
         return PROBE_MATCH;
@@ -200,7 +192,7 @@ static int probe_http_method(const char *p, int len, const char *opt)
 }
 
 /* Is the buffer the beginning of an HTTP connection?  */
-static int is_http_protocol(const char *p, int len, struct proto *proto)
+static int is_http_protocol(const char *p, int len, struct sslhcfg_protocols_item* proto)
 {
     int res;
     /* If it's got HTTP in the request (HTTP/1.1) then it's HTTP */
@@ -226,7 +218,7 @@ static int is_http_protocol(const char *p, int len, struct proto *proto)
 }
 
 /* Says if it's TLS, optionally with SNI and ALPN lists in proto->data */
-static int is_tls_protocol(const char *p, int len, struct proto *proto)
+static int is_tls_protocol(const char *p, int len, struct sslhcfg_protocols_item* proto)
 {
     switch (parse_tls_header(proto->data, p, len)) {
     case TLS_MATCH: return PROBE_MATCH;
@@ -246,7 +238,7 @@ static int probe_adb_cnxn_message(const char *p)
     return !memcmp(&p[0], "CNXN", 4) && !memcmp(&p[24], "host:", 5);
 }
 
-static int is_adb_protocol(const char *p, int len, struct proto *proto)
+static int is_adb_protocol(const char *p, int len, struct sslhcfg_protocols_item* proto)
 {
     /* amessage.data_length is not being checked, under the assumption that
      * a packet >= 30 bytes will have "something" in the payload field.
@@ -285,7 +277,7 @@ static int is_adb_protocol(const char *p, int len, struct proto *proto)
     return probe_adb_cnxn_message(&p[sizeof(empty_message)]);
 }
 
-static int is_socks5_protocol(const char *p_in, int len, struct proto *proto)
+static int is_socks5_protocol(const char *p_in, int len, struct sslhcfg_protocols_item* proto)
 {
     unsigned char* p = (unsigned char*)p_in;
     int i;
@@ -318,7 +310,7 @@ static int is_socks5_protocol(const char *p_in, int len, struct proto *proto)
     return PROBE_MATCH;
 }
 
-static int regex_probe(const char *p, int len, struct proto *proto)
+static int regex_probe(const char *p, int len, struct sslhcfg_protocols_item* proto)
 {
 #ifdef ENABLE_REGEX
     regex_t **probe = proto->data;
@@ -344,8 +336,8 @@ static int regex_probe(const char *p, int len, struct proto *proto)
 int probe_client_protocol(struct connection *cnx)
 {
     char buffer[BUFSIZ];
-    struct proto *p, *last_p = cnx->proto;
-    int n, res, again = 0;
+    struct sslhcfg_protocols_item* p;
+    int i, n, res, again = 0;
 
     n = read(cnx->q[0].fd, buffer, sizeof(buffer));
     /* It's possible that read() returns an error, e.g. if the client
@@ -355,26 +347,26 @@ int probe_client_protocol(struct connection *cnx)
      * connection will just fail later normally). */
 
     if (n > 0) {
-        if (verbose > 1) {
+        if (cfg.verbose > 1) {
             fprintf(stderr, "hexdump of incoming packet:\n");
             hexdump(buffer, n);
         }
         defer_write(&cnx->q[1], buffer, n);
     }
 
-    for (p = cnx->proto; p; p = p->next) {
+    for (i = 0; i < cfg.protocols_len; i++) {
         char* probe_str[3] = {"PROBE_NEXT", "PROBE_MATCH", "PROBE_AGAIN"};
+        p = &cfg.protocols[i];
+
         if (! p->probe) continue;
 
         /* Don't probe last protocol if it is anyprot (and store last protocol) */
-        if (! p->next) {
-            last_p = p;
-            if (!strcmp(p->description, "anyprot"))
-                break;
-        }
+        if ((i == cfg.protocols_len - 1) && (!strcmp(p->name, "anyprot")))
+            break;
 
+        if (cfg.verbose) fprintf(stderr, "probing for %s\n", p->name);
         res = p->probe(cnx->q[1].begin_deferred_data, cnx->q[1].deferred_data_size, p);
-        if (verbose) fprintf(stderr, "probing for %s: %s\n", p->description, probe_str[res]);
+        if (cfg.verbose) fprintf(stderr, "probed for %s: %s\n", p->name, probe_str[res]);
 
         if (res == PROBE_MATCH) {
             cnx->proto = p;
@@ -387,31 +379,21 @@ int probe_client_protocol(struct connection *cnx)
         return PROBE_AGAIN;
 
     /* Everything failed: match the last one */
-    cnx->proto = last_p;
+    cnx->proto = &cfg.protocols[cfg.protocols_len-1];
     return PROBE_MATCH;
 }
 
-/* Returns the structure for specified protocol or NULL if not found */
-static struct proto* get_protocol(const char* description)
-{
-    int i;
-
-    for (i = 0; i < ARRAY_SIZE(builtins); i++) {
-        if (!strcmp(builtins[i].description, description)) {
-            return &builtins[i];
-        }
-    }
-    return NULL;
-}
-
 /* Returns the probe for specified protocol:
  * parameter is the description in builtins[], or "regex" 
  * */
 T_PROBE* get_probe(const char* description) {
-    struct proto* p = get_protocol(description);
+    int i;
 
-    if (p)
-        return p->probe;
+    for (i = 0; i < ARRAY_SIZE(builtins); i++) {
+        if (!strcmp(builtins[i].name, description)) {
+            return builtins[i].probe;
+        }
+    }
 
     /* Special case of "regex" probe (we don't want to set it in builtins
      * because builtins is also used to build the command-line options and
diff --git a/probe.h b/probe.h
index 2476542..5ee199c 100644
--- a/probe.h
+++ b/probe.h
@@ -12,30 +12,19 @@ typedef enum {
     PROBE_AGAIN, /* Not enough data for this probe, try again with more data */
 } probe_result;
 
-struct proto;
-typedef int T_PROBE(const char*, int, struct proto*);
+struct sslhcfg_protocols_item;
+typedef int T_PROBE(const char*, int, struct sslhcfg_protocols_item*);
 
-/* For each protocol we need: */
-struct proto {
-    const char* description;  /* a string that says what it is (for logging and command-line parsing) */
-    const char* service;      /* service name to do libwrap checks */
-    struct addrinfo *saddr; /* list of addresses to try and switch that protocol */
-    int log_level;  /* 0: No logging of connection
-                     * 1: Log incoming connection
-                     */
-    int keepalive; /* 0: No keepalive ; 1: Set Keepalive for this connection */
-    int fork; /* 0: Connection can run within shared process ; 1: Separate process required for this connection */
-
-    /* function to probe that protocol; parameters are buffer and length
-     * containing the data to probe, and a pointer to the protocol structure */
+struct protocol_probe_desc {
+    const char* name;
     T_PROBE* probe;
-    /* opaque pointer ; used to pass list of regex to regex probe, or TLSProtocol struct to sni/alpn probe */
-    void* data;
-    struct proto *next; /* pointer to next protocol in list, NULL if last */
 };
 
+
+#include "sslh-conf.h"
+
 /* Returns a pointer to the array of builtin protocols */
-struct proto * get_builtins(void);
+struct protocol_probe_desc* get_builtins(void);
 
 /* Returns the number of builtin protocols */
 int get_num_builtins(void);
@@ -44,10 +33,10 @@ int get_num_builtins(void);
 T_PROBE* get_probe(const char* description);
 
 /* Returns the head of the configured protocols */
-struct proto* get_first_protocol(void);
+struct sslhcfg_protocols_item* get_first_protocol(void);
 
 /* Set the list of configured protocols */
-void set_protocol_list(struct proto*);
+void set_protocol_list(struct sslhcfg_protocols_item*);
 
 /* probe_client_protocol
  *
@@ -65,7 +54,7 @@ void set_ontimeout(const char* name);
  *
  * Returns the protocol to connect to in case of timeout
  */
-struct proto* timeout_protocol(void);
+struct sslhcfg_protocols_item* timeout_protocol(void);
 
 void hexdump(const char*, unsigned int);
 
diff --git a/sslh-conf.c b/sslh-conf.c
index 2d59e99..c5afa62 100644
--- a/sslh-conf.c
+++ b/sslh-conf.c
@@ -1,273 +1,318 @@
 /* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct)
- * on Thu Nov 22 17:16:04 2018. */
+ * on Fri Dec  7 08:27:14 2018. */
 
 
 
+#define _GNU_SOURCE
 #include <string.h>
 #include <libconfig.h>
 #include <stdlib.h>
 #include "sslh-conf.h"
 
-static int config_protocols_parser(
+static int sslhcfg_protocols_parser(
         config_setting_t* cfg, 
-        struct config_protocols_items* config_protocols, 
+        struct sslhcfg_protocols_item* sslhcfg_protocols, 
         const char** errmsg) 
 {
         config_setting_t* setting;
+        char* tmp;
         *errmsg = NULL;
 
         if (config_setting_lookup(cfg, "name")) {
-            if (config_setting_lookup_string(cfg, "name", &config_protocols->name) == CONFIG_FALSE) {
+            if (config_setting_lookup_string(cfg, "name", &sslhcfg_protocols->name) == CONFIG_FALSE) {
                 *errmsg = "Parsing of option \"name\" failed";
                 return 0;
             }
         } else {
-            *errmsg = "Mandatory option \"name\" is missing";
+            *errmsg = "Mandatory option \"sslhcfg_protocols.name\" is missing";
             return 0;
         }
         if (config_setting_lookup(cfg, "host")) {
-            if (config_setting_lookup_string(cfg, "host", &config_protocols->host) == CONFIG_FALSE) {
+            if (config_setting_lookup_string(cfg, "host", &sslhcfg_protocols->host) == CONFIG_FALSE) {
                 *errmsg = "Parsing of option \"host\" failed";
                 return 0;
             }
         } else {
-            *errmsg = "Mandatory option \"host\" is missing";
+            *errmsg = "Mandatory option \"sslhcfg_protocols.host\" is missing";
             return 0;
         }
+        if (asprintf(&tmp, "%s", sslhcfg_protocols->host) == -1) {
+            *errmsg = "asprintf: cannot allocate memory";
+            return 0;
+        }
+        sslhcfg_protocols->host = tmp;
         if (config_setting_lookup(cfg, "port")) {
-            if (config_setting_lookup_string(cfg, "port", &config_protocols->port) == CONFIG_FALSE) {
+            if (config_setting_lookup_string(cfg, "port", &sslhcfg_protocols->port) == CONFIG_FALSE) {
                 *errmsg = "Parsing of option \"port\" failed";
                 return 0;
             }
         } else {
-            *errmsg = "Mandatory option \"port\" is missing";
+            *errmsg = "Mandatory option \"sslhcfg_protocols.port\" is missing";
             return 0;
         }
-        config_protocols->service = NULL;
+        if (asprintf(&tmp, "%s", sslhcfg_protocols->port) == -1) {
+            *errmsg = "asprintf: cannot allocate memory";
+            return 0;
+        }
+        sslhcfg_protocols->port = tmp;
+        sslhcfg_protocols->service = NULL;
         if (config_setting_lookup(cfg, "service")) {
-            if (config_setting_lookup_string(cfg, "service", &config_protocols->service) == CONFIG_FALSE) {
-                *errmsg = "Parsing of option service failed";
+            if (config_setting_lookup_string(cfg, "service", &sslhcfg_protocols->service) == CONFIG_FALSE) {
+                *errmsg = "Parsing of option \"service\" failed";
                 return 0;
             } else {
-		config_protocols->service_is_present = 1;
+		sslhcfg_protocols->service_is_present = 1;
 	}
 ;
         }
-        config_protocols->fork = 0;
+        sslhcfg_protocols->fork = 0;
         if (config_setting_lookup(cfg, "fork")) {
-            if (config_setting_lookup_bool(cfg, "fork", &config_protocols->fork) == CONFIG_FALSE) {
-                *errmsg = "Parsing of option fork failed";
+            if (config_setting_lookup_bool(cfg, "fork", &sslhcfg_protocols->fork) == CONFIG_FALSE) {
+                *errmsg = "Parsing of option \"fork\" failed";
                 return 0;
             } ;
         }
-        config_protocols->log_level = 1;
+        sslhcfg_protocols->log_level = 1;
         if (config_setting_lookup(cfg, "log_level")) {
-            if (config_setting_lookup_int(cfg, "log_level", &config_protocols->log_level) == CONFIG_FALSE) {
-                *errmsg = "Parsing of option log_level failed";
+            if (config_setting_lookup_int(cfg, "log_level", &sslhcfg_protocols->log_level) == CONFIG_FALSE) {
+                *errmsg = "Parsing of option \"log_level\" failed";
                 return 0;
             } ;
         }
-        config_protocols->keepalive = 0;
+        sslhcfg_protocols->keepalive = 0;
         if (config_setting_lookup(cfg, "keepalive")) {
-            if (config_setting_lookup_bool(cfg, "keepalive", &config_protocols->keepalive) == CONFIG_FALSE) {
-                *errmsg = "Parsing of option keepalive failed";
+            if (config_setting_lookup_bool(cfg, "keepalive", &sslhcfg_protocols->keepalive) == CONFIG_FALSE) {
+                *errmsg = "Parsing of option \"keepalive\" failed";
                 return 0;
             } ;
         }
-        config_protocols->sni_hostnames = NULL;
-        config_protocols->sni_hostnames_len = 0;
+        sslhcfg_protocols->sni_hostnames = NULL;
+        sslhcfg_protocols->sni_hostnames_len = 0;
         if ((setting = config_setting_lookup(cfg, "sni_hostnames"))) {
             int len = config_setting_length(setting);
-            config_protocols->sni_hostnames = malloc(len * sizeof(*config_protocols->sni_hostnames));
-            config_protocols->sni_hostnames_len = len;
+            sslhcfg_protocols->sni_hostnames = malloc(len * sizeof(*sslhcfg_protocols->sni_hostnames));
+            sslhcfg_protocols->sni_hostnames_len = len;
             for (int i = 0; i < len; i++) {
                 config_setting_t* s = config_setting_get_elem(setting, i);
-                config_protocols->sni_hostnames[i] = config_setting_get_string(s);
+                sslhcfg_protocols->sni_hostnames[i] = config_setting_get_string(s);
             }
         }
-        config_protocols->alpn_protocols = NULL;
-        config_protocols->alpn_protocols_len = 0;
+        sslhcfg_protocols->alpn_protocols = NULL;
+        sslhcfg_protocols->alpn_protocols_len = 0;
         if ((setting = config_setting_lookup(cfg, "alpn_protocols"))) {
             int len = config_setting_length(setting);
-            config_protocols->alpn_protocols = malloc(len * sizeof(*config_protocols->alpn_protocols));
-            config_protocols->alpn_protocols_len = len;
+            sslhcfg_protocols->alpn_protocols = malloc(len * sizeof(*sslhcfg_protocols->alpn_protocols));
+            sslhcfg_protocols->alpn_protocols_len = len;
             for (int i = 0; i < len; i++) {
                 config_setting_t* s = config_setting_get_elem(setting, i);
-                config_protocols->alpn_protocols[i] = config_setting_get_string(s);
+                sslhcfg_protocols->alpn_protocols[i] = config_setting_get_string(s);
             }
         }
-        config_protocols->regex_patterns = NULL;
-        config_protocols->regex_patterns_len = 0;
+        sslhcfg_protocols->regex_patterns = NULL;
+        sslhcfg_protocols->regex_patterns_len = 0;
         if ((setting = config_setting_lookup(cfg, "regex_patterns"))) {
             int len = config_setting_length(setting);
-            config_protocols->regex_patterns = malloc(len * sizeof(*config_protocols->regex_patterns));
-            config_protocols->regex_patterns_len = len;
+            sslhcfg_protocols->regex_patterns = malloc(len * sizeof(*sslhcfg_protocols->regex_patterns));
+            sslhcfg_protocols->regex_patterns_len = len;
             for (int i = 0; i < len; i++) {
                 config_setting_t* s = config_setting_get_elem(setting, i);
-                config_protocols->regex_patterns[i] = config_setting_get_string(s);
+                sslhcfg_protocols->regex_patterns[i] = config_setting_get_string(s);
             }
         }
 	return 1;
 }
 
-static int config_listen_parser(
+static int sslhcfg_listen_parser(
         config_setting_t* cfg, 
-        struct config_listen_items* config_listen, 
+        struct sslhcfg_listen_item* sslhcfg_listen, 
         const char** errmsg) 
 {
         config_setting_t* setting;
+        char* tmp;
         *errmsg = NULL;
 
         if (config_setting_lookup(cfg, "host")) {
-            if (config_setting_lookup_string(cfg, "host", &config_listen->host) == CONFIG_FALSE) {
+            if (config_setting_lookup_string(cfg, "host", &sslhcfg_listen->host) == CONFIG_FALSE) {
                 *errmsg = "Parsing of option \"host\" failed";
                 return 0;
             }
         } else {
-            *errmsg = "Mandatory option \"host\" is missing";
+            *errmsg = "Mandatory option \"sslhcfg_listen.host\" is missing";
             return 0;
         }
+        if (asprintf(&tmp, "%s", sslhcfg_listen->host) == -1) {
+            *errmsg = "asprintf: cannot allocate memory";
+            return 0;
+        }
+        sslhcfg_listen->host = tmp;
         if (config_setting_lookup(cfg, "port")) {
-            if (config_setting_lookup_string(cfg, "port", &config_listen->port) == CONFIG_FALSE) {
+            if (config_setting_lookup_string(cfg, "port", &sslhcfg_listen->port) == CONFIG_FALSE) {
                 *errmsg = "Parsing of option \"port\" failed";
                 return 0;
             }
         } else {
-            *errmsg = "Mandatory option \"port\" is missing";
+            *errmsg = "Mandatory option \"sslhcfg_listen.port\" is missing";
             return 0;
         }
-        config_listen->keepalive = 0;
+        if (asprintf(&tmp, "%s", sslhcfg_listen->port) == -1) {
+            *errmsg = "asprintf: cannot allocate memory";
+            return 0;
+        }
+        sslhcfg_listen->port = tmp;
+        sslhcfg_listen->keepalive = 0;
         if (config_setting_lookup(cfg, "keepalive")) {
-            if (config_setting_lookup_bool(cfg, "keepalive", &config_listen->keepalive) == CONFIG_FALSE) {
-                *errmsg = "Parsing of option keepalive failed";
+            if (config_setting_lookup_bool(cfg, "keepalive", &sslhcfg_listen->keepalive) == CONFIG_FALSE) {
+                *errmsg = "Parsing of option \"keepalive\" failed";
                 return 0;
             } ;
         }
 	return 1;
 }
 
-int config_parser(
+static int sslhcfg_parser(
         config_setting_t* cfg, 
-        struct config_items* config, 
+        struct sslhcfg_item* sslhcfg, 
         const char** errmsg) 
 {
         config_setting_t* setting;
+        char* tmp;
         *errmsg = NULL;
 
-        config->verbose = 0;
+        sslhcfg->verbose = 0;
         if (config_setting_lookup(cfg, "verbose")) {
-            if (config_setting_lookup_bool(cfg, "verbose", &config->verbose) == CONFIG_FALSE) {
-                *errmsg = "Parsing of option verbose failed";
+            if (config_setting_lookup_int(cfg, "verbose", &sslhcfg->verbose) == CONFIG_FALSE) {
+                *errmsg = "Parsing of option \"verbose\" failed";
                 return 0;
             } ;
         }
-        config->foreground = 0;
+        sslhcfg->foreground = 0;
         if (config_setting_lookup(cfg, "foreground")) {
-            if (config_setting_lookup_bool(cfg, "foreground", &config->foreground) == CONFIG_FALSE) {
-                *errmsg = "Parsing of option foreground failed";
+            if (config_setting_lookup_bool(cfg, "foreground", &sslhcfg->foreground) == CONFIG_FALSE) {
+                *errmsg = "Parsing of option \"foreground\" failed";
                 return 0;
             } ;
         }
-        config->inetd = 0;
+        sslhcfg->inetd = 0;
         if (config_setting_lookup(cfg, "inetd")) {
-            if (config_setting_lookup_bool(cfg, "inetd", &config->inetd) == CONFIG_FALSE) {
-                *errmsg = "Parsing of option inetd failed";
+            if (config_setting_lookup_bool(cfg, "inetd", &sslhcfg->inetd) == CONFIG_FALSE) {
+                *errmsg = "Parsing of option \"inetd\" failed";
                 return 0;
             } ;
         }
-        config->numeric = 0;
+        sslhcfg->numeric = 0;
         if (config_setting_lookup(cfg, "numeric")) {
-            if (config_setting_lookup_bool(cfg, "numeric", &config->numeric) == CONFIG_FALSE) {
-                *errmsg = "Parsing of option numeric failed";
+            if (config_setting_lookup_bool(cfg, "numeric", &sslhcfg->numeric) == CONFIG_FALSE) {
+                *errmsg = "Parsing of option \"numeric\" failed";
                 return 0;
             } ;
         }
-        config->transparent = 0;
+        sslhcfg->transparent = 0;
         if (config_setting_lookup(cfg, "transparent")) {
-            if (config_setting_lookup_bool(cfg, "transparent", &config->transparent) == CONFIG_FALSE) {
-                *errmsg = "Parsing of option transparent failed";
+            if (config_setting_lookup_bool(cfg, "transparent", &sslhcfg->transparent) == CONFIG_FALSE) {
+                *errmsg = "Parsing of option \"transparent\" failed";
                 return 0;
             } ;
         }
-        config->timeout = 2;
+        sslhcfg->timeout = 2;
         if (config_setting_lookup(cfg, "timeout")) {
-            if (config_setting_lookup_int(cfg, "timeout", &config->timeout) == CONFIG_FALSE) {
-                *errmsg = "Parsing of option timeout failed";
+            if (config_setting_lookup_int(cfg, "timeout", &sslhcfg->timeout) == CONFIG_FALSE) {
+                *errmsg = "Parsing of option \"timeout\" failed";
                 return 0;
             } ;
         }
-        config->user = NULL;
+        sslhcfg->user = NULL;
         if (config_setting_lookup(cfg, "user")) {
-            if (config_setting_lookup_string(cfg, "user", &config->user) == CONFIG_FALSE) {
-                *errmsg = "Parsing of option user failed";
+            if (config_setting_lookup_string(cfg, "user", &sslhcfg->user) == CONFIG_FALSE) {
+                *errmsg = "Parsing of option \"user\" failed";
                 return 0;
             } else {
-		config->user_is_present = 1;
+		sslhcfg->user_is_present = 1;
 	}
 ;
         }
-        config->pidfile = NULL;
+        sslhcfg->pidfile = NULL;
         if (config_setting_lookup(cfg, "pidfile")) {
-            if (config_setting_lookup_string(cfg, "pidfile", &config->pidfile) == CONFIG_FALSE) {
-                *errmsg = "Parsing of option pidfile failed";
+            if (config_setting_lookup_string(cfg, "pidfile", &sslhcfg->pidfile) == CONFIG_FALSE) {
+                *errmsg = "Parsing of option \"pidfile\" failed";
                 return 0;
             } else {
-		config->pidfile_is_present = 1;
+		sslhcfg->pidfile_is_present = 1;
 	}
 ;
         }
+        sslhcfg->chroot = NULL;
         if (config_setting_lookup(cfg, "chroot")) {
-            if (config_setting_lookup_string(cfg, "chroot", &config->chroot) == CONFIG_FALSE) {
+            if (config_setting_lookup_string(cfg, "chroot", &sslhcfg->chroot) == CONFIG_FALSE) {
                 *errmsg = "Parsing of option \"chroot\" failed";
                 return 0;
-            }
-        } else {
-            *errmsg = "Mandatory option \"chroot\" is missing";
-            return 0;
+            } else {
+		sslhcfg->chroot_is_present = 1;
+	}
+;
         }
-        config->syslog_facility = "auth";
+        sslhcfg->syslog_facility = "auth";
         if (config_setting_lookup(cfg, "syslog_facility")) {
-            if (config_setting_lookup_string(cfg, "syslog_facility", &config->syslog_facility) == CONFIG_FALSE) {
-                *errmsg = "Parsing of option syslog_facility failed";
+            if (config_setting_lookup_string(cfg, "syslog_facility", &sslhcfg->syslog_facility) == CONFIG_FALSE) {
+                *errmsg = "Parsing of option \"syslog_facility\" failed";
                 return 0;
             } ;
         }
-        config->on_timeout = "ssh";
+        sslhcfg->on_timeout = "ssh";
         if (config_setting_lookup(cfg, "on_timeout")) {
-            if (config_setting_lookup_string(cfg, "on_timeout", &config->on_timeout) == CONFIG_FALSE) {
-                *errmsg = "Parsing of option on_timeout failed";
+            if (config_setting_lookup_string(cfg, "on_timeout", &sslhcfg->on_timeout) == CONFIG_FALSE) {
+                *errmsg = "Parsing of option \"on_timeout\" failed";
                 return 0;
             } ;
         }
 
-        config->listen = NULL;
-        config->listen_len = 0;
+        sslhcfg->listen = NULL;
+        sslhcfg->listen_len = 0;
         if ((setting = config_setting_lookup(cfg, "listen"))) {
             int len = config_setting_length(setting);
-            config->listen = malloc(len * sizeof(*config->listen));
-            config->listen_len = len;
+            sslhcfg->listen = malloc(len * sizeof(*sslhcfg->listen));
+            sslhcfg->listen_len = len;
             for (int i = 0; i < len; i++) {
                 config_setting_t* s = config_setting_get_elem(setting, i);
-                int res = config_listen_parser(s, &config->listen[i], errmsg);
+                int res = sslhcfg_listen_parser(s, &sslhcfg->listen[i], errmsg);
                 if (!res) return 0;
             }
         }
 
-        config->protocols = NULL;
-        config->protocols_len = 0;
+        sslhcfg->protocols = NULL;
+        sslhcfg->protocols_len = 0;
         if ((setting = config_setting_lookup(cfg, "protocols"))) {
             int len = config_setting_length(setting);
-            config->protocols = malloc(len * sizeof(*config->protocols));
-            config->protocols_len = len;
+            sslhcfg->protocols = malloc(len * sizeof(*sslhcfg->protocols));
+            sslhcfg->protocols_len = len;
             for (int i = 0; i < len; i++) {
                 config_setting_t* s = config_setting_get_elem(setting, i);
-                int res = config_protocols_parser(s, &config->protocols[i], errmsg);
+                int res = sslhcfg_protocols_parser(s, &sslhcfg->protocols[i], errmsg);
                 if (!res) return 0;
             }
         }
 	return 1;
 }
 
+/* Public parser API: returns 0 on failure, 1 on success */
+int sslhcfg_parse_file(const char* filename, struct sslhcfg_item* cfg, const char**errmsg)
+{
+    config_t c;
+
+    config_init(&c);
+    if (config_read_file(&c, filename) == CONFIG_FALSE) {
+        if (config_error_line(&c) != 0) {
+           asprintf(errmsg, "%s:%d:%s", 
+                    filename,
+                    config_error_line(&c),
+                    config_error_text(&c));
+           return 0;
+        }
+        asprintf(errmsg, "%s:%s", filename, config_error_text(&c));
+        return 0;
+    }
+    return sslhcfg_parser(config_lookup(&c, "/"), cfg, errmsg);
+}
+
 static void indent(int depth) 
 {
     int i;
@@ -275,98 +320,95 @@ static void indent(int depth)
         printf("    ");
 }
 
-static void config_protocols_print(
-        struct config_protocols_items* config_protocols,
+static void sslhcfg_protocols_print(
+        struct sslhcfg_protocols_item* sslhcfg_protocols,
         int depth) 
 {
         int i;
         indent(depth);
-        printf("name: %s\n", config_protocols->name);
+        printf("name: %s\n", sslhcfg_protocols->name);
         indent(depth);
-        printf("host: %s\n", config_protocols->host);
+        printf("host: %s\n", sslhcfg_protocols->host);
         indent(depth);
-        printf("port: %s\n", config_protocols->port);
+        printf("port: %s\n", sslhcfg_protocols->port);
         indent(depth);
-        printf("service: %s\n", config_protocols->service);
+        printf("service: %s\n", sslhcfg_protocols->service);
         indent(depth);
-        printf("fork: %d\n", config_protocols->fork);
+        printf("fork: %d\n", sslhcfg_protocols->fork);
         indent(depth);
-        printf("log_level: %d\n", config_protocols->log_level);
+        printf("log_level: %d\n", sslhcfg_protocols->log_level);
         indent(depth);
-        printf("keepalive: %d\n", config_protocols->keepalive);
+        printf("keepalive: %d\n", sslhcfg_protocols->keepalive);
         indent(depth);
-        printf("sni_hostnames [%d]:\n", config_protocols->sni_hostnames_len);
-        for (i = 0; i < config_protocols->sni_hostnames_len; i++) {
+        printf("sni_hostnames [%d]:\n", sslhcfg_protocols->sni_hostnames_len);
+        for (i = 0; i < sslhcfg_protocols->sni_hostnames_len; i++) {
             indent(depth+1);
-            printf("%d:\t%s\n", i, config_protocols->sni_hostnames[i]);
+            printf("%d:\t%s\n", i, sslhcfg_protocols->sni_hostnames[i]);
         }
         indent(depth);
-        printf("alpn_protocols [%d]:\n", config_protocols->alpn_protocols_len);
-        for (i = 0; i < config_protocols->alpn_protocols_len; i++) {
+        printf("alpn_protocols [%d]:\n", sslhcfg_protocols->alpn_protocols_len);
+        for (i = 0; i < sslhcfg_protocols->alpn_protocols_len; i++) {
             indent(depth+1);
-            printf("%d:\t%s\n", i, config_protocols->alpn_protocols[i]);
+            printf("%d:\t%s\n", i, sslhcfg_protocols->alpn_protocols[i]);
         }
         indent(depth);
-        printf("regex_patterns [%d]:\n", config_protocols->regex_patterns_len);
-        for (i = 0; i < config_protocols->regex_patterns_len; i++) {
+        printf("regex_patterns [%d]:\n", sslhcfg_protocols->regex_patterns_len);
+        for (i = 0; i < sslhcfg_protocols->regex_patterns_len; i++) {
             indent(depth+1);
-            printf("%d:\t%s\n", i, config_protocols->regex_patterns[i]);
+            printf("%d:\t%s\n", i, sslhcfg_protocols->regex_patterns[i]);
         }
-   printf("\n");
 }
 
-static void config_listen_print(
-        struct config_listen_items* config_listen,
+static void sslhcfg_listen_print(
+        struct sslhcfg_listen_item* sslhcfg_listen,
         int depth) 
 {
         int i;
         indent(depth);
-        printf("host: %s\n", config_listen->host);
+        printf("host: %s\n", sslhcfg_listen->host);
         indent(depth);
-        printf("port: %s\n", config_listen->port);
+        printf("port: %s\n", sslhcfg_listen->port);
         indent(depth);
-        printf("keepalive: %d\n", config_listen->keepalive);
-   printf("\n");
+        printf("keepalive: %d\n", sslhcfg_listen->keepalive);
 }
 
-void config_print(
-        struct config_items* config,
+void sslhcfg_print(
+        struct sslhcfg_item* sslhcfg,
         int depth) 
 {
         int i;
         indent(depth);
-        printf("verbose: %d\n", config->verbose);
+        printf("verbose: %d\n", sslhcfg->verbose);
         indent(depth);
-        printf("foreground: %d\n", config->foreground);
+        printf("foreground: %d\n", sslhcfg->foreground);
         indent(depth);
-        printf("inetd: %d\n", config->inetd);
+        printf("inetd: %d\n", sslhcfg->inetd);
         indent(depth);
-        printf("numeric: %d\n", config->numeric);
+        printf("numeric: %d\n", sslhcfg->numeric);
         indent(depth);
-        printf("transparent: %d\n", config->transparent);
+        printf("transparent: %d\n", sslhcfg->transparent);
         indent(depth);
-        printf("timeout: %d\n", config->timeout);
+        printf("timeout: %d\n", sslhcfg->timeout);
         indent(depth);
-        printf("user: %s\n", config->user);
+        printf("user: %s\n", sslhcfg->user);
         indent(depth);
-        printf("pidfile: %s\n", config->pidfile);
+        printf("pidfile: %s\n", sslhcfg->pidfile);
         indent(depth);
-        printf("chroot: %s\n", config->chroot);
+        printf("chroot: %s\n", sslhcfg->chroot);
         indent(depth);
-        printf("syslog_facility: %s\n", config->syslog_facility);
+        printf("syslog_facility: %s\n", sslhcfg->syslog_facility);
         indent(depth);
-        printf("on_timeout: %s\n", config->on_timeout);
+        printf("on_timeout: %s\n", sslhcfg->on_timeout);
 
         indent(depth);
-        printf("listen [%d]:\n", config->listen_len);
-        for (int i = 0; i < config->listen_len; i++) {
-            config_listen_print(&config->listen[i], depth+1);
+        printf("listen [%d]:\n", sslhcfg->listen_len);
+        for (int i = 0; i < sslhcfg->listen_len; i++) {
+            sslhcfg_listen_print(&sslhcfg->listen[i], depth+1);
         }
 
         indent(depth);
-        printf("protocols [%d]:\n", config->protocols_len);
-        for (int i = 0; i < config->protocols_len; i++) {
-            config_protocols_print(&config->protocols[i], depth+1);
+        printf("protocols [%d]:\n", sslhcfg->protocols_len);
+        for (int i = 0; i < sslhcfg->protocols_len; i++) {
+            sslhcfg_protocols_print(&sslhcfg->protocols[i], depth+1);
         }
-   printf("\n");
 }
diff --git a/sslh-conf.h b/sslh-conf.h
index a7af686..9b1a3d8 100644
--- a/sslh-conf.h
+++ b/sslh-conf.h
@@ -1,32 +1,43 @@
 /* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct)
- * on Thu Nov 22 17:16:04 2018. */
+ * on Fri Dec  7 08:27:14 2018. */
 
-struct config_protocols_items {
+ 
+#ifndef C2S_SSLHCFG_H
+#define C2S_SSLHCFG_H
+#include <libconfig.h>
+
+#include "probe.h"
+#include  <sys/types.h>
+#include  <sys/socket.h>
+#include  <netdb.h>
+
+struct sslhcfg_listen_item {
+	char*	host;
+	char*	port;
+	int	keepalive;
+};
+
+struct sslhcfg_protocols_item {
 	const char*	name;
-	const char*	host;
-	const char*	port;
+	char*	host;
+	char*	port;
 	int	service_is_present;
 	const char*	service;
 	int	fork;
 	int	log_level;
 	int	keepalive;
-	int sni_hostnames_len;
+	size_t	sni_hostnames_len;
 	const char** sni_hostnames;
-	int alpn_protocols_len;
+	size_t	alpn_protocols_len;
 	const char** alpn_protocols;
-	int regex_patterns_len;
+	size_t	regex_patterns_len;
 	const char** regex_patterns;
+	T_PROBE*	probe;
+	struct addrinfo*	saddr;
+	void*	data;
 };
 
-
-struct config_listen_items {
-	const char*	host;
-	const char*	port;
-	int	keepalive;
-};
-
-
-struct config_items {
+struct sslhcfg_item {
 	int	verbose;
 	int	foreground;
 	int	inetd;
@@ -37,20 +48,23 @@ struct config_items {
 	const char*	user;
 	int	pidfile_is_present;
 	const char*	pidfile;
+	int	chroot_is_present;
 	const char*	chroot;
 	const char*	syslog_facility;
 	const char*	on_timeout;
-	int listen_len;
-	struct config_listen_items* listen;
-	int protocols_len;
-	struct config_protocols_items* protocols;
+	size_t	listen_len;
+	struct sslhcfg_listen_item* listen;
+	size_t	protocols_len;
+	struct sslhcfg_protocols_item* protocols;
 };
 
-int config_parser(
-        config_setting_t* cfg, 
-        struct config_items* config, 
+int sslhcfg_parse_file(
+        const char* filename,
+        struct sslhcfg_item* sslhcfg, 
         const char** errmsg);
 
-void config_print(
-    struct config_items *config,
+void sslhcfg_print(
+    struct sslhcfg_item *sslhcfg,
     int depth);
+
+#endif
diff --git a/sslh-fork.c b/sslh-fork.c
index 78bf460..164be80 100644
--- a/sslh-fork.c
+++ b/sslh-fork.c
@@ -54,7 +54,7 @@ int shovel(struct connection *cnx)
           if (FD_ISSET(cnx->q[i].fd, &fds)) {
               res = fd2fd(&cnx->q[1-i], &cnx->q[i]);
               if (!res) {
-                  if (verbose) 
+                  if (cfg.verbose) 
                       fprintf(stderr, "%s %s", i ? "client" : "server", "socket closed\n");
                   return res;
               }
@@ -79,7 +79,7 @@ void start_shoveler(int in_socket)
    FD_ZERO(&fds);
    FD_SET(in_socket, &fds);
    memset(&tv, 0, sizeof(tv));
-   tv.tv_sec = probing_timeout;
+   tv.tv_sec = cfg.timeout;
 
    while (res == PROBE_AGAIN) {
        /* POSIX does not guarantee that tv will be updated, but the client can
@@ -94,8 +94,8 @@ void start_shoveler(int in_socket)
        } else {
            /* Timed out: it's necessarily SSH */
            cnx.proto = timeout_protocol();
-           if (verbose) 
-               log_message(LOG_INFO, "timed out, connect to %s\n", cnx.proto->description);
+           if (cfg.verbose) 
+               log_message(LOG_INFO, "timed out, connect to %s\n", cnx.proto->name);
            break;
        }
    }
@@ -120,7 +120,7 @@ void start_shoveler(int in_socket)
    close(in_socket);
    close(out_socket);
    
-   if (verbose)
+   if (cfg.verbose)
       fprintf(stderr, "connection closed down\n");
 
    exit(0);
@@ -161,7 +161,7 @@ void main_loop(int listen_sockets[], int num_addr_listen)
             while (1)
             {
                 in_socket = accept(listen_sockets[i], 0, 0);
-                if (verbose) fprintf(stderr, "accepted fd %d\n", in_socket);
+                if (cfg.verbose) fprintf(stderr, "accepted fd %d\n", in_socket);
 
                 switch(fork()) {
                 case -1: log_message(LOG_ERR, "fork failed: err %d: %s\n", errno, strerror(errno));
diff --git a/sslh-main.c b/sslh-main.c
index aaadc74..4c715c2 100644
--- a/sslh-main.c
+++ b/sslh-main.c
@@ -2,7 +2,7 @@
 # main: processing of config file, command line options and start the main
 # loop.
 #
-# Copyright (C) 2007-2016  Yves Rutschle
+# Copyright (C) 2007-2018  Yves Rutschle
 # 
 # This program is free software; you can redistribute it
 # and/or modify it under the terms of the GNU General Public
@@ -63,12 +63,11 @@ const char* USAGE_STRING =
 #define OPT_ONTIMEOUT   257
 
 static struct option const_options[] = {
-    { "inetd",      no_argument,            &inetd,         1 },
-    { "foreground", no_argument,            &foreground,    1 },
-    { "background", no_argument,            &background,    1 },
-    { "transparent", no_argument,           &transparent,   1 },
-    { "numeric",    no_argument,            &numeric,       1 },
-    { "verbose",    no_argument,            &verbose,       1 },
+    { "inetd",      no_argument,            &cfg.inetd,         1 },
+    { "foreground", no_argument,            &cfg.foreground,    1 },
+    { "transparent", no_argument,           &cfg.transparent,   1 },
+    { "numeric",    no_argument,            &cfg.numeric,       1 },
+    { "verbose",    no_argument,            &cfg.verbose,       1 },
     { "user",       required_argument,      0,              'u' },
     { "config",     optional_argument,      0,              'F' },
     { "pidfile",    required_argument,      0,              'P' },
@@ -78,22 +77,23 @@ static struct option const_options[] = {
     { "listen",     required_argument,      0,              'p' },
     {}
 };
+
 static struct option* all_options;
-static struct proto* builtins;
+static struct protocol_probe_desc* builtins;
 static const char *optstr = "vt:T:p:VP:C:F::";
 
 
 
 static void print_usage(void)
 {
-    struct proto *p;
+    struct protocol_probe_desc *p;
     int i;
     int res;
     char *prots = "";
 
     p = get_builtins();
     for (i = 0; i < get_num_builtins(); i++) {
-        res = asprintf(&prots, "%s\t[--%s <addr>]\n", prots, p[i].description);
+        res = asprintf(&prots, "%s\t[--%s <addr>]\n", prots, p[i].name);
         CHECK_RES_DIE(res, "asprintf");
     }
 
@@ -121,12 +121,14 @@ static void printsettings(void)
 {
     char buf[NI_MAXHOST];
     struct addrinfo *a;
-    struct proto *p;
+    int i;
+    struct sslhcfg_protocols_item *p;
     
-    for (p = get_first_protocol(); p; p = p->next) {
+    for (i = 0; i < cfg.protocols_len; i++ ) {
+        p = &cfg.protocols[i];
         fprintf(stderr,
                 "%s addr: %s. libwrap service: %s log_level: %d family %d %d [%s] [%s]\n",
-                p->description, 
+                p->name, 
                 sprintaddr(buf, sizeof(buf), p->saddr), 
                 p->service,
                 p->log_level,
@@ -142,8 +144,8 @@ static void printsettings(void)
                 sprintaddr(buf, sizeof(buf), a), 
                 a->ai_flags & SO_KEEPALIVE ? "keepalive" : "");
     }
-    fprintf(stderr, "timeout: %d\non-timeout: %s\n", probing_timeout,
-            timeout_protocol()->description);
+    fprintf(stderr, "timeout: %d\non-timeout: %s\n", cfg.timeout,
+            timeout_protocol()->name);
 }
 
 
@@ -177,42 +179,22 @@ void cmd_ssl_to_tls(int argc, char* argv[])
  * out: newly allocated list of addrinfo to listen to
  */
 #ifdef LIBCONFIG
-static int config_listen(config_t *config, struct addrinfo **listen) 
+static int config_resolve_listen(struct addrinfo **listen)
 {
-    config_setting_t *setting, *addr;
-    int len, i, keepalive;
-    const char *hostname, *port;
+    int i;
+    for (i = 0; i < cfg.listen_len; i++) {
+        resolve_split_name(listen, cfg.listen[i].host, cfg.listen[i].port);
 
-    setting = config_lookup(config, "listen");
-    if (setting) {
-        len = config_setting_length(setting);
-        for (i = 0; i < len; i++) {
-            addr = config_setting_get_elem(setting, i);
-            if (! (config_setting_lookup_string(addr, "host", &hostname) &&
-                   config_setting_lookup_string(addr, "port", &port))) {
-                fprintf(stderr,
-                            "line %d:Incomplete specification (hostname and port required)\n",
-                            config_setting_source_line(addr));
-                return -1;
-            }
-
-            keepalive = 0;
-            config_setting_lookup_bool(addr, "keepalive", &keepalive);
-
-            resolve_split_name(listen, hostname, port);
-
-            /* getaddrinfo returned a list of addresses corresponding to the
-             * specification; move the pointer to the end of that list before
-             * processing the next specification, while setting flags for
-             * start_listen_sockets() through ai_flags (which is not meant for
-             * that, but is only used as hint in getaddrinfo, so it's OK) */
-            for (; *listen; listen = &((*listen)->ai_next)) {
-                if (keepalive)
-                    (*listen)->ai_flags = SO_KEEPALIVE;
-            }
+        /* getaddrinfo returned a list of addresses corresponding to the
+         * specification; move the pointer to the end of that list before
+         * processing the next specification, while setting flags for
+         * start_listen_sockets() through ai_flags (which is not meant for
+         * that, but is only used as hint in getaddrinfo, so it's OK) */
+        for (; *listen; listen = &((*listen)->ai_next)) {
+            if (cfg.listen[i].keepalive)
+                (*listen)->ai_flags = SO_KEEPALIVE;
         }
     }
-
     return 0;
 }
 #endif
@@ -220,241 +202,92 @@ static int config_listen(config_t *config, struct addrinfo **listen)
 
 
 #ifdef LIBCONFIG
-static void setup_regex_probe(struct proto *p, config_setting_t* probes)
+static void setup_regex_probe(struct sslhcfg_protocols_item *p)
 {
-#ifdef ENABLE_REGEX
-    int num_probes, errsize, i, res;
-    char *err;
-    const char * expr;
-    regex_t** probe_list;
+    int num_patterns, i, res;
+    regex_t** pattern_list;
+    size_t errsize;
+    char* err;
 
-    num_probes = config_setting_length(probes);
-    if (!num_probes) {
-        fprintf(stderr, "%s: no probes specified\n", p->description);
-        exit(1);
-    }
+    num_patterns = p->regex_patterns_len;
 
-    p->probe = get_probe("regex");
-    probe_list = calloc(num_probes + 1, sizeof(*probe_list));
-    CHECK_ALLOC(probe_list, "calloc");
-    p->data = (void*)probe_list;
+    pattern_list = calloc(num_patterns + 1, sizeof(*pattern_list));
+    CHECK_ALLOC(pattern_list, "calloc");
+    p->data = (void*)pattern_list;
 
-    for (i = 0; i < num_probes; i++) {
-        probe_list[i] = malloc(sizeof(*(probe_list[i])));
-        CHECK_ALLOC(probe_list[i], "malloc");
-        expr = config_setting_get_string_elem(probes, i);
-        if (expr == NULL) {
-            fprintf(stderr, "%s: invalid probe specified\n", p->description);
-            exit(1);
-        }
-        res = regcomp(probe_list[i], expr, REG_EXTENDED);
+    for (i = 0; i < num_patterns; i++) {
+        pattern_list[i] = malloc(sizeof(*(pattern_list[i])));
+        CHECK_ALLOC(pattern_list[i], "malloc");
+        res = regcomp(pattern_list[i], p->regex_patterns[i], REG_EXTENDED);
         if (res) {
-            err = malloc(errsize = regerror(res, probe_list[i], NULL, 0));
+            err = malloc(errsize = regerror(res, pattern_list[i], NULL, 0));
             CHECK_ALLOC(err, "malloc");
-            regerror(res, probe_list[i], err, errsize);
-            fprintf(stderr, "%s:%s\n", expr, err);
+            regerror(res, pattern_list[i], err, errsize);
+            fprintf(stderr, "%s:%s\n", pattern_list[i], err);
             free(err);
             exit(1);
         }
     }
-#else
-    fprintf(stderr, "line %d: regex probe specified but not compiled in\n", config_setting_source_line(probes));
-    exit(5);
-#endif
 }
 #endif
 
-#ifdef LIBCONFIG
-static void setup_sni_alpn_list(struct proto *p, config_setting_t* config_items, const char* name, int alpn)
-{
-    int num_probes, i, max_server_name_len, server_name_len;
-    const char * config_item, *server_name;
-    char** sni_hostname_list;
-
-    num_probes = config_setting_length(config_items);
-    if (!num_probes) {
-        fprintf(stderr, "%s: no %s specified\n", p->description, name);
-        return;
-    }
-
-    max_server_name_len = 0;
-    for (i = 0; i < num_probes; i++) {
-        server_name = config_setting_get_string_elem(config_items, i);
-        if (server_name == NULL) {
-            fprintf(stderr, "%s: invalid %s specified\n", p->description, name);
-            exit(1);
-        }
-        server_name_len = strlen(server_name);
-        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);
-    CHECK_ALLOC(sni_hostname_list, "calloc");
-
-    for (i = 0; i < num_probes; i++) {
-        config_item = config_setting_get_string_elem(config_items, i);
-        if (config_item == NULL) {
-            fprintf(stderr, "%s: invalid %s specified\n", p->description, name);
-            exit(1);
-        }
-        sni_hostname_list[i] = malloc(max_server_name_len);
-        CHECK_ALLOC(sni_hostname_list[i], "malloc");
-        strcpy (sni_hostname_list[i], config_item);
-        if(verbose) fprintf(stderr, "%s: %s[%d]: %s\n", p->description, name, i, sni_hostname_list[i]);
-    }
-
-    p->data = (void*)tls_data_set_list(p->data, alpn, sni_hostname_list);
-}
-
-static void setup_sni_alpn(struct proto *p, config_setting_t* prot)
-{
-    config_setting_t *sni_hostnames, *alpn_protocols;
-
-    p->data = (void*)new_tls_data();
-    sni_hostnames = config_setting_get_member(prot, "sni_hostnames");
-    alpn_protocols = config_setting_get_member(prot, "alpn_protocols");
-
-    if(sni_hostnames && config_setting_is_array(sni_hostnames)) {
-        setup_sni_alpn_list(p, sni_hostnames, "sni_hostnames", 0);
-    }
-    if(alpn_protocols && config_setting_is_array(alpn_protocols)) {
-        setup_sni_alpn_list(p, alpn_protocols, "alpn_protocols", 1);
-    }
-}
-#endif
-
-/* Extract configuration for protocols to connect to.
- * out: newly-allocated list of protocols
+/* For each protocol in the configuration, resolve address and set up protocol
+ * options if required
  */
 #ifdef LIBCONFIG
-static int config_protocols(config_t *config, struct proto **prots)
+static int config_protocols()
 {
-    config_setting_t *setting, *prot, *patterns;
-    const char *hostname, *port, *cfg_name;
-    char* name;
-    int i, num_prots;
-    struct proto *p, *prev = NULL;
+    int i;
+    for (i = 0; i < cfg.protocols_len; i++) {
+        struct sslhcfg_protocols_item* p = &(cfg.protocols[i]);
+        if (resolve_split_name(&(p->saddr), p->host, p->port)) {
+            fprintf(stderr, "cannot resolve %s:%s\n", p->host, p->port);
+            exit(1);
+        }
 
-    setting = config_lookup(config, "protocols");
-    if (setting) {
-        num_prots = config_setting_length(setting);
-        for (i = 0; i < num_prots; i++) {
-            p = calloc(1, sizeof(*p));
-            CHECK_ALLOC(p, "calloc");
-            if (i == 0) *prots = p;
-            if (prev) prev->next = p;
-            prev = p;
+        p->probe = get_probe(p->name);
+        if (!p->probe) {
+            fprintf(stderr, "%s: probe unknown\n", p->name);
+            exit(1);
+        }
 
-            prot = config_setting_get_elem(setting, i);
-            if ((config_setting_lookup_string(prot, "name", &cfg_name) &&
-                 config_setting_lookup_string(prot, "host", &hostname) &&
-                 config_setting_lookup_string(prot, "port", &port)
-                )) {
-                /* To removed in v1.21 */
-                name = strdup(cfg_name);
-                ssl_to_tls(name);
-                /* /remove */
-                p->description = name;
-                config_setting_lookup_string(prot, "service", &(p->service));
-                config_setting_lookup_bool(prot, "keepalive", &p->keepalive);
-                config_setting_lookup_bool(prot, "fork", &p->fork);
+        if (!strcmp(cfg.protocols[i].name, "regex")) {
+            setup_regex_probe(&cfg.protocols[i]);
+        }
 
-                if (config_setting_lookup_int(prot, "log_level", &p->log_level) == CONFIG_FALSE) {
-                    p->log_level = 1;
-                }
-
-                if (resolve_split_name(&(p->saddr), hostname, port)) {
-                    fprintf(stderr, "line %d: cannot resolve %s:%s\n", config_setting_source_line(prot), hostname, port);
-                    exit(1);
-                }
-
-                p->probe = get_probe(name);
-                if (!p->probe) {
-                    fprintf(stderr, "line %d: %s: probe unknown\n", config_setting_source_line(prot), 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);
-                    }
-                }
-
-                /* Probe-specific options: SNI/ALPN */
-                if (!strcmp(name, "tls")) {
-                    setup_sni_alpn(p, prot);
-                }
-
-            } else {
-                fprintf(stderr, "line %d: Illegal protocol description (missing name, host or port)\n", config_setting_source_line(prot));
-                exit(1);
-            }
+        if (!strcmp(cfg.protocols[i].name, "tls")) {
+            cfg.protocols[i].data = (void*)new_tls_data();
+            if (cfg.protocols[i].sni_hostnames_len)
+                tls_data_set_list(cfg.protocols[i].data, 0,
+                                  cfg.protocols[i].sni_hostnames,
+                                  cfg.protocols[i].sni_hostnames_len);
+            if (cfg.protocols[i].alpn_protocols_len)
+                tls_data_set_list(cfg.protocols[i].data, 1, 
+                                  cfg.protocols[i].alpn_protocols,
+                                  cfg.protocols[i].alpn_protocols_len);
         }
     }
-
-    return 0;
 }
 #endif
 
 /* Parses a config file
  * in: *filename
  * out: *listen, a newly-allocated linked list of listen addrinfo
- *      *prots, a newly-allocated linked list of protocols
  *      1 on error, 0 on success
  */
 #ifdef LIBCONFIG
-static int config_parse(char *filename, struct addrinfo **listen, struct proto **prots)
+static int config_parse(char *filename, struct addrinfo **listen)
 {
-    config_t config;
-    int timeout;
-    const char* str;
+    int res;
+    const char* err;
 
-    config_init(&config);
-    if (config_read_file(&config, filename) == CONFIG_FALSE) {
-        /* If it's a parse error then there will be a line number for the failure
-         * an I/O error (such as non-existent file) will have the error line as 0
-         */
-        if (config_error_line(&config) != 0) {
-            fprintf(stderr, "%s:%d:%s\n", 
-                    filename,
-                    config_error_line(&config),
-                    config_error_text(&config));
-            exit(1);
-        }
-        fprintf(stderr, "%s:%s\n", 
-                filename,
-                config_error_text(&config));
+    if (!sslhcfg_parse_file(filename, &cfg, &err)) {
+        fprintf(stderr, err);
         return 1;
     }
 
-    if(config_lookup_bool(&config, "verbose", &verbose) == CONFIG_FALSE) {
-	config_lookup_int(&config, "verbose", &verbose);
-    }
-
-    config_lookup_bool(&config, "inetd", &inetd);
-    config_lookup_bool(&config, "foreground", &foreground);
-    config_lookup_bool(&config, "numeric", &numeric);
-    config_lookup_bool(&config, "transparent", &transparent);
-
-    if (config_lookup_int(&config, "timeout", (int *)&timeout) == CONFIG_TRUE) {
-        probing_timeout = timeout;
-    }
-
-    if (config_lookup_string(&config, "on-timeout", &str)) {
-        set_ontimeout(str);
-    }
-
-    config_lookup_string(&config, "user", &user_name);
-    config_lookup_string(&config, "pidfile", &pid_file);
-    config_lookup_string(&config, "chroot", &chroot_path);
-
-    config_lookup_string(&config, "syslog_facility", &facility);
-
-    config_listen(&config, listen);
-    config_protocols(&config, prots);
+    config_resolve_listen(listen);
+    config_protocols();
 
     return 0;
 }
@@ -467,12 +300,12 @@ static int config_parse(char *filename, struct addrinfo **listen, struct proto *
  * prot: array of protocols
  * n_prots: number of protocols in *prot
  * */
-static void append_protocols(struct option *options, int n_opts, struct proto *prot , int n_prots)
+static void append_protocols(struct option *options, int n_opts, struct protocol_probe_desc* prot , int n_prots)
 {
     int o, p;
 
     for (o = n_opts, p = 0; p < n_prots; o++, p++) {
-        options[o].name = prot[p].description;
+        options[o].name = prot[p].name;
         options[o].has_arg = required_argument;
         options[o].flag = 0;
         options[o].val = p + PROT_SHIFT;
@@ -497,7 +330,7 @@ static void make_alloptions(void)
  *
  * prots: newly-allocated list of configured protocols, if any.
  */
-static void cmdline_config(int argc, char* argv[], struct proto** prots)
+static void cmdline_config(int argc, char* argv[], struct sslhcfg_protocols_item** prots)
 {
 #ifdef LIBCONFIG
     int c, res;
@@ -513,19 +346,19 @@ static void cmdline_config(int argc, char* argv[], struct proto** prots)
     opterr = 0; /* we're missing protocol options at this stage so don't output errors */
     while ((c = getopt_long_only(argc, argv, optstr, all_options, NULL)) != -1) {
         if (c == 'v') {
-            verbose++;
+            cfg.verbose++;
         }
         if (c == 'F') {
             config_filename = optarg;
             if (config_filename) {
-                res = config_parse(config_filename, &addr_listen, prots);
+                res = config_parse(config_filename, &addr_listen);
             } else {
                 /* No configuration file specified -- try default file locations */
-                res = config_parse("/etc/sslh/sslh.cfg", &addr_listen, prots);
-                if (!res && verbose) fprintf(stderr, "Using /etc/sslh/sslh.cfg\n");
+                res = config_parse("/etc/sslh/sslh.cfg", &addr_listen);
+                if (!res && cfg.verbose) fprintf(stderr, "Using /etc/sslh/sslh.cfg\n");
                 if (res) {
-                    res = config_parse("/etc/sslh.cfg", &addr_listen, prots);
-                    if (!res && verbose) fprintf(stderr, "Using /etc/sslh.cfg\n");
+                    res = config_parse("/etc/sslh.cfg", &addr_listen);
+                    if (!res && cfg.verbose) fprintf(stderr, "Using /etc/sslh.cfg\n");
                 }
             }
             if (res)
@@ -539,11 +372,11 @@ static void cmdline_config(int argc, char* argv[], struct proto** prots)
 
 /* Parse command-line options. prots points to a list of configured protocols,
  * potentially non-allocated */
-static void parse_cmdline(int argc, char* argv[], struct proto* prots)
+static void parse_cmdline(int argc, char* argv[], struct sslhcfg_protocols_item* prots)
 {
     int c;
     struct addrinfo **a;
-    struct proto *p;
+    int background, i;
 
     optind = 1;
     opterr = 1;
@@ -552,29 +385,25 @@ next_arg:
         if (c == 0) continue;
 
         if (c >= PROT_SHIFT) {
-            if (prots)
-                for (p = prots; p && p->next; p = p->next) {
-                    /* override if protocol was already defined by config file 
-                     * (note it only overrides address and use builtin probe) */
-                    if (!strcmp(p->description, builtins[c-PROT_SHIFT].description)) {
-                        resolve_name(&(p->saddr), optarg);
-                        p->probe = builtins[c-PROT_SHIFT].probe;
-                        goto next_arg;
-                    }
+            int prot_num = c - PROT_SHIFT;
+            for (i = 0; i < cfg.protocols_len; i++) {
+                /* override if protocol was already defined by config file  */
+                if (!strcmp(cfg.protocols[i].name, builtins[prot_num].name)) {
+                    resolve_name(&(cfg.protocols[i].saddr), optarg);
+                    goto next_arg;
                 }
+            }
             /* At this stage, it's a new protocol: add it to the end of the
              * list */
-            if (!prots) {
-                /* No protocols yet -- create the list */
-                p = prots = calloc(1, sizeof(*p));
-                CHECK_ALLOC(p, "calloc");
-            } else {
-                p->next = calloc(1, sizeof(*p));
-                CHECK_ALLOC(p->next, "calloc");
-                p = p->next;
-            }
-            memcpy(p, &builtins[c-PROT_SHIFT], sizeof(*p));
-            resolve_name(&(p->saddr), optarg);
+            cfg.protocols_len++;
+            cfg.protocols = realloc(cfg.protocols, cfg.protocols_len * sizeof(*cfg.protocols));
+            CHECK_ALLOC(cfg.protocols, "realloc");
+
+            /* set up name, target and probe. everything else defaults to 0 */
+            memset(&cfg.protocols[cfg.protocols_len-1], 0, sizeof(cfg.protocols[0]));
+            cfg.protocols[cfg.protocols_len-1].probe = get_probe(builtins[prot_num].name);
+            cfg.protocols[cfg.protocols_len-1].name = builtins[prot_num].name;
+            resolve_name(&cfg.protocols[cfg.protocols_len-1].saddr, optarg);
             continue;
         }
 
@@ -590,7 +419,7 @@ next_arg:
             break;
 
         case 't':
-             probing_timeout = atoi(optarg);
+             cfg.timeout = atoi(optarg);
             break;
 
         case OPT_ONTIMEOUT:
@@ -610,19 +439,19 @@ next_arg:
             exit(0);
 
         case 'u':
-            user_name = optarg;
+            cfg.user = optarg;
             break;
 
         case 'P':
-            pid_file = optarg;
+            cfg.pidfile = optarg;
             break;
 
         case 'C':
-            chroot_path = optarg;
+            cfg.chroot = optarg;
             break;
 
         case 'v':
-            verbose++;
+            cfg.verbose++;
             break;
 
         default:
@@ -631,16 +460,16 @@ next_arg:
         }
     }
 
-    if (!prots) {
+    return;
+
+    if (!cfg.protocols_len) {
         fprintf(stderr, "At least one target protocol must be specified.\n");
         exit(2);
     }
 
-    set_protocol_list(prots);
-
 /* If compiling with systemd socket support no need to require listen address */
 #ifndef SYSTEMD
-    if (!addr_listen && !inetd) {
+    if (!addr_listen && !cfg.inetd) {
         fprintf(stderr, "No listening address specified; use at least one -p option\n");
         exit(1);
     }
@@ -648,7 +477,7 @@ next_arg:
 
     /* Did command-line override foreground setting? */
     if (background)
-        foreground = 0;
+        cfg.foreground = 0;
 
 }
 
@@ -658,26 +487,29 @@ int main(int argc, char *argv[])
    extern char *optarg;
    extern int optind;
    int res, num_addr_listen;
-   struct proto* protocols = NULL;
+   struct sslhcfg_protocols_item* protocols = NULL;
 
    int *listen_sockets;
 
-   /* Init defaults */
-   pid_file = NULL;
-   user_name = NULL;
-   chroot_path = NULL;
+   /* Init defaults -- conf2struct sets them when parsing a config file 
+    * but we may configure entirely from the command line */
+   cfg.pidfile = NULL;
+   cfg.user = NULL;
+   cfg.chroot = NULL;
+   cfg.syslog_facility = "auth";
+   cfg.timeout = 2;
 
    cmdline_config(argc, argv, &protocols);
    parse_cmdline(argc, argv, protocols);
 
-   if (inetd)
+   if (cfg.inetd)
    {
-       verbose = 0;
+       cfg.verbose = 0;
        start_shoveler(0);
        exit(0);
    }
 
-   if (verbose)
+   if (cfg.verbose)
        printsettings();
 
    num_addr_listen = start_listen_sockets(&listen_sockets, addr_listen);
@@ -689,7 +521,7 @@ int main(int argc, char *argv[])
     }
 #endif
 
-   if (!foreground) {
+   if (!cfg.foreground) {
        if (fork() > 0) exit(0); /* Detach */
 
        /* New session -- become group leader */
@@ -701,16 +533,16 @@ int main(int argc, char *argv[])
 
    setup_signals();
 
-   if (pid_file)
-       write_pid_file(pid_file);
+   if (cfg.pidfile)
+       write_pid_file(cfg.pidfile);
 
    /* Open syslog connection before we drop privs/chroot */
    setup_syslog(argv[0]);
 
-   if (user_name || chroot_path)
-       drop_privileges(user_name, chroot_path);
+   if (cfg.user || cfg.chroot)
+       drop_privileges(cfg.user, cfg.chroot);
 
-   if (verbose)
+   if (cfg.verbose)
        printcaps();
 
    main_loop(listen_sockets, num_addr_listen);
diff --git a/sslh-select.c b/sslh-select.c
index ebc2cc8..a2ced55 100644
--- a/sslh-select.c
+++ b/sslh-select.c
@@ -60,7 +60,7 @@ int tidy_connection(struct connection *cnx, fd_set *fds, fd_set *fds2)
 
     for (i = 0; i < 2; i++) {
         if (cnx->q[i].fd != -1) {
-            if (verbose)
+            if (cfg.verbose)
                 fprintf(stderr, "closing fd %d\n", cnx->q[i].fd);
 
             FD_CLR(cnx->q[i].fd, fds);
@@ -111,7 +111,7 @@ int accept_new_connection(int listen_socket, struct connection *cnx[], int* cnx_
         /* nothing */
     }
     if (free >= *cnx_size)  {
-        if (verbose)
+        if (cfg.verbose)
             fprintf(stderr, "buying more slots from the slot machine.\n");
         new = realloc(*cnx, (*cnx_size + cnx_num_alloc) * sizeof((*cnx)[0]));
         if (!new) {
@@ -127,9 +127,9 @@ int accept_new_connection(int listen_socket, struct connection *cnx[], int* cnx_
     }
     (*cnx)[free].q[0].fd = in_socket;
     (*cnx)[free].state = ST_PROBING;
-    (*cnx)[free].probe_timeout = time(NULL) + probing_timeout;
+    (*cnx)[free].probe_timeout = time(NULL) + cfg.timeout;
 
-    if (verbose) 
+    if (cfg.verbose) 
         fprintf(stderr, "accepted fd %d on slot %d\n", in_socket, free);
 
     return in_socket;
@@ -169,7 +169,7 @@ void shovel(struct connection *cnx, int active_fd,
     read_q = &cnx->q[active_fd];
     write_q = &cnx->q[1-active_fd];
 
-    if (verbose)
+    if (cfg.verbose)
         fprintf(stderr, "activity on fd%d\n", read_q->fd);
 
     switch(fd2fd(write_q, read_q)) {
@@ -223,7 +223,7 @@ void shovel_single(struct connection *cnx)
           if (FD_ISSET(cnx->q[i].fd, &fds_w)) {
               res = flush_deferred(&cnx->q[i]);
               if ((res == -1) && ((errno == EPIPE) || (errno == ECONNRESET))) {
-                  if (verbose)
+                  if (cfg.verbose)
                       fprintf(stderr, "%s socket closed\n", i ? "server" : "client");
                   return;
               }
@@ -231,7 +231,7 @@ void shovel_single(struct connection *cnx)
           if (FD_ISSET(cnx->q[i].fd, &fds_r)) {
               res = fd2fd(&cnx->q[1-i], &cnx->q[i]);
               if (!res) {
-                  if (verbose)
+                  if (cfg.verbose)
                       fprintf(stderr, "socket closed\n");
                   return;
               }
@@ -269,7 +269,7 @@ void connect_proxy(struct connection *cnx)
     close(in_socket);
     close(out_socket);
 
-    if (verbose)
+    if (cfg.verbose)
         fprintf(stderr, "connection closed down\n");
 
     exit(0);
@@ -329,12 +329,12 @@ void main_loop(int listen_sockets[], int num_addr_listen)
     while (1)
     {
         memset(&tv, 0, sizeof(tv));
-        tv.tv_sec = probing_timeout;
+        tv.tv_sec = cfg.timeout;
 
         memcpy(&readfds, &fds_r, sizeof(readfds));
         memcpy(&writefds, &fds_w, sizeof(writefds));
 
-        if (verbose)
+        if (cfg.verbose)
             fprintf(stderr, "selecting... max_fd=%d num_probing=%d\n", max_fd, num_probing);
         res = select(max_fd, &readfds, &writefds, NULL, num_probing ? &tv : NULL);
         if (res < 0)
@@ -363,7 +363,7 @@ void main_loop(int listen_sockets[], int num_addr_listen)
                         if ((res == -1) && ((errno == EPIPE) || (errno == ECONNRESET))) {
                             if (cnx[i].state == ST_PROBING) num_probing--;
                             tidy_connection(&cnx[i], &fds_r, &fds_w);
-                            if (verbose)
+                            if (cfg.verbose)
                                 fprintf(stderr, "closed slot %d\n", i);
                         } else {
                             /* If no deferred data is left, stop monitoring the fd 
@@ -383,7 +383,7 @@ void main_loop(int listen_sockets[], int num_addr_listen)
             for (j = 0; j < 2; j++) {
                 if (is_fd_active(cnx[i].q[j].fd, &readfds) || 
                     ((cnx[i].state == ST_PROBING) && (cnx[i].probe_timeout < time(NULL)))) {
-                    if (verbose)
+                    if (cfg.verbose)
                         fprintf(stderr, "processing fd%d slot %d\n", j, i);
 
                     switch (cnx[i].state) {
@@ -399,10 +399,10 @@ void main_loop(int listen_sockets[], int num_addr_listen)
                          * data so probe the protocol */
                         if ((cnx[i].probe_timeout < time(NULL))) {
                             cnx[i].proto = timeout_protocol();
-                            if (verbose) 
+                            if (cfg.verbose) 
                                 log_message(LOG_INFO, 
                                             "timed out, connect to %s\n", 
-                                            cnx[i].proto->description);
+                                            cnx[i].proto->name);
                         } else {
                             res = probe_client_protocol(&cnx[i]);
                             if (res == PROBE_AGAIN)
diff --git a/sslhconf.cfg b/sslhconf.cfg
index 9ed160a..684d120 100644
--- a/sslhconf.cfg
+++ b/sslhconf.cfg
@@ -1,52 +1,71 @@
 header: "sslh-conf.h";
 parser: "sslh-conf.c";
 
-config: (
-        { name: "verbose"; type: "boolean"; default: false },
-        { name: "foreground"; type: "boolean"; default: false; },
-        { name: "inetd"; type: "boolean"; default: false; },
-        { name: "numeric"; type: "boolean"; default: false; },
-        { name: "transparent"; type: "boolean"; default: false; },
-        { name: "timeout"; type: "int"; default: 2; },
-        { name: "user"; type: "string"; optional: true; },
-        { name: "pidfile"; type: "string"; optional: true; },
-        { name: "chroot"; type: "string";  },
-        { name: "syslog_facility"; type: "string"; default: "auth"; },
+# List of includes to define runtime types
+# (bug in libconfig? if swallows the brackets if they start
+# the string)
+includes: (
+           "probe.h",
+           " <sys/types.h>",
+           " <sys/socket.h>",
+           " <netdb.h>"
+          );
 
-        {name: "on_timeout"; type: "string"; default: "ssh"; },
+config: {
+         name : "sslhcfg",
+         type: "list",
+         items: (
+            { name: "verbose"; type: "int"; default: 0; },
+            { name: "foreground"; type: "boolean"; default: false; },
+            { name: "inetd"; type: "boolean"; default: false; },
+            { name: "numeric"; type: "boolean"; default: false; },
+            { name: "transparent"; type: "boolean"; default: false; },
+            { name: "timeout"; type: "int"; default: 2; },
+            { name: "user"; type: "string"; optional: true; },
+            { name: "pidfile"; type: "string"; optional: true; },
+            { name: "chroot"; type: "string"; optional: true; },
+            { name: "syslog_facility"; type: "string"; default: "auth"; },
 
-        { name: "listen", 
-          type: "list", 
-          items: (
-               { name: "host"; type: "string"; },
-               { name: "port"; type: "string"; },
-               { name: "keepalive"; type: "boolean"; default: false; }
-           )
-        },
+            {name: "on-timeout"; type: "string"; default: "ssh"; },
 
-        { name: "protocols",
-          type: "list",
-          items: (
-            { name: "name"; type: "string"; },
-            { name: "host"; type: "string"; },
-            { name: "port"; type: "string"; },
-            { name: "service"; type: "string"; optional: true; },
-            { name: "fork"; type: "boolean"; default: false },
-            { name: "log_level"; type: "int"; default: 1 },
-            { name: "keepalive"; type: "boolean"; default: false },
-            { name: "sni_hostnames",
-                type: "array", 
-                element_type: "string"
+            { name: "listen", 
+                type: "list", 
+                items: (
+                    { name: "host"; type: "string"; var: true; },
+                    { name: "port"; type: "string"; var: true; },
+                    { name: "keepalive"; type: "boolean"; default: false; }
+                )
             },
-            { name: "alpn_protocols",
-              type: "array",
-              element_type: "string" 
-            },
-            { name: "regex_patterns",
-              type: "array",
-              element_type: "string"
-              }
-          )
-        }
-)
+
+            { name: "protocols",
+                type: "list",
+                items: (
+                    { name: "name"; type: "string"; },
+                    { name: "host"; type: "string"; var: true; },
+                    { name: "port"; type: "string"; var: true; },
+                    { name: "service"; type: "string"; optional: true; },
+                    { name: "fork"; type: "boolean"; default: false },
+                    { name: "log_level"; type: "int"; default: 1 },
+                    { name: "keepalive"; type: "boolean"; default: false },
+                    { name: "sni_hostnames",
+                        type: "array", 
+                        element_type: "string"
+                    },
+                    { name: "alpn_protocols",
+                    type: "array",
+                    element_type: "string" 
+                    },
+                    { name: "regex_patterns",
+                    type: "array",
+                    element_type: "string"
+                    },
+
+                    # Runtime data
+                    { name: "probe"; type: "runtime"; c_type: "T_PROBE*" },
+                    { name: "saddr"; type: "runtime"; c_type: "struct addrinfo*" },
+                    { name: "data"; type: "runtime"; c_type: "void*" }
+                )
+            }
+        )
+}
 
diff --git a/t b/t
index c9e4167..057e4d5 100755
--- a/t
+++ b/t
@@ -31,11 +31,11 @@ my $SSH_MIX_SSL =       1;
 # coverage, but do not necessarily result in an actual test
 # (e.g. some tests need to be run with valgrind to check all
 # memory management code).
-my $RB_CNX_NOSERVER =           1;
-my $RB_PARAM_NOHOST =           1;
-my $RB_WRONG_USERNAME =         1;
-my $RB_OPEN_PID_FILE =          1;
-my $RB_RESOLVE_ADDRESS =        1;
+my $RB_CNX_NOSERVER =           0;
+my $RB_PARAM_NOHOST =           0;
+my $RB_WRONG_USERNAME =         0;
+my $RB_OPEN_PID_FILE =          0;
+my $RB_RESOLVE_ADDRESS =        0;
 
 `lcov --directory . --zerocounters`;
 
@@ -161,6 +161,16 @@ sub test_probes {
                     );
                 }
             }
+        } elsif ($p->{name} eq 'regex') {
+            foreach my $pattern (@{$p->{regex_patterns}}) {
+                $pattern =~ /(\w+)/;
+                my $out = $1;
+                test_probe(
+                    data => $out,
+                    expected => $p->{name},
+                    %opts
+                );
+            }
         } else {
             test_probe(
                 data => $pattern,
diff --git a/test.cfg b/test.cfg
index 7a7824b..55a5e53 100644
--- a/test.cfg
+++ b/test.cfg
@@ -30,13 +30,16 @@ protocols:
      { name: "openvpn";  host: "localhost"; port: "9004"; },
      { name: "xmpp";  host: "localhost"; port: "9009"; },
      { name: "adb";  host: "localhost"; port: "9010"; },
+     { name: "regex"; host: "localhost"; port: "9011";
+         regex_patterns: [ "^foo", "^bar" ];
+     },
      { name: "tls"; host: "localhost"; port: "9021"; alpn_protocols: [ "alpn1", "alpn2" ]; sni_hostnames: [ "sni1" ]; },
-     { name: "ssl"; host: "localhost"; port: "9022"; alpn_protocols: [ "alpn1", "alpn2" ]; sni_hostnames: [ "sni2", "sni3" ]; },
+     { name: "tls"; host: "localhost"; port: "9022"; alpn_protocols: [ "alpn1", "alpn2" ]; sni_hostnames: [ "sni2", "sni3" ]; },
      { name: "tls"; host: "localhost"; port: "9023"; alpn_protocols: [ "alpn3" ]; },
      { name: "tls"; host: "localhost"; port: "9024"; sni_hostnames: [ "sni3" ]; },
-     { name: "ssl"; host: "localhost"; port: "9025"; },
+     { name: "tls"; host: "localhost"; port: "9025"; },
      { name: "anyprot";  host: "localhost"; port: "9099";  }
 );
 
-on-timeout: "ssh";
+on_timeout: "ssh";
 
diff --git a/tls.c b/tls.c
index 2f3e55f..c8b3fae 100644
--- a/tls.c
+++ b/tls.c
@@ -32,6 +32,7 @@
 #include <stdlib.h> /* malloc() */
 #include <fnmatch.h> /* fnmatch() */
 #include "tls.h"
+#include "sslh-conf.h"
 
 #define TLS_HEADER_LEN 5
 #define TLS_HANDSHAKE_CONTENT_TYPE 0x16
@@ -48,14 +49,16 @@ typedef struct {
 
 struct TLSProtocol {
     TLS_MATCHMODE match_mode;
-    char** sni_hostname_list;
-    char** alpn_protocol_list;
+    int sni_list_len;
+    const char** sni_hostname_list;
+    int alpn_list_len;
+    const char** alpn_protocol_list;
 };
 
 static int parse_extensions(const struct TLSProtocol *, const char *, size_t);
 static int parse_server_name_extension(const struct TLSProtocol *, const char *, size_t);
 static int parse_alpn_extension(const struct TLSProtocol *, const char *, size_t);
-static int has_match(char**, const char*, size_t);
+static int has_match(const char**, size_t, const char*, size_t);
 
 /* Parse a TLS packet for the Server Name Indication and ALPN extension in the client
  * hello handshake, returning a status code
@@ -79,14 +82,14 @@ parse_tls_header(const struct TLSProtocol *tls_data, const char *data, size_t da
 
     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");
+        if (cfg.verbose) fprintf(stderr, "Request did not begin with TLS handshake.\n");
         return TLS_EPROTOCOL;
     }
 
     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 cannot be parsed.\n",
+        if (cfg.verbose) fprintf(stderr, "Received SSL %d.%d handshake which cannot be parsed.\n",
               tls_version_major, tls_version_minor);
 
         return TLS_EVERSION;
@@ -108,7 +111,7 @@ parse_tls_header(const struct TLSProtocol *tls_data, const char *data, size_t da
         return TLS_EPROTOCOL;
     }
     if (data[pos] != TLS_HANDSHAKE_TYPE_CLIENT_HELLO) {
-        if (verbose) fprintf(stderr, "Not a client hello\n");
+        if (cfg.verbose) fprintf(stderr, "Not a client hello\n");
 
         return TLS_EPROTOCOL;
     }
@@ -141,7 +144,7 @@ parse_tls_header(const struct TLSProtocol *tls_data, const char *data, size_t da
     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");
+        if (cfg.verbose) fprintf(stderr, "Received SSL 3.0 handshake without extensions\n");
         return TLS_EVERSION;
     }
 
@@ -219,13 +222,13 @@ parse_server_name_extension(const struct TLSProtocol *tls_data, const char *data
 
         switch (data[pos]) { /* name type */
             case 0x00: /* host_name */
-                if(has_match(tls_data->sni_hostname_list, data + pos + 3, len)) {
+                if(has_match(tls_data->sni_hostname_list, tls_data->sni_list_len, data + pos + 3, len)) {
                     return len;
                 } else {
                     return TLS_ENOEXT;
                 }
             default:
-                if (verbose) fprintf(stderr, "Unknown server name extension name type: %d\n",
+                if (cfg.verbose) fprintf(stderr, "Unknown server name extension name type: %d\n",
                       data[pos]);
         }
         pos += 3 + len;
@@ -248,10 +251,10 @@ parse_alpn_extension(const struct TLSProtocol *tls_data, const char *data, size_
         if (pos + 1 + len > data_len)
             return TLS_EPROTOCOL;
 
-        if (len > 0 && has_match(tls_data->alpn_protocol_list, data + pos + 1, len)) {
+        if (len > 0 && has_match(tls_data->alpn_protocol_list, tls_data->alpn_list_len, data + pos + 1, len)) {
             return len;
         } else if (len > 0) {
-            if (verbose) fprintf(stderr, "Unknown ALPN name: %.*s\n", (int)len, data + pos + 1);
+            if (cfg.verbose) fprintf(stderr, "Unknown ALPN name: %.*s\n", (int)len, data + pos + 1);
         }
         pos += 1 + len;
     }
@@ -263,15 +266,17 @@ parse_alpn_extension(const struct TLSProtocol *tls_data, const char *data, size_
 }
 
 static int
-has_match(char** list, const char* name, size_t name_len) {
-    char **item;
+has_match(const char** list, size_t list_len, const char* name, size_t name_len) {
+    const char **item;
+    int i;
     char *name_nullterminated = malloc(name_len+1);
     CHECK_ALLOC(name_nullterminated, "malloc");
     memcpy(name_nullterminated, name, name_len);
     name_nullterminated[name_len]='\0';
 
-    for (item = list; *item; item++) {
-        if (verbose) fprintf(stderr, "matching [%.*s] with [%s]\n", (int)name_len, name, *item);
+    for (i = 0; i < list_len; i++) {
+        item = &list[i];
+        if (cfg.verbose) fprintf(stderr, "matching [%.*s] with [%s]\n", (int)name_len, name, *item);
         if(!fnmatch(*item, name_nullterminated, 0)) {
             free(name_nullterminated);
             return 1;
@@ -292,12 +297,14 @@ new_tls_data() {
 }
 
 struct TLSProtocol *
-tls_data_set_list(struct TLSProtocol *tls_data, int alpn, char** list) {
+tls_data_set_list(struct TLSProtocol *tls_data, int alpn, const char** list, size_t list_len) {
     if (alpn) {
         tls_data->alpn_protocol_list = list;
+        tls_data->alpn_list_len = list_len;
         tls_data->match_mode.tls_match_alpn = 1;
     } else {
         tls_data->sni_hostname_list = list;
+        tls_data->sni_list_len = list_len;
         tls_data->match_mode.tls_match_sni = 1;
     }
 
diff --git a/tls.h b/tls.h
index 6ef0ebf..c6cf1a7 100644
--- a/tls.h
+++ b/tls.h
@@ -33,7 +33,7 @@ struct TLSProtocol;
 int parse_tls_header(const struct TLSProtocol *tls_data, const char *data, size_t data_len);
 
 struct TLSProtocol *new_tls_data();
-struct TLSProtocol *tls_data_set_list(struct TLSProtocol *, int, char**);
+struct TLSProtocol *tls_data_set_list(struct TLSProtocol *, int, const char**, size_t);
 
 #define TLS_MATCH       1
 #define TLS_NOMATCH     0