blob: 14052c2ee4e8d8000ca8b15301480f16261e55de [file] [log] [blame]
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
#include "ProtocolFactory.h"
#include "qpid/Plugin.h"
#include "qpid/sys/ssl/check.h"
#include "qpid/sys/ssl/util.h"
#include "qpid/sys/ssl/SslHandler.h"
#include "qpid/sys/ssl/SslIo.h"
#include "qpid/sys/ssl/SslSocket.h"
#include "qpid/broker/Broker.h"
#include "qpid/log/Statement.h"
#include <boost/bind.hpp>
#include <memory>
namespace qpid {
namespace sys {
struct SslServerOptions : ssl::SslOptions
{
uint16_t port;
bool clientAuth;
SslServerOptions() : port(5671),
clientAuth(false)
{
addOptions()
("ssl-port", optValue(port, "PORT"), "Port on which to listen for SSL connections")
("ssl-require-client-authentication", optValue(clientAuth),
"Forces clients to authenticate in order to establish an SSL connection");
}
};
class SslProtocolFactory : public ProtocolFactory {
const bool tcpNoDelay;
qpid::sys::ssl::SslSocket listener;
const uint16_t listeningPort;
std::auto_ptr<qpid::sys::ssl::SslAcceptor> acceptor;
public:
SslProtocolFactory(const SslServerOptions&, int backlog, bool nodelay);
void accept(Poller::shared_ptr, ConnectionCodec::Factory*);
void connect(Poller::shared_ptr, const std::string& host, int16_t port,
ConnectionCodec::Factory*,
boost::function2<void, int, std::string> failed);
uint16_t getPort() const;
std::string getHost() const;
bool supports(const std::string& capability);
private:
void established(Poller::shared_ptr, const qpid::sys::ssl::SslSocket&, ConnectionCodec::Factory*,
bool isClient);
};
// Static instance to initialise plugin
static struct SslPlugin : public Plugin {
SslServerOptions options;
Options* getOptions() { return &options; }
~SslPlugin() { ssl::shutdownNSS(); }
void earlyInitialize(Target&) {
}
void initialize(Target& target) {
broker::Broker* broker = dynamic_cast<broker::Broker*>(&target);
// Only provide to a Broker
if (broker) {
if (options.certDbPath.empty()) {
QPID_LOG(warning, "SSL plugin not enabled, you must set --qpid-ssl-cert-db to enable it.");
} else {
try {
ssl::initNSS(options, true);
const broker::Broker::Options& opts = broker->getOptions();
ProtocolFactory::shared_ptr protocol(new SslProtocolFactory(options,
opts.connectionBacklog, opts.tcpNoDelay));
QPID_LOG(notice, "Listening for SSL connections on TCP port " << protocol->getPort());
broker->registerProtocolFactory("ssl", protocol);
} catch (const std::exception& e) {
QPID_LOG(error, "Failed to initialise SSL plugin: " << e.what());
}
}
}
}
} sslPlugin;
SslProtocolFactory::SslProtocolFactory(const SslServerOptions& options, int backlog, bool nodelay) :
tcpNoDelay(nodelay), listeningPort(listener.listen(options.port, backlog, options.certName, options.clientAuth))
{}
void SslProtocolFactory::established(Poller::shared_ptr poller, const qpid::sys::ssl::SslSocket& s,
ConnectionCodec::Factory* f, bool isClient) {
qpid::sys::ssl::SslHandler* async = new qpid::sys::ssl::SslHandler(s.getPeerAddress(), f);
if (tcpNoDelay) {
s.setTcpNoDelay(tcpNoDelay);
QPID_LOG(info, "Set TCP_NODELAY on connection to " << s.getPeerAddress());
}
if (isClient)
async->setClient();
qpid::sys::ssl::SslIO* aio = new qpid::sys::ssl::SslIO(s,
boost::bind(&qpid::sys::ssl::SslHandler::readbuff, async, _1, _2),
boost::bind(&qpid::sys::ssl::SslHandler::eof, async, _1),
boost::bind(&qpid::sys::ssl::SslHandler::disconnect, async, _1),
boost::bind(&qpid::sys::ssl::SslHandler::closedSocket, async, _1, _2),
boost::bind(&qpid::sys::ssl::SslHandler::nobuffs, async, _1),
boost::bind(&qpid::sys::ssl::SslHandler::idle, async, _1));
async->init(aio, 4);
aio->start(poller);
}
uint16_t SslProtocolFactory::getPort() const {
return listeningPort; // Immutable no need for lock.
}
std::string SslProtocolFactory::getHost() const {
return listener.getSockname();
}
void SslProtocolFactory::accept(Poller::shared_ptr poller,
ConnectionCodec::Factory* fact) {
acceptor.reset(
new qpid::sys::ssl::SslAcceptor(listener,
boost::bind(&SslProtocolFactory::established, this, poller, _1, fact, false)));
acceptor->start(poller);
}
void SslProtocolFactory::connect(
Poller::shared_ptr poller,
const std::string& host, int16_t port,
ConnectionCodec::Factory* fact,
ConnectFailedCallback failed)
{
// Note that the following logic does not cause a memory leak.
// The allocated Socket is freed either by the SslConnector
// upon connection failure or by the SslIoHandle upon connection
// shutdown. The allocated SslConnector frees itself when it
// is no longer needed.
qpid::sys::ssl::SslSocket* socket = new qpid::sys::ssl::SslSocket();
new qpid::sys::ssl::SslConnector (*socket, poller, host, port,
boost::bind(&SslProtocolFactory::established, this, poller, _1, fact, true),
failed);
}
namespace
{
const std::string SSL = "ssl";
}
bool SslProtocolFactory::supports(const std::string& capability)
{
std::string s = capability;
transform(s.begin(), s.end(), s.begin(), tolower);
return s == SSL;
}
}} // namespace qpid::sys