| From 5ebeb8b5d90f9f47418b6b8d898ace8f1b4d4104 Mon Sep 17 00:00:00 2001 |
| From: Adam Langley <[email protected]> |
| Date: Mon, 15 Apr 2013 18:07:47 -0400 |
| |
| This change adds support for ALPN[1] in OpenSSL. ALPN is the IETF |
| blessed version of NPN and we'll be supporting both ALPN and NPN for |
| some time yet. |
| |
| [1] https://tools.ietf.org/html/draft-ietf-tls-applayerprotoneg-00 |
| --- |
| apps/s_client.c | 40 +++++++++++++- |
| ssl/s3_lib.c | 13 +++++ |
| ssl/ssl.h | 45 +++++++++++++++ |
| ssl/ssl3.h | 10 ++++ |
| ssl/ssl_lib.c | 87 +++++++++++++++++++++++++++++ |
| ssl/t1_lib.c | 167 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- |
| ssl/tls1.h | 3 + |
| 7 files changed, 362 insertions(+), 3 deletions(-) |
| |
| diff --git a/apps/s_client.c b/apps/s_client.c |
| index 791e277..cb1efcd 100644 |
| --- a/apps/s_client.c |
| +++ b/apps/s_client.c |
| @@ -359,6 +359,7 @@ static void sc_usage(void) |
| BIO_printf(bio_err," -no_ticket - disable use of RFC4507bis session tickets\n"); |
| # ifndef OPENSSL_NO_NEXTPROTONEG |
| BIO_printf(bio_err," -nextprotoneg arg - enable NPN extension, considering named protocols supported (comma-separated list)\n"); |
| + BIO_printf(bio_err," -alpn arg - enable ALPN extension, considering named protocols supported (comma-separated list)\n"); |
| # endif |
| #endif |
| BIO_printf(bio_err," -cutthrough - enable 1-RTT full-handshake for strong ciphers\n"); |
| @@ -611,6 +612,7 @@ int MAIN(int argc, char **argv) |
| {NULL,0}; |
| # ifndef OPENSSL_NO_NEXTPROTONEG |
| const char *next_proto_neg_in = NULL; |
| + const char *alpn_in = NULL; |
| # endif |
| #endif |
| char *sess_in = NULL; |
| @@ -883,6 +885,11 @@ int MAIN(int argc, char **argv) |
| if (--argc < 1) goto bad; |
| next_proto_neg_in = *(++argv); |
| } |
| + else if (strcmp(*argv,"-alpn") == 0) |
| + { |
| + if (--argc < 1) goto bad; |
| + alpn_in = *(++argv); |
| + } |
| # endif |
| #endif |
| else if (strcmp(*argv,"-cutthrough") == 0) |
| @@ -1157,9 +1164,23 @@ bad: |
| */ |
| if (socket_type == SOCK_DGRAM) SSL_CTX_set_read_ahead(ctx, 1); |
| |
| -#if !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG) |
| +#if !defined(OPENSSL_NO_TLSEXT) |
| +# if !defined(OPENSSL_NO_NEXTPROTONEG) |
| if (next_proto.data) |
| SSL_CTX_set_next_proto_select_cb(ctx, next_proto_cb, &next_proto); |
| +# endif |
| + if (alpn_in) |
| + { |
| + unsigned short alpn_len; |
| + unsigned char *alpn = next_protos_parse(&alpn_len, alpn_in); |
| + |
| + if (alpn == NULL) |
| + { |
| + BIO_printf(bio_err, "Error parsing -alpn argument\n"); |
| + goto end; |
| + } |
| + SSL_CTX_set_alpn_protos(ctx, alpn, alpn_len); |
| + } |
| #endif |
| |
| /* Enable handshake cutthrough for client connections using |
| @@ -2077,7 +2098,8 @@ static void print_stuff(BIO *bio, SSL *s, int full) |
| } |
| #endif |
| |
| -#if !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG) |
| +#if !defined(OPENSSL_NO_TLSEXT) |
| +# if !defined(OPENSSL_NO_NEXTPROTONEG) |
| if (next_proto.status != -1) { |
| const unsigned char *proto; |
| unsigned int proto_len; |
| @@ -2086,6 +2108,20 @@ static void print_stuff(BIO *bio, SSL *s, int full) |
| BIO_write(bio, proto, proto_len); |
| BIO_write(bio, "\n", 1); |
| } |
| + { |
| + const unsigned char *proto; |
| + unsigned int proto_len; |
| + SSL_get0_alpn_selected(s, &proto, &proto_len); |
| + if (proto_len > 0) |
| + { |
| + BIO_printf(bio, "ALPN protocol: "); |
| + BIO_write(bio, proto, proto_len); |
| + BIO_write(bio, "\n", 1); |
| + } |
| + else |
| + BIO_printf(bio, "No ALPN negotiated\n"); |
| + } |
| +# endif |
| #endif |
| |
| #ifndef OPENSSL_NO_SRTP |
| diff --git a/ssl/s3_lib.c b/ssl/s3_lib.c |
| index 5e46393..2cd1654 100644 |
| --- a/ssl/s3_lib.c |
| +++ b/ssl/s3_lib.c |
| @@ -2996,6 +2996,11 @@ void ssl3_free(SSL *s) |
| BIO_free(s->s3->handshake_buffer); |
| } |
| if (s->s3->handshake_dgst) ssl3_free_digest_list(s); |
| +#ifndef OPENSSL_NO_TLSEXT |
| + if (s->s3->alpn_selected) |
| + OPENSSL_free(s->s3->alpn_selected); |
| +#endif |
| + |
| #ifndef OPENSSL_NO_SRP |
| SSL_SRP_CTX_free(s); |
| #endif |
| @@ -3055,6 +3060,14 @@ void ssl3_clear(SSL *s) |
| if (s->s3->handshake_dgst) { |
| ssl3_free_digest_list(s); |
| } |
| + |
| +#if !defined(OPENSSL_NO_TLSEXT) |
| + if (s->s3->alpn_selected) |
| + { |
| + free(s->s3->alpn_selected); |
| + s->s3->alpn_selected = NULL; |
| + } |
| +#endif |
| memset(s->s3,0,sizeof *s->s3); |
| s->s3->rbuf.buf = rp; |
| s->s3->wbuf.buf = wp; |
| diff --git a/ssl/ssl.h b/ssl/ssl.h |
| index e8c73fa..612c7aa 100644 |
| --- a/ssl/ssl.h |
| +++ b/ssl/ssl.h |
| @@ -1019,6 +1019,31 @@ struct ssl_ctx_st |
| void *arg); |
| void *next_proto_select_cb_arg; |
| # endif |
| + |
| + /* ALPN information |
| + * (we are in the process of transitioning from NPN to ALPN.) */ |
| + |
| + /* For a server, this contains a callback function that allows the |
| + * server to select the protocol for the connection. |
| + * out: on successful return, this must point to the raw protocol |
| + * name (without the length prefix). |
| + * outlen: on successful return, this contains the length of |*out|. |
| + * in: points to the client's list of supported protocols in |
| + * wire-format. |
| + * inlen: the length of |in|. */ |
| + int (*alpn_select_cb)(SSL *s, |
| + const unsigned char **out, |
| + unsigned char *outlen, |
| + const unsigned char* in, |
| + unsigned int inlen, |
| + void *arg); |
| + void *alpn_select_cb_arg; |
| + |
| + /* For a client, this contains the list of supported protocols in wire |
| + * format. */ |
| + unsigned char* alpn_client_proto_list; |
| + unsigned alpn_client_proto_list_len; |
| + |
| /* SRTP profiles we are willing to do from RFC 5764 */ |
| STACK_OF(SRTP_PROTECTION_PROFILE) *srtp_profiles; |
| |
| @@ -1120,6 +1145,21 @@ void SSL_get0_next_proto_negotiated(const SSL *s, |
| #define OPENSSL_NPN_NO_OVERLAP 2 |
| #endif |
| |
| +int SSL_CTX_set_alpn_protos(SSL_CTX *ctx, const unsigned char* protos, |
| + unsigned protos_len); |
| +int SSL_set_alpn_protos(SSL *ssl, const unsigned char* protos, |
| + unsigned protos_len); |
| +void SSL_CTX_set_alpn_select_cb(SSL_CTX* ctx, |
| + int (*cb) (SSL *ssl, |
| + const unsigned char **out, |
| + unsigned char *outlen, |
| + const unsigned char *in, |
| + unsigned int inlen, |
| + void *arg), |
| + void *arg); |
| +void SSL_get0_alpn_selected(const SSL *ssl, const unsigned char **data, |
| + unsigned *len); |
| + |
| #ifndef OPENSSL_NO_PSK |
| /* the maximum length of the buffer given to callbacks containing the |
| * resulting identity/psk */ |
| @@ -1422,6 +1462,11 @@ struct ssl_st |
| char tlsext_channel_id_enabled; |
| /* The client's Channel ID private key. */ |
| EVP_PKEY *tlsext_channel_id_private; |
| + |
| + /* For a client, this contains the list of supported protocols in wire |
| + * format. */ |
| + unsigned char* alpn_client_proto_list; |
| + unsigned alpn_client_proto_list_len; |
| #else |
| #define session_ctx ctx |
| #endif /* OPENSSL_NO_TLSEXT */ |
| diff --git a/ssl/ssl3.h b/ssl/ssl3.h |
| index 3229995..28c46d5 100644 |
| --- a/ssl/ssl3.h |
| +++ b/ssl/ssl3.h |
| @@ -551,6 +551,16 @@ typedef struct ssl3_state_st |
| * each are big-endian values. */ |
| unsigned char tlsext_channel_id[64]; |
| |
| + /* ALPN information |
| + * (we are in the process of transitioning from NPN to ALPN.) */ |
| + |
| + /* In a server these point to the selected ALPN protocol after the |
| + * ClientHello has been processed. In a client these contain the |
| + * protocol that the server selected once the ServerHello has been |
| + * processed. */ |
| + unsigned char *alpn_selected; |
| + unsigned alpn_selected_len; |
| + |
| /* These point to the digest function to use for signatures made with |
| * each type of public key. A NULL value indicates that the default |
| * digest should be used, which is SHA1 as of TLS 1.2. |
| diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c |
| index e360550..b472423 100644 |
| --- a/ssl/ssl_lib.c |
| +++ b/ssl/ssl_lib.c |
| @@ -359,6 +359,17 @@ SSL *SSL_new(SSL_CTX *ctx) |
| # ifndef OPENSSL_NO_NEXTPROTONEG |
| s->next_proto_negotiated = NULL; |
| # endif |
| + |
| + if (s->ctx->alpn_client_proto_list) |
| + { |
| + s->alpn_client_proto_list = |
| + OPENSSL_malloc(s->ctx->alpn_client_proto_list_len); |
| + if (s->alpn_client_proto_list == NULL) |
| + goto err; |
| + memcpy(s->alpn_client_proto_list, s->ctx->alpn_client_proto_list, |
| + s->ctx->alpn_client_proto_list_len); |
| + s->alpn_client_proto_list_len = s->ctx->alpn_client_proto_list_len; |
| + } |
| #endif |
| |
| s->verify_result=X509_V_OK; |
| @@ -564,6 +575,8 @@ void SSL_free(SSL *s) |
| OPENSSL_free(s->tlsext_ocsp_resp); |
| if (s->tlsext_channel_id_private) |
| EVP_PKEY_free(s->tlsext_channel_id_private); |
| + if (s->alpn_client_proto_list) |
| + OPENSSL_free(s->alpn_client_proto_list); |
| #endif |
| |
| if (s->client_CA != NULL) |
| @@ -1615,6 +1628,78 @@ void SSL_CTX_set_next_proto_select_cb(SSL_CTX *ctx, int (*cb) (SSL *s, unsigned |
| ctx->next_proto_select_cb_arg = arg; |
| } |
| # endif |
| + |
| +/* SSL_CTX_set_alpn_protos sets the ALPN protocol list on |ctx| to |protos|. |
| + * |protos| must be in wire-format (i.e. a series of non-empty, 8-bit |
| + * length-prefixed strings). |
| + * |
| + * Returns 0 on success. */ |
| +int SSL_CTX_set_alpn_protos(SSL_CTX *ctx, const unsigned char* protos, |
| + unsigned protos_len) |
| + { |
| + if (ctx->alpn_client_proto_list) |
| + OPENSSL_free(ctx->alpn_client_proto_list); |
| + |
| + ctx->alpn_client_proto_list = OPENSSL_malloc(protos_len); |
| + if (!ctx->alpn_client_proto_list) |
| + return 1; |
| + memcpy(ctx->alpn_client_proto_list, protos, protos_len); |
| + ctx->alpn_client_proto_list_len = protos_len; |
| + |
| + return 0; |
| + } |
| + |
| +/* SSL_set_alpn_protos sets the ALPN protocol list on |ssl| to |protos|. |
| + * |protos| must be in wire-format (i.e. a series of non-empty, 8-bit |
| + * length-prefixed strings). |
| + * |
| + * Returns 0 on success. */ |
| +int SSL_set_alpn_protos(SSL *ssl, const unsigned char* protos, |
| + unsigned protos_len) |
| + { |
| + if (ssl->alpn_client_proto_list) |
| + OPENSSL_free(ssl->alpn_client_proto_list); |
| + |
| + ssl->alpn_client_proto_list = OPENSSL_malloc(protos_len); |
| + if (!ssl->alpn_client_proto_list) |
| + return 1; |
| + memcpy(ssl->alpn_client_proto_list, protos, protos_len); |
| + ssl->alpn_client_proto_list_len = protos_len; |
| + |
| + return 0; |
| + } |
| + |
| +/* SSL_CTX_set_alpn_select_cb sets a callback function on |ctx| that is called |
| + * during ClientHello processing in order to select an ALPN protocol from the |
| + * client's list of offered protocols. */ |
| +void SSL_CTX_set_alpn_select_cb(SSL_CTX* ctx, |
| + int (*cb) (SSL *ssl, |
| + const unsigned char **out, |
| + unsigned char *outlen, |
| + const unsigned char *in, |
| + unsigned int inlen, |
| + void *arg), |
| + void *arg) |
| + { |
| + ctx->alpn_select_cb = cb; |
| + ctx->alpn_select_cb_arg = arg; |
| + } |
| + |
| +/* SSL_get0_alpn_selected gets the selected ALPN protocol (if any) from |ssl|. |
| + * On return it sets |*data| to point to |*len| bytes of protocol name (not |
| + * including the leading length-prefix byte). If the server didn't respond with |
| + * a negotiated protocol then |*len| will be zero. */ |
| +void SSL_get0_alpn_selected(const SSL *ssl, const unsigned char **data, |
| + unsigned *len) |
| + { |
| + *data = NULL; |
| + if (ssl->s3) |
| + *data = ssl->s3->alpn_selected; |
| + if (*data == NULL) |
| + *len = 0; |
| + else |
| + *len = ssl->s3->alpn_selected_len; |
| + } |
| #endif |
| |
| int SSL_export_keying_material(SSL *s, unsigned char *out, size_t olen, |
| @@ -1955,6 +2040,8 @@ void SSL_CTX_free(SSL_CTX *a) |
| #ifndef OPENSSL_NO_TLSEXT |
| if (a->tlsext_channel_id_private) |
| EVP_PKEY_free(a->tlsext_channel_id_private); |
| + if (a->alpn_client_proto_list != NULL) |
| + OPENSSL_free(a->alpn_client_proto_list); |
| #endif |
| |
| OPENSSL_free(a); |
| diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c |
| index 1f93a6f..b2e049a 100644 |
| --- a/ssl/t1_lib.c |
| +++ b/ssl/t1_lib.c |
| @@ -659,6 +659,18 @@ unsigned char *ssl_add_clienthello_tlsext(SSL *s, unsigned char *p, unsigned cha |
| s2n(0,ret); |
| } |
| |
| + if (s->alpn_client_proto_list && !s->s3->tmp.finish_md_len) |
| + { |
| + if ((size_t)(limit - ret) < 6 + s->alpn_client_proto_list_len) |
| + return NULL; |
| + s2n(TLSEXT_TYPE_application_layer_protocol_negotiation,ret); |
| + s2n(2 + s->alpn_client_proto_list_len,ret); |
| + s2n(s->alpn_client_proto_list_len,ret); |
| + memcpy(ret, s->alpn_client_proto_list, |
| + s->alpn_client_proto_list_len); |
| + ret += s->alpn_client_proto_list_len; |
| + } |
| + |
| #ifndef OPENSSL_NO_SRTP |
| if(SSL_get_srtp_profiles(s)) |
| { |
| @@ -879,6 +891,21 @@ unsigned char *ssl_add_serverhello_tlsext(SSL *s, unsigned char *p, unsigned cha |
| s2n(0,ret); |
| } |
| |
| + if (s->s3->alpn_selected) |
| + { |
| + const unsigned char *selected = s->s3->alpn_selected; |
| + unsigned len = s->s3->alpn_selected_len; |
| + |
| + if ((long)(limit - ret - 4 - 2 - 1 - len) < 0) |
| + return NULL; |
| + s2n(TLSEXT_TYPE_application_layer_protocol_negotiation,ret); |
| + s2n(3 + len,ret); |
| + s2n(1 + len,ret); |
| + *ret++ = len; |
| + memcpy(ret, selected, len); |
| + ret += len; |
| + } |
| + |
| if ((extdatalen = ret-p-2)== 0) |
| return p; |
| |
| @@ -966,6 +993,76 @@ static void ssl_check_for_safari(SSL *s, const unsigned char *data, const unsign |
| s->is_probably_safari = 1; |
| } |
| |
| +/* tls1_alpn_handle_client_hello is called to process the ALPN extension in a |
| + * ClientHello. |
| + * data: the contents of the extension, not including the type and length. |
| + * data_len: the number of bytes in |data| |
| + * al: a pointer to the alert value to send in the event of a non-zero |
| + * return. |
| + * |
| + * returns: 0 on success. */ |
| +static int tls1_alpn_handle_client_hello(SSL *s, const unsigned char *data, |
| + unsigned data_len, int *al) |
| + { |
| + unsigned i; |
| + unsigned proto_len; |
| + const unsigned char *selected; |
| + unsigned char selected_len; |
| + int r; |
| + |
| + if (s->ctx->alpn_select_cb == NULL) |
| + return 0; |
| + |
| + if (data_len < 2) |
| + goto parse_error; |
| + |
| + /* data should contain a uint16 length followed by a series of 8-bit, |
| + * length-prefixed strings. */ |
| + i = ((unsigned) data[0]) << 8 | |
| + ((unsigned) data[1]); |
| + data_len -= 2; |
| + data += 2; |
| + if (data_len != i) |
| + goto parse_error; |
| + |
| + if (data_len < 2) |
| + goto parse_error; |
| + |
| + for (i = 0; i < data_len;) |
| + { |
| + proto_len = data[i]; |
| + i++; |
| + |
| + if (proto_len == 0) |
| + goto parse_error; |
| + |
| + if (i + proto_len < i || i + proto_len > data_len) |
| + goto parse_error; |
| + |
| + i += proto_len; |
| + } |
| + |
| + r = s->ctx->alpn_select_cb(s, &selected, &selected_len, data, data_len, |
| + s->ctx->alpn_select_cb_arg); |
| + if (r == SSL_TLSEXT_ERR_OK) { |
| + if (s->s3->alpn_selected) |
| + OPENSSL_free(s->s3->alpn_selected); |
| + s->s3->alpn_selected = OPENSSL_malloc(selected_len); |
| + if (!s->s3->alpn_selected) |
| + { |
| + *al = SSL_AD_INTERNAL_ERROR; |
| + return -1; |
| + } |
| + memcpy(s->s3->alpn_selected, selected, selected_len); |
| + s->s3->alpn_selected_len = selected_len; |
| + } |
| + return 0; |
| + |
| +parse_error: |
| + *al = SSL_AD_DECODE_ERROR; |
| + return -1; |
| + } |
| + |
| int ssl_parse_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char *d, int n, int *al) |
| { |
| unsigned short type; |
| @@ -988,6 +1085,12 @@ int ssl_parse_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char *d, in |
| s->s3->next_proto_neg_seen = 0; |
| #endif |
| |
| + if (s->s3->alpn_selected) |
| + { |
| + OPENSSL_free(s->s3->alpn_selected); |
| + s->s3->alpn_selected = NULL; |
| + } |
| + |
| #ifndef OPENSSL_NO_HEARTBEATS |
| s->tlsext_heartbeat &= ~(SSL_TLSEXT_HB_ENABLED | |
| SSL_TLSEXT_HB_DONT_SEND_REQUESTS); |
| @@ -1420,7 +1523,8 @@ int ssl_parse_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char *d, in |
| #endif |
| #ifndef OPENSSL_NO_NEXTPROTONEG |
| else if (type == TLSEXT_TYPE_next_proto_neg && |
| - s->s3->tmp.finish_md_len == 0) |
| + s->s3->tmp.finish_md_len == 0 && |
| + s->s3->alpn_selected == NULL) |
| { |
| /* We shouldn't accept this extension on a |
| * renegotiation. |
| @@ -1444,6 +1548,16 @@ int ssl_parse_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char *d, in |
| else if (type == TLSEXT_TYPE_channel_id && s->tlsext_channel_id_enabled) |
| s->s3->tlsext_channel_id_valid = 1; |
| |
| + else if (type == TLSEXT_TYPE_application_layer_protocol_negotiation && |
| + s->ctx->alpn_select_cb && |
| + s->s3->tmp.finish_md_len == 0) |
| + { |
| + if (tls1_alpn_handle_client_hello(s, data, size, al) != 0) |
| + return 0; |
| + /* ALPN takes precedence over NPN. */ |
| + s->s3->next_proto_neg_seen = 0; |
| + } |
| + |
| /* session ticket processed earlier */ |
| #ifndef OPENSSL_NO_SRTP |
| else if (type == TLSEXT_TYPE_use_srtp) |
| @@ -1508,6 +1622,12 @@ int ssl_parse_serverhello_tlsext(SSL *s, unsigned char **p, unsigned char *d, in |
| s->s3->next_proto_neg_seen = 0; |
| #endif |
| |
| + if (s->s3->alpn_selected) |
| + { |
| + OPENSSL_free(s->s3->alpn_selected); |
| + s->s3->alpn_selected = NULL; |
| + } |
| + |
| #ifndef OPENSSL_NO_HEARTBEATS |
| s->tlsext_heartbeat &= ~(SSL_TLSEXT_HB_ENABLED | |
| SSL_TLSEXT_HB_DONT_SEND_REQUESTS); |
| @@ -1677,6 +1797,51 @@ int ssl_parse_serverhello_tlsext(SSL *s, unsigned char **p, unsigned char *d, in |
| else if (type == TLSEXT_TYPE_channel_id) |
| s->s3->tlsext_channel_id_valid = 1; |
| |
| + else if (type == TLSEXT_TYPE_application_layer_protocol_negotiation) |
| + { |
| + unsigned len; |
| + |
| + /* We must have requested it. */ |
| + if (s->alpn_client_proto_list == NULL) |
| + { |
| + *al = TLS1_AD_UNSUPPORTED_EXTENSION; |
| + return 0; |
| + } |
| + if (size < 4) |
| + { |
| + *al = TLS1_AD_DECODE_ERROR; |
| + return 0; |
| + } |
| + /* The extension data consists of: |
| + * uint16 list_length |
| + * uint8 proto_length; |
| + * uint8 proto[proto_length]; */ |
| + len = data[0]; |
| + len <<= 8; |
| + len |= data[1]; |
| + if (len != (unsigned) size - 2) |
| + { |
| + *al = TLS1_AD_DECODE_ERROR; |
| + return 0; |
| + } |
| + len = data[2]; |
| + if (len != (unsigned) size - 3) |
| + { |
| + *al = TLS1_AD_DECODE_ERROR; |
| + return 0; |
| + } |
| + if (s->s3->alpn_selected) |
| + OPENSSL_free(s->s3->alpn_selected); |
| + s->s3->alpn_selected = OPENSSL_malloc(len); |
| + if (!s->s3->alpn_selected) |
| + { |
| + *al = TLS1_AD_INTERNAL_ERROR; |
| + return 0; |
| + } |
| + memcpy(s->s3->alpn_selected, data + 3, len); |
| + s->s3->alpn_selected_len = len; |
| + } |
| + |
| else if (type == TLSEXT_TYPE_renegotiate) |
| { |
| if(!ssl_parse_serverhello_renegotiate_ext(s, data, size, al)) |
| diff --git a/ssl/tls1.h b/ssl/tls1.h |
| index 8fc1ff4..c6670f4 100644 |
| --- a/ssl/tls1.h |
| +++ b/ssl/tls1.h |
| @@ -230,6 +230,9 @@ extern "C" { |
| /* ExtensionType value from RFC5620 */ |
| #define TLSEXT_TYPE_heartbeat 15 |
| |
| +/* ExtensionType value from draft-ietf-tls-applayerprotoneg-00 */ |
| +#define TLSEXT_TYPE_application_layer_protocol_negotiation 16 |
| + |
| /* ExtensionType value from RFC4507 */ |
| #define TLSEXT_TYPE_session_ticket 35 |
| |
| -- |
| 1.8.2.1 |
| |