diff --git a/src/common/api/LowLevelAPISession.h b/src/common/api/LowLevelAPISession.h new file mode 100644 index 0000000..6d8e8b4 --- /dev/null +++ b/src/common/api/LowLevelAPISession.h @@ -0,0 +1,37 @@ +/* + * You may redistribute this program and/or modify it under the terms of + * the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#ifndef APIPROCESSOR_H_ +#define APIPROCESSOR_H_ + +#include "APIMessage.pb.h" + +namespace p2pnet { +namespace api { + +/** + * APIProcessor is an interface that is used to have just `process(APIMessage message)` fuinction. + * Its meaning is 'something that could process API requests'. + */ +class LowLevelAPISession { +public: + virtual ~LowLevelAPISession(){}; + + virtual void process(APIMessage message) = 0; + virtual void shutdown() = 0; // This signal is generated if remote endpoint is disconnected. +}; + +} /* namespace api */ +} /* namespace p2pnet */ + +#endif /* APIPROCESSOR_H_ */ diff --git a/src/common/api/UnixAPISocket.cpp b/src/common/api/UnixAPISocket.cpp new file mode 100644 index 0000000..e73e2db --- /dev/null +++ b/src/common/api/UnixAPISocket.cpp @@ -0,0 +1,89 @@ +/* + * You may redistribute this program and/or modify it under the terms of + * the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "UnixAPISocket.h" +#include "../Config.h" + +namespace p2pnet { +namespace api { +namespace unix { + +std::string getSocketPath() { + return ConfigManager::getInstance()->getValue("api.unix.system_sock"); +} + +std::string getFallbackSocketPath() { + return ConfigManager::getInstance()->getDirectory()+"unix_api.sock"; +} + +/* UnixSocket */ +UnixAPISocket::UnixAPISocket(boost::asio::io_service& io_service, LowLevelAPISession* session) : session_socket(io_service) { + session_ptr = session; +} +UnixAPISocket::~UnixAPISocket() {} + +stream_protocol::socket& UnixAPISocket::getSocket() { + return session_socket; +} + +void UnixAPISocket::startReceive() { + char* buffer = new char[MESSAGE_SIZE_LENGTH]; + boost::asio::async_read(session_socket, boost::asio::buffer(buffer, MESSAGE_SIZE_LENGTH), + std::bind(&UnixAPISocket::handleReceiveSize, this, std::placeholders::_1, buffer)); +} + +void UnixAPISocket::handleReceiveSize(const boost::system::error_code& error, char* char_message_size) { + if(error){ + session_ptr->shutdown(); + return; + } + MESSAGE_SIZE_TYPE size = ntohl(*reinterpret_cast(char_message_size)); + delete[] char_message_size; + + char* buffer = new char[size]; + boost::asio::async_read(session_socket, boost::asio::buffer(buffer, size), + std::bind(&UnixAPISocket::handleReceive, this, std::placeholders::_1, buffer, std::placeholders::_2)); +} + +void UnixAPISocket::handleReceive(const boost::system::error_code& error, char* message, uint32_t size) { + if(error){ + session_ptr->shutdown(); + return; + } + APIMessage message_proto; + message_proto.ParseFromArray(message, size); + delete[] message; + session_ptr->process(message_proto); + startReceive(); +} + +void UnixAPISocket::send(APIMessage message) { + auto native_size = message.ByteSize(); + MESSAGE_SIZE_TYPE size_netlong = htonl(native_size); + + std::cout << size_netlong; + + auto charlen_s = std::string(reinterpret_cast(&size_netlong), MESSAGE_SIZE_LENGTH); + boost::asio::async_write(session_socket, boost::asio::buffer(charlen_s+message.SerializeAsString()), + std::bind(&UnixAPISocket::handleSend, this, std::placeholders::_1)); +} + +void UnixAPISocket::handleSend(const boost::system::error_code& error) { + if(error){ + session_ptr->shutdown(); + } +} + +} /* namespace unix */ +} /* namespace api */ +} /* namespace p2pnet */ diff --git a/src/common/api/UnixAPISocket.h b/src/common/api/UnixAPISocket.h new file mode 100644 index 0000000..013faf9 --- /dev/null +++ b/src/common/api/UnixAPISocket.h @@ -0,0 +1,55 @@ +/* + * You may redistribute this program and/or modify it under the terms of + * the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#ifndef UNIXAPISOCKET_H_ +#define UNIXAPISOCKET_H_ + +#include +#include "LowLevelAPISession.h" + +#define MESSAGE_SIZE_TYPE uint32_t +#define MESSAGE_SIZE_LENGTH sizeof(MESSAGE_SIZE_TYPE) + +using boost::asio::local::stream_protocol; + +namespace p2pnet { +namespace api { +namespace unix { + +std::string getSocketPath(); +std::string getFallbackSocketPath(); + +class UnixAPISocket { + stream_protocol::socket session_socket; + LowLevelAPISession* session_ptr; + +public: + UnixAPISocket(boost::asio::io_service& io_service, LowLevelAPISession* session); + virtual ~UnixAPISocket(); + + stream_protocol::socket& getSocket(); + + void startReceive(); + void handleReceiveSize(const boost::system::error_code& error, char* char_message_size); + void handleReceive(const boost::system::error_code& error, char* message, uint32_t size); + + void handleSend(const boost::system::error_code& error); + + void send(APIMessage message); +}; + +} /* namespace unix */ +} /* namespace api */ +} /* namespace p2pnet */ + +#endif /* UNIXAPISOCKET_H_ */ diff --git a/src/daemon/Daemon.cpp b/src/daemon/Daemon.cpp index 1d756cf..bab38aa 100644 --- a/src/daemon/Daemon.cpp +++ b/src/daemon/Daemon.cpp @@ -42,7 +42,7 @@ int Daemon::run() { void Daemon::initAPI() { if(config_manager->getValue("api.unix.enabled")){ try { - api_unix = std::unique_ptr(new api::UnixAPIServer(AsioIOService::getIOService())); + api_unix = std::make_shared(AsioIOService::getIOService()); } catch (boost::system::system_error& e) { log() << "Unable to initialize Unix Socket API. Exception caught: " << e.what() << std::endl; } diff --git a/src/daemon/Daemon.h b/src/daemon/Daemon.h index 354d801..5c6b198 100644 --- a/src/daemon/Daemon.h +++ b/src/daemon/Daemon.h @@ -48,7 +48,7 @@ class Daemon : public Loggable { std::unique_ptr discovery_udpv4; std::unique_ptr discovery_udpv6; - std::unique_ptr api_unix; + std::shared_ptr api_unix; Daemon(); virtual ~Daemon(); diff --git a/src/daemon/api/APISession.cpp b/src/daemon/api/APISession.cpp index 708e0e5..21165d0 100644 --- a/src/daemon/api/APISession.cpp +++ b/src/daemon/api/APISession.cpp @@ -19,22 +19,21 @@ namespace p2pnet { namespace api { APISession::APISession() { - log() << "New API session started."; + log() << "New API session started" << std::endl; +} +APISession::~APISession() { + log() << "API session shut down" << std::endl; } -APISession::~APISession() {} - -void APISession::process(std::string message) { - APIMessage message_received; - message_received.ParseFromString(message); - switch(message_received.type()){ +void APISession::process(APIMessage message) { + switch(message.type()){ case APIMessage::GENERATE_PRIVATE_KEY: auto privkey = crypto::PrivateKeyDSA::generateNewKey(); APIMessage message_reply; message_reply.set_type(APIMessage::GENERATE_PRIVATE_KEY_CALLBACK); message_reply.set_privkey_cert(privkey.toPEM()); - send(message_reply.SerializeAsString()); + send(message_reply); } } diff --git a/src/daemon/api/APISession.h b/src/daemon/api/APISession.h index 9361cce..6f8651b 100644 --- a/src/daemon/api/APISession.h +++ b/src/daemon/api/APISession.h @@ -15,18 +15,21 @@ #define APISESSION_H_ #include "../endpoint/LocalEndpoint.h" +#include "../../common/api/LowLevelAPISession.h" namespace p2pnet { namespace api { -class APISession : Loggable { +class APISession : Loggable, public LowLevelAPISession { std::map> endpoints; public: APISession(); virtual ~APISession(); - virtual void send(std::string message) = 0; - void process(std::string message); + virtual void send(APIMessage message) = 0; + void process(APIMessage message); + + std::string getComponentName(){return "APISession";} }; } /* namespace api */ diff --git a/src/daemon/api/UnixAPIServer.cpp b/src/daemon/api/UnixAPIServer.cpp index be82a6f..00da43f 100644 --- a/src/daemon/api/UnixAPIServer.cpp +++ b/src/daemon/api/UnixAPIServer.cpp @@ -13,63 +13,40 @@ */ #include "UnixAPIServer.h" #include +#include namespace p2pnet { namespace api { +namespace unix { /* UnixAPISession */ -UnixAPISession::UnixAPISession(boost::asio::io_service& io_service) : session_socket(io_service) { +UnixAPISession::UnixAPISession(boost::asio::io_service& io_service, UnixAPIServer* parent) : socket(io_service, this) { + parent_apiserver = parent; } UnixAPISession::~UnixAPISession() { } -stream_protocol::socket& UnixAPISession::getSocket() { - return session_socket; +void UnixAPISession::send(APIMessage message) { + socket.send(message); } -void UnixAPISession::startReceive() { - char* buffer = new char[4]; - boost::asio::async_read(session_socket, boost::asio::buffer(buffer, 4), - std::bind(&UnixAPISession::handleReceiveSize, this, buffer)); +void UnixAPISession::shutdown(){ + parent_apiserver->unix_sessions.erase(shared_from_this()); } -void UnixAPISession::handleReceiveSize(char* char_message_size) { - MESSAGE_SIZE_TYPE size = ntohl(*reinterpret_cast(char_message_size)); - delete[] char_message_size; - - char* buffer = new char[size]; - boost::asio::async_read(session_socket, boost::asio::buffer(buffer, MESSAGE_SIZE_LENGTH), - std::bind(&UnixAPISession::handleReceive, this, buffer, std::placeholders::_2)); -} - -void UnixAPISession::handleReceive(char* message, uint32_t size) { - std::string message_s(message, size); - delete[] message; - process(message_s); -} - -void UnixAPISession::send(std::string message) { - MESSAGE_SIZE_TYPE size_netlong = htonl(message.length()); - auto charlen_s = std::string(reinterpret_cast(&size_netlong), MESSAGE_SIZE_LENGTH); - boost::asio::async_write(session_socket, boost::asio::buffer(charlen_s+message), - std::bind(&UnixAPISession::handleSend, this)); -} - -void UnixAPISession::handleSend() {} - /* UnixAPIServer */ UnixAPIServer::UnixAPIServer(boost::asio::io_service& io_service) : asio_io_service(io_service) { - socket_path = getSocketPath(); - try { - // Try default path (i.e. that, from config: api.unix.system_sock) + socket_path = getSocketPath(); // Try default path (i.e. that, from config: api.unix.system_sock) + std::remove(socket_path.c_str()); acceptor_ptr = std::unique_ptr(new stream_protocol::acceptor(asio_io_service, stream_protocol::endpoint(socket_path))); } catch(boost::system::system_error& e){ socket_path = getFallbackSocketPath(); + std::remove(socket_path.c_str()); acceptor_ptr = std::unique_ptr(new stream_protocol::acceptor(asio_io_service, stream_protocol::endpoint(socket_path))); } log() << "Unix API initialized at: " << socket_path << std::endl; @@ -79,23 +56,16 @@ UnixAPIServer::UnixAPIServer(boost::asio::io_service& io_service) : UnixAPIServer::~UnixAPIServer() {} void UnixAPIServer::accept() { - auto new_session = std::make_shared(asio_io_service); + auto new_session = std::make_shared(asio_io_service, this); unix_sessions.insert(new_session); - acceptor_ptr->async_accept(new_session->getSocket(), std::bind(&UnixAPISession::startReceive, new_session)); + acceptor_ptr->async_accept(new_session->getUnixSocket().getSocket(), std::bind(&UnixAPIServer::handleAccept, this, new_session)); } void UnixAPIServer::handleAccept(std::shared_ptr new_session) { - new_session->startReceive(); + new_session->getUnixSocket().startReceive(); accept(); } -std::string UnixAPIServer::getSocketPath() const { - return getValue("api.unix.system_sock"); -} - -std::string UnixAPIServer::getFallbackSocketPath() const { - return ConfigManager::getInstance()->getDirectory()+"unix_api.sock"; -} - +} /* namespace unix */ } /* namespace api */ } /* namespace p2pnet */ diff --git a/src/daemon/api/UnixAPIServer.h b/src/daemon/api/UnixAPIServer.h index 47142ed..5ae7c1e 100644 --- a/src/daemon/api/UnixAPIServer.h +++ b/src/daemon/api/UnixAPIServer.h @@ -18,6 +18,10 @@ #include "APIServer.h" #include "../../common/Config.h" +#include "../../common/api/UnixAPISocket.h" + +#include "../../library/export.h" + #include #include @@ -28,25 +32,31 @@ using boost::asio::local::stream_protocol; namespace p2pnet { namespace api { +namespace unix { -class UnixAPISession : public APISession { - stream_protocol::socket session_socket; +class UnixAPIServer; +class UnixAPISession : public APISession, public std::enable_shared_from_this { + UnixAPISocket socket; + UnixAPIServer* parent_apiserver; public: - UnixAPISession(boost::asio::io_service& io_service); + UnixAPISession(boost::asio::io_service& io_service, UnixAPIServer* parent); virtual ~UnixAPISession(); - stream_protocol::socket& getSocket(); + void send(APIMessage message); + //void process(APIMessage message); <--- Inherited from APISession. + + void shutdown(); // Careful! Acts like `delete this;` - void startReceive(); - void handleReceiveSize(char* char_message_size); - void handleReceive(char* message, uint32_t size); + UnixAPISocket& getUnixSocket(){return socket;} - void send(std::string message); - void handleSend(); + std::string getComponentName() const { + return "UnixAPISession"; + } }; class UnixAPIServer : public APIServer, ConfigClient, Loggable { + friend class UnixAPISession; std::set> unix_sessions; std::unique_ptr acceptor_ptr; @@ -66,10 +76,12 @@ class UnixAPIServer : public APIServer, ConfigClient, Loggable { void accept(); void handleAccept(std::shared_ptr new_session); - std::string getSocketPath() const; - std::string getFallbackSocketPath() const; + std::string getComponentName() { + return "UnixAPIServer"; + } }; +} /* namespace unix */ } /* namespace api */ } /* namespace p2pnet */ diff --git a/src/library/UnixAPI.cpp b/src/library/UnixAPI.cpp new file mode 100644 index 0000000..39bcf61 --- /dev/null +++ b/src/library/UnixAPI.cpp @@ -0,0 +1,43 @@ +/* + * You may redistribute this program and/or modify it under the terms of + * the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "UnixAPI.h" +#include "../common/api/APIMessage.pb.h" +#include "export.h" + +namespace p2pnet { +namespace api { + +UnixAPI::UnixAPI(boost::asio::io_service& io_service) : UnixAPISocket(io_service, this) {} + +UnixAPI::~UnixAPI() {} + +void UnixAPI::process(APIMessage message) {} + +void UnixAPI::connect() { + try { + socket_path = unix::getSocketPath(); + getSocket().connect(stream_protocol::endpoint(socket_path)); + } catch (boost::system::system_error& e) { + socket_path = unix::getFallbackSocketPath(); + getSocket().connect(stream_protocol::endpoint(socket_path)); + } // TODO: Yep, uncatched for now. + log() << "Connected to daemon on: " << socket_path << std::endl; +} + +void UnixAPI::shutdown(){ + getSocket().shutdown(getSocket().shutdown_both); +}; + +} /* namespace api */ +} /* namespace p2pnet */ diff --git a/src/library/UnixAPI.h b/src/library/UnixAPI.h new file mode 100644 index 0000000..9beed5f --- /dev/null +++ b/src/library/UnixAPI.h @@ -0,0 +1,40 @@ +/* + * You may redistribute this program and/or modify it under the terms of + * the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#ifndef UNIXAPI_H_ +#define UNIXAPI_H_ + +#include "../common/Loggable.h" +#include "../common/api/UnixAPISocket.h" +#include "../common/api/LowLevelAPISession.h" + +namespace p2pnet { +namespace api { + +class UnixAPI : public unix::UnixAPISocket, public LowLevelAPISession, Loggable { + std::string socket_path; +public: + UnixAPI(boost::asio::io_service& io_service); + virtual ~UnixAPI(); + + void process(APIMessage message); + + void shutdown(); + + void connect(); +}; + +} /* namespace api */ +} /* namespace p2pnet */ + +#endif /* UNIXAPI_H_ */