Skip to content

Commit

Permalink
Added working (!) simple implementation of Unix API. Tested, works fine.
Browse files Browse the repository at this point in the history
  • Loading branch information
GamePad64 committed Jan 10, 2014
1 parent f523619 commit 2fb6728
Show file tree
Hide file tree
Showing 11 changed files with 317 additions and 69 deletions.
37 changes: 37 additions & 0 deletions 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 <http://www.gnu.org/licenses/>.
*/
#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_ */
89 changes: 89 additions & 0 deletions 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 <http://www.gnu.org/licenses/>.
*/
#include "UnixAPISocket.h"
#include "../Config.h"

namespace p2pnet {
namespace api {
namespace unix {

std::string getSocketPath() {
return ConfigManager::getInstance()->getValue<std::string>("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<MESSAGE_SIZE_TYPE*>(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<char*>(&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 */
55 changes: 55 additions & 0 deletions 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 <http://www.gnu.org/licenses/>.
*/
#ifndef UNIXAPISOCKET_H_
#define UNIXAPISOCKET_H_

#include <boost/asio.hpp>
#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_ */
2 changes: 1 addition & 1 deletion src/daemon/Daemon.cpp
Expand Up @@ -42,7 +42,7 @@ int Daemon::run() {
void Daemon::initAPI() {
if(config_manager->getValue<bool>("api.unix.enabled")){
try {
api_unix = std::unique_ptr<api::UnixAPIServer>(new api::UnixAPIServer(AsioIOService::getIOService()));
api_unix = std::make_shared<api::unix::UnixAPIServer>(AsioIOService::getIOService());
} catch (boost::system::system_error& e) {
log() << "Unable to initialize Unix Socket API. Exception caught: " << e.what() << std::endl;
}
Expand Down
2 changes: 1 addition & 1 deletion src/daemon/Daemon.h
Expand Up @@ -48,7 +48,7 @@ class Daemon : public Loggable {
std::unique_ptr<discovery::UDPLPDv4> discovery_udpv4;
std::unique_ptr<discovery::UDPLPDv6> discovery_udpv6;

std::unique_ptr<api::UnixAPIServer> api_unix;
std::shared_ptr<api::unix::UnixAPIServer> api_unix;

Daemon();
virtual ~Daemon();
Expand Down
15 changes: 7 additions & 8 deletions src/daemon/api/APISession.cpp
Expand Up @@ -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);
}
}

Expand Down
9 changes: 6 additions & 3 deletions src/daemon/api/APISession.h
Expand Up @@ -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<uint32_t, std::shared_ptr<endpoint::LocalEndpoint>> 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 */
Expand Down
60 changes: 15 additions & 45 deletions src/daemon/api/UnixAPIServer.cpp
Expand Up @@ -13,63 +13,40 @@
*/
#include "UnixAPIServer.h"
#include <boost/lexical_cast.hpp>
#include <cstdio>

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<MESSAGE_SIZE_TYPE*>(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<char*>(&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<stream_protocol::acceptor>(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<stream_protocol::acceptor>(new stream_protocol::acceptor(asio_io_service, stream_protocol::endpoint(socket_path)));
}
log() << "Unix API initialized at: " << socket_path << std::endl;
Expand All @@ -79,23 +56,16 @@ UnixAPIServer::UnixAPIServer(boost::asio::io_service& io_service) :
UnixAPIServer::~UnixAPIServer() {}

void UnixAPIServer::accept() {
auto new_session = std::make_shared<UnixAPISession>(asio_io_service);
auto new_session = std::make_shared<UnixAPISession>(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<UnixAPISession> new_session) {
new_session->startReceive();
new_session->getUnixSocket().startReceive();
accept();
}

std::string UnixAPIServer::getSocketPath() const {
return getValue<std::string>("api.unix.system_sock");
}

std::string UnixAPIServer::getFallbackSocketPath() const {
return ConfigManager::getInstance()->getDirectory()+"unix_api.sock";
}

} /* namespace unix */
} /* namespace api */
} /* namespace p2pnet */

0 comments on commit 2fb6728

Please sign in to comment.