/* * ConnectionManager.cpp * * Copyright (C) 2008 Matthias Schiffer * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along * with this program. If not, see . */ #include #include "ConnectionManager.h" #include "Application.h" #include #include #include #include #include #include #include "Requests/DaemonStateUpdateRequest.h" #include "RequestHandlers/ConnectionRequestHandlerGroup.h" #include "RequestHandlers/DaemonRequestHandlerGroup.h" #include "RequestHandlers/UserRequestHandlerGroup.h" #include #include #include namespace Mad { namespace Server { bool ConnectionManager::ServerConnection::send(const Net::Packet &packet) { return connection->send(packet); } ConnectionManager::ServerConnection::ServerConnection(Common::Application *application0, boost::shared_ptr connection0) : Common::Connection(application0), application(application0), connection(connection0), type(UNKNOWN), hostInfo(0) { connection->connectSignalReceive(boost::bind(&ServerConnection::receive, this, _1)); } bool ConnectionManager::ServerConnection::isConnected() const { return connection->isConnected(); } bool ConnectionManager::ServerConnection::disconnect() { connection->disconnect(); return true; } boost::shared_ptr ConnectionManager::ServerConnection::authenticate(const std::string &method, const std::string &subMethod, const std::string &user, const std::vector &data, std::vector &response) { if(!isIdentified()) type = CLIENT; authContext = application->getAuthManager()->authenticate(method, subMethod, user, data, response, authContext); return authContext; } /*void* ConnectionManager::ServerConnection::getCertificate(size_t *size) const { const gnutls_datum_t *cert = connection->getCertificate(); *size = cert->size; return cert->data; } void* ConnectionManager::ServerConnection::getPeerCertificate(size_t *size) const { const gnutls_datum_t *cert = connection->getPeerCertificate(); *size = cert->size; return cert->data; }*/ boost::asio::ip::tcp::endpoint ConnectionManager::parseAddress(const std::string &str) throw(Core::Exception) { try { if(str == "*") return boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v6(), DEFAULT_PORT); boost::smatch match; static const boost::regex r1("\\*:(\\d+)", boost::regex_constants::perl); if(boost::regex_match(str, match, r1)) return boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v6(), std::atoi(match[1].str().c_str())); static const boost::regex r2("\\[(.+)\\]:(\\d+)", boost::regex_constants::perl); if(boost::regex_match(str, match, r2)) return boost::asio::ip::tcp::endpoint(boost::asio::ip::address_v6::from_string(match[1].str()), std::atoi(match[2].str().c_str())); static const boost::regex r3("\\[(.+)\\]", boost::regex_constants::perl); if(boost::regex_match(str, match, r3)) return boost::asio::ip::tcp::endpoint(boost::asio::ip::address_v6::from_string(match[1].str()), DEFAULT_PORT); static const boost::regex r4("((?:\\d{1,3}\\.){3}\\d{1,3}):(\\d+)", boost::regex_constants::perl); if(boost::regex_match(str, match, r4)) return boost::asio::ip::tcp::endpoint(boost::asio::ip::address_v4::from_string(match[1].str()), std::atoi(match[2].str().c_str())); static const boost::regex r5("((?:\\d{1,3}\\.){3}\\d{1,3})", boost::regex_constants::perl); if(boost::regex_match(str, match, r5)) return boost::asio::ip::tcp::endpoint(boost::asio::ip::address_v4::from_string(match[1].str()), DEFAULT_PORT); } catch(...) {} throw Core::Exception(Core::Exception::INVALID_INPUT); } void ConnectionManager::updateState(Common::HostInfo *hostInfo, Common::HostInfo::State state) { hostInfo->setState(state); for(std::set >::iterator con = connections.begin(); con != connections.end(); ++con) { if((*con)->getConnectionType() == ServerConnection::CLIENT) { boost::shared_ptr request(new Requests::DaemonStateUpdateRequest(application, hostInfo->getName(), state)); application->getRequestManager()->sendRequest(con->get(), request); } } } bool ConnectionManager::handleConfigEntry(const Core::ConfigEntry &entry, bool handled) { if(handled) return false; if(entry[0].getKey().matches("Listen") && entry[1].empty()) { try { listenerAddresses.push_back(parseAddress(entry[0][0])); } catch(Core::Exception &e) { application->logf(Core::Logger::LOG_WARNING, "ConnectionManager: Invalid listen address '%s'", entry[0][0].c_str()); } return true; } else if(entry[0].getKey().matches("X509TrustFile") && entry[1].empty()) { x509TrustFile = entry[0][0]; return true; } else if(entry[0].getKey().matches("X509CrlFile") && entry[1].empty()) { x509CrlFile = entry[0][0]; return true; } else if(entry[0].getKey().matches("X509CertFile") && entry[1].empty()) { x509CertFile = entry[0][0]; return true; } else if(entry[0].getKey().matches("X509KeyFile") && entry[1].empty()) { x509KeyFile = entry[0][0]; return true; } else if(entry[0].getKey().matches("Daemon")) { if(entry[0].getSize() == 1) { if(entry[1].empty()) { daemonInfo.insert(std::make_pair(entry[0][0], Common::HostInfo(entry[0][0]))); return true; } else if(entry[1].getKey().matches("IpAddress") && entry[2].empty()) { daemonInfo[entry[0][0]].setIP(entry[1][0]); return true; } } } return false; } void ConnectionManager::configFinished() { if(listenerAddresses.empty()) listenerAddresses.push_back(parseAddress("*")); for(std::vector::const_iterator address = listenerAddresses.begin(); address != listenerAddresses.end(); ++address) { try { boost::shared_ptr listener(new Net::Listener(application, x509CertFile, x509KeyFile, *address)); listener->connectSignalNewConnection(boost::bind(&ConnectionManager::handleNewConnection, this, _1)); listeners.push_back(listener); } catch(Core::Exception &e) { // TODO Log error } } } void ConnectionManager::handleNewConnection(boost::shared_ptr con) { boost::shared_ptr connection(new ServerConnection(application, con)); con->connectSignalDisconnected(boost::bind(&ConnectionManager::handleDisconnect, this, boost::weak_ptr(connection))); connections.insert(connection); application->getRequestManager()->registerConnection(connection.get()); con->startReceive(); } void ConnectionManager::handleDisconnect(boost::weak_ptr con) { boost::shared_ptr connection = con.lock(); if(!connection) return; if(connection->getHostInfo()) updateState(connection->getHostInfo(), Common::HostInfo::INACTIVE); connections.erase(connection); application->getRequestManager()->unregisterConnection(connection.get()); } ConnectionManager::ConnectionManager(Application *application0) : application(application0), connectionRequestHandlerGroup(new RequestHandlers::ConnectionRequestHandlerGroup(application)), daemonRequestHandlerGroup(new RequestHandlers::DaemonRequestHandlerGroup), userRequestHandlerGroup(new RequestHandlers::UserRequestHandlerGroup(application)) { application->getRequestManager()->registerPacketType("FSInfo"); application->getRequestManager()->registerPacketType("GetStatus"); application->getRequestManager()->registerRequestHandlerGroup(connectionRequestHandlerGroup); application->getRequestManager()->registerRequestHandlerGroup(daemonRequestHandlerGroup); application->getRequestManager()->registerRequestHandlerGroup(userRequestHandlerGroup); application->getConfigManager()->registerConfigurable(this); } ConnectionManager::~ConnectionManager() { application->getConfigManager()->unregisterConfigurable(this); connections.clear(); application->getRequestManager()->unregisterRequestHandlerGroup(userRequestHandlerGroup); application->getRequestManager()->unregisterRequestHandlerGroup(daemonRequestHandlerGroup); application->getRequestManager()->unregisterRequestHandlerGroup(connectionRequestHandlerGroup); application->getRequestManager()->unregisterPacketType("FSInfo"); application->getRequestManager()->unregisterPacketType("GetStatus"); } boost::shared_ptr ConnectionManager::getDaemonConnection(const std::string &name) const throw (Core::Exception) { std::map::const_iterator hostIt = daemonInfo.find(name); if(hostIt == daemonInfo.end()) throw Core::Exception(Core::Exception::UNKNOWN_DAEMON); const Common::HostInfo *hostInfo = &hostIt->second; if(hostInfo->getState() != Common::HostInfo::INACTIVE) { for(std::set >::const_iterator it = connections.begin(); it != connections.end(); ++it) { if((*it)->getHostInfo() == hostInfo) { return *it; } } } throw Core::Exception(Core::Exception::NOT_AVAILABLE); } std::string ConnectionManager::getDaemonName(const Common::Connection *con) const throw (Core::Exception) { const ServerConnection *connection = dynamic_cast(con); if(connection && connection->getConnectionType() == ServerConnection::DAEMON) return connection->getHostInfo()->getName(); throw Core::Exception(Core::Exception::UNKNOWN_DAEMON); } void ConnectionManager::identifyDaemonConnection(Common::Connection *con, const std::string &name) throw (Core::Exception) { // TODO Logging ServerConnection *connection = dynamic_cast(con); if(!connection) throw Core::Exception(Core::Exception::INVALID_INPUT); if(connection->isIdentified()) throw Core::Exception(Core::Exception::ALREADY_IDENTIFIED); if(daemonInfo.count(name) == 0) throw Core::Exception(Core::Exception::UNKNOWN_DAEMON); Common::HostInfo *hostInfo = &daemonInfo[name]; if(hostInfo->getState() != Common::HostInfo::INACTIVE) { try { getDaemonConnection(name)->disconnect(); application->log(Core::Logger::LOG_WARNING, "Disconnecting old connection."); } catch(Core::Exception&) {} } connection->identify(hostInfo); updateState(hostInfo, Common::HostInfo::RUNNING); } boost::shared_ptr ConnectionManager::authenticateConnection(Common::Connection *con, const std::string &method, const std::string &subMethod, const std::string &user, const std::vector &data, std::vector &response) { // TODO Logging ServerConnection *connection = dynamic_cast(con); if(!connection) throw Core::Exception(Core::Exception::INVALID_INPUT); if(!connection->isIdentified()) connection->identify(); return connection->authenticate(method, subMethod, user, data, response); } std::vector ConnectionManager::getDaemonList() const { std::vector ret; for(std::map::const_iterator daemon = daemonInfo.begin(); daemon != daemonInfo.end(); ++daemon) { ret.push_back(daemon->second); } return ret; } } }