Commit e7d3133ba5bed4d03677431e1a5e769114a3979b
Merge pull request #74 from moparisthebest/master
Add ALPN protocol based probeyrutschle committed on 1/22/2016, 3:21:32 PM
Parent: 0b6cc0d9091ffe0a8709a47fbf88cac71e54e976
Parent: 8af039d3eb3e890482fd6755532d7786a104cae8
Files changed
ChangeLog | changed |
example.cfg | changed |
probe.c | changed |
probe.h | changed |
sslh-main.c | changed |
tls.c | changed |
tls.h | changed |
ChangeLog | ||
---|---|---|
@@ -1,18 +1,18 @@ | ||
1 | 1 | vNEXT: |
2 | 2 | Added USELIBPCRE to make use of regex engine |
3 | 3 | optional. |
4 | 4 | |
5 | - Added support for RFC4366 SNI | |
5 | + Added support for RFC4366 SNI and RFC7301 ALPN | |
6 | 6 | (Travis Burtrum) |
7 | 7 | |
8 | 8 | Changed connection log to include the name of the probe that |
9 | 9 | triggered. |
10 | 10 | |
11 | 11 | Changed configuration file format: 'probe' field is |
12 | 12 | no longer required, 'name' field can now contain |
13 | - 'sni' or 'regex', with corresponding options (see | |
14 | - example.org) | |
13 | + 'tls' or 'regex', with corresponding options (see | |
14 | + example.cfg) | |
15 | 15 | Added 'log_level' option to each protocol, which |
16 | 16 | allows to turn off generation of log at each |
17 | 17 | connection. |
18 | 18 |
example.cfg | ||
---|---|---|
@@ -23,33 +23,49 @@ | ||
23 | 23 | # List of protocols |
24 | 24 | # |
25 | 25 | # Each protocol entry consists of: |
26 | 26 | # name: name of the probe. These are listed on the command |
27 | -# line (ssh -?), plus 'regex', 'sni' and 'timeout'. | |
27 | +# line (ssh -?), plus 'regex' and 'timeout'. | |
28 | 28 | |
29 | 29 | # service: (optional) libwrap service name (see hosts_access(5)) |
30 | 30 | # host, port: where to connect when this probe succeeds |
31 | 31 | # |
32 | 32 | # Probe-specific options: |
33 | -# sni: | |
34 | -# sni_hotnames: list of FQDN for that target | |
33 | +# tls: | |
34 | +# sni_hostnames: list of FQDN for that target | |
35 | +# alpn_protocols: list of ALPN protocols for that target, see: | |
36 | +# https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids | |
37 | +# | |
38 | +# if both sni_hostnames AND alpn_protocols are specified, both must match | |
39 | +# if neither are set, it is just checked whether this is the TLS protocol or not | |
35 | 40 | # regex: |
36 | 41 | # regex_patterns: list of patterns to match for |
37 | 42 | # that target. |
38 | 43 | # |
39 | 44 | # sslh will try each probe in order they are declared, and |
40 | 45 | # connect to the first that matches. |
41 | 46 | # |
42 | -# You can specify several of 'regex' and 'sni'. | |
47 | +# You can specify several of 'regex' and 'tls'. | |
43 | 48 | |
44 | 49 | protocols: |
45 | 50 | ( |
46 | 51 | { name: "ssh"; service: "ssh"; host: "localhost"; port: "22"; }, |
47 | 52 | { name: "http"; host: "localhost"; port: "80"; }, |
48 | 53 | |
49 | - { name: "sni"; host: "localhost"; port: "993"; sni_hostnames: [ "mail.rutschle.net", "mail.englishintoulouse.com" ]; log_level: 0; }, | |
50 | - { name: "sni"; host: "localhost"; port: "xmpp-client"; sni_hostnames: [ "im.rutschle.net", "im.englishintoulouse.com" ]; log_level: 0;}, | |
54 | +# match BOTH ALPN/SNI | |
55 | + { name: "tls"; host: "localhost"; port: "5223"; alpn_protocols: [ "xmpp-client" ]; sni_hostnames: [ "im.somethingelse.net" ]; log_level: 0;}, | |
51 | 56 | |
57 | +# just match ALPN | |
58 | + { name: "tls"; host: "localhost"; port: "443"; alpn_protocols: [ "h2", "http/1.1", "spdy/1", "spdy/2", "spdy/3" ]; log_level: 0; }, | |
59 | + { name: "tls"; host: "localhost"; port: "xmpp-client"; alpn_protocols: [ "xmpp-client" ]; log_level: 0;}, | |
60 | + | |
61 | +# just match SNI | |
62 | + { name: "tls"; host: "localhost"; port: "993"; sni_hostnames: [ "mail.rutschle.net", "mail.englishintoulouse.com" ]; log_level: 0; }, | |
63 | + { name: "tls"; host: "localhost"; port: "xmpp-client"; sni_hostnames: [ "im.rutschle.net", "im.englishintoulouse.com" ]; log_level: 0;}, | |
64 | + | |
65 | +# catch anything else TLS | |
66 | + { name: "tls"; host: "localhost"; port: "443"; }, | |
67 | + | |
52 | 68 | # OpenVPN |
53 | 69 | { name: "regex"; host: "localhost"; port: "1194"; regex_patterns: [ "^\x00[\x0D-\xFF]$", "^\x00[\x0D-\xFF]\x38" ]; }, |
54 | 70 | # Jabber |
55 | 71 | { name: "regex"; host: "localhost"; port: "5222"; regex_patterns: [ "jabber" ]; }, |
probe.c | ||
---|---|---|
@@ -216,34 +216,19 @@ | ||
216 | 216 | |
217 | 217 | return PROBE_NEXT; |
218 | 218 | } |
219 | 219 | |
220 | -static int is_sni_protocol(const char *p, int len, struct proto *proto) | |
220 | +static int is_sni_alpn_protocol(const char *p, int len, struct proto *proto) | |
221 | 221 | { |
222 | 222 | int valid_tls; |
223 | - char *hostname; | |
224 | - char **sni_hostname; | |
225 | 223 | |
226 | - valid_tls = parse_tls_header(p, len, &hostname); | |
224 | + valid_tls = parse_tls_header(proto->data, p, len); | |
227 | 225 | |
228 | 226 | if(valid_tls < 0) |
229 | 227 | return -1 == valid_tls ? PROBE_AGAIN : PROBE_NEXT; |
230 | 228 | |
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; | |
229 | + /* There *was* a valid match */ | |
230 | + return PROBE_MATCH; | |
246 | 231 | } |
247 | 232 | |
248 | 233 | static int is_tls_protocol(const char *p, int len, struct proto *proto) |
249 | 234 | { |
@@ -362,11 +347,11 @@ | ||
362 | 347 | * regexp is not legal on the command line)*/ |
363 | 348 | if (!strcmp(description, "regex")) |
364 | 349 | return regex_probe; |
365 | 350 | |
366 | - /* Special case of "sni" probe for same reason as above*/ | |
367 | - if (!strcmp(description, "sni")) | |
368 | - return is_sni_protocol; | |
351 | + /* Special case of "sni/alpn" probe for same reason as above*/ | |
352 | + if (!strcmp(description, "sni_alpn")) | |
353 | + return is_sni_alpn_protocol; | |
369 | 354 | |
370 | 355 | /* Special case of "timeout" is allowed as a probe name in the |
371 | 356 | * configuration file even though it's not really a probe */ |
372 | 357 | if (!strcmp(description, "timeout")) |
probe.h | ||
---|---|---|
@@ -26,9 +26,10 @@ | ||
26 | 26 | |
27 | 27 | /* function to probe that protocol; parameters are buffer and length |
28 | 28 | * containing the data to probe, and a pointer to the protocol structure */ |
29 | 29 | T_PROBE* probe; |
30 | - void* data; /* opaque pointer ; used to pass list of regex to regex probe, or sni hostnames to sni probe */ | |
30 | + /* opaque pointer ; used to pass list of regex to regex probe, or TLSProtocol struct to sni/alpn probe */ | |
31 | + void* data; | |
31 | 32 | struct proto *next; /* pointer to next protocol in list, NULL if last */ |
32 | 33 | }; |
33 | 34 | |
34 | 35 | /* Returns a pointer to the array of builtin protocols */ |
sslh-main.c | ||
---|---|---|
@@ -212,46 +212,59 @@ | ||
212 | 212 | } |
213 | 213 | |
214 | 214 | |
215 | 215 | |
216 | -static void setup_sni_hostnames(struct proto *p, config_setting_t* sni_hostnames) | |
216 | +static void setup_sni_alpn_list(struct proto *p, config_setting_t* config_items, const char* name, int alpn) | |
217 | 217 | { |
218 | 218 | int num_probes, i, max_server_name_len, server_name_len; |
219 | - const char * sni_hostname; | |
219 | + const char * config_item; | |
220 | 220 | char** sni_hostname_list; |
221 | 221 | |
222 | - num_probes = config_setting_length(sni_hostnames); | |
222 | + if(!config_items || !config_setting_is_array(config_items)) { | |
223 | + fprintf(stderr, "%s: no %s specified\n", p->description, name); | |
224 | + return; | |
225 | + } | |
226 | + num_probes = config_setting_length(config_items); | |
223 | 227 | if (!num_probes) { |
224 | - fprintf(stderr, "%s: no sni_hostnames specified\n", p->description); | |
225 | - exit(1); | |
228 | + fprintf(stderr, "%s: no %s specified\n", p->description, name); | |
229 | + return; | |
226 | 230 | } |
227 | 231 | |
228 | 232 | max_server_name_len = 0; |
229 | 233 | for (i = 0; i < num_probes; i++) { |
230 | - server_name_len = strlen(config_setting_get_string_elem(sni_hostnames, i)); | |
234 | + server_name_len = strlen(config_setting_get_string_elem(config_items, i)); | |
231 | 235 | if(server_name_len > max_server_name_len) |
232 | 236 | max_server_name_len = server_name_len; |
233 | 237 | } |
234 | 238 | |
235 | 239 | sni_hostname_list = calloc(num_probes + 1, ++max_server_name_len); |
236 | - p->data = (void*)sni_hostname_list; | |
237 | 240 | |
238 | 241 | for (i = 0; i < num_probes; i++) { |
239 | - sni_hostname = config_setting_get_string_elem(sni_hostnames, i); | |
242 | + config_item = config_setting_get_string_elem(config_items, i); | |
240 | 243 | sni_hostname_list[i] = malloc(max_server_name_len); |
241 | - strcpy (sni_hostname_list[i], sni_hostname); | |
242 | - if(verbose) fprintf(stderr, "sni_hostnames[%d]: %s\n", i, sni_hostname_list[i]); | |
244 | + strcpy (sni_hostname_list[i], config_item); | |
245 | + if(verbose) fprintf(stderr, "%s: %s[%d]: %s\n", p->description, name, i, sni_hostname_list[i]); | |
243 | 246 | } |
247 | + | |
248 | + p->data = (void*)tls_data_set_list(p->data, alpn, sni_hostname_list); | |
244 | 249 | } |
250 | + | |
251 | +static void setup_sni_alpn(struct proto *p, config_setting_t* sni_hostnames, config_setting_t* alpn_protocols) | |
252 | +{ | |
253 | + p->data = (void*)new_tls_data(); | |
254 | + p->probe = get_probe("sni_alpn"); | |
255 | + setup_sni_alpn_list(p, sni_hostnames, "sni_hostnames", 0); | |
256 | + setup_sni_alpn_list(p, alpn_protocols, "alpn_protocols", 1); | |
257 | +} | |
245 | 258 | |
246 | 259 | |
247 | 260 | /* Extract configuration for protocols to connect to. |
248 | 261 | * out: newly-allocated list of protocols |
249 | 262 | */ |
250 | 263 | |
251 | 264 | static int config_protocols(config_t *config, struct proto **prots) |
252 | 265 | { |
253 | - config_setting_t *setting, *prot, *patterns, *sni_hostnames; | |
266 | + config_setting_t *setting, *prot, *patterns, *sni_hostnames, *alpn_protocols; | |
254 | 267 | const char *hostname, *port, *name; |
255 | 268 | int i, num_prots; |
256 | 269 | struct proto *p, *prev = NULL; |
257 | 270 | |
@@ -278,9 +291,9 @@ | ||
278 | 291 | |
279 | 292 | resolve_split_name(&(p->saddr), hostname, port); |
280 | 293 | |
281 | 294 | p->probe = get_probe(name); |
282 | - if (!p->probe) { | |
295 | + if (!p->probe || !strcmp(name, "sni_alpn")) { | |
283 | 296 | fprintf(stderr, "line %d: %s: probe unknown\n", config_setting_source_line(prot), name); |
284 | 297 | exit(1); |
285 | 298 | } |
286 | 299 | |
@@ -291,15 +304,18 @@ | ||
291 | 304 | setup_regex_probe(p, patterns); |
292 | 305 | } |
293 | 306 | } |
294 | 307 | |
295 | - /* Probe-specific options: SNI hostnames */ | |
296 | - if (!strcmp(name, "sni")) { | |
308 | + /* Probe-specific options: SNI/ALPN */ | |
309 | + if (!strcmp(name, "tls")) { | |
297 | 310 | sni_hostnames = config_setting_get_member(prot, "sni_hostnames"); |
298 | - if (sni_hostnames && config_setting_is_array(sni_hostnames)) { | |
299 | - setup_sni_hostnames(p, sni_hostnames); | |
311 | + alpn_protocols = config_setting_get_member(prot, "alpn_protocols"); | |
312 | + | |
313 | + if((sni_hostnames && config_setting_is_array(sni_hostnames)) || (alpn_protocols && config_setting_is_array(alpn_protocols))) { | |
314 | + setup_sni_alpn(p, sni_hostnames, alpn_protocols); | |
300 | 315 | } |
301 | 316 | } |
317 | + | |
302 | 318 | } |
303 | 319 | } |
304 | 320 | } |
305 | 321 |
tls.c | ||
---|---|---|
@@ -29,10 +29,8 @@ | ||
29 | 29 | * TLS handshake and RFC4366. |
30 | 30 | */ |
31 | 31 | |
32 | 32 | |
33 | - | |
34 | - | |
35 | 33 | |
36 | 34 | |
37 | 35 | |
38 | 36 | |
@@ -41,57 +39,43 @@ | ||
41 | 39 | |
42 | 40 | |
43 | 41 | |
44 | 42 | |
45 | -static int parse_extensions(const char *, size_t, char **); | |
46 | -static int parse_server_name_extension(const char *, size_t, char **); | |
47 | 43 | |
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 */ | |
44 | +struct TLSProtocol { | |
45 | + int use_alpn; | |
46 | + char** sni_hostname_list; | |
47 | + char** alpn_protocol_list; | |
53 | 48 | }; |
54 | 49 | |
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) | |
50 | +static int parse_extensions(const struct TLSProtocol *, const char *, size_t); | |
51 | +static int parse_server_name_extension(const struct TLSProtocol *, const char *, size_t); | |
52 | +static int parse_alpn_extension(const struct TLSProtocol *, const char *, size_t); | |
53 | +static int has_match(char**, const char*, size_t); | |
54 | + | |
55 | +/* Parse a TLS packet for the Server Name Indication and ALPN extension in the client | |
56 | + * hello handshake, returning a status code | |
58 | 57 | * |
59 | 58 | * Returns: |
60 | 59 | * >=0 - length of the hostname and updates *hostname |
61 | 60 | * caller is responsible for freeing *hostname |
62 | 61 | * -1 - Incomplete request |
63 | 62 | * -2 - No Host header included in this request |
64 | 63 | * -3 - Invalid hostname pointer |
65 | - * -4 - malloc failure | |
66 | 64 | * < -4 - Invalid TLS client hello |
67 | 65 | */ |
68 | 66 | int |
69 | -parse_tls_header(const char *data, size_t data_len, char **hostname) { | |
67 | +parse_tls_header(const struct TLSProtocol *tls_data, const char *data, size_t data_len) { | |
70 | 68 | char tls_content_type; |
71 | 69 | char tls_version_major; |
72 | 70 | char tls_version_minor; |
73 | 71 | size_t pos = TLS_HEADER_LEN; |
74 | 72 | size_t len; |
75 | 73 | |
76 | - if (hostname == NULL) | |
77 | - return -3; | |
78 | - | |
79 | 74 | /* Check that our TCP payload is at least large enough for a TLS header */ |
80 | 75 | if (data_len < TLS_HEADER_LEN) |
81 | 76 | return -1; |
82 | 77 | |
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 | 78 | tls_content_type = data[0]; |
95 | 79 | if (tls_content_type != TLS_HANDSHAKE_CONTENT_TYPE) { |
96 | 80 | if (verbose) fprintf(stderr, "Request did not begin with TLS handshake.\n"); |
97 | 81 | return -5; |
@@ -99,17 +83,17 @@ | ||
99 | 83 | |
100 | 84 | tls_version_major = data[1]; |
101 | 85 | tls_version_minor = data[2]; |
102 | 86 | if (tls_version_major < 3) { |
103 | - if (verbose) fprintf(stderr, "Received SSL %d.%d handshake which which can not support SNI.\n", | |
87 | + if (verbose) fprintf(stderr, "Received SSL %d.%d handshake which cannot be parsed.\n", | |
104 | 88 | tls_version_major, tls_version_minor); |
105 | 89 | |
106 | 90 | return -2; |
107 | 91 | } |
108 | 92 | |
109 | 93 | /* TLS record length */ |
110 | 94 | len = ((unsigned char)data[3] << 8) + |
111 | - (unsigned char)data[4] + TLS_HEADER_LEN; | |
95 | + (unsigned char)data[4] + TLS_HEADER_LEN; | |
112 | 96 | data_len = MIN(data_len, len); |
113 | 97 | |
114 | 98 | /* Check we received entire TLS record length */ |
115 | 99 | if (data_len < len) |
@@ -166,65 +150,103 @@ | ||
166 | 150 | pos += 2; |
167 | 151 | |
168 | 152 | if (pos + len > data_len) |
169 | 153 | return -5; |
170 | - return parse_extensions(data + pos, len, hostname); | |
154 | + return parse_extensions(tls_data, data + pos, len); | |
171 | 155 | } |
172 | 156 | |
173 | -int | |
174 | -parse_extensions(const char *data, size_t data_len, char **hostname) { | |
157 | +static int | |
158 | +parse_extensions(const struct TLSProtocol *tls_data, const char *data, size_t data_len) { | |
175 | 159 | size_t pos = 0; |
176 | 160 | size_t len; |
161 | + int last_matched = 0; | |
177 | 162 | |
163 | + if (tls_data == NULL) | |
164 | + return -3; | |
165 | + | |
178 | 166 | /* Parse each 4 bytes for the extension header */ |
179 | 167 | while (pos + 4 <= data_len) { |
180 | 168 | /* Extension Length */ |
181 | - len = ((unsigned char)data[pos + 2] << 8) + | |
182 | - (unsigned char)data[pos + 3]; | |
169 | + len = ((unsigned char) data[pos + 2] << 8) + | |
170 | + (unsigned char) data[pos + 3]; | |
183 | 171 | |
172 | + if (pos + 4 + len > data_len) | |
173 | + return -5; | |
174 | + | |
175 | + size_t extension_type = ((unsigned char) data[pos] << 8) + | |
176 | + (unsigned char) data[pos + 1]; | |
177 | + | |
178 | + | |
184 | 179 | /* 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); | |
180 | + /* There can be only one extension of each type, so we break | |
181 | + our state and move pos to beginning of the extension here */ | |
182 | + if (tls_data->use_alpn == 2) { | |
183 | + /* we want BOTH alpn and sni to match */ | |
184 | + if (extension_type == 0x00) { /* Server Name */ | |
185 | + if (parse_server_name_extension(tls_data, data + pos + 4, len)) { | |
186 | + /* SNI matched */ | |
187 | + if(last_matched) { | |
188 | + /* this is only true if ALPN matched, so return true */ | |
189 | + return last_matched; | |
190 | + } else { | |
191 | + /* otherwise store that SNI matched */ | |
192 | + last_matched = 1; | |
193 | + } | |
194 | + } else { | |
195 | + // both can't match | |
196 | + return -2; | |
197 | + } | |
198 | + } else if (extension_type == 0x10) { /* ALPN */ | |
199 | + if (parse_alpn_extension(tls_data, data + pos + 4, len)) { | |
200 | + /* ALPN matched */ | |
201 | + if(last_matched) { | |
202 | + /* this is only true if SNI matched, so return true */ | |
203 | + return last_matched; | |
204 | + } else { | |
205 | + /* otherwise store that ALPN matched */ | |
206 | + last_matched = 1; | |
207 | + } | |
208 | + } else { | |
209 | + // both can't match | |
210 | + return -2; | |
211 | + } | |
212 | + } | |
213 | + | |
214 | + } else if (extension_type == 0x00 && tls_data->use_alpn == 0) { /* Server Name */ | |
215 | + return parse_server_name_extension(tls_data, data + pos + 4, len); | |
216 | + } else if (extension_type == 0x10 && tls_data->use_alpn == 1) { /* ALPN */ | |
217 | + return parse_alpn_extension(tls_data, data + pos + 4, len); | |
191 | 218 | } |
219 | + | |
192 | 220 | pos += 4 + len; /* Advance to the next extension header */ |
193 | 221 | } |
222 | + | |
194 | 223 | /* Check we ended where we expected to */ |
195 | 224 | if (pos != data_len) |
196 | 225 | return -5; |
197 | 226 | |
198 | 227 | return -2; |
199 | 228 | } |
200 | 229 | |
201 | -int | |
202 | -parse_server_name_extension(const char *data, size_t data_len, | |
203 | - char **hostname) { | |
230 | +static int | |
231 | +parse_server_name_extension(const struct TLSProtocol *tls_data, const char *data, size_t data_len) { | |
204 | 232 | size_t pos = 2; /* skip server name list length */ |
205 | 233 | size_t len; |
206 | 234 | |
207 | 235 | while (pos + 3 < data_len) { |
208 | 236 | len = ((unsigned char)data[pos + 1] << 8) + |
209 | - (unsigned char)data[pos + 2]; | |
237 | + (unsigned char)data[pos + 2]; | |
210 | 238 | |
211 | 239 | if (pos + 3 + len > data_len) |
212 | 240 | return -5; |
213 | 241 | |
214 | 242 | switch (data[pos]) { /* name type */ |
215 | 243 | case 0x00: /* host_name */ |
216 | - *hostname = malloc(len + 1); | |
217 | - if (*hostname == NULL) { | |
218 | - if (verbose) fprintf(stderr, "malloc() failure\n"); | |
219 | - return -4; | |
244 | + if(has_match(tls_data->sni_hostname_list, data + pos + 3, len)) { | |
245 | + return len; | |
246 | + } else { | |
247 | + return -2; | |
220 | 248 | } |
221 | - | |
222 | - strncpy(*hostname, data + pos + 3, len); | |
223 | - | |
224 | - (*hostname)[len] = '\0'; | |
225 | - | |
226 | - return len; | |
227 | 249 | default: |
228 | 250 | if (verbose) fprintf(stderr, "Unknown server name extension name type: %d\n", |
229 | 251 | data[pos]); |
230 | 252 | } |
@@ -235,4 +257,71 @@ | ||
235 | 257 | return -5; |
236 | 258 | |
237 | 259 | return -2; |
238 | 260 | } |
261 | + | |
262 | +static int | |
263 | +parse_alpn_extension(const struct TLSProtocol *tls_data, const char *data, size_t data_len) { | |
264 | + size_t pos = 2; | |
265 | + size_t len; | |
266 | + | |
267 | + while (pos + 1 < data_len) { | |
268 | + len = (unsigned char)data[pos]; | |
269 | + | |
270 | + if (pos + 1 + len > data_len) | |
271 | + return -5; | |
272 | + | |
273 | + if (len > 0 && has_match(tls_data->alpn_protocol_list, data + pos + 1, len)) { | |
274 | + return len; | |
275 | + } else if (len > 0) { | |
276 | + if (verbose) fprintf(stderr, "Unknown ALPN name: %.*s\n", (int)len, data + pos + 1); | |
277 | + } | |
278 | + pos += 1 + len; | |
279 | + } | |
280 | + /* Check we ended where we expected to */ | |
281 | + if (pos != data_len) | |
282 | + return -5; | |
283 | + | |
284 | + return -2; | |
285 | +} | |
286 | + | |
287 | +static int | |
288 | +has_match(char** list, const char* name, size_t name_len) { | |
289 | + char **item; | |
290 | + | |
291 | + for (item = list; *item; item++) { | |
292 | + if (verbose) fprintf(stderr, "matching [%.*s] with [%s]\n", (int)name_len, name, *item); | |
293 | + if(!strncmp(*item, name, name_len)) { | |
294 | + return 1; | |
295 | + } | |
296 | + } | |
297 | + return 0; | |
298 | +} | |
299 | + | |
300 | +struct TLSProtocol * | |
301 | +new_tls_data() { | |
302 | + struct TLSProtocol *tls_data = malloc(sizeof(struct TLSProtocol)); | |
303 | + if (tls_data != NULL) { | |
304 | + tls_data->use_alpn = -1; | |
305 | + } | |
306 | + | |
307 | + return tls_data; | |
308 | +} | |
309 | + | |
310 | +struct TLSProtocol * | |
311 | +tls_data_set_list(struct TLSProtocol *tls_data, int alpn, char** list) { | |
312 | + if (alpn) { | |
313 | + tls_data->alpn_protocol_list = list; | |
314 | + if(tls_data->use_alpn == 0) | |
315 | + tls_data->use_alpn = 2; | |
316 | + else | |
317 | + tls_data->use_alpn = 1; | |
318 | + } else { | |
319 | + tls_data->sni_hostname_list = list; | |
320 | + if(tls_data->use_alpn == 1) | |
321 | + tls_data->use_alpn = 2; | |
322 | + else | |
323 | + tls_data->use_alpn = 0; | |
324 | + } | |
325 | + | |
326 | + return tls_data; | |
327 | +} |
tls.h | ||
---|---|---|
@@ -27,7 +27,12 @@ | ||
27 | 27 | |
28 | 28 | |
29 | 29 | |
30 | 30 | |
31 | -int parse_tls_header(const char *data, size_t data_len, char **hostname); | |
31 | +struct TLSProtocol; | |
32 | 32 | |
33 | +int parse_tls_header(const struct TLSProtocol *tls_data, const char *data, size_t data_len); | |
34 | + | |
35 | +struct TLSProtocol *new_tls_data(); | |
36 | +struct TLSProtocol *tls_data_set_list(struct TLSProtocol *, int, char**); | |
37 | + | |
33 | 38 |
Built with git-ssb-web