DRDE/ACTK1_0/session/TLSSession.cpp

556 lines
14 KiB
C++

#include "session/TLSSession.hpp"
#include "se/CLTRID.hpp"
#include "se/PollRequestCommand.hpp"
#include "se/LoginCommand.hpp"
#include "se/LogoutCommand.hpp"
#include "se/ReceiveSE.hpp"
#include "se/Response.hpp"
#include "se/ResultCode.hpp"
#include "common/ErrorPkg.hpp"
#include "session/CertificateUserMismatchException.hpp"
#include "session/UserPassMismatchException.hpp"
#include "session/SessionLimitExceededException.hpp"
#include "session/UninitialisedSessionException.hpp"
#include "session/GreetingException.hpp"
#include "session/TLSContext.hpp"
#include "session/TLSSocket.hpp"
#include "common/Logger.hpp"
#include "session/Timer.hpp"
#include "xml/ParsingException.hpp"
#include "common/AutoMutex.hpp"
#include "session/SessionProperties.hpp"
#include "se/Greeting.hpp"
#include <xalanc/XalanDOM/XalanText.hpp>
#include <sys/time.h>
#include <errno.h>
using namespace std;
using namespace xalanc;
namespace {
PollRequestCommand& poll()
{
static PollRequestCommand poll;
return poll;
}
const string& pollXML()
{
static const string str(poll().toXML());
return str;
}
}
TLSSession::TLSSession(SessionProperties* props)
throw (SessionConfigurationException)
: _inUse(false), _isOpen(false), _isInvalid(true), commandCounter(NULL)
{
pthread_mutex_init(&mtx, NULL);
pthread_cond_init(&cond, NULL);
configure(props);
const string pname("biz.meansofproduction.dnseppus.session");
debugLogger = Logger::getLogger(pname + ".debug");
supportLogger = Logger::getLogger(pname + ".support");
userLogger = Logger::getLogger(pname + ".user");
parser = auto_ptr<XMLParser>(new XMLParser);
}
TLSSession::~TLSSession()
{
pthread_mutex_destroy(&mtx);
if (pthread_cond_destroy(&cond) == EBUSY)
{
try
{
supportLogger->LOG_SEVERE(
"Condition variable for TLSSession destroyed while in use.");
}
catch (...)
{ }
}
// default dtor for TLSSocket is sufficient.
}
void TLSSession::configure(SessionProperties* props)
throw (SessionConfigurationException)
{
hostName = props->getHostname();
port = props->getPort();
username = props->getClientID();
password = props->getClientPW();
eppVersion = props->getVersion();
language = props->getLanguage();
objURIs = props->getObjURIs();
extURIs = props->getExtURIs();
acquireTimeout = props->getAcquireTimeout();
commandCounter.reset(new CommandCounter(props->getCommandLimitInterval()));
validationEnabled = props->enforceStrictValidation();
try
{
ctx = std::auto_ptr<TLSContext>(
new TLSContext(props->getPrivateKeyFilename(),
props->getCertFilename(),
props->getCAFilename(),
props->getPrivateKeyPassphrase()));
}
catch (const EPPException& e)
{
SessionConfigurationException sce("Error during TLSSession::configure.");
sce.causedBy(e);
throw sce;
}
}
void TLSSession::open() throw (SessionOpenException)
{
debugLogger->LOG_FINEST("enter");
_isInvalid = true;
try
{
openSocket();
processGreeting();
login();
_isOpen = true;
_isInvalid = false;
}
catch (EPPException& e)
{
userLogger->LOG_WARNING(e.getMessage());
SessionOpenException seo("Session open failed.");
seo.causedBy(e);
throw seo;
}
debugLogger->LOG_FINEST("exit");
}
void TLSSession::processGreeting() throw (EPPIOException, GreetingException)
{
if (greeting.get() == NULL)
{
greeting = auto_ptr<Greeting>(new Greeting);
}
try
{
read(*greeting.get());
}
catch (EPPException& pe)
{
GreetingException ge("Error while processing greeting.");
ge.causedBy(pe);
throw ge;
}
}
void TLSSession::openSocket()
{
debugLogger->LOG_FINEST("enter");
socket = ctx->createSocket(hostName, port, SO_TIMEOUT);
debugLogger->LOG_FINEST("exit");
}
void TLSSession::login()
throw (LoginException, EPPIOException, SessionLimitExceededException)
{
LoginCommand login(username, password, newPW.get(), eppVersion, language,
objURIs, extURIs);
CLTRID::setClID(username);
writeXML(login.toXML(), login.getCommandType());
mruTime = Timer::now();
Response response;
try
{
read(response);
}
catch (ParsingException &pe)
{
LoginException le("Parse error during login.");
le.causedBy(pe);
throw le;
}
Result result(response.getResults()[0]);
string msg = result.getResultMessage();
switch (result.getResultCode())
{
case ResultCode::SUCCESS:
return;
case ResultCode::CMD_USE_ERR:
userLogger->warning(ErrorPkg::getMessage ("epp.login.fail.loggedin"));
throw LoginException(msg);
case ResultCode::AUTHENT_ERROR_CLOSING:
case ResultCode::AUTHENT_ERR:
{
const string cn = socket->getCertificateCommonName();
if (cn != username)
{
string_vec args;
args.push_back("<<pw>>");
args.push_back("<<clID>>");
string_vec vals;
vals.push_back(password);
vals.push_back(username);
userLogger->severe(
ErrorPkg::getMessage("epp.login.fail.auth.pw", args, vals));
CertificateUserMismatchException certMismatch(username, password);
LoginException le;
le.causedBy(certMismatch);
throw le;
}
else
{
string_vec args;
args.push_back("<<clID>>");
args.push_back("<<cn>>");
string_vec vals;
vals.push_back(username);
vals.push_back(cn);
userLogger->severe(
ErrorPkg::getMessage("epp.login.fail.auth.match",
args, vals));
UserPassMismatchException userPassMis(msg);
LoginException le;
le.causedBy(userPassMis);
throw le;
}
}
break;
case ResultCode::UNIMPL_OBJ_SVC:
{
const XalanNode *node = result.getResultValue();
if (node)
{
msg = c_str(
TranscodeToLocalCodePage(dynamic_cast<const XalanText *>(node)->getData()));
}
userLogger->severe(
ErrorPkg::getMessage("epp.login.fail.unimpl.objsvc", "<<uri>>", msg));
throw LoginException(msg);
}
break;
case ResultCode::PARAM_VAL_SYNTAX_ERR:
throw LoginException(result.getResultMessage());
case ResultCode::SESS_LIM_EXCEEDED_CLOSING:
throw SessionLimitExceededException();
case ResultCode::CMD_FAILED:
case ResultCode::CMD_FAILED_CLOSING:
throw LoginException("Login failed because server is closing");
}
}
void TLSSession::close()
{
// Closing an already closed Session is a no-op.
if (!_isOpen) return;
_isOpen = false;
try
{
logout();
}
catch (const LogoutException &le)
{
debugLogger->LOG_FINE(le.getMessage());
debugLogger->LOG_FINE("net.event.socket_closed");
}
closeSocket();
}
void TLSSession::changePassword(const string& newPassword)
{
newPW = auto_ptr<string>(new string(newPassword));
open();
close();
}
void TLSSession::logout() throw (LogoutException)
{
LogoutCommand logout;
Response response;
try
{
writeXML(logout.toXML(), logout.getCommandType());
read(response);
const vector<Result>& results = response.getResults();
switch (results[0].getResultCode())
{
case ResultCode::SUCCESS:
return;
case ResultCode::CMD_FAILED:
throw LogoutException (results[0].getResultMessage());
}
}
catch (const EPPIOException &ioe)
{
LogoutException le("IO exception during logout operation.");
le.causedBy(ioe);
throw le;
}
catch (const ParsingException &pe)
{
LogoutException le("Parsing error during logout operation.");
le.causedBy(pe);
throw le;
}
}
void TLSSession::closeSocket()
{
socket = auto_ptr<TLSSocket>();
}
string TLSSession::read() throw (EPPIOException)
{
debugLogger->LOG_FINEST ("enter");
int n = readSize();
debugLogger->LOG_FINEST ("PDU size: " + StringUtils::makeString(n));
string data (readData(n));
supportLogger->LOG_INFO(data);
debugLogger->LOG_FINEST("exit");
return data;
}
void TLSSession::read(ReceiveSE& receivedElement) throw (EPPIOException, ParsingException)
{
auto_ptr<XMLDocument> doc(readToDocument());
receivedElement.fromXML(doc.get());
}
XMLDocument* TLSSession::readToDocument()
throw (EPPIOException, ParsingException)
{
string xml = read();
return parser->parse(xml);
}
void TLSSession::writeXML(const string& xml)
throw (EPPIOException, ParsingException)
{
if (validationEnabled)
{
try
{
auto_ptr<XMLDocument> doc(parser->parse(xml));
}
catch (ParsingException& e)
{
userLogger->warning("Outbound service element malformed: " + xml);
userLogger->warning(e.getMessage());
throw e;
}
}
doWrite(xml);
mruTime = Timer::now();
}
void TLSSession::write(Command& command) throw (EPPIOException, ParsingException)
{
return writeXML(command.toXML());
}
void TLSSession::doWrite(const string &xml) throw (EPPIOException)
{
debugLogger->LOG_FINEST("enter");
try
{
if (socket.get() == NULL)
throw EPPIOException("The socket is not initialised.");
writeSize(xml.length());
writeData(xml);
userLogger->info(xml);
}
catch (EPPException& e)
{
_isInvalid = true;
EPPIOException eio("An error occured while writing to the socket.");
eio.causedBy(e);
throw eio;
}
debugLogger->LOG_FINEST("exit");
}
void TLSSession::writeXML(const string& xml, const CommandType* cmdType)
throw (EPPIOException)
{
debugLogger->LOG_FINER("writing command " + cmdType->toString());
doWrite(xml);
incCommandCounter(cmdType);
}
void TLSSession::keepAlive() throw (EPPIOException)
{
debugLogger->LOG_FINEST("enter");
try
{
acquire();
writeXML(pollXML(), poll().getCommandType());
read();
release();
}
catch (EPPTimeoutException& e)
{
userLogger->LOG_WARNING(e.getMessage());
}
catch (EPPInterruptedException &ie)
{
userLogger->LOG_INFO(ie.getMessage());
}
debugLogger->LOG_FINEST("exit");
}
int TLSSession::readSize() throw (EPPIOException)
{
int size = 0;
try
{
size = socket->readInt() - 4;
}
catch(SSLException &e)
{
throw EPPIOException("Error when reading data size from socket.");
}
return size;
}
void TLSSession::writeSize(int size) throw (EPPIOException)
{
try
{
socket->writeInt(size + 4);
}
catch(SSLException &e)
{
throw EPPIOException("Error when writing data size to socket.");
}
}
string TLSSession::readData(int length) throw (EPPIOException)
{
string inputBuffer;
try
{
socket->readFully(inputBuffer, length);
}
catch(SSLException &e)
{
throw EPPIOException("Error when reading data from socket.");
}
return inputBuffer;
}
void TLSSession::writeData(const string &xml) throw (EPPIOException)
{
try
{
socket->writeBytes(xml);
}
catch(SSLException &e)
{
throw EPPIOException("Error when writing data to socket.");
}
}
void TLSSession::incCommandCounter(const CommandType* type)
{
debugLogger->LOG_FINEST("enter");
commandCounter->increment(type);
debugLogger->LOG_FINEST("exit");
}
void TLSSession::incResultCounter(int resultCode)
{
debugLogger->LOG_FINEST("enter");
resultCounter.increment(resultCode);
debugLogger->LOG_FINEST("exit");
}
int TLSSession::getCommandCount()
{
debugLogger->LOG_FINEST("enter & exit");
return commandCounter->getTotal();
}
int TLSSession::getCommandCount(const CommandType* type)
{
debugLogger->LOG_FINEST("enter & exit");
return commandCounter->getCount(type);
}
int TLSSession::getResultCodeCount(int resultCode)
{
debugLogger->LOG_FINEST("enter & exit");
return resultCounter.getValue(resultCode);
}
long TLSSession::getMruInterval() const
{
return Timer::msDiff(mruTime);
}
void TLSSession::acquire() throw (EPPInterruptedException, EPPTimeoutException)
{
debugLogger->LOG_FINEST("enter");
{
AutoMutex lock(&mtx);
while (_inUse)
{
const struct timespec until(Timer::msOffset2abs(acquireTimeout));
switch (pthread_cond_timedwait(&cond, &mtx, &until))
{
case EINTR:
throw EPPInterruptedException(
"Interupted while waiting to acquire a session.");
case ETIMEDOUT:
throw EPPTimeoutException(
"Timed-out while waiting to acquire session");
default:
// Default implies signalled.
break;
}
debugLogger->LOG_FINE(ErrorPkg::getMessage("thread.wakeup.info"));
}
_inUse = true;
}
debugLogger->LOG_FINEST("exit");
}
void TLSSession::release() throw (IllegalStateException)
{
debugLogger->LOG_FINEST("enter");
{
if (!_inUse)
throw IllegalStateException(
"Attempt to release a session that was not in use");
AutoMutex lock(&mtx);
_inUse = false;
pthread_cond_signal(&cond);
}
debugLogger->LOG_FINEST("exit");
}