Merged support for the ALPN extension
diff --git a/ChangeLog b/ChangeLog
index f08432a..f667bf6 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,7 +1,12 @@
PolarSSL ChangeLog (Sorted per branch, date)
+ABI Alert: ALPN changes the ABI for the next release.
+
= PolarSSL 1.3 branch
+Features
+ * Support for the ALPN SSL extension
+
Changes
* x509_crt_info() now prints information about parsed extensions as well
diff --git a/include/polarssl/config.h b/include/polarssl/config.h
index 665ff23..c2c2708 100644
--- a/include/polarssl/config.h
+++ b/include/polarssl/config.h
@@ -861,6 +861,16 @@
#define POLARSSL_SSL_PROTO_TLS1_2
/**
+ * \def POLARSSL_SSL_ALPN
+ *
+ * Enable support for Application Layer Protocol Negotiation.
+ * draft-ietf-tls-applayerprotoneg-05
+ *
+ * Comment this macro to disable support for ALPN.
+ */
+#define POLARSSL_SSL_ALPN
+
+/**
* \def POLARSSL_SSL_SESSION_TICKETS
*
* Enable support for RFC 5077 session tickets in SSL.
diff --git a/include/polarssl/ssl.h b/include/polarssl/ssl.h
index c1aff67..c866b6f 100644
--- a/include/polarssl/ssl.h
+++ b/include/polarssl/ssl.h
@@ -320,6 +320,7 @@
#define SSL_ALERT_MSG_UNSUPPORTED_EXT 110 /* 0x6E */
#define SSL_ALERT_MSG_UNRECOGNIZED_NAME 112 /* 0x70 */
#define SSL_ALERT_MSG_UNKNOWN_PSK_IDENTITY 115 /* 0x73 */
+#define SSL_ALERT_MSG_NO_APPLICATION_PROTOCOL 120 /* 0x78 */
#define SSL_HS_HELLO_REQUEST 0
#define SSL_HS_CLIENT_HELLO 1
@@ -348,6 +349,8 @@
#define TLS_EXT_SIG_ALG 13
+#define TLS_EXT_ALPN 16
+
#define TLS_EXT_SESSION_TICKET 35
#define TLS_EXT_RENEGOTIATION_INFO 0xFF01
@@ -762,6 +765,14 @@
size_t hostname_len;
#endif
+#if defined(POLARSSL_SSL_ALPN)
+ /*
+ * ALPN extension
+ */
+ const char **alpn_list; /*!< ordered list of supported protocols */
+ const char *alpn_chosen; /*!< negotiated protocol */
+#endif
+
/*
* Secure renegotiation
*/
@@ -1232,6 +1243,30 @@
void *p_sni );
#endif /* POLARSSL_SSL_SERVER_NAME_INDICATION */
+#if defined(POLARSSL_SSL_ALPN)
+/**
+ * \brief Set the supported Application Layer Protocols.
+ *
+ * \param ssl SSL context
+ * \param protos NULL-terminated list of supported protocols,
+ * in decreasing preference order.
+ *
+ * \return 0 on success, or POLARSSL_ERR_SSL_BAD_INPUT_DATA.
+ */
+int ssl_set_alpn_protocols( ssl_context *ssl, const char **protos );
+
+/**
+ * \brief Get the name of the negotiated Application Layer Protocol.
+ * This function should be called after the handshake is
+ * completed.
+ *
+ * \param ssl SSL context
+ *
+ * \return Protcol name, or NULL if no protocol was negotiated.
+ */
+const char *ssl_get_alpn_protocol( const ssl_context *ssl );
+#endif /* POLARSSL_SSL_ALPN */
+
/**
* \brief Set the maximum supported version sent from the client side
* and/or accepted at the server side
diff --git a/library/ssl_cli.c b/library/ssl_cli.c
index b509e37..0a69f4d 100644
--- a/library/ssl_cli.c
+++ b/library/ssl_cli.c
@@ -383,6 +383,54 @@
}
#endif /* POLARSSL_SSL_SESSION_TICKETS */
+#if defined(POLARSSL_SSL_ALPN)
+static void ssl_write_alpn_ext( ssl_context *ssl,
+ unsigned char *buf, size_t *olen )
+{
+ unsigned char *p = buf;
+ const char **cur;
+
+ if( ssl->alpn_list == NULL )
+ {
+ *olen = 0;
+ return;
+ }
+
+ SSL_DEBUG_MSG( 3, ( "client hello, adding alpn extension" ) );
+
+ *p++ = (unsigned char)( ( TLS_EXT_ALPN >> 8 ) & 0xFF );
+ *p++ = (unsigned char)( ( TLS_EXT_ALPN ) & 0xFF );
+
+ /*
+ * opaque ProtocolName<1..2^8-1>;
+ *
+ * struct {
+ * ProtocolName protocol_name_list<2..2^16-1>
+ * } ProtocolNameList;
+ */
+
+ /* Skip writing extension and list length for now */
+ p += 4;
+
+ for( cur = ssl->alpn_list; *cur != NULL; cur++ )
+ {
+ *p = (unsigned char)( strlen( *cur ) & 0xFF );
+ memcpy( p + 1, *cur, *p );
+ p += 1 + *p;
+ }
+
+ *olen = p - buf;
+
+ /* List length = olen - 2 (ext_type) - 2 (ext_len) - 2 (list_len) */
+ buf[4] = (unsigned char)( ( ( *olen - 6 ) >> 8 ) & 0xFF );
+ buf[5] = (unsigned char)( ( ( *olen - 6 ) ) & 0xFF );
+
+ /* Extension length = olen - 2 (ext_type) - 2 (ext_len) */
+ buf[2] = (unsigned char)( ( ( *olen - 4 ) >> 8 ) & 0xFF );
+ buf[3] = (unsigned char)( ( ( *olen - 4 ) ) & 0xFF );
+}
+#endif /* POLARSSL_SSL_ALPN */
+
static int ssl_write_client_hello( ssl_context *ssl )
{
int ret;
@@ -595,6 +643,11 @@
ext_len += olen;
#endif
+#if defined(POLARSSL_SSL_ALPN)
+ ssl_write_alpn_ext( ssl, p + 2 + ext_len, &olen );
+ ext_len += olen;
+#endif
+
SSL_DEBUG_MSG( 3, ( "client hello, total extension length: %d",
ext_len ) );
@@ -753,6 +806,54 @@
}
#endif /* POLARSSL_ECDH_C || POLARSSL_ECDSA_C */
+#if defined(POLARSSL_SSL_ALPN)
+static int ssl_parse_alpn_ext( ssl_context *ssl,
+ const unsigned char *buf, size_t len )
+{
+ size_t list_len, name_len;
+ const char **p;
+
+ /* If we didn't send it, the server shouldn't send it */
+ if( ssl->alpn_list == NULL )
+ return( POLARSSL_ERR_SSL_BAD_HS_SERVER_HELLO );
+
+ /*
+ * opaque ProtocolName<1..2^8-1>;
+ *
+ * struct {
+ * ProtocolName protocol_name_list<2..2^16-1>
+ * } ProtocolNameList;
+ *
+ * the "ProtocolNameList" MUST contain exactly one "ProtocolName"
+ */
+
+ /* Min length is 2 (list_len) + 1 (name_len) + 1 (name) */
+ if( len < 4 )
+ return( POLARSSL_ERR_SSL_BAD_HS_SERVER_HELLO );
+
+ list_len = ( buf[0] << 8 ) | buf[1];
+ if( list_len != len - 2 )
+ return( POLARSSL_ERR_SSL_BAD_HS_SERVER_HELLO );
+
+ name_len = buf[2];
+ if( name_len != list_len - 1 )
+ return( POLARSSL_ERR_SSL_BAD_HS_SERVER_HELLO );
+
+ /* Check that the server chosen protocol was in our list and save it */
+ for( p = ssl->alpn_list; *p != NULL; p++ )
+ {
+ if( name_len == strlen( *p ) &&
+ memcmp( buf + 3, *p, name_len ) == 0 )
+ {
+ ssl->alpn_chosen = *p;
+ return( 0 );
+ }
+ }
+
+ return( POLARSSL_ERR_SSL_BAD_HS_SERVER_HELLO );
+}
+#endif /* POLARSSL_SSL_ALPN */
+
static int ssl_parse_server_hello( ssl_context *ssl )
{
int ret, i, comp;
@@ -1023,6 +1124,16 @@
break;
#endif /* POLARSSL_ECDH_C || POLARSSL_ECDSA_C */
+#if defined(POLARSSL_SSL_ALPN)
+ case TLS_EXT_ALPN:
+ SSL_DEBUG_MSG( 3, ( "found alpn extension" ) );
+
+ if( ( ret = ssl_parse_alpn_ext( ssl, ext + 4, ext_size ) ) != 0 )
+ return( ret );
+
+ break;
+#endif /* POLARSSL_SSL_ALPN */
+
default:
SSL_DEBUG_MSG( 3, ( "unknown extension found: %d (ignoring)",
ext_id ) );
diff --git a/library/ssl_srv.c b/library/ssl_srv.c
index 16fb264..08f6eea 100644
--- a/library/ssl_srv.c
+++ b/library/ssl_srv.c
@@ -683,6 +683,69 @@
}
#endif /* POLARSSL_SSL_SESSION_TICKETS */
+#if defined(POLARSSL_SSL_ALPN)
+static int ssl_parse_alpn_ext( ssl_context *ssl,
+ unsigned char *buf, size_t len )
+{
+ size_t list_len, cur_len;
+ const unsigned char *theirs, *start, *end;
+ const char **ours;
+
+ /* If ALPN not configured, just ignore the extension */
+ if( ssl->alpn_list == NULL )
+ return( 0 );
+
+ /*
+ * opaque ProtocolName<1..2^8-1>;
+ *
+ * struct {
+ * ProtocolName protocol_name_list<2..2^16-1>
+ * } ProtocolNameList;
+ */
+
+ /* Min length is 2 (list_len) + 1 (name_len) + 1 (name) */
+ if( len < 4 )
+ return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO );
+
+ list_len = ( buf[0] << 8 ) | buf[1];
+ if( list_len != len - 2 )
+ return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO );
+
+ /*
+ * Use our order of preference
+ */
+ start = buf + 2;
+ end = buf + len;
+ for( ours = ssl->alpn_list; *ours != NULL; ours++ )
+ {
+ for( theirs = start; theirs != end; theirs += cur_len )
+ {
+ /* If the list is well formed, we should get equality first */
+ if( theirs > end )
+ return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO );
+
+ cur_len = *theirs++;
+
+ /* Empty strings MUST NOT be included */
+ if( cur_len == 0 )
+ return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO );
+
+ if( cur_len == strlen( *ours ) &&
+ memcmp( theirs, *ours, cur_len ) == 0 )
+ {
+ ssl->alpn_chosen = *ours;
+ return( 0 );
+ }
+ }
+ }
+
+ /* If we get there, no match was found */
+ ssl_send_alert_message( ssl, SSL_ALERT_LEVEL_FATAL,
+ SSL_ALERT_MSG_NO_APPLICATION_PROTOCOL );
+ return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO );
+}
+#endif /* POLARSSL_SSL_ALPN */
+
/*
* Auxiliary functions for ServerHello parsing and related actions
*/
@@ -1385,6 +1448,16 @@
break;
#endif /* POLARSSL_SSL_SESSION_TICKETS */
+#if defined(POLARSSL_SSL_ALPN)
+ case TLS_EXT_ALPN:
+ SSL_DEBUG_MSG( 3, ( "found alpn extension" ) );
+
+ ret = ssl_parse_alpn_ext( ssl, ext + 4, ext_size );
+ if( ret != 0 )
+ return( ret );
+ break;
+#endif /* POLARSSL_SSL_SESSION_TICKETS */
+
default:
SSL_DEBUG_MSG( 3, ( "unknown extension found: %d (ignoring)",
ext_id ) );
@@ -1625,6 +1698,42 @@
}
#endif /* POLARSSL_ECDH_C || POLARSSL_ECDSA_C */
+#if defined(POLARSSL_SSL_ALPN )
+static void ssl_write_alpn_ext( ssl_context *ssl,
+ unsigned char *buf, size_t *olen )
+{
+ if( ssl->alpn_chosen == NULL )
+ {
+ *olen = 0;
+ return;
+ }
+
+ SSL_DEBUG_MSG( 3, ( "server hello, adding alpn extension" ) );
+
+ /*
+ * 0 . 1 ext identifier
+ * 2 . 3 ext length
+ * 4 . 5 protocol list length
+ * 6 . 6 protocol name length
+ * 7 . 7+n protocol name
+ */
+ buf[0] = (unsigned char)( ( TLS_EXT_ALPN >> 8 ) & 0xFF );
+ buf[1] = (unsigned char)( ( TLS_EXT_ALPN ) & 0xFF );
+
+ *olen = 7 + strlen( ssl->alpn_chosen );
+
+ buf[2] = (unsigned char)( ( ( *olen - 4 ) >> 8 ) & 0xFF );
+ buf[3] = (unsigned char)( ( ( *olen - 4 ) ) & 0xFF );
+
+ buf[4] = (unsigned char)( ( ( *olen - 6 ) >> 8 ) & 0xFF );
+ buf[5] = (unsigned char)( ( ( *olen - 6 ) ) & 0xFF );
+
+ buf[6] = (unsigned char)( ( ( *olen - 7 ) ) & 0xFF );
+
+ memcpy( buf + 7, ssl->alpn_chosen, *olen - 7 );
+}
+#endif /* POLARSSL_ECDH_C || POLARSSL_ECDSA_C */
+
static int ssl_write_server_hello( ssl_context *ssl )
{
#if defined(POLARSSL_HAVE_TIME)
@@ -1791,6 +1900,11 @@
ext_len += olen;
#endif
+#if defined(POLARSSL_SSL_ALPN)
+ ssl_write_alpn_ext( ssl, p + 2 + ext_len, &olen );
+ ext_len += olen;
+#endif
+
SSL_DEBUG_MSG( 3, ( "server hello, total extension length: %d", ext_len ) );
*p++ = (unsigned char)( ( ext_len >> 8 ) & 0xFF );
diff --git a/library/ssl_tls.c b/library/ssl_tls.c
index 8d43488..38843a3 100644
--- a/library/ssl_tls.c
+++ b/library/ssl_tls.c
@@ -3521,6 +3521,10 @@
ssl->session = NULL;
}
+#if defined(POLARSSL_SSL_ALPN)
+ ssl->alpn_chosen = NULL;
+#endif
+
if( ( ret = ssl_handshake_init( ssl ) ) != 0 )
return( ret );
@@ -3915,6 +3919,37 @@
}
#endif /* POLARSSL_SSL_SERVER_NAME_INDICATION */
+#if defined(POLARSSL_SSL_ALPN)
+int ssl_set_alpn_protocols( ssl_context *ssl, const char **protos )
+{
+ size_t cur_len, tot_len;
+ const char **p;
+
+ /*
+ * "Empty strings MUST NOT be included and byte strings MUST NOT be
+ * truncated". Check lengths now rather than later.
+ */
+ tot_len = 0;
+ for( p = protos; *p != NULL; p++ )
+ {
+ cur_len = strlen( *p );
+ tot_len += cur_len;
+
+ if( cur_len == 0 || cur_len > 255 || tot_len > 65535 )
+ return( POLARSSL_ERR_SSL_BAD_INPUT_DATA );
+ }
+
+ ssl->alpn_list = protos;
+
+ return( 0 );
+}
+
+const char *ssl_get_alpn_protocol( const ssl_context *ssl )
+{
+ return ssl->alpn_chosen;
+}
+#endif /* POLARSSL_SSL_ALPN */
+
void ssl_set_max_version( ssl_context *ssl, int major, int minor )
{
if( major >= SSL_MIN_MAJOR_VERSION && major <= SSL_MAX_MAJOR_VERSION &&
diff --git a/programs/ssl/ssl_client2.c b/programs/ssl/ssl_client2.c
index b5bfaed..d4428f2 100644
--- a/programs/ssl/ssl_client2.c
+++ b/programs/ssl/ssl_client2.c
@@ -65,6 +65,7 @@
#define DFL_RECONNECT 0
#define DFL_RECO_DELAY 0
#define DFL_TICKETS SSL_SESSION_TICKETS_ENABLED
+#define DFL_ALPN_STRING NULL
#define LONG_HEADER "User-agent: blah-blah-blah-blah-blah-blah-blah-blah-" \
"-01--blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-" \
@@ -108,6 +109,7 @@
int reconnect; /* attempt to resume session */
int reco_delay; /* delay in seconds before resuming session */
int tickets; /* enable / disable session tickets */
+ const char *alpn_string; /* ALPN supported protocols */
} opt;
static void my_debug( void *ctx, int level, const char *str )
@@ -248,11 +250,19 @@
#if defined(POLARSSL_TIMING_C)
#define USAGE_TIME \
- " reco_delay=%%d default: 0 seconds\n"
+ " reco_delay=%%d default: 0 seconds\n"
#else
#define USAGE_TIME ""
#endif /* POLARSSL_TIMING_C */
+#if defined(POLARSSL_SSL_ALPN)
+#define USAGE_ALPN \
+ " alpn=%%s default: \"\" (disabled)\n" \
+ " example: spdy/1,http/1.1\n"
+#else
+#define USAGE_ALPN ""
+#endif /* POLARSSL_SSL_ALPN */
+
#define USAGE \
"\n usage: ssl_client2 param=<>...\n" \
"\n acceptable parameters:\n" \
@@ -278,6 +288,7 @@
USAGE_TICKETS \
USAGE_MAX_FRAG_LEN \
USAGE_TRUNC_HMAC \
+ USAGE_ALPN \
"\n" \
" min_version=%%s default: \"\" (ssl3)\n" \
" max_version=%%s default: \"\" (tls1_2)\n" \
@@ -311,6 +322,9 @@
unsigned char psk[256];
size_t psk_len = 0;
#endif
+#if defined(POLARSSL_SSL_ALPN)
+ const char *alpn_list[10];
+#endif
const char *pers = "ssl_client2";
entropy_context entropy;
@@ -336,6 +350,9 @@
x509_crt_init( &clicert );
pk_init( &pkey );
#endif
+#if defined(POLARSSL_SSL_ALPN)
+ memset( alpn_list, 0, sizeof alpn_list );
+#endif
if( argc == 0 )
{
@@ -383,6 +400,7 @@
opt.reconnect = DFL_RECONNECT;
opt.reco_delay = DFL_RECO_DELAY;
opt.tickets = DFL_TICKETS;
+ opt.alpn_string = DFL_ALPN_STRING;
for( i = 1; i < argc; i++ )
{
@@ -475,6 +493,10 @@
if( opt.tickets < 0 || opt.tickets > 2 )
goto usage;
}
+ else if( strcmp( p, "alpn" ) == 0 )
+ {
+ opt.alpn_string = q;
+ }
else if( strcmp( p, "min_version" ) == 0 )
{
if( strcmp( q, "ssl3" ) == 0 )
@@ -635,6 +657,26 @@
}
#endif /* POLARSSL_KEY_EXCHANGE__SOME__PSK_ENABLED */
+#if defined(POLARSSL_SSL_ALPN)
+ if( opt.alpn_string != NULL )
+ {
+ p = (char *) opt.alpn_string;
+ i = 0;
+
+ /* Leave room for a final NULL in alpn_list */
+ while( i < (int) sizeof alpn_list - 1 && *p != '\0' )
+ {
+ alpn_list[i++] = p;
+
+ /* Terminate the current string and move on to next one */
+ while( *p != ',' && *p != '\0' )
+ p++;
+ if( *p == ',' )
+ *p++ = '\0';
+ }
+ }
+#endif /* POLARSSL_SSL_ALPN */
+
/*
* 0. Initialize the RNG and the session data
*/
@@ -806,6 +848,11 @@
ssl_set_truncated_hmac( &ssl, SSL_TRUNC_HMAC_ENABLED );
#endif
+#if defined(POLARSSL_SSL_ALPN)
+ if( opt.alpn_string != NULL )
+ ssl_set_alpn_protocols( &ssl, alpn_list );
+#endif
+
ssl_set_rng( &ssl, ctr_drbg_random, &ctr_drbg );
ssl_set_dbg( &ssl, my_debug, stdout );
@@ -878,6 +925,15 @@
printf( " ok\n [ Protocol is %s ]\n [ Ciphersuite is %s ]\n",
ssl_get_version( &ssl ), ssl_get_ciphersuite( &ssl ) );
+#if defined(POLARSSL_SSL_ALPN)
+ if( opt.alpn_string != NULL )
+ {
+ const char *alp = ssl_get_alpn_protocol( &ssl );
+ printf( " [ Application Layer Protocol is %s ]\n",
+ alp ? alp : "(none)" );
+ }
+#endif
+
if( opt.reconnect != 0 )
{
printf(" . Saving session for reuse..." );
diff --git a/programs/ssl/ssl_server2.c b/programs/ssl/ssl_server2.c
index 4e199c3..758188b 100644
--- a/programs/ssl/ssl_server2.c
+++ b/programs/ssl/ssl_server2.c
@@ -85,6 +85,7 @@
#define DFL_CACHE_MAX -1
#define DFL_CACHE_TIMEOUT -1
#define DFL_SNI NULL
+#define DFL_ALPN_STRING NULL
#define LONG_RESPONSE "<p>01-blah-blah-blah-blah-blah-blah-blah-blah-blah\r\n" \
"02-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah\r\n" \
@@ -131,6 +132,7 @@
int cache_max; /* max number of session cache entries */
int cache_timeout; /* expiration delay of session cache entries */
char *sni; /* string decribing sni information */
+ const char *alpn_string; /* ALPN supported protocols */
} opt;
static void my_debug( void *ctx, int level, const char *str )
@@ -245,6 +247,14 @@
#define USAGE_MAX_FRAG_LEN ""
#endif /* POLARSSL_SSL_MAX_FRAGMENT_LENGTH */
+#if defined(POLARSSL_SSL_ALPN)
+#define USAGE_ALPN \
+ " alpn=%%s default: \"\" (disabled)\n" \
+ " example: spdy/1,http/1.1\n"
+#else
+#define USAGE_ALPN ""
+#endif /* POLARSSL_SSL_ALPN */
+
#define USAGE \
"\n usage: ssl_server2 param=<>...\n" \
"\n acceptable parameters:\n" \
@@ -267,6 +277,7 @@
USAGE_TICKETS \
USAGE_CACHE \
USAGE_MAX_FRAG_LEN \
+ USAGE_ALPN \
"\n" \
" min_version=%%s default: \"ssl3\"\n" \
" max_version=%%s default: \"tls1_2\"\n" \
@@ -429,6 +440,9 @@
#if defined(POLARSSL_SNI)
sni_entry *sni_info = NULL;
#endif
+#if defined(POLARSSL_SSL_ALPN)
+ const char *alpn_list[10];
+#endif
#if defined(POLARSSL_MEMORY_BUFFER_ALLOC_C)
unsigned char alloc_buf[100000];
#endif
@@ -456,6 +470,9 @@
#if defined(POLARSSL_SSL_CACHE_C)
ssl_cache_init( &cache );
#endif
+#if defined(POLARSSL_SSL_ALPN)
+ memset( alpn_list, 0, sizeof alpn_list );
+#endif
if( argc == 0 )
{
@@ -504,6 +521,7 @@
opt.cache_max = DFL_CACHE_MAX;
opt.cache_timeout = DFL_CACHE_TIMEOUT;
opt.sni = DFL_SNI;
+ opt.alpn_string = DFL_ALPN_STRING;
for( i = 1; i < argc; i++ )
{
@@ -653,6 +671,10 @@
else
goto usage;
}
+ else if( strcmp( p, "alpn" ) == 0 )
+ {
+ opt.alpn_string = q;
+ }
else if( strcmp( p, "tickets" ) == 0 )
{
opt.tickets = atoi( q );
@@ -760,6 +782,26 @@
}
#endif /* POLARSSL_KEY_EXCHANGE__SOME__PSK_ENABLED */
+#if defined(POLARSSL_SSL_ALPN)
+ if( opt.alpn_string != NULL )
+ {
+ p = (char *) opt.alpn_string;
+ i = 0;
+
+ /* Leave room for a final NULL in alpn_list */
+ while( i < (int) sizeof alpn_list - 1 && *p != '\0' )
+ {
+ alpn_list[i++] = p;
+
+ /* Terminate the current string and move on to next one */
+ while( *p != ',' && *p != '\0' )
+ p++;
+ if( *p == ',' )
+ *p++ = '\0';
+ }
+ }
+#endif /* POLARSSL_SSL_ALPN */
+
/*
* 0. Initialize the RNG and the session data
*/
@@ -974,6 +1016,11 @@
ssl_set_max_frag_len( &ssl, opt.mfl_code );
#endif
+#if defined(POLARSSL_SSL_ALPN)
+ if( opt.alpn_string != NULL )
+ ssl_set_alpn_protocols( &ssl, alpn_list );
+#endif
+
ssl_set_rng( &ssl, ctr_drbg_random, &ctr_drbg );
ssl_set_dbg( &ssl, my_debug, stdout );
@@ -1103,6 +1150,15 @@
printf( " ok\n [ Protocol is %s ]\n [ Ciphersuite is %s ]\n",
ssl_get_version( &ssl ), ssl_get_ciphersuite( &ssl ) );
+#if defined(POLARSSL_SSL_ALPN)
+ if( opt.alpn_string != NULL )
+ {
+ const char *alp = ssl_get_alpn_protocol( &ssl );
+ printf( " [ Application Layer Protocol is %s ]\n",
+ alp ? alp : "(none)" );
+ }
+#endif
+
#if defined(POLARSSL_X509_CRT_PARSE_C)
/*
* 5. Verify the server certificate
diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh
index 60efe8d..916ce77 100755
--- a/tests/ssl-opt.sh
+++ b/tests/ssl-opt.sh
@@ -21,6 +21,8 @@
TESTS=0
FAILS=0
+CONFIG_H='../include/polarssl/config.h'
+
MEMCHECK=0
FILTER='.*'
EXCLUDE='SSLv2' # disabled by default, needs OpenSSL compiled with SSLv2
@@ -804,6 +806,8 @@
-C "ssl_handshake returned" \
-c "Read from server: .* bytes read"
+# Tests for version negotiation
+
run_test "Version check #1 (all -> 1.2)" \
"$P_SRV" \
"$P_CLI" \
@@ -874,6 +878,96 @@
-c "ssl_handshake returned" \
-s "SSL - Handshake protocol not within min/max boundaries"
+# Tests for ALPN extension
+
+if grep '^#define POLARSSL_SSL_ALPN' $CONFIG_H >/dev/null; then
+
+run_test "ALPN #0 (none)" \
+ "$P_SRV debug_level=4" \
+ "$P_CLI debug_level=4" \
+ 0 \
+ -C "client hello, adding alpn extension" \
+ -S "found alpn extension" \
+ -C "got an alert message, type: \\[2:120]" \
+ -S "server hello, adding alpn extension" \
+ -C "found alpn extension " \
+ -C "Application Layer Protocol is" \
+ -S "Application Layer Protocol is"
+
+run_test "ALPN #1 (client only)" \
+ "$P_SRV debug_level=4" \
+ "$P_CLI debug_level=4 alpn=abc,1234" \
+ 0 \
+ -c "client hello, adding alpn extension" \
+ -s "found alpn extension" \
+ -C "got an alert message, type: \\[2:120]" \
+ -S "server hello, adding alpn extension" \
+ -C "found alpn extension " \
+ -c "Application Layer Protocol is (none)" \
+ -S "Application Layer Protocol is"
+
+run_test "ALPN #2 (server only)" \
+ "$P_SRV debug_level=4 alpn=abc,1234" \
+ "$P_CLI debug_level=4" \
+ 0 \
+ -C "client hello, adding alpn extension" \
+ -S "found alpn extension" \
+ -C "got an alert message, type: \\[2:120]" \
+ -S "server hello, adding alpn extension" \
+ -C "found alpn extension " \
+ -C "Application Layer Protocol is" \
+ -s "Application Layer Protocol is (none)"
+
+run_test "ALPN #3 (both, common cli1-srv1)" \
+ "$P_SRV debug_level=4 alpn=abc,1234" \
+ "$P_CLI debug_level=4 alpn=abc,1234" \
+ 0 \
+ -c "client hello, adding alpn extension" \
+ -s "found alpn extension" \
+ -C "got an alert message, type: \\[2:120]" \
+ -s "server hello, adding alpn extension" \
+ -c "found alpn extension" \
+ -c "Application Layer Protocol is abc" \
+ -s "Application Layer Protocol is abc"
+
+run_test "ALPN #4 (both, common cli2-srv1)" \
+ "$P_SRV debug_level=4 alpn=abc,1234" \
+ "$P_CLI debug_level=4 alpn=1234,abc" \
+ 0 \
+ -c "client hello, adding alpn extension" \
+ -s "found alpn extension" \
+ -C "got an alert message, type: \\[2:120]" \
+ -s "server hello, adding alpn extension" \
+ -c "found alpn extension" \
+ -c "Application Layer Protocol is abc" \
+ -s "Application Layer Protocol is abc"
+
+run_test "ALPN #5 (both, common cli1-srv2)" \
+ "$P_SRV debug_level=4 alpn=abc,1234" \
+ "$P_CLI debug_level=4 alpn=1234,abcde" \
+ 0 \
+ -c "client hello, adding alpn extension" \
+ -s "found alpn extension" \
+ -C "got an alert message, type: \\[2:120]" \
+ -s "server hello, adding alpn extension" \
+ -c "found alpn extension" \
+ -c "Application Layer Protocol is 1234" \
+ -s "Application Layer Protocol is 1234"
+
+run_test "ALPN #6 (both, no common)" \
+ "$P_SRV debug_level=4 alpn=abc,123" \
+ "$P_CLI debug_level=4 alpn=1234,abcde" \
+ 1 \
+ -c "client hello, adding alpn extension" \
+ -s "found alpn extension" \
+ -c "got an alert message, type: \\[2:120]" \
+ -S "server hello, adding alpn extension" \
+ -C "found alpn extension" \
+ -C "Application Layer Protocol is 1234" \
+ -S "Application Layer Protocol is 1234"
+
+fi
+
# Final report
echo "------------------------------------------------------------------------"