Remote protocol now over SSL

This commit is contained in:
emeric
2014-06-25 17:19:50 +02:00
parent 47a12b220f
commit acb2a207d9
6 changed files with 121 additions and 68 deletions
+10
View File
@@ -37,6 +37,16 @@ AC_CHECK_LIB([avformat],
, ,
[AC_MSG_ERROR([libavformat not found!])]) [AC_MSG_ERROR([libavformat not found!])])
AC_CHECK_LIB([ssl],
[SSL_CTX_new],
,
[AC_MSG_ERROR([libssl not found!])])
AC_CHECK_LIB([crypto],
[ASN1_STRING_to_UTF8],
,
[AC_MSG_ERROR([libcrypto not found!])])
AC_CHECK_LIB([boost_system], AC_CHECK_LIB([boost_system],
[main], [main],
, ,
+43 -12
View File
@@ -15,26 +15,49 @@
namespace Remote { namespace Remote {
namespace Server { namespace Server {
Connection::Connection(boost::asio::ip::tcp::socket socket, Connection::Connection(boost::asio::io_service& ioService,
boost::asio::ssl::context& context,
ConnectionManager& manager, ConnectionManager& manager,
RequestHandler& handler) RequestHandler& handler)
: _closing(false), : _closing(false),
_socket(std::move(socket)), _socket(ioService, context),
_connectionManager(manager), _connectionManager(manager),
_requestHandler(handler) _requestHandler(handler)
{ {
std::cout << "Server::Connection::Connection, Creating connection" << std::endl; std::cout << "Server::Connection::Connection, Creating connection" << std::endl;
} }
boost::asio::ip::tcp::socket&
Connection::socket()
{
return _socket;
}
void void
Connection::start() Connection::start()
{ {
std::cout << "Starting connection..." << std::endl;
_socket.async_handshake(boost::asio::ssl::stream_base::server,
boost::bind(&Connection::handleHandshake, this,
boost::asio::placeholders::error));
}
void
Connection::handleHandshake(const boost::system::error_code& error)
{
if (!error)
{
std::cout << "Handshake successfully performed... Now reading messages" << std::endl;
readMsg();
}
else if (error != boost::asio::error::operation_aborted)
{
std::cerr << "Connection::handleHandshake: " << error.message() << std::endl;
_connectionManager.stop(shared_from_this());
}
else
std::cerr << "Handshake error: " << error.message() << std::endl;
}
void
Connection::readMsg()
{
// Read a header first
boost::asio::streambuf::mutable_buffers_type bufs = _inputStreamBuf.prepare(Remote::Header::size); boost::asio::streambuf::mutable_buffers_type bufs = _inputStreamBuf.prepare(Remote::Header::size);
boost::asio::async_read(_socket, boost::asio::async_read(_socket,
@@ -50,13 +73,19 @@ Connection::stop()
{ {
if (!_closing) if (!_closing)
{ {
boost::system::error_code ec;
_closing = true; _closing = true;
std::cout << "Server::Connection::stop, Stopping connection " << this << std::endl; std::cout << "Server::Connection::stop, Stopping connection " << this << std::endl;
_socket.close(); _socket.shutdown(ec);
if (ec)
std::cerr << "Error while shutting down connection " << this << ": " << ec.message() << std::endl;
std::cout << "Server::Connection::stop, connection stopped " << this << std::endl; std::cout << "Server::Connection::stop, connection stopped " << this << std::endl;
} }
else else
std::cout << "Close in progress..." << std::endl; std::cout << "Stop: close already in progress..." << std::endl;
} }
void void
@@ -83,7 +112,7 @@ Connection::handleReadHeader(const boost::system::error_code& error, std::size_t
return; return;
} }
// Now read the real message // Now read the real message payload
boost::asio::streambuf::mutable_buffers_type bufs = _inputStreamBuf.prepare(header.getDataSize()); boost::asio::streambuf::mutable_buffers_type bufs = _inputStreamBuf.prepare(header.getDataSize());
boost::asio::async_read(_socket, boost::asio::async_read(_socket,
@@ -178,7 +207,9 @@ Connection::handleReadMsg(const boost::system::error_code& error, std::size_t by
_connectionManager.stop(shared_from_this()); _connectionManager.stop(shared_from_this());
} }
} }
start();
// All good here, read another message
readMsg();
// Initiate graceful Connection closure. // Initiate graceful Connection closure.
// boost::system::error_code ignored_ec; // boost::system::error_code ignored_ec;
+12 -3
View File
@@ -5,6 +5,7 @@
#include <memory> #include <memory>
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include "RequestHandler.hpp" #include "RequestHandler.hpp"
@@ -20,16 +21,18 @@ class Connection : public std::enable_shared_from_this<Connection>
{ {
public: public:
typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> ssl_socket;
Connection(const Connection&) = delete; Connection(const Connection&) = delete;
Connection& operator=(const Connection&) = delete; Connection& operator=(const Connection&) = delete;
typedef std::shared_ptr<Connection> pointer; typedef std::shared_ptr<Connection> pointer;
/// Construct a connection with the given io_service. /// Construct a connection with the given io_service.
explicit Connection(boost::asio::ip::tcp::socket socket, explicit Connection(boost::asio::io_service& ioService, boost::asio::ssl::context& context,
ConnectionManager& manager, RequestHandler& handler); ConnectionManager& manager, RequestHandler& handler);
boost::asio::ip::tcp::socket& socket(); ssl_socket::lowest_layer_type& getSocket() {return _socket.lowest_layer();}
/// Start the first asynchronous operation for the connection. /// Start the first asynchronous operation for the connection.
void start(); void start();
@@ -40,6 +43,12 @@ class Connection : public std::enable_shared_from_this<Connection>
private: private:
bool _closing; bool _closing;
/// Read a new message on the the connection
void readMsg();
/// Handle completion of ssl handshake
void handleHandshake(const boost::system::error_code& error);
/// Handle completion of a read operation. /// Handle completion of a read operation.
void handleReadHeader(const boost::system::error_code& e, void handleReadHeader(const boost::system::error_code& e,
std::size_t bytes_transferred); std::size_t bytes_transferred);
@@ -48,7 +57,7 @@ class Connection : public std::enable_shared_from_this<Connection>
std::size_t bytes_transferred); std::size_t bytes_transferred);
/// Socket for the connection. /// Socket for the connection.
boost::asio::ip::tcp::socket _socket; ssl_socket _socket;
/// The manager for this connection. /// The manager for this connection.
ConnectionManager& _connectionManager; ConnectionManager& _connectionManager;
+20 -4
View File
@@ -1,6 +1,7 @@
#include <utility> #include <utility>
#include <boost/asio/placeholders.hpp>
#include <boost/bind.hpp> #include <boost/bind.hpp>
#include "Server.hpp" #include "Server.hpp"
@@ -13,9 +14,18 @@ Server::Server(boost::asio::io_service& ioService, const endpoint_type& bindEndp
_ioService(ioService), _ioService(ioService),
_acceptor(_ioService, bindEndpoint, true /*SO_REUSEADDR*/), _acceptor(_ioService, bindEndpoint, true /*SO_REUSEADDR*/),
_connectionManager(), _connectionManager(),
_socket(_ioService), _context(boost::asio::ssl::context::tlsv1_server),
_requestHandler(dbPath) _requestHandler(dbPath)
{ {
_context.set_options( boost::asio::ssl::context::default_workarounds // TODO check this thing
| boost::asio::ssl::context::single_dh_use
| boost::asio::ssl::context::no_sslv2
| boost::asio::ssl::context::no_sslv3
);
// context_.set_password_callback(boost::bind(&server::get_password, this));
_context.use_certificate_chain_file("cert.pem"); // TODO parametrize
_context.use_private_key_file("privkey.pem", boost::asio::ssl::context::pem); // TODO parametrize
_context.use_tmp_dh_file("dh2048.pem"); // TODO parametrize
} }
void void
@@ -30,12 +40,16 @@ Server::run()
void void
Server::asyncAccept() Server::asyncAccept()
{ {
_acceptor.async_accept(_socket, boost::bind(&Server::handleAccept, this, boost::asio::placeholders::error)); std::shared_ptr<Connection> newConnection = std::make_shared<Connection>(_ioService, _context, _connectionManager, _requestHandler);
_acceptor.async_accept(newConnection->getSocket(),
boost::bind(&Server::handleAccept, this, newConnection, boost::asio::placeholders::error));
} }
void void
Server::handleAccept(boost::system::error_code ec) Server::handleAccept(std::shared_ptr<Connection> newConnection, boost::system::error_code ec)
{ {
std::cout << "Server::handleAccept..." << std::endl;
// Check whether the server was stopped before this // Check whether the server was stopped before this
// completion handler had a chance to run. // completion handler had a chance to run.
if (!_acceptor.is_open()) if (!_acceptor.is_open())
@@ -45,8 +59,10 @@ Server::handleAccept(boost::system::error_code ec)
if (!ec) if (!ec)
{ {
_connectionManager.start(std::make_shared<Connection>(std::move(_socket), _connectionManager, _requestHandler)); _connectionManager.start(newConnection);
// Accept another connection
// TODO: add some limit?
asyncAccept(); asyncAccept();
} }
else else
+4 -3
View File
@@ -2,6 +2,8 @@
#define REMOTE_SERVER_HPP #define REMOTE_SERVER_HPP
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include "Connection.hpp" #include "Connection.hpp"
@@ -32,7 +34,7 @@ class Server
private: private:
/// Perform an asynchronous accept operation. /// Perform an asynchronous accept operation.
void asyncAccept(); void asyncAccept();
void handleAccept(boost::system::error_code ec); void handleAccept(std::shared_ptr<Connection> newConnection, boost::system::error_code ec);
boost::asio::io_service& _ioService; boost::asio::io_service& _ioService;
@@ -42,8 +44,7 @@ class Server
/// The connection manager which owns all live connections. /// The connection manager which owns all live connections.
ConnectionManager _connectionManager; ConnectionManager _connectionManager;
/// The next socket to be accepted. boost::asio::ssl::context _context;
boost::asio::ip::tcp::socket _socket;
/// The handler for all incoming requests. /// The handler for all incoming requests.
RequestHandler _requestHandler; RequestHandler _requestHandler;
+29 -43
View File
@@ -11,8 +11,6 @@
#include "remote/messages/Header.hpp" #include "remote/messages/Header.hpp"
#include "remote/messages/messages.pb.h" #include "remote/messages/messages.pb.h"
#include "av/Common.hpp"
#include "TestDatabase.hpp" #include "TestDatabase.hpp"
struct GenreInfo struct GenreInfo
@@ -80,44 +78,22 @@ struct Cover
}; };
// Ugly class for testing purposes // Ugly class for testing purposes
class TestServer
{
public:
TestServer(boost::asio::ip::tcp::endpoint endpoint)
: _db(TestDatabase::create()),
_server(_ioService, endpoint, _db->getPath()),
_thread( boost::bind(&TestServer::threadEntry, this))
{
_server.run();
}
~TestServer()
{
_server.stop();
_thread.join();
}
private:
void threadEntry(void)
{
_ioService.run();
}
boost::asio::io_service _ioService;
std::unique_ptr<DatabaseHandler> _db;
Remote::Server::Server _server;
std::thread _thread;
};
class TestClient class TestClient
{ {
public: public:
TestClient(boost::asio::ip::tcp::endpoint endpoint) TestClient(boost::asio::ip::tcp::endpoint endpoint)
:_socket(_ioService) : _context(boost::asio::ssl::context::sslv23), // be large on this
_socket(_ioService, _context)
{ {
_socket.connect(endpoint); boost::asio::ip::tcp::resolver resolver(_ioService);
_socket.set_verify_mode(boost::asio::ssl::verify_peer);
_socket.set_verify_callback(boost::bind(&TestClient::verifyCertificate, this, _1, _2));
boost::asio::connect(_socket.lowest_layer(), resolver.resolve(endpoint));
_socket.handshake(boost::asio::ssl::stream_base::client);
} }
void getArtists(std::vector<ArtistInfo>& artists) void getArtists(std::vector<ArtistInfo>& artists)
@@ -433,6 +409,19 @@ class TestClient
private: private:
bool verifyCertificate(bool preverified, boost::asio::ssl::verify_context& ctx)
{
// In this example we will simply print the certificate's subject name.
std::array<char, 256> subject_name;
X509* cert = X509_STORE_CTX_get_current_cert(ctx.native_handle());
X509_NAME_oneline(X509_get_subject_name(cert), subject_name.data(), subject_name.size());
std::cout << "Verifying '" << std::string(subject_name.data()) << "', preverified = " << std::boolalpha << preverified << std::endl;
return true; // preverified;
}
void mediaAudioPrepare(uint64_t audioId) void mediaAudioPrepare(uint64_t audioId)
{ {
@@ -646,8 +635,11 @@ class TestClient
} }
} }
typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> ssl_socket;
boost::asio::io_service _ioService; boost::asio::io_service _ioService;
boost::asio::ip::tcp::socket _socket; boost::asio::ssl::context _context;
ssl_socket _socket;
boost::asio::streambuf _inputStreamBuf; boost::asio::streambuf _inputStreamBuf;
boost::asio::streambuf _outputStreamBuf; boost::asio::streambuf _outputStreamBuf;
@@ -658,19 +650,13 @@ class TestClient
int main() int main()
{ {
try { try {
// lib init
Av::AvInit();
Transcode::AvConvTranscoder::init();
bool extendedTests = true; bool extendedTests = true;
// Runs in its own thread std::cout << "Running test... extendedTests = " << std::boolalpha << extendedTests << std::endl;
// Listen on any
TestServer testServer( boost::asio::ip::tcp::endpoint( boost::asio::ip::tcp::v4(), 5081));
// Client // Client
// connect to loopback // connect to loopback
TestClient client( boost::asio::ip::tcp::endpoint( boost::asio::ip::address_v4::loopback(), 5081)); TestClient client( boost::asio::ip::tcp::endpoint( boost::asio::ip::address_v4::loopback(), 5080));
// ****** Artists ********* // ****** Artists *********
std::vector<ArtistInfo> artists; std::vector<ArtistInfo> artists;