diff --git a/examples/server/server.c b/examples/server/server.c index b745299b4..3805417a9 100644 --- a/examples/server/server.c +++ b/examples/server/server.c @@ -785,8 +785,8 @@ THREAD_RETURN CYASSL_THREAD server_test(void* args) #ifdef HAVE_ALPN if (alpnList != NULL) { int err; - char *protocol_name = NULL; - word16 protocol_nameSz = 0; + char *protocol_name = NULL, *list = NULL; + word16 protocol_nameSz = 0, listSz = 0; err = wolfSSL_ALPN_GetProtocol(ssl, &protocol_name, &protocol_nameSz); if (err == SSL_SUCCESS) @@ -796,9 +796,17 @@ THREAD_RETURN CYASSL_THREAD server_test(void* args) printf("No ALPN response sent (no match)\n"); else printf("Getting ALPN protocol name failed\n"); + + err = wolfSSL_ALPN_GetPeerProtocol(ssl, &list, &listSz); + if (err == SSL_SUCCESS) + printf("List of protocol names sent by Client: %s (%d)\n", + list, listSz); + else + printf("Get list of client's protocol name failed\n"); + + free(list); } #endif - if(echoData == 0 && throughput == 0) { ret = SSL_read(ssl, input, sizeof(input)-1); if (ret > 0) { diff --git a/src/internal.c b/src/internal.c index 0f20143de..9ad569ca1 100644 --- a/src/internal.c +++ b/src/internal.c @@ -1843,6 +1843,9 @@ int InitSSL(WOLFSSL* ssl, WOLFSSL_CTX* ctx) #ifdef HAVE_MAX_FRAGMENT ssl->max_fragment = MAX_RECORD_SIZE; #endif +#ifdef HAVE_ALPN + ssl->alpn_client_list = NULL; +#endif #endif /* default alert state (none) */ @@ -2063,7 +2066,14 @@ void SSL_ResourceFree(WOLFSSL* ssl) #endif /* HAVE_PK_CALLBACKS */ #ifdef HAVE_TLS_EXTENSIONS TLSX_FreeAll(ssl->extensions); + +#ifdef HAVE_ALPN + if (ssl->alpn_client_list != NULL) { + XFREE(ssl->alpn_client_list, NULL, DYNAMIC_TYPE_TMP_BUFFER); + ssl->alpn_client_list = NULL; + } #endif +#endif /* HAVE_TLS_EXTENSIONS */ #ifdef HAVE_NETX if (ssl->nxCtx.nxPacket) nx_packet_release(ssl->nxCtx.nxPacket); diff --git a/src/ssl.c b/src/ssl.c index cdd4e4690..292352dc2 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -949,7 +949,29 @@ int wolfSSL_UseALPN(WOLFSSL* ssl, char *protocol_name_list, int wolfSSL_ALPN_GetProtocol(WOLFSSL* ssl, char **protocol_name, word16 *size) { return TLSX_ALPN_GetRequest(ssl ? ssl->extensions : NULL, - (void **)protocol_name, size); + (void **)protocol_name, size); +} + +int wolfSSL_ALPN_GetPeerProtocol(WOLFSSL* ssl, char **list, word16 *listSz) +{ + if (list == NULL || listSz == NULL) + return BAD_FUNC_ARG; + + if (ssl->alpn_client_list == NULL) + return BUFFER_ERROR; + + *listSz = (word16)XSTRLEN(ssl->alpn_client_list); + if (*listSz == 0) + return BUFFER_ERROR; + + *list = (char *)XMALLOC((*listSz)+1, NULL, DYNAMIC_TYPE_OUT_BUFFER); + if (*list == NULL) + return MEMORY_ERROR; + + XSTRNCPY(*list, ssl->alpn_client_list, (*listSz)+1); + (*list)[*listSz] = 0; + + return SSL_SUCCESS; } #endif /* HAVE_ALPN */ diff --git a/src/tls.c b/src/tls.c index 4c7683fc5..b6b4f630c 100644 --- a/src/tls.c +++ b/src/tls.c @@ -995,11 +995,11 @@ static int TLSX_SetALPN(TLSX** extensions, const void* data, word16 size) static int TLSX_ALPN_ParseAndSet(WOLFSSL *ssl, byte *input, word16 length, byte isRequest) { - word16 size = 0; - word16 offset = 0; - int r = BUFFER_ERROR; - TLSX *extension; - ALPN *alpn = NULL, *list; + word16 size = 0, offset = 0, idx = 0; + int r = BUFFER_ERROR; + byte match = 0; + TLSX *extension; + ALPN *alpn = NULL, *list; extension = TLSX_Find(ssl->extensions, WOLFSSL_ALPN); if (extension == NULL) @@ -1023,20 +1023,46 @@ static int TLSX_ALPN_ParseAndSet(WOLFSSL *ssl, byte *input, word16 length, list = (ALPN*)extension->data; + /* keep the list sent by client */ + if (isRequest) { + if (ssl->alpn_client_list != NULL) + XFREE(ssl->alpn_client_list, NULL, DYNAMIC_TYPE_TMP_BUFFER); + + ssl->alpn_client_list = (char *)XMALLOC(size, NULL, + DYNAMIC_TYPE_TMP_BUFFER); + if (ssl->alpn_client_list == NULL) + return MEMORY_ERROR; + } + for (size = 0; offset < length; offset += size) { size = input[offset++]; if (offset + size > length) return BUFFER_ERROR; - alpn = TLSX_ALPN_Find(list, (char*)input + offset, size); - if (alpn != NULL) { - WOLFSSL_MSG("ALPN protocol match"); - break; + if (isRequest) { + XMEMCPY(ssl->alpn_client_list+idx, (char*)input + offset, size); + idx += size; + ssl->alpn_client_list[idx++] = ','; + } + + if (!match) { + alpn = TLSX_ALPN_Find(list, (char*)input + offset, size); + if (alpn != NULL) { + WOLFSSL_MSG("ALPN protocol match"); + match = 1; + + /* skip reading other values if not required */ + if (!isRequest) + break; + } } } - if (alpn == NULL) { + if (isRequest) + ssl->alpn_client_list[idx-1] = 0; + + if (!match) { WOLFSSL_MSG("No ALPN protocol match"); /* do nothing if no protocol match between client and server and option @@ -1159,7 +1185,7 @@ int TLSX_ALPN_GetRequest(TLSX* extensions, void** data, word16 *dataSz) #else /* HAVE_ALPN */ -#define ALPN_FREE_ALL(list) +#define ALPN_FREE_ALL(list) 0 #define ALPN_GET_SIZE(list) 0 #define ALPN_WRITE(a, b) 0 #define ALPN_PARSE(a, b, c, d) 0 diff --git a/tests/api.c b/tests/api.c index 4103bb25b..ccd03748c 100644 --- a/tests/api.c +++ b/tests/api.c @@ -1311,6 +1311,26 @@ static void verify_ALPN_matching_spdy2(WOLFSSL* ssl) AssertIntEQ(0, XMEMCMP(nego_proto, proto, protoSz)); } +static void verify_ALPN_client_list(WOLFSSL* ssl) +{ + /* http/1.1,spdy/1,spdy/2,spdy/3 */ + char alpn_list[] = {0x68, 0x74, 0x74, 0x70, 0x2f, 0x31, 0x2e, 0x31, 0x2c, + 0x73, 0x70, 0x64, 0x79, 0x2f, 0x31, 0x2c, + 0x73, 0x70, 0x64, 0x79, 0x2f, 0x32, 0x2c, + 0x73, 0x70, 0x64, 0x79, 0x2f, 0x33}; + char *clist = NULL; + word16 clistSz = 0; + + AssertIntEQ(SSL_SUCCESS, wolfSSL_ALPN_GetPeerProtocol(ssl, &clist, + &clistSz)); + + /* check value */ + AssertIntEQ(1, sizeof(alpn_list) == clistSz); + AssertIntEQ(0, XMEMCMP(alpn_list, clist, clistSz)); + + XFREE(clist, 0, DYNAMIC_TYPE_OUT_BUFFER); +} + static void test_wolfSSL_UseALPN_connection(void) { unsigned long i; @@ -1335,6 +1355,10 @@ static void test_wolfSSL_UseALPN_connection(void) {0, 0, use_ALPN_all_continue, verify_ALPN_not_matching_continue}, {0, 0, use_ALPN_unknown_continue, 0}, + /* success case read protocol send by client */ + {0, 0, use_ALPN_all, 0}, + {0, 0, use_ALPN_one, verify_ALPN_client_list}, + /* missmatch behavior with same list * the first and only this one must be taken */ {0, 0, use_ALPN_all, 0}, diff --git a/wolfssl/internal.h b/wolfssl/internal.h index f6ca61848..add081d78 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -1551,6 +1551,7 @@ WOLFSSL_LOCAL int TLSX_UseALPN(TLSX** extensions, const void* data, word16 size, byte options); WOLFSSL_LOCAL int TLSX_ALPN_SetOptions(TLSX** extensions, const byte option); + #endif /* HAVE_ALPN */ /* Maximum Fragment Length */ @@ -2448,6 +2449,9 @@ struct WOLFSSL { #ifdef HAVE_SECURE_RENEGOTIATION SecureRenegotiation* secure_renegotiation; /* valid pointer indicates */ #endif /* user turned on */ + #ifdef HAVE_ALPN + char* alpn_client_list; /* keep the client's list */ + #endif /* of accepted protocols */ #if !defined(NO_WOLFSSL_CLIENT) && defined(HAVE_SESSION_TICKET) CallbackSessionTicket session_ticket_cb; void* session_ticket_ctx; diff --git a/wolfssl/ssl.h b/wolfssl/ssl.h index 51d050f8f..d852d2be1 100644 --- a/wolfssl/ssl.h +++ b/wolfssl/ssl.h @@ -1372,6 +1372,8 @@ WOLFSSL_API int wolfSSL_UseALPN(WOLFSSL* ssl, char *protocol_name_list, WOLFSSL_API int wolfSSL_ALPN_GetProtocol(WOLFSSL* ssl, char **protocol_name, unsigned short *size); +WOLFSSL_API int wolfSSL_ALPN_GetPeerProtocol(WOLFSSL* ssl, char **list, + unsigned short *listSz); #endif /* HAVE_ALPN */ /* Maximum Fragment Length */