From e33ac02351e6bfdb8624d73d4f2159ed9aa2cc03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Blin?= Date: Tue, 24 Oct 2017 16:29:24 -0400 Subject: [PATCH 01/22] test restbed --- .gitignore | 5 ++ CMakeLists.txt | 8 ++- include/opendht/dhtproxyserver.h | 37 +++++++++++++ include/opendht/dhtrunner.h | 5 ++ src/dhtproxyserver.cpp | 90 ++++++++++++++++++++++++++++++++ src/dhtrunner.cpp | 7 +++ tools/dhtchat.cpp | 2 + 7 files changed, 152 insertions(+), 2 deletions(-) create mode 100644 include/opendht/dhtproxyserver.h create mode 100644 src/dhtproxyserver.cpp diff --git a/.gitignore b/.gitignore index 9d50e4d5..c79e0d15 100644 --- a/.gitignore +++ b/.gitignore @@ -48,6 +48,11 @@ Makefile.in # Python *.pyc +python/opendht.cpp +python/setup.py + +# Doxygen +doc/Doxyfile # git backup files *.orig diff --git a/CMakeLists.txt b/CMakeLists.txt index 2ea5b834..373a4c3d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -104,6 +104,7 @@ list (APPEND opendht_SOURCES src/node.cpp src/value.cpp src/dht.cpp + src/dhtproxyserver.cpp src/storage.h src/listener.h src/search.h @@ -131,6 +132,7 @@ list (APPEND opendht_HEADERS include/opendht/node.h include/opendht/value.h include/opendht/dht.h + include/opendht/dhtproxyserver.h include/opendht/callbacks.h include/opendht/routing_table.h include/opendht/node_cache.h @@ -177,7 +179,8 @@ if (OPENDHT_STATIC) target_link_libraries(opendht-static ${argon2_LIBRARIES}) target_include_directories(opendht-static SYSTEM PRIVATE ${argon2_INCLUDE_DIRS}) endif () - target_link_libraries(opendht-static ${CMAKE_THREAD_LIBS_INIT} ${GNUTLS_LIBRARIES} ${Nettle_LIBRARIES}) + # TODO clean RESTBED link + target_link_libraries(opendht-static ${CMAKE_THREAD_LIBS_INIT} ${GNUTLS_LIBRARIES} ${Nettle_LIBRARIES} -lrestbed) install (TARGETS opendht-static DESTINATION ${CMAKE_INSTALL_LIBDIR} EXPORT opendht) endif () @@ -198,7 +201,8 @@ if (OPENDHT_SHARED) target_link_libraries(opendht PRIVATE ${argon2_LIBRARIES}) target_include_directories(opendht SYSTEM PRIVATE ${argon2_INCLUDE_DIRS}) endif () - target_link_libraries(opendht PRIVATE ${CMAKE_THREAD_LIBS_INIT} ${GNUTLS_LIBRARIES} ${Nettle_LIBRARIES}) + # TODO clean RESTBED link + target_link_libraries(opendht PRIVATE ${CMAKE_THREAD_LIBS_INIT} ${GNUTLS_LIBRARIES} ${Nettle_LIBRARIES} -lrestbed) install (TARGETS opendht DESTINATION ${CMAKE_INSTALL_LIBDIR} EXPORT opendht) endif () diff --git a/include/opendht/dhtproxyserver.h b/include/opendht/dhtproxyserver.h new file mode 100644 index 00000000..ed5699e5 --- /dev/null +++ b/include/opendht/dhtproxyserver.h @@ -0,0 +1,37 @@ +// TODO gpl + +#pragma once + +#include "def.h" + +#include +#include + +// TODO add ifdef RESTBED +#include + +namespace dht { + +class DhtRunner; + +class OPENDHT_PUBLIC DhtProxyServer +{ +public: + DhtProxyServer(DhtRunner* dht); + virtual ~DhtProxyServer(); + + DhtProxyServer(const DhtProxyServer& other) = default; + DhtProxyServer(DhtProxyServer&& other) = default; + DhtProxyServer& operator=(const DhtProxyServer& other) = default; + DhtProxyServer& operator=(DhtProxyServer&& other) = default; + +private: + void getId(const std::shared_ptr& session) const; + void getNodeId(const std::shared_ptr& session) const; + + std::thread server_thread {}; + std::unique_ptr service_; + DhtRunner* dht_; +}; + +} diff --git a/include/opendht/dhtrunner.h b/include/opendht/dhtrunner.h index fd3346c0..35f14423 100644 --- a/include/opendht/dhtrunner.h +++ b/include/opendht/dhtrunner.h @@ -36,6 +36,8 @@ #include #include +#include "dhtproxyserver.h" + namespace dht { struct Node; @@ -362,6 +364,8 @@ public: */ void join(); + void startProxyInterface(); // TODO arguments + private: static constexpr std::chrono::seconds BOOTSTRAP_PERIOD {10}; @@ -391,6 +395,7 @@ private: std::mutex sock_mtx {}; std::vector> rcv {}; + std::unique_ptr proxy_server_; /** true if currently actively boostraping */ std::atomic_bool bootstraping {false}; /* bootstrap nodes given as (host, service) pairs */ diff --git a/src/dhtproxyserver.cpp b/src/dhtproxyserver.cpp new file mode 100644 index 00000000..ae771bf3 --- /dev/null +++ b/src/dhtproxyserver.cpp @@ -0,0 +1,90 @@ +// TODO GPL + +#include "dhtproxyserver.h" + +#include + +#include "dhtrunner.h" + +namespace dht { + +DhtProxyServer::DhtProxyServer(DhtRunner* dht) : dht_(dht) +{ + // NOTE in c++14, use make_unique + service_ = std::unique_ptr(new restbed::Service()); + + auto resource = std::make_shared(); + resource->set_path("/getId"); + resource->set_method_handler("GET", + [this](const std::shared_ptr session) + { + this->getId(session); + } + ); + service_->publish(resource); + resource = std::make_shared(); + resource->set_path("/getNodeId"); + resource->set_method_handler("GET", + [this](const std::shared_ptr session) + { + this->getNodeId(session); + } + ); + service_->publish(resource); + + server_thread = std::thread([this]() { + auto settings = std::make_shared(); + settings->set_port(1984); // TODO add argument and move this + settings->set_default_header("Connection", "close"); + service_->start(settings); + }); +} + +DhtProxyServer::~DhtProxyServer() +{ + if (server_thread.joinable()) + server_thread.join(); +} + +void +DhtProxyServer::getId(const std::shared_ptr& session) const +{ + const auto request = session->get_request(); + + int content_length = std::stoi(request->get_header("Content-Length", "0")); + + session->fetch(content_length, + [&](const std::shared_ptr s, const restbed::Bytes& b) + { + if (dht_) { + auto id = dht_->getId().toString(); + s->close(restbed::OK, id, {{"Content-Length", std::to_string(id.length())}}); + } else { + s->close(restbed::OK, "", {{"Content-Length", "0"}}); + } + } + ); +} + +void +DhtProxyServer::getNodeId(const std::shared_ptr& session) const +{ + const auto request = session->get_request(); + + int content_length = std::stoi(request->get_header("Content-Length", "0")); + + session->fetch(content_length, + [&](const std::shared_ptr s, const restbed::Bytes& b) + { + if (dht_) { + auto id = dht_->getNodeId().toString(); + s->close(restbed::OK, id, {{"Content-Length", std::to_string(id.length())}}); + } else { + s->close(restbed::OK, "", {{"Content-Length", "0"}}); + } + } + ); +} + + +} diff --git a/src/dhtrunner.cpp b/src/dhtrunner.cpp index 66b4ebbd..16883539 100644 --- a/src/dhtrunner.cpp +++ b/src/dhtrunner.cpp @@ -800,4 +800,11 @@ DhtRunner::findCertificate(InfoHash hash, std::function(new DhtProxyServer(this)); +} + } diff --git a/tools/dhtchat.cpp b/tools/dhtchat.cpp index 8e48723f..b64b4c95 100644 --- a/tools/dhtchat.cpp +++ b/tools/dhtchat.cpp @@ -67,6 +67,8 @@ main(int argc, char **argv) if (not params.bootstrap.first.empty()) dht.bootstrap(params.bootstrap.first.c_str(), params.bootstrap.second.c_str()); + dht.startProxyInterface(); + print_node_info(dht, params); std::cout << " type 'c {hash}' to join a channel" << std::endl << std::endl; From 32153b11ab5f4a3e89fedb9b4b13f1d7dee76a1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Blin?= Date: Wed, 25 Oct 2017 17:32:40 -0400 Subject: [PATCH 02/22] proxy putSigned --- include/opendht/dhtproxyserver.h | 1 + src/dhtproxyserver.cpp | 74 ++++++++++++++++++++++++++++++-- tools/dhtchat.cpp | 14 +++++- 3 files changed, 84 insertions(+), 5 deletions(-) diff --git a/include/opendht/dhtproxyserver.h b/include/opendht/dhtproxyserver.h index ed5699e5..6a05b52a 100644 --- a/include/opendht/dhtproxyserver.h +++ b/include/opendht/dhtproxyserver.h @@ -28,6 +28,7 @@ public: private: void getId(const std::shared_ptr& session) const; void getNodeId(const std::shared_ptr& session) const; + void putSigned(const std::shared_ptr& session) const; std::thread server_thread {}; std::unique_ptr service_; diff --git a/src/dhtproxyserver.cpp b/src/dhtproxyserver.cpp index ae771bf3..02668092 100644 --- a/src/dhtproxyserver.cpp +++ b/src/dhtproxyserver.cpp @@ -4,7 +4,9 @@ #include +#include "default_types.h" #include "dhtrunner.h" +#include "msgpack.hpp" namespace dht { @@ -31,12 +33,31 @@ DhtProxyServer::DhtProxyServer(DhtRunner* dht) : dht_(dht) } ); service_->publish(resource); + resource = std::make_shared(); + resource->set_path("/putSigned"); + resource->set_method_handler("PUT", + [this](const std::shared_ptr session) + { + this->putSigned(session); + } + ); + service_->publish(resource); server_thread = std::thread([this]() { auto settings = std::make_shared(); - settings->set_port(1984); // TODO add argument and move this settings->set_default_header("Connection", "close"); - service_->start(settings); + int port = 1984; + int started = false; + while (!started) + { + try { + settings->set_port(port); // TODO add argument and move this + service_->start(settings); + started = true; + } catch (std::system_error& e) { + port += 1; + } + } }); } @@ -60,7 +81,7 @@ DhtProxyServer::getId(const std::shared_ptr& session) const auto id = dht_->getId().toString(); s->close(restbed::OK, id, {{"Content-Length", std::to_string(id.length())}}); } else { - s->close(restbed::OK, "", {{"Content-Length", "0"}}); + s->close(restbed::NOT_FOUND, "", {{"Content-Length", "0"}}); } } ); @@ -80,7 +101,52 @@ DhtProxyServer::getNodeId(const std::shared_ptr& session) cons auto id = dht_->getNodeId().toString(); s->close(restbed::OK, id, {{"Content-Length", std::to_string(id.length())}}); } else { - s->close(restbed::OK, "", {{"Content-Length", "0"}}); + s->close(restbed::NOT_FOUND, "", {{"Content-Length", "0"}}); + } + } + ); +} + +void +DhtProxyServer::putSigned(const std::shared_ptr& session) const +{ + const auto request = session->get_request(); + + int content_length = std::stoi(request->get_header("Content-Length", "0")); + + session->fetch(content_length, + [&](const std::shared_ptr s, const restbed::Bytes& b) + { + if (dht_) { + if(b.empty()) { + std::string response("Missing parameters"); + s->close(restbed::BAD_REQUEST, response, {{"Content-Length", std::to_string(response.size())}}); + } else { + restbed::Bytes buf(b); + + unsigned char delimiter = static_cast('\n'); + unsigned int idx = 0; + std::vector hashBuf; + while(idx < buf.size() && buf[idx] != delimiter) { + hashBuf.emplace_back(buf[idx]); + ++idx; + } + ++idx; + std::vector valueBuf; + while(idx < buf.size()) { + valueBuf.emplace_back(buf[idx]); + ++idx; + } + InfoHash hash(hashBuf.data(), hashBuf.size()); + msgpack::unpacked msg; + msgpack::unpack(msg, reinterpret_cast(valueBuf.data()), valueBuf.size()); + auto value = std::make_shared(msg.get()); + + dht_->putSigned(hash, value); + s->close(restbed::OK, "", {{"Content-Length", "0"}}); + } + } else { + s->close(restbed::NOT_FOUND, "", {{"Content-Length", "0"}}); } } ); diff --git a/tools/dhtchat.cpp b/tools/dhtchat.cpp index b64b4c95..5e80bef8 100644 --- a/tools/dhtchat.cpp +++ b/tools/dhtchat.cpp @@ -20,6 +20,8 @@ #include "tools_common.h" #include +#include + extern "C" { #include } @@ -131,7 +133,17 @@ main(int argc, char **argv) std::cout << "Message publishing failed !" << std::endl; }); } else { - dht.putSigned(room, dht::ImMessage(rand_id(rd), std::move(line), now), [](bool ok) { + auto value = std::make_shared( dht::ImMessage(rand_id(rd), std::move(line), now)); + + // TODO TEMP + auto valueStr = packMsg(value); + std::cout << std::endl; + for (const auto& v: valueStr) { + std::cout << "\\x" << std::hex << static_cast(v); + } + std::cout << std::endl; + + dht.putSigned(room, value, [](bool ok) { //dht.cancelPut(room, id); if (not ok) std::cout << "Message publishing failed !" << std::endl; From 6f641073ae000e0988a32463ecc4c48a3a4f3b2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Blin?= Date: Thu, 26 Oct 2017 13:52:49 -0400 Subject: [PATCH 03/22] proxy put and putEncrypted --- include/opendht/dhtproxyserver.h | 30 ++++++++ src/dhtproxyserver.cpp | 125 +++++++++++++++++++++++++++++++ 2 files changed, 155 insertions(+) diff --git a/include/opendht/dhtproxyserver.h b/include/opendht/dhtproxyserver.h index 6a05b52a..0021583b 100644 --- a/include/opendht/dhtproxyserver.h +++ b/include/opendht/dhtproxyserver.h @@ -26,9 +26,39 @@ public: DhtProxyServer& operator=(DhtProxyServer&& other) = default; private: + /** + * Method: GET + */ void getId(const std::shared_ptr& session) const; + /** + * Method: GET + */ void getNodeId(const std::shared_ptr& session) const; + /** + * Method: PUT + * Body: vector of unsigned char + * first line: InfoHash hash + * second line: Value value to sign and put + * @param session + */ void putSigned(const std::shared_ptr& session) const; + /** + * Method: PUT + * Body: vector of unsigned char + * first line: InfoHash hash + * second line: InfoHash to + * third line: Value value to encrypt and put + * @param session + */ + void putEncrypted(const std::shared_ptr& session) const; + /** + * Method: PUT + * Body: vector of unsigned char + * first line: InfoHash hash + * second line: Value value to put + * @param session + */ + void put(const std::shared_ptr& session) const; std::thread server_thread {}; std::unique_ptr service_; diff --git a/src/dhtproxyserver.cpp b/src/dhtproxyserver.cpp index 02668092..2a804a56 100644 --- a/src/dhtproxyserver.cpp +++ b/src/dhtproxyserver.cpp @@ -42,6 +42,24 @@ DhtProxyServer::DhtProxyServer(DhtRunner* dht) : dht_(dht) } ); service_->publish(resource); + resource = std::make_shared(); + resource->set_path("/putEncrypted"); + resource->set_method_handler("PUT", + [this](const std::shared_ptr session) + { + this->putEncrypted(session); + } + ); + service_->publish(resource); + resource = std::make_shared(); + resource->set_path("/put"); + resource->set_method_handler("PUT", + [this](const std::shared_ptr session) + { + this->put(session); + } + ); + service_->publish(resource); server_thread = std::thread([this]() { auto settings = std::make_shared(); @@ -125,6 +143,7 @@ DhtProxyServer::putSigned(const std::shared_ptr& session) cons restbed::Bytes buf(b); unsigned char delimiter = static_cast('\n'); + // Retrieve hash unsigned int idx = 0; std::vector hashBuf; while(idx < buf.size() && buf[idx] != delimiter) { @@ -132,11 +151,13 @@ DhtProxyServer::putSigned(const std::shared_ptr& session) cons ++idx; } ++idx; + // Retrieve value std::vector valueBuf; while(idx < buf.size()) { valueBuf.emplace_back(buf[idx]); ++idx; } + // Build parameters InfoHash hash(hashBuf.data(), hashBuf.size()); msgpack::unpacked msg; msgpack::unpack(msg, reinterpret_cast(valueBuf.data()), valueBuf.size()); @@ -152,5 +173,109 @@ DhtProxyServer::putSigned(const std::shared_ptr& session) cons ); } +void +DhtProxyServer::putEncrypted(const std::shared_ptr& session) const +{ + const auto request = session->get_request(); + + int content_length = std::stoi(request->get_header("Content-Length", "0")); + + session->fetch(content_length, + [&](const std::shared_ptr s, const restbed::Bytes& b) + { + if (dht_) { + if(b.empty()) { + std::string response("Missing parameters"); + s->close(restbed::BAD_REQUEST, response, {{"Content-Length", std::to_string(response.size())}}); + } else { + restbed::Bytes buf(b); + + unsigned char delimiter = static_cast('\n'); + // Retrieve hash + unsigned int idx = 0; + std::vector hashBuf; + while(idx < buf.size() && buf[idx] != delimiter) { + hashBuf.emplace_back(buf[idx]); + ++idx; + } + ++idx; + // Retrieve to + std::vector toBuf; + while(idx < buf.size() && buf[idx] != delimiter) { + toBuf.emplace_back(buf[idx]); + ++idx; + } + ++idx; + // Retrieve value + std::vector valueBuf; + while(idx < buf.size()) { + valueBuf.emplace_back(buf[idx]); + ++idx; + } + InfoHash hash(hashBuf.data(), hashBuf.size()); + InfoHash to(toBuf.data(), toBuf.size()); + msgpack::unpacked msg; + msgpack::unpack(msg, reinterpret_cast(valueBuf.data()), valueBuf.size()); + auto value = std::make_shared(msg.get()); + + dht_->putEncrypted(hash, to, value); + s->close(restbed::OK, "", {{"Content-Length", "0"}}); + } + } else { + s->close(restbed::NOT_FOUND, "", {{"Content-Length", "0"}}); + } + } + ); +} + +void +DhtProxyServer::put(const std::shared_ptr& session) const +{ + // TODO test with encrypted and signed value to send + const auto request = session->get_request(); + + int content_length = std::stoi(request->get_header("Content-Length", "0")); + + session->fetch(content_length, + [&](const std::shared_ptr s, const restbed::Bytes& b) + { + if (dht_) { + if(b.empty()) { + std::string response("Missing parameters"); + s->close(restbed::BAD_REQUEST, response, {{"Content-Length", std::to_string(response.size())}}); + } else { + restbed::Bytes buf(b); + + unsigned char delimiter = static_cast('\n'); + // Retrieve hash + unsigned int idx = 0; + std::vector hashBuf; + while(idx < buf.size() && buf[idx] != delimiter) { + hashBuf.emplace_back(buf[idx]); + ++idx; + } + ++idx; + // Retrieve value + std::vector valueBuf; + while(idx < buf.size()) { + valueBuf.emplace_back(buf[idx]); + ++idx; + } + // Build parameters + InfoHash hash(hashBuf.data(), hashBuf.size()); + msgpack::unpacked msg; + msgpack::unpack(msg, reinterpret_cast(valueBuf.data()), valueBuf.size()); + auto value = std::make_shared(msg.get()); + + auto response = value->toString(); + dht_->put(hash, value); + s->close(restbed::OK, response, {{"Content-Length", std::to_string(response.length())}}); + } + } else { + s->close(restbed::NOT_FOUND, "", {{"Content-Length", "0"}}); + } + } + ); +} } From 58fdfa94520139d43ca7f716cbf83bb98e1ec813 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Blin?= Date: Thu, 26 Oct 2017 16:01:45 -0400 Subject: [PATCH 04/22] add get hash endpoint --- include/opendht/dhtproxyserver.h | 5 +++ include/opendht/dhtrunner.h | 6 +++- src/dhtproxyserver.cpp | 62 +++++++++++++++++++++++++------- src/dhtrunner.cpp | 13 +++++++ tools/dhtchat.cpp | 14 +------- 5 files changed, 73 insertions(+), 27 deletions(-) diff --git a/include/opendht/dhtproxyserver.h b/include/opendht/dhtproxyserver.h index 0021583b..fd600341 100644 --- a/include/opendht/dhtproxyserver.h +++ b/include/opendht/dhtproxyserver.h @@ -26,6 +26,11 @@ public: DhtProxyServer& operator=(DhtProxyServer&& other) = default; private: + /** + * Method: GET + * PathParameter: hash + */ + void get(const std::shared_ptr& session) const; /** * Method: GET */ diff --git a/include/opendht/dhtrunner.h b/include/opendht/dhtrunner.h index 35f14423..b384aba8 100644 --- a/include/opendht/dhtrunner.h +++ b/include/opendht/dhtrunner.h @@ -364,7 +364,9 @@ public: */ void join(); - void startProxyInterface(); // TODO arguments + void startProxyInterface(); // TODO arguments and move in the constructor + void startProxyClient(const std::string& url); + void stopProxyClient(); private: static constexpr std::chrono::seconds BOOTSTRAP_PERIOD {10}; @@ -396,6 +398,8 @@ private: std::vector> rcv {}; std::unique_ptr proxy_server_; + std::atomic_bool proxyfing {false}; + /** true if currently actively boostraping */ std::atomic_bool bootstraping {false}; /* bootstrap nodes given as (host, service) pairs */ diff --git a/src/dhtproxyserver.cpp b/src/dhtproxyserver.cpp index 2a804a56..935cf287 100644 --- a/src/dhtproxyserver.cpp +++ b/src/dhtproxyserver.cpp @@ -16,6 +16,15 @@ DhtProxyServer::DhtProxyServer(DhtRunner* dht) : dht_(dht) service_ = std::unique_ptr(new restbed::Service()); auto resource = std::make_shared(); + resource->set_path("/get/{hash: .*}"); + resource->set_method_handler("GET", + [this](const std::shared_ptr session) + { + this->get(session); + } + ); + service_->publish(resource); + resource = std::make_shared(); resource->set_path("/getId"); resource->set_method_handler("GET", [this](const std::shared_ptr session) @@ -81,10 +90,37 @@ DhtProxyServer::DhtProxyServer(DhtRunner* dht) : dht_(dht) DhtProxyServer::~DhtProxyServer() { + service_->stop(); if (server_thread.joinable()) server_thread.join(); } +void +DhtProxyServer::get(const std::shared_ptr& session) const +{ + const auto request = session->get_request(); + + int content_length = std::stoi(request->get_header("Content-Length", "0")); + auto hash = request->get_path_parameter("hash"); + + session->fetch(content_length, + [&](const std::shared_ptr s, const restbed::Bytes& b) + { + if (dht_) { + dht_->get(InfoHash(hash), [s](std::shared_ptr value) { + s->yield(restbed::OK, value->toString()); + return true; + }, [s](bool ok) { + auto response = std::to_string(ok); + s->close(restbed::OK, response);//); + }); + } else { + s->close(restbed::NOT_FOUND, ""); + } + } + ); +} + void DhtProxyServer::getId(const std::shared_ptr& session) const { @@ -97,9 +133,9 @@ DhtProxyServer::getId(const std::shared_ptr& session) const { if (dht_) { auto id = dht_->getId().toString(); - s->close(restbed::OK, id, {{"Content-Length", std::to_string(id.length())}}); + s->close(restbed::OK, id); } else { - s->close(restbed::NOT_FOUND, "", {{"Content-Length", "0"}}); + s->close(restbed::NOT_FOUND, ""); } } ); @@ -117,9 +153,9 @@ DhtProxyServer::getNodeId(const std::shared_ptr& session) cons { if (dht_) { auto id = dht_->getNodeId().toString(); - s->close(restbed::OK, id, {{"Content-Length", std::to_string(id.length())}}); + s->close(restbed::OK, id); } else { - s->close(restbed::NOT_FOUND, "", {{"Content-Length", "0"}}); + s->close(restbed::NOT_FOUND, ""); } } ); @@ -138,7 +174,7 @@ DhtProxyServer::putSigned(const std::shared_ptr& session) cons if (dht_) { if(b.empty()) { std::string response("Missing parameters"); - s->close(restbed::BAD_REQUEST, response, {{"Content-Length", std::to_string(response.size())}}); + s->close(restbed::BAD_REQUEST, response); } else { restbed::Bytes buf(b); @@ -164,10 +200,10 @@ DhtProxyServer::putSigned(const std::shared_ptr& session) cons auto value = std::make_shared(msg.get()); dht_->putSigned(hash, value); - s->close(restbed::OK, "", {{"Content-Length", "0"}}); + s->close(restbed::OK, ""); } } else { - s->close(restbed::NOT_FOUND, "", {{"Content-Length", "0"}}); + s->close(restbed::NOT_FOUND, ""); } } ); @@ -186,7 +222,7 @@ DhtProxyServer::putEncrypted(const std::shared_ptr& session) c if (dht_) { if(b.empty()) { std::string response("Missing parameters"); - s->close(restbed::BAD_REQUEST, response, {{"Content-Length", std::to_string(response.size())}}); + s->close(restbed::BAD_REQUEST, response); } else { restbed::Bytes buf(b); @@ -219,10 +255,10 @@ DhtProxyServer::putEncrypted(const std::shared_ptr& session) c auto value = std::make_shared(msg.get()); dht_->putEncrypted(hash, to, value); - s->close(restbed::OK, "", {{"Content-Length", "0"}}); + s->close(restbed::OK, ""); } } else { - s->close(restbed::NOT_FOUND, "", {{"Content-Length", "0"}}); + s->close(restbed::NOT_FOUND, ""); } } ); @@ -242,7 +278,7 @@ DhtProxyServer::put(const std::shared_ptr& session) const if (dht_) { if(b.empty()) { std::string response("Missing parameters"); - s->close(restbed::BAD_REQUEST, response, {{"Content-Length", std::to_string(response.size())}}); + s->close(restbed::BAD_REQUEST, response); } else { restbed::Bytes buf(b); @@ -269,10 +305,10 @@ DhtProxyServer::put(const std::shared_ptr& session) const auto response = value->toString(); dht_->put(hash, value); - s->close(restbed::OK, response, {{"Content-Length", std::to_string(response.length())}}); + s->close(restbed::OK, response); } } else { - s->close(restbed::NOT_FOUND, "", {{"Content-Length", "0"}}); + s->close(restbed::NOT_FOUND, ""); } } ); diff --git a/src/dhtrunner.cpp b/src/dhtrunner.cpp index 16883539..c1532feb 100644 --- a/src/dhtrunner.cpp +++ b/src/dhtrunner.cpp @@ -807,4 +807,17 @@ DhtRunner::startProxyInterface() proxy_server_ = std::unique_ptr(new DhtProxyServer(this)); } +void +DhtRunner::startProxyClient(const std::string& url) +{ + proxyfing = true; + // TODO DhtProxyClient +} + +void +DhtRunner::stopProxyClient() +{ + proxyfing = false; +} + } diff --git a/tools/dhtchat.cpp b/tools/dhtchat.cpp index 5e80bef8..b64b4c95 100644 --- a/tools/dhtchat.cpp +++ b/tools/dhtchat.cpp @@ -20,8 +20,6 @@ #include "tools_common.h" #include -#include - extern "C" { #include } @@ -133,17 +131,7 @@ main(int argc, char **argv) std::cout << "Message publishing failed !" << std::endl; }); } else { - auto value = std::make_shared( dht::ImMessage(rand_id(rd), std::move(line), now)); - - // TODO TEMP - auto valueStr = packMsg(value); - std::cout << std::endl; - for (const auto& v: valueStr) { - std::cout << "\\x" << std::hex << static_cast(v); - } - std::cout << std::endl; - - dht.putSigned(room, value, [](bool ok) { + dht.putSigned(room, dht::ImMessage(rand_id(rd), std::move(line), now), [](bool ok) { //dht.cancelPut(room, id); if (not ok) std::cout << "Message publishing failed !" << std::endl; From 1413d32ee6f26840cf03675d460e98adc44d70a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Blin?= Date: Thu, 26 Oct 2017 17:08:53 -0400 Subject: [PATCH 05/22] add listen endpoint --- include/opendht/dhtproxyserver.h | 5 +++++ src/dhtproxyserver.cpp | 37 ++++++++++++++++++++++++++++++++ tools/dhtnode.cpp | 2 +- 3 files changed, 43 insertions(+), 1 deletion(-) diff --git a/include/opendht/dhtproxyserver.h b/include/opendht/dhtproxyserver.h index fd600341..22c6d9d7 100644 --- a/include/opendht/dhtproxyserver.h +++ b/include/opendht/dhtproxyserver.h @@ -39,6 +39,11 @@ private: * Method: GET */ void getNodeId(const std::shared_ptr& session) const; + /** + * Method: GET + * PathParameter: hash + */ + void listen(const std::shared_ptr& session) const; /** * Method: PUT * Body: vector of unsigned char diff --git a/src/dhtproxyserver.cpp b/src/dhtproxyserver.cpp index 935cf287..7fb8c8d2 100644 --- a/src/dhtproxyserver.cpp +++ b/src/dhtproxyserver.cpp @@ -2,12 +2,15 @@ #include "dhtproxyserver.h" +#include #include #include "default_types.h" #include "dhtrunner.h" #include "msgpack.hpp" +#include + namespace dht { DhtProxyServer::DhtProxyServer(DhtRunner* dht) : dht_(dht) @@ -43,6 +46,15 @@ DhtProxyServer::DhtProxyServer(DhtRunner* dht) : dht_(dht) ); service_->publish(resource); resource = std::make_shared(); + resource->set_path("/listen/{hash: .*}"); + resource->set_method_handler("GET", + [this](const std::shared_ptr session) + { + this->listen(session); + } + ); + service_->publish(resource); + resource = std::make_shared(); resource->set_path("/putSigned"); resource->set_method_handler("PUT", [this](const std::shared_ptr session) @@ -75,6 +87,8 @@ DhtProxyServer::DhtProxyServer(DhtRunner* dht) : dht_(dht) settings->set_default_header("Connection", "close"); int port = 1984; int started = false; + std::chrono::seconds sec(3600); + settings->set_connection_timeout(sec); // TODO while (!started) { try { @@ -161,6 +175,29 @@ DhtProxyServer::getNodeId(const std::shared_ptr& session) cons ); } +void +DhtProxyServer::listen(const std::shared_ptr& session) const +{ + const auto request = session->get_request(); + + int content_length = std::stoi(request->get_header("Content-Length", "0")); + auto hash = request->get_path_parameter("hash"); + + session->fetch(content_length, + [&](const std::shared_ptr s, const restbed::Bytes& b) + { + if (dht_) { + dht_->listen(InfoHash(hash), [s](std::shared_ptr value) { + s->yield(restbed::OK, value->toString()); + return true; + }); + } else { + s->close(restbed::NOT_FOUND, ""); + } + } + ); +} + void DhtProxyServer::putSigned(const std::shared_ptr& session) const { diff --git a/tools/dhtnode.cpp b/tools/dhtnode.cpp index 96cb394d..50255e50 100644 --- a/tools/dhtnode.cpp +++ b/tools/dhtnode.cpp @@ -166,7 +166,7 @@ void cmd_loop(std::shared_ptr& dht, dht_params& params) if (op.empty()) continue; - static const std::set VALID_OPS {"g", "l", "cl", "il", "ii", "p", "pp", "cpp", "s", "e", "a"}; + static const std::set VALID_OPS {"g", "l", "cl", "il", "ii", "p", "pp", "cpp", "s", "e", "a", "q"}; if (VALID_OPS.find(op) == VALID_OPS.cend()) { std::cout << "Unknown command: " << op << std::endl; std::cout << " (type 'h' or 'help' for a list of possible commands)" << std::endl; From ca465d7159010cd6b409e30a105280326301ed8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Blin?= Date: Fri, 27 Oct 2017 14:17:32 -0400 Subject: [PATCH 06/22] set timeout to a high value + add jsoncpp --- CMakeLists.txt | 9 +++++---- src/dhtproxyserver.cpp | 5 +++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 373a4c3d..a6423e4b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -83,6 +83,7 @@ include_directories ( include/ include/opendht/ ${CMAKE_CURRENT_BINARY_DIR}/include/ + /usr/include/jsoncpp ) # Install dirs @@ -179,8 +180,8 @@ if (OPENDHT_STATIC) target_link_libraries(opendht-static ${argon2_LIBRARIES}) target_include_directories(opendht-static SYSTEM PRIVATE ${argon2_INCLUDE_DIRS}) endif () - # TODO clean RESTBED link - target_link_libraries(opendht-static ${CMAKE_THREAD_LIBS_INIT} ${GNUTLS_LIBRARIES} ${Nettle_LIBRARIES} -lrestbed) + # TODO clean RESTBED link and jsoncpp + target_link_libraries(opendht-static ${CMAKE_THREAD_LIBS_INIT} ${GNUTLS_LIBRARIES} ${Nettle_LIBRARIES} -lrestbed -ljsoncpp) install (TARGETS opendht-static DESTINATION ${CMAKE_INSTALL_LIBDIR} EXPORT opendht) endif () @@ -201,8 +202,8 @@ if (OPENDHT_SHARED) target_link_libraries(opendht PRIVATE ${argon2_LIBRARIES}) target_include_directories(opendht SYSTEM PRIVATE ${argon2_INCLUDE_DIRS}) endif () - # TODO clean RESTBED link - target_link_libraries(opendht PRIVATE ${CMAKE_THREAD_LIBS_INIT} ${GNUTLS_LIBRARIES} ${Nettle_LIBRARIES} -lrestbed) + # TODO clean RESTBED link and jsoncpp + target_link_libraries(opendht PRIVATE ${CMAKE_THREAD_LIBS_INIT} ${GNUTLS_LIBRARIES} ${Nettle_LIBRARIES} -lrestbed -ljsoncpp) install (TARGETS opendht DESTINATION ${CMAKE_INSTALL_LIBDIR} EXPORT opendht) endif () diff --git a/src/dhtproxyserver.cpp b/src/dhtproxyserver.cpp index 7fb8c8d2..ad05b47f 100644 --- a/src/dhtproxyserver.cpp +++ b/src/dhtproxyserver.cpp @@ -10,6 +10,7 @@ #include "msgpack.hpp" #include +#include namespace dht { @@ -87,8 +88,8 @@ DhtProxyServer::DhtProxyServer(DhtRunner* dht) : dht_(dht) settings->set_default_header("Connection", "close"); int port = 1984; int started = false; - std::chrono::seconds sec(3600); - settings->set_connection_timeout(sec); // TODO + std::chrono::milliseconds sec(std::numeric_limits::max()); + settings->set_connection_timeout(sec); // there is a timeout, but really huge while (!started) { try { From a3348c85d6f535308cae4a73ef16986025b5fede Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Blin?= Date: Fri, 27 Oct 2017 16:33:10 -0400 Subject: [PATCH 07/22] value to JSON and begin to clean endpoints --- include/opendht/dhtproxyserver.h | 39 ++++-- include/opendht/value.h | 5 + src/dhtproxyserver.cpp | 200 +++++++++++++------------------ src/value.cpp | 30 +++++ 4 files changed, 143 insertions(+), 131 deletions(-) diff --git a/include/opendht/dhtproxyserver.h b/include/opendht/dhtproxyserver.h index 22c6d9d7..0495835e 100644 --- a/include/opendht/dhtproxyserver.h +++ b/include/opendht/dhtproxyserver.h @@ -27,23 +27,38 @@ public: private: /** - * Method: GET - * PathParameter: hash + * Return the PublicKey id, the node id and node stats + * Method: GET "/" + * Result: HTTP 200, body: {"id":"xxxx", "node_id":"xxxx", "ipv4":"xxxxxx", "ipv6": "xxxxx"} + * On error: HTTP 404, body: {"err":"xxxx"} + */ + void getNodeInfo(const std::shared_ptr& session) const; + /** + * Return Values of a InfoHash + * Method: GET "/{InfoHash: .*}" + * Return: Multiple JSON object in parts. For 2 values, you will have 3 parts: + * {"data":"xxxx" (...) "type":3} (HTTP/1.1 200 OK Content-Type: application/json) + * {"data":"xxxx" (...) "type":3} (HTTP/1.1 200 OK Content-Type: application/json) + * {"ok": 1} + * + * On error: HTTP 404, body: {"err":"xxxx"} */ void get(const std::shared_ptr& session) const; + /** - * Method: GET - */ - void getId(const std::shared_ptr& session) const; - /** - * Method: GET - */ - void getNodeId(const std::shared_ptr& session) const; - /** - * Method: GET - * PathParameter: hash + * Listen incoming Values of a InfoHash. + * Method: LISTEN "/{InfoHash: .*}" + * Return: Multiple JSON object in parts. For 2 values, you will have 2 parts: + * {"data":"xxxx" (...) "type":3} (HTTP/1.1 200 OK Content-Type: application/json) + * {"data":"xxxx" (...) "type":3} (HTTP/1.1 200 OK Content-Type: application/json) + * + * On error: HTTP 404, body: {"err":"xxxx"} */ void listen(const std::shared_ptr& session) const; + + + + /** * Method: PUT * Body: vector of unsigned char diff --git a/include/opendht/value.h b/include/opendht/value.h index 6e5a33db..f4e0c8fe 100644 --- a/include/opendht/value.h +++ b/include/opendht/value.h @@ -37,6 +37,9 @@ #include #include + +#include + namespace dht { struct Value; @@ -417,6 +420,8 @@ struct OPENDHT_PUBLIC Value return ss.str(); } + Json::Value toJson() const; + /** Return the size in bytes used by this value in memory (minimum). */ size_t size() const; diff --git a/src/dhtproxyserver.cpp b/src/dhtproxyserver.cpp index ad05b47f..03e3e60c 100644 --- a/src/dhtproxyserver.cpp +++ b/src/dhtproxyserver.cpp @@ -9,7 +9,7 @@ #include "dhtrunner.h" #include "msgpack.hpp" -#include +#include #include namespace dht { @@ -19,73 +19,36 @@ DhtProxyServer::DhtProxyServer(DhtRunner* dht) : dht_(dht) // NOTE in c++14, use make_unique service_ = std::unique_ptr(new restbed::Service()); - auto resource = std::make_shared(); - resource->set_path("/get/{hash: .*}"); - resource->set_method_handler("GET", - [this](const std::shared_ptr session) - { - this->get(session); - } - ); - service_->publish(resource); - resource = std::make_shared(); - resource->set_path("/getId"); - resource->set_method_handler("GET", - [this](const std::shared_ptr session) - { - this->getId(session); - } - ); - service_->publish(resource); - resource = std::make_shared(); - resource->set_path("/getNodeId"); - resource->set_method_handler("GET", - [this](const std::shared_ptr session) - { - this->getNodeId(session); - } - ); - service_->publish(resource); - resource = std::make_shared(); - resource->set_path("/listen/{hash: .*}"); - resource->set_method_handler("GET", - [this](const std::shared_ptr session) - { - this->listen(session); - } - ); - service_->publish(resource); - resource = std::make_shared(); - resource->set_path("/putSigned"); - resource->set_method_handler("PUT", - [this](const std::shared_ptr session) - { - this->putSigned(session); - } - ); - service_->publish(resource); - resource = std::make_shared(); - resource->set_path("/putEncrypted"); - resource->set_method_handler("PUT", - [this](const std::shared_ptr session) - { - this->putEncrypted(session); - } - ); - service_->publish(resource); - resource = std::make_shared(); - resource->set_path("/put"); - resource->set_method_handler("PUT", - [this](const std::shared_ptr session) - { - this->put(session); - } - ); - service_->publish(resource); - server_thread = std::thread([this]() { + // Create endpoints + auto resource = std::make_shared(); + resource->set_path("/"); + resource->set_method_handler("GET", + [this](const std::shared_ptr session) + { + this->getNodeInfo(session); + } + ); + service_->publish(resource); + resource = std::make_shared(); + resource->set_path("/{hash: .*}"); + resource->set_method_handler("GET", + [this](const std::shared_ptr session) + { + this->get(session); + } + ); + resource->set_method_handler("LISTEN", + [this](const std::shared_ptr session) + { + this->listen(session); + } + ); + service_->publish(resource); + + // Start server auto settings = std::make_shared(); - settings->set_default_header("Connection", "close"); + settings->set_default_header("Content-Type", "application/json"); int port = 1984; int started = false; std::chrono::milliseconds sec(std::numeric_limits::max()); @@ -110,67 +73,55 @@ DhtProxyServer::~DhtProxyServer() server_thread.join(); } +void +DhtProxyServer::getNodeInfo(const std::shared_ptr& session) const +{ + const auto request = session->get_request(); + int content_length = std::stoi(request->get_header("Content-Length", "0")); + session->fetch(content_length, + [&](const std::shared_ptr s, const restbed::Bytes& b) + { + if (dht_) { + Json::Value result; + result["id"] = dht_->getId().toString(); + result["node_id"] = dht_->getNodeId().toString(); + result["ipv4"] = dht_->getNodesStats(AF_INET).toString(); + result["ipv6"] = dht_->getNodesStats(AF_INET6).toString(); + Json::FastWriter writer; + s->close(restbed::OK, writer.write(result)); + } + else + s->close(restbed::NOT_FOUND, "{\"err\":\"Incorrect DhtRunner\"}"); + } + ); +} + void DhtProxyServer::get(const std::shared_ptr& session) const { + static constexpr dht::InfoHash INVALID_ID {}; const auto request = session->get_request(); - int content_length = std::stoi(request->get_header("Content-Length", "0")); auto hash = request->get_path_parameter("hash"); - session->fetch(content_length, [&](const std::shared_ptr s, const restbed::Bytes& b) { if (dht_) { - dht_->get(InfoHash(hash), [s](std::shared_ptr value) { - s->yield(restbed::OK, value->toString()); + InfoHash infoHash(hash); + if (infoHash == INVALID_ID) { + infoHash = InfoHash::get(hash); + } + Json::FastWriter writer; + dht_->get(infoHash, [s, &writer](std::shared_ptr value) { + Json::Value result; + s->yield(restbed::OK, writer.write(value->toJson())); return true; - }, [s](bool ok) { + }, [s, &writer](bool ok) { auto response = std::to_string(ok); - s->close(restbed::OK, response);//); + s->close(restbed::OK, "{\"ok\": " + response + "}"); }); } else { - s->close(restbed::NOT_FOUND, ""); - } - } - ); -} - -void -DhtProxyServer::getId(const std::shared_ptr& session) const -{ - const auto request = session->get_request(); - - int content_length = std::stoi(request->get_header("Content-Length", "0")); - - session->fetch(content_length, - [&](const std::shared_ptr s, const restbed::Bytes& b) - { - if (dht_) { - auto id = dht_->getId().toString(); - s->close(restbed::OK, id); - } else { - s->close(restbed::NOT_FOUND, ""); - } - } - ); -} - -void -DhtProxyServer::getNodeId(const std::shared_ptr& session) const -{ - const auto request = session->get_request(); - - int content_length = std::stoi(request->get_header("Content-Length", "0")); - - session->fetch(content_length, - [&](const std::shared_ptr s, const restbed::Bytes& b) - { - if (dht_) { - auto id = dht_->getNodeId().toString(); - s->close(restbed::OK, id); - } else { - s->close(restbed::NOT_FOUND, ""); + s->close(restbed::NOT_FOUND, "{\"err\":\"Incorrect DhtRunner\"}"); } } ); @@ -179,26 +130,37 @@ DhtProxyServer::getNodeId(const std::shared_ptr& session) cons void DhtProxyServer::listen(const std::shared_ptr& session) const { + static constexpr dht::InfoHash INVALID_ID {}; const auto request = session->get_request(); - int content_length = std::stoi(request->get_header("Content-Length", "0")); auto hash = request->get_path_parameter("hash"); - + InfoHash infoHash(hash); + if (infoHash == INVALID_ID) + infoHash = InfoHash::get(hash); + size_t token; session->fetch(content_length, [&](const std::shared_ptr s, const restbed::Bytes& b) { if (dht_) { - dht_->listen(InfoHash(hash), [s](std::shared_ptr value) { - s->yield(restbed::OK, value->toString()); + Json::FastWriter writer; + token = dht_->listen(infoHash, [s, &writer](std::shared_ptr value) { + Json::Value result; + s->yield(restbed::OK, writer.write(value->toJson())); return true; - }); + }).get(); } else { - s->close(restbed::NOT_FOUND, ""); + s->close(restbed::NOT_FOUND, "{\"err\":\"Incorrect DhtRunner\"}"); } } ); + + // TODO cancel listen at the end } + + + + void DhtProxyServer::putSigned(const std::shared_ptr& session) const { diff --git a/src/value.cpp b/src/value.cpp index e3d3aece..41612bb1 100644 --- a/src/value.cpp +++ b/src/value.cpp @@ -22,6 +22,10 @@ #include "default_types.h" #include "securedht.h" // print certificate ID + +#include + + namespace dht { const std::string Query::QUERY_PARSE_ERROR {"Error parsing query."}; @@ -167,6 +171,32 @@ Value::msgpack_unpack_body(const msgpack::object& o) } } +Json::Value +Value::toJson() const +{ + // TODO check + Json::Value val; + val["id"] = std::to_string(id); + if (isEncrypted()) { + val["cypher"] = std::string(cypher.begin(), cypher.end()); + } else { + if (isSigned()) + val["sig"] = std::string(signature.begin(), signature.end()); + bool has_owner = owner && *owner; + if (has_owner) { // isSigned + val["seq"] = seq; + val["owner"] = owner->toString(); + if (recipient != InfoHash()) + val["to"] = recipient.toString(); + } + val["type"] = type; + val["data"] = std::string(data.begin(), data.end()); + if (not user_type.empty()) + val["utype"] = user_type; + } + return val; +} + bool FieldValue::operator==(const FieldValue& vfd) const { From c4ba8670e403e7063fa84f6effb9b563f88810c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Blin?= Date: Mon, 30 Oct 2017 11:43:49 -0400 Subject: [PATCH 08/22] add Putxxx value endpoints --- CMakeLists.txt | 2 + include/opendht/base64.h | 6 + include/opendht/dhtproxyserver.h | 52 ++++---- include/opendht/value.h | 40 +++++- src/base64.cpp | 95 ++++++++++++++ src/dhtproxyserver.cpp | 211 +++++++++++++++---------------- src/value.cpp | 7 +- 7 files changed, 276 insertions(+), 137 deletions(-) create mode 100644 include/opendht/base64.h create mode 100644 src/base64.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index a6423e4b..18cd7322 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -105,6 +105,7 @@ list (APPEND opendht_SOURCES src/node.cpp src/value.cpp src/dht.cpp + src/base64.cpp src/dhtproxyserver.cpp src/storage.h src/listener.h @@ -133,6 +134,7 @@ list (APPEND opendht_HEADERS include/opendht/node.h include/opendht/value.h include/opendht/dht.h + include/opendht/base64.h include/opendht/dhtproxyserver.h include/opendht/callbacks.h include/opendht/routing_table.h diff --git a/include/opendht/base64.h b/include/opendht/base64.h new file mode 100644 index 00000000..256e2640 --- /dev/null +++ b/include/opendht/base64.h @@ -0,0 +1,6 @@ +#ifndef _BASE64_H_ +#define _BASE64_H_ +#include +std::string base64_encode(unsigned char const* , unsigned int len); +std::string base64_decode(std::string const& s); +#endif diff --git a/include/opendht/dhtproxyserver.h b/include/opendht/dhtproxyserver.h index 0495835e..94b02a48 100644 --- a/include/opendht/dhtproxyserver.h +++ b/include/opendht/dhtproxyserver.h @@ -33,6 +33,7 @@ private: * On error: HTTP 404, body: {"err":"xxxx"} */ void getNodeInfo(const std::shared_ptr& session) const; + /** * Return Values of a InfoHash * Method: GET "/{InfoHash: .*}" @@ -56,35 +57,36 @@ private: */ void listen(const std::shared_ptr& session) const; - - - /** - * Method: PUT - * Body: vector of unsigned char - * first line: InfoHash hash - * second line: Value value to sign and put - * @param session - */ - void putSigned(const std::shared_ptr& session) const; - /** - * Method: PUT - * Body: vector of unsigned char - * first line: InfoHash hash - * second line: InfoHash to - * third line: Value value to encrypt and put - * @param session - */ - void putEncrypted(const std::shared_ptr& session) const; - /** - * Method: PUT - * Body: vector of unsigned char - * first line: InfoHash hash - * second line: Value value to put - * @param session + * Put a value on the DHT + * Method: POST "/{InfoHash: .*}" + * body = Value to put in JSON + * Return: {"ok":"1"} + * On error: HTTP 404, body: {"err":"xxxx"} if no dht + * HTTP 400, body: {"err":"xxxx"} if bad json */ void put(const std::shared_ptr& session) const; + /** + * Put a value to sign by the proxy on the DHT + * Method: SIGN "/{InfoHash: .*}" + * body = Value to put in JSON + * Return: {"ok":"1"} + * On error: HTTP 404, body: {"err":"xxxx"} if no dht + * HTTP 400, body: {"err":"xxxx"} if bad json + */ + void putSigned(const std::shared_ptr& session) const; + + /** + * Put a value to encrypt by the proxy on the DHT + * Method: ENCRYPT "/{InfoHash: .*}" + * body = Value to put in JSON + * Return: {"ok":"1"} + * On error: HTTP 404, body: {"err":"xxxx"} if no dht + * HTTP 400, body: {"err":"xxxx"} if bad json + */ + void putEncrypted(const std::shared_ptr& session) const; + std::thread server_thread {}; std::unique_ptr service_; DhtRunner* dht_; diff --git a/include/opendht/value.h b/include/opendht/value.h index f4e0c8fe..9113805b 100644 --- a/include/opendht/value.h +++ b/include/opendht/value.h @@ -19,6 +19,7 @@ #pragma once +#include "base64.h" #include "infohash.h" #include "crypto.h" #include "utils.h" @@ -37,7 +38,6 @@ #include #include - #include namespace dht { @@ -349,6 +349,44 @@ struct OPENDHT_PUBLIC Value Value(ValueType::Id t, const uint8_t* dat_ptr, size_t dat_len, Id id = INVALID_ID) : id(id), type(t), data(dat_ptr, dat_ptr+dat_len) {} + Value(Json::Value& json) { + + try { + if (json.isMember("id")) + id = ValueType::Id(json["id"].asInt()); + } catch (...) { } + if (json.isMember("cypher")) { + auto cypherStr = json["cypher"].asString(); + cypherStr = base64_decode(cypherStr); + cypher = std::vector(cypherStr.begin(), cypherStr.end()); + } + if (json.isMember("sig")) { + auto sigStr = json["sig"].asString(); + sigStr = base64_decode(sigStr); + signature = std::vector(sigStr.begin(), sigStr.end()); + } + if (json.isMember("seq")) + seq = json["seq"].asInt(); + if (json.isMember("owner")) { + auto ownerStr = json["owner"].asString(); + auto ownerBlob = std::vector(ownerStr.begin(), ownerStr.end()); + owner = std::make_shared(ownerBlob); + } + if (json.isMember("to")) { + auto toStr = json["to"].asString(); + recipient = InfoHash(toStr); + } + if (json.isMember("type")) + type = json["type"].asInt(); + if (json.isMember("data")){ + auto dataStr = json["data"].asString(); + dataStr = base64_decode(dataStr); + data = std::vector(dataStr.begin(), dataStr.end()); + } + if (json.isMember("utype")) + user_type = json["utype"].asString(); + } + template Value(ValueType::Id t, const Type& d, Id id = INVALID_ID) : id(id), type(t), data(packMsg(d)) {} diff --git a/src/base64.cpp b/src/base64.cpp new file mode 100644 index 00000000..d11c28f2 --- /dev/null +++ b/src/base64.cpp @@ -0,0 +1,95 @@ +#include "base64.h" + +static const std::string base64_chars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + + +static inline bool is_base64(unsigned char c) { + return (isalnum(c) || (c == '+') || (c == '/')); +} + +std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) { + std::string ret; + int i = 0; + int j = 0; + unsigned char char_array_3[3]; + unsigned char char_array_4[4]; + + while (in_len--) { + char_array_3[i++] = *(bytes_to_encode++); + if (i == 3) { + char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; + char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); + char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); + char_array_4[3] = char_array_3[2] & 0x3f; + + for(i = 0; (i <4) ; i++) + ret += base64_chars[char_array_4[i]]; + i = 0; + } + } + + if (i) + { + for(j = i; j < 3; j++) + char_array_3[j] = '\0'; + + char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; + char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); + char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); + char_array_4[3] = char_array_3[2] & 0x3f; + + for (j = 0; (j < i + 1); j++) + ret += base64_chars[char_array_4[j]]; + + while((i++ < 3)) + ret += '='; + + } + + return ret; + +} + +std::string base64_decode(std::string const& encoded_string) { + size_t in_len = encoded_string.size(); + size_t i = 0; + size_t j = 0; + int in_ = 0; + unsigned char char_array_4[4], char_array_3[3]; + std::string ret; + + while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) { + char_array_4[i++] = encoded_string[in_]; in_++; + if (i ==4) { + for (i = 0; i <4; i++) + char_array_4[i] = static_cast(base64_chars.find(char_array_4[i])); + + char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + + for (i = 0; (i < 3); i++) + ret += char_array_3[i]; + i = 0; + } + } + + if (i) { + for (j = i; j <4; j++) + char_array_4[j] = 0; + + for (j = 0; j <4; j++) + char_array_4[j] = static_cast(base64_chars.find(char_array_4[j])); + + char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + + for (j = 0; (j < i - 1); j++) ret += char_array_3[j]; + } + + return ret; +} diff --git a/src/dhtproxyserver.cpp b/src/dhtproxyserver.cpp index 03e3e60c..ea8ea0af 100644 --- a/src/dhtproxyserver.cpp +++ b/src/dhtproxyserver.cpp @@ -12,6 +12,8 @@ #include #include +#include + namespace dht { DhtProxyServer::DhtProxyServer(DhtRunner* dht) : dht_(dht) @@ -44,6 +46,27 @@ DhtProxyServer::DhtProxyServer(DhtRunner* dht) : dht_(dht) this->listen(session); } ); + resource->set_method_handler("POST", + [this](const std::shared_ptr session) + { + this->put(session); + } + ); + resource->set_method_handler("SIGN", + [this](const std::shared_ptr session) + { + this->putSigned(session); + } + ); + service_->publish(resource); + resource = std::make_shared(); + resource->set_path("/{hash: .*}/{hash: .*}"); + resource->set_method_handler("ENCRYPT", + [this](const std::shared_ptr session) + { + this->putEncrypted(session); + } + ); service_->publish(resource); // Start server @@ -51,7 +74,8 @@ DhtProxyServer::DhtProxyServer(DhtRunner* dht) : dht_(dht) settings->set_default_header("Content-Type", "application/json"); int port = 1984; int started = false; - std::chrono::milliseconds sec(std::numeric_limits::max()); + std::chrono::seconds sec(3600); + //std::chrono::milliseconds sec(std::numeric_limits::max()); settings->set_connection_timeout(sec); // there is a timeout, but really huge while (!started) { @@ -153,20 +177,21 @@ DhtProxyServer::listen(const std::shared_ptr& session) const } } ); - // TODO cancel listen at the end } - - - void -DhtProxyServer::putSigned(const std::shared_ptr& session) const +DhtProxyServer::put(const std::shared_ptr& session) const { + // TODO test with encrypted and signed value to send + static constexpr dht::InfoHash INVALID_ID {}; const auto request = session->get_request(); - int content_length = std::stoi(request->get_header("Content-Length", "0")); + auto hash = request->get_path_parameter("hash"); + InfoHash infoHash(hash); + if (infoHash == INVALID_ID) + infoHash = InfoHash::get(hash); session->fetch(content_length, [&](const std::shared_ptr s, const restbed::Bytes& b) @@ -177,30 +202,60 @@ DhtProxyServer::putSigned(const std::shared_ptr& session) cons s->close(restbed::BAD_REQUEST, response); } else { restbed::Bytes buf(b); + Json::Value root; + Json::Reader reader; + std::string strJson(buf.begin(), buf.end()); + bool parsingSuccessful = reader.parse(strJson.c_str(), root); + if (parsingSuccessful) { + auto value = std::make_shared(root); - unsigned char delimiter = static_cast('\n'); - // Retrieve hash - unsigned int idx = 0; - std::vector hashBuf; - while(idx < buf.size() && buf[idx] != delimiter) { - hashBuf.emplace_back(buf[idx]); - ++idx; + auto response = value->toString(); + dht_->put(infoHash, value); + s->close(restbed::OK, response); + } else { + s->close(restbed::BAD_REQUEST, "Incorrect JSON"); } - ++idx; - // Retrieve value - std::vector valueBuf; - while(idx < buf.size()) { - valueBuf.emplace_back(buf[idx]); - ++idx; - } - // Build parameters - InfoHash hash(hashBuf.data(), hashBuf.size()); - msgpack::unpacked msg; - msgpack::unpack(msg, reinterpret_cast(valueBuf.data()), valueBuf.size()); - auto value = std::make_shared(msg.get()); + } + } else { + s->close(restbed::NOT_FOUND, ""); + } + } + ); +} - dht_->putSigned(hash, value); - s->close(restbed::OK, ""); +void +DhtProxyServer::putSigned(const std::shared_ptr& session) const +{ + static constexpr dht::InfoHash INVALID_ID {}; + const auto request = session->get_request(); + int content_length = std::stoi(request->get_header("Content-Length", "0")); + auto hash = request->get_path_parameter("hash"); + InfoHash infoHash(hash); + if (infoHash == INVALID_ID) + infoHash = InfoHash::get(hash); + + session->fetch(content_length, + [&](const std::shared_ptr s, const restbed::Bytes& b) + { + if (dht_) { + if(b.empty()) { + std::string response("Missing parameters"); + s->close(restbed::BAD_REQUEST, response); + } else { + restbed::Bytes buf(b); + Json::Value root; + Json::Reader reader; + std::string strJson(buf.begin(), buf.end()); + bool parsingSuccessful = reader.parse(strJson.c_str(), root); + if (parsingSuccessful) { + auto value = std::make_shared(root); + + auto response = value->toString(); + dht_->putSigned(infoHash, value); + s->close(restbed::OK, response); + } else { + s->close(restbed::BAD_REQUEST, "Incorrect JSON" + strJson); + } } } else { s->close(restbed::NOT_FOUND, ""); @@ -212,9 +267,17 @@ DhtProxyServer::putSigned(const std::shared_ptr& session) cons void DhtProxyServer::putEncrypted(const std::shared_ptr& session) const { + static constexpr dht::InfoHash INVALID_ID {}; const auto request = session->get_request(); - int content_length = std::stoi(request->get_header("Content-Length", "0")); + auto hash = request->get_path_parameter("hash"); + InfoHash infoHash(hash); + if (infoHash == INVALID_ID) + infoHash = InfoHash::get(hash); + auto toHash = request->get_path_parameter("to"); + InfoHash toInfoHash(toHash); + if (toInfoHash == INVALID_ID) + toInfoHash = InfoHash::get(toHash); session->fetch(content_length, [&](const std::shared_ptr s, const restbed::Bytes& b) @@ -225,87 +288,19 @@ DhtProxyServer::putEncrypted(const std::shared_ptr& session) c s->close(restbed::BAD_REQUEST, response); } else { restbed::Bytes buf(b); + Json::Value root; + Json::Reader reader; + std::string strJson(buf.begin(), buf.end()); + bool parsingSuccessful = reader.parse(strJson.c_str(), root); + if (parsingSuccessful) { + auto value = std::make_shared(root); - unsigned char delimiter = static_cast('\n'); - // Retrieve hash - unsigned int idx = 0; - std::vector hashBuf; - while(idx < buf.size() && buf[idx] != delimiter) { - hashBuf.emplace_back(buf[idx]); - ++idx; + auto response = value->toString(); + dht_->putEncrypted(infoHash, toInfoHash, value); + s->close(restbed::OK, response); + } else { + s->close(restbed::BAD_REQUEST, "Incorrect JSON"); } - ++idx; - // Retrieve to - std::vector toBuf; - while(idx < buf.size() && buf[idx] != delimiter) { - toBuf.emplace_back(buf[idx]); - ++idx; - } - ++idx; - // Retrieve value - std::vector valueBuf; - while(idx < buf.size()) { - valueBuf.emplace_back(buf[idx]); - ++idx; - } - InfoHash hash(hashBuf.data(), hashBuf.size()); - InfoHash to(toBuf.data(), toBuf.size()); - msgpack::unpacked msg; - msgpack::unpack(msg, reinterpret_cast(valueBuf.data()), valueBuf.size()); - auto value = std::make_shared(msg.get()); - - dht_->putEncrypted(hash, to, value); - s->close(restbed::OK, ""); - } - } else { - s->close(restbed::NOT_FOUND, ""); - } - } - ); -} - -void -DhtProxyServer::put(const std::shared_ptr& session) const -{ - // TODO test with encrypted and signed value to send - const auto request = session->get_request(); - - int content_length = std::stoi(request->get_header("Content-Length", "0")); - - session->fetch(content_length, - [&](const std::shared_ptr s, const restbed::Bytes& b) - { - if (dht_) { - if(b.empty()) { - std::string response("Missing parameters"); - s->close(restbed::BAD_REQUEST, response); - } else { - restbed::Bytes buf(b); - - unsigned char delimiter = static_cast('\n'); - // Retrieve hash - unsigned int idx = 0; - std::vector hashBuf; - while(idx < buf.size() && buf[idx] != delimiter) { - hashBuf.emplace_back(buf[idx]); - ++idx; - } - ++idx; - // Retrieve value - std::vector valueBuf; - while(idx < buf.size()) { - valueBuf.emplace_back(buf[idx]); - ++idx; - } - // Build parameters - InfoHash hash(hashBuf.data(), hashBuf.size()); - msgpack::unpacked msg; - msgpack::unpack(msg, reinterpret_cast(valueBuf.data()), valueBuf.size()); - auto value = std::make_shared(msg.get()); - - auto response = value->toString(); - dht_->put(hash, value); - s->close(restbed::OK, response); } } else { s->close(restbed::NOT_FOUND, ""); diff --git a/src/value.cpp b/src/value.cpp index 41612bb1..156a7938 100644 --- a/src/value.cpp +++ b/src/value.cpp @@ -24,6 +24,7 @@ #include +#include namespace dht { @@ -178,10 +179,10 @@ Value::toJson() const Json::Value val; val["id"] = std::to_string(id); if (isEncrypted()) { - val["cypher"] = std::string(cypher.begin(), cypher.end()); + val["cypher"] = base64_encode(cypher.data(), cypher.size()); } else { if (isSigned()) - val["sig"] = std::string(signature.begin(), signature.end()); + val["sig"] = base64_encode(signature.data(), signature.size()); bool has_owner = owner && *owner; if (has_owner) { // isSigned val["seq"] = seq; @@ -190,7 +191,7 @@ Value::toJson() const val["to"] = recipient.toString(); } val["type"] = type; - val["data"] = std::string(data.begin(), data.end()); + val["data"] = base64_encode(data.data(), data.size()); if (not user_type.empty()) val["utype"] = user_type; } From 20abc3e4957493734eef9af9177ed220902b6a14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Blin?= Date: Mon, 30 Oct 2017 13:54:10 -0400 Subject: [PATCH 09/22] add LISTEN endpoint --- include/opendht/dhtproxyserver.h | 1 + src/dhtproxyserver.cpp | 19 ++++++++++++++++--- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/include/opendht/dhtproxyserver.h b/include/opendht/dhtproxyserver.h index 94b02a48..bda4fa8c 100644 --- a/include/opendht/dhtproxyserver.h +++ b/include/opendht/dhtproxyserver.h @@ -90,6 +90,7 @@ private: std::thread server_thread {}; std::unique_ptr service_; DhtRunner* dht_; + mutable std::vector> listenThreads_; }; } diff --git a/src/dhtproxyserver.cpp b/src/dhtproxyserver.cpp index ea8ea0af..21a9cc42 100644 --- a/src/dhtproxyserver.cpp +++ b/src/dhtproxyserver.cpp @@ -93,6 +93,10 @@ DhtProxyServer::DhtProxyServer(DhtRunner* dht) : dht_(dht) DhtProxyServer::~DhtProxyServer() { service_->stop(); + // listenThreads_ will stop because there is no more sessions + for (auto& listenThread: listenThreads_) + if (listenThread->joinable()) + listenThread->join(); if (server_thread.joinable()) server_thread.join(); } @@ -169,15 +173,24 @@ DhtProxyServer::listen(const std::shared_ptr& session) const Json::FastWriter writer; token = dht_->listen(infoHash, [s, &writer](std::shared_ptr value) { Json::Value result; - s->yield(restbed::OK, writer.write(value->toJson())); - return true; + if (!s->is_closed()) + s->yield(restbed::OK, writer.write(value->toJson())); + return !s->is_closed(); }).get(); + // Handle client deconnection + listenThreads_.emplace_back(std::shared_ptr( + new std::thread([&]() { + while(!s->is_closed()) + std::this_thread::sleep_for(std::chrono::seconds(2)); + if (dht_) + dht_->cancelListen(infoHash, token); + })) + ); } else { s->close(restbed::NOT_FOUND, "{\"err\":\"Incorrect DhtRunner\"}"); } } ); - // TODO cancel listen at the end } From 7ddd6629e36cdd83229a68e41aab73282c87ddee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Blin?= Date: Tue, 31 Oct 2017 15:24:52 -0400 Subject: [PATCH 10/22] add getFiltered --- include/opendht/dhtproxyserver.h | 16 +++++++-- src/dhtproxyserver.cpp | 61 +++++++++++++++++++++++++++----- 2 files changed, 66 insertions(+), 11 deletions(-) diff --git a/include/opendht/dhtproxyserver.h b/include/opendht/dhtproxyserver.h index bda4fa8c..ac4f5a6e 100644 --- a/include/opendht/dhtproxyserver.h +++ b/include/opendht/dhtproxyserver.h @@ -79,14 +79,26 @@ private: /** * Put a value to encrypt by the proxy on the DHT - * Method: ENCRYPT "/{InfoHash: .*}" - * body = Value to put in JSON + * Method: ENCRYPT "/{hash: .*}" + * body = Value to put in JSON + "to":"infoHash" * Return: {"ok":"1"} * On error: HTTP 404, body: {"err":"xxxx"} if no dht * HTTP 400, body: {"err":"xxxx"} if bad json */ void putEncrypted(const std::shared_ptr& session) const; + /** + * Return Values of a InfoHash filtered by a value id + * Method: GET "/{InfoHash: .*}/{ValueId: .*}" + * Return: Multiple JSON object in parts. For 2 values, you will have 3 parts: + * {"data":"xxxx" (...) "type":3} (HTTP/1.1 200 OK Content-Type: application/json) + * {"data":"xxxx" (...) "type":3} (HTTP/1.1 200 OK Content-Type: application/json) + * {"ok": 1} + * + * On error: HTTP 404, body: {"err":"xxxx"} + */ + void getFiltered(const std::shared_ptr& session) const; + std::thread server_thread {}; std::unique_ptr service_; DhtRunner* dht_; diff --git a/src/dhtproxyserver.cpp b/src/dhtproxyserver.cpp index 21a9cc42..13f4fe67 100644 --- a/src/dhtproxyserver.cpp +++ b/src/dhtproxyserver.cpp @@ -52,15 +52,13 @@ DhtProxyServer::DhtProxyServer(DhtRunner* dht) : dht_(dht) this->put(session); } ); + // NOTE/TODO ENCRYPT AND SIGN must be optionnal resource->set_method_handler("SIGN", [this](const std::shared_ptr session) { this->putSigned(session); } ); - service_->publish(resource); - resource = std::make_shared(); - resource->set_path("/{hash: .*}/{hash: .*}"); resource->set_method_handler("ENCRYPT", [this](const std::shared_ptr session) { @@ -68,6 +66,15 @@ DhtProxyServer::DhtProxyServer(DhtRunner* dht) : dht_(dht) } ); service_->publish(resource); + resource = std::make_shared(); + resource->set_path("/{hash: .*}/{value: .*}"); + resource->set_method_handler("GET", + [this](const std::shared_ptr session) + { + this->getFiltered(session); + } + ); + service_->publish(resource); // Start server auto settings = std::make_shared(); @@ -287,10 +294,6 @@ DhtProxyServer::putEncrypted(const std::shared_ptr& session) c InfoHash infoHash(hash); if (infoHash == INVALID_ID) infoHash = InfoHash::get(hash); - auto toHash = request->get_path_parameter("to"); - InfoHash toInfoHash(toHash); - if (toInfoHash == INVALID_ID) - toInfoHash = InfoHash::get(toHash); session->fetch(content_length, [&](const std::shared_ptr s, const restbed::Bytes& b) @@ -305,14 +308,22 @@ DhtProxyServer::putEncrypted(const std::shared_ptr& session) c Json::Reader reader; std::string strJson(buf.begin(), buf.end()); bool parsingSuccessful = reader.parse(strJson.c_str(), root); - if (parsingSuccessful) { + auto toHash = root["to"].asString(); + if (parsingSuccessful && !toHash.empty()) { auto value = std::make_shared(root); + auto toHash = request->get_path_parameter("to"); + InfoHash toInfoHash(toHash); + if (toInfoHash == INVALID_ID) + toInfoHash = InfoHash::get(toHash); auto response = value->toString(); dht_->putEncrypted(infoHash, toInfoHash, value); s->close(restbed::OK, response); } else { - s->close(restbed::BAD_REQUEST, "Incorrect JSON"); + if(!parsingSuccessful) + s->close(restbed::BAD_REQUEST, "Incorrect JSON"); + else + s->close(restbed::BAD_REQUEST, "No destination found"); } } } else { @@ -322,4 +333,36 @@ DhtProxyServer::putEncrypted(const std::shared_ptr& session) c ); } +void +DhtProxyServer::getFiltered(const std::shared_ptr& session) const +{ + static constexpr dht::InfoHash INVALID_ID {}; + const auto request = session->get_request(); + int content_length = std::stoi(request->get_header("Content-Length", "0")); + auto hash = request->get_path_parameter("hash"); + auto value = request->get_path_parameter("value"); + session->fetch(content_length, + [&](const std::shared_ptr s, const restbed::Bytes& b) + { + if (dht_) { + InfoHash infoHash(hash); + if (infoHash == INVALID_ID) { + infoHash = InfoHash::get(hash); + } + Json::FastWriter writer; + dht_->get(infoHash, [s, &writer](std::shared_ptr value) { + Json::Value result; + s->yield(restbed::OK, writer.write(value->toJson())); + return true; + }, [s, &writer](bool ok) { + auto response = std::to_string(ok); + s->close(restbed::OK, "{\"ok\": " + response + "}"); + }, {}, value); + } else { + s->close(restbed::NOT_FOUND, "{\"err\":\"Incorrect DhtRunner\"}"); + } + } + ); +} + } From d608da4307dec7555f50ebb6fe58f837383eb002 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Blin?= Date: Wed, 1 Nov 2017 10:03:01 -0400 Subject: [PATCH 11/22] cmake: add optionnal dependencies for proxy --- CMakeLists.txt | 19 +++++++++++++++---- cmake/FindJsoncpp.cmake | 18 ++++++++++++++++++ cmake/FindRestbed.cmake | 18 ++++++++++++++++++ 3 files changed, 51 insertions(+), 4 deletions(-) create mode 100644 cmake/FindJsoncpp.cmake create mode 100644 cmake/FindRestbed.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 18cd7322..171a1c7d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,6 +16,9 @@ option (OPENDHT_SYSTEMD "Install systemd module" OFF) option (OPENDHT_ARGON2 "Use included argon2 sources" OFF) option (OPENDHT_LTO "Build with LTO" OFF) option (OPENDHT_SANITIZE "Build with address sanitizer and stack protector" OFF) +option (OPENDHT_PROXY_SERVER "Enable DHT proxy server, use Restbed and jsoncpp" OFF) +option (OPENDHT_PROXY_SERVER_OPTIONAL "Enable sign and encrypt endpoints" OFF) +option (OPENDHT_PROXY_CLIENT "Enable DHT proxy client, use Restbed and jsoncpp" OFF) find_package(Doxygen) option (OPENDHT_DOCUMENTATION "Create and install the HTML based API documentation (requires Doxygen)" ${DOXYGEN_FOUND}) @@ -41,6 +44,10 @@ if (NOT OPENDHT_ARGON2) set(OPENDHT_ARGON2 ON) endif() endif () +if (OPENDHT_PROXY_SERVER OR OPENDHT_PROXY_CLIENT) + find_package(Restbed REQUIRED) + find_package(Jsoncpp REQUIRED) +endif() # Build flags set (CMAKE_CXX_STANDARD 11) @@ -77,6 +84,12 @@ endif () if (Nettle_INCLUDE_DIRS) include_directories (SYSTEM "${Nettle_INCLUDE_DIRS}") endif () +if (Restbed_INCLUDE_DIRS) + include_directories (SYSTEM "${Restbed_INCLUDE_DIRS}") +endif () +if (Jsoncpp_INCLUDE_DIRS) + include_directories (SYSTEM "${Jsoncpp_INCLUDE_DIRS}") +endif () link_directories (${Nettle_LIBRARY_DIRS}) include_directories ( ./ @@ -182,8 +195,7 @@ if (OPENDHT_STATIC) target_link_libraries(opendht-static ${argon2_LIBRARIES}) target_include_directories(opendht-static SYSTEM PRIVATE ${argon2_INCLUDE_DIRS}) endif () - # TODO clean RESTBED link and jsoncpp - target_link_libraries(opendht-static ${CMAKE_THREAD_LIBS_INIT} ${GNUTLS_LIBRARIES} ${Nettle_LIBRARIES} -lrestbed -ljsoncpp) + target_link_libraries(opendht-static ${CMAKE_THREAD_LIBS_INIT} ${GNUTLS_LIBRARIES} ${Nettle_LIBRARIES} ${Restbed_LIBRARIES} ${Jsoncpp_LIBRARIES}) install (TARGETS opendht-static DESTINATION ${CMAKE_INSTALL_LIBDIR} EXPORT opendht) endif () @@ -204,8 +216,7 @@ if (OPENDHT_SHARED) target_link_libraries(opendht PRIVATE ${argon2_LIBRARIES}) target_include_directories(opendht SYSTEM PRIVATE ${argon2_INCLUDE_DIRS}) endif () - # TODO clean RESTBED link and jsoncpp - target_link_libraries(opendht PRIVATE ${CMAKE_THREAD_LIBS_INIT} ${GNUTLS_LIBRARIES} ${Nettle_LIBRARIES} -lrestbed -ljsoncpp) + target_link_libraries(opendht PRIVATE ${CMAKE_THREAD_LIBS_INIT} ${GNUTLS_LIBRARIES} ${Nettle_LIBRARIES} ${Restbed_LIBRARIES} ${Jsoncpp_LIBRARIES}) install (TARGETS opendht DESTINATION ${CMAKE_INSTALL_LIBDIR} EXPORT opendht) endif () diff --git a/cmake/FindJsoncpp.cmake b/cmake/FindJsoncpp.cmake new file mode 100644 index 00000000..fccc2da3 --- /dev/null +++ b/cmake/FindJsoncpp.cmake @@ -0,0 +1,18 @@ +find_path (JSONCPP_INCLUDE jsoncpp + HINTS + "/usr/include" + "/usr/local/include" + "/opt/local/include" +) + +if (JSONCPP_INCLUDE) + message(STATUS "${green}Found Jsoncpp: ${JSONCPP_INCLUDE}") +else() + message(FATAL_ERROR "${red}Failed to locate Jsoncpp.}") +endif() + +if (JSONCPP_INCLUDE) + set(JSONCPP_FOUND TRUE) + set(Jsoncpp_LIBRARIES jsoncpp) + set(Jsoncpp_INCLUDE_DIRS ${JSONCPP_INCLUDE}/jsoncpp) +endif() diff --git a/cmake/FindRestbed.cmake b/cmake/FindRestbed.cmake new file mode 100644 index 00000000..440e5596 --- /dev/null +++ b/cmake/FindRestbed.cmake @@ -0,0 +1,18 @@ +find_path (RESTBED_INCLUDE restbed + HINTS + "/usr/include" + "/usr/local/include" + "/opt/local/include" +) + +if (RESTBED_INCLUDE) + message(STATUS "${green}Found Restbed: ${RESTBED_INCLUDE}") +else() + message(FATAL_ERROR "${red}Failed to locate Restbed.}") +endif() + +if (RESTBED_INCLUDE) + set(RESTBED_FOUND TRUE) + set(Restbed_LIBRARIES restbed) + set(Restbed_INCLUDE_DIRS ${RESTBED_INCLUDE}/restbed) +endif() From dc66b3847a834eb40ba16e563724a0a95131c435 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Blin?= Date: Wed, 1 Nov 2017 11:00:00 -0400 Subject: [PATCH 12/22] make code for DHT Proxy Server optionnal --- CMakeLists.txt | 18 ++++++++++++++---- include/opendht/base64.h | 8 +++++--- include/opendht/dhtproxyserver.h | 6 ++++-- include/opendht/dhtrunner.h | 9 ++++++--- include/opendht/value.h | 8 +++++++- src/base64.cpp | 2 ++ src/dhtproxyserver.cpp | 2 ++ src/dhtrunner.cpp | 15 ++------------- src/value.cpp | 6 ++++-- 9 files changed, 46 insertions(+), 28 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 171a1c7d..30a934a0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -118,8 +118,6 @@ list (APPEND opendht_SOURCES src/node.cpp src/value.cpp src/dht.cpp - src/base64.cpp - src/dhtproxyserver.cpp src/storage.h src/listener.h src/search.h @@ -147,8 +145,6 @@ list (APPEND opendht_HEADERS include/opendht/node.h include/opendht/value.h include/opendht/dht.h - include/opendht/base64.h - include/opendht/dhtproxyserver.h include/opendht/callbacks.h include/opendht/routing_table.h include/opendht/node_cache.h @@ -162,6 +158,20 @@ list (APPEND opendht_HEADERS include/opendht/indexation/pht.h ) +if (OPENDHT_PROXY_SERVER) + add_definitions(-DOPENDHT_PROXY_SERVER=true) + list (APPEND opendht_HEADERS + include/opendht/dhtproxyserver.h + include/opendht/base64.h + ) + list (APPEND opendht_SOURCES + src/dhtproxyserver.cpp + src/base64.cpp + ) +else () + add_definitions(-DENABLE_PROXY_SERVER=false) +endif () + if(OPENDHT_ARGON2) # make sure argon2 submodule is up to date and initialized message("Initializing Argon2 submodule") diff --git a/include/opendht/base64.h b/include/opendht/base64.h index 256e2640..8f4d9ebb 100644 --- a/include/opendht/base64.h +++ b/include/opendht/base64.h @@ -1,6 +1,8 @@ -#ifndef _BASE64_H_ -#define _BASE64_H_ +#if OPENDHT_PROXY_SERVER +#pragma once + #include + std::string base64_encode(unsigned char const* , unsigned int len); std::string base64_decode(std::string const& s); -#endif +#endif // OPENDHT_PROXY_SERVER diff --git a/include/opendht/dhtproxyserver.h b/include/opendht/dhtproxyserver.h index ac4f5a6e..380db0f5 100644 --- a/include/opendht/dhtproxyserver.h +++ b/include/opendht/dhtproxyserver.h @@ -1,13 +1,13 @@ // TODO gpl +#if OPENDHT_PROXY_SERVER + #pragma once #include "def.h" #include #include - -// TODO add ifdef RESTBED #include namespace dht { @@ -106,3 +106,5 @@ private: }; } + +#endif //OPENDHT_PROXY_SERVER diff --git a/include/opendht/dhtrunner.h b/include/opendht/dhtrunner.h index b384aba8..e2237fbf 100644 --- a/include/opendht/dhtrunner.h +++ b/include/opendht/dhtrunner.h @@ -36,7 +36,9 @@ #include #include +#if OPENDHT_PROXY_SERVER #include "dhtproxyserver.h" +#endif namespace dht { @@ -364,9 +366,9 @@ public: */ void join(); +#if OPENDHT_PROXY_SERVER void startProxyInterface(); // TODO arguments and move in the constructor - void startProxyClient(const std::string& url); - void stopProxyClient(); +#endif //OPENDHT_PROXY_SERVER private: static constexpr std::chrono::seconds BOOTSTRAP_PERIOD {10}; @@ -397,8 +399,9 @@ private: std::mutex sock_mtx {}; std::vector> rcv {}; +#if OPENDHT_PROXY_SERVER std::unique_ptr proxy_server_; - std::atomic_bool proxyfing {false}; +#endif //OPENDHT_PROXY_SERVER /** true if currently actively boostraping */ std::atomic_bool bootstraping {false}; diff --git a/include/opendht/value.h b/include/opendht/value.h index 9113805b..4119d7ab 100644 --- a/include/opendht/value.h +++ b/include/opendht/value.h @@ -19,7 +19,6 @@ #pragma once -#include "base64.h" #include "infohash.h" #include "crypto.h" #include "utils.h" @@ -38,7 +37,10 @@ #include #include +#if OPENDHT_PROXY_SERVER #include +#include "base64.h" +#endif //OPENDHT_PROXY_SERVER namespace dht { @@ -349,6 +351,7 @@ struct OPENDHT_PUBLIC Value Value(ValueType::Id t, const uint8_t* dat_ptr, size_t dat_len, Id id = INVALID_ID) : id(id), type(t), data(dat_ptr, dat_ptr+dat_len) {} +#if OPENDHT_PROXY_SERVER Value(Json::Value& json) { try { @@ -386,6 +389,7 @@ struct OPENDHT_PUBLIC Value if (json.isMember("utype")) user_type = json["utype"].asString(); } +#endif //OPENDHT_PROXY_SERVER template Value(ValueType::Id t, const Type& d, Id id = INVALID_ID) @@ -458,7 +462,9 @@ struct OPENDHT_PUBLIC Value return ss.str(); } +#if OPENDHT_PROXY_SERVER Json::Value toJson() const; +#endif //OPENDHT_PROXY_SERVER /** Return the size in bytes used by this value in memory (minimum). */ size_t size() const; diff --git a/src/base64.cpp b/src/base64.cpp index d11c28f2..4068c003 100644 --- a/src/base64.cpp +++ b/src/base64.cpp @@ -1,3 +1,4 @@ +#if OPENDHT_PROXY_SERVER #include "base64.h" static const std::string base64_chars = @@ -93,3 +94,4 @@ std::string base64_decode(std::string const& encoded_string) { return ret; } +#endif // OPENDHT_PROXY_SERVER diff --git a/src/dhtproxyserver.cpp b/src/dhtproxyserver.cpp index 13f4fe67..e297d02d 100644 --- a/src/dhtproxyserver.cpp +++ b/src/dhtproxyserver.cpp @@ -1,5 +1,6 @@ // TODO GPL +#if OPENDHT_PROXY_SERVER #include "dhtproxyserver.h" #include @@ -366,3 +367,4 @@ DhtProxyServer::getFiltered(const std::shared_ptr& session) co } } +#endif //OPENDHT_PROXY_SERVER diff --git a/src/dhtrunner.cpp b/src/dhtrunner.cpp index c1532feb..8019ef73 100644 --- a/src/dhtrunner.cpp +++ b/src/dhtrunner.cpp @@ -800,24 +800,13 @@ DhtRunner::findCertificate(InfoHash hash, std::function(new DhtProxyServer(this)); } - -void -DhtRunner::startProxyClient(const std::string& url) -{ - proxyfing = true; - // TODO DhtProxyClient -} - -void -DhtRunner::stopProxyClient() -{ - proxyfing = false; -} +#endif //OPENDHT_PROXY_SERVER } diff --git a/src/value.cpp b/src/value.cpp index 156a7938..1565ce8d 100644 --- a/src/value.cpp +++ b/src/value.cpp @@ -22,9 +22,9 @@ #include "default_types.h" #include "securedht.h" // print certificate ID - -#include +#if OPENDHT_PROXY_SERVER #include +#endif //OPENDHT_PROXY_SERVER namespace dht { @@ -172,6 +172,7 @@ Value::msgpack_unpack_body(const msgpack::object& o) } } +#if OPENDHT_PROXY_SERVER Json::Value Value::toJson() const { @@ -197,6 +198,7 @@ Value::toJson() const } return val; } +#endif //OPENDHT_PROXY_SERVER bool FieldValue::operator==(const FieldValue& vfd) const From 8344138bc8c0c1f680ded99b2669b4f72ef58548 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Blin?= Date: Wed, 1 Nov 2017 12:25:38 -0400 Subject: [PATCH 13/22] add optional + clean start/stop --- CMakeLists.txt | 5 +++++ include/opendht/dhtproxyserver.h | 6 +++++- include/opendht/dhtrunner.h | 3 ++- src/dhtproxyserver.cpp | 36 +++++++++++++++++--------------- src/dhtrunner.cpp | 11 ++++++++-- 5 files changed, 40 insertions(+), 21 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 30a934a0..c94b818f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -160,6 +160,11 @@ list (APPEND opendht_HEADERS if (OPENDHT_PROXY_SERVER) add_definitions(-DOPENDHT_PROXY_SERVER=true) + if (OPENDHT_PROXY_SERVER_OPTIONAL) + add_definitions(-DOPENDHT_PROXY_SERVER_OPTIONAL=true) + else () + add_definitions(-DOPENDHT_PROXY_SERVER_OPTIONAL=false) + endif() list (APPEND opendht_HEADERS include/opendht/dhtproxyserver.h include/opendht/base64.h diff --git a/include/opendht/dhtproxyserver.h b/include/opendht/dhtproxyserver.h index 380db0f5..17f13f9a 100644 --- a/include/opendht/dhtproxyserver.h +++ b/include/opendht/dhtproxyserver.h @@ -17,7 +17,7 @@ class DhtRunner; class OPENDHT_PUBLIC DhtProxyServer { public: - DhtProxyServer(DhtRunner* dht); + DhtProxyServer(DhtRunner* dht, unsigned int port = 8000); virtual ~DhtProxyServer(); DhtProxyServer(const DhtProxyServer& other) = default; @@ -25,6 +25,8 @@ public: DhtProxyServer& operator=(const DhtProxyServer& other) = default; DhtProxyServer& operator=(DhtProxyServer&& other) = default; + void stop(); + private: /** * Return the PublicKey id, the node id and node stats @@ -67,6 +69,7 @@ private: */ void put(const std::shared_ptr& session) const; +#if OPENDHT_PROXY_SERVER_OPTIONAL /** * Put a value to sign by the proxy on the DHT * Method: SIGN "/{InfoHash: .*}" @@ -86,6 +89,7 @@ private: * HTTP 400, body: {"err":"xxxx"} if bad json */ void putEncrypted(const std::shared_ptr& session) const; +#endif // OPENDHT_PROXY_SERVER_OPTIONAL /** * Return Values of a InfoHash filtered by a value id diff --git a/include/opendht/dhtrunner.h b/include/opendht/dhtrunner.h index e2237fbf..8e6cfd6b 100644 --- a/include/opendht/dhtrunner.h +++ b/include/opendht/dhtrunner.h @@ -367,7 +367,8 @@ public: void join(); #if OPENDHT_PROXY_SERVER - void startProxyInterface(); // TODO arguments and move in the constructor + void startProxyInterface(unsigned int port=8000); + void stopProxyInterface(); #endif //OPENDHT_PROXY_SERVER private: diff --git a/src/dhtproxyserver.cpp b/src/dhtproxyserver.cpp index e297d02d..de35c8cf 100644 --- a/src/dhtproxyserver.cpp +++ b/src/dhtproxyserver.cpp @@ -17,12 +17,12 @@ namespace dht { -DhtProxyServer::DhtProxyServer(DhtRunner* dht) : dht_(dht) +DhtProxyServer::DhtProxyServer(DhtRunner* dht, unsigned int port) : dht_(dht) { // NOTE in c++14, use make_unique service_ = std::unique_ptr(new restbed::Service()); - server_thread = std::thread([this]() { + server_thread = std::thread([this, port]() { // Create endpoints auto resource = std::make_shared(); resource->set_path("/"); @@ -53,7 +53,7 @@ DhtProxyServer::DhtProxyServer(DhtRunner* dht) : dht_(dht) this->put(session); } ); - // NOTE/TODO ENCRYPT AND SIGN must be optionnal +#if OPENDHT_PROXY_SERVER_OPTIONAL resource->set_method_handler("SIGN", [this](const std::shared_ptr session) { @@ -66,6 +66,7 @@ DhtProxyServer::DhtProxyServer(DhtRunner* dht) : dht_(dht) this->putEncrypted(session); } ); +#endif // OPENDHT_PROXY_SERVER_OPTIONAL service_->publish(resource); resource = std::make_shared(); resource->set_path("/{hash: .*}/{value: .*}"); @@ -80,25 +81,24 @@ DhtProxyServer::DhtProxyServer(DhtRunner* dht) : dht_(dht) // Start server auto settings = std::make_shared(); settings->set_default_header("Content-Type", "application/json"); - int port = 1984; - int started = false; - std::chrono::seconds sec(3600); - //std::chrono::milliseconds sec(std::numeric_limits::max()); - settings->set_connection_timeout(sec); // there is a timeout, but really huge - while (!started) - { - try { - settings->set_port(port); // TODO add argument and move this - service_->start(settings); - started = true; - } catch (std::system_error& e) { - port += 1; - } + std::chrono::milliseconds timeout(std::numeric_limits::max()); + settings->set_connection_timeout(timeout); // there is a timeout, but really huge + settings->set_port(port); + try { + service_->start(settings); + } catch(std::system_error& e) { + // Fail silently for now. } }); } DhtProxyServer::~DhtProxyServer() +{ + stop(); +} + +void +DhtProxyServer::stop() { service_->stop(); // listenThreads_ will stop because there is no more sessions @@ -244,6 +244,7 @@ DhtProxyServer::put(const std::shared_ptr& session) const ); } +#if OPENDHT_PROXY_SERVER_OPTIONAL void DhtProxyServer::putSigned(const std::shared_ptr& session) const { @@ -333,6 +334,7 @@ DhtProxyServer::putEncrypted(const std::shared_ptr& session) c } ); } +#endif // OPENDHT_PROXY_SERVER_OPTIONAL void DhtProxyServer::getFiltered(const std::shared_ptr& session) const diff --git a/src/dhtrunner.cpp b/src/dhtrunner.cpp index 8019ef73..c33ba231 100644 --- a/src/dhtrunner.cpp +++ b/src/dhtrunner.cpp @@ -802,10 +802,17 @@ DhtRunner::findCertificate(InfoHash hash, std::function(new DhtProxyServer(this)); + proxy_server_ = std::unique_ptr(new DhtProxyServer(this, port)); +} + +void +DhtRunner::stopProxyInterface() +{ + if (proxy_server_) + proxy_server_.reset(nullptr); } #endif //OPENDHT_PROXY_SERVER From 8e43775394d8a74709384c567c4fd3520f32f84b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Blin?= Date: Wed, 1 Nov 2017 14:04:09 -0400 Subject: [PATCH 14/22] update dhtnode + clean code --- CMakeLists.txt | 3 - cmake/FindRestbed.cmake | 1 - include/opendht/base64.h | 35 +++++++- include/opendht/dhtproxyserver.h | 45 ++++++++-- include/opendht/dhtrunner.h | 11 ++- include/opendht/value.h | 13 ++- src/base64.cpp | 149 +++++++++++++++++-------------- src/dhtproxyserver.cpp | 30 ++++++- src/dhtrunner.cpp | 4 +- src/value.cpp | 7 +- tools/dhtchat.cpp | 2 - tools/dhtnode.cpp | 26 +++++- 12 files changed, 230 insertions(+), 96 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c94b818f..333b1a6b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -84,9 +84,6 @@ endif () if (Nettle_INCLUDE_DIRS) include_directories (SYSTEM "${Nettle_INCLUDE_DIRS}") endif () -if (Restbed_INCLUDE_DIRS) - include_directories (SYSTEM "${Restbed_INCLUDE_DIRS}") -endif () if (Jsoncpp_INCLUDE_DIRS) include_directories (SYSTEM "${Jsoncpp_INCLUDE_DIRS}") endif () diff --git a/cmake/FindRestbed.cmake b/cmake/FindRestbed.cmake index 440e5596..05f1e814 100644 --- a/cmake/FindRestbed.cmake +++ b/cmake/FindRestbed.cmake @@ -14,5 +14,4 @@ endif() if (RESTBED_INCLUDE) set(RESTBED_FOUND TRUE) set(Restbed_LIBRARIES restbed) - set(Restbed_INCLUDE_DIRS ${RESTBED_INCLUDE}/restbed) endif() diff --git a/include/opendht/base64.h b/include/opendht/base64.h index 8f4d9ebb..294762a8 100644 --- a/include/opendht/base64.h +++ b/include/opendht/base64.h @@ -1,8 +1,39 @@ +/* + * Copyright (C) 2004-2017 Savoir-faire Linux Inc. + * + * This program is free software; you can redistribute it 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + #if OPENDHT_PROXY_SERVER #pragma once #include +#include + +/** + * Encode a string into base64 + * @param begin + * @param end + * @return the encoded vector in a string + */ +std::string base64_encode(const std::vector& str); +/** + * Decode a base64 encoded string + * @param str to decode + * @return the decoded string + */ +std::string base64_decode(std::string const& str); -std::string base64_encode(unsigned char const* , unsigned int len); -std::string base64_decode(std::string const& s); #endif // OPENDHT_PROXY_SERVER diff --git a/include/opendht/dhtproxyserver.h b/include/opendht/dhtproxyserver.h index 17f13f9a..52d9f0e1 100644 --- a/include/opendht/dhtproxyserver.h +++ b/include/opendht/dhtproxyserver.h @@ -1,4 +1,20 @@ -// TODO gpl +/* + * Copyright (C) 2016 Savoir-faire Linux Inc. + * Author : Sébastien Blin + * + * This program is free software; you can redistribute it 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 . + */ #if OPENDHT_PROXY_SERVER @@ -14,9 +30,19 @@ namespace dht { class DhtRunner; +/** + * Describes the REST API + */ class OPENDHT_PUBLIC DhtProxyServer { public: + /** + * Start the Http server for OpenDHT + * @param dht the DhtRunner linked to this proxy server + * @param port to listen + * @note if the server fails to start (if port is already used or reserved), + * it will fails silently + */ DhtProxyServer(DhtRunner* dht, unsigned int port = 8000); virtual ~DhtProxyServer(); @@ -25,13 +51,16 @@ public: DhtProxyServer& operator=(const DhtProxyServer& other) = default; DhtProxyServer& operator=(DhtProxyServer&& other) = default; + /** + * Stop the DhtProxyServer + */ void stop(); private: /** * Return the PublicKey id, the node id and node stats * Method: GET "/" - * Result: HTTP 200, body: {"id":"xxxx", "node_id":"xxxx", "ipv4":"xxxxxx", "ipv6": "xxxxx"} + * Result: HTTP 200, body: Value in JSON format (one part = one value) * On error: HTTP 404, body: {"err":"xxxx"} */ void getNodeInfo(const std::shared_ptr& session) const; @@ -40,8 +69,8 @@ private: * Return Values of a InfoHash * Method: GET "/{InfoHash: .*}" * Return: Multiple JSON object in parts. For 2 values, you will have 3 parts: - * {"data":"xxxx" (...) "type":3} (HTTP/1.1 200 OK Content-Type: application/json) - * {"data":"xxxx" (...) "type":3} (HTTP/1.1 200 OK Content-Type: application/json) + * Value in JSON format (HTTP/1.1 200 OK Content-Type: application/json) + * Value in JSON format (HTTP/1.1 200 OK Content-Type: application/json) * {"ok": 1} * * On error: HTTP 404, body: {"err":"xxxx"} @@ -52,8 +81,8 @@ private: * Listen incoming Values of a InfoHash. * Method: LISTEN "/{InfoHash: .*}" * Return: Multiple JSON object in parts. For 2 values, you will have 2 parts: - * {"data":"xxxx" (...) "type":3} (HTTP/1.1 200 OK Content-Type: application/json) - * {"data":"xxxx" (...) "type":3} (HTTP/1.1 200 OK Content-Type: application/json) + * Value in JSON format (HTTP/1.1 200 OK Content-Type: application/json) + * Value in JSON format (HTTP/1.1 200 OK Content-Type: application/json) * * On error: HTTP 404, body: {"err":"xxxx"} */ @@ -95,8 +124,8 @@ private: * Return Values of a InfoHash filtered by a value id * Method: GET "/{InfoHash: .*}/{ValueId: .*}" * Return: Multiple JSON object in parts. For 2 values, you will have 3 parts: - * {"data":"xxxx" (...) "type":3} (HTTP/1.1 200 OK Content-Type: application/json) - * {"data":"xxxx" (...) "type":3} (HTTP/1.1 200 OK Content-Type: application/json) + * Value in JSON format (HTTP/1.1 200 OK Content-Type: application/json) + * Value in JSON format (HTTP/1.1 200 OK Content-Type: application/json) * {"ok": 1} * * On error: HTTP 404, body: {"err":"xxxx"} diff --git a/include/opendht/dhtrunner.h b/include/opendht/dhtrunner.h index 8e6cfd6b..acaac185 100644 --- a/include/opendht/dhtrunner.h +++ b/include/opendht/dhtrunner.h @@ -367,8 +367,15 @@ public: void join(); #if OPENDHT_PROXY_SERVER - void startProxyInterface(unsigned int port=8000); - void stopProxyInterface(); + /** + * Start the proxy server + * @param port used for the server + */ + void startProxyServer(unsigned int port=8000); + /** + * Stop the proxy server + */ + void stopProxyServer(); #endif //OPENDHT_PROXY_SERVER private: diff --git a/include/opendht/value.h b/include/opendht/value.h index 4119d7ab..e27e0f12 100644 --- a/include/opendht/value.h +++ b/include/opendht/value.h @@ -352,8 +352,11 @@ struct OPENDHT_PUBLIC Value : id(id), type(t), data(dat_ptr, dat_ptr+dat_len) {} #if OPENDHT_PROXY_SERVER + /** + * Build a value from a json object + * @param json + */ Value(Json::Value& json) { - try { if (json.isMember("id")) id = ValueType::Id(json["id"].asInt()); @@ -463,6 +466,14 @@ struct OPENDHT_PUBLIC Value } #if OPENDHT_PROXY_SERVER + /** + * Build a json object from a value + * Example: + * { + * "data":"base64ofdata", + * id":"0", "seq":0,"type":3 + * } + */ Json::Value toJson() const; #endif //OPENDHT_PROXY_SERVER diff --git a/src/base64.cpp b/src/base64.cpp index 4068c003..2732f40d 100644 --- a/src/base64.cpp +++ b/src/base64.cpp @@ -1,97 +1,114 @@ +/* + * Copyright (C) 2004-2017 Savoir-faire Linux Inc. + * + * This program is free software; you can redistribute it 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ #if OPENDHT_PROXY_SERVER #include "base64.h" static const std::string base64_chars = - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz" - "0123456789+/"; + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; static inline bool is_base64(unsigned char c) { - return (isalnum(c) || (c == '+') || (c == '/')); + return (isalnum(c) || (c == '+') || (c == '/')); } -std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) { - std::string ret; - int i = 0; - int j = 0; - unsigned char char_array_3[3]; - unsigned char char_array_4[4]; +std::string base64_encode(const std::vector& str) { + unsigned char const* bytes_to_encode = str.data(); + unsigned int in_len = str.size(); + std::string ret; + int i = 0; + int j = 0; + unsigned char char_array_3[3]; + unsigned char char_array_4[4]; - while (in_len--) { - char_array_3[i++] = *(bytes_to_encode++); - if (i == 3) { - char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; - char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); - char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); - char_array_4[3] = char_array_3[2] & 0x3f; + while (in_len--) { + char_array_3[i++] = *(bytes_to_encode++); + if (i == 3) { + char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; + char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); + char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); + char_array_4[3] = char_array_3[2] & 0x3f; - for(i = 0; (i <4) ; i++) - ret += base64_chars[char_array_4[i]]; - i = 0; + for(i = 0; (i <4) ; i++) + ret += base64_chars[char_array_4[i]]; + i = 0; + } } - } - if (i) - { - for(j = i; j < 3; j++) - char_array_3[j] = '\0'; + if (i) + { + for(j = i; j < 3; j++) + char_array_3[j] = '\0'; - char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; - char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); - char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); - char_array_4[3] = char_array_3[2] & 0x3f; + char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; + char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); + char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); + char_array_4[3] = char_array_3[2] & 0x3f; - for (j = 0; (j < i + 1); j++) - ret += base64_chars[char_array_4[j]]; + for (j = 0; (j < i + 1); j++) + ret += base64_chars[char_array_4[j]]; - while((i++ < 3)) - ret += '='; - - } - - return ret; + while((i++ < 3)) + ret += '='; + } + return ret; } std::string base64_decode(std::string const& encoded_string) { - size_t in_len = encoded_string.size(); - size_t i = 0; - size_t j = 0; - int in_ = 0; - unsigned char char_array_4[4], char_array_3[3]; - std::string ret; + size_t in_len = encoded_string.size(); + size_t i = 0; + size_t j = 0; + int in_ = 0; + unsigned char char_array_4[4], char_array_3[3]; + std::string ret; - while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) { - char_array_4[i++] = encoded_string[in_]; in_++; - if (i ==4) { - for (i = 0; i <4; i++) - char_array_4[i] = static_cast(base64_chars.find(char_array_4[i])); + while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) { + char_array_4[i++] = encoded_string[in_]; in_++; + if (i ==4) { + for (i = 0; i <4; i++) + char_array_4[i] = static_cast(base64_chars.find(char_array_4[i])); - char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); - char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); - char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; - for (i = 0; (i < 3); i++) - ret += char_array_3[i]; - i = 0; + for (i = 0; (i < 3); i++) + ret += char_array_3[i]; + i = 0; + } } - } - if (i) { - for (j = i; j <4; j++) - char_array_4[j] = 0; + if (i) { + for (j = i; j <4; j++) + char_array_4[j] = 0; - for (j = 0; j <4; j++) - char_array_4[j] = static_cast(base64_chars.find(char_array_4[j])); + for (j = 0; j <4; j++) + char_array_4[j] = static_cast(base64_chars.find(char_array_4[j])); - char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); - char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); - char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; - for (j = 0; (j < i - 1); j++) ret += char_array_3[j]; - } + for (j = 0; (j < i - 1); j++) ret += char_array_3[j]; + } - return ret; + return ret; } #endif // OPENDHT_PROXY_SERVER diff --git a/src/dhtproxyserver.cpp b/src/dhtproxyserver.cpp index de35c8cf..8557f874 100644 --- a/src/dhtproxyserver.cpp +++ b/src/dhtproxyserver.cpp @@ -1,4 +1,20 @@ -// TODO GPL +/* + * Copyright (C) 2016 Savoir-faire Linux Inc. + * Author : Sébastien Blin + * + * This program is free software; you can redistribute it 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 . + */ #if OPENDHT_PROXY_SERVER #include "dhtproxyserver.h" @@ -117,6 +133,7 @@ DhtProxyServer::getNodeInfo(const std::shared_ptr& session) co session->fetch(content_length, [&](const std::shared_ptr s, const restbed::Bytes& b) { + (void)b; if (dht_) { Json::Value result; result["id"] = dht_->getId().toString(); @@ -142,6 +159,7 @@ DhtProxyServer::get(const std::shared_ptr& session) const session->fetch(content_length, [&](const std::shared_ptr s, const restbed::Bytes& b) { + (void)b; if (dht_) { InfoHash infoHash(hash); if (infoHash == INVALID_ID) { @@ -149,10 +167,12 @@ DhtProxyServer::get(const std::shared_ptr& session) const } Json::FastWriter writer; dht_->get(infoHash, [s, &writer](std::shared_ptr value) { + // Send values as soon as we get them Json::Value result; s->yield(restbed::OK, writer.write(value->toJson())); return true; }, [s, &writer](bool ok) { + // Communication is finished auto response = std::to_string(ok); s->close(restbed::OK, "{\"ok\": " + response + "}"); }); @@ -177,15 +197,17 @@ DhtProxyServer::listen(const std::shared_ptr& session) const session->fetch(content_length, [&](const std::shared_ptr s, const restbed::Bytes& b) { + (void)b; if (dht_) { Json::FastWriter writer; token = dht_->listen(infoHash, [s, &writer](std::shared_ptr value) { - Json::Value result; + // Send values as soon as we get them if (!s->is_closed()) s->yield(restbed::OK, writer.write(value->toJson())); return !s->is_closed(); }).get(); // Handle client deconnection + // NOTE: for now, there is no handler, so we test the session in a thread listenThreads_.emplace_back(std::shared_ptr( new std::thread([&]() { while(!s->is_closed()) @@ -205,7 +227,7 @@ DhtProxyServer::listen(const std::shared_ptr& session) const void DhtProxyServer::put(const std::shared_ptr& session) const { - // TODO test with encrypted and signed value to send + // TODO test with the proxy client static constexpr dht::InfoHash INVALID_ID {}; const auto request = session->get_request(); int content_length = std::stoi(request->get_header("Content-Length", "0")); @@ -228,6 +250,7 @@ DhtProxyServer::put(const std::shared_ptr& session) const std::string strJson(buf.begin(), buf.end()); bool parsingSuccessful = reader.parse(strJson.c_str(), root); if (parsingSuccessful) { + // Build the Value from json auto value = std::make_shared(root); auto response = value->toString(); @@ -347,6 +370,7 @@ DhtProxyServer::getFiltered(const std::shared_ptr& session) co session->fetch(content_length, [&](const std::shared_ptr s, const restbed::Bytes& b) { + (void)b; if (dht_) { InfoHash infoHash(hash); if (infoHash == INVALID_ID) { diff --git a/src/dhtrunner.cpp b/src/dhtrunner.cpp index c33ba231..8988c3e8 100644 --- a/src/dhtrunner.cpp +++ b/src/dhtrunner.cpp @@ -802,14 +802,14 @@ DhtRunner::findCertificate(InfoHash hash, std::function(new DhtProxyServer(this, port)); } void -DhtRunner::stopProxyInterface() +DhtRunner::stopProxyServer() { if (proxy_server_) proxy_server_.reset(nullptr); diff --git a/src/value.cpp b/src/value.cpp index 1565ce8d..a4e02bc4 100644 --- a/src/value.cpp +++ b/src/value.cpp @@ -176,14 +176,13 @@ Value::msgpack_unpack_body(const msgpack::object& o) Json::Value Value::toJson() const { - // TODO check Json::Value val; val["id"] = std::to_string(id); if (isEncrypted()) { - val["cypher"] = base64_encode(cypher.data(), cypher.size()); + val["cypher"] = base64_encode(cypher); } else { if (isSigned()) - val["sig"] = base64_encode(signature.data(), signature.size()); + val["sig"] = base64_encode(signature); bool has_owner = owner && *owner; if (has_owner) { // isSigned val["seq"] = seq; @@ -192,7 +191,7 @@ Value::toJson() const val["to"] = recipient.toString(); } val["type"] = type; - val["data"] = base64_encode(data.data(), data.size()); + val["data"] = base64_encode(data); if (not user_type.empty()) val["utype"] = user_type; } diff --git a/tools/dhtchat.cpp b/tools/dhtchat.cpp index b64b4c95..8e48723f 100644 --- a/tools/dhtchat.cpp +++ b/tools/dhtchat.cpp @@ -67,8 +67,6 @@ main(int argc, char **argv) if (not params.bootstrap.first.empty()) dht.bootstrap(params.bootstrap.first.c_str(), params.bootstrap.second.c_str()); - dht.startProxyInterface(); - print_node_info(dht, params); std::cout << " type 'c {hash}' to join a channel" << std::endl << std::endl; diff --git a/tools/dhtnode.cpp b/tools/dhtnode.cpp index 50255e50..cb4dc125 100644 --- a/tools/dhtnode.cpp +++ b/tools/dhtnode.cpp @@ -55,16 +55,23 @@ void print_help() { << " ll Print basic information and stats about the current node." << std::endl << " ls [key] Print basic information about current search(es)." << std::endl << " ld [key] Print basic information about currenty stored values on this node (or key)." << std::endl - << " lr Print the full current routing table of this node" << std::endl; + << " lr Print the full current routing table of this node." << std::endl; + +#if OPENDHT_PROXY_SERVER + std::cout << std::endl << "Operations with the proxy:" << std::endl + << " psr [port] Start the proxy interface on port." << std::endl + << " pst Stop the proxy interface." << std::endl; +#endif //OPENDHT_PROXY_SERVER std::cout << std::endl << "Operations on the DHT:" << std::endl - << " b Ping potential node at given IP address/port." << std::endl + << " b Ping potential node at given IP address/port." << std::endl << " g Get values at ." << std::endl << " l Listen for value changes at ." << std::endl << " p Put string value at ." << std::endl << " pp Put string value at (persistent version)." << std::endl << " s Put string value at , signed with our generated private key." << std::endl << " e Put string value at , encrypted for with its public key (if found)." << std::endl; + std::cout << std::endl << "Indexation operations on the DHT:" << std::endl << " il [exact match] Lookup the index named with the key ." << std::endl << " Set [exact match] to 'false' for inexact match lookup." << std::endl @@ -162,6 +169,21 @@ void cmd_loop(std::shared_ptr& dht, dht_params& params) dht->setLogFilter(filter); continue; } +#if OPENDHT_PROXY_SERVER + else if (op == "psr") { + iss >> idstr; + try { + unsigned int port = std::stoi(idstr); + dht->startProxyServer(port); + } catch (...) { + dht->startProxyServer(); + } + continue; + } else if (op == "pst") { + dht->stopProxyServer(); + continue; + } +#endif //OPENDHT_PROXY_SERVER if (op.empty()) continue; From 61a148e83f53596f8217879b0dd456eac0d9990e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Blin?= Date: Wed, 1 Nov 2017 14:08:42 -0400 Subject: [PATCH 15/22] update README.md --- README.md | 8 ++++++-- tools/dhtnode.cpp | 8 ++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 0b6d58bb..885afaff 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@

- OpenDHT + OpenDHT

A lightweight C++11 Distributed Hash Table implementation originally based on https://github.com/jech/dht by Juliusz Chroboczek. @@ -18,7 +18,7 @@ See the wiki: #### How-to build and install -Build instructions : +Build instructions: #### How-to build a simple client app ```bash @@ -94,6 +94,10 @@ for r in results: - Build tested with GCC 4.8+ (GNU/Linux, Android, Windows with MinGW), Clang/LLVM (Linux, macOS). - Build tested with Microsoft Visual Studio 2015 +### Optional dependencies +- restbed 4.0+, used for the REST API. +- jsoncpp 1.7.4-3+, used for the REST API. + ## Contact IRC: join us on Freenode at [`#opendht`](https://webchat.freenode.net/?channels=%23opendht). diff --git a/tools/dhtnode.cpp b/tools/dhtnode.cpp index cb4dc125..31f845a0 100644 --- a/tools/dhtnode.cpp +++ b/tools/dhtnode.cpp @@ -59,8 +59,8 @@ void print_help() { #if OPENDHT_PROXY_SERVER std::cout << std::endl << "Operations with the proxy:" << std::endl - << " psr [port] Start the proxy interface on port." << std::endl - << " pst Stop the proxy interface." << std::endl; + << " pst [port] Start the proxy interface on port." << std::endl + << " psp Stop the proxy interface." << std::endl; #endif //OPENDHT_PROXY_SERVER std::cout << std::endl << "Operations on the DHT:" << std::endl @@ -170,7 +170,7 @@ void cmd_loop(std::shared_ptr& dht, dht_params& params) continue; } #if OPENDHT_PROXY_SERVER - else if (op == "psr") { + else if (op == "pst") { iss >> idstr; try { unsigned int port = std::stoi(idstr); @@ -179,7 +179,7 @@ void cmd_loop(std::shared_ptr& dht, dht_params& params) dht->startProxyServer(); } continue; - } else if (op == "pst") { + } else if (op == "psp") { dht->stopProxyServer(); continue; } From 9abc88f378927c8d30d22b4d760afeffe56141c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Blin?= Date: Wed, 1 Nov 2017 17:03:33 -0400 Subject: [PATCH 16/22] add --enable-proxy-server option for autotools --- configure.ac | 20 ++++++++++++++++++++ src/Makefile.am | 5 +++++ tools/Makefile.am | 6 ++++++ 3 files changed, 31 insertions(+) diff --git a/configure.ac b/configure.ac index 504de170..6689c437 100644 --- a/configure.ac +++ b/configure.ac @@ -128,6 +128,26 @@ AM_COND_IF([ENABLE_TOOLS], [ ]) ]) +AC_ARG_ENABLE([proxy_server], AS_HELP_STRING([--enable-proxy-server], [Enable proxy server ability]), proxy_server=yes, proxy_server=no) +AM_CONDITIONAL(ENABLE_PROXY_SERVER, test x$proxy_server == xyes) +AM_COND_IF([ENABLE_PROXY_SERVER], [ + AC_CHECK_LIB(restbed, exit,, AC_MSG_ERROR([Missing restbed files])) + PKG_CHECK_MODULES([Jsoncpp], [jsoncpp >= 1.7.4]) + CPPFLAGS+=" -DOPENDHT_PROXY_SERVER=true -ljsoncpp -lrestbed" + + AC_ARG_ENABLE([proxy_server_optionals], AS_HELP_STRING([--enable-proxy-server-optionals], + [Enable proxy server ability]), proxy_server_optionals=yes, proxy_server_optionals=no) + AM_CONDITIONAL(ENABLE_PROXY_SERVER_OPTIONALS, test x$proxy_server_optionals == xyes) + AM_COND_IF([ENABLE_PROXY_SERVER_OPTIONALS], [ + CPPFLAGS+=" -DOPENDHT_PROXY_SERVER_OPTIONAL=true" + ], [ + CPPFLAGS+=" -DOPENDHT_PROXY_SERVER_OPTIONAL=false" + ]) +], [ + CPPFLAGS+=" -DOPENDHT_PROXY_SERVER=false" +]) + + AC_CONFIG_FILES([doc/Doxyfile doc/Makefile]) dnl Configure setup.py if we build the python module diff --git a/src/Makefile.am b/src/Makefile.am index f4614795..e9dd2e9f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -54,6 +54,11 @@ nobase_include_HEADERS = \ ../include/opendht/rng.h \ ../include/opendht/indexation/pht.h +if ENABLE_PROXY_SERVER +libopendht_la_SOURCES += base64.cpp dhtproxyserver.cpp +nobase_include_HEADERS += ../include/opendht/base64.h ../include/opendht/dhtproxyserver.h +endif + clean-local: rm -rf libargon2.la diff --git a/tools/Makefile.am b/tools/Makefile.am index 610fd76f..821ddce1 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -11,3 +11,9 @@ dhtchat_LDFLAGS = -lopendht -lreadline -L@top_builddir@/src/.libs @Argon2_LDFLAG dhtscanner_SOURCES = dhtscanner.cpp dhtscanner_LDFLAGS = -lopendht -lreadline -L@top_builddir@/src/.libs @Argon2_LDFLAGS@ @GnuTLS_LIBS@ + +if ENABLE_PROXY_SERVER +dhtnode_LDFLAGS += -lrestbed -ljsoncpp +dhtchat_LDFLAGS += -lrestbed -ljsoncpp +dhtscanner_LDFLAGS += -lrestbed -ljsoncpp +endif From 6413571301daaa2910ea5482b4084887156e63e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Blin?= Date: Fri, 3 Nov 2017 10:32:48 -0400 Subject: [PATCH 17/22] clean code --- CMakeLists.txt | 15 ++- README.md | 6 +- configure.ac | 12 +-- include/opendht.h | 3 + include/opendht/base64.h | 39 -------- .../{dhtproxyserver.h => dht_proxy_server.h} | 38 ++++++-- include/opendht/dhtrunner.h | 20 ---- include/opendht/value.h | 38 +------- src/Makefile.am | 4 +- src/base64.cpp | 43 +++++---- src/base64.h | 44 +++++++++ ...htproxyserver.cpp => dht_proxy_server.cpp} | 93 +++++++++++-------- src/dhtrunner.cpp | 16 ---- src/value.cpp | 40 +++++++- tools/Makefile.am | 6 -- tools/dhtnode.cpp | 20 ++-- 16 files changed, 224 insertions(+), 213 deletions(-) delete mode 100644 include/opendht/base64.h rename include/opendht/{dhtproxyserver.h => dht_proxy_server.h} (80%) create mode 100644 src/base64.h rename src/{dhtproxyserver.cpp => dht_proxy_server.cpp} (85%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 333b1a6b..4efc5af5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,7 +17,7 @@ option (OPENDHT_ARGON2 "Use included argon2 sources" OFF) option (OPENDHT_LTO "Build with LTO" OFF) option (OPENDHT_SANITIZE "Build with address sanitizer and stack protector" OFF) option (OPENDHT_PROXY_SERVER "Enable DHT proxy server, use Restbed and jsoncpp" OFF) -option (OPENDHT_PROXY_SERVER_OPTIONAL "Enable sign and encrypt endpoints" OFF) +option (OPENDHT_PROXY_SERVER_IDENTITY "Allow clients to use the node identity" OFF) option (OPENDHT_PROXY_CLIENT "Enable DHT proxy client, use Restbed and jsoncpp" OFF) find_package(Doxygen) @@ -93,7 +93,6 @@ include_directories ( include/ include/opendht/ ${CMAKE_CURRENT_BINARY_DIR}/include/ - /usr/include/jsoncpp ) # Install dirs @@ -157,17 +156,17 @@ list (APPEND opendht_HEADERS if (OPENDHT_PROXY_SERVER) add_definitions(-DOPENDHT_PROXY_SERVER=true) - if (OPENDHT_PROXY_SERVER_OPTIONAL) - add_definitions(-DOPENDHT_PROXY_SERVER_OPTIONAL=true) + if (OPENDHT_PROXY_SERVER_IDENTITY) + add_definitions(-DOPENDHT_PROXY_SERVER_IDENTITY=true) else () - add_definitions(-DOPENDHT_PROXY_SERVER_OPTIONAL=false) + add_definitions(-DOPENDHT_PROXY_SERVER_IDENTITY=false) endif() list (APPEND opendht_HEADERS - include/opendht/dhtproxyserver.h - include/opendht/base64.h + include/opendht/dht_proxy_server.h ) list (APPEND opendht_SOURCES - src/dhtproxyserver.cpp + src/dht_proxy_server.cpp + src/base64.h src/base64.cpp ) else () diff --git a/README.md b/README.md index 885afaff..1ce85436 100644 --- a/README.md +++ b/README.md @@ -91,13 +91,11 @@ for r in results: - msgpack-c 1.2+, used for data serialization. - GnuTLS 3.3+, used for cryptographic operations. - Nettle 2.4+, a GnuTLS dependency for crypto. +- (optional) restbed 4.0+, used for the REST API. +- (optional) jsoncpp 1.7.4-3+, used for the REST API. - Build tested with GCC 4.8+ (GNU/Linux, Android, Windows with MinGW), Clang/LLVM (Linux, macOS). - Build tested with Microsoft Visual Studio 2015 -### Optional dependencies -- restbed 4.0+, used for the REST API. -- jsoncpp 1.7.4-3+, used for the REST API. - ## Contact IRC: join us on Freenode at [`#opendht`](https://webchat.freenode.net/?channels=%23opendht). diff --git a/configure.ac b/configure.ac index 6689c437..5863244b 100644 --- a/configure.ac +++ b/configure.ac @@ -135,13 +135,13 @@ AM_COND_IF([ENABLE_PROXY_SERVER], [ PKG_CHECK_MODULES([Jsoncpp], [jsoncpp >= 1.7.4]) CPPFLAGS+=" -DOPENDHT_PROXY_SERVER=true -ljsoncpp -lrestbed" - AC_ARG_ENABLE([proxy_server_optionals], AS_HELP_STRING([--enable-proxy-server-optionals], - [Enable proxy server ability]), proxy_server_optionals=yes, proxy_server_optionals=no) - AM_CONDITIONAL(ENABLE_PROXY_SERVER_OPTIONALS, test x$proxy_server_optionals == xyes) - AM_COND_IF([ENABLE_PROXY_SERVER_OPTIONALS], [ - CPPFLAGS+=" -DOPENDHT_PROXY_SERVER_OPTIONAL=true" + AC_ARG_ENABLE([proxy_server_identity], AS_HELP_STRING([--enable-proxy-server-identity], + [Enable proxy server ability]), proxy_server_identity=yes, proxy_server_identity=no) + AM_CONDITIONAL(ENABLE_PROXY_SERVER_IDENTITY, test x$proxy_server_identity == xyes) + AM_COND_IF([ENABLE_PROXY_SERVER_IDENTITY], [ + CPPFLAGS+=" -DOPENDHT_PROXY_SERVER_IDENTITY=true" ], [ - CPPFLAGS+=" -DOPENDHT_PROXY_SERVER_OPTIONAL=false" + CPPFLAGS+=" -DOPENDHT_PROXY_SERVER_IDENTITY=false" ]) ], [ CPPFLAGS+=" -DOPENDHT_PROXY_SERVER=false" diff --git a/include/opendht.h b/include/opendht.h index 98d3ece9..e003a216 100644 --- a/include/opendht.h +++ b/include/opendht.h @@ -19,6 +19,9 @@ #pragma once #include "opendht/dhtrunner.h" +#if OPENDHT_PROXY_SERVER +#include "opendht/dht_proxy_server.h" +#endif #include "opendht/log.h" #include "opendht/default_types.h" #include "opendht/indexation/pht.h" diff --git a/include/opendht/base64.h b/include/opendht/base64.h deleted file mode 100644 index 294762a8..00000000 --- a/include/opendht/base64.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2004-2017 Savoir-faire Linux Inc. - * - * This program is free software; you can redistribute it 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, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#if OPENDHT_PROXY_SERVER -#pragma once - -#include -#include - -/** - * Encode a string into base64 - * @param begin - * @param end - * @return the encoded vector in a string - */ -std::string base64_encode(const std::vector& str); -/** - * Decode a base64 encoded string - * @param str to decode - * @return the decoded string - */ -std::string base64_decode(std::string const& str); - -#endif // OPENDHT_PROXY_SERVER diff --git a/include/opendht/dhtproxyserver.h b/include/opendht/dht_proxy_server.h similarity index 80% rename from include/opendht/dhtproxyserver.h rename to include/opendht/dht_proxy_server.h index 52d9f0e1..d614ecf0 100644 --- a/include/opendht/dhtproxyserver.h +++ b/include/opendht/dht_proxy_server.h @@ -21,6 +21,8 @@ #pragma once #include "def.h" +#include "sockaddr.h" +#include "infohash.h" #include #include @@ -29,6 +31,7 @@ namespace dht { class DhtRunner; +class InfoHash; /** * Describes the REST API @@ -43,13 +46,13 @@ public: * @note if the server fails to start (if port is already used or reserved), * it will fails silently */ - DhtProxyServer(DhtRunner* dht, unsigned int port = 8000); + DhtProxyServer(std::shared_ptr dht, in_port_t port = 8000); virtual ~DhtProxyServer(); - DhtProxyServer(const DhtProxyServer& other) = default; - DhtProxyServer(DhtProxyServer&& other) = default; - DhtProxyServer& operator=(const DhtProxyServer& other) = default; - DhtProxyServer& operator=(DhtProxyServer&& other) = default; + DhtProxyServer(const DhtProxyServer& other) = delete; + DhtProxyServer(DhtProxyServer&& other) = delete; + DhtProxyServer& operator=(const DhtProxyServer& other) = delete; + DhtProxyServer& operator=(DhtProxyServer&& other) = delete; /** * Stop the DhtProxyServer @@ -62,6 +65,7 @@ private: * Method: GET "/" * Result: HTTP 200, body: Value in JSON format (one part = one value) * On error: HTTP 404, body: {"err":"xxxx"} + * @param session */ void getNodeInfo(const std::shared_ptr& session) const; @@ -74,6 +78,7 @@ private: * {"ok": 1} * * On error: HTTP 404, body: {"err":"xxxx"} + * @param session */ void get(const std::shared_ptr& session) const; @@ -85,6 +90,7 @@ private: * Value in JSON format (HTTP/1.1 200 OK Content-Type: application/json) * * On error: HTTP 404, body: {"err":"xxxx"} + * @param session */ void listen(const std::shared_ptr& session) const; @@ -95,10 +101,11 @@ private: * Return: {"ok":"1"} * On error: HTTP 404, body: {"err":"xxxx"} if no dht * HTTP 400, body: {"err":"xxxx"} if bad json + * @param session */ void put(const std::shared_ptr& session) const; -#if OPENDHT_PROXY_SERVER_OPTIONAL +#if OPENDHT_PROXY_SERVER_IDENTITY /** * Put a value to sign by the proxy on the DHT * Method: SIGN "/{InfoHash: .*}" @@ -106,6 +113,7 @@ private: * Return: {"ok":"1"} * On error: HTTP 404, body: {"err":"xxxx"} if no dht * HTTP 400, body: {"err":"xxxx"} if bad json + * @param session */ void putSigned(const std::shared_ptr& session) const; @@ -116,9 +124,10 @@ private: * Return: {"ok":"1"} * On error: HTTP 404, body: {"err":"xxxx"} if no dht * HTTP 400, body: {"err":"xxxx"} if bad json + * @param session */ void putEncrypted(const std::shared_ptr& session) const; -#endif // OPENDHT_PROXY_SERVER_OPTIONAL +#endif // OPENDHT_PROXY_SERVER_IDENTITY /** * Return Values of a InfoHash filtered by a value id @@ -129,13 +138,24 @@ private: * {"ok": 1} * * On error: HTTP 404, body: {"err":"xxxx"} + * @param session */ void getFiltered(const std::shared_ptr& session) const; std::thread server_thread {}; std::unique_ptr service_; - DhtRunner* dht_; - mutable std::vector> listenThreads_; + std::shared_ptr dht_; + + // Handle client quit for listen. + // NOTE: can be simplified when we will supports restbed 5.0 + std::thread listenThread_; + struct SessionToHashToken { + std::shared_ptr session; + InfoHash hash; + size_t token; + }; + mutable std::vector currentListeners_; + std::atomic_bool stopListeners {false}; }; } diff --git a/include/opendht/dhtrunner.h b/include/opendht/dhtrunner.h index acaac185..fd3346c0 100644 --- a/include/opendht/dhtrunner.h +++ b/include/opendht/dhtrunner.h @@ -36,10 +36,6 @@ #include #include -#if OPENDHT_PROXY_SERVER -#include "dhtproxyserver.h" -#endif - namespace dht { struct Node; @@ -366,18 +362,6 @@ public: */ void join(); -#if OPENDHT_PROXY_SERVER - /** - * Start the proxy server - * @param port used for the server - */ - void startProxyServer(unsigned int port=8000); - /** - * Stop the proxy server - */ - void stopProxyServer(); -#endif //OPENDHT_PROXY_SERVER - private: static constexpr std::chrono::seconds BOOTSTRAP_PERIOD {10}; @@ -407,10 +391,6 @@ private: std::mutex sock_mtx {}; std::vector> rcv {}; -#if OPENDHT_PROXY_SERVER - std::unique_ptr proxy_server_; -#endif //OPENDHT_PROXY_SERVER - /** true if currently actively boostraping */ std::atomic_bool bootstraping {false}; /* bootstrap nodes given as (host, service) pairs */ diff --git a/include/opendht/value.h b/include/opendht/value.h index e27e0f12..60dd1d99 100644 --- a/include/opendht/value.h +++ b/include/opendht/value.h @@ -39,7 +39,6 @@ #if OPENDHT_PROXY_SERVER #include -#include "base64.h" #endif //OPENDHT_PROXY_SERVER namespace dht { @@ -356,42 +355,7 @@ struct OPENDHT_PUBLIC Value * Build a value from a json object * @param json */ - Value(Json::Value& json) { - try { - if (json.isMember("id")) - id = ValueType::Id(json["id"].asInt()); - } catch (...) { } - if (json.isMember("cypher")) { - auto cypherStr = json["cypher"].asString(); - cypherStr = base64_decode(cypherStr); - cypher = std::vector(cypherStr.begin(), cypherStr.end()); - } - if (json.isMember("sig")) { - auto sigStr = json["sig"].asString(); - sigStr = base64_decode(sigStr); - signature = std::vector(sigStr.begin(), sigStr.end()); - } - if (json.isMember("seq")) - seq = json["seq"].asInt(); - if (json.isMember("owner")) { - auto ownerStr = json["owner"].asString(); - auto ownerBlob = std::vector(ownerStr.begin(), ownerStr.end()); - owner = std::make_shared(ownerBlob); - } - if (json.isMember("to")) { - auto toStr = json["to"].asString(); - recipient = InfoHash(toStr); - } - if (json.isMember("type")) - type = json["type"].asInt(); - if (json.isMember("data")){ - auto dataStr = json["data"].asString(); - dataStr = base64_decode(dataStr); - data = std::vector(dataStr.begin(), dataStr.end()); - } - if (json.isMember("utype")) - user_type = json["utype"].asString(); - } + Value(Json::Value& json); #endif //OPENDHT_PROXY_SERVER template diff --git a/src/Makefile.am b/src/Makefile.am index e9dd2e9f..4ff14741 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -55,8 +55,8 @@ nobase_include_HEADERS = \ ../include/opendht/indexation/pht.h if ENABLE_PROXY_SERVER -libopendht_la_SOURCES += base64.cpp dhtproxyserver.cpp -nobase_include_HEADERS += ../include/opendht/base64.h ../include/opendht/dhtproxyserver.h +libopendht_la_SOURCES += base64.h base64.cpp dht_proxy_server.cpp +nobase_include_HEADERS += ../include/opendht/dht_proxy_server.h endif clean-local: diff --git a/src/base64.cpp b/src/base64.cpp index 2732f40d..4971e9e0 100644 --- a/src/base64.cpp +++ b/src/base64.cpp @@ -1,21 +1,29 @@ /* - * Copyright (C) 2004-2017 Savoir-faire Linux Inc. - * - * This program is free software; you can redistribute it 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, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -#if OPENDHT_PROXY_SERVER + base64.cpp and base64.h + + Copyright (C) 2004-2008 René Nyffenegger + + This source code is provided 'as-is', without any express or implied + warranty. In no event will the author be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this source code must not be misrepresented; you must not + claim that you wrote the original source code. If you use this source code + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original source code. + + 3. This notice may not be removed or altered from any source distribution. + + René Nyffenegger rene.nyffenegger@adp-gmbh.ch + +*/ #include "base64.h" static const std::string base64_chars = @@ -111,4 +119,3 @@ std::string base64_decode(std::string const& encoded_string) { return ret; } -#endif // OPENDHT_PROXY_SERVER diff --git a/src/base64.h b/src/base64.h new file mode 100644 index 00000000..ad3d2814 --- /dev/null +++ b/src/base64.h @@ -0,0 +1,44 @@ +/* + base64.cpp and base64.h + + Copyright (C) 2004-2008 René Nyffenegger + + This source code is provided 'as-is', without any express or implied + warranty. In no event will the author be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this source code must not be misrepresented; you must not + claim that you wrote the original source code. If you use this source code + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original source code. + + 3. This notice may not be removed or altered from any source distribution. + + René Nyffenegger rene.nyffenegger@adp-gmbh.ch + +*/ +#pragma once + +#include +#include + +/** + * Encode a string into base64 + * @param begin + * @param end + * @return the encoded vector in a string + */ +std::string base64_encode(const std::vector& str); +/** + * Decode a base64 encoded string + * @param str to decode + * @return the decoded string + */ +std::string base64_decode(std::string const& str); diff --git a/src/dhtproxyserver.cpp b/src/dht_proxy_server.cpp similarity index 85% rename from src/dhtproxyserver.cpp rename to src/dht_proxy_server.cpp index 8557f874..ba6a998d 100644 --- a/src/dhtproxyserver.cpp +++ b/src/dht_proxy_server.cpp @@ -17,7 +17,7 @@ */ #if OPENDHT_PROXY_SERVER -#include "dhtproxyserver.h" +#include "dht_proxy_server.h" #include #include @@ -29,11 +29,10 @@ #include #include -#include - namespace dht { -DhtProxyServer::DhtProxyServer(DhtRunner* dht, unsigned int port) : dht_(dht) +DhtProxyServer::DhtProxyServer(std::shared_ptr dht, in_port_t port) +: dht_(dht) { // NOTE in c++14, use make_unique service_ = std::unique_ptr(new restbed::Service()); @@ -69,7 +68,7 @@ DhtProxyServer::DhtProxyServer(DhtRunner* dht, unsigned int port) : dht_(dht) this->put(session); } ); -#if OPENDHT_PROXY_SERVER_OPTIONAL +#if OPENDHT_PROXY_SERVER_IDENTITY resource->set_method_handler("SIGN", [this](const std::shared_ptr session) { @@ -82,7 +81,7 @@ DhtProxyServer::DhtProxyServer(DhtRunner* dht, unsigned int port) : dht_(dht) this->putEncrypted(session); } ); -#endif // OPENDHT_PROXY_SERVER_OPTIONAL +#endif // OPENDHT_PROXY_SERVER_IDENTITY service_->publish(resource); resource = std::make_shared(); resource->set_path("/{hash: .*}/{value: .*}"); @@ -106,6 +105,26 @@ DhtProxyServer::DhtProxyServer(DhtRunner* dht, unsigned int port) : dht_(dht) // Fail silently for now. } }); + + listenThread_ = std::thread([this]() { + auto stop = false; + while (!stop) { + auto listener = currentListeners_.begin(); + while (listener != currentListeners_.end()) { + if (listener->session->is_closed() && dht_) { + dht_->cancelListen(listener->hash, listener->token); + // Remove listener if unused + listener = currentListeners_.erase(listener); + } else { + ++listener; + } + } + //NOTE: When supports restbed 5.0: service_->is_up() and remove stopListeners + stop = stopListeners; + if (!stop) + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + }); } DhtProxyServer::~DhtProxyServer() @@ -117,10 +136,10 @@ void DhtProxyServer::stop() { service_->stop(); + stopListeners = true; // listenThreads_ will stop because there is no more sessions - for (auto& listenThread: listenThreads_) - if (listenThread->joinable()) - listenThread->join(); + if (listenThread_.joinable()) + listenThread_.join(); if (server_thread.joinable()) server_thread.join(); } @@ -131,7 +150,7 @@ DhtProxyServer::getNodeInfo(const std::shared_ptr& session) co const auto request = session->get_request(); int content_length = std::stoi(request->get_header("Content-Length", "0")); session->fetch(content_length, - [&](const std::shared_ptr s, const restbed::Bytes& b) + [=](const std::shared_ptr s, const restbed::Bytes& b) { (void)b; if (dht_) { @@ -152,17 +171,16 @@ DhtProxyServer::getNodeInfo(const std::shared_ptr& session) co void DhtProxyServer::get(const std::shared_ptr& session) const { - static constexpr dht::InfoHash INVALID_ID {}; const auto request = session->get_request(); int content_length = std::stoi(request->get_header("Content-Length", "0")); auto hash = request->get_path_parameter("hash"); session->fetch(content_length, - [&](const std::shared_ptr s, const restbed::Bytes& b) + [=](const std::shared_ptr s, const restbed::Bytes& b) { (void)b; if (dht_) { InfoHash infoHash(hash); - if (infoHash == INVALID_ID) { + if (!infoHash) { infoHash = InfoHash::get(hash); } Json::FastWriter writer; @@ -186,21 +204,20 @@ DhtProxyServer::get(const std::shared_ptr& session) const void DhtProxyServer::listen(const std::shared_ptr& session) const { - static constexpr dht::InfoHash INVALID_ID {}; const auto request = session->get_request(); int content_length = std::stoi(request->get_header("Content-Length", "0")); auto hash = request->get_path_parameter("hash"); InfoHash infoHash(hash); - if (infoHash == INVALID_ID) + if (!infoHash) infoHash = InfoHash::get(hash); - size_t token; session->fetch(content_length, - [&](const std::shared_ptr s, const restbed::Bytes& b) + [=](const std::shared_ptr s, const restbed::Bytes& b) { (void)b; if (dht_) { + s->yield(restbed::OK, ""); // Open the connection Json::FastWriter writer; - token = dht_->listen(infoHash, [s, &writer](std::shared_ptr value) { + size_t token = dht_->listen(infoHash, [s, &writer](std::shared_ptr value) { // Send values as soon as we get them if (!s->is_closed()) s->yield(restbed::OK, writer.write(value->toJson())); @@ -208,14 +225,12 @@ DhtProxyServer::listen(const std::shared_ptr& session) const }).get(); // Handle client deconnection // NOTE: for now, there is no handler, so we test the session in a thread - listenThreads_.emplace_back(std::shared_ptr( - new std::thread([&]() { - while(!s->is_closed()) - std::this_thread::sleep_for(std::chrono::seconds(2)); - if (dht_) - dht_->cancelListen(infoHash, token); - })) - ); + // will be the case in restbed 5.0 + SessionToHashToken listener; + listener.session = s; + listener.hash = infoHash; + listener.token = token; + currentListeners_.emplace_back(listener); } else { s->close(restbed::NOT_FOUND, "{\"err\":\"Incorrect DhtRunner\"}"); } @@ -228,16 +243,15 @@ void DhtProxyServer::put(const std::shared_ptr& session) const { // TODO test with the proxy client - static constexpr dht::InfoHash INVALID_ID {}; const auto request = session->get_request(); int content_length = std::stoi(request->get_header("Content-Length", "0")); auto hash = request->get_path_parameter("hash"); InfoHash infoHash(hash); - if (infoHash == INVALID_ID) + if (!infoHash) infoHash = InfoHash::get(hash); session->fetch(content_length, - [&](const std::shared_ptr s, const restbed::Bytes& b) + [=](const std::shared_ptr s, const restbed::Bytes& b) { if (dht_) { if(b.empty()) { @@ -267,20 +281,19 @@ DhtProxyServer::put(const std::shared_ptr& session) const ); } -#if OPENDHT_PROXY_SERVER_OPTIONAL +#if OPENDHT_PROXY_SERVER_IDENTITY void DhtProxyServer::putSigned(const std::shared_ptr& session) const { - static constexpr dht::InfoHash INVALID_ID {}; const auto request = session->get_request(); int content_length = std::stoi(request->get_header("Content-Length", "0")); auto hash = request->get_path_parameter("hash"); InfoHash infoHash(hash); - if (infoHash == INVALID_ID) + if (!infoHash) infoHash = InfoHash::get(hash); session->fetch(content_length, - [&](const std::shared_ptr s, const restbed::Bytes& b) + [=](const std::shared_ptr s, const restbed::Bytes& b) { if (dht_) { if(b.empty()) { @@ -312,16 +325,15 @@ DhtProxyServer::putSigned(const std::shared_ptr& session) cons void DhtProxyServer::putEncrypted(const std::shared_ptr& session) const { - static constexpr dht::InfoHash INVALID_ID {}; const auto request = session->get_request(); int content_length = std::stoi(request->get_header("Content-Length", "0")); auto hash = request->get_path_parameter("hash"); InfoHash infoHash(hash); - if (infoHash == INVALID_ID) + if (!infoHash) infoHash = InfoHash::get(hash); session->fetch(content_length, - [&](const std::shared_ptr s, const restbed::Bytes& b) + [=](const std::shared_ptr s, const restbed::Bytes& b) { if (dht_) { if(b.empty()) { @@ -338,7 +350,7 @@ DhtProxyServer::putEncrypted(const std::shared_ptr& session) c auto value = std::make_shared(root); auto toHash = request->get_path_parameter("to"); InfoHash toInfoHash(toHash); - if (toInfoHash == INVALID_ID) + if (toinfoHash) toInfoHash = InfoHash::get(toHash); auto response = value->toString(); @@ -357,23 +369,22 @@ DhtProxyServer::putEncrypted(const std::shared_ptr& session) c } ); } -#endif // OPENDHT_PROXY_SERVER_OPTIONAL +#endif // OPENDHT_PROXY_SERVER_IDENTITY void DhtProxyServer::getFiltered(const std::shared_ptr& session) const { - static constexpr dht::InfoHash INVALID_ID {}; const auto request = session->get_request(); int content_length = std::stoi(request->get_header("Content-Length", "0")); auto hash = request->get_path_parameter("hash"); auto value = request->get_path_parameter("value"); session->fetch(content_length, - [&](const std::shared_ptr s, const restbed::Bytes& b) + [=](const std::shared_ptr s, const restbed::Bytes& b) { (void)b; if (dht_) { InfoHash infoHash(hash); - if (infoHash == INVALID_ID) { + if (!infoHash) { infoHash = InfoHash::get(hash); } Json::FastWriter writer; diff --git a/src/dhtrunner.cpp b/src/dhtrunner.cpp index 8988c3e8..66b4ebbd 100644 --- a/src/dhtrunner.cpp +++ b/src/dhtrunner.cpp @@ -800,20 +800,4 @@ DhtRunner::findCertificate(InfoHash hash, std::function(new DhtProxyServer(this, port)); -} - -void -DhtRunner::stopProxyServer() -{ - if (proxy_server_) - proxy_server_.reset(nullptr); -} -#endif //OPENDHT_PROXY_SERVER - } diff --git a/src/value.cpp b/src/value.cpp index a4e02bc4..759c17fb 100644 --- a/src/value.cpp +++ b/src/value.cpp @@ -23,7 +23,7 @@ #include "securedht.h" // print certificate ID #if OPENDHT_PROXY_SERVER -#include +#include "base64.h" #endif //OPENDHT_PROXY_SERVER @@ -173,6 +173,44 @@ Value::msgpack_unpack_body(const msgpack::object& o) } #if OPENDHT_PROXY_SERVER +Value::Value(Json::Value& json) +{ + try { + if (json.isMember("id")) + id = ValueType::Id(json["id"].asInt()); + } catch (...) { } + if (json.isMember("cypher")) { + auto cypherStr = json["cypher"].asString(); + cypherStr = base64_decode(cypherStr); + cypher = std::vector(cypherStr.begin(), cypherStr.end()); + } + if (json.isMember("sig")) { + auto sigStr = json["sig"].asString(); + sigStr = base64_decode(sigStr); + signature = std::vector(sigStr.begin(), sigStr.end()); + } + if (json.isMember("seq")) + seq = json["seq"].asInt(); + if (json.isMember("owner")) { + auto ownerStr = json["owner"].asString(); + auto ownerBlob = std::vector(ownerStr.begin(), ownerStr.end()); + owner = std::make_shared(ownerBlob); + } + if (json.isMember("to")) { + auto toStr = json["to"].asString(); + recipient = InfoHash(toStr); + } + if (json.isMember("type")) + type = json["type"].asInt(); + if (json.isMember("data")){ + auto dataStr = json["data"].asString(); + dataStr = base64_decode(dataStr); + data = std::vector(dataStr.begin(), dataStr.end()); + } + if (json.isMember("utype")) + user_type = json["utype"].asString(); +} + Json::Value Value::toJson() const { diff --git a/tools/Makefile.am b/tools/Makefile.am index 821ddce1..610fd76f 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -11,9 +11,3 @@ dhtchat_LDFLAGS = -lopendht -lreadline -L@top_builddir@/src/.libs @Argon2_LDFLAG dhtscanner_SOURCES = dhtscanner.cpp dhtscanner_LDFLAGS = -lopendht -lreadline -L@top_builddir@/src/.libs @Argon2_LDFLAGS@ @GnuTLS_LIBS@ - -if ENABLE_PROXY_SERVER -dhtnode_LDFLAGS += -lrestbed -ljsoncpp -dhtchat_LDFLAGS += -lrestbed -ljsoncpp -dhtscanner_LDFLAGS += -lrestbed -ljsoncpp -endif diff --git a/tools/dhtnode.cpp b/tools/dhtnode.cpp index 31f845a0..ce0896b4 100644 --- a/tools/dhtnode.cpp +++ b/tools/dhtnode.cpp @@ -60,7 +60,7 @@ void print_help() { #if OPENDHT_PROXY_SERVER std::cout << std::endl << "Operations with the proxy:" << std::endl << " pst [port] Start the proxy interface on port." << std::endl - << " psp Stop the proxy interface." << std::endl; + << " psp [port] Stop the proxy interface on port." << std::endl; #endif //OPENDHT_PROXY_SERVER std::cout << std::endl << "Operations on the DHT:" << std::endl @@ -90,6 +90,9 @@ void cmd_loop(std::shared_ptr& dht, dht_params& params) #endif std::map indexes; +#if OPENDHT_PROXY_SERVER + std::map> proxies; +#endif //OPENDHT_PROXY_SERVER while (true) { @@ -174,13 +177,18 @@ void cmd_loop(std::shared_ptr& dht, dht_params& params) iss >> idstr; try { unsigned int port = std::stoi(idstr); - dht->startProxyServer(port); - } catch (...) { - dht->startProxyServer(); - } + auto proxyServer = std::unique_ptr( + new DhtProxyServer(dht, port) + ); + proxies.emplace(idstr, std::move(proxyServer)); + } catch (...) { } continue; } else if (op == "psp") { - dht->stopProxyServer(); + iss >> idstr; + auto it = proxies.find(idstr); + if (it != proxies.end()) { + it->second.reset(nullptr); + } continue; } #endif //OPENDHT_PROXY_SERVER From 67ba435ba685263052d985525a16ed04b3c2f69d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Blin?= Date: Mon, 6 Nov 2017 14:24:52 -0500 Subject: [PATCH 18/22] base64: take the old implementation from ring-daemon --- include/opendht/dht_proxy_server.h | 2 +- src/base64.cpp | 234 ++++++++++++++++------------- src/base64.h | 56 +++---- src/dht_proxy_server.cpp | 2 +- tools/dhtnode.cpp | 16 +- 5 files changed, 161 insertions(+), 149 deletions(-) diff --git a/include/opendht/dht_proxy_server.h b/include/opendht/dht_proxy_server.h index d614ecf0..e2b74525 100644 --- a/include/opendht/dht_proxy_server.h +++ b/include/opendht/dht_proxy_server.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Savoir-faire Linux Inc. + * Copyright (C) 2017 Savoir-faire Linux Inc. * Author : Sébastien Blin * * This program is free software; you can redistribute it and/or modify diff --git a/src/base64.cpp b/src/base64.cpp index 4971e9e0..2e95c80a 100644 --- a/src/base64.cpp +++ b/src/base64.cpp @@ -1,121 +1,143 @@ /* - base64.cpp and base64.h + * Copyright (C) 2004-2017 Savoir-faire Linux Inc. + * + * This program is free software; you can redistribute it 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 . + */ - Copyright (C) 2004-2008 René Nyffenegger - - This source code is provided 'as-is', without any express or implied - warranty. In no event will the author be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this source code must not be misrepresented; you must not - claim that you wrote the original source code. If you use this source code - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original source code. - - 3. This notice may not be removed or altered from any source distribution. - - René Nyffenegger rene.nyffenegger@adp-gmbh.ch - -*/ #include "base64.h" -static const std::string base64_chars = - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz" - "0123456789+/"; +#include +#include +/* Mainly based on the following stackoverflow question: + * http://stackoverflow.com/questions/342409/how-do-i-base64-encode-decode-in-c + */ +static const char encoding_table[] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', + 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', + 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', + 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', + 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', + '3', '4', '5', '6', '7', '8', '9', '+', '/' +}; -static inline bool is_base64(unsigned char c) { - return (isalnum(c) || (c == '+') || (c == '/')); +static const size_t mod_table[] = { 0, 2, 1 }; + +char *base64_encode(const uint8_t *input, size_t input_length, + char *output, size_t *output_length) +{ + size_t i, j; + size_t out_sz = *output_length; + *output_length = 4 * ((input_length + 2) / 3); + if (out_sz < *output_length || output == nullptr) + return nullptr; + + for (i = 0, j = 0; i < input_length; ) { + uint8_t octet_a = i < input_length ? input[i++] : 0; + uint8_t octet_b = i < input_length ? input[i++] : 0; + uint8_t octet_c = i < input_length ? input[i++] : 0; + + uint32_t triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c; + + output[j++] = encoding_table[(triple >> 3 * 6) & 0x3F]; + output[j++] = encoding_table[(triple >> 2 * 6) & 0x3F]; + output[j++] = encoding_table[(triple >> 1 * 6) & 0x3F]; + output[j++] = encoding_table[(triple >> 0 * 6) & 0x3F]; + } + + for (i = 0; i < mod_table[input_length % 3]; i++) + output[*output_length - 1 - i] = '='; + + return output; } -std::string base64_encode(const std::vector& str) { - unsigned char const* bytes_to_encode = str.data(); - unsigned int in_len = str.size(); - std::string ret; - int i = 0; - int j = 0; - unsigned char char_array_3[3]; - unsigned char char_array_4[4]; +uint8_t *base64_decode(const char *input, size_t input_length, + uint8_t *output, size_t *output_length) +{ + size_t i, j; + uint8_t decoding_table[256]; - while (in_len--) { - char_array_3[i++] = *(bytes_to_encode++); - if (i == 3) { - char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; - char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); - char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); - char_array_4[3] = char_array_3[2] & 0x3f; + uint8_t c; + for (c = 0; c < 64; c++) + decoding_table[static_cast(encoding_table[c])] = c; - for(i = 0; (i <4) ; i++) - ret += base64_chars[char_array_4[i]]; - i = 0; - } + if (input_length % 4 != 0) + return nullptr; + + size_t out_sz = *output_length; + *output_length = input_length / 4 * 3; + if (input[input_length - 1] == '=') + (*output_length)--; + if (input[input_length - 2] == '=') + (*output_length)--; + + if (out_sz < *output_length || output == nullptr) + return nullptr; + + for (i = 0, j = 0; i < input_length;) { + uint8_t sextet_a = input[i] == '=' ? 0 & i++ + : decoding_table[static_cast(input[i++])]; + uint8_t sextet_b = input[i] == '=' ? 0 & i++ + : decoding_table[static_cast(input[i++])]; + uint8_t sextet_c = input[i] == '=' ? 0 & i++ + : decoding_table[static_cast(input[i++])]; + uint8_t sextet_d = input[i] == '=' ? 0 & i++ + : decoding_table[static_cast(input[i++])]; + + uint32_t triple = (sextet_a << 3 * 6) + + (sextet_b << 2 * 6) + + (sextet_c << 1 * 6) + + (sextet_d << 0 * 6); + + if (j < *output_length) + output[j++] = (triple >> 2 * 8) & 0xFF; + if (j < *output_length) + output[j++] = (triple >> 1 * 8) & 0xFF; + if (j < *output_length) + output[j++] = (triple >> 0 * 8) & 0xFF; } - if (i) - { - for(j = i; j < 3; j++) - char_array_3[j] = '\0'; - - char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; - char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); - char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); - char_array_4[3] = char_array_3[2] & 0x3f; - - for (j = 0; (j < i + 1); j++) - ret += base64_chars[char_array_4[j]]; - - while((i++ < 3)) - ret += '='; - } - - return ret; + return output; } -std::string base64_decode(std::string const& encoded_string) { - size_t in_len = encoded_string.size(); - size_t i = 0; - size_t j = 0; - int in_ = 0; - unsigned char char_array_4[4], char_array_3[3]; - std::string ret; - - while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) { - char_array_4[i++] = encoded_string[in_]; in_++; - if (i ==4) { - for (i = 0; i <4; i++) - char_array_4[i] = static_cast(base64_chars.find(char_array_4[i])); - - char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); - char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); - char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; - - for (i = 0; (i < 3); i++) - ret += char_array_3[i]; - i = 0; - } - } - - if (i) { - for (j = i; j <4; j++) - char_array_4[j] = 0; - - for (j = 0; j <4; j++) - char_array_4[j] = static_cast(base64_chars.find(char_array_4[j])); - - char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); - char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); - char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; - - for (j = 0; (j < i - 1); j++) ret += char_array_3[j]; - } - - return ret; +std::string +base64_encode(const std::vector::const_iterator begin, + const std::vector::const_iterator end) +{ + size_t output_length = 4 * ((std::distance(begin, end) + 2) / 3); + std::string out; + out.resize(output_length); + base64_encode(&(*begin), std::distance(begin, end), + &(*out.begin()), &output_length); + out.resize(output_length); + return out; +} + + +std::string +base64_encode(const std::vector& str) +{ + return base64_encode(str.cbegin(), str.cend()); +} + +std::string +base64_decode(const std::string& str) +{ + size_t output_length = str.length() / 4 * 3 + 2; + std::vector output; + output.resize(output_length); + base64_decode(str.data(), str.size(), output.data(), &output_length); + output.resize(output_length); + return std::string(output.begin(), output.end()); } diff --git a/src/base64.h b/src/base64.h index ad3d2814..3f5f39e1 100644 --- a/src/base64.h +++ b/src/base64.h @@ -1,44 +1,36 @@ /* - base64.cpp and base64.h + * Copyright (C) 2004-2017 Savoir-faire Linux Inc. + * + * This program is free software; you can redistribute it 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 . + */ - Copyright (C) 2004-2008 René Nyffenegger - - This source code is provided 'as-is', without any express or implied - warranty. In no event will the author be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this source code must not be misrepresented; you must not - claim that you wrote the original source code. If you use this source code - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original source code. - - 3. This notice may not be removed or altered from any source distribution. - - René Nyffenegger rene.nyffenegger@adp-gmbh.ch - -*/ #pragma once #include #include /** - * Encode a string into base64 - * @param begin - * @param end - * @return the encoded vector in a string + * Encode a buffer in base64. + * + * @param str the input buffer + * @return a base64-encoded buffer */ std::string base64_encode(const std::vector& str); /** - * Decode a base64 encoded string - * @param str to decode - * @return the decoded string + * Decode a buffer in base64. + * + * @param str the input buffer + * @return a base64-decoded buffer */ -std::string base64_decode(std::string const& str); +std::string base64_decode(const std::string& str); diff --git a/src/dht_proxy_server.cpp b/src/dht_proxy_server.cpp index ba6a998d..9f70e2bf 100644 --- a/src/dht_proxy_server.cpp +++ b/src/dht_proxy_server.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Savoir-faire Linux Inc. + * Copyright (C) 2017 Savoir-faire Linux Inc. * Author : Sébastien Blin * * This program is free software; you can redistribute it and/or modify diff --git a/tools/dhtnode.cpp b/tools/dhtnode.cpp index ce0896b4..0c18af27 100644 --- a/tools/dhtnode.cpp +++ b/tools/dhtnode.cpp @@ -91,7 +91,7 @@ void cmd_loop(std::shared_ptr& dht, dht_params& params) std::map indexes; #if OPENDHT_PROXY_SERVER - std::map> proxies; + std::map> proxies; #endif //OPENDHT_PROXY_SERVER while (true) @@ -177,18 +177,16 @@ void cmd_loop(std::shared_ptr& dht, dht_params& params) iss >> idstr; try { unsigned int port = std::stoi(idstr); - auto proxyServer = std::unique_ptr( - new DhtProxyServer(dht, port) - ); - proxies.emplace(idstr, std::move(proxyServer)); + proxies.emplace(port, new DhtProxyServer(dht, port)); } catch (...) { } continue; } else if (op == "psp") { iss >> idstr; - auto it = proxies.find(idstr); - if (it != proxies.end()) { - it->second.reset(nullptr); - } + try { + auto it = proxies.find(std::stoi(idstr)); + if (it != proxies.end()) + proxies.erase(it); + } catch (...) { } continue; } #endif //OPENDHT_PROXY_SERVER From 38ad5b9ef2a9ae707b0546d18da44c4f56ec0015 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Blin?= Date: Mon, 6 Nov 2017 16:49:46 -0500 Subject: [PATCH 19/22] add --proxyserver option for dhtnode --- tools/dhtnode.cpp | 5 ++++- tools/tools_common.h | 10 ++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/tools/dhtnode.cpp b/tools/dhtnode.cpp index 0c18af27..d8c81a9a 100644 --- a/tools/dhtnode.cpp +++ b/tools/dhtnode.cpp @@ -29,7 +29,7 @@ extern "C" { using namespace dht; void print_usage() { - std::cout << "Usage: dhtnode [-v [-l logfile]] [-i] [-d] [-n network_id] [-p local_port] [-b bootstrap_host[:port]]" << std::endl << std::endl; + std::cout << "Usage: dhtnode [-v [-l logfile]] [-i] [-d] [-n network_id] [-p local_port] [-b bootstrap_host[:port]] [--proxyserver local_port]" << std::endl << std::endl; std::cout << "dhtnode, a simple OpenDHT command line node runner." << std::endl; std::cout << "Report bugs to: http://opendht.net" << std::endl; } @@ -92,6 +92,9 @@ void cmd_loop(std::shared_ptr& dht, dht_params& params) std::map indexes; #if OPENDHT_PROXY_SERVER std::map> proxies; + if (params.proxyserver != 0) { + proxies.emplace(params.proxyserver, new DhtProxyServer(dht, params.proxyserver)); + } #endif //OPENDHT_PROXY_SERVER while (true) diff --git a/tools/tools_common.h b/tools/tools_common.h index 933cc7e2..cd3e1970 100644 --- a/tools/tools_common.h +++ b/tools/tools_common.h @@ -121,6 +121,7 @@ struct dht_params { bool daemonize {false}; bool service {false}; std::pair bootstrap {}; + in_port_t proxyserver {0}; }; static const constexpr struct option long_options[] = { @@ -134,6 +135,7 @@ static const constexpr struct option long_options[] = { {"service", no_argument , nullptr, 's'}, {"logfile", required_argument, nullptr, 'l'}, {"syslog", no_argument , nullptr, 'L'}, + {"proxyserver",required_argument, nullptr, 'S'}, {nullptr, 0 , nullptr, 0} }; @@ -151,6 +153,14 @@ parseArgs(int argc, char **argv) { std::cout << "Invalid port: " << port_arg << std::endl; } break; + case 'S': { + int port_arg = atoi(optarg); + if (port_arg >= 0 && port_arg < 0x10000) + params.proxyserver = port_arg; + else + std::cout << "Invalid port: " << port_arg << std::endl; + } + break; case 'n': params.network = strtoul(optarg, nullptr, 0); break; From bbe922dd7d6092e86f555e174cd1cf474c1fd49b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Blin?= Date: Tue, 7 Nov 2017 13:30:06 -0500 Subject: [PATCH 20/22] remove headers, fix HTTP status, fix get / --- include/opendht/callbacks.h | 10 +++ include/opendht/dht_proxy_server.h | 41 ++++----- src/callbacks.cpp | 20 +++++ src/dht_proxy_server.cpp | 134 +++++++++++++++-------------- 4 files changed, 120 insertions(+), 85 deletions(-) diff --git a/include/opendht/callbacks.h b/include/opendht/callbacks.h index 21f2dd0c..e354ba21 100644 --- a/include/opendht/callbacks.h +++ b/include/opendht/callbacks.h @@ -26,6 +26,10 @@ #include #include +#if OPENDHT_PROXY_SERVER +#include +#endif //OPENDHT_PROXY_SERVER + namespace dht { struct Node; @@ -47,6 +51,12 @@ struct OPENDHT_PUBLIC NodeStats { unsigned table_depth; unsigned getKnownNodes() const { return good_nodes + dubious_nodes; } std::string toString() const; +#if OPENDHT_PROXY_SERVER + /** + * Build a json object from a NodeStats + */ + Json::Value toJson() const; +#endif //OPENDHT_PROXY_SERVER }; /** diff --git a/include/opendht/dht_proxy_server.h b/include/opendht/dht_proxy_server.h index e2b74525..5ac64c4e 100644 --- a/include/opendht/dht_proxy_server.h +++ b/include/opendht/dht_proxy_server.h @@ -31,7 +31,6 @@ namespace dht { class DhtRunner; -class InfoHash; /** * Describes the REST API @@ -64,7 +63,7 @@ private: * Return the PublicKey id, the node id and node stats * Method: GET "/" * Result: HTTP 200, body: Value in JSON format (one part = one value) - * On error: HTTP 404, body: {"err":"xxxx"} + * On error: HTTP 503, body: {"err":"xxxx"} * @param session */ void getNodeInfo(const std::shared_ptr& session) const; @@ -72,12 +71,11 @@ private: /** * Return Values of a InfoHash * Method: GET "/{InfoHash: .*}" - * Return: Multiple JSON object in parts. For 2 values, you will have 3 parts: - * Value in JSON format (HTTP/1.1 200 OK Content-Type: application/json) - * Value in JSON format (HTTP/1.1 200 OK Content-Type: application/json) - * {"ok": 1} + * Return: Multiple JSON object in parts. Example: + * Value in JSON format\n + * Value in JSON format * - * On error: HTTP 404, body: {"err":"xxxx"} + * On error: HTTP 503, body: {"err":"xxxx"} * @param session */ void get(const std::shared_ptr& session) const; @@ -85,11 +83,11 @@ private: /** * Listen incoming Values of a InfoHash. * Method: LISTEN "/{InfoHash: .*}" - * Return: Multiple JSON object in parts. For 2 values, you will have 2 parts: - * Value in JSON format (HTTP/1.1 200 OK Content-Type: application/json) - * Value in JSON format (HTTP/1.1 200 OK Content-Type: application/json) + * Return: Multiple JSON object in parts. Example: + * Value in JSON format\n + * Value in JSON format * - * On error: HTTP 404, body: {"err":"xxxx"} + * On error: HTTP 503, body: {"err":"xxxx"} * @param session */ void listen(const std::shared_ptr& session) const; @@ -98,8 +96,8 @@ private: * Put a value on the DHT * Method: POST "/{InfoHash: .*}" * body = Value to put in JSON - * Return: {"ok":"1"} - * On error: HTTP 404, body: {"err":"xxxx"} if no dht + * Return: HTTP 200 if success and the value put in JSON + * On error: HTTP 503, body: {"err":"xxxx"} if no dht * HTTP 400, body: {"err":"xxxx"} if bad json * @param session */ @@ -110,8 +108,8 @@ private: * Put a value to sign by the proxy on the DHT * Method: SIGN "/{InfoHash: .*}" * body = Value to put in JSON - * Return: {"ok":"1"} - * On error: HTTP 404, body: {"err":"xxxx"} if no dht + * Return: HTTP 200 if success and the value put in JSON + * On error: HTTP 503, body: {"err":"xxxx"} if no dht * HTTP 400, body: {"err":"xxxx"} if bad json * @param session */ @@ -121,8 +119,8 @@ private: * Put a value to encrypt by the proxy on the DHT * Method: ENCRYPT "/{hash: .*}" * body = Value to put in JSON + "to":"infoHash" - * Return: {"ok":"1"} - * On error: HTTP 404, body: {"err":"xxxx"} if no dht + * Return: HTTP 200 if success and the value put in JSON + * On error: HTTP 503, body: {"err":"xxxx"} if no dht * HTTP 400, body: {"err":"xxxx"} if bad json * @param session */ @@ -132,12 +130,11 @@ private: /** * Return Values of a InfoHash filtered by a value id * Method: GET "/{InfoHash: .*}/{ValueId: .*}" - * Return: Multiple JSON object in parts. For 2 values, you will have 3 parts: - * Value in JSON format (HTTP/1.1 200 OK Content-Type: application/json) - * Value in JSON format (HTTP/1.1 200 OK Content-Type: application/json) - * {"ok": 1} + * Return: Multiple JSON object in parts. Example: + * Value in JSON format\n + * Value in JSON format * - * On error: HTTP 404, body: {"err":"xxxx"} + * On error: HTTP 503, body: {"err":"xxxx"} * @param session */ void getFiltered(const std::shared_ptr& session) const; diff --git a/src/callbacks.cpp b/src/callbacks.cpp index 8908a2df..89719ef1 100644 --- a/src/callbacks.cpp +++ b/src/callbacks.cpp @@ -68,4 +68,24 @@ NodeStats::toString() const return ss.str(); } +#if OPENDHT_PROXY_SERVER +/** + * Build a json object from a NodeStats + */ +Json::Value +NodeStats::toJson() const +{ + Json::Value val; + val["good"] = static_cast(good_nodes); + val["dubious"] = static_cast(dubious_nodes); + val["incoming"] = static_cast(incoming_nodes); + if (table_depth > 1) { + val["table_depth"] = static_cast(table_depth); + unsigned long tot_nodes = 8 * std::exp2(table_depth); + val["network_size_estimation"] = static_cast(tot_nodes); + } + return val; +} +#endif //OPENDHT_PROXY_SERVER + } diff --git a/src/dht_proxy_server.cpp b/src/dht_proxy_server.cpp index 9f70e2bf..f813f239 100644 --- a/src/dht_proxy_server.cpp +++ b/src/dht_proxy_server.cpp @@ -19,13 +19,12 @@ #if OPENDHT_PROXY_SERVER #include "dht_proxy_server.h" -#include -#include - #include "default_types.h" #include "dhtrunner.h" #include "msgpack.hpp" +#include +#include #include #include @@ -96,13 +95,14 @@ DhtProxyServer::DhtProxyServer(std::shared_ptr dht, in_port_t port) // Start server auto settings = std::make_shared(); settings->set_default_header("Content-Type", "application/json"); + settings->set_default_header("Connection", "keep-alive"); std::chrono::milliseconds timeout(std::numeric_limits::max()); settings->set_connection_timeout(timeout); // there is a timeout, but really huge settings->set_port(port); try { service_->start(settings); } catch(std::system_error& e) { - // Fail silently for now. + std::cerr << "Error running server on port " << port << ": " << e.what() << std::endl; } }); @@ -150,20 +150,21 @@ DhtProxyServer::getNodeInfo(const std::shared_ptr& session) co const auto request = session->get_request(); int content_length = std::stoi(request->get_header("Content-Length", "0")); session->fetch(content_length, - [=](const std::shared_ptr s, const restbed::Bytes& b) + [=](const std::shared_ptr s, const restbed::Bytes& /*b*/) { - (void)b; if (dht_) { Json::Value result; - result["id"] = dht_->getId().toString(); + auto id = dht_->getId(); + if (id) + result["id"] = id.toString(); result["node_id"] = dht_->getNodeId().toString(); - result["ipv4"] = dht_->getNodesStats(AF_INET).toString(); - result["ipv6"] = dht_->getNodesStats(AF_INET6).toString(); + result["ipv4"] = dht_->getNodesStats(AF_INET).toJson(); + result["ipv6"] = dht_->getNodesStats(AF_INET6).toJson(); Json::FastWriter writer; s->close(restbed::OK, writer.write(result)); } else - s->close(restbed::NOT_FOUND, "{\"err\":\"Incorrect DhtRunner\"}"); + s->close(restbed::SERVICE_UNAVAILABLE, "{\"err\":\"Incorrect DhtRunner\"}"); } ); } @@ -175,35 +176,36 @@ DhtProxyServer::get(const std::shared_ptr& session) const int content_length = std::stoi(request->get_header("Content-Length", "0")); auto hash = request->get_path_parameter("hash"); session->fetch(content_length, - [=](const std::shared_ptr s, const restbed::Bytes& b) + [=](const std::shared_ptr s, const restbed::Bytes& /*b* */) { - (void)b; if (dht_) { InfoHash infoHash(hash); if (!infoHash) { infoHash = InfoHash::get(hash); } - Json::FastWriter writer; - dht_->get(infoHash, [s, &writer](std::shared_ptr value) { - // Send values as soon as we get them - Json::Value result; - s->yield(restbed::OK, writer.write(value->toJson())); - return true; - }, [s, &writer](bool ok) { - // Communication is finished - auto response = std::to_string(ok); - s->close(restbed::OK, "{\"ok\": " + response + "}"); + s->yield(restbed::OK, "", [=]( const std::shared_ptr< restbed::Session > s) { + dht_->get(infoHash, [s](std::shared_ptr value) { + // Send values as soon as we get them + Json::FastWriter writer; + s->yield(writer.write(value->toJson()), [](const std::shared_ptr /*session*/){ }); + return true; + }, [s](bool /*ok* */) { + // Communication is finished + s->close(); + }); }); } else { - s->close(restbed::NOT_FOUND, "{\"err\":\"Incorrect DhtRunner\"}"); + s->close(restbed::SERVICE_UNAVAILABLE, "{\"err\":\"Incorrect DhtRunner\"}"); } } ); + } void DhtProxyServer::listen(const std::shared_ptr& session) const { + const auto request = session->get_request(); int content_length = std::stoi(request->get_header("Content-Length", "0")); auto hash = request->get_path_parameter("hash"); @@ -211,28 +213,33 @@ DhtProxyServer::listen(const std::shared_ptr& session) const if (!infoHash) infoHash = InfoHash::get(hash); session->fetch(content_length, - [=](const std::shared_ptr s, const restbed::Bytes& b) + [=](const std::shared_ptr s, const restbed::Bytes& /*b* */) { - (void)b; if (dht_) { - s->yield(restbed::OK, ""); // Open the connection - Json::FastWriter writer; - size_t token = dht_->listen(infoHash, [s, &writer](std::shared_ptr value) { - // Send values as soon as we get them - if (!s->is_closed()) - s->yield(restbed::OK, writer.write(value->toJson())); - return !s->is_closed(); - }).get(); - // Handle client deconnection - // NOTE: for now, there is no handler, so we test the session in a thread - // will be the case in restbed 5.0 - SessionToHashToken listener; - listener.session = s; - listener.hash = infoHash; - listener.token = token; - currentListeners_.emplace_back(listener); + InfoHash infoHash(hash); + if (!infoHash) { + infoHash = InfoHash::get(hash); + } + s->yield(restbed::OK, "", [=]( const std::shared_ptr< restbed::Session > s) { + size_t token = dht_->listen(infoHash, [s](std::shared_ptr value) { + // Send values as soon as we get them + if (!s->is_closed()) { + Json::FastWriter writer; + s->yield(writer.write(value->toJson()), [](const std::shared_ptr /*session*/){ }); + } + return !s->is_closed(); + }).get(); + // Handle client deconnection + // NOTE: for now, there is no handler, so we test the session in a thread + // will be the case in restbed 5.0 + SessionToHashToken listener; + listener.session = s; + listener.hash = infoHash; + listener.token = token; + currentListeners_.emplace_back(listener); + }); } else { - s->close(restbed::NOT_FOUND, "{\"err\":\"Incorrect DhtRunner\"}"); + s->close(restbed::SERVICE_UNAVAILABLE, "{\"err\":\"Incorrect DhtRunner\"}"); } } ); @@ -267,15 +274,15 @@ DhtProxyServer::put(const std::shared_ptr& session) const // Build the Value from json auto value = std::make_shared(root); - auto response = value->toString(); + Json::FastWriter writer; dht_->put(infoHash, value); - s->close(restbed::OK, response); + s->close(restbed::OK, writer.write(value->toJson())); } else { s->close(restbed::BAD_REQUEST, "Incorrect JSON"); } } } else { - s->close(restbed::NOT_FOUND, ""); + s->close(restbed::SERVICE_UNAVAILABLE, "{\"err\":\"Incorrect DhtRunner\"}"); } } ); @@ -308,15 +315,15 @@ DhtProxyServer::putSigned(const std::shared_ptr& session) cons if (parsingSuccessful) { auto value = std::make_shared(root); - auto response = value->toString(); + Json::FastWriter writer; dht_->putSigned(infoHash, value); - s->close(restbed::OK, response); + s->close(restbed::OK, writer.write(value->toJson())); } else { s->close(restbed::BAD_REQUEST, "Incorrect JSON" + strJson); } } } else { - s->close(restbed::NOT_FOUND, ""); + s->close(restbed::SERVICE_UNAVAILABLE, "{\"err\":\"Incorrect DhtRunner\"}"); } } ); @@ -353,9 +360,9 @@ DhtProxyServer::putEncrypted(const std::shared_ptr& session) c if (toinfoHash) toInfoHash = InfoHash::get(toHash); - auto response = value->toString(); + Json::FastWriter writer; dht_->putEncrypted(infoHash, toInfoHash, value); - s->close(restbed::OK, response); + s->close(restbed::OK, writer.write(value->toJson())); } else { if(!parsingSuccessful) s->close(restbed::BAD_REQUEST, "Incorrect JSON"); @@ -364,7 +371,7 @@ DhtProxyServer::putEncrypted(const std::shared_ptr& session) c } } } else { - s->close(restbed::NOT_FOUND, ""); + s->close(restbed::SERVICE_UNAVAILABLE, "{\"err\":\"Incorrect DhtRunner\"}"); } } ); @@ -379,25 +386,26 @@ DhtProxyServer::getFiltered(const std::shared_ptr& session) co auto hash = request->get_path_parameter("hash"); auto value = request->get_path_parameter("value"); session->fetch(content_length, - [=](const std::shared_ptr s, const restbed::Bytes& b) + [=](const std::shared_ptr s, const restbed::Bytes& /*b* */) { - (void)b; if (dht_) { InfoHash infoHash(hash); if (!infoHash) { infoHash = InfoHash::get(hash); } - Json::FastWriter writer; - dht_->get(infoHash, [s, &writer](std::shared_ptr value) { - Json::Value result; - s->yield(restbed::OK, writer.write(value->toJson())); - return true; - }, [s, &writer](bool ok) { - auto response = std::to_string(ok); - s->close(restbed::OK, "{\"ok\": " + response + "}"); - }, {}, value); + s->yield(restbed::OK, "", [=]( const std::shared_ptr< restbed::Session > s) { + dht_->get(infoHash, [s](std::shared_ptr v) { + // Send values as soon as we get them + Json::FastWriter writer; + s->yield(writer.write(v->toJson()), [](const std::shared_ptr /*session*/){ }); + return true; + }, [s](bool /*ok* */) { + // Communication is finished + s->close(); + }, {}, value); + }); } else { - s->close(restbed::NOT_FOUND, "{\"err\":\"Incorrect DhtRunner\"}"); + s->close(restbed::SERVICE_UNAVAILABLE, "{\"err\":\"Incorrect DhtRunner\"}"); } } ); From 2ca2835c9b73e60f9e184a8dd50faea5633a9574 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Blin?= Date: Wed, 8 Nov 2017 09:43:42 -0500 Subject: [PATCH 21/22] add options method handler --- include/opendht/dht_proxy_server.h | 8 ++++++++ src/dht_proxy_server.cpp | 21 ++++++++++++++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/include/opendht/dht_proxy_server.h b/include/opendht/dht_proxy_server.h index 5ac64c4e..733ebdb2 100644 --- a/include/opendht/dht_proxy_server.h +++ b/include/opendht/dht_proxy_server.h @@ -139,6 +139,14 @@ private: */ void getFiltered(const std::shared_ptr& session) const; + /** + * Respond allowed Methods + * Method: OPTIONS "/{hash: .*}" + * Return: HTTP 200 + Allow: allowed methods + * See https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/OPTIONS + */ + void handleOptionsMethod(const std::shared_ptr& session) const; + std::thread server_thread {}; std::unique_ptr service_; std::shared_ptr dht_; diff --git a/src/dht_proxy_server.cpp b/src/dht_proxy_server.cpp index f813f239..2b2e34ec 100644 --- a/src/dht_proxy_server.cpp +++ b/src/dht_proxy_server.cpp @@ -81,6 +81,12 @@ DhtProxyServer::DhtProxyServer(std::shared_ptr dht, in_port_t port) } ); #endif // OPENDHT_PROXY_SERVER_IDENTITY + resource->set_method_handler("OPTIONS", + [this](const std::shared_ptr session) + { + this->handleOptionsMethod(session); + } + ); service_->publish(resource); resource = std::make_shared(); resource->set_path("/{hash: .*}/{value: .*}"); @@ -357,7 +363,7 @@ DhtProxyServer::putEncrypted(const std::shared_ptr& session) c auto value = std::make_shared(root); auto toHash = request->get_path_parameter("to"); InfoHash toInfoHash(toHash); - if (toinfoHash) + if (toInfoHash) toInfoHash = InfoHash::get(toHash); Json::FastWriter writer; @@ -378,6 +384,19 @@ DhtProxyServer::putEncrypted(const std::shared_ptr& session) c } #endif // OPENDHT_PROXY_SERVER_IDENTITY +void +DhtProxyServer::handleOptionsMethod(const std::shared_ptr& session) const +{ +#if OPENDHT_PROXY_SERVER_IDENTITY + const auto allowed = "OPTIONS, GET, POST, LISTEN, SIGN, ENCRYPT"; +#else + const auto allowed = "OPTIONS, GET, POST, LISTEN"; +#endif //OPENDHT_PROXY_SERVER_IDENTITY + session->close(restbed::OK, {{"Access-Control-Allow-Methods", allowed}, + {"Access-Control-Allow-Headers", "content-type"}, + {"Access-Control-Max-Age", "86400"}}); +} + void DhtProxyServer::getFiltered(const std::shared_ptr& session) const { From e2c534df5ababd4504918bf1aa0b6caf71ff62c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Blin?= Date: Wed, 8 Nov 2017 14:06:24 -0500 Subject: [PATCH 22/22] add std::bind, fix put, avoid overflow on base64 --- include/opendht/dht_proxy_server.h | 6 +- src/base64.cpp | 2 +- src/dht_proxy_server.cpp | 132 ++++++++++------------------- 3 files changed, 49 insertions(+), 91 deletions(-) diff --git a/include/opendht/dht_proxy_server.h b/include/opendht/dht_proxy_server.h index 733ebdb2..d2eb6a38 100644 --- a/include/opendht/dht_proxy_server.h +++ b/include/opendht/dht_proxy_server.h @@ -62,7 +62,7 @@ private: /** * Return the PublicKey id, the node id and node stats * Method: GET "/" - * Result: HTTP 200, body: Value in JSON format (one part = one value) + * Result: HTTP 200, body: Node infos in JSON format * On error: HTTP 503, body: {"err":"xxxx"} * @param session */ @@ -98,7 +98,7 @@ private: * body = Value to put in JSON * Return: HTTP 200 if success and the value put in JSON * On error: HTTP 503, body: {"err":"xxxx"} if no dht - * HTTP 400, body: {"err":"xxxx"} if bad json + * HTTP 400, body: {"err":"xxxx"} if bad json or HTTP 502 if put fails * @param session */ void put(const std::shared_ptr& session) const; @@ -157,7 +157,7 @@ private: struct SessionToHashToken { std::shared_ptr session; InfoHash hash; - size_t token; + std::future token; }; mutable std::vector currentListeners_; std::atomic_bool stopListeners {false}; diff --git a/src/base64.cpp b/src/base64.cpp index 2e95c80a..7e255582 100644 --- a/src/base64.cpp +++ b/src/base64.cpp @@ -72,7 +72,7 @@ uint8_t *base64_decode(const char *input, size_t input_length, for (c = 0; c < 64; c++) decoding_table[static_cast(encoding_table[c])] = c; - if (input_length % 4 != 0) + if (input_length % 4 != 0 || input_length < 2) return nullptr; size_t out_sz = *output_length; diff --git a/src/dht_proxy_server.cpp b/src/dht_proxy_server.cpp index 2b2e34ec..586a8b77 100644 --- a/src/dht_proxy_server.cpp +++ b/src/dht_proxy_server.cpp @@ -28,6 +28,8 @@ #include #include +using namespace std::placeholders; + namespace dht { DhtProxyServer::DhtProxyServer(std::shared_ptr dht, in_port_t port) @@ -40,62 +42,22 @@ DhtProxyServer::DhtProxyServer(std::shared_ptr dht, in_port_t port) // Create endpoints auto resource = std::make_shared(); resource->set_path("/"); - resource->set_method_handler("GET", - [this](const std::shared_ptr session) - { - this->getNodeInfo(session); - } - ); + resource->set_method_handler("GET", std::bind(&DhtProxyServer::getNodeInfo, this, _1)); service_->publish(resource); resource = std::make_shared(); resource->set_path("/{hash: .*}"); - resource->set_method_handler("GET", - [this](const std::shared_ptr session) - { - this->get(session); - } - ); - resource->set_method_handler("LISTEN", - [this](const std::shared_ptr session) - { - this->listen(session); - } - ); - resource->set_method_handler("POST", - [this](const std::shared_ptr session) - { - this->put(session); - } - ); + resource->set_method_handler("GET", std::bind(&DhtProxyServer::get, this, _1)); + resource->set_method_handler("LISTEN", std::bind(&DhtProxyServer::listen, this, _1)); + resource->set_method_handler("POST", std::bind(&DhtProxyServer::put, this, _1)); #if OPENDHT_PROXY_SERVER_IDENTITY - resource->set_method_handler("SIGN", - [this](const std::shared_ptr session) - { - this->putSigned(session); - } - ); - resource->set_method_handler("ENCRYPT", - [this](const std::shared_ptr session) - { - this->putEncrypted(session); - } - ); + resource->set_method_handler("SIGN", std::bind(&DhtProxyServer::putSigned, this, _1)); + resource->set_method_handler("ENCRYPT", std::bind(&DhtProxyServer::putEncrypted, this, _1)); #endif // OPENDHT_PROXY_SERVER_IDENTITY - resource->set_method_handler("OPTIONS", - [this](const std::shared_ptr session) - { - this->handleOptionsMethod(session); - } - ); + resource->set_method_handler("OPTIONS", std::bind(&DhtProxyServer::handleOptionsMethod, this, _1)); service_->publish(resource); resource = std::make_shared(); resource->set_path("/{hash: .*}/{value: .*}"); - resource->set_method_handler("GET", - [this](const std::shared_ptr session) - { - this->getFiltered(session); - } - ); + resource->set_method_handler("GET", std::bind(&DhtProxyServer::getFiltered, this, _1)); service_->publish(resource); // Start server @@ -118,7 +80,7 @@ DhtProxyServer::DhtProxyServer(std::shared_ptr dht, in_port_t port) auto listener = currentListeners_.begin(); while (listener != currentListeners_.end()) { if (listener->session->is_closed() && dht_) { - dht_->cancelListen(listener->hash, listener->token); + dht_->cancelListen(listener->hash, std::move(listener->token)); // Remove listener if unused listener = currentListeners_.erase(listener); } else { @@ -226,36 +188,32 @@ DhtProxyServer::listen(const std::shared_ptr& session) const if (!infoHash) { infoHash = InfoHash::get(hash); } - s->yield(restbed::OK, "", [=]( const std::shared_ptr< restbed::Session > s) { - size_t token = dht_->listen(infoHash, [s](std::shared_ptr value) { - // Send values as soon as we get them - if (!s->is_closed()) { - Json::FastWriter writer; - s->yield(writer.write(value->toJson()), [](const std::shared_ptr /*session*/){ }); - } - return !s->is_closed(); - }).get(); + s->yield(restbed::OK); // Handle client deconnection // NOTE: for now, there is no handler, so we test the session in a thread // will be the case in restbed 5.0 - SessionToHashToken listener; - listener.session = s; - listener.hash = infoHash; - listener.token = token; - currentListeners_.emplace_back(listener); + SessionToHashToken listener; + listener.session = session; + listener.hash = infoHash; + listener.token = dht_->listen(infoHash, [s](std::shared_ptr value) { + // Send values as soon as we get them + if (!s->is_closed()) { + Json::FastWriter writer; + s->yield(writer.write(value->toJson()), [](const std::shared_ptr /*session*/){ }); + } + return !s->is_closed(); }); + currentListeners_.emplace_back(std::move(listener)); } else { - s->close(restbed::SERVICE_UNAVAILABLE, "{\"err\":\"Incorrect DhtRunner\"}"); + session->close(restbed::SERVICE_UNAVAILABLE, "{\"err\":\"Incorrect DhtRunner\"}"); } } ); } - void DhtProxyServer::put(const std::shared_ptr& session) const { - // TODO test with the proxy client const auto request = session->get_request(); int content_length = std::stoi(request->get_header("Content-Length", "0")); auto hash = request->get_path_parameter("hash"); @@ -268,7 +226,7 @@ DhtProxyServer::put(const std::shared_ptr& session) const { if (dht_) { if(b.empty()) { - std::string response("Missing parameters"); + std::string response("{\"err\":\"Missing parameters\"}"); s->close(restbed::BAD_REQUEST, response); } else { restbed::Bytes buf(b); @@ -280,11 +238,16 @@ DhtProxyServer::put(const std::shared_ptr& session) const // Build the Value from json auto value = std::make_shared(root); - Json::FastWriter writer; - dht_->put(infoHash, value); - s->close(restbed::OK, writer.write(value->toJson())); + dht_->put(infoHash, value, [s, value](bool ok) { + if (ok) { + Json::FastWriter writer; + s->close(restbed::OK, writer.write(value->toJson())); + } else { + s->close(restbed::BAD_GATEWAY, "{\"err\":\"put failed\"}"); + } + }); } else { - s->close(restbed::BAD_REQUEST, "Incorrect JSON"); + s->close(restbed::BAD_REQUEST, "{\"err\":\"Incorrect JSON\"}"); } } } else { @@ -310,7 +273,7 @@ DhtProxyServer::putSigned(const std::shared_ptr& session) cons { if (dht_) { if(b.empty()) { - std::string response("Missing parameters"); + std::string response("{\"err\":\"Missing parameters\"}"); s->close(restbed::BAD_REQUEST, response); } else { restbed::Bytes buf(b); @@ -325,7 +288,7 @@ DhtProxyServer::putSigned(const std::shared_ptr& session) cons dht_->putSigned(infoHash, value); s->close(restbed::OK, writer.write(value->toJson())); } else { - s->close(restbed::BAD_REQUEST, "Incorrect JSON" + strJson); + s->close(restbed::BAD_REQUEST, "{\"err\":\"Incorrect JSON\"}"); } } } else { @@ -341,16 +304,16 @@ DhtProxyServer::putEncrypted(const std::shared_ptr& session) c const auto request = session->get_request(); int content_length = std::stoi(request->get_header("Content-Length", "0")); auto hash = request->get_path_parameter("hash"); - InfoHash infoHash(hash); - if (!infoHash) - infoHash = InfoHash::get(hash); + InfoHash key(hash); + if (!key) + key = InfoHash::get(hash); session->fetch(content_length, [=](const std::shared_ptr s, const restbed::Bytes& b) { if (dht_) { if(b.empty()) { - std::string response("Missing parameters"); + std::string response("{\"err\":\"Missing parameters\"}"); s->close(restbed::BAD_REQUEST, response); } else { restbed::Bytes buf(b); @@ -358,22 +321,17 @@ DhtProxyServer::putEncrypted(const std::shared_ptr& session) c Json::Reader reader; std::string strJson(buf.begin(), buf.end()); bool parsingSuccessful = reader.parse(strJson.c_str(), root); - auto toHash = root["to"].asString(); - if (parsingSuccessful && !toHash.empty()) { + InfoHash to(root["to"].asString()); + if (parsingSuccessful && toInfoHash) { auto value = std::make_shared(root); - auto toHash = request->get_path_parameter("to"); - InfoHash toInfoHash(toHash); - if (toInfoHash) - toInfoHash = InfoHash::get(toHash); - Json::FastWriter writer; - dht_->putEncrypted(infoHash, toInfoHash, value); + dht_->putEncrypted(key, to, value); s->close(restbed::OK, writer.write(value->toJson())); } else { if(!parsingSuccessful) - s->close(restbed::BAD_REQUEST, "Incorrect JSON"); + s->close(restbed::BAD_REQUEST, "{\"err\":\"Incorrect JSON\"}"); else - s->close(restbed::BAD_REQUEST, "No destination found"); + s->close(restbed::BAD_REQUEST, "{\"err\":\"No destination found\"}"); } } } else {