Index: dart/runtime/bin/secure_socket.cc |
diff --git a/dart/runtime/bin/secure_socket.cc b/dart/runtime/bin/secure_socket.cc |
index 350bc64afa395e1ca399f5441a3b524712b3ceaf..2e22fef31edcffd635810cafe51cb9814534b85c 100644 |
--- a/dart/runtime/bin/secure_socket.cc |
+++ b/dart/runtime/bin/secure_socket.cc |
@@ -60,6 +60,7 @@ static void ThrowPRException(const char* exception_type, |
free(const_cast<char*>(message)); |
} |
Dart_ThrowException(exception); |
+ UNREACHABLE(); |
} |
@@ -121,6 +122,8 @@ void FUNCTION_NAME(SecureSocket_Connect)(Dart_NativeArguments args) { |
DartUtils::GetBooleanValue(Dart_GetNativeArgument(args, 7)); |
bool send_client_certificate = |
DartUtils::GetBooleanValue(Dart_GetNativeArgument(args, 8)); |
+ Dart_Handle protocols_handle = |
+ ThrowIfError(Dart_GetNativeArgument(args, 9)); |
const char* host_name = NULL; |
// TODO(whesse): Is truncating a Dart string containing \0 what we want? |
@@ -142,6 +145,10 @@ void FUNCTION_NAME(SecureSocket_Connect)(Dart_NativeArguments args) { |
// If this is a server connection, it must have a certificate to connect with. |
ASSERT(!is_server || certificate_name != NULL); |
+ // The protocols_handle is guaranteed to be a valid Uint8List. |
+ // It will have the correct length encoding of the protocols array. |
+ ASSERT(!Dart_IsNull(protocols_handle)); |
+ |
GetFilter(args)->Connect(host_name, |
&raw_addr, |
static_cast<int>(port), |
@@ -149,7 +156,8 @@ void FUNCTION_NAME(SecureSocket_Connect)(Dart_NativeArguments args) { |
certificate_name, |
request_client_certificate, |
require_client_certificate, |
- send_client_certificate); |
+ send_client_certificate, |
+ protocols_handle); |
} |
@@ -166,6 +174,12 @@ void FUNCTION_NAME(SecureSocket_Handshake)(Dart_NativeArguments args) { |
} |
+void FUNCTION_NAME(SecureSocket_GetSelectedProtocol)( |
+ Dart_NativeArguments args) { |
+ GetFilter(args)->GetSelectedProtocol(args); |
+} |
+ |
+ |
void FUNCTION_NAME(SecureSocket_Renegotiate)(Dart_NativeArguments args) { |
bool use_session_cache = |
DartUtils::GetBooleanValue(Dart_GetNativeArgument(args, 1)); |
@@ -658,7 +672,8 @@ void SSLFilter::Connect(const char* host_name, |
const char* certificate_name, |
bool request_client_certificate, |
bool require_client_certificate, |
- bool send_client_certificate) { |
+ bool send_client_certificate, |
+ Dart_Handle protocols_handle) { |
is_server_ = is_server; |
if (in_handshake_) { |
FATAL("Connect called twice on the same _SecureFilter."); |
@@ -673,12 +688,49 @@ void SSLFilter::Connect(const char* host_name, |
ThrowPRException("TlsException", "Failed SSL_ImportFD call"); |
} |
+ |
+ SECStatus status; |
+ |
+ // Enable ALPN (application layer protocol negogiation) if the caller provides |
+ // a valid list of supported protocols. |
+ { |
+ Dart_TypedData_Type protocols_type; |
+ uint8_t* protocol_string = NULL; |
+ intptr_t protocol_string_len = 0; |
+ |
+ Dart_Handle result = Dart_TypedDataAcquireData( |
+ protocols_handle, |
+ &protocols_type, |
+ reinterpret_cast<void**>(&protocol_string), |
+ &protocol_string_len); |
+ if (Dart_IsError(result)) { |
+ Dart_PropagateError(result); |
+ } |
+ |
+ if (protocols_type != Dart_TypedData_kUint8) { |
+ Dart_TypedDataReleaseData(protocols_handle); |
+ Dart_PropagateError(Dart_NewApiError( |
+ "Unexpected type for protocols (expected valid Uint8List).")); |
+ } |
+ |
+ if (protocol_string_len > 0) { |
+ status = SSL_OptionSet(filter_, SSL_ENABLE_ALPN, PR_TRUE); |
+ ASSERT(status == SECSuccess); |
+ |
+ status = SSL_SetNextProtoNego(filter_, |
+ protocol_string, |
+ protocol_string_len); |
+ ASSERT(status == SECSuccess); |
+ } |
+ |
+ Dart_TypedDataReleaseData(protocols_handle); |
+ } |
+ |
SSLVersionRange vrange; |
vrange.min = SSL_LIBRARY_VERSION_3_0; |
vrange.max = SSL_LIBRARY_VERSION_TLS_1_2; |
SSL_VersionRangeSet(filter_, &vrange); |
- SECStatus status; |
if (is_server) { |
CERTCertificate* certificate = NULL; |
if (strstr(certificate_name, "CN=") != NULL) { |
@@ -720,6 +772,7 @@ void SSLFilter::Connect(const char* host_name, |
certificate_name); |
} |
} |
+ |
// kt_rsa (key type RSA) is an enum constant from the NSS libraries. |
// TODO(whesse): Allow different key types. |
status = SSL_ConfigSecureServer(filter_, certificate, key, kt_rsa); |
@@ -823,6 +876,46 @@ void SSLFilter::Handshake() { |
} |
} |
+void SSLFilter::GetSelectedProtocol(Dart_NativeArguments args) { |
+ // Space for the selected protocol. |
+ const unsigned int kBufferSize = 256; |
+ unsigned char buffer[kBufferSize + 1]; |
+ |
+ unsigned int outLength = 0; |
+ SSLNextProtoState outState; |
+ |
+ SECStatus status = SSL_GetNextProto( |
+ filter_, &outState, buffer, &outLength, kBufferSize); |
+ if (status == SECSuccess) { |
+ if (outState == SSL_NEXT_PROTO_SELECTED || |
+ outState == SSL_NEXT_PROTO_NEGOTIATED) { |
+ ASSERT(outLength <= kBufferSize); |
+ buffer[outLength] = '\0'; |
+ Dart_Handle protocol_string = DartUtils::NewString( |
+ reinterpret_cast<const char *>(&buffer[0])); |
+ if (Dart_IsError(protocol_string)) { |
+ ThrowPRException("HandshakeException", |
+ "Protocol selected via ALPN, unable to get protocol " |
+ "string."); |
+ } else { |
+ Dart_SetReturnValue(args, protocol_string); |
+ } |
+ } else if (outState == SSL_NEXT_PROTO_NO_OVERLAP) { |
+ ThrowPRException("HandshakeException", |
+ "Client and Server could not agree upon a protocol"); |
+ } else if (outState == SSL_NEXT_PROTO_NO_SUPPORT) { |
+ // A value of `null` denotes that the client did not support protocol |
+ // negogiation. |
+ Dart_SetReturnValue(args, Dart_Null()); |
+ } else { |
+ UNREACHABLE(); |
+ } |
+ } else { |
+ ThrowPRException("HandshakeException", |
+ "Could not retrieve selected protocol via ALPN"); |
+ } |
+} |
+ |
void SSLFilter::Renegotiate(bool use_session_cache, |
bool request_client_certificate, |