/* * 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 Core::String &method, const Core::String &subMethod, const Core::String &user, const std::vector &data, std::vector &response) { if(!isIdentified()) type = CLIENT; authContext = application->getAuthManager()->authenticate(method, subMethod, user, data, response, authContext); if(authContext->isAuthenticated()) connection->setReceiveLimit(0xFFFFFFFF); // 4 Gigs return authContext; } 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); } } } void ConnectionManager::configure() { x509TrustFile = application->getConfigManager()->get("X509TrustFile"); x509CrlFile = application->getConfigManager()->get("X509CrlFile"); x509CertFile = application->getConfigManager()->get("X509CertFile"); x509KeyFile = application->getConfigManager()->get("X509KeyFile"); std::vector listenEntries = application->getConfigManager()->getEntries("Listen"); if(!listenEntries.empty()) { for(std::vector::iterator listenEntry = listenEntries.begin(); listenEntry != listenEntries.end(); ++listenEntry) { try { boost::shared_ptr listener(new Net::Listener(application, x509CertFile, x509KeyFile, parseAddress((*listenEntry)->getValue().toString()))); listener->connectSignalNewConnection(boost::bind(&ConnectionManager::handleNewConnection, this, _1)); listeners.push_back(listener); } catch(Core::Exception &e) { application->logf(Core::Logger::LOG_WARNING, "ConnectionManager: Invalid listen address '%s'", (*listenEntry)->getValue().toLocale().c_str()); } } } else { boost::shared_ptr listener(new Net::Listener(application, x509CertFile, x509KeyFile, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v6(), DEFAULT_PORT))); listener->connectSignalNewConnection(boost::bind(&ConnectionManager::handleNewConnection, this, _1)); listeners.push_back(listener); } std::vector daemonEntries = application->getConfigManager()->getEntries("Daemon"); for(std::vector::iterator daemonEntry = daemonEntries.begin(); daemonEntry != daemonEntries.end(); ++daemonEntry) { Core::String name = (*daemonEntry)->getValue(); if(name.isEmpty()) continue; Common::HostInfo daemon(name); daemon.setIP((*daemonEntry)->get("IpAddress")); daemonInfo.insert(std::make_pair(name, daemon)); } } 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 Core::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); } Core::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 Core::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 Core::String &method, const Core::String &subMethod, const Core::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; } } }