git ssb

0+

cel / sslh



Commit ca461ea077d748dde25eb6610e66514fa0151baf

Added support for RFC4366 SNI (Server Name Indication). Changed configuration file format accordingly.

Yves Rutschle committed on 7/17/2015, 1:05:06 PM
Parent: 5886bd2d43db3ee6a9ae58e3e47c34e07cf80f00
Parent: 8fdaf6eb08555e01c9993ffbf52f7e4ac40c3d15

Files changed

ChangeLogchanged
Makefilechanged
basic.cfgchanged
example.cfgchanged
probe.cchanged
probe.hchanged
sslh-main.cchanged
sslh.podchanged
tls.cadded
tls.hadded
ChangeLogView
@@ -1,8 +1,16 @@
11 vNEXT:
22 Added USELIBPCRE to make use of regex engine
33 optional.
44
5+ Added support for RFC4366 SNI
6+ (Travis Burtrum)
7+
8+ Changed configuration file format: 'probe' field is
9+ no longer required, 'name' field can now contain
10+ 'sni' or 'regex', with corresponding options (see
11+ example.org)
12+
513 v1.17: 09MAR2015
614 Support RFC5952-style IPv6 addresses, e.g. [::]:443.
715
816 Transparant proxy support for FreeBSD.
MakefileView
@@ -22,9 +22,9 @@
2222 CC ?= gcc
2323 CFLAGS ?=-Wall -g $(CFLAGS_COV)
2424
2525 LIBS=
26-OBJS=common.o sslh-main.o probe.o
26+OBJS=common.o sslh-main.o probe.o tls.o
2727
2828 ifneq ($(strip $(USELIBWRAP)),)
2929 LIBS:=$(LIBS) -lwrap
3030 CPPFLAGS+=-DLIBWRAP
@@ -62,9 +62,9 @@
6262 $(CC) $(CFLAGS) $(LDFLAGS) -o sslh-select sslh-select.o $(OBJS) $(LIBS)
6363 #strip sslh-select
6464
6565 echosrv: $(OBJS) echosrv.o
66- $(CC) $(CFLAGS) $(LDFLAGS) -o echosrv echosrv.o probe.o common.o $(LIBS)
66+ $(CC) $(CFLAGS) $(LDFLAGS) -o echosrv echosrv.o probe.o common.o tls.o $(LIBS)
6767
6868 $(MAN): sslh.pod Makefile
6969 pod2man --section=8 --release=$(VERSION) --center=" " sslh.pod | gzip -9 - > $(MAN)
7070
basic.cfgView
@@ -18,12 +18,12 @@
1818 );
1919
2020 protocols:
2121 (
22- { name: "ssh"; service: "ssh"; host: "localhost"; port: "22"; probe: "builtin"; },
23- { name: "openvpn"; host: "localhost"; port: "1194"; probe: "builtin"; },
24- { name: "xmpp"; host: "localhost"; port: "5222"; probe: "builtin"; },
25- { name: "http"; host: "localhost"; port: "80"; probe: "builtin"; },
26- { name: "ssl"; host: "localhost"; port: "443"; probe: "builtin"; },
27- { name: "anyprot"; host: "localhost"; port: "443"; probe: "builtin"; }
22+ { name: "ssh"; service: "ssh"; host: "localhost"; port: "22"; },
23+ { name: "openvpn"; host: "localhost"; port: "1194"; },
24+ { name: "xmpp"; host: "localhost"; port: "5222"; },
25+ { name: "http"; host: "localhost"; port: "80"; },
26+ { name: "ssl"; host: "localhost"; port: "443"; },
27+ { name: "anyprot"; host: "localhost"; port: "443"; }
2828 );
2929
example.cfgView
@@ -22,32 +22,50 @@
2222
2323 # List of protocols
2424 #
2525 # Each protocol entry consists of:
26-# name: name of the protocol
26+# name: name of the probe. These are listed on the command
27+# line (ssh -?), plus 'regex', 'sni' and 'timeout'.
28+
2729 # service: (optional) libwrap service name (see hosts_access(5))
28-# host: host name to connect that protocol
29-# port: port number to connect that protocol
30-# probe: "builtin" or a list of regular expressions
31-# (can be left out, e.g. to use with on-timeout)
30+# host, port: where to connect when this probe succeeds
31+#
32+# Probe-specific options:
33+# sni:
34+# sni_hotnames: list of FQDN for that target
35+# regex:
36+# regex_patterns: list of patterns to match for
37+# that target.
3238 #
3339 # sslh will try each probe in order they are declared, and
3440 # connect to the first that matches.
35-
41+#
42+# You can specify several of 'regex' and 'sni'.
43+
3644 protocols:
3745 (
38- { name: "ssh"; service: "ssh"; host: "localhost"; port: "22"; probe: "builtin"; },
39- { name: "openvpn"; host: "localhost"; port: "1194"; probe: [ "^\x00[\x0D-\xFF]$", "^\x00[\x0D-\xFF]\x38" ]; },
40- { name: "xmpp"; host: "localhost"; port: "5222"; probe: [ "jabber" ]; },
41- { name: "http"; host: "localhost"; port: "80"; probe: "builtin"; },
42- { name: "ssl"; host: "localhost"; port: "443"; probe: [ "" ]; },
46+ { name: "ssh"; service: "ssh"; host: "localhost"; port: "22"; },
47+ { name: "http"; host: "localhost"; port: "80"; },
48+
49+ { name: "sni"; host: "localhost"; port: "993"; sni_hostnames: [ "mail.rutschle.net", "mail.englishintoulouse.com" ]; },
50+ { name: "sni"; host: "localhost"; port: "xmpp-client"; sni_hostnames: [ "im.rutschle.net", "im.englishintoulouse.com" ]; },
51+
52+# OpenVPN
53+ { name: "regex"; host: "localhost"; port: "1194"; regex_patterns: [ "^\x00[\x0D-\xFF]$", "^\x00[\x0D-\xFF]\x38" ]; },
54+# Jabber
55+ { name: "regex"; host: "localhost"; port: "5222"; regex_patterns: [ "jabber" ]; },
56+
57+# Catch-all
58+ { name: "regex"; host: "localhost"; port: "443"; regex_patterns: [ "" ]; },
59+
60+# Where to connect in case of timeout (defaults to ssh)
4361 { name: "timeout"; service: "daytime"; host: "localhost"; port: "daytime"; }
4462 );
4563
4664 # Optionally, specify to which protocol to connect in case
4765 # of timeout (defaults to "ssh").
48-# You can timeout to any arbitrary address by setting a
49-# protocol with no probe, as is the case with this example.
66+# You can timeout to any arbitrary address by setting an
67+# entry in 'protocols' named "timeout".
5068 # This enables you to set a tcpd service name for this
5169 # protocol too.
5270 on-timeout: "timeout";
5371
probe.cView
@@ -1,8 +1,8 @@
11 /*
22 # probe.c: Code for probing protocols
33 #
4-# Copyright (C) 2007-2012 Yves Rutschle
4+# Copyright (C) 2007-2015 Yves Rutschle
55 #
66 # This program is free software; you can redistribute it
77 # and/or modify it under the terms of the GNU General Public
88 # License as published by the Free Software Foundation; either
@@ -216,8 +216,36 @@
216216
217217 return PROBE_NEXT;
218218 }
219219
220+static int is_sni_protocol(const char *p, int len, struct proto *proto)
221+{
222+ int valid_tls;
223+ char *hostname;
224+ char **sni_hostname;
225+
226+ valid_tls = parse_tls_header(p, len, &hostname);
227+
228+ if(valid_tls < 0)
229+ return -1 == valid_tls ? PROBE_AGAIN : PROBE_NEXT;
230+
231+ if (verbose) fprintf(stderr, "sni hostname: %s\n", hostname);
232+
233+ /* Assume does not match */
234+ valid_tls = PROBE_NEXT;
235+
236+ for (sni_hostname = proto->data; *sni_hostname; sni_hostname++) {
237+ fprintf(stderr, "matching [%s] with [%s]\n", hostname, *sni_hostname);
238+ if(!strcmp(hostname, *sni_hostname)) {
239+ valid_tls = PROBE_MATCH;
240+ break;
241+ }
242+ }
243+
244+ free(hostname);
245+ return valid_tls;
246+}
247+
220248 static int is_tls_protocol(const char *p, int len, struct proto *proto)
221249 {
222250 if (len < 3)
223251 return PROBE_AGAIN;
@@ -334,8 +362,17 @@
334362 * regexp is not legal on the command line)*/
335363 if (!strcmp(description, "regex"))
336364 return regex_probe;
337365
366+ /* Special case of "sni" probe for same reason as above*/
367+ if (!strcmp(description, "sni"))
368+ return is_sni_protocol;
369+
370+ /* Special case of "timeout" is allowed as a probe name in the
371+ * configuration file even though it's not really a probe */
372+ if (!strcmp(description, "timeout"))
373+ return is_true;
374+
338375 return NULL;
339376 }
340377
341378
probe.hView
@@ -3,8 +3,9 @@
33 #ifndef __PROBE_H_
44 #define __PROBE_H_
55
66 #include "common.h"
7+#include "tls.h"
78
89 typedef enum {
910 PROBE_NEXT, /* Enough data, probe failed -- it's some other protocol */
1011 PROBE_MATCH, /* Enough data, probe successful -- it's the current protocol */
@@ -22,9 +23,9 @@
2223
2324 /* function to probe that protocol; parameters are buffer and length
2425 * containing the data to probe, and a pointer to the protocol structure */
2526 T_PROBE* probe;
26- void* data; /* opaque pointer ; used to pass list of regex to regex probe */
27+ void* data; /* opaque pointer ; used to pass list of regex to regex probe, or sni hostnames to sni probe */
2728 struct proto *next; /* pointer to next protocol in list, NULL if last */
2829 };
2930
3031 /* Returns a pointer to the array of builtin protocols */
sslh-main.cView
@@ -210,15 +210,47 @@
210210 #endif
211211 }
212212 #endif
213213
214+#ifdef LIBCONFIG
215+static void setup_sni_hostnames(struct proto *p, config_setting_t* sni_hostnames)
216+{
217+ int num_probes, i, max_server_name_len, server_name_len;
218+ const char * sni_hostname;
219+ char** sni_hostname_list;
220+
221+ num_probes = config_setting_length(sni_hostnames);
222+ if (!num_probes) {
223+ fprintf(stderr, "%s: no sni_hostnames specified\n", p->description);
224+ exit(1);
225+ }
226+
227+ max_server_name_len = 0;
228+ for (i = 0; i < num_probes; i++) {
229+ server_name_len = strlen(config_setting_get_string_elem(sni_hostnames, i));
230+ if(server_name_len > max_server_name_len)
231+ max_server_name_len = server_name_len;
232+ }
233+
234+ sni_hostname_list = calloc(num_probes + 1, ++max_server_name_len);
235+ p->data = (void*)sni_hostname_list;
236+
237+ for (i = 0; i < num_probes; i++) {
238+ sni_hostname = config_setting_get_string_elem(sni_hostnames, i);
239+ sni_hostname_list[i] = malloc(max_server_name_len);
240+ strcpy (sni_hostname_list[i], sni_hostname);
241+ if(verbose) fprintf(stderr, "sni_hostnames[%d]: %s\n", i, sni_hostname_list[i]);
242+ }
243+}
244+#endif
245+
214246 /* Extract configuration for protocols to connect to.
215247 * out: newly-allocated list of protocols
216248 */
217249 #ifdef LIBCONFIG
218250 static int config_protocols(config_t *config, struct proto **prots)
219251 {
220- config_setting_t *setting, *prot, *probes;
252+ config_setting_t *setting, *prot, *patterns, *sni_hostnames;
221253 const char *hostname, *port, *name;
222254 int i, num_prots;
223255 struct proto *p, *prev = NULL;
224256
@@ -240,30 +272,27 @@
240272 config_setting_lookup_string(prot, "service", &(p->service));
241273
242274 resolve_split_name(&(p->saddr), hostname, port);
243275
276+ p->probe = get_probe(name);
277+ if (!p->probe) {
278+ fprintf(stderr, "line %d: %s: probe unknown\n", config_setting_source_line(prot), name);
279+ exit(1);
280+ }
244281
245- probes = config_setting_get_member(prot, "probe");
246- if (probes) {
247- if (config_setting_is_array(probes)) {
248- /* If 'probe' is an array, setup a regex probe using the
249- * array of strings as pattern */
282+ /* Probe-specific options: regex patterns */
283+ if (!strcmp(name, "regex")) {
284+ patterns = config_setting_get_member(prot, "regex_patterns");
285+ if (patterns && config_setting_is_array(patterns)) {
286+ setup_regex_probe(p, patterns);
287+ }
288+ }
250289
251- setup_regex_probe(p, probes);
252-
253- } else {
254- /* if 'probe' is 'builtin', set the probe to the
255- * appropriate builtin protocol */
256- if (!strcmp(config_setting_get_string(probes), "builtin")) {
257- p->probe = get_probe(name);
258- if (!p->probe) {
259- fprintf(stderr, "%s: no builtin probe for this protocol\n", name);
260- exit(1);
261- }
262- } else {
263- fprintf(stderr, "%s: illegal probe name\n", name);
264- exit(1);
265- }
290+ /* Probe-specific options: SNI hostnames */
291+ if (!strcmp(name, "sni")) {
292+ sni_hostnames = config_setting_get_member(prot, "sni_hostnames");
293+ if (sni_hostnames && config_setting_is_array(sni_hostnames)) {
294+ setup_sni_hostnames(p, sni_hostnames);
266295 }
267296 }
268297 }
269298 }
sslh.podView
@@ -50,16 +50,12 @@
5050 and the list of protocols).
5151
5252 The configuration file makes it possible to specify
5353 protocols using regular expressions: a list of regular
54-expressions is given as the I<probe> parameter, and if the
54+expressions is given as the I<regex_patterns> parameter, and if the
5555 first packet received from the client matches any of these
5656 expressions, B<sslh> connects to that protocol.
5757
58-Alternatively, the I<probe> parameter can be set to
59-"builtin", to use the compiled probes which are much faster
60-than regular expressions.
61-
6258 =head2 Probing protocols
6359
6460 When receiving an incoming connection, B<sslh> will read the
6561 first bytes sent be the connecting client. It will then
tls.cView
@@ -1,0 +1,238 @@
1+/*
2+ * Copyright (c) 2011 and 2012, Dustin Lundquist <dustin@null-ptr.net>
3+ * All rights reserved.
4+ *
5+ * Redistribution and use in source and binary forms, with or without
6+ * modification, are permitted provided that the following conditions are met:
7+ *
8+ * 1. Redistributions of source code must retain the above copyright notice,
9+ * this list of conditions and the following disclaimer.
10+ * 2. Redistributions in binary form must reproduce the above copyright
11+ * notice, this list of conditions and the following disclaimer in the
12+ * documentation and/or other materials provided with the distribution.
13+ *
14+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
18+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24+ * POSSIBILITY OF SUCH DAMAGE.
25+ */
26+/*
27+ * This is a minimal TLS implementation intended only to parse the server name
28+ * extension. This was created based primarily on Wireshark dissection of a
29+ * TLS handshake and RFC4366.
30+ */
31+#include <stdio.h>
32+#include <stdlib.h> /* malloc() */
33+#include <string.h> /* strncpy() */
34+#include <sys/socket.h>
35+#include "tls.h"
36+
37+#define TLS_HEADER_LEN 5
38+#define TLS_HANDSHAKE_CONTENT_TYPE 0x16
39+#define TLS_HANDSHAKE_TYPE_CLIENT_HELLO 0x01
40+
41+#ifndef MIN
42+#define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
43+#endif
44+
45+static int parse_extensions(const char *, size_t, char **);
46+static int parse_server_name_extension(const char *, size_t, char **);
47+
48+const char tls_alert[] = {
49+ 0x15, /* TLS Alert */
50+ 0x03, 0x01, /* TLS version */
51+ 0x00, 0x02, /* Payload length */
52+ 0x02, 0x28, /* Fatal, handshake failure */
53+};
54+
55+/* Parse a TLS packet for the Server Name Indication extension in the client
56+ * hello handshake, returning the first servername found (pointer to static
57+ * array)
58+ *
59+ * Returns:
60+ * >=0 - length of the hostname and updates *hostname
61+ * caller is responsible for freeing *hostname
62+ * -1 - Incomplete request
63+ * -2 - No Host header included in this request
64+ * -3 - Invalid hostname pointer
65+ * -4 - malloc failure
66+ * < -4 - Invalid TLS client hello
67+ */
68+int
69+parse_tls_header(const char *data, size_t data_len, char **hostname) {
70+ char tls_content_type;
71+ char tls_version_major;
72+ char tls_version_minor;
73+ size_t pos = TLS_HEADER_LEN;
74+ size_t len;
75+
76+ if (hostname == NULL)
77+ return -3;
78+
79+ /* Check that our TCP payload is at least large enough for a TLS header */
80+ if (data_len < TLS_HEADER_LEN)
81+ return -1;
82+
83+ /* SSL 2.0 compatible Client Hello
84+ *
85+ * High bit of first byte (length) and content type is Client Hello
86+ *
87+ * See RFC5246 Appendix E.2
88+ */
89+ if (data[0] & 0x80 && data[2] == 1) {
90+ if (verbose) fprintf(stderr, "Received SSL 2.0 Client Hello which can not support SNI.\n");
91+ return -2;
92+ }
93+
94+ tls_content_type = data[0];
95+ if (tls_content_type != TLS_HANDSHAKE_CONTENT_TYPE) {
96+ if (verbose) fprintf(stderr, "Request did not begin with TLS handshake.\n");
97+ return -5;
98+ }
99+
100+ tls_version_major = data[1];
101+ tls_version_minor = data[2];
102+ if (tls_version_major < 3) {
103+ if (verbose) fprintf(stderr, "Received SSL %d.%d handshake which which can not support SNI.\n",
104+ tls_version_major, tls_version_minor);
105+
106+ return -2;
107+ }
108+
109+ /* TLS record length */
110+ len = ((unsigned char)data[3] << 8) +
111+ (unsigned char)data[4] + TLS_HEADER_LEN;
112+ data_len = MIN(data_len, len);
113+
114+ /* Check we received entire TLS record length */
115+ if (data_len < len)
116+ return -1;
117+
118+ /*
119+ * Handshake
120+ */
121+ if (pos + 1 > data_len) {
122+ return -5;
123+ }
124+ if (data[pos] != TLS_HANDSHAKE_TYPE_CLIENT_HELLO) {
125+ if (verbose) fprintf(stderr, "Not a client hello\n");
126+
127+ return -5;
128+ }
129+
130+ /* Skip past fixed length records:
131+ 1 Handshake Type
132+ 3 Length
133+ 2 Version (again)
134+ 32 Random
135+ to Session ID Length
136+ */
137+ pos += 38;
138+
139+ /* Session ID */
140+ if (pos + 1 > data_len)
141+ return -5;
142+ len = (unsigned char)data[pos];
143+ pos += 1 + len;
144+
145+ /* Cipher Suites */
146+ if (pos + 2 > data_len)
147+ return -5;
148+ len = ((unsigned char)data[pos] << 8) + (unsigned char)data[pos + 1];
149+ pos += 2 + len;
150+
151+ /* Compression Methods */
152+ if (pos + 1 > data_len)
153+ return -5;
154+ len = (unsigned char)data[pos];
155+ pos += 1 + len;
156+
157+ if (pos == data_len && tls_version_major == 3 && tls_version_minor == 0) {
158+ if (verbose) fprintf(stderr, "Received SSL 3.0 handshake without extensions\n");
159+ return -2;
160+ }
161+
162+ /* Extensions */
163+ if (pos + 2 > data_len)
164+ return -5;
165+ len = ((unsigned char)data[pos] << 8) + (unsigned char)data[pos + 1];
166+ pos += 2;
167+
168+ if (pos + len > data_len)
169+ return -5;
170+ return parse_extensions(data + pos, len, hostname);
171+}
172+
173+int
174+parse_extensions(const char *data, size_t data_len, char **hostname) {
175+ size_t pos = 0;
176+ size_t len;
177+
178+ /* Parse each 4 bytes for the extension header */
179+ while (pos + 4 <= data_len) {
180+ /* Extension Length */
181+ len = ((unsigned char)data[pos + 2] << 8) +
182+ (unsigned char)data[pos + 3];
183+
184+ /* Check if it's a server name extension */
185+ if (data[pos] == 0x00 && data[pos + 1] == 0x00) {
186+ /* There can be only one extension of each type, so we break
187+ our state and move p to beinnging of the extension here */
188+ if (pos + 4 + len > data_len)
189+ return -5;
190+ return parse_server_name_extension(data + pos + 4, len, hostname);
191+ }
192+ pos += 4 + len; /* Advance to the next extension header */
193+ }
194+ /* Check we ended where we expected to */
195+ if (pos != data_len)
196+ return -5;
197+
198+ return -2;
199+}
200+
201+int
202+parse_server_name_extension(const char *data, size_t data_len,
203+ char **hostname) {
204+ size_t pos = 2; /* skip server name list length */
205+ size_t len;
206+
207+ while (pos + 3 < data_len) {
208+ len = ((unsigned char)data[pos + 1] << 8) +
209+ (unsigned char)data[pos + 2];
210+
211+ if (pos + 3 + len > data_len)
212+ return -5;
213+
214+ switch (data[pos]) { /* name type */
215+ case 0x00: /* host_name */
216+ *hostname = malloc(len + 1);
217+ if (*hostname == NULL) {
218+ if (verbose) fprintf(stderr, "malloc() failure\n");
219+ return -4;
220+ }
221+
222+ strncpy(*hostname, data + pos + 3, len);
223+
224+ (*hostname)[len] = '\0';
225+
226+ return len;
227+ default:
228+ if (verbose) fprintf(stderr, "Unknown server name extension name type: %d\n",
229+ data[pos]);
230+ }
231+ pos += 3 + len;
232+ }
233+ /* Check we ended where we expected to */
234+ if (pos != data_len)
235+ return -5;
236+
237+ return -2;
238+}
tls.hView
@@ -1,0 +1,33 @@
1+/*
2+ * Copyright (c) 2011 and 2012, Dustin Lundquist <dustin@null-ptr.net>
3+ * All rights reserved.
4+ *
5+ * Redistribution and use in source and binary forms, with or without
6+ * modification, are permitted provided that the following conditions are met:
7+ *
8+ * 1. Redistributions of source code must retain the above copyright notice,
9+ * this list of conditions and the following disclaimer.
10+ * 2. Redistributions in binary form must reproduce the above copyright
11+ * notice, this list of conditions and the following disclaimer in the
12+ * documentation and/or other materials provided with the distribution.
13+ *
14+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
18+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24+ * POSSIBILITY OF SUCH DAMAGE.
25+ */
26+#ifndef TLS_H
27+#define TLS_H
28+
29+#include "common.h"
30+
31+int parse_tls_header(const char *data, size_t data_len, char **hostname);
32+
33+#endif

Built with git-ssb-web