Files
sdk/runtime/bin/secure_socket.cc
T
sgjesse@google.com 54f6939ffc Ensure that there is no "hidden" DNS lookup in secure socket code
The required setting of the internet adddress of the peer for NSS is
now using the internet address which have already been looked up in
Dart code for normal secure socket connect.

In the cases where SecureSocket.secure and RawSecureSocket.secure is
used with a host name, the internet address of the already connected
socket will be used. If that is not sufficient an IntetnetAddress from
DNS lookup in Dart can be passed instead of just a host name.

R=ajohnsen@google.com, whesse@google.com
BUG=http://dartbug.com/11103

Review URL: https://codereview.chromium.org//17381012

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@24174 260f80e4-7a28-3924-810f-c04153c831b5
2013-06-19 09:07:43 +00:00

754 lines
26 KiB
C++

// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include "bin/secure_socket.h"
#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <stdio.h>
#include <string.h>
#include <key.h>
#include <keyt.h>
#include <nss.h>
#include <pk11pub.h>
#include <prerror.h>
#include <prinit.h>
#include <prnetdb.h>
#include <secmod.h>
#include <ssl.h>
#include <sslproto.h>
#include "bin/builtin.h"
#include "bin/dartutils.h"
#include "bin/net/nss_memio.h"
#include "bin/socket.h"
#include "bin/thread.h"
#include "bin/utils.h"
#include "platform/utils.h"
#include "include/dart_api.h"
namespace dart {
namespace bin {
bool SSLFilter::library_initialized_ = false;
dart::Mutex SSLFilter::mutex_; // To protect library initialization.
// The password is needed when creating secure server sockets. It can
// be null if only secure client sockets are used.
const char* SSLFilter::password_ = NULL;
static const int kSSLFilterNativeFieldIndex = 0;
static SSLFilter* GetFilter(Dart_NativeArguments args) {
SSLFilter* filter;
Dart_Handle dart_this = ThrowIfError(Dart_GetNativeArgument(args, 0));
ASSERT(Dart_IsInstance(dart_this));
ThrowIfError(Dart_GetNativeInstanceField(
dart_this,
kSSLFilterNativeFieldIndex,
reinterpret_cast<intptr_t*>(&filter)));
return filter;
}
static void SetFilter(Dart_NativeArguments args, SSLFilter* filter) {
Dart_Handle dart_this = ThrowIfError(Dart_GetNativeArgument(args, 0));
ASSERT(Dart_IsInstance(dart_this));
ThrowIfError(Dart_SetNativeInstanceField(
dart_this,
kSSLFilterNativeFieldIndex,
reinterpret_cast<intptr_t>(filter)));
}
void FUNCTION_NAME(SecureSocket_Init)(Dart_NativeArguments args) {
Dart_EnterScope();
Dart_Handle dart_this = ThrowIfError(Dart_GetNativeArgument(args, 0));
SSLFilter* filter = new SSLFilter;
SetFilter(args, filter);
filter->Init(dart_this);
Dart_ExitScope();
}
void FUNCTION_NAME(SecureSocket_Connect)(Dart_NativeArguments args) {
Dart_EnterScope();
Dart_Handle host_name_object = ThrowIfError(Dart_GetNativeArgument(args, 1));
Dart_Handle host_sockaddr_storage_object =
ThrowIfError(Dart_GetNativeArgument(args, 2));
Dart_Handle port_object = ThrowIfError(Dart_GetNativeArgument(args, 3));
bool is_server = DartUtils::GetBooleanValue(Dart_GetNativeArgument(args, 4));
Dart_Handle certificate_name_object =
ThrowIfError(Dart_GetNativeArgument(args, 5));
bool request_client_certificate =
DartUtils::GetBooleanValue(Dart_GetNativeArgument(args, 6));
bool require_client_certificate =
DartUtils::GetBooleanValue(Dart_GetNativeArgument(args, 7));
bool send_client_certificate =
DartUtils::GetBooleanValue(Dart_GetNativeArgument(args, 8));
const char* host_name = NULL;
// TODO(whesse): Is truncating a Dart string containing \0 what we want?
ThrowIfError(Dart_StringToCString(host_name_object, &host_name));
RawAddr raw_addr;
Dart_TypedData_Type type;
uint8_t* buffer = NULL;
intptr_t len;
ThrowIfError(Dart_TypedDataAcquireData(host_sockaddr_storage_object,
&type,
reinterpret_cast<void**>(&buffer),
&len));
ASSERT(static_cast<size_t>(len) <= sizeof(raw_addr));
memmove(&raw_addr, buffer, len);
Dart_TypedDataReleaseData(host_sockaddr_storage_object);
int64_t port;
if (!DartUtils::GetInt64Value(port_object, &port)) {
FATAL("The range of port_object was checked in Dart - it cannot fail here");
}
const char* certificate_name = NULL;
if (Dart_IsString(certificate_name_object)) {
ThrowIfError(Dart_StringToCString(certificate_name_object,
&certificate_name));
}
// If this is a server connection, it must have a certificate to connect with.
ASSERT(!is_server || certificate_name != NULL);
GetFilter(args)->Connect(host_name,
&raw_addr,
static_cast<int>(port),
is_server,
certificate_name,
request_client_certificate,
require_client_certificate,
send_client_certificate);
Dart_ExitScope();
}
void FUNCTION_NAME(SecureSocket_Destroy)(Dart_NativeArguments args) {
Dart_EnterScope();
SSLFilter* filter = GetFilter(args);
SetFilter(args, NULL);
filter->Destroy();
delete filter;
Dart_ExitScope();
}
void FUNCTION_NAME(SecureSocket_Handshake)(Dart_NativeArguments args) {
Dart_EnterScope();
GetFilter(args)->Handshake();
Dart_ExitScope();
}
void FUNCTION_NAME(SecureSocket_RegisterHandshakeCompleteCallback)(
Dart_NativeArguments args) {
Dart_EnterScope();
Dart_Handle handshake_complete =
ThrowIfError(Dart_GetNativeArgument(args, 1));
if (!Dart_IsClosure(handshake_complete)) {
Dart_ThrowException(DartUtils::NewDartArgumentError(
"Illegal argument to RegisterHandshakeCompleteCallback"));
}
GetFilter(args)->RegisterHandshakeCompleteCallback(handshake_complete);
Dart_ExitScope();
}
void FUNCTION_NAME(SecureSocket_RegisterBadCertificateCallback)(
Dart_NativeArguments args) {
Dart_EnterScope();
Dart_Handle callback =
ThrowIfError(Dart_GetNativeArgument(args, 1));
if (!Dart_IsClosure(callback) && !Dart_IsNull(callback)) {
Dart_ThrowException(DartUtils::NewDartArgumentError(
"Illegal argument to RegisterBadCertificateCallback"));
}
GetFilter(args)->RegisterBadCertificateCallback(callback);
Dart_ExitScope();
}
void FUNCTION_NAME(SecureSocket_ProcessBuffer)(Dart_NativeArguments args) {
Dart_EnterScope();
Dart_Handle buffer_id_object = ThrowIfError(Dart_GetNativeArgument(args, 1));
int64_t buffer_id = DartUtils::GetIntegerValue(buffer_id_object);
if (buffer_id < 0 || buffer_id >= SSLFilter::kNumBuffers) {
Dart_ThrowException(DartUtils::NewDartArgumentError(
"Illegal argument to ProcessBuffer"));
}
intptr_t bytes_read =
GetFilter(args)->ProcessBuffer(static_cast<int>(buffer_id));
Dart_SetReturnValue(args, Dart_NewInteger(bytes_read));
Dart_ExitScope();
}
void FUNCTION_NAME(SecureSocket_InitializeLibrary)
(Dart_NativeArguments args) {
Dart_EnterScope();
Dart_Handle certificate_database_object =
ThrowIfError(Dart_GetNativeArgument(args, 0));
// Check that the type is string, and get the UTF-8 C string value from it.
const char* certificate_database = NULL;
if (Dart_IsString(certificate_database_object)) {
ThrowIfError(Dart_StringToCString(certificate_database_object,
&certificate_database));
} else if (!Dart_IsNull(certificate_database_object)) {
Dart_ThrowException(DartUtils::NewDartArgumentError(
"Non-String certificate directory argument to SetCertificateDatabase"));
}
// Leave certificate_database as NULL if no value was provided.
Dart_Handle password_object = ThrowIfError(Dart_GetNativeArgument(args, 1));
// Check that the type is string or null,
// and get the UTF-8 C string value from it.
const char* password = NULL;
if (Dart_IsString(password_object)) {
ThrowIfError(Dart_StringToCString(password_object, &password));
} else if (Dart_IsNull(password_object)) {
// Pass the empty string as the password.
password = "";
} else {
Dart_ThrowException(DartUtils::NewDartArgumentError(
"Password argument to SetCertificateDatabase is not a String or null"));
}
Dart_Handle builtin_roots_object =
ThrowIfError(Dart_GetNativeArgument(args, 2));
// Check that the type is boolean, and get the boolean value from it.
bool builtin_roots = true;
if (Dart_IsBoolean(builtin_roots_object)) {
ThrowIfError(Dart_BooleanValue(builtin_roots_object, &builtin_roots));
} else {
Dart_ThrowException(DartUtils::NewDartArgumentError(
"UseBuiltinRoots argument to SetCertificateDatabase is not a bool"));
}
SSLFilter::InitializeLibrary(certificate_database, password, builtin_roots);
Dart_ExitScope();
}
void FUNCTION_NAME(SecureSocket_PeerCertificate)
(Dart_NativeArguments args) {
Dart_EnterScope();
Dart_SetReturnValue(args, GetFilter(args)->PeerCertificate());
Dart_ExitScope();
}
static Dart_Handle X509FromCertificate(CERTCertificate* certificate) {
PRTime start_validity;
PRTime end_validity;
SECStatus status =
CERT_GetCertTimes(certificate, &start_validity, &end_validity);
if (status != SECSuccess) {
ThrowPRException("Cannot get validity times from certificate");
}
int64_t start_epoch_ms = start_validity / PR_USEC_PER_MSEC;
int64_t end_epoch_ms = end_validity / PR_USEC_PER_MSEC;
Dart_Handle subject_name_object =
DartUtils::NewString(certificate->subjectName);
Dart_Handle issuer_name_object =
DartUtils::NewString(certificate->issuerName);
Dart_Handle start_epoch_ms_int = Dart_NewInteger(start_epoch_ms);
Dart_Handle end_epoch_ms_int = Dart_NewInteger(end_epoch_ms);
Dart_Handle date_class =
DartUtils::GetDartClass(DartUtils::kCoreLibURL, "DateTime");
Dart_Handle from_milliseconds =
DartUtils::NewString("fromMillisecondsSinceEpoch");
Dart_Handle start_validity_date =
Dart_New(date_class, from_milliseconds, 1, &start_epoch_ms_int);
Dart_Handle end_validity_date =
Dart_New(date_class, from_milliseconds, 1, &end_epoch_ms_int);
Dart_Handle x509_class =
DartUtils::GetDartClass(DartUtils::kIOLibURL, "X509Certificate");
Dart_Handle arguments[] = { subject_name_object,
issuer_name_object,
start_validity_date,
end_validity_date };
return Dart_New(x509_class, Dart_Null(), 4, arguments);
}
void SSLFilter::Init(Dart_Handle dart_this) {
if (!library_initialized_) {
InitializeLibrary(NULL, "", true, false);
}
ASSERT(string_start_ == NULL);
string_start_ = Dart_NewPersistentHandle(DartUtils::NewString("start"));
ASSERT(string_start_ != NULL);
ASSERT(string_length_ == NULL);
string_length_ = Dart_NewPersistentHandle(DartUtils::NewString("length"));
ASSERT(string_length_ != NULL);
ASSERT(bad_certificate_callback_ == NULL);
bad_certificate_callback_ = Dart_NewPersistentHandle(Dart_Null());
ASSERT(bad_certificate_callback_ != NULL);
InitializeBuffers(dart_this);
filter_ = memio_CreateIOLayer(kMemioBufferSize);
}
void SSLFilter::InitializeBuffers(Dart_Handle dart_this) {
// Create SSLFilter buffers as ExternalUint8Array objects.
Dart_Handle dart_buffers_object = ThrowIfError(
Dart_GetField(dart_this, DartUtils::NewString("buffers")));
Dart_Handle dart_buffer_object =
Dart_ListGetAt(dart_buffers_object, kReadPlaintext);
Dart_Handle external_buffer_class =
Dart_InstanceGetClass(dart_buffer_object);
Dart_Handle dart_buffer_size = ThrowIfError(
Dart_GetField(external_buffer_class, DartUtils::NewString("SIZE")));
int64_t buffer_size = DartUtils::GetIntegerValue(dart_buffer_size);
Dart_Handle dart_encrypted_buffer_size = ThrowIfError(
Dart_GetField(external_buffer_class,
DartUtils::NewString("ENCRYPTED_SIZE")));
int64_t encrypted_buffer_size =
DartUtils::GetIntegerValue(dart_encrypted_buffer_size);
if (buffer_size <= 0 || buffer_size > 1024 * 1024) {
Dart_ThrowException(
DartUtils::NewString("Invalid buffer size in _ExternalBuffer"));
}
if (encrypted_buffer_size <= 0 || encrypted_buffer_size > 1024 * 1024) {
Dart_ThrowException(DartUtils::NewString(
"Invalid encrypted buffer size in _ExternalBuffer"));
}
buffer_size_ = static_cast<int>(buffer_size);
encrypted_buffer_size_ = static_cast<int>(encrypted_buffer_size);
Dart_Handle data_identifier = DartUtils::NewString("data");
for (int i = 0; i < kNumBuffers; ++i) {
int size = isEncrypted(i) ? encrypted_buffer_size_ : buffer_size_;
dart_buffer_objects_[i] =
Dart_NewPersistentHandle(Dart_ListGetAt(dart_buffers_object, i));
ASSERT(dart_buffer_objects_[i] != NULL);
buffers_[i] = new uint8_t[size];
Dart_Handle data = ThrowIfError(
Dart_NewExternalTypedData(Dart_TypedData_kUint8, buffers_[i], size));
ThrowIfError(
Dart_SetField(Dart_HandleFromPersistent(dart_buffer_objects_[i]),
data_identifier,
data));
}
}
void SSLFilter::RegisterHandshakeCompleteCallback(Dart_Handle complete) {
ASSERT(NULL == handshake_complete_);
handshake_complete_ = Dart_NewPersistentHandle(complete);
ASSERT(handshake_complete_ != NULL);
}
void SSLFilter::RegisterBadCertificateCallback(Dart_Handle callback) {
ASSERT(bad_certificate_callback_ != NULL);
Dart_DeletePersistentHandle(bad_certificate_callback_);
bad_certificate_callback_ = Dart_NewPersistentHandle(callback);
ASSERT(bad_certificate_callback_ != NULL);
}
static const char* builtin_roots_module =
#if defined(TARGET_OS_LINUX) || defined(TARGET_OS_ANDROID)
"name=\"Root Certs\" library=\"libnssckbi.so\"";
#elif defined(TARGET_OS_MACOS)
"name=\"Root Certs\" library=\"libnssckbi.dylib\"";
#elif defined(TARGET_OS_WINDOWS)
"name=\"Root Certs\" library=\"nssckbi.dll\"";
#else
#error Automatic target os detection failed.
#endif
void SSLFilter::InitializeLibrary(const char* certificate_database,
const char* password,
bool use_builtin_root_certificates,
bool report_duplicate_initialization) {
MutexLocker locker(&mutex_);
SECStatus status;
if (!library_initialized_) {
password_ = strdup(password); // This one copy persists until Dart exits.
PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
// TODO(whesse): Verify there are no UTF-8 issues here.
if (certificate_database == NULL || certificate_database[0] == '\0') {
status = NSS_NoDB_Init(NULL);
if (status != SECSuccess) {
mutex_.Unlock(); // MutexLocker destructor not called when throwing.
ThrowPRException("Failed NSS_NoDB_Init call.");
}
if (use_builtin_root_certificates) {
SECMODModule* module = SECMOD_LoadUserModule(
const_cast<char*>(builtin_roots_module), NULL, PR_FALSE);
if (!module) {
mutex_.Unlock(); // MutexLocker destructor not called when throwing.
ThrowPRException("Failed to load builtin root certificates.");
}
}
} else {
PRUint32 init_flags = NSS_INIT_READONLY;
if (!use_builtin_root_certificates) {
init_flags |= NSS_INIT_NOMODDB;
}
status = NSS_Initialize(certificate_database,
"",
"",
SECMOD_DB,
init_flags);
if (status != SECSuccess) {
mutex_.Unlock(); // MutexLocker destructor not called when throwing.
ThrowPRException("Failed NSS_Init call.");
}
}
library_initialized_ = true;
status = NSS_SetDomesticPolicy();
if (status != SECSuccess) {
mutex_.Unlock(); // MutexLocker destructor not called when throwing.
ThrowPRException("Failed NSS_SetDomesticPolicy call.");
}
// Enable TLS, as well as SSL3 and SSL2.
status = SSL_OptionSetDefault(SSL_ENABLE_TLS, PR_TRUE);
if (status != SECSuccess) {
mutex_.Unlock(); // MutexLocker destructor not called when throwing.
ThrowPRException("Failed SSL_OptionSetDefault enable TLS call.");
}
status = SSL_ConfigServerSessionIDCache(0, 0, 0, NULL);
if (status != SECSuccess) {
mutex_.Unlock(); // MutexLocker destructor not called when throwing.
ThrowPRException("Failed SSL_ConfigServerSessionIDCache call.");
}
} else if (report_duplicate_initialization) {
mutex_.Unlock(); // MutexLocker destructor not called when throwing.
ThrowException("Called SSLFilter::InitializeLibrary more than once");
}
}
char* PasswordCallback(PK11SlotInfo* slot, PRBool retry, void* arg) {
if (!retry) {
return PL_strdup(static_cast<char*>(arg)); // Freed by NSS internals.
}
return NULL;
}
SECStatus BadCertificateCallback(void* filter, PRFileDesc* fd) {
SSLFilter* ssl_filter = static_cast<SSLFilter*>(filter);
Dart_Handle callback = ssl_filter->bad_certificate_callback();
if (Dart_IsNull(callback)) return SECFailure;
Dart_EnterScope();
Dart_Handle x509_object = ssl_filter->PeerCertificate();
Dart_Handle result =
ThrowIfError(Dart_InvokeClosure(callback, 1, &x509_object));
bool c_result = Dart_IsBoolean(result) && DartUtils::GetBooleanValue(result);
Dart_ExitScope();
return c_result ? SECSuccess : SECFailure;
}
Dart_Handle SSLFilter::PeerCertificate() {
CERTCertificate* certificate = SSL_PeerCertificate(filter_);
if (certificate == NULL) return Dart_Null();
Dart_Handle x509_object = X509FromCertificate(certificate);
CERT_DestroyCertificate(certificate);
return x509_object;
}
void SSLFilter::Connect(const char* host_name,
RawAddr* raw_addr,
int port,
bool is_server,
const char* certificate_name,
bool request_client_certificate,
bool require_client_certificate,
bool send_client_certificate) {
is_server_ = is_server;
if (in_handshake_) {
ThrowException("Connect called while already in handshake state.");
}
if (!is_server && certificate_name != NULL) {
client_certificate_name_ = strdup(certificate_name);
}
filter_ = SSL_ImportFD(NULL, filter_);
if (filter_ == NULL) {
ThrowPRException("Failed SSL_ImportFD call");
}
SECStatus status;
if (is_server) {
PK11_SetPasswordFunc(PasswordCallback);
CERTCertificate* certificate = NULL;
if (strstr(certificate_name, "CN=") != NULL) {
// Look up certificate using the distinguished name (DN) certificate_name.
CERTCertDBHandle* certificate_database = CERT_GetDefaultCertDB();
if (certificate_database == NULL) {
ThrowPRException("Certificate database cannot be loaded");
}
certificate = CERT_FindCertByNameString(certificate_database,
const_cast<char*>(certificate_name));
if (certificate == NULL) {
ThrowPRException(
"Cannot find server certificate by distinguished name");
}
} else {
// Look up certificate using the nickname certificate_name.
certificate = PK11_FindCertFromNickname(
const_cast<char*>(certificate_name),
static_cast<void*>(const_cast<char*>(password_)));
if (certificate == NULL) {
ThrowPRException("Cannot find server certificate by nickname");
}
}
SECKEYPrivateKey* key = PK11_FindKeyByAnyCert(
certificate,
static_cast<void*>(const_cast<char*>(password_)));
if (key == NULL) {
CERT_DestroyCertificate(certificate);
if (PR_GetError() == -8177) {
ThrowPRException("Certificate database password incorrect");
} else {
ThrowPRException("Failed PK11_FindKeyByAnyCert call."
" Cannot find private key for certificate");
}
}
// 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);
CERT_DestroyCertificate(certificate);
SECKEY_DestroyPrivateKey(key);
if (status != SECSuccess) {
ThrowPRException("Failed SSL_ConfigSecureServer call");
}
if (request_client_certificate) {
status = SSL_OptionSet(filter_, SSL_REQUEST_CERTIFICATE, PR_TRUE);
if (status != SECSuccess) {
ThrowPRException("Failed SSL_OptionSet(REQUEST_CERTIFICATE) call");
}
PRBool require_cert = require_client_certificate ? PR_TRUE : PR_FALSE;
status = SSL_OptionSet(filter_, SSL_REQUIRE_CERTIFICATE, require_cert);
if (status != SECSuccess) {
ThrowPRException("Failed SSL_OptionSet(REQUIRE_CERTIFICATE) call");
}
}
} else { // Client.
if (SSL_SetURL(filter_, host_name) == -1) {
ThrowPRException("Failed SetURL call");
}
// This disables the SSL session cache for client connections.
// This resolves issue 7208, but degrades performance.
// TODO(7230): Reenable session cache, without breaking client connections.
status = SSL_OptionSet(filter_, SSL_NO_CACHE, PR_TRUE);
if (status != SECSuccess) {
ThrowPRException("Failed SSL_OptionSet(NO_CACHE) call");
}
if (send_client_certificate) {
status = SSL_GetClientAuthDataHook(
filter_,
NSS_GetClientAuthData,
static_cast<void*>(client_certificate_name_));
if (status != SECSuccess) {
ThrowPRException("Failed SSL_GetClientAuthDataHook call");
}
}
}
// Install bad certificate callback, and pass 'this' to it if it is called.
status = SSL_BadCertHook(filter_,
BadCertificateCallback,
static_cast<void*>(this));
PRBool as_server = is_server ? PR_TRUE : PR_FALSE;
status = SSL_ResetHandshake(filter_, as_server);
if (status != SECSuccess) {
ThrowPRException("Failed SSL_ResetHandshake call");
}
// Set the peer address from the address passed. The DNS has already
// been done in Dart code, so just use that address. This relies on
// following about PRNetAddr: "The raw member of the union is
// equivalent to struct sockaddr", which is stated in the NSS
// documentation.
PRNetAddr peername;
memset(&peername, 0, sizeof(peername));
intptr_t len = SocketAddress::GetAddrLength(*raw_addr);
ASSERT(static_cast<size_t>(len) <= sizeof(peername));
memmove(&peername, &raw_addr->addr, len);
// Adjust the address family field for BSD, whose sockaddr
// structure has a one-byte length and one-byte address family
// field at the beginning. PRNetAddr has a two-byte address
// family field at the beginning.
peername.raw.family = raw_addr->addr.sa_family;
memio_SetPeerName(filter_, &peername);
}
void SSLFilter::Handshake() {
SECStatus status = SSL_ForceHandshake(filter_);
if (status == SECSuccess) {
if (in_handshake_) {
ThrowIfError(Dart_InvokeClosure(
Dart_HandleFromPersistent(handshake_complete_), 0, NULL));
in_handshake_ = false;
}
} else {
PRErrorCode error = PR_GetError();
if (error == PR_WOULD_BLOCK_ERROR) {
if (!in_handshake_) {
in_handshake_ = true;
}
} else {
if (is_server_) {
ThrowPRException("Unexpected handshake error in server");
} else {
ThrowPRException("Unexpected handshake error in client");
}
}
}
}
void SSLFilter::Destroy() {
for (int i = 0; i < kNumBuffers; ++i) {
Dart_DeletePersistentHandle(dart_buffer_objects_[i]);
delete[] buffers_[i];
}
Dart_DeletePersistentHandle(string_start_);
Dart_DeletePersistentHandle(string_length_);
Dart_DeletePersistentHandle(handshake_complete_);
Dart_DeletePersistentHandle(bad_certificate_callback_);
free(client_certificate_name_);
PR_Close(filter_);
}
intptr_t SSLFilter::ProcessBuffer(int buffer_index) {
int size = isEncrypted(buffer_index) ? encrypted_buffer_size_ : buffer_size_;
Dart_Handle buffer_object =
Dart_HandleFromPersistent(dart_buffer_objects_[buffer_index]);
Dart_Handle start_object = ThrowIfError(
Dart_GetField(buffer_object, Dart_HandleFromPersistent(string_start_)));
Dart_Handle length_object = ThrowIfError(
Dart_GetField(buffer_object, Dart_HandleFromPersistent(string_length_)));
int64_t unsafe_start = DartUtils::GetIntegerValue(start_object);
int64_t unsafe_length = DartUtils::GetIntegerValue(length_object);
ASSERT(unsafe_start >= 0);
ASSERT(unsafe_start < size);
ASSERT(unsafe_length >= 0);
ASSERT(unsafe_length <= size);
int start = static_cast<int>(unsafe_start);
int length = static_cast<int>(unsafe_length);
uint8_t* buffer = buffers_[buffer_index];
int bytes_processed = 0;
switch (buffer_index) {
case kReadPlaintext: {
int bytes_free = size - start - length;
bytes_processed = PR_Read(filter_,
buffer + start + length,
bytes_free);
if (bytes_processed < 0) {
ASSERT(bytes_processed == -1);
// TODO(whesse): Handle unexpected errors here.
PRErrorCode pr_error = PR_GetError();
if (PR_WOULD_BLOCK_ERROR != pr_error) {
ThrowPRException("Error reading plaintext from SSLFilter");
}
bytes_processed = 0;
}
break;
}
case kWriteEncrypted: {
const uint8_t* buf1;
const uint8_t* buf2;
unsigned int len1;
unsigned int len2;
int bytes_free = size - start - length;
memio_Private* secret = memio_GetSecret(filter_);
memio_GetWriteParams(secret, &buf1, &len1, &buf2, &len2);
int bytes_to_send =
dart::Utils::Minimum(len1, static_cast<unsigned>(bytes_free));
if (bytes_to_send > 0) {
memmove(buffer + start + length, buf1, bytes_to_send);
bytes_processed = bytes_to_send;
}
bytes_to_send = dart::Utils::Minimum(len2,
static_cast<unsigned>(bytes_free - bytes_processed));
if (bytes_to_send > 0) {
memmove(buffer + start + length + bytes_processed, buf2,
bytes_to_send);
bytes_processed += bytes_to_send;
}
if (bytes_processed > 0) {
memio_PutWriteResult(secret, bytes_processed);
}
break;
}
case kReadEncrypted: {
if (length > 0) {
bytes_processed = length;
memio_Private* secret = memio_GetSecret(filter_);
uint8_t* filter_buf;
int free_bytes = memio_GetReadParams(secret, &filter_buf);
if (free_bytes < bytes_processed) bytes_processed = free_bytes;
memmove(filter_buf,
buffer + start,
bytes_processed);
memio_PutReadResult(secret, bytes_processed);
}
break;
}
case kWritePlaintext: {
if (length > 0) {
bytes_processed = PR_Write(filter_,
buffer + start,
length);
}
if (bytes_processed < 0) {
ASSERT(bytes_processed == -1);
// TODO(whesse): Handle unexpected errors here.
PRErrorCode pr_error = PR_GetError();
if (PR_WOULD_BLOCK_ERROR != pr_error) {
ThrowPRException("Error reading plaintext from SSLFilter");
}
bytes_processed = 0;
}
break;
}
}
return bytes_processed;
}
} // namespace bin
} // namespace dart