-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
R=juberti@webrtc.org Review URL: https://webrtc-codereview.appspot.com/17309004 Patch from Manish Jethani <manish.jethani@gmail.com>. git-svn-id: http://webrtc.googlecode.com/svn/trunk@7269 4adac7df-926f-26a2-2b94-8c16560cd09d
- Loading branch information
Showing
6 changed files
with
366 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,342 @@ | ||
/* | ||
* Copyright 2014 The WebRTC Project Authors. All rights reserved. | ||
* | ||
* Use of this source code is governed by a BSD-style license | ||
* that can be found in the LICENSE file in the root of the source | ||
* tree. An additional intellectual property rights grant can be found | ||
* in the file PATENTS. All contributing project authors may | ||
* be found in the AUTHORS file in the root of the source tree. | ||
*/ | ||
|
||
#include <string> | ||
|
||
#include "webrtc/base/gunit.h" | ||
#include "webrtc/base/ipaddress.h" | ||
#include "webrtc/base/socketstream.h" | ||
#include "webrtc/base/ssladapter.h" | ||
#include "webrtc/base/sslstreamadapter.h" | ||
#include "webrtc/base/stream.h" | ||
#include "webrtc/base/virtualsocketserver.h" | ||
|
||
static const int kTimeout = 5000; | ||
|
||
static rtc::AsyncSocket* CreateSocket(const rtc::SSLMode& ssl_mode) { | ||
rtc::SocketAddress address(rtc::IPAddress(INADDR_ANY), 0); | ||
|
||
rtc::AsyncSocket* socket = rtc::Thread::Current()-> | ||
socketserver()->CreateAsyncSocket( | ||
address.family(), (ssl_mode == rtc::SSL_MODE_DTLS) ? | ||
SOCK_DGRAM : SOCK_STREAM); | ||
socket->Bind(address); | ||
|
||
return socket; | ||
} | ||
|
||
static std::string GetSSLProtocolName(const rtc::SSLMode& ssl_mode) { | ||
return (ssl_mode == rtc::SSL_MODE_DTLS) ? "DTLS" : "TLS"; | ||
} | ||
|
||
class SSLAdapterTestDummyClient : public sigslot::has_slots<> { | ||
public: | ||
explicit SSLAdapterTestDummyClient(const rtc::SSLMode& ssl_mode) | ||
: ssl_mode_(ssl_mode) { | ||
rtc::AsyncSocket* socket = CreateSocket(ssl_mode_); | ||
|
||
ssl_adapter_.reset(rtc::SSLAdapter::Create(socket)); | ||
|
||
// Ignore any certificate errors for the purpose of testing. | ||
// Note: We do this only because we don't have a real certificate. | ||
// NEVER USE THIS IN PRODUCTION CODE! | ||
ssl_adapter_->set_ignore_bad_cert(true); | ||
|
||
ssl_adapter_->SignalReadEvent.connect(this, | ||
&SSLAdapterTestDummyClient::OnSSLAdapterReadEvent); | ||
ssl_adapter_->SignalCloseEvent.connect(this, | ||
&SSLAdapterTestDummyClient::OnSSLAdapterCloseEvent); | ||
} | ||
|
||
rtc::AsyncSocket::ConnState GetState() const { | ||
return ssl_adapter_->GetState(); | ||
} | ||
|
||
const std::string& GetReceivedData() const { | ||
return data_; | ||
} | ||
|
||
int Connect(const std::string& hostname, const rtc::SocketAddress& address) { | ||
LOG(LS_INFO) << "Starting " << GetSSLProtocolName(ssl_mode_) | ||
<< " handshake with " << hostname; | ||
|
||
if (ssl_adapter_->StartSSL(hostname.c_str(), false) != 0) { | ||
return -1; | ||
} | ||
|
||
LOG(LS_INFO) << "Initiating connection with " << address; | ||
|
||
return ssl_adapter_->Connect(address); | ||
} | ||
|
||
int Close() { | ||
return ssl_adapter_->Close(); | ||
} | ||
|
||
int Send(const std::string& message) { | ||
LOG(LS_INFO) << "Client sending '" << message << "'"; | ||
|
||
return ssl_adapter_->Send(message.data(), message.length()); | ||
} | ||
|
||
void OnSSLAdapterReadEvent(rtc::AsyncSocket* socket) { | ||
char buffer[4096] = ""; | ||
|
||
// Read data received from the server and store it in our internal buffer. | ||
int read = socket->Recv(buffer, sizeof(buffer) - 1); | ||
if (read != -1) { | ||
buffer[read] = '\0'; | ||
|
||
LOG(LS_INFO) << "Client received '" << buffer << "'"; | ||
|
||
data_ += buffer; | ||
} | ||
} | ||
|
||
void OnSSLAdapterCloseEvent(rtc::AsyncSocket* socket, int error) { | ||
// OpenSSLAdapter signals handshake failure with a close event, but without | ||
// closing the socket! Let's close the socket here. This way GetState() can | ||
// return CS_CLOSED after failure. | ||
if (socket->GetState() != rtc::AsyncSocket::CS_CLOSED) { | ||
socket->Close(); | ||
} | ||
} | ||
|
||
private: | ||
const rtc::SSLMode ssl_mode_; | ||
|
||
rtc::scoped_ptr<rtc::SSLAdapter> ssl_adapter_; | ||
|
||
std::string data_; | ||
}; | ||
|
||
class SSLAdapterTestDummyServer : public sigslot::has_slots<> { | ||
public: | ||
explicit SSLAdapterTestDummyServer(const rtc::SSLMode& ssl_mode) | ||
: ssl_mode_(ssl_mode) { | ||
// Generate a key pair and a certificate for this host. | ||
ssl_identity_.reset(rtc::SSLIdentity::Generate(GetHostname())); | ||
|
||
server_socket_.reset(CreateSocket(ssl_mode_)); | ||
|
||
server_socket_->SignalReadEvent.connect(this, | ||
&SSLAdapterTestDummyServer::OnServerSocketReadEvent); | ||
|
||
server_socket_->Listen(1); | ||
|
||
LOG(LS_INFO) << ((ssl_mode_ == rtc::SSL_MODE_DTLS) ? "UDP" : "TCP") | ||
<< " server listening on " << server_socket_->GetLocalAddress(); | ||
} | ||
|
||
rtc::SocketAddress GetAddress() const { | ||
return server_socket_->GetLocalAddress(); | ||
} | ||
|
||
std::string GetHostname() const { | ||
// Since we don't have a real certificate anyway, the value here doesn't | ||
// really matter. | ||
return "example.com"; | ||
} | ||
|
||
const std::string& GetReceivedData() const { | ||
return data_; | ||
} | ||
|
||
int Send(const std::string& message) { | ||
if (ssl_stream_adapter_ == NULL | ||
|| ssl_stream_adapter_->GetState() != rtc::SS_OPEN) { | ||
// No connection yet. | ||
return -1; | ||
} | ||
|
||
LOG(LS_INFO) << "Server sending '" << message << "'"; | ||
|
||
size_t written; | ||
int error; | ||
|
||
rtc::StreamResult r = ssl_stream_adapter_->Write(message.data(), | ||
message.length(), &written, &error); | ||
if (r == rtc::SR_SUCCESS) { | ||
return written; | ||
} else { | ||
return -1; | ||
} | ||
} | ||
|
||
void OnServerSocketReadEvent(rtc::AsyncSocket* socket) { | ||
if (ssl_stream_adapter_ != NULL) { | ||
// Only a single connection is supported. | ||
return; | ||
} | ||
|
||
rtc::SocketAddress address; | ||
rtc::AsyncSocket* new_socket = socket->Accept(&address); | ||
rtc::SocketStream* stream = new rtc::SocketStream(new_socket); | ||
|
||
ssl_stream_adapter_.reset(rtc::SSLStreamAdapter::Create(stream)); | ||
ssl_stream_adapter_->SetServerRole(); | ||
|
||
// SSLStreamAdapter is normally used for peer-to-peer communication, but | ||
// here we're testing communication between a client and a server | ||
// (e.g. a WebRTC-based application and an RFC 5766 TURN server), where | ||
// clients are not required to provide a certificate during handshake. | ||
// Accordingly, we must disable client authentication here. | ||
ssl_stream_adapter_->set_client_auth_enabled(false); | ||
|
||
ssl_stream_adapter_->SetIdentity(ssl_identity_->GetReference()); | ||
|
||
// Set a bogus peer certificate digest. | ||
unsigned char digest[20]; | ||
size_t digest_len = sizeof(digest); | ||
ssl_stream_adapter_->SetPeerCertificateDigest(rtc::DIGEST_SHA_1, digest, | ||
digest_len); | ||
|
||
ssl_stream_adapter_->StartSSLWithPeer(); | ||
|
||
ssl_stream_adapter_->SignalEvent.connect(this, | ||
&SSLAdapterTestDummyServer::OnSSLStreamAdapterEvent); | ||
} | ||
|
||
void OnSSLStreamAdapterEvent(rtc::StreamInterface* stream, int sig, int err) { | ||
if (sig & rtc::SE_READ) { | ||
char buffer[4096] = ""; | ||
|
||
size_t read; | ||
int error; | ||
|
||
// Read data received from the client and store it in our internal | ||
// buffer. | ||
rtc::StreamResult r = stream->Read(buffer, | ||
sizeof(buffer) - 1, &read, &error); | ||
if (r == rtc::SR_SUCCESS) { | ||
buffer[read] = '\0'; | ||
|
||
LOG(LS_INFO) << "Server received '" << buffer << "'"; | ||
|
||
data_ += buffer; | ||
} | ||
} | ||
} | ||
|
||
private: | ||
const rtc::SSLMode ssl_mode_; | ||
|
||
rtc::scoped_ptr<rtc::AsyncSocket> server_socket_; | ||
rtc::scoped_ptr<rtc::SSLStreamAdapter> ssl_stream_adapter_; | ||
|
||
rtc::scoped_ptr<rtc::SSLIdentity> ssl_identity_; | ||
|
||
std::string data_; | ||
}; | ||
|
||
class SSLAdapterTestBase : public testing::Test, | ||
public sigslot::has_slots<> { | ||
public: | ||
explicit SSLAdapterTestBase(const rtc::SSLMode& ssl_mode) | ||
: ssl_mode_(ssl_mode), | ||
ss_scope_(new rtc::VirtualSocketServer(NULL)), | ||
server_(new SSLAdapterTestDummyServer(ssl_mode_)), | ||
client_(new SSLAdapterTestDummyClient(ssl_mode_)), | ||
handshake_wait_(kTimeout) { | ||
} | ||
|
||
static void SetUpTestCase() { | ||
rtc::InitializeSSL(); | ||
} | ||
|
||
static void TearDownTestCase() { | ||
rtc::CleanupSSL(); | ||
} | ||
|
||
void SetHandshakeWait(int wait) { | ||
handshake_wait_ = wait; | ||
} | ||
|
||
void TestHandshake(bool expect_success) { | ||
int rv; | ||
|
||
// The initial state is CS_CLOSED | ||
ASSERT_EQ(rtc::AsyncSocket::CS_CLOSED, client_->GetState()); | ||
|
||
rv = client_->Connect(server_->GetHostname(), server_->GetAddress()); | ||
ASSERT_EQ(0, rv); | ||
|
||
// Now the state should be CS_CONNECTING | ||
ASSERT_EQ(rtc::AsyncSocket::CS_CONNECTING, client_->GetState()); | ||
|
||
if (expect_success) { | ||
// If expecting success, the client should end up in the CS_CONNECTED | ||
// state after handshake. | ||
EXPECT_EQ_WAIT(rtc::AsyncSocket::CS_CONNECTED, client_->GetState(), | ||
handshake_wait_); | ||
|
||
LOG(LS_INFO) << GetSSLProtocolName(ssl_mode_) << " handshake complete."; | ||
|
||
} else { | ||
// On handshake failure the client should end up in the CS_CLOSED state. | ||
EXPECT_EQ_WAIT(rtc::AsyncSocket::CS_CLOSED, client_->GetState(), | ||
handshake_wait_); | ||
|
||
LOG(LS_INFO) << GetSSLProtocolName(ssl_mode_) << " handshake failed."; | ||
} | ||
} | ||
|
||
void TestTransfer(const std::string& message) { | ||
int rv; | ||
|
||
rv = client_->Send(message); | ||
ASSERT_EQ(static_cast<int>(message.length()), rv); | ||
|
||
// The server should have received the client's message. | ||
EXPECT_EQ_WAIT(message, server_->GetReceivedData(), kTimeout); | ||
|
||
rv = server_->Send(message); | ||
ASSERT_EQ(static_cast<int>(message.length()), rv); | ||
|
||
// The client should have received the server's message. | ||
EXPECT_EQ_WAIT(message, client_->GetReceivedData(), kTimeout); | ||
|
||
LOG(LS_INFO) << "Transfer complete."; | ||
} | ||
|
||
private: | ||
const rtc::SSLMode ssl_mode_; | ||
|
||
const rtc::SocketServerScope ss_scope_; | ||
|
||
rtc::scoped_ptr<SSLAdapterTestDummyServer> server_; | ||
rtc::scoped_ptr<SSLAdapterTestDummyClient> client_; | ||
|
||
int handshake_wait_; | ||
}; | ||
|
||
class SSLAdapterTestTLS : public SSLAdapterTestBase { | ||
public: | ||
SSLAdapterTestTLS() : SSLAdapterTestBase(rtc::SSL_MODE_TLS) {} | ||
}; | ||
|
||
|
||
#if SSL_USE_OPENSSL | ||
|
||
// Basic tests: TLS | ||
|
||
// Test that handshake works | ||
TEST_F(SSLAdapterTestTLS, TestTLSConnect) { | ||
TestHandshake(true); | ||
} | ||
|
||
// Test transfer between client and server | ||
TEST_F(SSLAdapterTestTLS, TestTLSTransfer) { | ||
TestHandshake(true); | ||
TestTransfer("Hello, world!"); | ||
} | ||
|
||
#endif // SSL_USE_OPENSSL | ||
|
Oops, something went wrong.