AMQP-CPP/src/linux_tcp/sslshutdown.h

181 lines
5.1 KiB
C
Raw Normal View History

2018-03-06 15:40:44 +08:00
/**
* SslShutdown.h
*
* Class that takes care of the final handshake to close a SSL connection
*
* @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com>
* @copyright 2018 Copernica BV
*/
/**
* Include guard
*/
#pragma once
/**
* Begin of namespace
*/
namespace AMQP {
/**
* Class definition
*/
class SslShutdown : public TcpState, private Watchable
{
private:
/**
* The SSL context
* @var SslWrapper
*/
SslWrapper _ssl;
/**
* Socket file descriptor
* @var int
*/
int _socket;
2018-03-08 19:11:45 +08:00
/**
* Have we already notified user space of connection end?
* @var bool
*/
bool _finalized;
2018-03-06 15:40:44 +08:00
/**
* Proceed with the next operation after the previous operation was
* a success, possibly changing the filedescriptor-monitor
2018-03-08 19:11:45 +08:00
* @param monitor object to check if connection still exists
2018-03-06 15:40:44 +08:00
* @return TcpState*
*/
2018-03-08 19:11:45 +08:00
TcpState *proceed(const Monitor &monitor)
2018-03-06 15:40:44 +08:00
{
// we're no longer interested in events
_handler->monitor(_connection, _socket, 0);
// close the socket
close(_socket);
// forget the socket
_socket = -1;
2018-03-08 19:11:45 +08:00
// if we have already told user space that connection is gone
if (_finalized) return new TcpClosed(this);
// object will be finalized now
_finalized = true;
// inform user space that the party is over
_handler->onClosed(_connection);
// go to the final state (if not yet disconnected)
return monitor.valid() ? new TcpClosed(this) : nullptr;
2018-03-06 15:40:44 +08:00
}
/**
* Method to repeat the previous call
2018-03-08 19:11:45 +08:00
* @param monitor object to check if connection still exists
2018-03-06 15:40:44 +08:00
* @param result result of an earlier openssl operation
* @return TcpState*
*/
2018-03-08 19:11:45 +08:00
TcpState *repeat(const Monitor &monitor, int result)
2018-03-06 15:40:44 +08:00
{
// error was returned, so we must investigate what is going on
2018-03-07 05:03:53 +08:00
auto error = OpenSSL::SSL_get_error(_ssl, result);
2018-03-06 15:40:44 +08:00
// check the error
switch (error) {
case SSL_ERROR_WANT_READ:
// the operation must be repeated when readable
_handler->monitor(_connection, _socket, readable);
return this;
case SSL_ERROR_WANT_WRITE:
// wait until socket becomes writable again
_handler->monitor(_connection, _socket, readable | writable);
return this;
default:
2018-03-08 19:11:45 +08:00
// the shutdown failed, ignore this if user was already notified of an error
if (_finalized) return new TcpClosed(this);
2018-03-06 15:40:44 +08:00
2018-03-08 19:11:45 +08:00
// object will be finalized now
_finalized = true;
// inform user space that the party is over
_handler->onError(_connection, "ssl shutdown error");
// go to the final state (if not yet disconnected)
return monitor.valid() ? new TcpClosed(this) : nullptr;
2018-03-06 15:40:44 +08:00
}
}
public:
/**
* Constructor
* @param connection Parent TCP connection object
* @param socket The socket filedescriptor
* @param ssl The SSL structure
2018-03-08 19:11:45 +08:00
* @param finalized Is the user already notified of connection end (onError() has been called)
2018-03-06 15:40:44 +08:00
* @param handler User-supplied handler object
*/
2018-03-08 19:11:45 +08:00
SslShutdown(TcpConnection *connection, int socket, SslWrapper &&ssl, bool finalized, TcpHandler *handler) :
2018-03-06 15:40:44 +08:00
TcpState(connection, handler),
_ssl(std::move(ssl)),
2018-03-08 19:11:45 +08:00
_socket(socket),
_finalized(finalized)
2018-03-06 15:40:44 +08:00
{
// tell the handler to monitor the socket if there is an out
_handler->monitor(_connection, _socket, readable);
}
/**
* Destructor
*/
virtual ~SslShutdown() noexcept
{
2018-03-08 19:11:45 +08:00
// skip if socket is already gond
if (_socket < 0) return;
2018-03-06 15:40:44 +08:00
// we no longer have to monitor the socket
_handler->monitor(_connection, _socket, 0);
// close the socket
close(_socket);
}
/**
* The filedescriptor of this connection
* @return int
*/
virtual int fileno() const override { return _socket; }
/**
* Process the filedescriptor in the object
2018-03-08 19:11:45 +08:00
* @param monitor Object to check if connection still exists
2018-03-06 15:40:44 +08:00
* @param fd The filedescriptor that is active
* @param flags AMQP::readable and/or AMQP::writable
* @return New implementation object
*/
2018-03-08 19:11:45 +08:00
virtual TcpState *process(const Monitor &monitor, int fd, int flags) override
2018-03-06 15:40:44 +08:00
{
// the socket must be the one this connection writes to
if (fd != _socket) return this;
// close the connection
2018-03-07 05:03:53 +08:00
auto result = OpenSSL::SSL_shutdown(_ssl);
2018-03-06 15:40:44 +08:00
// if this is a success, we can proceed with the event loop
2018-03-08 19:11:45 +08:00
if (result > 0) return proceed(monitor);
2018-03-06 15:40:44 +08:00
// the operation failed, we may have to repeat our call
2018-03-08 19:11:45 +08:00
else return repeat(monitor, result);
2018-03-06 15:40:44 +08:00
}
};
/**
* End of namespace
*/
}