comparison src/stream/ngx_stream_ssl_preread_module.c @ 7227:79eb4f7b6725

Stream ssl_preread: $ssl_preread_alpn_protocols variable. The variable keeps a comma-separated list of protocol names from ALPN TLS extension defined by RFC 7301.
author Roman Arutyunyan <arut@nginx.com>
date Mon, 12 Mar 2018 16:03:08 +0300
parents 2a288909abc6
children 0f811890f2f0
comparison
equal deleted inserted replaced
7226:0b1eb40de6da 7227:79eb4f7b6725
15 15
16 16
17 typedef struct { 17 typedef struct {
18 size_t left; 18 size_t left;
19 size_t size; 19 size_t size;
20 size_t ext;
20 u_char *pos; 21 u_char *pos;
21 u_char *dst; 22 u_char *dst;
22 u_char buf[4]; 23 u_char buf[4];
23 ngx_str_t host; 24 ngx_str_t host;
25 ngx_str_t alpn;
24 ngx_log_t *log; 26 ngx_log_t *log;
25 ngx_pool_t *pool; 27 ngx_pool_t *pool;
26 ngx_uint_t state; 28 ngx_uint_t state;
27 } ngx_stream_ssl_preread_ctx_t; 29 } ngx_stream_ssl_preread_ctx_t;
28 30
29 31
30 static ngx_int_t ngx_stream_ssl_preread_handler(ngx_stream_session_t *s); 32 static ngx_int_t ngx_stream_ssl_preread_handler(ngx_stream_session_t *s);
31 static ngx_int_t ngx_stream_ssl_preread_parse_record( 33 static ngx_int_t ngx_stream_ssl_preread_parse_record(
32 ngx_stream_ssl_preread_ctx_t *ctx, u_char *pos, u_char *last); 34 ngx_stream_ssl_preread_ctx_t *ctx, u_char *pos, u_char *last);
33 static ngx_int_t ngx_stream_ssl_preread_server_name_variable( 35 static ngx_int_t ngx_stream_ssl_preread_server_name_variable(
36 ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data);
37 static ngx_int_t ngx_stream_ssl_preread_alpn_protocols_variable(
34 ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data); 38 ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data);
35 static ngx_int_t ngx_stream_ssl_preread_add_variables(ngx_conf_t *cf); 39 static ngx_int_t ngx_stream_ssl_preread_add_variables(ngx_conf_t *cf);
36 static void *ngx_stream_ssl_preread_create_srv_conf(ngx_conf_t *cf); 40 static void *ngx_stream_ssl_preread_create_srv_conf(ngx_conf_t *cf);
37 static char *ngx_stream_ssl_preread_merge_srv_conf(ngx_conf_t *cf, void *parent, 41 static char *ngx_stream_ssl_preread_merge_srv_conf(ngx_conf_t *cf, void *parent,
38 void *child); 42 void *child);
83 static ngx_stream_variable_t ngx_stream_ssl_preread_vars[] = { 87 static ngx_stream_variable_t ngx_stream_ssl_preread_vars[] = {
84 88
85 { ngx_string("ssl_preread_server_name"), NULL, 89 { ngx_string("ssl_preread_server_name"), NULL,
86 ngx_stream_ssl_preread_server_name_variable, 0, 0, 0 }, 90 ngx_stream_ssl_preread_server_name_variable, 0, 0, 0 },
87 91
92 { ngx_string("ssl_preread_alpn_protocols"), NULL,
93 ngx_stream_ssl_preread_alpn_protocols_variable, 0, 0, 0 },
94
88 ngx_stream_null_variable 95 ngx_stream_null_variable
89 }; 96 };
90 97
91 98
92 static ngx_int_t 99 static ngx_int_t
137 while (last - p >= 5) { 144 while (last - p >= 5) {
138 145
139 if (p[0] != 0x16) { 146 if (p[0] != 0x16) {
140 ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0, 147 ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0,
141 "ssl preread: not a handshake"); 148 "ssl preread: not a handshake");
149 ngx_stream_set_ctx(s, NULL, ngx_stream_ssl_preread_module);
142 return NGX_DECLINED; 150 return NGX_DECLINED;
143 } 151 }
144 152
145 if (p[1] != 3) { 153 if (p[1] != 3) {
146 ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0, 154 ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0,
147 "ssl preread: unsupported SSL version"); 155 "ssl preread: unsupported SSL version");
156 ngx_stream_set_ctx(s, NULL, ngx_stream_ssl_preread_module);
148 return NGX_DECLINED; 157 return NGX_DECLINED;
149 } 158 }
150 159
151 len = (p[3] << 8) + p[4]; 160 len = (p[3] << 8) + p[4];
152 161
156 } 165 }
157 166
158 p += 5; 167 p += 5;
159 168
160 rc = ngx_stream_ssl_preread_parse_record(ctx, p, p + len); 169 rc = ngx_stream_ssl_preread_parse_record(ctx, p, p + len);
170
171 if (rc == NGX_DECLINED) {
172 ngx_stream_set_ctx(s, NULL, ngx_stream_ssl_preread_module);
173 return NGX_DECLINED;
174 }
175
161 if (rc != NGX_AGAIN) { 176 if (rc != NGX_AGAIN) {
162 return rc; 177 return rc;
163 } 178 }
164 179
165 p += len; 180 p += len;
173 188
174 static ngx_int_t 189 static ngx_int_t
175 ngx_stream_ssl_preread_parse_record(ngx_stream_ssl_preread_ctx_t *ctx, 190 ngx_stream_ssl_preread_parse_record(ngx_stream_ssl_preread_ctx_t *ctx,
176 u_char *pos, u_char *last) 191 u_char *pos, u_char *last)
177 { 192 {
178 size_t left, n, size; 193 size_t left, n, size, ext;
179 u_char *dst, *p; 194 u_char *dst, *p;
180 195
181 enum { 196 enum {
182 sw_start = 0, 197 sw_start = 0,
183 sw_header, /* handshake msg_type, length */ 198 sw_header, /* handshake msg_type, length */
190 sw_cm, /* compression_methods */ 205 sw_cm, /* compression_methods */
191 sw_ext, /* extension */ 206 sw_ext, /* extension */
192 sw_ext_header, /* extension_type, extension_data length */ 207 sw_ext_header, /* extension_type, extension_data length */
193 sw_sni_len, /* SNI length */ 208 sw_sni_len, /* SNI length */
194 sw_sni_host_head, /* SNI name_type, host_name length */ 209 sw_sni_host_head, /* SNI name_type, host_name length */
195 sw_sni_host /* SNI host_name */ 210 sw_sni_host, /* SNI host_name */
211 sw_alpn_len, /* ALPN length */
212 sw_alpn_proto_len, /* ALPN protocol_name length */
213 sw_alpn_proto_data /* ALPN protocol_name */
196 } state; 214 } state;
197 215
198 ngx_log_debug2(NGX_LOG_DEBUG_STREAM, ctx->log, 0, 216 ngx_log_debug2(NGX_LOG_DEBUG_STREAM, ctx->log, 0,
199 "ssl preread: state %ui left %z", ctx->state, ctx->left); 217 "ssl preread: state %ui left %z", ctx->state, ctx->left);
200 218
201 state = ctx->state; 219 state = ctx->state;
202 size = ctx->size; 220 size = ctx->size;
203 left = ctx->left; 221 left = ctx->left;
222 ext = ctx->ext;
204 dst = ctx->dst; 223 dst = ctx->dst;
205 p = ctx->buf; 224 p = ctx->buf;
206 225
207 for ( ;; ) { 226 for ( ;; ) {
208 n = ngx_min((size_t) (last - pos), size); 227 n = ngx_min((size_t) (last - pos), size);
297 dst = p; 316 dst = p;
298 size = 4; 317 size = 4;
299 break; 318 break;
300 319
301 case sw_ext_header: 320 case sw_ext_header:
302 if (p[0] == 0 && p[1] == 0) { 321 if (p[0] == 0 && p[1] == 0 && ctx->host.data == NULL) {
303 /* SNI extension */ 322 /* SNI extension */
304 state = sw_sni_len; 323 state = sw_sni_len;
305 dst = NULL; 324 dst = p;
306 size = 2; 325 size = 2;
307 break; 326 break;
308 } 327 }
309 328
329 if (p[0] == 0 && p[1] == 16 && ctx->alpn.data == NULL) {
330 /* ALPN extension */
331 state = sw_alpn_len;
332 dst = p;
333 size = 2;
334 break;
335 }
336
310 state = sw_ext; 337 state = sw_ext;
311 dst = NULL; 338 dst = NULL;
312 size = (p[2] << 8) + p[3]; 339 size = (p[2] << 8) + p[3];
313 break; 340 break;
314 341
315 case sw_sni_len: 342 case sw_sni_len:
343 ext = (p[0] << 8) + p[1];
316 state = sw_sni_host_head; 344 state = sw_sni_host_head;
317 dst = p; 345 dst = p;
318 size = 3; 346 size = 3;
319 break; 347 break;
320 348
323 ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0, 351 ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0,
324 "ssl preread: SNI hostname type is not DNS"); 352 "ssl preread: SNI hostname type is not DNS");
325 return NGX_DECLINED; 353 return NGX_DECLINED;
326 } 354 }
327 355
328 state = sw_sni_host;
329 size = (p[1] << 8) + p[2]; 356 size = (p[1] << 8) + p[2];
357
358 if (ext < 3 + size) {
359 ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0,
360 "ssl preread: SNI format error");
361 return NGX_DECLINED;
362 }
363 ext -= 3 + size;
330 364
331 ctx->host.data = ngx_pnalloc(ctx->pool, size); 365 ctx->host.data = ngx_pnalloc(ctx->pool, size);
332 if (ctx->host.data == NULL) { 366 if (ctx->host.data == NULL) {
333 return NGX_ERROR; 367 return NGX_ERROR;
334 } 368 }
335 369
370 state = sw_sni_host;
336 dst = ctx->host.data; 371 dst = ctx->host.data;
337 break; 372 break;
338 373
339 case sw_sni_host: 374 case sw_sni_host:
340 ctx->host.len = (p[1] << 8) + p[2]; 375 ctx->host.len = (p[1] << 8) + p[2];
341 376
342 ngx_log_debug1(NGX_LOG_DEBUG_STREAM, ctx->log, 0, 377 ngx_log_debug1(NGX_LOG_DEBUG_STREAM, ctx->log, 0,
343 "ssl preread: SNI hostname \"%V\"", &ctx->host); 378 "ssl preread: SNI hostname \"%V\"", &ctx->host);
344 return NGX_OK; 379
380 state = sw_ext;
381 dst = NULL;
382 size = ext;
383 break;
384
385 case sw_alpn_len:
386 ext = (p[0] << 8) + p[1];
387
388 ctx->alpn.data = ngx_pnalloc(ctx->pool, ext);
389 if (ctx->alpn.data == NULL) {
390 return NGX_ERROR;
391 }
392
393 state = sw_alpn_proto_len;
394 dst = p;
395 size = 1;
396 break;
397
398 case sw_alpn_proto_len:
399 size = p[0];
400
401 if (size == 0) {
402 ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0,
403 "ssl preread: ALPN empty protocol");
404 return NGX_DECLINED;
405 }
406
407 if (ext < 1 + size) {
408 ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0,
409 "ssl preread: ALPN format error");
410 return NGX_DECLINED;
411 }
412 ext -= 1 + size;
413
414 state = sw_alpn_proto_data;
415 dst = ctx->alpn.data + ctx->alpn.len;
416 break;
417
418 case sw_alpn_proto_data:
419 ctx->alpn.len += p[0];
420
421 ngx_log_debug1(NGX_LOG_DEBUG_STREAM, ctx->log, 0,
422 "ssl preread: ALPN protocols \"%V\"", &ctx->alpn);
423
424 if (ext) {
425 ctx->alpn.data[ctx->alpn.len++] = ',';
426
427 state = sw_alpn_proto_len;
428 dst = p;
429 size = 1;
430 break;
431 }
432
433 state = sw_ext;
434 dst = NULL;
435 size = 0;
436 break;
345 } 437 }
346 438
347 if (left < size) { 439 if (left < size) {
348 ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0, 440 ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0,
349 "ssl preread: failed to parse handshake"); 441 "ssl preread: failed to parse handshake");
352 } 444 }
353 445
354 ctx->state = state; 446 ctx->state = state;
355 ctx->size = size; 447 ctx->size = size;
356 ctx->left = left; 448 ctx->left = left;
449 ctx->ext = ext;
357 ctx->dst = dst; 450 ctx->dst = dst;
358 451
359 return NGX_AGAIN; 452 return NGX_AGAIN;
360 } 453 }
361 454
382 return NGX_OK; 475 return NGX_OK;
383 } 476 }
384 477
385 478
386 static ngx_int_t 479 static ngx_int_t
480 ngx_stream_ssl_preread_alpn_protocols_variable(ngx_stream_session_t *s,
481 ngx_variable_value_t *v, uintptr_t data)
482 {
483 ngx_stream_ssl_preread_ctx_t *ctx;
484
485 ctx = ngx_stream_get_module_ctx(s, ngx_stream_ssl_preread_module);
486
487 if (ctx == NULL) {
488 v->not_found = 1;
489 return NGX_OK;
490 }
491
492 v->valid = 1;
493 v->no_cacheable = 0;
494 v->not_found = 0;
495 v->len = ctx->alpn.len;
496 v->data = ctx->alpn.data;
497
498 return NGX_OK;
499 }
500
501
502 static ngx_int_t
387 ngx_stream_ssl_preread_add_variables(ngx_conf_t *cf) 503 ngx_stream_ssl_preread_add_variables(ngx_conf_t *cf)
388 { 504 {
389 ngx_stream_variable_t *var, *v; 505 ngx_stream_variable_t *var, *v;
390 506
391 for (v = ngx_stream_ssl_preread_vars; v->name.len; v++) { 507 for (v = ngx_stream_ssl_preread_vars; v->name.len; v++) {