Compare commits

...
111 Commits
Author SHA1 Message Date
Adrien Béraud 5593107dc4 uv 2017-10-17 19:00:53 +02:00
Adrien Béraud 679ac3badb tcp 2017-10-17 19:00:53 +02:00
Adrien Beraud 419fc24f1f wip 2017-10-17 19:00:53 +02:00
Adrien Beraud 2acdc1f1aa wip 2017-10-17 19:00:52 +02:00
Adrien Béraud 631c5f91ec libsodium 2017-10-17 19:00:13 +02:00
Adrien Béraud 81213d9d50 tcp 2017-10-17 19:00:13 +02:00
Adrien Béraud 5c87e81c42 allow to retrieve nodes from ping callback 2017-10-17 18:58:07 +02:00
Adrien Beraud aa0fbbce76 infohash: use 256 bits and SHA256 2017-10-17 18:58:07 +02:00
Adrien Beraud ac06dbe45f expired 2017-10-17 18:58:07 +02:00
Adrien Béraud 4a97265b22 use libuv for networking and event scheduling 2017-10-17 18:58:07 +02:00
Adrien Béraud 5a75b54e04 SockAddr: add constructor from simple sockaddr* 2017-10-17 18:57:41 +02:00
Adrien Beraud 64185d74d1 secure_vector: use mlock to prevent swapping to disc 2017-10-17 18:57:41 +02:00
Adrien Beraud c16b73b056 crypto: use SecureBlob 2017-10-17 18:57:41 +02:00
Adrien Béraud 775eb2e599 update to version 1.4.0
* improved Doxygen documentation
* cmake: add support to build documentation
* cmake: improve build on macOS
* API change: storage policies, getPublicAddress now use SockAddr
* NodeExport: added msgpack serialization
* fixed issue that could cause occasional request failures
* minor bug fixes
* minor performance improvements
2017-10-17 18:37:08 +02:00
Adrien Béraud 376486e64f node: use full range of socket IDs 2017-10-17 15:25:09 +02:00
Adrien Béraud 63013bf374 SearchNode: prevent copy, check request access 2017-10-17 03:17:52 +02:00
Adrien Béraud 488aa771a9 Node: don't expire existing requests 2017-10-16 17:12:33 +02:00
Adrien Béraud 6bc2f19a36 dht: log total number of nodes in onGetValuesDone 2017-10-16 17:12:33 +02:00
Adrien Beraud 2dd94fbfe8 cmake: use CMAKE_THREAD_LIBS_INIT as library 2017-10-16 01:26:40 +02:00
Adrien Béraud be2b9fd7dc NodeCache: handle end() lower_bound for non-empty map 2017-10-15 15:57:14 +02:00
Adrien Béraud 9d4e6e0984 rate limiter: use deterministic maintenance
Using deterministic maintenance sets an upper bound
to the size of the address map.
2017-10-15 01:18:32 +02:00
Adrien Beraud 4856a6b424 PublicKey: getId compatible with HASH_LEN = 32 2017-10-13 15:43:56 +02:00
Adrien Béraud 7df375fc75 NodeCache: fix invalid pointer access 2017-10-13 15:40:09 +02:00
Adrien Béraud 63e0c1266a dht: use OpStatus in put
Factorize shared pointers.
2017-10-13 02:07:14 +02:00
Adrien Béraud 1a54aff7bc storage policies: use SockAddr
API-breaking change to use const SockAddr& instead of sockaddr*.
2017-10-13 01:10:09 +02:00
Adrien Béraud f2b9f8eeda dht: move bucket time update to routing table 2017-10-13 01:10:09 +02:00
Adrien Béraud e9f007025f node: make most fields private 2017-10-13 01:10:09 +02:00
Adrien Béraud 6ce1531e1f node: make id const 2017-10-12 15:44:34 +02:00
Adrien Béraud 84a45c4f91 dht: factorize shared pointer dereference 2017-10-12 15:44:14 +02:00
Adrien Béraud e20ae56cab dht: support client mode
In client mode, a node won't store data
and won't participate in routing.
2017-10-12 15:44:14 +02:00
Adrien Béraud 4ce0168916 network engine: move ParsedMessage to separate file 2017-10-11 23:51:44 +02:00
Adrien Béraud 024dd42e5a dht: remove processMessage 2017-10-11 17:36:20 +02:00
Adrien Béraud 98ec789526 nertwork engine: move tid management to Node 2017-10-11 17:36:20 +02:00
Adrien Béraud 3bb5e7ee12 network engine: move transaction ownership to Node 2017-10-11 17:36:20 +02:00
Adrien Béraud 8c529b7d7f request: add getTid(), getSocket() 2017-10-11 13:47:53 +02:00
Adrien Béraud be7494961c network engine: move socket management to Node 2017-10-11 02:23:56 +02:00
Adrien Béraud bdb0bef0ee network engine: move RequestAnswer out of class 2017-10-11 02:22:03 +02:00
Adrien Béraud 73f26e120d doc: improve documentation 2017-10-10 18:16:33 +02:00
Adrien Béraud 9f4b16e4f5 cmake: add doxygen documentation support
Automatically build documentation if doxygen is found.
2017-10-10 17:45:34 +02:00
Adrien Béraud 323ad774e5 value: restore stream state after change 2017-10-09 15:14:20 +02:00
Adrien Béraud d0bcd39341 IpServiceAnnouncement: use SockAddr 2017-10-09 15:06:34 +02:00
Adrien Béraud 42125367d7 SockAddr: add documentation 2017-10-09 14:46:35 +02:00
Adrien Béraud d87a4ef28c InfoHash: add operator== 2017-10-07 11:35:56 +02:00
Adrien Béraud a3f4388556 InfoHash: increase toFloat precision 2017-10-07 11:35:56 +02:00
Adrien Béraud 97ee4b6fea dht: move onNewNode to RoutingTable 2017-10-07 11:35:56 +02:00
Adrien Béraud 0abd288958 SockAddr: factorize setters 2017-10-05 14:09:56 +02:00
Adrien Béraud 131073e211 SockAddr: cleanup 2017-09-27 11:36:42 +02:00
Adrien Béraud 078c4f064a cmake: use Threads package 2017-09-26 16:42:33 +02:00
Adrien Béraud f17a4e66f5 cmake: add flag to enable address sanitizer and stack protector 2017-09-26 16:35:38 +02:00
Adrien Béraud dd13ebb6d1 cmake: use private linking for private dependencies
Users of the OpenDHT shared library don't need to link
against GnuTLS or Nettle.
2017-09-26 16:34:18 +02:00
Adrien Béraud 1c45f34262 SockAddr: use pointer to sockaddr
Save memory and reduce copies by using a pointer
to a sockaddr structure of the right size instead of
using a full sockaddr_storage structure for every address.
2017-09-26 16:30:29 +02:00
Adrien Beraud a82f575232 NodeExport: add ostream operator 2017-08-30 10:48:49 +02:00
Adrien Beraud 1d84b34cf6 value: avoid checking failbit directly
failbit is implementation defined, use the bool operator instead.
2017-08-30 10:46:30 +02:00
Adrien Beraud bedb51bf52 crypto: add EC curve identity generation 2017-08-28 12:03:15 -04:00
Adrien Béraud 1b9e9452c4 NodeExport: add msgpack serialization 2017-08-28 12:02:09 -04:00
Adrien Beraud 3bdaf2e20b update to version 1.3.6
Bugfix release.
2017-08-11 14:54:31 -04:00
Adrien Beraud a1d67b881b python: fix invalid pointer access 2017-08-11 12:44:40 -04:00
Adrien Beraud c40750f6e4 dht: use proper value type to compute expiration 2017-08-10 15:00:34 -04:00
Adrien Béraud 63b40d3e3a cmake: check library paths 2017-07-22 11:25:40 -04:00
Adrien Béraud 21f5918af8 cmake: use version 3.1, cleanup 2017-07-22 10:59:36 -04:00
Adrien Beraud 6989236985 update to version 1.3.5
Bugfix release.
2017-07-20 00:49:29 -04:00
Adrien Beraud 1efa120010 dhtcluster: allow to resize cluster 2017-07-20 00:47:47 -04:00
Adrien Béraud a99ca1cf05 Merge pull request #205 from atraczyk/master
add listener.h, search.h, and storage.h to VS project
2017-07-19 19:03:14 -04:00
atraczyk 8fc8209731 add listener.h, search.h, and storage.h to VS project 2017-07-19 18:39:49 -04:00
Adrien Beraud 6f3eafc5ee dht: set refill time even when no nodes where found 2017-07-16 23:30:58 -04:00
Adrien Beraud 83d1cb97c8 dht: cleanup sendCachedPing 2017-07-16 23:30:58 -04:00
Adrien Beraud 477799a2b9 dht: remove unused method 2017-07-16 23:30:58 -04:00
Adrien Beraud 7e8c9ce944 dht: use SockAddr& in makeToken 2017-07-16 23:30:58 -04:00
Adrien Beraud 6decb6609a dht: use uint64_t to store secret 2017-07-16 23:30:58 -04:00
Adrien Beraud f1e7cd897d dht: use crypto::hash 2017-07-16 18:11:06 -04:00
Adrien Beraud 9bfaa224bc dht: move set_nonblocking to NetworkEngine 2017-07-16 18:10:49 -04:00
Adrien Beraud 5657661249 dht: move Search out of the Dht class 2017-07-16 17:46:54 -04:00
Adrien Beraud e69d61d8c7 dht: move request to src 2017-07-16 17:45:24 -04:00
Adrien Beraud 729c055f5b scanner: fix reset and IPv6 print issue 2017-07-16 16:23:18 -04:00
Adrien Beraud 61f91d626b dht: move Storage, Listener out of the Dht class 2017-07-16 15:04:18 -04:00
Adrien Beraud 732d10713d dht: minor coverity fixes 2017-07-16 13:39:14 -04:00
Adrien Beraud 15e5fa433b network engine: handle EAFNOSUPPORT 2017-07-15 23:46:33 -04:00
Adrien Beraud 43ff0e9e52 network engine: handle EAGAIN, ENETUNREACH
Handle a few common error codes from send():
* EAGAIN: reschedule without increasing retry count.
  This prevent nodes from failing on slow/very slow connections.
* ENETUNREACH, EHOSTUNREACH: expire the node immediately instead
  of waiting for an answer, to expire IPv6 nodes quickly if no
  IPv6 network is available.
2017-07-15 23:36:33 -04:00
Adrien Beraud cf32e78f1b scanner: enable logging, use rbegin() instead of prev(end()) 2017-07-15 23:25:17 -04:00
Adrien Beraud f170726b4b scanner: make steps asynchronously from UI 2017-07-15 23:24:19 -04:00
Adrien Beraud ebb20bdf0c python: NodeSet: return insertion status 2017-07-15 14:17:51 -04:00
Adrien Beraud 2ca7302352 ice candidates: reduce lifetime to 1 minute 2017-07-13 09:58:03 -04:00
Adrien Beraud 8a123f9f68 network: prevent sending packets larger than 1280 bytes 2017-07-12 11:24:58 -04:00
Adrien Béraud 7e92eb6192 Merge pull request #202 from atraczyk/master
clean up vs2015 project files and add a build script
2017-07-11 11:41:10 -04:00
atraczyk 56edf1720c clean up vs2015 project files and add a build script 2017-07-11 10:51:50 -04:00
Adrien Beraud 7c32dc49b1 update to version 1.3.4
Bugfix release that also adds SystemD modules
2017-06-30 01:32:23 +02:00
Adrien Beraud 5cb5ab019f routing table: fix invalid iterator access
When calling findClosestNodes with an key referencing the first bucket,
the itp iterator could point to before-begin() and dereference it.
2017-06-29 13:07:31 +02:00
Adrien Beraud 42fa09a6fa dhtnode: graceful signal handling in service and daemon modes 2017-06-29 10:20:31 +02:00
Adrien Beraud 673884a409 dhtnode: add service mode
Service mode is similar to the daemon mode but:
* doesn't fork
* logs to stdout/stderr by default
  instead of syslog
2017-06-29 09:57:55 +02:00
Simon Désaulniers ec6296e44b http_server: enable WHERE filtering for GET method 2017-06-27 09:59:01 +02:00
Simon Désaulniers b96d0de8a2 python: make use of Filter, Where in DhtRunner.get 2017-06-27 09:59:01 +02:00
Simon Désaulniers 98c4e39ed1 python: expose queries to API 2017-06-26 17:56:44 -04:00
Simon Désaulniers 9c483880cb value: prevent queries redundant information 2017-06-22 18:39:25 -04:00
Simon Désaulniers ae7e298946 http_server: put values, specific user_type,vid 2017-06-22 18:39:18 -04:00
Simon Désaulniers 7197c1fbb6 python: expose user_type member of Value 2017-06-22 17:42:57 -04:00
Adrien Béraud c10e04d526 Merge pull request #195 from GrossoMoreira/patch-1
Avoid dereferencing an iterator to 'end()'
2017-06-20 14:21:29 +02:00
Pedro Moreira ee883492ee Avoid dereferencing an iterator to 'end()'
*h.end() only works on VS 2015 with
_SECURE_SCL=0
_ITERATOR_DEBUG_LEVEL=0

Also, if these flags are used when compiling opendht, it seems that any client program that wants to link against opendht also needs the same flags enabled, which is a terrible requirement to force on a client.

The proposed change makes the code a bit cleaner and doesn't require any preprocessor tricks.
2017-06-20 13:19:29 +02:00
Adrien Beraud 2ed99db9b1 pkg-config: add argon2 dependency when not using included version 2017-06-18 22:14:27 +02:00
Adrien Beraud c11dcf5d76 dhtcluster: add systemd module 2017-06-01 18:35:43 +02:00
Adrien Beraud 4132e990ec dhtcluster: log cleanup 2017-06-01 18:28:33 +02:00
Adrien Beraud 76eba8c235 dhtcluster: add service mode
The service mode is like the daemon mode
but doesn't fork.
2017-06-01 18:27:12 +02:00
Adrien Beraud e1ac26773b dhtcluster: install with tools 2017-06-01 17:20:29 +02:00
Adrien Beraud 339ea70230 python/cluster: rename to dhtcluster 2017-06-01 17:19:50 +02:00
Adrien Beraud 943a39178c python/cluster: add syslog support
Use syslog in daemonized mode
2017-06-01 15:57:34 +02:00
Adrien Beraud ae0cb58a5c python/cluster: remove unused methods 2017-06-01 15:56:18 +02:00
Adrien Beraud dd116d8ad8 python/cluster: add clean stop on signal 2017-06-01 15:53:41 +02:00
Adrien Beraud 74c3c5bc87 systemd service: use pkg-config to find proper install location 2017-05-30 13:47:30 +02:00
Adrien Beraud b6bfa14872 systemd service: bind on port 4222 by default 2017-05-30 13:22:58 +02:00
Adrien Beraud 50a769c8fb dhtnode: add systemd service
Install a systemd service when configured
with -DOPENDHT_SYSTEMD (CMake only).
2017-05-30 12:41:47 +02:00
Adrien Beraud 39a254ce9f log: add syslog support
Add support to log using standard syslog,
add related option in dhtnode.
dhtnode will use syslog by default
when starting in daemon mode and
no logfile is defined.
2017-05-30 12:20:09 +02:00
Adrien Beraud 3aa720fe1f python/cluster: bind on ::0 by default on IPv6 2017-05-23 17:59:05 +02:00
69 changed files with 4863 additions and 3676 deletions

No files matched your search

+79 -33
View File
@@ -1,42 +1,55 @@
cmake_minimum_required (VERSION 2.8.6)
cmake_minimum_required (VERSION 3.1)
project (opendht)
set (opendht_VERSION_MAJOR 1)
set (opendht_VERSION_MINOR 3.3)
set (opendht_VERSION_MINOR 4.0)
set (opendht_VERSION ${opendht_VERSION_MAJOR}.${opendht_VERSION_MINOR})
set (PACKAGE_VERSION ${opendht_VERSION})
set (VERSION "${opendht_VERSION}")
list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
include(GNUInstallDirs)
set (prefix ${CMAKE_INSTALL_PREFIX})
set (exec_prefix "\${prefix}")
set (libdir "\${exec_prefix}/${CMAKE_INSTALL_LIBDIR}")
set (includedir "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}")
# Options
option (OPENDHT_STATIC "Build static library" ON)
option (OPENDHT_SHARED "Build shared library" ON)
option (OPENDHT_LOG "Build with logs" ON)
option (OPENDHT_PYTHON "Build Python bindings" OFF)
option (OPENDHT_TOOLS "Build DHT tools" ON)
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)
find_package(Doxygen)
option (OPENDHT_DOCUMENTATION "Create and install the HTML based API documentation (requires Doxygen)" ${DOXYGEN_FOUND})
# Dependencies
list (APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
find_package (Threads)
find_package (PkgConfig)
pkg_search_module(libuv libuv)
pkg_search_module(libsodium libsodium)
find_package (GnuTLS 3.3 REQUIRED)
pkg_search_module (Nettle nettle)
find_package (Msgpack 1.2 REQUIRED)
if (OPENDHT_TOOLS)
find_package (Readline 6 REQUIRED)
endif ()
if (NOT OPENDHT_ARGON2)
find_package(PkgConfig)
pkg_search_module(argon2 libargon2)
if (NOT argon2_FOUND)
if (argon2_FOUND)
message("Argon2 found " ${argon2_req})
set(argon2_lib ", libargon2")
else ()
message("Argon2 not found, using included version.")
set(OPENDHT_ARGON2 ON)
endif()
endif ()
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread -std=c++11 -Wno-return-type -Wall -Wextra -Wnon-virtual-dtor -pedantic-errors -fvisibility=hidden")
# Build flags
set (CMAKE_CXX_STANDARD 14)
set (CMAKE_CXX_STANDARD_REQUIRED on)
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-return-type -Wall -Wextra -Wnon-virtual-dtor -pedantic-errors -fvisibility=hidden")
if (OPENDHT_SANITIZE)
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fstack-protector-strong")
endif ()
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DMSGPACK_DISABLE_LEGACY_NIL -DMSGPACK_DISABLE_LEGACY_CONVERT")
if (NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
@@ -56,6 +69,40 @@ if (OPENDHT_LTO)
endif ()
endif ()
if (MSGPACK_INCLUDE_DIRS)
include_directories (SYSTEM "${MSGPACK_INCLUDE_DIRS}")
endif ()
if (GNUTLS_INCLUDE_DIRS)
include_directories (SYSTEM "${GNUTLS_INCLUDE_DIRS}")
endif ()
if (Nettle_INCLUDE_DIRS)
include_directories (SYSTEM "${Nettle_INCLUDE_DIRS}")
endif ()
if (libuv_INCLUDE_DIRS)
include_directories (SYSTEM "${libuv_INCLUDE_DIRS}")
endif ()
if (libsodium_INCLUDE_DIRS)
include_directories (SYSTEM "${libsodium_INCLUDE_DIRS}")
endif ()
link_directories (${libuv_LIBRARY_DIRS} ${libsodium_LIBRARY_DIRS} ${Nettle_LIBRARY_DIRS})
include_directories (
./
include/
include/opendht/
${CMAKE_CURRENT_BINARY_DIR}/include/
)
# Install dirs
include (GNUInstallDirs)
set (prefix ${CMAKE_INSTALL_PREFIX})
set (exec_prefix "\${prefix}")
set (libdir "\${exec_prefix}/${CMAKE_INSTALL_LIBDIR}")
set (includedir "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}")
set (bindir "${CMAKE_INSTALL_FULL_BINDIR}")
set (sysconfdir "${CMAKE_INSTALL_FULL_SYSCONFDIR}")
set (top_srcdir "${CMAKE_CURRENT_SOURCE_DIR}")
# Sources
list (APPEND opendht_SOURCES
src/utils.cpp
src/infohash.cpp
@@ -64,6 +111,11 @@ list (APPEND opendht_SOURCES
src/node.cpp
src/value.cpp
src/dht.cpp
src/storage.h
src/listener.h
src/search.h
src/parsed_message.h
src/request.h
src/callbacks.cpp
src/routing_table.cpp
src/node_cache.cpp
@@ -99,18 +151,6 @@ list (APPEND opendht_HEADERS
include/opendht/indexation/pht.h
)
configure_file (
opendht.pc.in
opendht.pc
@ONLY
)
include_directories(
./
include/
include/opendht/
${CMAKE_CURRENT_BINARY_DIR}/include/
)
if(OPENDHT_ARGON2)
# make sure argon2 submodule is up to date and initialized
message("Initializing Argon2 submodule")
@@ -128,10 +168,7 @@ if(OPENDHT_ARGON2)
include_directories(argon2/include/)
endif()
if (NOT DEFINED CMAKE_INSTALL_LIBDIR)
set(CMAKE_INSTALL_LIBDIR lib)
endif ()
# Targets
if (OPENDHT_STATIC)
add_library (opendht-static STATIC
${opendht_SOURCES}
@@ -147,7 +184,7 @@ 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 gnutls nettle)
target_link_libraries(opendht-static ${CMAKE_THREAD_LIBS_INIT} ${GNUTLS_LIBRARIES} ${Nettle_LIBRARIES} ${libuv_LIBRARIES} ${libsodium_LIBRARIES})
install (TARGETS opendht-static DESTINATION ${CMAKE_INSTALL_LIBDIR} EXPORT opendht)
endif ()
@@ -160,15 +197,15 @@ if (OPENDHT_SHARED)
set_target_properties (opendht PROPERTIES SOVERSION ${opendht_VERSION_MAJOR} VERSION ${opendht_VERSION})
target_compile_definitions(opendht PRIVATE OPENDHT_BUILD)
if (OPENDHT_LTO)
target_link_libraries(opendht -flto -fuse-linker-plugin)
target_link_libraries(opendht PRIVATE -flto -fuse-linker-plugin)
endif ()
if (OPENDHT_ARGON2)
target_include_directories(opendht SYSTEM PRIVATE argon2)
else ()
target_link_libraries(opendht ${argon2_LIBRARIES})
target_link_libraries(opendht PRIVATE ${argon2_LIBRARIES})
target_include_directories(opendht SYSTEM PRIVATE ${argon2_INCLUDE_DIRS})
endif ()
target_link_libraries(opendht gnutls nettle)
target_link_libraries(opendht PRIVATE ${CMAKE_THREAD_LIBS_INIT} ${GNUTLS_LIBRARIES} ${Nettle_LIBRARIES} ${libuv_LIBRARIES} ${libsodium_LIBRARIES})
install (TARGETS opendht DESTINATION ${CMAKE_INSTALL_LIBDIR} EXPORT opendht)
endif ()
@@ -181,11 +218,20 @@ if (OPENDHT_PYTHON)
add_subdirectory(python)
endif ()
# CMake module
include(CMakePackageConfigHelpers)
write_basic_package_version_file("${CMAKE_CURRENT_BINARY_DIR}/opendhtConfigVersion.cmake"
VERSION ${opendht_VERSION}
COMPATIBILITY AnyNewerVersion
)
# PkgConfig module
configure_file (
opendht.pc.in
opendht.pc
@ONLY
)
# Install targets
install (DIRECTORY include DESTINATION ${CMAKE_INSTALL_PREFIX})
install (FILES ${CMAKE_CURRENT_BINARY_DIR}/opendht.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
install (EXPORT opendht DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/opendht FILE opendhtConfig.cmake)
-162
View File
@@ -1,162 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{2B85EA05-086B-4DF6-A140-DF52BDAEEEEA}</ProjectGuid>
<RootNamespace>argon</RootNamespace>
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<WholeProgramOptimization>false</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<WholeProgramOptimization>false</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<TargetExt>.lib</TargetExt>
<OutDir>$(ProjectDir)..\..\</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<TargetExt>.lib</TargetExt>
<OutDir>$(ProjectDir)..\..\</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<TargetExt>.lib</TargetExt>
<OutDir>$(ProjectDir)..\..\</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<TargetExt>.lib</TargetExt>
<OutDir>$(ProjectDir)..\..\</OutDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32_NATIVE;WIN32_LEAN_AND_MEAN;_MBCS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Lib>
<OutputFile>$(OutDir)\lib\x86\$(TargetName)$(TargetExt)</OutputFile>
</Lib>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32_NATIVE;WIN32_LEAN_AND_MEAN;_MBCS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Lib>
<OutputFile>$(OutDir)\lib\x64\$(TargetName)$(TargetExt)</OutputFile>
</Lib>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32_NATIVE;WIN32_LEAN_AND_MEAN;_MBCS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
<Lib>
<OutputFile>$(OutDir)\lib\x86\$(TargetName)$(TargetExt)</OutputFile>
</Lib>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32_NATIVE;WIN32_LEAN_AND_MEAN;_MBCS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
<Lib>
<OutputFile>$(OutDir)\lib\x64\$(TargetName)$(TargetExt)</OutputFile>
</Lib>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\argon2\argon2.c" />
<ClCompile Include="..\argon2\core.c" />
<ClCompile Include="..\argon2\encoding.c" />
<ClCompile Include="..\argon2\opt.c" />
<ClCompile Include="..\argon2\ref.c" />
<ClCompile Include="..\argon2\thread.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\argon2\argon2.h" />
<ClInclude Include="..\argon2\core.h" />
<ClInclude Include="..\argon2\encoding.h" />
<ClInclude Include="..\argon2\opt.h" />
<ClInclude Include="..\argon2\ref.h" />
<ClInclude Include="..\argon2\thread.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
-151
View File
@@ -1,151 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{36A40B5B-FBAD-4830-9F74-7B4E5788B7A6}</ProjectGuid>
<RootNamespace>blake</RootNamespace>
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<WholeProgramOptimization>false</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<WholeProgramOptimization>false</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<TargetExt>.lib</TargetExt>
<OutDir>$(ProjectDir)..\..\</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<TargetExt>.lib</TargetExt>
<OutDir>$(ProjectDir)..\..\</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<TargetExt>.lib</TargetExt>
<OutDir>$(ProjectDir)..\..\</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<TargetExt>.lib</TargetExt>
<OutDir>$(ProjectDir)..\..\</OutDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
</ClCompile>
<Lib>
<OutputFile>$(OutDir)\lib\x86\$(TargetName)$(TargetExt)</OutputFile>
</Lib>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
</ClCompile>
<Lib>
<OutputFile>$(OutDir)\lib\x64\$(TargetName)$(TargetExt)</OutputFile>
</Lib>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
</ClCompile>
<Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
<Lib>
<OutputFile>$(OutDir)\lib\x86\$(TargetName)$(TargetExt)</OutputFile>
</Lib>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
</ClCompile>
<Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
<Lib>
<OutputFile>$(OutDir)\lib\x64\$(TargetName)$(TargetExt)</OutputFile>
</Lib>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\argon2\blake2\blake2b.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\argon2\blake2\blake2-impl.h" />
<ClInclude Include="..\argon2\blake2\blake2.h" />
<ClInclude Include="..\argon2\blake2\blamka-round-opt.h" />
<ClInclude Include="..\argon2\blake2\blamka-round-ref.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
+35
View File
@@ -0,0 +1,35 @@
@echo on
SETLOCAL EnableDelayedExpansion
set SRC=%~dp0
set PATH=%PATH%;%ProgramFiles(x86)%\MSBuild\14.0\Bin\
set MSBUILD_ARGS=/nologo /p:Configuration=Release /p:Platform=x64 /verbosity:normal /maxcpucount:%NUMBER_OF_PROCESSORS%
set TOBUILD=( ^
contrib\build\argon2\vs2015\Argon2Ref\Argon2Ref.vcxproj, ^
contrib\build\gmp\SMP\libgmp.vcxproj, ^
contrib\build\nettle\SMP\libnettle.vcxproj, ^
contrib\build\nettle\SMP\libhogweed.vcxproj, ^
contrib\build\libiconv\SMP\libiconv.vcxproj, ^
contrib\build\nettle\SMP\libiconv.vcxproj, ^
contrib\build\zlib\SMP\libzlib.vcxproj, ^
contrib\build\gnutls\SMP\libgnutls.vcxproj, ^
contrib\build\msgpack-c\msgpack_vc8.vcxproj, ^
opendht.vcxproj, ^
dhtchat.vcxproj, ^
dhtscanner.vcxproj, ^
dhtnode.vcxproj ^
)
for %%I in %TOBUILD% do (
call :build "%SRC%%%I"
)
exit /B %ERRORLEVEL%
:build
echo "Building project: " %*
msbuild %* %MSBUILD_ARGS%
exit /B 0
+39
View File
@@ -0,0 +1,39 @@
--- a/vs2015/Argon2Ref/Argon2Ref.vcxproj
+++ b/vs2015/Argon2Ref/Argon2Ref.vcxproj
@@ -59,7 +59,7 @@
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
- <ConfigurationType>Application</ConfigurationType>
+ <ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
@@ -117,8 +117,8 @@
<IncludePath>$(SolutionDir)include;$(IncludePath)</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
- <OutDir>$(SolutionDir)vs2015\build\</OutDir>
- <IntDir>$(SolutionDir)vs2015\build\$(ProjectName)\</IntDir>
+ <OutDir>$(ProjectDir)..\..\..\lib\x64</OutDir>
+ <IntDir>$(ProjectDir)vs2015\build\$(ProjectName)\</IntDir>
<IncludePath>$(SolutionDir)include;$(IncludePath)</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseStatic|x64'">
@@ -179,6 +179,7 @@
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(ProjectDir)..\..\include</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
@@ -223,4 +224,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
-</Project>
+</Project>
\ No newline at end of file
--
2.10.2.windows.1
@@ -17,4 +17,6 @@ rename %BUILD%\phc-winner-argon2-%ARGON2_VERSION% argon2
cd %BUILD%\argon2
git apply --reject --whitespace=fix %SRC%\argon2\argon2-uwp.patch
cd %SRC%
+3 -1
View File
@@ -79,6 +79,7 @@
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<IntDir>$(ProjectName)\$(Platform)\$(Configuration)\</IntDir>
<OutDir>$(ProjectDir)..\..\bin\x64</OutDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
@@ -148,8 +149,9 @@
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<AdditionalDependencies>crypt32.lib;Argon2Ref.lib;libgnutls.lib;opendht.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(ProjectDir)contrib\build\lib\x64;$(ProjectDir)..\..\lib\x64</AdditionalLibraryDirectories>
<AdditionalLibraryDirectories>$(ProjectDir)contrib\build\lib\x64;$(ProjectDir)..\..\lib\x64;$(ProjectDir)vs2015\build</AdditionalLibraryDirectories>
<AdditionalOptions>/ignore:4049 %(AdditionalOptions)</AdditionalOptions>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
+3 -1
View File
@@ -79,6 +79,7 @@
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<IntDir>$(ProjectName)\$(Platform)\$(Configuration)\</IntDir>
<OutDir>$(ProjectDir)..\..\bin\x64</OutDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
@@ -148,8 +149,9 @@
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<AdditionalDependencies>crypt32.lib;Argon2Ref.lib;libgnutls.lib;opendht.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(ProjectDir)contrib\build\lib\x64;$(ProjectDir)..\..\lib\x64</AdditionalLibraryDirectories>
<AdditionalLibraryDirectories>$(ProjectDir)contrib\build\lib\x64;$(ProjectDir)..\..\lib\x64;$(ProjectDir)vs2015\build</AdditionalLibraryDirectories>
<AdditionalOptions>/ignore:4049 %(AdditionalOptions)</AdditionalOptions>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
+3 -1
View File
@@ -79,6 +79,7 @@
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<IntDir>$(ProjectName)\$(Platform)\$(Configuration)\</IntDir>
<OutDir>$(ProjectDir)..\..\bin\x64</OutDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
@@ -148,8 +149,9 @@
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<AdditionalDependencies>crypt32.lib;Argon2Ref.lib;libgnutls.lib;opendht.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(ProjectDir)contrib\build\lib\x64;$(ProjectDir)..\..\lib\x64</AdditionalLibraryDirectories>
<AdditionalLibraryDirectories>$(ProjectDir)contrib\build\lib\x64;$(ProjectDir)..\..\lib\x64;$(ProjectDir)vs2015\build</AdditionalLibraryDirectories>
<AdditionalOptions>/ignore:4049 %(AdditionalOptions)</AdditionalOptions>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
+2
View File
@@ -0,0 +1,2 @@
@echo on
call contrib\src\fetch_and_patch.bat
-221
View File
@@ -1,221 +0,0 @@
--- /dev/null
+++ b/msgpack_vc8.vcxproj
@@ -0,0 +1,214 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectName>MessagePack</ProjectName>
+ <ProjectGuid>{122A2EA4-B283-4241-9655-786DE78283B2}</ProjectGuid>
+ <RootNamespace>MessagePack</RootNamespace>
+ <Keyword>Win32Proj</Keyword>
+ <WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <PlatformToolset>v140</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ <WholeProgramOptimization>false</WholeProgramOptimization>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <PlatformToolset>v140</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ <WholeProgramOptimization>false</WholeProgramOptimization>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <PlatformToolset>v140</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <PlatformToolset>v140</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup>
+ <_ProjectFileVersion>14.0.25123.0</_ProjectFileVersion>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <OutDir>$(ProjectDir)..\</OutDir>
+ <IntDir>$(Configuration)\</IntDir>
+ <TargetName>msgpackd</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <TargetName>msgpackd</TargetName>
+ <OutDir>$(ProjectDir)..\</OutDir>
+ <IntDir>$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <OutDir>$(ProjectDir)..\</OutDir>
+ <IntDir>$(Configuration)\</IntDir>
+ <TargetName>msgpack</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <TargetName>msgpack</TargetName>
+ <OutDir>$(ProjectDir)..\</OutDir>
+ <IntDir>$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>.;include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32_NATIVE;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>true</MinimalRebuild>
+ <BasicRuntimeChecks>StackFrameRuntimeCheck</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <PrecompiledHeader />
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ </ClCompile>
+ <Lib>
+ <OutputFile>$(OutDir)\lib\x86\$(TargetName)$(TargetExt)</OutputFile>
+ </Lib>
+ <PostBuildEvent>
+ <Command>mkdir $(OutDir)\include
+
+mkdir $(OutDir)\include\msgpack
+
+copy include\*.h $(OutDir)\include\
+
+copy include\*.hpp $(OutDir)\include\
+xcopy /S /Y include\msgpack\*.h $(OutDir)\include\msgpack
+xcopy /S /Y include\msgpack\*.hpp $(OutDir)\include\msgpack
+</Command>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>.;include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32_NATIVE;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <BasicRuntimeChecks>StackFrameRuntimeCheck</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ </ClCompile>
+ <Lib>
+ <OutputFile>$(OutDir)\lib\x64\$(TargetName)$(TargetExt)</OutputFile>
+ </Lib>
+ <PostBuildEvent>
+ <Command>mkdir $(OutDir)\include
+
+mkdir $(OutDir)\include\msgpack
+
+copy include\*.h $(OutDir)\include\
+
+copy include\*.hpp $(OutDir)\include\
+xcopy /S /Y include\msgpack\*.h $(OutDir)\include\msgpack
+xcopy /S /Y include\msgpack\*.hpp $(OutDir)\include\msgpack
+</Command>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <AdditionalIncludeDirectories>.;include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32_NATIVE;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <PrecompiledHeader />
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <ProgramDataBaseFileName>$(OutDir)\lib\x86\$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Lib>
+ <OutputFile>$(OutDir)\lib\x86\$(TargetName)$(TargetExt)</OutputFile>
+ <LinkTimeCodeGeneration />
+ </Lib>
+ <PostBuildEvent>
+ <Command>mkdir $(OutDir)\include
+
+mkdir $(OutDir)\include\msgpack
+
+copy include\*.h $(OutDir)\include\
+
+copy include\*.hpp $(OutDir)\include\
+xcopy /S /Y include\msgpack\*.h $(OutDir)\include\msgpack
+xcopy /S /Y include\msgpack\*.hpp $(OutDir)\include\msgpack
+</Command>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <AdditionalIncludeDirectories>.;include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>MSGPACK_USE_CPP03;WIN32_NATIVE;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <ProgramDataBaseFileName>$(OutDir)\lib\x64\$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Lib>
+ <OutputFile>$(OutDir)\lib\x64\$(TargetName)$(TargetExt)</OutputFile>
+ <LinkTimeCodeGeneration />
+ </Lib>
+ <PostBuildEvent>
+ <Command>mkdir $(OutDir)\include
+
+mkdir $(OutDir)\include\msgpack
+
+copy include\*.h $(OutDir)\include\
+
+copy include\*.hpp $(OutDir)\include\
+xcopy /S /Y include\msgpack\*.h $(OutDir)\include\msgpack
+xcopy /S /Y include\msgpack\*.hpp $(OutDir)\include\msgpack
+</Command>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="src\objectc.c" />
+ <ClCompile Include="src\unpack.c" />
+ <ClCompile Include="src\version.c" />
+ <ClCompile Include="src\vrefbuffer.c" />
+ <ClCompile Include="src\zone.c" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="include\msgpack.hpp" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
\ No newline at end of file
--
2.8.1.windows.1
+5
View File
@@ -57,6 +57,9 @@
<ClInclude Include="..\include\opendht\securedht.h" />
<ClInclude Include="..\include\opendht\utils.h" />
<ClInclude Include="..\include\opendht\value.h" />
<ClInclude Include="..\src\listener.h" />
<ClInclude Include="..\src\search.h" />
<ClInclude Include="..\src\storage.h" />
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{711397CE-E5D5-467D-9457-8716C047E50C}</ProjectGuid>
@@ -229,6 +232,8 @@ copy ..\include\opendht.h $(OutDir)\include\</Command>
<Lib>
<OutputFile>$(OutDir)\lib\x64\$(TargetName)$(TargetExt)</OutputFile>
<LinkTimeCodeGeneration>false</LinkTimeCodeGeneration>
<AdditionalLibraryDirectories>
</AdditionalLibraryDirectories>
</Lib>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+125 -36
View File
@@ -1,43 +1,132 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ClCompile Include="..\src\callbacks.cpp" />
<ClCompile Include="..\src\crypto.cpp" />
<ClCompile Include="..\src\default_types.cpp" />
<ClCompile Include="..\src\dht.cpp" />
<ClCompile Include="..\src\dhtrunner.cpp" />
<ClCompile Include="..\src\indexation\pht.cpp" />
<ClCompile Include="..\src\infohash.cpp" />
<ClCompile Include="..\src\log.cpp" />
<ClCompile Include="..\src\network_engine.cpp" />
<ClCompile Include="..\src\node.cpp" />
<ClCompile Include="..\src\node_cache.cpp" />
<ClCompile Include="..\src\routing_table.cpp" />
<ClCompile Include="..\src\securedht.cpp" />
<ClCompile Include="..\src\utils.cpp" />
<ClCompile Include="..\src\value.cpp" />
<ClCompile Include="..\src\callbacks.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\crypto.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\default_types.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\dht.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\dhtrunner.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\infohash.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\log.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\network_engine.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\node.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\node_cache.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\indexation\pht.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\routing_table.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\securedht.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\utils.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\value.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\include\opendht.h" />
<ClInclude Include="..\include\opendht\callbacks.h" />
<ClInclude Include="..\include\opendht\crypto.h" />
<ClInclude Include="..\include\opendht\default_types.h" />
<ClInclude Include="..\include\opendht\dht.h" />
<ClInclude Include="..\include\opendht\dhtrunner.h" />
<ClInclude Include="..\include\opendht\indexation\pht.h" />
<ClInclude Include="..\include\opendht\infohash.h" />
<ClInclude Include="..\include\opendht\log.h" />
<ClInclude Include="..\include\opendht\log_enable.h" />
<ClInclude Include="..\include\opendht\network_engine.h" />
<ClInclude Include="..\include\opendht\node.h" />
<ClInclude Include="..\include\opendht\node_cache.h" />
<ClInclude Include="..\include\opendht\rate_limiter.h" />
<ClInclude Include="..\include\opendht\request.h" />
<ClInclude Include="..\include\opendht\rng.h" />
<ClInclude Include="..\include\opendht\routing_table.h" />
<ClInclude Include="..\include\opendht\scheduler.h" />
<ClInclude Include="..\include\opendht\securedht.h" />
<ClInclude Include="..\include\opendht\utils.h" />
<ClInclude Include="..\include\opendht\value.h" />
<ClInclude Include="..\include\opendht\callbacks.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\include\opendht\crypto.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\include\opendht\default_types.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\include\opendht\dht.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\include\opendht\dhtrunner.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\include\opendht\infohash.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\src\listener.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\include\opendht\log.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\include\opendht\log_enable.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\include\opendht\network_engine.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\include\opendht\node.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\include\opendht\node_cache.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\include\opendht.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\include\opendht\indexation\pht.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\include\opendht\rate_limiter.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\include\opendht\request.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\include\opendht\rng.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\include\opendht\routing_table.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\include\opendht\scheduler.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\src\search.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\include\opendht\securedht.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\src\storage.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\include\opendht\utils.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\include\opendht\value.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{be73d179-6cf2-4c05-8d91-53c8499a7134}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{d1ab5bfe-3ab1-45ee-9324-b4b071887668}</UniqueIdentifier>
</Filter>
</ItemGroup>
</Project>
+6 -3
View File
@@ -1,7 +1,7 @@
dnl define macros
m4_define([opendht_major_version], 1)
m4_define([opendht_minor_version], 3)
m4_define([opendht_patch_version], 3)
m4_define([opendht_minor_version], 4)
m4_define([opendht_patch_version], 0)
m4_define([opendht_version],
[opendht_major_version.opendht_minor_version.opendht_patch_version])
@@ -104,7 +104,10 @@ AC_ARG_WITH([argon2], AS_HELP_STRING([--without-argon2], [Use included Argon2]))
AS_IF([test "x$with_argon2" != "xno"],
[PKG_CHECK_MODULES([Argon2], [libargon2], [have_argon2=yes], [have_argon2=no])],
[have_argon2=no])
AS_IF([test "x$have_argon2" = "xyes"], [], [
AS_IF([test "x$have_argon2" = "xyes"], [
AC_MSG_NOTICE([Using system Argon2])
AC_SUBST(argon2_lib, [", libargon2"])
], [
AS_IF([test "x$with_argon2" = "xyes"], [
AC_MSG_ERROR([Argon2 requested but not found])
],[
+13
View File
@@ -1,3 +1,16 @@
if (OPENDHT_TOOLS)
INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/dhtnode.1 DESTINATION ${CMAKE_INSTALL_PREFIX}/share/man/man1)
endif ()
if (OPENDHT_DOCUMENTATION)
if (NOT DOXYGEN_FOUND)
message(FATAL_ERROR "Doxygen is needed to build the documentation.")
endif()
configure_file (Doxyfile.in Doxyfile @ONLY)
add_custom_target(doc ALL
COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMENT "Generating API documentation with Doxygen"
VERBATIM)
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html DESTINATION share/doc/opendht)
endif()
+12 -1
View File
@@ -49,6 +49,12 @@ struct OPENDHT_PUBLIC NodeStats {
std::string toString() const;
};
struct OPENDHT_PUBLIC NetworkConfig {
std::string bind_addr {"::0"};
in_port_t bind_port {0};
};
/**
* Dht configuration.
*/
@@ -68,6 +74,8 @@ struct OPENDHT_PUBLIC Config {
/** Makes the DHT responsible to maintain its stored values. Consumes more ressources. */
bool maintain_storage;
NetworkConfig network_config;
};
/**
@@ -87,6 +95,7 @@ using QueryCallback = std::function<bool(const std::vector<std::shared_ptr<Field
using GetCallback = std::function<bool(const std::vector<std::shared_ptr<Value>>& values)>;
using GetCallbackSimple = std::function<bool(std::shared_ptr<Value> value)>;
using ShutdownCallback = std::function<void()>;
using StatusCallback = std::function<void(NodeStatus, NodeStatus)>;
using CertificateStoreQuery = std::function<std::vector<std::shared_ptr<crypto::Certificate>>(const InfoHash& pk_id)>;
@@ -95,10 +104,11 @@ typedef bool (*GetCallbackRaw)(std::shared_ptr<Value>, void *user_data);
OPENDHT_PUBLIC GetCallbackSimple bindGetCb(GetCallbackRaw raw_cb, void* user_data);
OPENDHT_PUBLIC GetCallback bindGetCb(GetCallbackSimple cb);
using DoneCallback = std::function<void(bool success, const std::vector<std::shared_ptr<Node>>& nodes)>;
using DoneCallback = std::function<void(bool success, std::vector<std::shared_ptr<Node>>&& nodes)>;
typedef void (*DoneCallbackRaw)(bool, std::vector<std::shared_ptr<Node>>*, void *user_data);
typedef void (*ShutdownCallbackRaw)(void *user_data);
typedef void (*DoneCallbackSimpleRaw)(bool, void *user_data);
typedef bool (*FilterRaw)(const Value&, void *user_data);
using DoneCallbackSimple = std::function<void(bool success)>;
@@ -106,5 +116,6 @@ OPENDHT_PUBLIC ShutdownCallback bindShutdownCb(ShutdownCallbackRaw shutdown_cb_r
OPENDHT_PUBLIC DoneCallback bindDoneCb(DoneCallbackSimple donecb);
OPENDHT_PUBLIC DoneCallback bindDoneCb(DoneCallbackRaw raw_cb, void* user_data);
OPENDHT_PUBLIC DoneCallbackSimple bindDoneCbSimple(DoneCallbackSimpleRaw raw_cb, void* user_data);
OPENDHT_PUBLIC Value::Filter bindFilterRaw(FilterRaw raw_filter, void* user_data);
}
+182 -8
View File
@@ -28,6 +28,8 @@ extern "C" {
#include <gnutls/x509.h>
}
#include <sodium.h>
#include <vector>
#include <memory>
@@ -35,7 +37,13 @@ extern "C" {
#include <iso646.h>
#endif
#include <sys/mman.h>
namespace dht {
/**
* Contains all crypto primitives
*/
namespace crypto {
class OPENDHT_PUBLIC CryptoException : public std::runtime_error {
@@ -496,11 +504,19 @@ public:
secure_vector() {}
secure_vector(secure_vector<T> const&) = default;
secure_vector(secure_vector<T> &&) = default;
explicit secure_vector(unsigned size): data_(size) {}
explicit secure_vector(unsigned size, T _item): data_(size, _item) {}
explicit secure_vector(const std::vector<T>& c): data_(c) {}
secure_vector(std::vector<T>&& c): data_(std::move(c)) {}
~secure_vector() { clean(); }
explicit secure_vector(unsigned size): data_(size) {
mlock(data_.data(), data_.size()*sizeof(T));
}
explicit secure_vector(unsigned size, T _item): data_(size, _item) {
mlock(data_.data(), data_.size()*sizeof(T));
}
explicit secure_vector(const std::vector<T>& c): data_(c) {
mlock(data_.data(), data_.size()*sizeof(T));
}
secure_vector(std::vector<T>&& c): data_(std::move(c)) {
mlock(data_.data(), data_.size()*sizeof(T));
}
~secure_vector() { clean(); munlock(data_.data(), data_.size()*sizeof(T)); }
static secure_vector<T> getRandom(size_t size) {
secure_vector<T> ret(size/sizeof(T));
@@ -513,24 +529,30 @@ public:
if (&c == this)
return *this;
clean();
munlock(data_.data(), data_.size()*sizeof(T));
data_ = c.data_;
mlock(data_.data(), data_.size()*sizeof(T));
return *this;
}
secure_vector<T>& operator=(secure_vector<T>&& c) {
if (&c == this)
return *this;
clean();
munlock(data_.data(), data_.size()*sizeof(T));
data_ = std::move(c.data_);
return *this;
}
secure_vector<T>& operator=(std::vector<T>&& c) {
clean();
munlock(data_.data(), data_.size()*sizeof(T));
data_ = std::move(c);
mlock(data_.data(), data_.size()*sizeof(T));
return *this;
}
std::vector<T>& writable() { clean(); return data_; }
const std::vector<T>& makeInsecure() const { return data_; }
const uint8_t* data() const { return data_.data(); }
uint8_t* data() { return data_.data(); }
void clean() {
clean(data_.begin(), data_.end());
@@ -547,14 +569,17 @@ public:
if (s < data_.size()) {
//shrink
clean(data_.begin()+s, data_.end());
munlock(data_.data()+s, (data_.size()-s)*sizeof(T));
data_.resize(s);
} else {
//grow
auto data = std::move(data_); // move protected data
clear();
data_.resize(s);
mlock(data_.data(), data_.size()*sizeof(T));
std::copy(data.begin(), data.end(), data_.begin());
clean(data.begin(), data.end());
munlock(data.data(), data.size()*sizeof(T));
}
}
@@ -571,8 +596,154 @@ private:
std::vector<T> data_;
};
template <class T, size_t N>
class OPENDHT_PUBLIC secure_array
{
public:
secure_array() {
mlock(data_.data(), data_.size()*sizeof(T));
}
explicit secure_array(T _item): data_( _item) {
mlock(data_.data(), data_.size()*sizeof(T));
}
explicit secure_array(const std::array<T,N>& c): data_(c) {
mlock(data_.data(), data_.size()*sizeof(T));
}
secure_array(secure_array<T,N> const&) = default;
secure_array(secure_array<T,N> &&) = delete;
~secure_array() { clean(); munlock(data_.data(), data_.size()*sizeof(T)); }
static secure_array<T,N> getRandom() {
secure_array<T,N> ret;
crypto::random_device rdev;
std::uniform_int_distribution<uint8_t> rand_byte;
std::generate_n((uint8_t*)ret.data_.data(), ret.size()*sizeof(T), std::bind(rand_byte, std::ref(rdev)));
return ret;
}
secure_array<T,N>& operator=(const secure_array<T,N>& c) {
if (&c == this)
return *this;
clean();
data_ = c.data_;
return *this;
}
secure_array<T,N>& operator=(secure_array<T,N>&& c) = delete;
std::array<T,N>& writable() { clean(); return data_; }
const std::array<T,N>& makeInsecure() const { return data_; }
const uint8_t* data() const { return data_.data(); }
uint8_t* data() { return data_.data(); }
void clean() {
clean(data_.begin(), data_.end());
}
size_t size() const { return data_.size(); }
private:
/**
* Securely wipe memory
*/
static void clean(const typename std::array<T,N>::iterator& i, const typename std::array<T,N>::iterator& j) {
volatile uint8_t* b = reinterpret_cast<uint8_t*>(&*i);
volatile uint8_t* e = reinterpret_cast<uint8_t*>(&*j);
std::fill(b, e, 0);
}
std::array<T, N> data_;
};
using SecureBlob = secure_vector<uint8_t>;
/*class EcPublicKey : public std::array<uint8_t, crypto_box_PUBLICKEYBYTES> {
public:
Blob encrypt(const Blob& message) const {
Blob ret(crypto_box_SEALBYTES+message.size());
crypto_box_seal(ret.data(), message.data(), message.size(), data());
return ret;
}
const InfoHash& getId() const {
return *reinterpret_cast<const InfoHash*>(this);
}
};*/
class EcSecretKey {
public:
using KeyData = secure_array<uint8_t, crypto_box_SECRETKEYBYTES>;
using EcPublicKey = InfoHash;
static EcSecretKey generate() {
EcSecretKey ret;
crypto_box_keypair(ret.pk.data(), ret.key.data());
return ret;
}
const EcPublicKey& getPublicKey() const {
return pk;
}
Blob encrypt(const uint8_t* data, size_t size, const EcPublicKey& pub) const {
Blob ret(crypto_box_NONCEBYTES+crypto_box_MACBYTES+size);
randombytes_buf(ret.data(), crypto_box_NONCEBYTES);
if (crypto_box_easy(ret.data()+crypto_box_NONCEBYTES, data, size, ret.data(),
pub.data(), key.data()) != 0) {
throw CryptoException("Can't encrypt data");
}
return ret;
}
Blob decrypt(const uint8_t* cypher, size_t cypher_size) const {
if (cypher_size <= crypto_box_SEALBYTES)
throw DecryptError("Unexpected cipher length");
Blob ret(cypher_size - crypto_box_SEALBYTES);
if (crypto_box_seal_open(ret.data(), cypher, cypher_size, pk.data(), key.data()) != 0) {
throw DecryptError("Can't decrypt data");
}
return ret;
}
Blob decrypt(const uint8_t* cypher, size_t cypher_size, const EcPublicKey& pub) const {
if (cypher_size <= crypto_box_NONCEBYTES+crypto_box_MACBYTES)
throw DecryptError("Unexpected cipher length");
Blob ret(cypher_size - crypto_box_NONCEBYTES - crypto_box_MACBYTES);
if (crypto_box_open_easy(ret.data(),
cypher+crypto_box_NONCEBYTES,
cypher_size-crypto_box_NONCEBYTES,
cypher,
pub.data(), key.data()) != 0) {
throw DecryptError("Can't decrypt data");
}
return ret;
}
SecureBlob computeSecret(const EcPublicKey& pub, bool sending) const {
// shared key = h(q ‖ client_publickey ‖ server_publickey)
crypto_generichash_state h;
crypto_generichash_init(&h, nullptr, 0U, crypto_generichash_BYTES);
{
std::array<uint8_t, crypto_scalarmult_BYTES> q;
if (auto err = crypto_scalarmult(q.data(), key.data(), pub.data()))
throw CryptoException("Can't compute secret: " + std::to_string(err));
crypto_generichash_update(&h, q.data(), q.size());
}
{
const auto& sender_pk = sending ? pk : pub;
const auto& receiver_pk = sending ? pub : pk;
crypto_generichash_update(&h, sender_pk.data(), sender_pk.size());
crypto_generichash_update(&h, receiver_pk.data(), receiver_pk.size());
}
SecureBlob ret(crypto_generichash_BYTES);
crypto_generichash_final(&h, ret.data(), ret.size());
return ret;
}
private:
KeyData key;
EcPublicKey pk;
};
/**
* Generate an RSA key pair (4096 bits) and a certificate.
* @param name the name used in the generated certificate
@@ -583,6 +754,9 @@ using SecureBlob = secure_vector<uint8_t>;
OPENDHT_PUBLIC Identity generateIdentity(const std::string& name, Identity ca, unsigned key_length, bool is_ca);
OPENDHT_PUBLIC Identity generateIdentity(const std::string& name = "dhtnode", Identity ca = {}, unsigned key_length = 4096);
OPENDHT_PUBLIC Identity generateEcIdentity(const std::string& name, Identity ca, bool is_ca);
OPENDHT_PUBLIC Identity generateEcIdentity(const std::string& name = "dhtnode", Identity ca = {});
/**
* Performs SHA512, SHA256 or SHA1, depending on hash_length.
@@ -601,18 +775,18 @@ OPENDHT_PUBLIC Blob hash(const Blob& data, size_t hash_length = 512/8);
* that can be transmitted in clear, and will be generated if
* not provided (32 bytes).
*/
OPENDHT_PUBLIC Blob stretchKey(const std::string& password, Blob& salt, size_t key_length = 512/8);
OPENDHT_PUBLIC SecureBlob stretchKey(const std::string& password, Blob& salt, size_t key_length = 512/8);
/**
* AES-GCM encryption. Key must be 128, 192 or 256 bits long (16, 24 or 32 bytes).
*/
OPENDHT_PUBLIC Blob aesEncrypt(const Blob& data, const Blob& key);
OPENDHT_PUBLIC Blob aesEncrypt(const Blob& data, const SecureBlob& key);
OPENDHT_PUBLIC Blob aesEncrypt(const Blob& data, const std::string& password);
/**
* AES-GCM decryption.
*/
OPENDHT_PUBLIC Blob aesDecrypt(const Blob& data, const Blob& key);
OPENDHT_PUBLIC Blob aesDecrypt(const Blob& data, const SecureBlob& key);
OPENDHT_PUBLIC Blob aesDecrypt(const Blob& data, const std::string& password);
}
+16 -31
View File
@@ -19,6 +19,7 @@
#pragma once
#include "value.h"
#include "sockaddr.h"
namespace dht {
enum class ImStatus : uint8_t {
@@ -45,7 +46,7 @@ public:
static Value::Filter getFilter() { return {}; }
static bool storePolicy(InfoHash key, std::shared_ptr<Value>& value, InfoHash from, const sockaddr* from_addr, socklen_t from_len);
static bool storePolicy(InfoHash key, std::shared_ptr<Value>& value, const InfoHash& from, const SockAddr&);
static Value::Filter ServiceFilter(std::string s);
@@ -204,15 +205,12 @@ private:
public:
static const ValueType TYPE;
IpServiceAnnouncement(in_port_t p = 0) {
ss.ss_family = 0;
setPort(p);
IpServiceAnnouncement(sa_family_t family = AF_UNSPEC, in_port_t p = 0) {
addr.setFamily(family);
addr.setPort(p);
}
IpServiceAnnouncement(const sockaddr* sa, socklen_t sa_len) {
if (sa)
std::copy_n((const uint8_t*)sa, sa_len, (uint8_t*)&ss);
}
IpServiceAnnouncement(const SockAddr& sa) : addr(sa) {}
IpServiceAnnouncement(const Blob& b) {
msgpack_unpack(unpackMsg(b).get());
@@ -221,53 +219,40 @@ public:
template <typename Packer>
void msgpack_pack(Packer& pk) const
{
pk.pack_array(2);
pk.pack(reinterpret_cast<const sockaddr_in*>(&ss)->sin_port);
if (ss.ss_family == AF_INET) {
pk.pack_bin(sizeof(in_addr));
pk.pack_bin_body((const char*)&reinterpret_cast<const sockaddr_in*>(&ss)->sin_addr, sizeof(in_addr));
} else if (ss.ss_family == AF_INET6) {
pk.pack_bin(sizeof(in6_addr));
pk.pack_bin_body((const char*)&reinterpret_cast<const sockaddr_in6*>(&ss)->sin6_addr, sizeof(in6_addr));
}
pk.pack_bin(addr.getLength());
pk.pack_bin_body((const char*)addr.get(), addr.getLength());
}
virtual void msgpack_unpack(msgpack::object o)
{
if (o.type != msgpack::type::ARRAY) throw msgpack::type_error();
if (o.via.array.size < 2) throw msgpack::type_error();
reinterpret_cast<sockaddr_in*>(&ss)->sin_port = o.via.array.ptr[0].as<in_port_t>();
auto ip_dat = o.via.array.ptr[1].as<Blob>();
if (ip_dat.size() == sizeof(in_addr))
std::copy(ip_dat.begin(), ip_dat.end(), (char*)&reinterpret_cast<sockaddr_in*>(&ss)->sin_addr);
else if (ip_dat.size() == sizeof(in6_addr))
std::copy(ip_dat.begin(), ip_dat.end(), (char*)&reinterpret_cast<sockaddr_in6*>(&ss)->sin6_addr);
if (o.type == msgpack::type::BIN)
addr = {(sockaddr*)o.via.bin.ptr, (socklen_t)o.via.bin.size};
else
throw msgpack::type_error();
}
in_port_t getPort() const {
return ntohs(reinterpret_cast<const sockaddr_in*>(&ss)->sin_port);
return addr.getPort();
}
void setPort(in_port_t p) {
reinterpret_cast<sockaddr_in*>(&ss)->sin_port = htons(p);
addr.setPort(p);
}
sockaddr_storage getPeerAddr() const {
return ss;
const SockAddr& getPeerAddr() const {
return addr;
}
virtual const ValueType& getType() const {
return TYPE;
}
static bool storePolicy(InfoHash, std::shared_ptr<Value>&, InfoHash, const sockaddr*, socklen_t);
static bool storePolicy(InfoHash, std::shared_ptr<Value>&, const InfoHash&, const SockAddr&);
/** print value for debugging */
friend std::ostream& operator<< (std::ostream&, const IpServiceAnnouncement&);
private:
sockaddr_storage ss;
SockAddr addr;
};
+48 -43
View File
@@ -45,12 +45,15 @@ namespace net {
struct Request;
} /* namespace net */
struct Storage;
struct ValueStorage;
class StorageBucket;
struct Listener;
struct LocalListener;
/**
* Main Dht class.
* Provides a Distributed Hash Table node.
*
* Must be given open UDP sockets and ::periodic must be
* called regularly.
*/
class OPENDHT_PUBLIC Dht {
public:
@@ -67,13 +70,13 @@ public:
* Initialise the Dht with two open sockets (for IPv4 and IP6)
* and an ID for the node.
*/
Dht(int s, int s6, Config config);
Dht(uv_loop_t* loop, Config config);
virtual ~Dht();
/**
* Get the ID of the node.
*/
inline const InfoHash& getNodeId() const { return myid; }
inline const InfoHash& getNodeId() const { return network_engine.getNodeId(); }
/**
* Get the current status of the node for the given family.
@@ -83,6 +86,9 @@ public:
NodeStatus getStatus() const {
return std::max(getStatus(AF_INET), getStatus(AF_INET6));
}
void setOnStatusChanged(StatusCallback&& cb) {
statusCb = std::move(cb);
}
/**
* Performs final operations before quitting.
@@ -130,13 +136,13 @@ public:
insertNode(n.id, SockAddr(n.ss, n.sslen));
}
void pingNode(const sockaddr*, socklen_t, DoneCallbackSimple&& cb={});
time_point periodic(const uint8_t *buf, size_t buflen, const SockAddr&);
time_point periodic(const uint8_t *buf, size_t buflen, const sockaddr* from, socklen_t fromlen) {
return periodic(buf, buflen, SockAddr(from, fromlen));
void bootstrap(const std::string& host, const std::string& service);
void clearBootstrap() {
bootstrap_nodes.clear();
}
void pingNode(const SockAddr&, DoneCallback&& cb={});
/**
* Get a value by searching on all available protocols (IPv4, IPv6),
* and call the provided get callback when values are found at key.
@@ -162,8 +168,8 @@ public:
* @param key the key for which to query data for.
* @param cb a function called when new values are found on the network.
* It should return false to stop the operation.
* @param donecb a function called when the operation is complete.
cb and donecb won't be called again afterward.
* @param done_cb a function called when the operation is complete.
cb and done_cb won't be called again afterward.
* @param q a query used to filter values on the remotes before they send a
* response.
*/
@@ -301,6 +307,10 @@ public:
std::vector<SockAddr> getPublicAddress(sa_family_t family = 0);
SockAddr getBoundAddr() {
return network_engine.getBoundAddr();
}
protected:
Logger DHT_LOG;
bool logFilerEnable_ {};
@@ -329,9 +339,6 @@ private:
/* Number of listening nodes */
static constexpr unsigned LISTEN_NODES {4};
/* The maximum number of values we store for a given hash. */
static constexpr unsigned MAX_VALUES {1024};
/* The maximum number of hashes we're willing to track. */
static constexpr unsigned MAX_HASHES {16384};
@@ -354,12 +361,7 @@ private:
struct SearchNode;
struct Get;
struct Announce;
struct LocalListener;
struct Search;
struct ValueStorage;
class StorageBucket;
struct Listener;
struct Storage;
// prevent copy
Dht(const Dht&) = delete;
@@ -367,8 +369,8 @@ private:
InfoHash myid {};
std::array<uint8_t, 8> secret {{}};
std::array<uint8_t, 8> oldsecret {{}};
uint64_t secret {};
uint64_t oldsecret {};
// registred types
std::map<ValueType::Id, ValueType> types;
@@ -380,6 +382,8 @@ private:
const bool is_bootstrap {false};
const bool maintain_storage {false};
std::vector<std::pair<std::string,std::string>> bootstrap_nodes {};
// the stuff
RoutingTable buckets4 {};
RoutingTable buckets6 {};
@@ -402,9 +406,7 @@ private:
// timing
Scheduler scheduler;
Sp<Scheduler::Job> nextNodesConfirmation {};
Sp<Scheduler::Job> nextStorageMaintenance {};
time_point mybucket_grow_time {time_point::min()}, mybucket6_grow_time {time_point::min()};
Sp<Job> nextNodesConfirmation {};
net::NetworkEngine network_engine;
unsigned pending_pings4 {0};
@@ -412,17 +414,23 @@ private:
using ReportedAddr = std::pair<unsigned, SockAddr>;
std::vector<ReportedAddr> reported_addr;
std::mt19937_64 rd {crypto::getSeededRandomEngine<std::mt19937_64>()};
NodeStatus status4 {NodeStatus::Disconnected},
status6 {NodeStatus::Disconnected};
StatusCallback statusCb {nullptr};
void updateStatus();
void rotateSecrets();
Blob makeToken(const sockaddr *sa, bool old) const;
bool tokenMatch(const Blob& token, const sockaddr *sa) const;
Blob makeToken(const SockAddr&, bool old) const;
bool tokenMatch(const Blob& token, const SockAddr&) const;
void reportedAddr(const SockAddr&);
// Storage
void storageAddListener(const InfoHash& id, const Sp<Node>& node, size_t tid, Query&& = {});
bool storageStore(const InfoHash& id, const Sp<Value>& value, time_point created, const SockAddr* sa = nullptr);
bool storageStore(const InfoHash& id, const Sp<Value>& value, time_point created, const SockAddr& sa = {});
void expireStore();
void expireStorage(InfoHash h);
void expireStore(decltype(store)::iterator);
@@ -460,13 +468,12 @@ private:
}
void expireBuckets(RoutingTable&);
int sendCachedPing(Bucket& b);
void sendCachedPing(Bucket& b);
bool bucketMaintenance(RoutingTable&);
void dumpBucket(const Bucket& b, std::ostream& out) const;
// Nodes
void onNewNode(const Sp<Node>& node, int confirm);
Sp<Node> findNode(const InfoHash& id, sa_family_t af);
const Sp<Node> findNode(const InfoHash& id, sa_family_t af) const;
bool trySearchInsert(const Sp<Node>& node);
@@ -506,7 +513,7 @@ private:
* @param query The query sent to the node.
*/
void searchNodeGetDone(const net::Request& status,
net::NetworkEngine::RequestAnswer&& answer,
net::RequestAnswer&& answer,
std::weak_ptr<Search> ws,
Sp<Query> query);
@@ -555,48 +562,46 @@ private:
bool neighbourhoodMaintenance(RoutingTable&);
void processMessage(const uint8_t *buf, size_t buflen, const SockAddr&);
void onError(Sp<net::Request> node, net::DhtProtocolException e);
/* when our address is reported by a distant peer. */
void onReportedAddr(const InfoHash& id, const SockAddr&);
/* when we receive a ping request */
net::NetworkEngine::RequestAnswer onPing(Sp<Node> node);
net::RequestAnswer onPing(Sp<Node> node);
/* when we receive a "find node" request */
net::NetworkEngine::RequestAnswer onFindNode(Sp<Node> node, const InfoHash& hash, want_t want);
net::RequestAnswer onFindNode(Sp<Node> node, const InfoHash& hash, want_t want);
void onFindNodeDone(const Sp<Node>& status,
net::NetworkEngine::RequestAnswer& a,
net::RequestAnswer& a,
Sp<Search> sr);
/* when we receive a "get values" request */
net::NetworkEngine::RequestAnswer onGetValues(Sp<Node> node,
net::RequestAnswer onGetValues(Sp<Node> node,
const InfoHash& hash,
want_t want,
const Query& q);
void onGetValuesDone(const Sp<Node>& status,
net::NetworkEngine::RequestAnswer& a,
net::RequestAnswer& a,
Sp<Search>& sr,
const Sp<Query>& orig_query);
/* when we receive a listen request */
net::NetworkEngine::RequestAnswer onListen(Sp<Node> node,
net::RequestAnswer onListen(Sp<Node> node,
const InfoHash& hash,
const Blob& token,
size_t socket_id,
const Query& query);
void onListenDone(const Sp<Node>& status,
net::NetworkEngine::RequestAnswer& a,
net::RequestAnswer& a,
Sp<Search>& sr);
/* when we receive an announce request */
net::NetworkEngine::RequestAnswer onAnnounce(Sp<Node> node,
net::RequestAnswer onAnnounce(Sp<Node> node,
const InfoHash& hash,
const Blob& token,
const std::vector<Sp<Value>>& v,
const time_point& created);
net::NetworkEngine::RequestAnswer onRefresh(Sp<Node> node,
net::RequestAnswer onRefresh(Sp<Node> node,
const InfoHash& hash,
const Blob& token,
const Value::Id& vid);
void onAnnounceDone(const Sp<Node>& status,
net::NetworkEngine::RequestAnswer& a,
net::RequestAnswer& a,
Sp<Search>& sr);
};
+44 -35
View File
@@ -26,6 +26,7 @@
#include "sockaddr.h"
#include "log_enable.h"
#include "def.h"
#include "uv_utils.h"
#include <thread>
#include <mutex>
@@ -51,8 +52,6 @@ struct SecureDhtConfig;
class OPENDHT_PUBLIC DhtRunner {
public:
typedef std::function<void(NodeStatus, NodeStatus)> StatusCallback;
DhtRunner();
virtual ~DhtRunner();
@@ -202,8 +201,11 @@ public:
* Insert known nodes to the routing table, without necessarly ping them.
* Usefull to restart a node and get things running fast without putting load on the network.
*/
void bootstrap(const std::vector<std::pair<sockaddr_storage, socklen_t>>& nodes, DoneCallbackSimple&& cb={});
void bootstrap(const SockAddr& addr, DoneCallbackSimple&& cb={});
void ping(const std::vector<std::pair<sockaddr_storage, socklen_t>>& nodes, DoneCallback&& cb={});
void ping(const SockAddr& addr, DoneCallback&& cb={});
void ping(const SockAddr& addr, DoneCallbackSimple&& cb={}) {
ping(addr, bindDoneCb(cb));
}
/**
* Insert known nodes to the routing table, without necessarly ping them.
@@ -241,16 +243,14 @@ public:
* Returns the currently bound address.
* @param f: address family of the bound address to retreive.
*/
const SockAddr& getBound(sa_family_t f = AF_INET) const {
return (f == AF_INET) ? bound4 : bound6;
}
SockAddr getBound() const;
/**
* Returns the currently bound port, in host byte order.
* @param f: address family of the bound port to retreive.
*/
in_port_t getBoundPort(sa_family_t f = AF_INET) const {
return ntohs(((sockaddr_in*)&getBound(f).first)->sin_port);
in_port_t getBoundPort() const {
return getBound().getPort();
}
std::pair<size_t, size_t> getStoreSize() const;
@@ -306,20 +306,24 @@ public:
* @param cb: Optional callback to receive general state information.
*/
void run(in_port_t port, const crypto::Identity identity, bool threaded = false, NetId network = 0) {
run(port, {
run({
/*.dht_config = */{
/*.node_config = */{
/*.node_id = */{},
/*.network = */network,
/*.is_bootstrap = */false,
/*.maintain_storage*/false
/*.maintain_storage = */false,
/* .network_config = */{
/*.addr = */ "::0",
/*.port = */ port
}
},
/*.id = */identity
},
/*.threaded = */threaded
});
}
void run(in_port_t port, Config config);
void run(Config config);
/**
* @param local4: Local IPv4 address and port to bind. Can be null.
@@ -329,25 +333,22 @@ public:
* @param threaded: If false, loop() must be called periodically. Otherwise a thread is launched.
* @param cb: Optional callback to receive general state information.
*/
void run(const sockaddr_in* local4, const sockaddr_in6* local6, Config config);
//void run(const sockaddr_in* local4, const sockaddr_in6* local6, Config config);
/**
* Same as @run(sockaddr_in, sockaddr_in6, Identity, bool, StatusCallback), but with string IP addresses and service (port).
*/
void run(const char* ip4, const char* ip6, const char* service, Config config);
//void run(const char* ip4, const char* ip6, const char* service, Config config);
void setOnStatusChanged(StatusCallback&& cb) {
statusCb = std::move(cb);
}
void setOnStatusChanged(StatusCallback&& cb);
/**
* In non-threaded mode, the user should call this method
* regularly and everytime a new packet is received.
* @return the next op
*/
time_point loop() {
std::lock_guard<std::mutex> lck(dht_mtx);
return loop_();
void loop() {
uv_loop_.runOnce();
}
/**
@@ -373,33 +374,41 @@ private:
*/
void tryBootstrapContinuously();
void doRun(const sockaddr_in* sin4, const sockaddr_in6* sin6, SecureDhtConfig config);
time_point loop_();
void doRun(SecureDhtConfig config);
void loop_();
static std::vector<std::pair<sockaddr_storage, socklen_t>> getAddrInfo(const std::string& host, const std::string& service);
NodeStatus getStatus() const {
return std::max(status4, status6);
void loopCallback() {
std::lock_guard<std::mutex> lck(dht_mtx);
if (running)
loop_();
else {
uv_close((uv_handle_t*)&uv_async_, nullptr);
uv_loop_.stop();
}
}
static void loop_callback(uv_async_t* handle) {
if (handle->data)
static_cast<DhtRunner*>(handle->data)->loopCallback();
}
std::unique_ptr<SecureDht> dht_;
EventLoop uv_loop_;
uv_async_t uv_async_;
mutable std::mutex dht_mtx {};
std::thread dht_thread {};
std::condition_variable cv {};
std::thread rcv_thread {};
std::mutex sock_mtx {};
std::vector<std::pair<Blob, SockAddr>> rcv {};
/** true if currently actively boostraping */
std::atomic_bool bootstraping {false};
/* bootstrap nodes given as (host, service) pairs */
std::vector<std::pair<std::string,std::string>> bootstrap_nodes_all {};
/*std::vector<std::pair<std::string,std::string>> bootstrap_nodes_all {};
std::vector<std::pair<std::string,std::string>> bootstrap_nodes {};
std::thread bootstrap_thread {};
std::thread bootstrap_thread {};*/
/** protects bootstrap_nodes, bootstrap_thread */
std::mutex bootstrap_mtx {};
std::condition_variable bootstrap_cv {};
/*std::mutex bootstrap_mtx {};
std::condition_variable bootstrap_cv {};*/
std::queue<std::function<void(SecureDht&)>> pending_ops_prio {};
std::queue<std::function<void(SecureDht&)>> pending_ops {};
@@ -407,12 +416,12 @@ private:
std::atomic<bool> running {false};
NodeStatus status4 {NodeStatus::Disconnected},
/*NodeStatus status4 {NodeStatus::Disconnected},
status6 {NodeStatus::Disconnected};
StatusCallback statusCb {nullptr};
SockAddr bound4 {};
SockAddr bound6 {};
SockAddr bound6 {};*/
};
}
+39 -9
View File
@@ -22,6 +22,8 @@
#include <msgpack.hpp>
#include <sodium.h>
#ifndef _WIN32
#include <netinet/in.h>
#include <netdb.h>
@@ -46,7 +48,7 @@ typedef uint16_t in_port_t;
// bytes
#define HASH_LEN 20u
#define HASH_LEN crypto_box_PUBLICKEYBYTES
namespace dht {
@@ -147,6 +149,12 @@ public:
std::copy_n(h, HASH_LEN, begin());
}
std::vector<uint8_t> encrypt(const uint8_t* msg, size_t msg_size) const {
std::vector<uint8_t> ret(crypto_box_SEALBYTES+msg_size);
crypto_box_seal(ret.data(), msg, msg_size, data());
return ret;
}
/**
* Constructor from an hexadecimal string (without "0x").
* hex must be at least 2.HASH_LEN characters long.
@@ -242,17 +250,24 @@ public:
num ^= (-b ^ num) & (1 << bit);
}
double
toFloat() const
{
double toFloat() const {
using D = size_t;
double v = 0.;
for (unsigned i = 0; i < std::min<size_t>(HASH_LEN, sizeof(unsigned)-1); i++)
v += *(cbegin()+i)/(double)(1<<(8*(i+1)));
for (size_t i = 0; i < std::min<size_t>(HASH_LEN, sizeof(D)-1); i++)
v += *(cbegin()+i) / (double)((D)1 << 8*(i+1));
return v;
}
bool
operator<(const InfoHash& o) const {
bool operator==(const InfoHash& h) const {
auto a = reinterpret_cast<const uint32_t*>(data());
auto b = reinterpret_cast<const uint32_t*>(h.data());
for (size_t i=0; i<(size()/sizeof(uint32_t)); i++)
if (a[i] != b[i])
return false;
return true;
}
bool operator<(const InfoHash& o) const {
for(unsigned i = 0; i < HASH_LEN; i++) {
if((*this)[i] != o[i])
return (*this)[i] < o[i];
@@ -299,10 +314,25 @@ public:
static constexpr const InfoHash zeroes {};
struct NodeExport {
struct OPENDHT_PUBLIC NodeExport {
InfoHash id;
sockaddr_storage ss;
socklen_t sslen;
template <typename Packer>
void msgpack_pack(Packer& pk) const
{
pk.pack_map(2);
pk.pack(std::string("id"));
pk.pack(id);
pk.pack(std::string("addr"));
pk.pack_bin(sslen);
pk.pack_bin_body((char*)&ss, sslen);
}
void msgpack_unpack(msgpack::object o);
OPENDHT_PUBLIC friend std::ostream& operator<< (std::ostream& s, const InfoHash& h);
};
}
+7 -2
View File
@@ -22,10 +22,12 @@
#include "dhtrunner.h"
#include <iostream>
#include <fstream>
#include <chrono>
namespace dht {
/**
* Logging-related functions
*/
namespace log {
/**
@@ -73,5 +75,8 @@ enableFileLogging(dht::DhtRunner &dht, const std::string &path);
OPENDHT_PUBLIC void
disableLogging(dht::DhtRunner &dht);
OPENDHT_PUBLIC void
enableSyslog(dht::DhtRunner &dht, const char* name);
} /* log */
} /* dht */
+8 -23
View File
@@ -42,41 +42,26 @@ struct TransPrefix : public std::array<uint8_t, 2> {
struct TransId final : public std::array<uint8_t, 4> {
static const constexpr uint16_t INVALID {0};
TransId() { std::fill_n(begin(), 4, 0); }
TransId() { std::fill(begin(), end(), 0); }
TransId(const std::array<char, 4>& o) { std::copy(o.begin(), o.end(), begin()); }
TransId(const TransPrefix prefix, uint16_t seqno = 0) {
std::copy_n(prefix.begin(), prefix.size(), begin());
*reinterpret_cast<uint16_t*>(data()+prefix.size()) = seqno;
}
TransId(const char* q, size_t l) : array<uint8_t, 4>() {
if (l > 4) {
length = 0;
} else {
std::copy_n(q, l, begin());
length = l;
}
}
uint16_t getTid() const {
return *reinterpret_cast<const uint16_t*>(&(*this)[2]);
TransId(uint32_t id) {
*reinterpret_cast<uint32_t*>(data()) = htonl(id);
}
uint32_t toInt() const {
return *reinterpret_cast<const uint32_t*>(&(*this)[0]);
return ntohl(*reinterpret_cast<const uint32_t*>(&(*this)[0]));
}
bool matches(const TransPrefix prefix, uint16_t* tid = nullptr) const {
if (std::equal(begin(), begin()+2, prefix.begin())) {
if (tid)
*tid = getTid();
return true;
} else
return false;
bool matches(const TransPrefix prefix) const {
return std::equal(begin(), begin()+2, prefix.begin());
}
unsigned length {4};
};
TransId unpackTid(msgpack::object& o);
} /* namespace net */
} /* dht */
+80 -125
View File
@@ -28,13 +28,14 @@
#include "rng.h"
#include "rate_limiter.h"
#include "net.h"
#include "callbacks.h"
#include "uv_utils.h"
#include <vector>
#include <string>
#include <functional>
#include <algorithm>
#include <memory>
#include <random>
#include <queue>
namespace dht {
@@ -81,6 +82,20 @@ private:
struct ParsedMessage;
/**
* Answer for a request.
*/
struct RequestAnswer {
Blob ntoken {};
Value::Id vid {};
std::vector<Sp<Value>> values {};
std::vector<Sp<FieldValueIndex>> fields {};
std::vector<Sp<Node>> nodes4 {};
std::vector<Sp<Node>> nodes6 {};
RequestAnswer() {}
RequestAnswer(ParsedMessage&& msg);
};
/*!
* @class NetworkEngine
* @brief An abstraction of communication protocol on the network.
@@ -101,26 +116,6 @@ struct ParsedMessage;
*/
class NetworkEngine final
{
public:
/*!
* @class RequestAnswer
* @brief Answer for a request.
* @details
* Answer for a request to be (de)serialized. Used for reponding to a node
* and looking up the response from a node.
*/
struct RequestAnswer {
Blob ntoken {};
Value::Id vid {};
std::vector<Sp<Value>> values {};
std::vector<Sp<FieldValueIndex>> fields {};
std::vector<Sp<Node>> nodes4 {};
std::vector<Sp<Node>> nodes6 {};
RequestAnswer() {}
RequestAnswer(ParsedMessage&& msg);
};
private:
/**
* Called when we receive an error message.
@@ -137,7 +132,7 @@ private:
/**
* Called when an addres is reported from a requested node.
*
* @param h: id
* @param h: id
* @param saddr_len (type: socklen_t) lenght of the sockaddr struct.
*/
std::function<void(const InfoHash&, const SockAddr&)> onReportedAddr;
@@ -206,12 +201,11 @@ private:
const Value::Id&)> onRefresh {};
public:
using SocketCb = std::function<void(const Sp<Node>&, RequestAnswer&&)>;
using RequestCb = std::function<void(const Request&, RequestAnswer&&)>;
using RequestExpiredCb = std::function<void(const Request&, bool)>;
NetworkEngine(Logger& log, Scheduler& scheduler);
NetworkEngine(InfoHash& myid, NetId net, int s, int s6, Logger& log, Scheduler& scheduler,
NetworkEngine(uv_loop_t*, Logger& log, Scheduler& scheduler);
NetworkEngine(uv_loop_t*, const NetworkConfig& config, NetId net, Logger& log, Scheduler& scheduler,
decltype(NetworkEngine::onError) onError,
decltype(NetworkEngine::onNewNode) onNewNode,
decltype(NetworkEngine::onReportedAddr) onReportedAddr,
@@ -224,7 +218,16 @@ public:
virtual ~NetworkEngine();
const InfoHash& getNodeId() const {
return id_key.getPublicKey();
}
void clear();
void close(OnClose cb) {
sock->close(cb);
tcp_sock->close();
cache.closeAll();
}
/**
* Sends values (with closest nodes) to a listenner.
@@ -244,13 +247,7 @@ public:
std::vector<Sp<Value>>&& values, const Query& q);
bool isRunning(sa_family_t af) const;
inline want_t want () const { return dht_socket >= 0 && dht_socket6 >= 0 ? (WANT4 | WANT6) : -1; }
/**
* Cancel a request. Setting req->cancelled = true is not enough in the case
* a request is "persistent".
*/
void cancelRequest(Sp<Request>& req);
inline want_t want () const { /*return dht_socket >= 0 && dht_socket6 >= 0 ? (WANT4 | WANT6) : -1;*/return WANT4 | WANT6; }
void connectivityChanged(sa_family_t);
@@ -268,7 +265,7 @@ public:
* @return the request with information concerning its success.
*/
Sp<Request>
sendPing(Sp<Node> n, RequestCb&& on_done, RequestExpiredCb&& on_expired);
sendPing(const Sp<Node>& n, RequestCb&& on_done, RequestExpiredCb&& on_expired);
/**
* Send a "ping" request to a given node.
*
@@ -280,8 +277,8 @@ public:
* @return the request with information concerning its success.
*/
Sp<Request>
sendPing(const sockaddr* sa, socklen_t salen, RequestCb&& on_done, RequestExpiredCb&& on_expired) {
return sendPing(std::make_shared<Node>(zeroes, sa, salen),
sendPing(const SockAddr& sa, RequestCb&& on_done, RequestExpiredCb&& on_expired) {
return sendPing(std::make_shared<Node>(zeroes, sa),
std::forward<RequestCb>(on_done),
std::forward<RequestExpiredCb>(on_expired));
}
@@ -297,7 +294,7 @@ public:
*
* @return the request with information concerning its success.
*/
Sp<Request> sendFindNode(Sp<Node> n,
Sp<Request> sendFindNode(const Sp<Node>& n,
const InfoHash& hash,
want_t want,
RequestCb&& on_done,
@@ -316,7 +313,7 @@ public:
*
* @return the request with information concerning its success.
*/
Sp<Request> sendGetValues(Sp<Node> n,
Sp<Request> sendGetValues(const Sp<Node>& n,
const InfoHash& hash,
const Query& query,
want_t want,
@@ -345,7 +342,7 @@ public:
*
* @return the request with information concerning its success.
*/
Sp<Request> sendListen(Sp<Node> n,
Sp<Request> sendListen(const Sp<Node>& n,
const InfoHash& hash,
const Query& query,
const Blob& token,
@@ -366,7 +363,7 @@ public:
*
* @return the request with information concerning its success.
*/
Sp<Request> sendAnnounceValue(Sp<Node> n,
Sp<Request> sendAnnounceValue(const Sp<Node>& n,
const InfoHash& hash,
const Sp<Value>& v,
time_point created,
@@ -386,43 +383,15 @@ public:
*
* @return the request with information concerning its success.
*/
Sp<Request> sendRefreshValue(Sp<Node> n,
Sp<Request> sendRefreshValue(const Sp<Node>& n,
const InfoHash& hash,
const Value::Id& vid,
const Blob& token,
RequestCb&& on_done,
RequestExpiredCb&& on_expired);
/**
* Opens a socket on which a node will be able allowed to write for further
* additionnal updates following the response to a previous request.
*
* @param node The node which will be allowed to write on this socket.
* @param cb The callback to execute once updates arrive on the socket.
*
* @return the socket.
*/
Sp<Socket> openSocket(const Sp<Node>& node, TransPrefix tp, SocketCb&& cb);
/**
* Closes a socket so that no further data will be red on that socket.
*
* @param socket The socket to close.
*/
void closeSocket(Sp<Socket> socket);
/**
* Parses a message and calls appropriate callbacks.
*
* @param buf The buffer containing the binary message.
* @param buflen The length of the buffer.
* @param from The address info of the sender.
* @param fromlen The length of the corresponding sockaddr structure.
* @param now The time to adjust the clock in the network engine.
*/
void processMessage(const uint8_t *buf, size_t buflen, const SockAddr& addr);
Sp<Node> insertNode(const InfoHash& myid, const SockAddr& addr) {
auto n = cache.getNode(myid, addr, scheduler.time(), 0);
auto n = cache.getNode(myid, addr, {}, scheduler.time(), 0);
onNewNode(n, 0);
return n;
}
@@ -440,6 +409,10 @@ public:
return cache.getCachedNodes(id, sa_f, count);
}
SockAddr getBoundAddr() {
return sock->getBoundAddr();
}
private:
struct PartialMessage;
@@ -447,7 +420,7 @@ private:
/***************
* Constants *
***************/
static constexpr long unsigned MAX_REQUESTS_PER_SEC {1600};
static constexpr size_t MAX_REQUESTS_PER_SEC {1600};
/* the length of a node info buffer in ipv4 format */
static const constexpr size_t NODE4_INFO_BUF_LEN {HASH_LEN + sizeof(in_addr) + sizeof(in_port_t)};
/* the length of a node info buffer in ipv6 format */
@@ -464,12 +437,24 @@ private:
static constexpr unsigned BLACKLISTED_MAX {10};
static constexpr size_t MTU {1280};
static constexpr size_t MAX_PACKET_VALUE_SIZE {8 * 1024};
static constexpr size_t MAX_PACKET_VALUE_SIZE {600};
static const std::string my_v;
static std::mt19937 rd_device;
void process(std::unique_ptr<ParsedMessage>&&, const SockAddr& from);
/**
* Parses a message and calls appropriate callbacks.
*
* @param buf The buffer containing the binary message.
* @param buflen The length of the buffer.
* @param from The address info of the sender.
* @param fromlen The length of the corresponding sockaddr structure.
* @param now The time to adjust the clock in the network engine.
*/
void processMessage(const uint8_t* data, size_t size, const SockAddr& addr);
Sp<Node> processEncrypted(const msgpack::object& o, const SockAddr& from);
Sp<Node> process(const msgpack::object& o, const SockAddr& addr);
Sp<Node> process(std::unique_ptr<ParsedMessage>&&, const SockAddr& from, const Sp<TcpSocket>& s = {});
bool rateLimit(const SockAddr& addr);
@@ -482,17 +467,7 @@ private:
* Sends a request to a node. Request::MAX_ATTEMPT_COUNT attempts will
* be made before the request expires.
*/
void sendRequest(Sp<Request>& request);
/**
* Generates a new request id, skipping the invalid id.
*
* @return the new id.
*/
uint16_t getNewTid() {
++transaction_id;
return transaction_id == TransId::INVALID ? ++transaction_id : transaction_id;
}
void sendRequest(const Sp<Request>& request);
struct MessageStats {
unsigned ping {0};
@@ -504,20 +479,24 @@ private:
};
// basic wrapper for socket sendto function
void send(const char *buf, size_t len, int flags, const SockAddr& addr);
int send(msgpack::sbuffer& msg, const Node& node, const Sp<TcpSocket>& sock = {});
int sendUDP(msgpack::sbuffer& msg, const SockAddr& addr);
int send(const Blob& msg, int flags, const Node& node, UdpSocket::OnSent&& cb);
void startTcp(const Sp<TcpSocket>& sock, const Sp<Node>& node = {});
void sendValueParts(TransId tid, const std::vector<Blob>& svals, const SockAddr& addr);
std::vector<Blob> packValueHeader(msgpack::sbuffer&, const std::vector<Sp<Value>>&);
std::vector<Blob> packValueHeader(msgpack::sbuffer&, const std::vector<Sp<Value>>&, bool stream);
void maintainRxBuffer(const TransId& tid);
/*************
* Answers *
*************/
/* answer to a ping request */
void sendPong(const SockAddr& addr, TransId tid);
void sendPong(const Sp<Node>& node, TransId tid);
/* answer to findnodes/getvalues request */
void sendNodesValues(const SockAddr& addr,
void sendNodesValues(const Sp<Node>& node,
TransId tid,
const Blob& nodes,
const Blob& nodes6,
@@ -532,66 +511,42 @@ private:
std::vector<Sp<Node>>& nodes,
std::vector<Sp<Node>>& nodes6);
/* answer to a listen request */
void sendListenConfirmation(const SockAddr& addr, TransId tid);
void sendListenConfirmation(const Sp<Node>& node, TransId tid);
/* answer to put request */
void sendValueAnnounced(const SockAddr& addr, TransId, Value::Id);
void sendValueAnnounced(const Sp<Node>& node, TransId, Value::Id);
/* answer in case of error */
void sendError(const SockAddr& addr,
void sendError(const Sp<Node>& node,
TransId tid,
uint16_t code,
const std::string& message,
bool include_id=false);
bool include_id=false,
const Sp<TcpSocket>& sock= {});
void deserializeNodes(ParsedMessage& msg, const SockAddr& from);
/* DHT info */
const InfoHash& myid;
//const InfoHash& myid;
const NetId network {0};
const int dht_socket {-1};
const int dht_socket6 {-1};
const Logger& DHT_LOG;
Sp<UdpSocket> sock;
Sp<TcpSocket> tcp_sock;
std::set<Sp<TcpSocket>> pending_connect;
NodeCache cache {};
/**
* A comparator to classify IP addresses, only considering the
* first 64 bits in IPv6.
*/
struct cmpSockAddr {
bool operator()(const SockAddr& a, const SockAddr& b) const {
if (a.second != b.second)
return a.second < b.second;
socklen_t start, len;
switch(a.getFamily()) {
case AF_INET:
start = offsetof(sockaddr_in, sin_addr);
len = sizeof(in_addr);
break;
case AF_INET6:
start = offsetof(sockaddr_in6, sin6_addr);
// don't consider more than 64 bits (IPv6)
len = 8;
break;
default:
start = 0;
len = a.second;
break;
}
crypto::EcSecretKey id_key;
//crypto::EcSecretKey id_key;
return std::memcmp((uint8_t*)&a.first+start, (uint8_t*)&b.first+start, len) < 0;
}
};
// global limiting should be triggered by at least 8 different IPs
using IpLimiter = RateLimiter<MAX_REQUESTS_PER_SEC/8>;
using IpLimiterMap = std::map<SockAddr, IpLimiter, SockAddr::ipCmp>;
IpLimiterMap address_rate_limiter {};
RateLimiter<MAX_REQUESTS_PER_SEC> rate_limiter {};
size_t limiter_maintenance {0};
// requests handling
uint16_t transaction_id {1};
std::map<TransId, Sp<Request>> requests {};
std::map<TransId, PartialMessage> partial_messages;
std::map<TransId, Sp<Socket>> opened_sockets {};
MessageStats in_stats {}, out_stats {};
std::set<SockAddr> blacklist {};
+104 -19
View File
@@ -22,36 +22,70 @@
#include "infohash.h" // includes socket structures
#include "utils.h"
#include "sockaddr.h"
#include "net.h"
#include "uv_utils.h"
#include "crypto.h"
#include <list>
#include <map>
namespace dht {
struct Node;
namespace net {
struct Request;
struct Socket;
struct RequestAnswer;
} /* namespace net */
using SocketCb = std::function<void(const Sp<Node>&, net::RequestAnswer&&)>;
using SocketId = uint32_t;
struct Socket {
Socket() {}
Socket(SocketCb&& on_receive) :
on_receive(std::move(on_receive)) {}
SocketCb on_receive {};
};
struct Node {
InfoHash id;
SockAddr addr;
const InfoHash id;
time_point time {time_point::min()}; /* last time eared about */
time_point reply_time {time_point::min()}; /* time of last correct reply received */
Sp<TcpSocket> sock;
InfoHash last_known_pk;
//std::list<crypto::EcSecretKey>
Node(const InfoHash& id, const SockAddr& addr, const Sp<TcpSocket>& s = {}, bool client=false);
Node(const InfoHash& id, const sockaddr* sa, socklen_t salen)
: id(id), addr(sa, salen) {}
Node(const InfoHash& id, const SockAddr& addr) : id(id), addr(addr) {}
: Node(id, SockAddr(sa, salen)) {}
~Node() {
if (sock)
sock->close();
}
InfoHash getId() const {
return id;
}
std::pair<const sockaddr*, socklen_t> getAddr() const {
return {(const sockaddr*)&addr.first, addr.second};
}
const SockAddr& getAddr() const { return addr; }
std::string getAddrStr() const {
return addr.toString();
}
bool isClient() const { return is_client; }
bool isIncoming() { return time > reply_time; }
const time_point& getTime() const { return time; }
const time_point& getReplyTime() const { return reply_time; }
void setTime(const time_point& t) { time = t; }
SockAddr getAddress() const {
return canStream() ? sock->getPeerAddr() : addr;
}
void startTCP() {
}
/**
* Makes notice about an additionnal authentication error with this node. Up
* to MAX_AUTH_ERRORS errors are accepted in order to let the node recover.
@@ -68,21 +102,70 @@ struct Node {
bool isPendingMessage() const;
size_t getPendingMessageCount() const;
NodeExport exportNode() const { return NodeExport {id, addr.first, addr.second}; }
bool isOld(const time_point& now) const {
return time + NODE_EXPIRE_TIME < now;
}
bool isRemovable(const time_point& now) const {
return isExpired() and isOld(now);
}
NodeExport exportNode() const {
NodeExport ne;
ne.id = id;
ne.sslen = addr.getLength();
std::memcpy(&ne.ss, addr.get(), ne.sslen);
return ne;
}
sa_family_t getFamily() const { return addr.getFamily(); }
void update(const SockAddr&);
void update(const SockAddr&, const Sp<TcpSocket>&);
void requested(std::shared_ptr<net::Request>& req);
void received(time_point now, std::shared_ptr<net::Request> req);
bool canStream() const {
return sock and not sock->isClosed() and sock->canWrite();
}
void requested(const Sp<net::Request>& req);
void received(time_point now, const Sp<net::Request>& req);
Sp<net::Request> getRequest(const net::TransId& tid);
void cancelRequest(const Sp<net::Request>& req);
void setExpired();
/**
* Opens a socket on which a node will be able allowed to write for further
* additionnal updates following the response to a previous request.
*
* @param node The node which will be allowed to write on this socket.
* @param cb The callback to execute once updates arrive on the socket.
*
* @return the socket.
*/
SocketId openSocket(SocketCb&& cb);
Socket* getSocket(SocketId id);
/**
* Closes a socket so that no further data will be red on that socket.
*
* @param socket The socket to close.
*/
void closeSocket(SocketId id);
/**
* Resets the state of the node so it's not expired anymore.
*/
void reset() { expired_ = false; reply_time = time_point::min(); }
/**
* Generates a new request id, skipping the invalid id.
*
* @return the new id.
*/
uint16_t getNewTid() {
++transaction_id;
return transaction_id == net::TransId::INVALID ? ++transaction_id : transaction_id;
}
std::string toString() const;
OPENDHT_PUBLIC friend std::ostream& operator<< (std::ostream& s, const Node& h);
@@ -99,15 +182,17 @@ private:
/* Number of times we accept authentication errors from this node. */
static const constexpr unsigned MAX_AUTH_ERRORS {3};
std::list<std::weak_ptr<net::Request>> requests_ {};
SockAddr addr;
bool is_client {false};
time_point time {time_point::min()}; /* last time eared about */
time_point reply_time {time_point::min()}; /* time of last correct reply received */
unsigned auth_errors {0};
bool expired_ {false};
SocketId transaction_id;
using TransactionDist = std::uniform_int_distribution<decltype(transaction_id)>;
void clearPendingQueue() {
requests_.remove_if([](std::weak_ptr<net::Request>& w) {
return w.expired();
});
}
std::map<net::TransId, Sp<net::Request>> requests_ {};
std::map<SocketId, Socket> sockets_;
};
}
+9 -5
View File
@@ -26,9 +26,9 @@
namespace dht {
struct NodeCache {
std::shared_ptr<Node> getNode(const InfoHash& id, sa_family_t family);
std::shared_ptr<Node> getNode(const InfoHash& id, const SockAddr&, time_point now, bool confirmed);
std::vector<std::shared_ptr<Node>> getCachedNodes(const InfoHash& id, sa_family_t sa_f, size_t count);
Sp<Node> getNode(const InfoHash& id, sa_family_t family);
Sp<Node> getNode(const InfoHash& id, const SockAddr&, const Sp<TcpSocket>&, time_point now, bool confirmed, bool client=false);
std::vector<Sp<Node>> getCachedNodes(const InfoHash& id, sa_family_t sa_f, size_t count) const;
/**
* Reset the connectivity state of every node,
@@ -36,15 +36,19 @@ struct NodeCache {
* To use in case of connectivity change etc.
*/
void clearBadNodes(sa_family_t family = 0);
void closeAll(sa_family_t family = 0);
private:
class NodeMap : public std::map<InfoHash, std::weak_ptr<Node>> {
public:
std::shared_ptr<Node> getNode(const InfoHash& id);
std::shared_ptr<Node> getNode(const InfoHash& id, const SockAddr&, time_point now, bool confirmed);
Sp<Node> getNode(const InfoHash& id);
Sp<Node> getNode(const InfoHash& id, const SockAddr&, const Sp<TcpSocket>&, time_point now, bool confirmed, bool client);
void clearBadNodes();
void closeAll();
};
const NodeMap& cache(sa_family_t af) const { return af == AF_INET ? cache_4 : cache_6; }
NodeMap& cache(sa_family_t af) { return af == AF_INET ? cache_4 : cache_6; }
NodeMap cache_4;
NodeMap cache_6;
};
+4 -4
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2016 Savoir-faire Linux Inc.
* Copyright (C) 2017 Savoir-faire Linux Inc.
* Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
@@ -26,10 +26,10 @@ namespace dht {
template<size_t Quota, unsigned long Perdiod=1>
class RateLimiter {
public:
/** Clear outdated records and return current quota usage */
/** Clear outdated records and return current quota usage */
size_t maintain(const time_point& now) {
using namespace std::chrono;
while (not records.empty() and duration_cast<seconds>(now - records.front()) >= seconds(Perdiod))
auto limit = now - std::chrono::seconds(Perdiod);
while (not records.empty() and records.front() < limit)
records.pop();
return records.size();
}
+24
View File
@@ -24,6 +24,9 @@
namespace dht {
static constexpr unsigned TARGET_NODES {8};
namespace net {
class NetworkEngine;
}
struct Bucket {
Bucket() : cached() {}
@@ -37,12 +40,25 @@ struct Bucket {
/** Return a random node in a bucket. */
Sp<Node> randomNode();
void sendCachedPing(net::NetworkEngine& ne);
/** Return false if there is a single non-expired node in this bucket. */
bool expired() const {
for (const auto& n : nodes)
if (not n->isExpired())
return false;
return true;
}
};
class RoutingTable : public std::list<Bucket> {
public:
using std::list<Bucket>::list;
time_point grow_time {time_point::min()};
bool is_client {false};
InfoHash middle(const RoutingTable::const_iterator&) const;
std::vector<Sp<Node>> findClosestNodes(const InfoHash id, time_point now, size_t count = TARGET_NODES) const;
@@ -65,6 +81,14 @@ public:
return empty() || (size() == 1 && front().nodes.empty());
}
void connectivityChanged(const time_point& now) {
grow_time = now;
for (auto& b : *this)
b.time = time_point::min();
}
bool onNewNode(const Sp<Node>& node, int comfirm, const time_point& now, const InfoHash& myid, net::NetworkEngine& ne);
/**
* Return a random id in the bucket's range.
*/
+86 -41
View File
@@ -22,6 +22,9 @@
#include "utils.h"
#include "log_enable.h"
#include "uv_utils.h"
#include <uv.h>
#include <functional>
#include <map>
@@ -36,12 +39,7 @@ namespace dht {
*/
class Scheduler {
public:
Scheduler(const Logger& l) : DHT_LOG(l) {}
struct Job {
Job(std::function<void()>&& f) : do_(std::move(f)) {}
std::function<void()> do_;
};
Scheduler(uv_loop_t* loop, const Logger& l) : loop_(loop), DHT_LOG(l) {}
/**
* Adds another job to the queue.
@@ -51,10 +49,32 @@ public:
*
* @return pointer to the newly scheduled job.
*/
Sp<Scheduler::Job> add(time_point t, std::function<void()>&& job_func) {
auto job = std::make_shared<Job>(std::move(job_func));
if (t != time_point::max())
timers.emplace(std::move(t), job);
Sp<Job> add(const time_point& t, std::function<void()>&& job_func) {
if (not loop_)
return {};
auto job = Job::make(loop_, std::move(job_func));
if (t != time_point::max()) {
garbageCollection();
pending_jobs.emplace(job);
job->run(t);
} else
job->cancel();
return job;
}
Sp<Job> add(const duration& t, std::function<void()>&& job_func) {
if (not loop_)
return {};
auto job = Job::make(loop_, std::move(job_func));
garbageCollection();
pending_jobs.emplace(job);
job->run(t);
return job;
}
Sp<Job> run(std::function<void()>&& job_func) {
auto job = Job::make(loop_, std::move(job_func));
garbageCollection();
pending_jobs.emplace(job);
job->run();
return job;
}
@@ -66,7 +86,9 @@ public:
*
* @return pointer to the newly scheduled job.
*/
void edit(Sp<Scheduler::Job>& job, time_point t) {
void edit(Sp<Job>& job, const duration& t = {}) {
if (not loop_)
return;
if (not job) {
DHT_LOG.ERR("editing an empty job");
return;
@@ -75,50 +97,73 @@ public:
// Force clearing old value.
auto task = std::move(job->do_);
job->do_ = {};
job->cancel();
job = add(t, std::move(task));
}
void edit(Sp<Job>& job, const time_point& t) {
if (not loop_)
return;
if (not job) {
DHT_LOG.ERR("editing an empty job");
return;
}
auto task = std::move(job->do_);
job->do_ = {};
job->cancel();
job = add(t, std::move(task));
}
/**
* Runs the jobs to do up to now.
*
* @return The time for the next job to run.
*/
time_point run() {
syncTime();
while (not timers.empty()) {
auto timer = timers.begin();
/*
* Running jobs scheduled before "now" prevents run+rescheduling
* loops before this method ends. It is garanteed by the fact that a
* job will at least be scheduled for "now" and not before.
*/
if (timer->first > now)
break;
auto job = std::move(timer->second);
timers.erase(timer);
if (job->do_)
job->do_();
}
return getNextJobTime();
void stop() {
loop_ = nullptr;
size_t del {0};
for (auto& w : pending_jobs)
if (auto job = w.lock()) {
job->cancel();
del++;
}
pending_jobs.clear();
DHT_LOG.DEBUG("stopping scheduler, %lu events canceled", del);
}
inline time_point getNextJobTime() const {
return timers.empty() ? time_point::max() : timers.begin()->first;
~Scheduler(){
stop();
}
/**
* Accessors for the common time reference used for synchronizing
* operations.
*/
inline const time_point& time() const { return now; }
inline time_point syncTime() { return (now = clock::now()); }
inline time_point time() const {
return time_point(std::chrono::duration_cast<duration>(std::chrono::milliseconds(uv_now(loop_))));
}
inline time_point syncTime() {
return time();
}
uv_loop_t* getLoop() const {
return loop_;
}
private:
time_point now {clock::now()};
std::multimap<time_point, Sp<Job>> timers {}; /* the jobs ordered by time */
std::set<std::weak_ptr<Job>, std::owner_less<std::weak_ptr<Job>>> pending_jobs {};
size_t lastSize_ {0};
uv_loop_t* loop_;
const Logger& DHT_LOG;
void garbageCollection() {
if (pending_jobs.size() > lastSize_ * 2) {
unsigned del {0};
for (auto it = pending_jobs.begin(); it != pending_jobs.end();) {
if (it->expired()) {
it = pending_jobs.erase(it);
del++;
} else
++it;
}
//DHT_LOG.DEBUG("pending_jobs cleanup: %u removed, %zu remaining", del, pending_jobs.size());
lastSize_ = pending_jobs.size();
}
}
};
}
+3 -3
View File
@@ -44,7 +44,7 @@ public:
* id: the identity to use for the crypto layer and to compute
* our own hash on the Dht.
*/
SecureDht(int s, int s6, Config config);
SecureDht(uv_loop_t* loop, Config config);
virtual ~SecureDht();
@@ -158,7 +158,7 @@ private:
const ValueType CERTIFICATE_TYPE = {
8, "Certificate", std::chrono::hours(24 * 7),
// A certificate can only be stored at its public key ID.
[](InfoHash id, Sp<Value>& v, InfoHash, const sockaddr*, socklen_t) {
[](InfoHash id, Sp<Value>& v, const InfoHash&, const SockAddr&) {
try {
crypto::Certificate crt(v->data);
// TODO check certificate signature
@@ -166,7 +166,7 @@ const ValueType CERTIFICATE_TYPE = {
} catch (const std::exception& e) {}
return false;
},
[](InfoHash, const Sp<Value>& o, Sp<Value>& n, InfoHash, const sockaddr*, socklen_t) {
[](InfoHash, const Sp<Value>& o, Sp<Value>& n, const InfoHash&, const SockAddr&) {
try {
return crypto::Certificate(o->data).getPublicKey().getId() == crypto::Certificate(n->data).getPublicKey().getId();
} catch (const std::exception& e) {}
+142 -38
View File
@@ -34,56 +34,116 @@ typedef uint16_t sa_family_t;
typedef uint16_t in_port_t;
#endif
#include <string>
#include <memory>
#include <cstring>
#include <cstddef>
namespace dht {
OPENDHT_PUBLIC std::string print_addr(const sockaddr* sa, socklen_t slen);
OPENDHT_PUBLIC std::string print_addr(const sockaddr_storage& ss, socklen_t sslen);
struct OPENDHT_PUBLIC SockAddr : public std::pair<sockaddr_storage, socklen_t> {
/**
* A Socket Address (sockaddr*), with abstraction for IPv4, IPv6 address families.
*/
class OPENDHT_PUBLIC SockAddr {
public:
SockAddr() : pair<sockaddr_storage, socklen_t>::pair({},0) {}
SockAddr(const SockAddr& o) : pair<sockaddr_storage, socklen_t>::pair({},o.second) {
std::copy_n((uint8_t*)&o.first, o.second, (uint8_t*)&first);
SockAddr() {}
SockAddr(const SockAddr& o) {
set(o.get(), o.getLength());
}
SockAddr(const sockaddr* sa, socklen_t len) : pair<sockaddr_storage, socklen_t>::pair({},len) {
if (len > sizeof(sockaddr_storage))
SockAddr(SockAddr&& o) : len(o.len), addr(std::move(o.addr)) {
o.len = 0;
}
/**
* Build from existing address.
*/
SockAddr(const sockaddr* sa, socklen_t length) {
if (length > sizeof(sockaddr_storage))
throw std::runtime_error("Socket address length is too large");
std::copy_n((uint8_t*)sa, len, (uint8_t*)&first);
set(sa, length);
}
SockAddr(const sockaddr* sa) {
socklen_t len = 0;
if (sa) {
if (sa->sa_family == AF_INET)
len = sizeof(sockaddr_in);
else if(sa->sa_family == AF_INET6)
len = sizeof(sockaddr_in6);
else
throw std::runtime_error("Unknown address family");
}
set(sa, len);
}
/**
* Build from an existing sockaddr_storage structure.
*/
SockAddr(const sockaddr_storage& ss, socklen_t len) : SockAddr((const sockaddr*)&ss, len) {}
bool operator<(const SockAddr& o) const {
if (second != o.second)
return second < o.second;
return std::memcmp((uint8_t*)&first, (uint8_t*)&o.first, second) < 0;
if (len != o.len)
return len < o.len;
return std::memcmp((const uint8_t*)get(), (const uint8_t*)o.get(), len) < 0;
}
bool equals(const SockAddr& o) const {
return second == o.second
&& std::memcmp((uint8_t*)&first, (uint8_t*)&o.first, second) == 0;
return len == o.len
&& std::memcmp((const uint8_t*)get(), (const uint8_t*)o.get(), len) == 0;
}
SockAddr& operator=(const SockAddr& o) {
std::copy_n((const uint8_t*)&o.first, o.second, (uint8_t*)&first);
second = o.second;
set(o.get(), o.getLength());
return *this;
}
SockAddr& operator=(SockAddr&& o) {
len = o.len;
o.len = 0;
addr = std::move(o.addr);
return *this;
}
std::string toString() const {
return print_addr(first, second);
}
sa_family_t getFamily() const { return second > sizeof(sa_family_t) ? first.ss_family : AF_UNSPEC; }
void setFamily(sa_family_t af) {
first.ss_family = af;
switch(af) {
case AF_INET:
second = sizeof(sockaddr_in);
break;
case AF_INET6:
second = sizeof(sockaddr_in6);
break;
}
return print_addr(get(), getLength());
}
/**
* Returns the address family or AF_UNSPEC if the address is not set.
*/
sa_family_t getFamily() const { return len > sizeof(sa_family_t) ? addr->sa_family : AF_UNSPEC; }
/**
* Resize the managed structure to the appropriate size (if needed),
* in which case the sockaddr structure is cleared to zero,
* and set the address family field (sa_family).
*/
void setFamily(sa_family_t af) {
socklen_t new_length;
switch(af) {
case AF_INET:
new_length = sizeof(sockaddr_in);
break;
case AF_INET6:
new_length = sizeof(sockaddr_in6);
break;
default:
new_length = 0;
}
if (new_length != len) {
len = new_length;
if (len) addr.reset((sockaddr*)std::calloc(len, 1));
else addr.reset();
}
if (len > sizeof(sa_family_t))
addr->sa_family = af;
}
/**
* Retreive the port (in host byte order) or 0 if the address is not
* of a supported family.
*/
in_port_t getPort() const {
switch(getFamily()) {
case AF_INET:
@@ -94,6 +154,10 @@ public:
return 0;
}
}
/**
* Set the port. The address must be of a supported family.
* @param p The port in host byte order.
*/
void setPort(in_port_t p) {
switch(getFamily()) {
case AF_INET:
@@ -105,17 +169,42 @@ public:
}
}
/**
* Returns the accessible byte length at the pointer returned by #get().
* If zero, #get() returns null.
*/
socklen_t getLength() const { return len; }
/**
* An address is defined to be true if its length is not zero.
*/
explicit operator bool() const noexcept {
return len;
}
/**
* Returns the address to the managed sockaddr structure.
* The accessible length is returned by #getLength().
*/
const sockaddr* get() const { return addr.get(); }
/**
* Returns the address to the managed sockaddr structure.
* The accessible length is returned by #getLength().
*/
sockaddr* get() { return addr.get(); }
const sockaddr_in& getIPv4() const {
return *reinterpret_cast<const sockaddr_in*>(&first);
return *reinterpret_cast<const sockaddr_in*>(get());
}
const sockaddr_in6& getIPv6() const {
return *reinterpret_cast<const sockaddr_in6*>(&first);
return *reinterpret_cast<const sockaddr_in6*>(get());
}
sockaddr_in& getIPv4() {
return *reinterpret_cast<sockaddr_in*>(&first);
return *reinterpret_cast<sockaddr_in*>(get());
}
sockaddr_in6& getIPv6() {
return *reinterpret_cast<sockaddr_in6*>(&first);
return *reinterpret_cast<sockaddr_in6*>(get());
}
/**
@@ -130,14 +219,17 @@ public:
bool isUnspecified() const;
bool isMappedIPv4() const;
SockAddr getMappedIPv4() const;
/**
* A comparator to classify IP addresses, only considering the
* first 64 bits in IPv6.
*/
struct ipCmp {
bool operator()(const SockAddr& a, const SockAddr& b) const {
if (a.second != b.second)
return a.second < b.second;
if (a.len != b.len)
return a.len < b.len;
socklen_t start, len;
switch(a.getFamily()) {
case AF_INET:
@@ -151,18 +243,30 @@ public:
break;
default:
start = 0;
len = a.second;
len = a.len;
break;
}
return std::memcmp((uint8_t*)&a.first+start, (uint8_t*)&b.first+start, len) < 0;
return std::memcmp((uint8_t*)a.get()+start,
(uint8_t*)b.get()+start, len) < 0;
}
};
private:
socklen_t len {0};
struct free_delete { void operator()(void* p) { std::free(p); } };
std::unique_ptr<sockaddr, free_delete> addr {};
void set(const sockaddr* sa, socklen_t length) {
if (len != length) {
len = length;
if (len) addr.reset((sockaddr*)std::malloc(len));
else addr.reset();
}
if (len)
std::memcpy((uint8_t*)get(), (const uint8_t*)sa, len);
}
};
OPENDHT_PUBLIC bool operator==(const SockAddr& a, const SockAddr& b);
OPENDHT_PUBLIC std::string printAddr(const SockAddr& addr);
}
+6
View File
@@ -32,6 +32,9 @@
#define WANT4 1
#define WANT6 2
/**
* OpenDHT C++ namespace
*/
namespace dht {
using NetId = uint32_t;
@@ -93,6 +96,9 @@ public:
// Serialization related definitions and utility functions
/**
* Arbitrary binary data.
*/
using Blob = std::vector<uint8_t>;
/**
+419
View File
@@ -0,0 +1,419 @@
#pragma once
#include "utils.h"
#include <uv.h>
namespace dht {
struct EventLoop {
using OnSignal = std::function<void(int)>;
EventLoop() {
uv_loop_init(&loop);
uv_signal_init(&loop, &sig_handler);
sig_handler.data = this;
uv_signal_start(&sig_handler, &EventLoop::signal_handler, SIGINT);
}
~EventLoop() {
uv_loop_close(&loop);
}
auto run() {
return uv_run(&loop, UV_RUN_DEFAULT);
}
auto runOnce() {
return uv_run(&loop, UV_RUN_NOWAIT);
}
void stop() {
uv_signal_stop(&sig_handler);
uv_walk(&loop, &EventLoop::close_walk_cb, this);
}
uv_loop_t* get() { return &loop; }
void setOnSignal(OnSignal cb) {
onSignal_ = cb;
}
private:
uv_loop_t loop;
uv_signal_t sig_handler;
OnSignal onSignal_;
void onSignal(int sig) {
if (onSignal_)
onSignal_(sig);
}
static void close_walk_cb(uv_handle_t* handle, void* /*arg*/) {
if (not uv_is_closing(handle))
uv_close(handle, [](uv_handle_t* handle) {
std::cout << "handle " << handle << " of type " << handle->type << " closed" << std::endl;
});
}
static void signal_handler(uv_signal_t* handle, int signum) {
if (handle->data) {
static_cast<EventLoop*>(handle->data)->onSignal(signum);
}
}
};
using GetAddrInfoCb = std::function<void(std::vector<SockAddr>&&)>;
void getAddrInfo(uv_loop_t* loop, const char* node, const char* service, GetAddrInfoCb&& cb);
struct Job : std::enable_shared_from_this<Job> {
Job(uv_loop_t* loop, std::function<void()>&& f = {}) : do_(std::move(f)) {
uv_timer_init(loop, &timer_);
timer_.data = nullptr;
}
void run(uint64_t t = 0) {
uv_timer_start(&timer_, &Job::timer_cb, t, 0);
}
inline void run(const duration& t) {
run(getTimeout(t));
}
inline void run(const time_point& t) {
run(getTimeout(t));
}
void cancel() {
if (timer_.data and not uv_is_closing((uv_handle_t*)&timer_)) {
uv_timer_stop(&timer_);
uv_close((uv_handle_t*)&timer_, &Job::close_cb);
}
}
static Sp<Job> make(uv_loop_t* loop, std::function<void()>&& f = {}) {
auto job = std::make_shared<Job>(loop, std::move(f));
job->timer_.data = new Sp<Job>(job);
return job;
}
static uint64_t getTimeout(const duration& d) {
return std::chrono::duration_cast<std::chrono::milliseconds>(d).count();
}
uint64_t getTimeout(const time_point& tp) const {
uint64_t t = getTimeout(tp.time_since_epoch());
uint64_t now = uv_now(timer_.loop);
return (t < now) ? 0 : t - now;
}
std::function<void()> do_;
private:
uv_timer_t timer_;
void onTimer() {
if (do_)
do_();
cancel();
}
void onClosed() {
if (timer_.data) {
delete static_cast<Sp<Job>*>(timer_.data);
timer_.data = nullptr;
}
}
static void timer_cb(uv_timer_t* handle) {
if (handle->data) {
auto timer = *static_cast<Sp<Job>*>(handle->data);
timer->onTimer();
}
}
static void close_cb(uv_handle_t* handle) {
if (handle->data) {
auto timer = *static_cast<Sp<Job>*>(handle->data);
timer->onClosed();
} else {
std::cerr << "Job::onClosed(): no data! " << handle << std::endl;
}
}
};
static inline void alloc_buffer(uv_handle_t* /*handle*/, size_t suggested_size, uv_buf_t *buf) {
buf->base = (char*)malloc(suggested_size);
buf->len = suggested_size;
}
using OnClose = std::function<void()>;
struct UdpSocket : public std::enable_shared_from_this<UdpSocket>
{
using OnReceive = std::function<void(const uint8_t* data, size_t size, const SockAddr& addr)>;
using OnSent = std::function<void(int status)>;
UdpSocket(uv_loop_t* loop) {
uv_udp_init(loop, &sock);
sock.data = nullptr;
}
void open(const char* bind_addr, in_port_t port, OnReceive cb) {
if (not sock.data)
sock.data = new std::shared_ptr<UdpSocket>(shared_from_this());
receive_cb = std::move(cb);
struct sockaddr_in6 addr;
uv_ip6_addr(bind_addr, port, &addr);
uv_udp_bind(&sock, (const struct sockaddr*)&addr, 0);
uv_udp_recv_start(&sock, &UdpSocket::on_alloc_buffer, &UdpSocket::on_receive);
}
int send(uint8_t* data, size_t size, const SockAddr& addr, OnSent&& cb = {}) {
auto req = new SendReq;
req->msg = data;
req->req.data = req;
req->cb = std::move(cb);
const uv_buf_t buf {
/*.base =*/ (char*)data,
/*.len =*/ size
};
return uv_udp_send(&req->req, &sock, &buf, 1, addr.get(), &UdpSocket::on_sent);
}
void close(OnClose cb) {
close_cb = cb;
uv_close((uv_handle_t*) &sock, &UdpSocket::on_close);
}
bool isRunning() const {
return uv_is_active((uv_handle_t*) &sock);
}
SockAddr getBoundAddr() {
sockaddr_storage addr;
int size = sizeof(addr);
uv_udp_getsockname(&sock, (sockaddr*)&addr, &size);
return {(const sockaddr*)&addr, (socklen_t)size};
}
private:
struct SendReq {
uv_udp_send_t req;
uint8_t* msg;
OnSent cb;
};
uv_udp_t sock;
OnReceive receive_cb;
OnClose close_cb;
void onAllocBuffer(size_t suggested_size, uv_buf_t* buf) {
buf->base = (char*)malloc(suggested_size);
buf->len = suggested_size;
}
void onReceive(ssize_t nread, const uv_buf_t* buf, const struct sockaddr* addr, unsigned /*flags*/) {
if (nread > 0 and addr and receive_cb) {
receive_cb((const uint8_t*)buf->base, nread, SockAddr(addr));
}
if (buf->base)
free(buf->base);
}
void onClosed() {
if (close_cb)
close_cb();
if (auto s = static_cast<std::shared_ptr<UdpSocket>*>(sock.data)) {
sock.data = nullptr;
delete s;
}
}
void onSent(SendReq& req, int status) {
if (req.cb) {
req.cb(status);
}
free(req.msg);
}
static std::shared_ptr<UdpSocket> get(uv_handle_t* handle) {
return handle->data ? *static_cast<std::shared_ptr<UdpSocket>*>(handle->data) : std::shared_ptr<UdpSocket>();
}
static void on_close(uv_handle_t* handle) {
if (auto node = get(handle))
node->onClosed();
}
static void on_sent(uv_udp_send_t* req, int status) {
if (auto data = static_cast<SendReq*>(req->data)) {
if (auto node = get((uv_handle_t*)req->handle))
node->onSent(*data, status);
delete data;
}
}
static void on_receive(uv_udp_t* handle, ssize_t nread, const uv_buf_t* buf, const struct sockaddr* addr, unsigned flags) {
if (auto node = get((uv_handle_t*)handle))
node->onReceive(nread, buf, addr, flags);
}
static inline void on_alloc_buffer(uv_handle_t* handle, size_t suggested_size, uv_buf_t *buf) {
if (auto node = get((uv_handle_t*)handle))
node->onAllocBuffer(suggested_size, buf);
}
};
struct TcpSocket : public std::enable_shared_from_this<TcpSocket>
{
using OnConnect = std::function<void(std::shared_ptr<TcpSocket>&& new_socket)>;
using OnConnected = std::function<void(int status)>;
using OnRead = std::function<void(const uint8_t* data, size_t size)>;
using OnWrite = std::function<void(int status)>;
TcpSocket(uv_loop_t* loop) {
uv_tcp_init(loop, &sock);
sock.data = nullptr;
}
void listen(const char* bind_addr, in_port_t port, OnConnect cb) {
if (not sock.data)
sock.data = new std::shared_ptr<TcpSocket>(shared_from_this());
connect_cb = cb;
struct sockaddr_in6 addr;
uv_ip6_addr(bind_addr, port, &addr);
uv_tcp_bind(&sock, (const struct sockaddr*)&addr, 0);
if (int r = uv_listen((uv_stream_t*)&sock, 128, &TcpSocket::on_new_connection)) {
fprintf(stderr, "Listen error %s\n", uv_strerror(r));
}
}
void connect(const sockaddr* addr, OnConnected&& cb) {
if (not sock.data)
sock.data = new std::shared_ptr<TcpSocket>(shared_from_this());
auto req = new ConnectReq;
req->connect.data = req;
req->sock = shared_from_this();
req->callback = std::move(cb);
uv_tcp_connect(&req->connect, &sock, addr, &TcpSocket::on_connect);
}
void start(OnRead&& cb) {
std::cerr << "TCP start" << std::endl;
read_cb = std::move(cb);
uv_read_start((uv_stream_t*)&sock, &TcpSocket::on_alloc_buffer, &TcpSocket::on_read);
}
int write(uint8_t* msg, size_t len, OnWrite&& cb = {}) {
std::cerr << "TCP write " << len << std::endl;
auto req = new WriteReq;
req->write.data = req;
req->msg = msg;
req->sock = shared_from_this();
req->callback = std::move(cb);
uv_buf_t buf;
buf.base = (char*)msg;
buf.len = len;
return uv_write(&req->write, (uv_stream_t *)&sock, &buf, 1, &TcpSocket::on_write_end);
}
void close(OnClose onclose = {}) {
std::cerr << "TCP close" << std::endl;
if (not isClosed()) {
close_cb = onclose;
uv_close((uv_handle_t*) &sock, &TcpSocket::on_close);
}
}
bool isClosed() {
return not sock.data or uv_is_closing((const uv_handle_t*) &sock);
}
bool canWrite() const {
return uv_is_writable((const uv_stream_t*)&sock);
}
SockAddr getBoundAddr() {
sockaddr_storage addr;
int size = sizeof(addr);
uv_tcp_getsockname(&sock, (sockaddr*)&addr, &size);
return {(const sockaddr*)&addr, (socklen_t)size};
}
SockAddr getPeerAddr() {
sockaddr_storage addr;
int size = sizeof(addr);
uv_tcp_getpeername(&sock, (sockaddr*)&addr, &size);
return {(const sockaddr*)&addr, (socklen_t)size};
}
private:
uv_tcp_t sock;
OnConnect connect_cb;
OnRead read_cb;
OnClose close_cb;
struct ConnectReq {
uv_connect_t connect;
std::shared_ptr<TcpSocket> sock;
OnConnected callback;
};
struct WriteReq {
uv_write_t write;
uint8_t* msg;
std::shared_ptr<TcpSocket> sock;
OnWrite callback;
};
std::shared_ptr<TcpSocket> accept() {
auto s = std::make_shared<TcpSocket>(sock.loop);
s->sock.data = new std::shared_ptr<TcpSocket>(s);
if (uv_accept((uv_stream_t*)&sock, (uv_stream_t*) &s->sock) == 0) {
return s;
} else {
s->close();
return {};
}
}
void onNewConnection(int status) {
if (status < 0) {
fprintf(stderr, "New connection error %s\n", uv_strerror(status));
return;
}
if (auto s = accept())
connect_cb(std::move(s));
fprintf(stderr, "on_new_connection end\n");
}
void onConnected(int status, OnConnected&& cb) {
if (cb)
cb(status);
}
void onAllocBuffer(size_t suggested_size, uv_buf_t* buf) {
buf->base = (char*)malloc(suggested_size);
buf->len = suggested_size;
}
void onRead(ssize_t nread, const uv_buf_t *buf) {
if (nread < 0) {
if (nread != UV_EOF) {
fprintf(stderr, "Read error %s\n", uv_err_name(nread));
} else {
fprintf(stderr, "EOF\n");
}
close();
} else if (nread > 0) {
if (read_cb)
read_cb((const uint8_t*)buf->base, nread);
}
if (buf->base)
free(buf->base);
}
void onWrite(int status, OnWrite&& cb) {
if (cb)
cb(status);
}
void onClosed() {
read_cb = {};
connect_cb = {};
if (close_cb) {
close_cb();
close_cb = {};
}
if (auto s = static_cast<std::shared_ptr<TcpSocket>*>(sock.data)) {
sock.data = nullptr;
delete s;
}
}
static std::shared_ptr<TcpSocket> get(uv_handle_t* handle) {
return handle->data ? *static_cast<std::shared_ptr<TcpSocket>*>(handle->data) : std::shared_ptr<TcpSocket>();
}
static void on_new_connection(uv_stream_t* handle, int status) {
if (auto node = get((uv_handle_t*)handle))
node->onNewConnection(status);
}
static void on_close(uv_handle_t* handle) {
if (auto node = get(handle))
node->onClosed();
}
static inline void on_alloc_buffer(uv_handle_t* handle, size_t suggested_size, uv_buf_t *buf) {
if (auto node = get((uv_handle_t*)handle))
node->onAllocBuffer(suggested_size, buf);
}
static void on_read(uv_stream_t* client, ssize_t nread, const uv_buf_t *buf) {
if (auto node = get((uv_handle_t*)client))
node->onRead(nread, buf);
}
static void on_write_end(uv_write_t* req, int status) {
if (auto write = static_cast<WriteReq*>(req->data)) {
write->sock->onWrite(status, std::move(write->callback));
if (write->msg)
free(write->msg);
delete write;
}
}
static void on_connect(uv_connect_t* req, int status) {
if (auto conn = static_cast<ConnectReq*>(req->data)) {
conn->sock->onConnected(status, std::move(conn->callback));
delete conn;
}
}
};
}
+35 -11
View File
@@ -22,6 +22,7 @@
#include "infohash.h"
#include "crypto.h"
#include "utils.h"
#include "sockaddr.h"
#include <msgpack.hpp>
@@ -51,7 +52,7 @@ struct Query;
* @param form_addr: network address of the incoming request.
* @param from_len: network address lendth of the incoming request.
*/
using StorePolicy = std::function<bool(InfoHash key, std::shared_ptr<Value>& value, InfoHash from, const sockaddr* from_addr, socklen_t from_len)>;
using StorePolicy = std::function<bool(InfoHash key, std::shared_ptr<Value>& value, const InfoHash& from, const SockAddr& addr)>;
/**
* An edition policy is applied once to every incoming value storage requests,
@@ -67,15 +68,15 @@ using StorePolicy = std::function<bool(InfoHash key, std::shared_ptr<Value>& val
* @param form_addr: network address of the incoming request.
* @param from_len: network address lendth of the incoming request.
*/
using EditPolicy = std::function<bool(InfoHash key, const std::shared_ptr<Value>& old_val, std::shared_ptr<Value>& new_val, InfoHash from, const sockaddr* from_addr, socklen_t from_len)>;
using EditPolicy = std::function<bool(InfoHash key, const std::shared_ptr<Value>& old_val, std::shared_ptr<Value>& new_val, const InfoHash& from, const SockAddr& addr)>;
static constexpr const size_t MAX_VALUE_SIZE {1024 * 64};
struct OPENDHT_PUBLIC ValueType {
typedef uint16_t Id;
static bool DEFAULT_STORE_POLICY(InfoHash, std::shared_ptr<Value>& v, InfoHash, const sockaddr*, socklen_t);
static bool DEFAULT_EDIT_POLICY(InfoHash, const std::shared_ptr<Value>&, std::shared_ptr<Value>&, InfoHash, const sockaddr*, socklen_t) {
static bool DEFAULT_STORE_POLICY(InfoHash, std::shared_ptr<Value>& v, const InfoHash&, const SockAddr&);
static bool DEFAULT_EDIT_POLICY(InfoHash, const std::shared_ptr<Value>&, std::shared_ptr<Value>&, const InfoHash&, const SockAddr&) {
return false;
}
@@ -651,7 +652,8 @@ struct OPENDHT_PUBLIC Select
* @return the resulting Select instance.
*/
Select& field(Value::Field field) {
fieldSelection_.emplace_back(field);
if (std::find(fieldSelection_.begin(), fieldSelection_.end(), field) == fieldSelection_.end())
fieldSelection_.emplace_back(field);
return *this;
}
@@ -670,6 +672,12 @@ struct OPENDHT_PUBLIC Select
fieldSelection_ = o.as<decltype(fieldSelection_)>();
}
std::string toString() const {
std::stringstream ss;
ss << *this;
return ss.str();
}
OPENDHT_PUBLIC friend std::ostream& operator<<(std::ostream& s, const dht::Select& q);
private:
std::vector<Value::Field> fieldSelection_ {};
@@ -697,7 +705,9 @@ struct OPENDHT_PUBLIC Where
* @return the resulting Where instance.
*/
Where& id(Value::Id id) {
filters_.emplace_back(Value::Field::Id, id);
FieldValue fv {Value::Field::Id, id};
if (std::find(filters_.begin(), filters_.end(), fv) == filters_.end())
filters_.emplace_back(std::move(fv));
return *this;
}
@@ -709,7 +719,9 @@ struct OPENDHT_PUBLIC Where
* @return the resulting Where instance.
*/
Where& valueType(ValueType::Id type) {
filters_.emplace_back(Value::Field::ValueType, type);
FieldValue fv {Value::Field::ValueType, type};
if (std::find(filters_.begin(), filters_.end(), fv) == filters_.end())
filters_.emplace_back(std::move(fv));
return *this;
}
@@ -721,7 +733,9 @@ struct OPENDHT_PUBLIC Where
* @return the resulting Where instance.
*/
Where& owner(InfoHash owner_pk_hash) {
filters_.emplace_back(Value::Field::OwnerPk, owner_pk_hash);
FieldValue fv {Value::Field::OwnerPk, owner_pk_hash};
if (std::find(filters_.begin(), filters_.end(), fv) == filters_.end())
filters_.emplace_back(std::move(fv));
return *this;
}
@@ -733,7 +747,9 @@ struct OPENDHT_PUBLIC Where
* @return the resulting Where instance.
*/
Where& seq(uint16_t seq_no) {
filters_.emplace_back(Value::Field::SeqNum, seq_no);
FieldValue fv {Value::Field::SeqNum, seq_no};
if (std::find(filters_.begin(), filters_.end(), fv) == filters_.end())
filters_.emplace_back(std::move(fv));
return *this;
}
@@ -745,7 +761,9 @@ struct OPENDHT_PUBLIC Where
* @return the resulting Where instance.
*/
Where& userType(std::string user_type) {
filters_.emplace_back(Value::Field::UserType, Blob {user_type.begin(), user_type.end()});
FieldValue fv {Value::Field::UserType, Blob {user_type.begin(), user_type.end()}};
if (std::find(filters_.begin(), filters_.end(), fv) == filters_.end())
filters_.emplace_back(std::move(fv));
return *this;
}
@@ -772,6 +790,12 @@ struct OPENDHT_PUBLIC Where
filters_ = o.as<decltype(filters_)>();
}
std::string toString() const {
std::stringstream ss;
ss << *this;
return ss.str();
}
OPENDHT_PUBLIC friend std::ostream& operator<<(std::ostream& s, const dht::Where& q);
private:
@@ -828,7 +852,7 @@ struct OPENDHT_PUBLIC Query
void msgpack_unpack(const msgpack::object& o);
std::string toString() {
std::string toString() const {
std::stringstream ss;
ss << *this;
return ss.str();
+1 -1
View File
@@ -7,5 +7,5 @@ Description: C++ Distributed Hash Table library
Version: @VERSION@
Libs: -L${libdir} -lopendht
Libs.private: -lpthread
Requires.private: gnutls >= 3.1
Requires.private: gnutls >= 3.1@argon2_lib@
Cflags: -I${includedir}
+3
View File
@@ -9,3 +9,6 @@ add_custom_target(python ALL
DEPENDS opendht opendht_cpp.pxd opendht.pyx)
install(CODE "execute_process(COMMAND python3 setup.py install --root=\$ENV{DESTDIR}/ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})")
if (OPENDHT_TOOLS)
install(PROGRAMS tools/dhtcluster.py DESTINATION ${CMAKE_INSTALL_BINDIR} RENAME dhtcluster)
endif()
+85 -17
View File
@@ -58,10 +58,12 @@ cdef inline void shutdown_callback(void* user_data) with gil:
ref.Py_DECREF(cbs)
cdef inline bool get_callback(shared_ptr[cpp.Value] value, void *user_data) with gil:
cb = (<object>user_data)['get']
cbs = <object>user_data
cb = cbs['get']
f = cbs['filter'] if 'filter' in cbs else None
pv = Value()
pv._value = value
return cb(pv)
return cb(pv) if not f or f(pv) else True
cdef inline void done_callback(bool done, cpp.vector[shared_ptr[cpp.Node]]* nodes, void *user_data) with gil:
node_ids = []
@@ -164,6 +166,59 @@ cdef class NodeEntry(_WithID):
n._node = self._v.second
return n
cdef class Query(object):
cdef cpp.Query _query
def __cinit__(self, str q_str=''):
self._query = cpp.Query(q_str.encode())
def __str__(self):
return self._query.toString().decode()
def buildFrom(self, Select s, Where w):
self._query = cpp.Query(s._select, w._where)
def isSatisfiedBy(self, Query q):
return self._query.isSatisfiedBy(q._query)
cdef class Select(object):
cdef cpp.Select _select
def __cinit__(self, str q_str=None):
if q_str:
self._select = cpp.Select(q_str.encode())
else:
self._select = cpp.Select()
def __str__(self):
return self._select.toString().decode()
def isSatisfiedBy(self, Select os):
return self._select.isSatisfiedBy(os._select)
def field(self, int field):
self._select.field(<cpp.Field> field)
return self
cdef class Where(object):
cdef cpp.Where _where
def __cinit__(self, str q_str=None):
if q_str:
self._where = cpp.Where(q_str.encode())
else:
self._where = cpp.Where()
def __str__(self):
return self._where.toString().decode()
def isSatisfiedBy(self, Where where):
return self._where.isSatisfiedBy(where._where)
def id(self, cpp.uint64_t id):
self._where.id(id)
return self
def valueType(self, cpp.uint16_t type):
self._where.valueType(type)
return self
def owner(self, InfoHash owner_pk_hash):
self._where.owner(owner_pk_hash._infohash)
return self
def seq(self, cpp.uint16_t seq_no):
self._where.seq(seq_no)
return self
def userType(self, str user_type):
self._where.userType(user_type.encode())
return self
cdef class Value(object):
cdef shared_ptr[cpp.Value] _value
def __init__(self, bytes val=b''):
@@ -187,6 +242,11 @@ cdef class Value(object):
return string(<char*>self._value.get().data.data(), self._value.get().data.size())
def __set__(self, bytes value):
self._value.get().data = value
property user_type:
def __get__(self):
return self._value.get().user_type.decode()
def __set__(self, str t):
self._value.get().user_type = t.encode()
property id:
def __get__(self):
return self._value.get().id
@@ -215,7 +275,7 @@ cdef class NodeSet(object):
def size(self):
return self._nodes.size()
def insert(self, NodeEntry l):
self._nodes.insert(l._v)
return self._nodes.insert(l._v).second
def extend(self, li):
for n in li:
self.insert(n)
@@ -240,6 +300,8 @@ cdef class NodeSet(object):
return s
def __iter__(self):
return NodeSetIter(self)
def __len__(self):
return self.size()
cdef class PrivateKey(_WithID):
cdef shared_ptr[cpp.PrivateKey] _key
@@ -382,6 +444,10 @@ cdef class DhtConfig(object):
self._config.dht_config.node_config.node_id = id._infohash
def setNetwork(self, unsigned netid):
self._config.dht_config.node_config.network = netid
def setPort(self, cpp.in_port_t port):
self._config.dht_config.node_config.network_config.bind_port = port
def setBindAddr(self, string addr):
self._config.dht_config.node_config.network_config.bind_addr = addr
def setMaintainStorage(self, bool maintain_storage):
self._config.dht_config.node_config.maintain_storage = maintain_storage
@@ -400,7 +466,7 @@ cdef class DhtRunner(_WithID):
if done_cb:
cb_obj = {'done':done_cb}
ref.Py_INCREF(cb_obj)
self.thisptr.get().bootstrap(addr._addr, cpp.bindDoneCbSimple(done_callback_simple, <void*>cb_obj))
self.thisptr.get().ping(addr._addr, cpp.bindDoneCbSimple(done_callback_simple, <void*>cb_obj))
else:
lock = threading.Condition()
pending = 0
@@ -424,12 +490,10 @@ cdef class DhtRunner(_WithID):
def run(self, Identity id=None, is_bootstrap=False, cpp.in_port_t port=0, str ipv4="", str ipv6="", DhtConfig config=DhtConfig()):
if id:
config.setIdentity(id)
if ipv4 or ipv6:
bind4 = ipv4.encode() if ipv4 else b''
bind6 = ipv6.encode() if ipv6 else b''
self.thisptr.get().run(bind4, bind6, str(port).encode(), config._config)
else:
self.thisptr.get().run(port, config._config)
config.setPort(port)
if ipv6:
config.setBindAddr(ipv6)
self.thisptr.get().run(config._config)
def join(self):
self.thisptr.get().join()
def shutdown(self, shutdown_cb=None):
@@ -444,9 +508,9 @@ cdef class DhtRunner(_WithID):
cpp.enableFileLogging(self.thisptr.get()[0], path.encode())
def isRunning(self):
return self.thisptr.get().isRunning()
def getBound(self, cpp.sa_family_t af = 0):
def getBound(self):
s = SockAddr()
s._addr = self.thisptr.get().getBound(af)
s._addr = self.thisptr.get().getBound()
return s
def getStorageLog(self):
return self.thisptr.get().getStorageLog().decode()
@@ -461,7 +525,7 @@ cdef class DhtRunner(_WithID):
stats.append(n)
return stats
def get(self, InfoHash key, get_cb=None, done_cb=None):
def get(self, InfoHash key, get_cb=None, done_cb=None, filter=None, Where where=None):
"""Retreive values associated with a key on the DHT.
key -- the key for which to search
@@ -471,9 +535,14 @@ cdef class DhtRunner(_WithID):
operation is completed.
"""
if get_cb:
cb_obj = {'get':get_cb, 'done':done_cb}
cb_obj = {'get':get_cb, 'done':done_cb, 'filter':filter}
ref.Py_INCREF(cb_obj)
self.thisptr.get().get(key._infohash, cpp.bindGetCb(get_callback, <void*>cb_obj), cpp.bindDoneCb(done_callback, <void*>cb_obj))
if where is None:
where = Where()
self.thisptr.get().get(key._infohash, cpp.bindGetCb(get_callback, <void*>cb_obj),
cpp.bindDoneCb(done_callback, <void*>cb_obj),
cpp.nullptr, #filter implemented in the get_callback
where._where)
else:
lock = threading.Condition()
pending = 0
@@ -489,7 +558,7 @@ cdef class DhtRunner(_WithID):
lock.notify()
with lock:
pending += 1
self.get(key, get_cb=tmp_get, done_cb=tmp_done)
self.get(key, get_cb=tmp_get, done_cb=tmp_done, filter=filter, where=where)
while pending > 0:
lock.wait()
return res
@@ -597,4 +666,3 @@ cdef class Pht(object):
val,
cpp.bindDoneCbSimple(done_callback_simple, <void*>cb_obj)
)
+48 -7
View File
@@ -16,7 +16,7 @@
# along with this program; If not, see <https://www.gnu.org/licenses/>.
from libc.stdint cimport *
from libcpp cimport bool
from libcpp cimport bool, nullptr_t, nullptr
from libcpp.string cimport string
from libcpp.vector cimport vector
from libcpp.utility cimport pair
@@ -73,7 +73,7 @@ cdef extern from "opendht/sockaddr.h" namespace "dht":
SockAddr() except +
string toString() const
in_port_t getPort() const
void setPort(in_port_t p)
void setPort(in_port_t p) const
sa_family_t getFamily() const
void setFamily(sa_family_t f)
@@ -120,6 +120,19 @@ cdef extern from "opendht/crypto.h" namespace "dht::crypto":
ctypedef TrustList.VerifyResult TrustListVerifyResult
cdef extern from "opendht/value.h" namespace "dht::Value":
cdef cppclass Field:
pass
cdef extern from "opendht/value.h" namespace "dht::Value::Field":
cdef Field None
cdef Field Id
cdef Field ValueType
cdef Field OwnerPk
cdef Field SeqNum
cdef Field UserType
cdef Field COUNT
cdef extern from "opendht/value.h" namespace "dht":
cdef cppclass Value:
Value() except +
@@ -133,6 +146,31 @@ cdef extern from "opendht/value.h" namespace "dht":
vector[uint8_t] data
string user_type
cdef cppclass Query:
Query() except +
Query(Select s, Where w) except +
Query(string q_str) except +
bool isSatisfiedBy(const Query& q) const
string toString() const
cdef cppclass Select:
Select() except +
Select(const string& q_str) except +
bool isSatisfiedBy(const Select& os) const
Select& field(Field field)
string toString() const
cdef cppclass Where:
Where() except +
Where(const string& q_str)
bool isSatisfiedBy(const Where& where) const
Where& id(uint64_t id)
Where& valueType(uint16_t type)
Where& owner(InfoHash owner_pk_hash)
Where& seq(uint16_t seq_no)
Where& userType(string user_type)
string toString() const
cdef extern from "opendht/node.h" namespace "dht":
cdef cppclass Node:
Node() except +
@@ -160,11 +198,15 @@ cdef extern from "opendht/callbacks.h" namespace "dht":
cdef DoneCallback bindDoneCb(DoneCallbackRaw cb, void *user_data)
cdef DoneCallbackSimple bindDoneCbSimple(DoneCallbackSimpleRaw cb, void *user_data)
cppclass NetworkConfig:
string bind_addr
in_port_t bind_port
cppclass Config:
InfoHash node_id
uint32_t network
bool is_bootstrap
bool maintain_storage
NetworkConfig network_config
cppclass SecureDhtConfig:
Config node_config
Identity id
@@ -180,17 +222,16 @@ cdef extern from "opendht/dhtrunner.h" namespace "dht":
InfoHash getId() const
InfoHash getNodeId() const
void bootstrap(const_char*, const_char*)
void bootstrap(const SockAddr&, DoneCallbackSimple done_cb)
void run(in_port_t, Config config)
void run(const_char*, const_char*, const_char*, Config config)
void ping(const SockAddr&, DoneCallbackSimple done_cb)
void run(Config config)
void join()
void shutdown(ShutdownCallback)
bool isRunning()
SockAddr getBound(sa_family_t af) const
SockAddr getBound() const
string getStorageLog() const
string getRoutingTablesLog(sa_family_t af) const
string getSearchesLog(sa_family_t af) const
void get(InfoHash key, GetCallback get_cb, DoneCallback done_cb)
void get(InfoHash key, GetCallback get_cb, DoneCallback done_cb, nullptr_t f, Where w)
void put(InfoHash key, shared_ptr[Value] val, DoneCallback done_cb)
ListenToken listen(InfoHash key, GetCallback get_cb)
void cancelListen(InfoHash key, SharedListenToken token)
+2 -2
View File
@@ -50,8 +50,8 @@ setup(name="opendht",
["@CURRENT_SOURCE_DIR@/opendht.pyx"],
include_dirs = ['@PROJECT_SOURCE_DIR@/include'],
language="c++",
extra_compile_args=["-std=c++11"],
extra_link_args=["-std=c++11"],
extra_compile_args=["-std=c++14"],
extra_link_args=["-std=c++14"],
libraries=["opendht"],
library_dirs = ['@CURRENT_BINARY_DIR@', '@PROJECT_BINARY_DIR@']
))
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright (C) 2015-2016 Savoir-faire Linux Inc.
# Copyright (C) 2015-2017 Savoir-faire Linux Inc.
# Author(s): Adrien Béraud <adrien.beraud@savoirfairelinux.com>
#
# This program is free software; you can redistribute it and/or modify
@@ -17,20 +17,22 @@
# along with this program; If not, see <http://www.gnu.org/licenses/>.
import os, sys, time, cmd
import subprocess
import signal
import argparse
import threading
import logging
import logging.handlers
import opendht as dht
logger = logging.getLogger('dhtcluster')
class NodeCluster(object):
nodes = []
node_uid = 0
@staticmethod
def run_node(ip4, ip6, p, bootstrap=None, is_bootstrap=False, logfile=None):
print("run_node", ip4, ip6, p, bootstrap, logfile)
logger.info("Running node on port %s with bootstrap %s", p, bootstrap)
n = dht.DhtRunner()
n.run(ipv4=ip4 if ip4 else "", ipv6=ip6 if ip6 else "", port=p, is_bootstrap=is_bootstrap)
if logfile:
@@ -43,7 +45,7 @@ class NodeCluster(object):
@staticmethod
def find_ip(iface):
if not iface or iface == 'any':
return ('0.0.0.0','')
return ('0.0.0.0','::0')
if_ip4 = netifaces.ifaddresses(iface)[netifaces.AF_INET][0]['addr']
if_ip6 = netifaces.ifaddresses(iface)[netifaces.AF_INET6][0]['addr']
@@ -60,30 +62,22 @@ class NodeCluster(object):
if bootstrap:
self.bootstrap = (bootstrap.hostname, str(bootstrap.port) if bootstrap.port else "4222")
else:
print("Using fallback bootstrap", self.ip4, self.port)
logger.info("Using fallback bootstrap %s %s", self.ip4, self.port)
self.bootstrap = ((self.ip4, str(self.port)))
if first_bootstrap:
print("Starting bootstrap node")
logger.info("Starting bootstrap node")
self.nodes.append(NodeCluster.run_node(self.ip4, self.ip6, self.port, self.bootstrap, is_bootstrap=True))
self.bootstrap = ((self.ip4, str(self.port)))
self.port += 1
#print(self.ip4, self.ip6, self.port)
def front(self):
if len(self.nodes) == 0:
return None
return self.nodes[0][1]
return self.nodes[0][1] if self.nodes else None
def get(self, i=None):
if not self.nodes:
def get(self, i):
if not self.nodes or i < 0 or i >= len(self.nodes):
return None
if i is None:
l = list(self.nodes)
random.shuffle(l)
return l[0][1]
else:
return self.nodes[i][1]
return self.nodes[i][1]
def getNodeInfoById(self, id=None):
if id:
@@ -100,64 +94,25 @@ class NodeCluster(object):
self.node_uid += 1
return n
def end_node(self, id=None, shutdown=False, last_msg_stats=None):
"""
Ends a running node.
@param id: The 40 hex chars id of the node.
@type id: bytes
@return: If a node was deleted or not.
@rtype : boolean
"""
lock = threading.Condition()
def shutdown_cb():
nonlocal lock
NodeCluster.Log.log('Done.')
with lock:
lock.notify()
def end_node(self):
if not self.nodes:
return
elif id:
n = self.getNodeInfoById(id)
if n:
if shutdown:
with lock:
print('Waiting for node to shutdown... ')
n[1].shutdown(shutdown_cb)
lock.wait()
if last_msg_stats:
last_msg_stats.append(self.getMessageStats())
n[1].join()
self.nodes.remove(n)
print(id, 'deleted !')
return True
else:
return False
else:
n = self.nodes.pop()
n[1].join()
return True
def replace_node(self, id=None, shutdown=False, last_msg_stats=None):
random.shuffle(self.nodes)
deleted = self.end_node(id=id, shutdown=shutdown, last_msg_stats=last_msg_stats)
if deleted:
self.launch_node()
def resize(self, n):
n = min(n, 500)
l = len(self.nodes)
if n == l:
return
if n > l:
print("Launching", n-l, "nodes", self.ip4, self.ip6)
logger.info("Launching %d nodes bound on IPv4 %s IPv6 %s", n-l, self.ip4, self.ip6)
for i in range(l, n):
self.launch_node()
else:
print("Ending", l-n, "nodes", self.ip4, self.ip6)
#random.shuffle(self.nodes)
logger.info("Ending %d nodes", l-n)
for i in range(n, l):
self.end_node()
@@ -198,13 +153,22 @@ class ClusterShell(cmd.Cmd):
return True
def do_node(self, arg):
if not arg:
setNode()
self.setNode()
else:
nodenum = int(arg)
if nodenum > len(self.net.nodes) or nodenum < 1:
node = self.net.get(nodenum-1)
if not node:
print("Invalid node number:", nodenum, " (accepted: 1-", len(self.net.nodes), ")")
else:
self.setNode(self.net.nodes[nodenum-1][1], nodenum)
self.setNode(node, nodenum)
def do_resize(self, arg):
if not arg:
return
try:
nodenum = int(arg)
self.net.resize(nodenum)
except Exception as e:
print("Can't resize:", e)
def do_ll(self, arg):
if self.node:
print('Node', self.node.getNodeId().decode())
@@ -234,32 +198,73 @@ if __name__ == '__main__':
import argparse
from urllib.parse import urlparse
net = None
run = True
def clean_quit():
global net, run
if run:
run = False
if net:
net.resize(0)
net = None
def quit_signal(signum, frame):
clean_quit()
try:
parser = argparse.ArgumentParser(description='Create a dht network of -n nodes')
parser.add_argument('-n', '--node-num', help='number of dht nodes to run', type=int, default=32)
parser.add_argument('-I', '--iface', help='local interface to bind', default='any')
parser.add_argument('-p', '--port', help='start of port range (port, port+node_num)', type=int, default=4000)
parser.add_argument('-b', '--bootstrap', help='bootstrap address')
parser.add_argument('-d', '--daemonize', help='daemonize process', action='store_true')
group = parser.add_mutually_exclusive_group()
group.add_argument('-d', '--daemonize', help='daemonize process', action='store_true')
group.add_argument('-s', '--service', help='service mode (not forking)', action='store_true')
parser.add_argument('-l', '--log', help='log file prefix')
args = parser.parse_args()
if args.bootstrap:
args.bootstrap = urlparse('dht://'+args.bootstrap)
net = NodeCluster(iface=args.iface, port=args.port, bootstrap=args.bootstrap, logfile=args.log)
net.resize(args.node_num)
if args.daemonize:
import daemon, lockfile
#pidfile=lockfile.FileLock('/var/run/dhtcluster.'+str(args.port))
with daemon.DaemonContext():
while True:
time.sleep(2)
# setup logging
if args.daemonize or args.service:
syslog = logging.handlers.SysLogHandler(address = '/dev/log')
syslog.setLevel(logging.DEBUG)
logger.setLevel(logging.DEBUG)
logger.addHandler(syslog)
else:
logging.basicConfig(stream=sys.stdout,level=logging.DEBUG)
# start cluster
net = NodeCluster(iface=args.iface, port=args.port, bootstrap=args.bootstrap, logfile=args.log)
# main loop
if args.daemonize:
import daemon
context = daemon.DaemonContext()
context.signal_map = {
signal.SIGHUP: 'terminate',
signal.SIGTERM: quit_signal,
signal.SIGINT: quit_signal,
signal.SIGQUIT: quit_signal
}
with context:
net.resize(args.node_num)
while net:
time.sleep(1)
elif args.service:
signal.signal(signal.SIGTERM, quit_signal)
signal.signal(signal.SIGINT, quit_signal)
signal.signal(signal.SIGQUIT, quit_signal)
net.resize(args.node_num)
while net:
time.sleep(1)
else:
net.resize(args.node_num)
ClusterShell(net).cmdloop()
except Exception as e:
traceback.print_tb(e.__traceback__)
print(type(e).__name__+':', e, file=sys.stderr)
except Exception:
logger.error("Exception ", exc_info=1)
finally:
if net:
net.resize(0)
clean_quit()
logger.warning("Ending node cluster")
for handler in logger.handlers:
handler.close()
logger.removeHandler(handler)
+17 -5
View File
@@ -32,23 +32,35 @@ class DhtServer(resource.Resource):
self.node.bootstrap(b_url.hostname, str(b_url.port) if b_url.port else '4222')
def render_GET(self, req):
uri = req.uri[1:]
h = dht.InfoHash(uri) if len(uri) == 40 else dht.InfoHash.get(uri.decode())
print('GET', '"'+uri.decode()+'"', h)
res = self.node.get(h)
uri = req.uri[1:].decode().rsplit('?', 1)[0]
h = dht.InfoHash(uri.encode()) if len(uri) == 40 else dht.InfoHash.get(uri)
w = dht.Where('WHERE '+''.join(k.decode()+'='+req.args[k][0].decode()+','
for k in req.args.keys()
if k in [b'id', b'user_type', b'value_type', b'owner', b'seq'])[:-1])
print('GET', '"'+uri+'"', h, w)
res = self.node.get(h, where=w)
req.setHeader(b"content-type", b"application/json")
return json.dumps({'{:x}'.format(v.id):{'base64':base64.b64encode(v.data).decode()} for v in res}).encode()
def render_POST(self, req):
uri = req.uri[1:]
data = req.args[b'data'][0] if b'data' in req.args else None
user_type = req.args[b'user_type'][0].decode() if b'user_type' in req.args else ""
try:
vid = int(req.args[b'id'][0].decode()) if b'id' in req.args else 0
except ValueError:
vid = 0
if not data and b'base64' in req.args:
data = base64.b64decode(req.args[b'base64'][0])
h = dht.InfoHash(uri) if len(uri) == 40 else dht.InfoHash.get(uri.decode())
print('POST', h, data)
req.setHeader(b"content-type", b"application/json")
if data:
self.node.put(h, dht.Value(data))
v = dht.Value(data)
if vid != 0:
v.id = vid
v.user_type = user_type
self.node.put(h, v)
return json.dumps({'success':True}).encode()
else:
req.setResponseCode(400)
+127 -91
View File
@@ -20,7 +20,7 @@ from pprint import pprint
from math import cos, sin, pi
import urllib3
import gzip
import asyncio
import queue
sys.path.append('..')
from opendht import *
@@ -37,14 +37,25 @@ import GeoIP
http = urllib3.PoolManager()
run = True
done = 0
all_nodes = NodeSet()
nodes_ip4s = {}
nodes_ip6s = {}
lats = []
lons = []
cities=[]
xys = []
colors = []
all_lines = []
points = []
not_found = []
plt.ion()
plt.figaspect(2.)
fig, axes = plt.subplots(2, 1)
fig.set_size_inches(12,16,forward=True)
fig.set_size_inches(8,16,forward=True)
fig.tight_layout()
fig.canvas.set_window_title('OpenDHT scanner')
@@ -70,6 +81,15 @@ exitbtn = Button(exitax, 'Exit')
reloadax = plt.axes([0.92, 0.90, 0.07, 0.04])
button = Button(reloadax, 'Reload')
collection = None
infos = [ringx.text(1.2, -0.8, ""),
ringx.text(1.2, -0.9, "")]
def exitcb(arg):
global run
run = False
exitbtn.on_clicked(exitcb)
def check_dl(fname, url):
if os.path.isfile(fname):
return
@@ -92,35 +112,35 @@ r = DhtRunner()
r.run(port=4112)
r.bootstrap("bootstrap.ring.cx", "4222")
all_lines = []
plt.pause(1)
plt.pause(2)
loop = asyncio.get_event_loop()
q = queue.Queue()
def step(cur_h, cur_depth):
global done, all_nodes, all_lines
global done
done += 1
a = 2.*pi*cur_h.toFloat()
b = a + 2.*pi/(2**(cur_depth))
arc = []
lines = []
q.put((stepUi, (cur_h, cur_depth, arc, lines)))
print("step", cur_h, cur_depth)
arc = ringx.add_patch(mpatches.Wedge([0.,0,], 1., a*180/pi, b*180/pi, fill=True, color="blue", alpha=0.5))
lines = ringx.plot([0, cos(a)], [0, sin(a)], 'k-', lw=1.2)
all_lines.extend(lines)
r.get(cur_h, gcb, lambda d, nodes: loop.call_soon_threadsafe(nextstep, cur_h, cur_depth, d, nodes, arc, lines))
r.get(cur_h, gcb, lambda d, nodes: stepdone(cur_h, cur_depth, d, nodes, arc, lines))
def nextstep(cur_h, cur_depth, ok, nodes, arc=None, lines=[]):
global done, all_nodes
if arc:
arc.remove()
del arc
def stepdone(cur_h, cur_depth, d, nodes, arc, lines):
#print("stepdone", cur_h, cur_depth)
q.put((nextstepUi, (nodes, arc, lines)))
nextstep(cur_h, cur_depth, d, nodes)
def nextstep(cur_h, cur_depth, ok, nodes):
global done
if nodes:
for l in lines:
l.set_color('#444444')
snodes = NodeSet()
snodes.extend(nodes)
all_nodes.extend(nodes)
depth = min(8, InfoHash.commonBits(snodes.first(), snodes.last())+6)
commonBits = 0
if len(nodes) > 1:
snodes = NodeSet()
snodes.extend(nodes)
commonBits = InfoHash.commonBits(snodes.first(), snodes.last())
depth = min(8, commonBits+6)
if cur_depth < depth:
for b in range(cur_depth, depth):
new_h = InfoHash(cur_h.toString());
@@ -130,14 +150,62 @@ def nextstep(cur_h, cur_depth, ok, nodes, arc=None, lines=[]):
print("step done with no nodes", ok, cur_h.toString().decode())
done -= 1
run = True
def exitcb(arg):
global run
run = False
exitbtn.on_clicked(exitcb)
def stepUi(cur_h, cur_depth, arc, lines):
global all_lines
a = 2.*pi*cur_h.toFloat()
b = a + 2.*pi/(2**(cur_depth))
arc.append(ringx.add_patch(mpatches.Wedge([0.,0,], 1., a*180/pi, b*180/pi, fill=True, color="blue", alpha=0.5)))
lines.extend(ringx.plot([0, cos(a)], [0, sin(a)], 'k-', lw=1.2))
all_lines.extend(lines)
def nextstepUi(nodes, arc=None, lines=[]):
for a in arc:
if a:
a.remove()
del a
for l in lines:
l.set_color('#aaaaaa')
if nodes:
appendNodes(nodes)
def appendNodes(nodes):
global all_nodes
for n in nodes:
if all_nodes.insert(n):
appendNewNode(n)
def appendNewNode(n):
global nodes_ip4s, nodes_ip6s, colors, xys
addr = b':'.join(n.getNode().getAddr().split(b':')[0:-1]).decode()
colors.append('red' if n.getNode().isExpired() else 'blue')
node_val = n.getId().toFloat()
xys.append((cos(node_val*2*pi), sin(node_val*2*pi)))
georecord = None
if addr[0] == '[':
addr = addr[1:-1]
if addr in nodes_ip6s:
nodes_ip6s[addr][1] += 1
else:
georecord = gi6.record_by_name_v6(addr)
nodes_ip6s[addr] = [n, 1, georecord]
else:
if addr in nodes_ip4s:
nodes_ip4s[addr][1] += 1
else:
georecord = gi.record_by_name(addr)
nodes_ip4s[addr] = [n, 1, georecord]
if georecord:
appendMapPoint(georecord)
def appendMapPoint(res):
global lons, lats, cities
lons.append(res['longitude'])
lats.append(res['latitude'])
cities.append(res['city'] if res['city'] else (str(int(res['latitude']))+'-'+str(int(res['longitude']))))
def restart(arg):
global collection, all_lines, all_nodes, points, done
global collection, all_lines, all_nodes, points, done, nodes_ip4s, nodes_ip6s, lats, lons, cities, xys, colors
if done:
return
for l in all_lines:
@@ -145,6 +213,13 @@ def restart(arg):
del l
all_lines = []
all_nodes = NodeSet()
nodes_ip4s = {}
nodes_ip6s = {}
lats = []
lons = []
cities=[]
xys = []
colors = []
if collection:
collection.remove()
del collection
@@ -160,67 +235,20 @@ def restart(arg):
step(start_h, 0)
plt.draw()
collection = None
points = []
not_found = []
infos = [ringx.text(1.2, -0.8, ""),
ringx.text(1.2, -0.9, "")]
def num_nodes(node_set):
return sorted([x for x in node_set.items()], key=lambda ip: ip[1][1])
def generate_set():
node_ipv4 = {}
node_ipv6 = {}
for n in all_nodes:
addr = b''.join(n.getNode().getAddr().split(b':')[0:-1]).decode()
if addr[0] == '[':
addr = addr[1:-1]
if addr in node_ipv6:
node_ipv6[addr][1] += 1
else:
node_ipv6[addr] = [n, 1, gi6.record_by_name_v6(addr)]
else:
if addr in node_ipv4:
node_ipv4[addr][1] += 1
else:
node_ipv4[addr] = [n, 1, gi.record_by_name(addr)]
return num_nodes(node_ipv4), num_nodes(node_ipv6)
def update_plot():
global done, m, collection, not_found, points
#print("update_plot", done)
global m, collection, points
for p in points:
p.remove()
del p
points = []
lats = []
lons = []
cities=[]
colors=[]
not_found.clear()
ip4s, ip6s = generate_set()
ares = []
for addr, n in ip4s:
ares.append((addr, n[0].getNode(), n[2]))
for addr, n in ip6s:
ares.append((addr, n[0].getNode(), n[2]))
for r in ares:
res = r[2]
n = r[1]
if res:
lats.append(res['latitude'])
lons.append(res['longitude'])
cities.append(res['city'] if res['city'] else (str(int(res['latitude']))+'-'+str(int(res['longitude']))))
colors.append('red' if n.isExpired() else 'blue')
else:
not_found.append(r[0])
x,y = m(lons,lats)
points.extend(m.plot(x,y,'bo'))
for name, xpt, ypt in zip(cities, x, y):
points.append(mpx.text(xpt+50000, ypt+50000, name))
node_val = [n.getId().toFloat() for n in all_nodes]
xys = [(cos(d*2*pi), sin(d*2*pi)) for d in node_val]
if collection:
collection.remove()
del collection
@@ -229,9 +257,21 @@ def update_plot():
int(fig.dpi), 6, sizes=(10,), facecolors=colors,
offsets = xys, transOffset = ringx.transData))
node_ip4s, node_ip6s = generate_set()
infos[0].set_text("{} different IPv4s".format(len(node_ip4s)))
infos[1].set_text("{} different IPv6s".format(len(node_ip6s)))
infos[0].set_text("{} different IPv4s".format(len(nodes_ip4s)))
infos[1].set_text("{} different IPv6s".format(len(nodes_ip6s)))
def d(arg):
pass
def handle_tasks():
while True:
try:
f = q.get_nowait()
f[0](*f[1])
q.task_done()
except Exception as e:
break;
update_plot()
if run:
# start first step
@@ -239,15 +279,9 @@ if run:
start_h.setBit(159, 1)
step(start_h, 0)
def d(arg):
pass
while run:
while run and done > 0:
loop.stop()
loop.run_forever()
update_plot()
plt.draw()
handle_tasks()
plt.pause(.5)
if not run:
@@ -255,13 +289,14 @@ while run:
button.on_clicked(restart)
node_ip4s, node_ip6s = generate_set()
node_ip4s = num_nodes(nodes_ip4s)
node_ip6s = num_nodes(nodes_ip6s)
print(all_nodes.size(), " nodes found")
print(all_nodes)
print(len(not_found), " nodes not geolocalized")
for n in not_found:
print(n)
#print(len(not_found), " nodes not geolocalized")
#for n in not_found:
# print(n)
print('')
print(len(node_ip4s), " different IPv4s :")
for ip in node_ip4s:
@@ -271,6 +306,7 @@ while run:
for ip in node_ip6s:
print(ip[0], ":", str(ip[1][1]), "nodes", ("(" + ip[1][2]['city'] + ")") if ip[1][2] and ip[1][2]['city'] else "")
handle_tasks()
while run and done == 0:
plt.pause(.5)
button.on_clicked(d)
+5 -4
View File
@@ -5,6 +5,11 @@ libopendht_la_LIBADD = @Argon2_LIBS@ @GnuTLS_LIBS@ @Nettle_LIBS@
libopendht_la_LDFLAGS = @LDFLAGS@ @Argon2_LDFLAGS@
libopendht_la_SOURCES = \
dht.cpp \
storage.h \
listener.h \
request.h \
search.h \
parsed_message.h \
node_cache.cpp \
callbacks.cpp \
routing_table.cpp \
@@ -49,9 +54,6 @@ nobase_include_HEADERS = \
../include/opendht/rng.h \
../include/opendht/indexation/pht.h
nobase_noinst_HEADERS = \
../include/opendht/request.h
clean-local:
rm -rf libargon2.la
@@ -81,4 +83,3 @@ noinst_HEADERS = \
@top_builddir@/argon2/src/encoding.h \
@top_builddir@/argon2/src/thread.h
endif
+26 -14
View File
@@ -116,7 +116,7 @@ bool aesKeySizeGood(size_t key_size)
#define GCM_DIGEST_SIZE GCM_BLOCK_SIZE
#endif
Blob aesEncrypt(const Blob& data, const Blob& key)
Blob aesEncrypt(const Blob& data, const SecureBlob& key)
{
if (not aesKeySizeGood(key.size()))
throw DecryptError("Wrong key size");
@@ -127,7 +127,7 @@ Blob aesEncrypt(const Blob& data, const Blob& key)
std::generate_n(ret.begin(), GCM_IV_SIZE, std::bind(rand_byte, std::ref(rdev)));
}
struct gcm_aes_ctx aes;
gcm_aes_set_key(&aes, key.size(), key.data());
gcm_aes_set_key(&aes, key.size(), key.makeInsecure().data());
gcm_aes_set_iv(&aes, GCM_IV_SIZE, ret.data());
gcm_aes_update(&aes, data.size(), data.data());
@@ -139,13 +139,13 @@ Blob aesEncrypt(const Blob& data, const Blob& key)
Blob aesEncrypt(const Blob& data, const std::string& password)
{
Blob salt;
Blob key = stretchKey(password, salt, 256 / 8);
SecureBlob key = stretchKey(password, salt, 256 / 8);
Blob encrypted = aesEncrypt(data, key);
encrypted.insert(encrypted.begin(), salt.begin(), salt.end());
return encrypted;
}
Blob aesDecrypt(const Blob& data, const Blob& key)
Blob aesDecrypt(const Blob& data, const SecureBlob& key)
{
if (not aesKeySizeGood(key.size()))
throw DecryptError("Wrong key size");
@@ -185,12 +185,12 @@ Blob aesDecrypt(const Blob& data, const std::string& password)
if (data.size() <= PASSWORD_SALT_LENGTH)
throw DecryptError("Wrong data size");
Blob salt {data.begin(), data.begin()+PASSWORD_SALT_LENGTH};
Blob key = stretchKey(password, salt, 256/8);
SecureBlob key = stretchKey(password, salt, 256/8);
Blob encrypted {data.begin()+PASSWORD_SALT_LENGTH, data.end()};
return aesDecrypt(encrypted, key);
}
Blob stretchKey(const std::string& password, Blob& salt, size_t key_length)
SecureBlob stretchKey(const std::string& password, Blob& salt, size_t key_length)
{
if (salt.empty()) {
salt.resize(PASSWORD_SALT_LENGTH);
@@ -506,11 +506,7 @@ PublicKey::encrypt(const Blob& data) const
unsigned aes_key_sz = aesKeySize(max_block_sz);
if (aes_key_sz == 0)
throw CryptoException("Key is not long enough for AES128");
Blob key(aes_key_sz);
{
crypto::random_device rdev;
std::generate_n(key.begin(), key.size(), std::bind(rand_byte, std::ref(rdev)));
}
auto key = SecureBlob::getRandom(aes_key_sz);
auto data_encrypted = aesEncrypt(data, key);
Blob ret;
@@ -529,7 +525,8 @@ PublicKey::getId() const
return {};
InfoHash id;
size_t sz = id.size();
if (auto err = gnutls_pubkey_get_key_id(pk, 0, id.data(), &sz))
constexpr int flags = (id.size() == 32) ? GNUTLS_KEYID_USE_SHA256 : 0;
if (auto err = gnutls_pubkey_get_key_id(pk, flags, id.data(), &sz))
throw CryptoException(std::string("Can't get public key ID: ") + gnutls_strerror(err));
if (sz != id.size())
throw CryptoException("Can't get public key ID: wrong output length.");
@@ -882,6 +879,19 @@ generateIdentity(const std::string& name, Identity ca, unsigned key_length) {
return generateIdentity(name, ca, key_length, !ca.first || !ca.second);
}
Identity
generateEcIdentity(const std::string& name, crypto::Identity ca, bool is_ca)
{
auto key = std::make_shared<PrivateKey>(PrivateKey::generateEC());
auto cert = std::make_shared<Certificate>(Certificate::generate(*key, name, ca, is_ca));
return {std::move(key), std::move(cert)};
}
Identity
generateEcIdentity(const std::string& name, Identity ca) {
return generateEcIdentity(name, ca, !ca.first || !ca.second);
}
Certificate
Certificate::generate(const PrivateKey& key, const std::string& name, Identity ca, bool is_ca)
{
@@ -916,16 +926,18 @@ Certificate::generate(const PrivateKey& key, const std::string& name, Identity c
gnutls_x509_crt_set_serial(cert, &cert_serial, sizeof(cert_serial));
}
unsigned key_usage = GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_DATA_ENCIPHERMENT;
unsigned key_usage = 0;
if (is_ca) {
gnutls_x509_crt_set_ca_status(cert, 1);
key_usage |= GNUTLS_KEY_KEY_CERT_SIGN | GNUTLS_KEY_CRL_SIGN;
} else {
key_usage |= GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_DATA_ENCIPHERMENT;
}
gnutls_x509_crt_set_key_usage(cert, key_usage);
if (ca.first && ca.second) {
if (not ca.second->isCA()) {
// Signing certificate must be CA.
std::cerr << "Error: signing certificate must be CA." << std::endl;
return {};
}
//if (gnutls_x509_crt_sign2(cert, ca.second->cert, ca.first->x509_key, get_dig(cert), 0) != GNUTLS_E_SUCCESS) {
+10 -11
View File
@@ -27,14 +27,14 @@ std::ostream& operator<< (std::ostream& s, const DhtMessage& v)
}
bool
DhtMessage::storePolicy(InfoHash h, std::shared_ptr<Value>& v, InfoHash f, const sockaddr* fa, socklen_t fas)
DhtMessage::storePolicy(InfoHash h, std::shared_ptr<Value>& v, const InfoHash& f, const SockAddr& sa)
{
try {
auto msg = unpackMsg<DhtMessage>(v->data);
if (msg.service.empty())
return false;
} catch (const std::exception& e) {}
return ValueType::DEFAULT_STORE_POLICY(h, v, f, fa, fas);
return ValueType::DEFAULT_STORE_POLICY(h, v, f, sa);
}
Value::Filter
@@ -54,12 +54,11 @@ DhtMessage::ServiceFilter(std::string s)
std::ostream& operator<< (std::ostream& s, const IpServiceAnnouncement& v)
{
s << "Peer: ";
s << "port " << v.getPort();
if (v.ss.ss_family == AF_INET || v.ss.ss_family == AF_INET6) {
if (v.addr) {
s << "Peer: ";
s << "port " << v.getPort();
char hbuf[NI_MAXHOST];
if (getnameinfo((sockaddr*)&v.ss, sizeof(v.ss), hbuf, sizeof(hbuf), nullptr, 0, NI_NUMERICHOST) == 0) {
if (getnameinfo(v.addr.get(), v.addr.getLength(), hbuf, sizeof(hbuf), nullptr, 0, NI_NUMERICHOST) == 0) {
s << " addr " << std::string(hbuf, strlen(hbuf));
}
}
@@ -67,17 +66,17 @@ std::ostream& operator<< (std::ostream& s, const IpServiceAnnouncement& v)
}
bool
IpServiceAnnouncement::storePolicy(InfoHash h, std::shared_ptr<Value>& v, InfoHash f, const sockaddr* from, socklen_t fromlen)
IpServiceAnnouncement::storePolicy(InfoHash h, std::shared_ptr<Value>& v, const InfoHash& f, const SockAddr& sa)
{
try {
auto msg = unpackMsg<IpServiceAnnouncement>(v->data);
if (msg.getPort() == 0)
return false;
IpServiceAnnouncement sa_addr {from, fromlen};
IpServiceAnnouncement sa_addr {sa};
sa_addr.setPort(msg.getPort());
// argument v is modified (not the value).
v = std::make_shared<Value>(IpServiceAnnouncement::TYPE, sa_addr, v->id);
return ValueType::DEFAULT_STORE_POLICY(h, v, f, from, fromlen);
return ValueType::DEFAULT_STORE_POLICY(h, v, f, sa);
} catch (const std::exception& e) {}
return false;
}
@@ -86,7 +85,7 @@ const ValueType DhtMessage::TYPE(1, "DHT message", std::chrono::minutes(5), DhtM
const ValueType IpServiceAnnouncement::TYPE(2, "Internet Service Announcement", std::chrono::minutes(15), IpServiceAnnouncement::storePolicy);
const ValueType ImMessage::TYPE = {3, "IM message", std::chrono::minutes(5)};
const ValueType TrustRequest::TYPE = {4, "Certificate trust request", std::chrono::hours(24*7)};
const ValueType IceCandidates::TYPE = {5, "ICE candidates", std::chrono::minutes(5)};
const ValueType IceCandidates::TYPE = {5, "ICE candidates", std::chrono::minutes(1)};
const std::array<std::reference_wrapper<const ValueType>, 5>
DEFAULT_TYPES
+314 -1522
View File
File diff suppressed because it is too large. Load diff
+90 -236
View File
@@ -20,98 +20,32 @@
#include "dhtrunner.h"
#include "securedht.h"
#ifndef _WIN32
#include <unistd.h>
#else
#include <io.h>
#endif
#ifndef _WIN32
#include <sys/socket.h>
#else
#include <winsock2.h>
#include <ws2tcpip.h>
#define close(x) closesocket(x)
#endif
namespace dht {
constexpr std::chrono::seconds DhtRunner::BOOTSTRAP_PERIOD;
DhtRunner::DhtRunner() : dht_()
{
#ifdef _WIN32
WSADATA wsd;
if (WSAStartup(MAKEWORD(2,2), &wsd) != 0)
throw DhtException("Can't initialize Winsock2");
#endif
uv_async_.data = this;
uv_async_init(uv_loop_.get(), &uv_async_, &DhtRunner::loop_callback);
}
DhtRunner::~DhtRunner()
{
join();
#ifdef _WIN32
WSACleanup();
#endif
}
void
DhtRunner::run(in_port_t port, DhtRunner::Config config)
{
sockaddr_in sin4;
std::fill_n((uint8_t*)&sin4, sizeof(sin4), 0);
sin4.sin_family = AF_INET;
sin4.sin_port = htons(port);
sockaddr_in6 sin6;
std::fill_n((uint8_t*)&sin6, sizeof(sin6), 0);
sin6.sin6_family = AF_INET6;
sin6.sin6_port = htons(port);
run(&sin4, &sin6, config);
}
void
DhtRunner::run(const char* ip4, const char* ip6, const char* service, DhtRunner::Config config)
{
auto res4 = getAddrInfo(ip4, service);
auto res6 = getAddrInfo(ip6, service);
run(res4.empty() ? nullptr : (sockaddr_in*) &res4.front().first,
res6.empty() ? nullptr : (sockaddr_in6*)&res6.front().first, config);
}
void
DhtRunner::run(const sockaddr_in* local4, const sockaddr_in6* local6, DhtRunner::Config config)
DhtRunner::run(DhtRunner::Config config)
{
if (running)
return;
if (rcv_thread.joinable())
rcv_thread.join();
running = true;
doRun(local4, local6, config.dht_config);
doRun(config.dht_config);
if (not config.threaded)
return;
dht_thread = std::thread([this]() {
while (running) {
std::unique_lock<std::mutex> lk(dht_mtx);
auto wakeup = loop_();
cv.wait_until(lk, wakeup, [this]() {
if (not running)
return true;
{
std::lock_guard<std::mutex> lck(sock_mtx);
if (not rcv.empty())
return true;
}
{
std::lock_guard<std::mutex> lck(storage_mtx);
if (not pending_ops_prio.empty())
return true;
auto s = getStatus();
if (not pending_ops.empty() and (s == NodeStatus::Connected or (s == NodeStatus::Disconnected and not bootstraping)))
return true;
}
return false;
});
}
uv_loop_.run();
});
}
@@ -121,21 +55,19 @@ DhtRunner::shutdown(ShutdownCallback cb) {
pending_ops_prio.emplace([=](SecureDht& dht) mutable {
dht.shutdown(cb);
});
cv.notify_all();
uv_async_send(&uv_async_);
}
void
DhtRunner::join()
{
running = false;
cv.notify_all();
bootstrap_cv.notify_all();
uv_async_send(&uv_async_);
//bootstrap_cv.notify_all();
if (dht_thread.joinable())
dht_thread.join();
if (rcv_thread.joinable())
rcv_thread.join();
if (bootstrap_thread.joinable())
bootstrap_thread.join();
/*if (bootstrap_thread.joinable())
bootstrap_thread.join();*/
{
std::lock_guard<std::mutex> lck(storage_mtx);
@@ -145,10 +77,10 @@ DhtRunner::join()
{
std::lock_guard<std::mutex> lck(dht_mtx);
dht_.reset();
status4 = NodeStatus::Disconnected;
/*status4 = NodeStatus::Disconnected;
status6 = NodeStatus::Disconnected;
bound4 = {};
bound6 = {};
bound6 = {};*/
}
}
@@ -175,6 +107,14 @@ DhtRunner::getNodeId() const
return dht_->getNodeId();
}
void
DhtRunner::setOnStatusChanged(StatusCallback&& cb)
{
std::lock_guard<std::mutex> lck(dht_mtx);
if (not dht_)
throw std::runtime_error("dht is not running");
dht_->setOnStatusChanged(std::move(cb));
}
std::pair<size_t, size_t>
DhtRunner::getStoreSize() const {
@@ -303,10 +243,15 @@ DhtRunner::getPublicAddressStr(sa_family_t af)
{
auto addrs = getPublicAddress(af);
std::vector<std::string> ret(addrs.size());
std::transform(addrs.begin(), addrs.end(), ret.begin(), dht::printAddr);
std::transform(addrs.begin(), addrs.end(), ret.begin(), [](const SockAddr& a) { return a.toString(); });
return ret;
}
SockAddr
DhtRunner::getBound() const {
return dht_->getBoundAddr();
}
void
DhtRunner::registerCertificate(std::shared_ptr<crypto::Certificate> cert) {
std::lock_guard<std::mutex> lck(dht_mtx);
@@ -318,42 +263,12 @@ DhtRunner::setLocalCertificateStore(CertificateStoreQuery&& query_method) {
dht_->setLocalCertificateStore(std::forward<CertificateStoreQuery>(query_method));
}
time_point
void
DhtRunner::loop_()
{
if (!dht_)
return {};
decltype(pending_ops) ops {};
{
std::lock_guard<std::mutex> lck(storage_mtx);
auto s = getStatus();
ops = (pending_ops_prio.empty() && (s == NodeStatus::Connected or (s == NodeStatus::Disconnected and not bootstraping))) ?
std::move(pending_ops) : std::move(pending_ops_prio);
}
while (not ops.empty()) {
ops.front()(*dht_);
ops.pop();
}
time_point wakeup {};
decltype(rcv) received {};
{
std::lock_guard<std::mutex> lck(sock_mtx);
// move to stack
received = std::move(rcv);
}
if (not received.empty()) {
for (const auto& pck : received) {
auto& buf = pck.first;
auto& from = pck.second;
wakeup = dht_->periodic(buf.data(), buf.size()-1, from);
}
received.clear();
} else {
wakeup = dht_->periodic(nullptr, 0, nullptr, 0);
}
return;
/*
NodeStatus nstatus4 = dht_->getStatus(AF_INET);
NodeStatus nstatus6 = dht_->getStatus(AF_INET6);
if (nstatus4 != status4 || nstatus6 != status6) {
@@ -371,102 +286,24 @@ DhtRunner::loop_()
if (statusCb)
statusCb(status4, status6);
}
return wakeup;
*/
decltype(pending_ops) ops {};
{
std::lock_guard<std::mutex> lck(storage_mtx);
auto s = dht_->getStatus();
ops = (pending_ops_prio.empty() && (s == NodeStatus::Connected or (s == NodeStatus::Disconnected and not bootstraping))) ?
std::move(pending_ops) : std::move(pending_ops_prio);
}
while (not ops.empty()) {
ops.front()(*dht_);
ops.pop();
}
}
void
DhtRunner::doRun(const sockaddr_in* sin4, const sockaddr_in6* sin6, SecureDht::Config config)
DhtRunner::doRun(SecureDht::Config config)
{
dht_.reset();
int s4 = -1,
s6 = -1;
bound4 = {};
if (sin4) {
s4 = socket(PF_INET, SOCK_DGRAM, 0);
if(s4 >= 0) {
int rc = bind(s4, (sockaddr*)sin4, sizeof(sockaddr_in));
if(rc < 0)
throw DhtException("Can't bind IPv4 socket on " + dht::print_addr((sockaddr*)sin4, sizeof(sockaddr_in)));
bound4.second = sizeof(bound4.first);
getsockname(s4, (sockaddr*)&bound4.first, &bound4.second);
}
}
#if 1
bound6 = {};
if (sin6) {
s6 = socket(PF_INET6, SOCK_DGRAM, 0);
if(s6 >= 0) {
int val = 1;
int rc = setsockopt(s6, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&val, sizeof(val));
if(rc < 0)
throw DhtException("Can't set IPV6_V6ONLY");
rc = bind(s6, (sockaddr*)sin6, sizeof(sockaddr_in6));
if(rc < 0)
throw DhtException("Can't bind IPv6 socket on " + dht::print_addr((sockaddr*)sin6, sizeof(sockaddr_in6)));
bound6.second = sizeof(bound6.first);
getsockname(s6, (sockaddr*)&bound6.first, &bound6.second);
}
}
#endif
dht_ = std::unique_ptr<SecureDht>(new SecureDht {s4, s6, config});
rcv_thread = std::thread([this,s4,s6]() {
try {
while (true) {
struct timeval tv {/*.tv_sec = */0, /*.tv_usec = */250000};
fd_set readfds;
FD_ZERO(&readfds);
if(s4 >= 0)
FD_SET(s4, &readfds);
if(s6 >= 0)
FD_SET(s6, &readfds);
int rc = select(s4 > s6 ? s4 + 1 : s6 + 1, &readfds, nullptr, nullptr, &tv);
if(rc < 0) {
if(errno != EINTR) {
perror("select");
std::this_thread::sleep_for( std::chrono::seconds(1) );
}
}
if(!running)
break;
if(rc > 0) {
std::array<uint8_t, 1024 * 64> buf;
SockAddr from;
from.second = sizeof(from.first);
if(s4 >= 0 && FD_ISSET(s4, &readfds))
rc = recvfrom(s4, (char*)buf.data(), buf.size(), 0, (struct sockaddr*)&from.first, &from.second);
else if(s6 >= 0 && FD_ISSET(s6, &readfds))
rc = recvfrom(s6, (char*)buf.data(), buf.size(), 0, (struct sockaddr*)&from.first, &from.second);
else
break;
if (rc > 0) {
{
std::lock_guard<std::mutex> lck(sock_mtx);
rcv.emplace_back(Blob {buf.begin(), buf.begin()+rc+1}, from);
}
cv.notify_all();
}
}
}
} catch (const std::exception& e) {
std::cerr << "Error in DHT networking thread: " << e.what() << std::endl;
}
if (s4 >= 0)
close(s4);
if (s6 >= 0)
close(s6);
});
dht_ = std::unique_ptr<SecureDht>(new SecureDht {uv_loop_.get(), config});
}
void
@@ -475,10 +312,11 @@ DhtRunner::get(InfoHash hash, GetCallback vcb, DoneCallback dcb, Value::Filter f
{
std::lock_guard<std::mutex> lck(storage_mtx);
pending_ops.emplace([=](SecureDht& dht) mutable {
std::cout << "DhtRunner dht.get " << hash << std::endl;
dht.get(hash, vcb, dcb, std::move(f), std::move(w));
});
}
cv.notify_all();
uv_async_send(&uv_async_);
}
void
@@ -493,7 +331,7 @@ void DhtRunner::query(const InfoHash& hash, QueryCallback cb, DoneCallback done_
dht.query(hash, cb, done_cb, std::move(q));
});
}
cv.notify_all();
uv_async_send(&uv_async_);
}
std::future<size_t>
@@ -506,7 +344,7 @@ DhtRunner::listen(InfoHash hash, GetCallback vcb, Value::Filter f, Where w)
ret_token->set_value(dht.listen(hash, vcb, std::move(f), std::move(w)));
});
}
cv.notify_all();
uv_async_send(&uv_async_);
return ret_token->get_future();
}
@@ -525,7 +363,7 @@ DhtRunner::cancelListen(InfoHash h, size_t token)
dht.cancelListen(h, token);
});
}
cv.notify_all();
uv_async_send(&uv_async_);
}
void
@@ -538,7 +376,7 @@ DhtRunner::cancelListen(InfoHash h, std::shared_future<size_t> token)
dht.cancelListen(h, tk);
});
}
cv.notify_all();
uv_async_send(&uv_async_);
}
void
@@ -551,7 +389,7 @@ DhtRunner::put(InfoHash hash, Value&& value, DoneCallback cb, time_point created
dht.put(hash, sv, cb, created, permanent);
});
}
cv.notify_all();
uv_async_send(&uv_async_);
}
void
@@ -563,7 +401,7 @@ DhtRunner::put(InfoHash hash, std::shared_ptr<Value> value, DoneCallback cb, tim
dht.put(hash, value, cb, created, permanent);
});
}
cv.notify_all();
uv_async_send(&uv_async_);
}
void
@@ -581,7 +419,7 @@ DhtRunner::cancelPut(const InfoHash& h , const Value::Id& id)
dht.cancelPut(h, id);
});
}
cv.notify_all();
uv_async_send(&uv_async_);
}
void
@@ -593,7 +431,7 @@ DhtRunner::putSigned(InfoHash hash, std::shared_ptr<Value> value, DoneCallback c
dht.putSigned(hash, value, cb);
});
}
cv.notify_all();
uv_async_send(&uv_async_);
}
void
@@ -617,7 +455,7 @@ DhtRunner::putEncrypted(InfoHash hash, InfoHash to, std::shared_ptr<Value> value
dht.putEncrypted(hash, to, value, cb);
});
}
cv.notify_all();
uv_async_send(&uv_async_);
}
void
@@ -632,6 +470,7 @@ DhtRunner::putEncrypted(const std::string& key, InfoHash to, Value&& value, Done
putEncrypted(InfoHash::get(key), to, std::forward<Value>(value), cb);
}
/*
void
DhtRunner::tryBootstrapContinuously()
{
@@ -689,7 +528,7 @@ DhtRunner::tryBootstrapContinuously()
}
} while (bootstraping);
});
}
}*/
std::vector<std::pair<sockaddr_storage, socklen_t>>
DhtRunner::getAddrInfo(const std::string& host, const std::string& service)
@@ -719,45 +558,60 @@ DhtRunner::getAddrInfo(const std::string& host, const std::string& service)
void
DhtRunner::bootstrap(const std::string& host, const std::string& service)
{
std::lock_guard<std::mutex> lck(bootstrap_mtx);
/*std::lock_guard<std::mutex> lck(bootstrap_mtx);
bootstrap_nodes_all.emplace_back(host, service);
bootstrap_nodes.emplace_back(host, service);
tryBootstrapContinuously();
tryBootstrapContinuously();*/
pending_ops_prio.emplace([=](SecureDht& dht) mutable {
dht.bootstrap(host, service);
});
uv_async_send(&uv_async_);
}
void
DhtRunner::clearBootstrap()
{
std::lock_guard<std::mutex> lck(bootstrap_mtx);
bootstrap_nodes_all.clear();
//std::lock_guard<std::mutex> lck(bootstrap_mtx);
//bootstrap_nodes_all.clear();
pending_ops_prio.emplace([=](SecureDht& dht) mutable {
dht.clearBootstrap();
});
uv_async_send(&uv_async_);
}
void
DhtRunner::bootstrap(const std::vector<std::pair<sockaddr_storage, socklen_t>>& nodes, DoneCallbackSimple&& cb)
DhtRunner::ping(const std::vector<std::pair<sockaddr_storage, socklen_t>>& nodes, DoneCallback&& cb)
{
std::lock_guard<std::mutex> lck(storage_mtx);
pending_ops_prio.emplace([=](SecureDht& dht) mutable {
auto rem = cb ? std::make_shared<std::pair<size_t, bool>>(nodes.size(), false) : nullptr;
struct GroupPing {
size_t remaining {0};
bool ok {false};
std::vector<Sp<Node>> nodes;
GroupPing(size_t s) : remaining(s) {}
};
Sp<GroupPing> rem = cb ? std::make_shared<GroupPing>(nodes.size()) : nullptr;
for (auto& node : nodes)
dht.pingNode((sockaddr*)&node.first, node.second, cb ? [rem,cb](bool ok) {
dht.pingNode(SockAddr((sockaddr*)&node.first, node.second), cb ? [rem,cb](bool ok, std::vector<Sp<Node>>&& nodes) {
auto& r = *rem;
r.first--;
r.second |= ok;
if (not r.first)
cb(r.second);
} : DoneCallbackSimple{});
r.remaining--;
r.ok |= ok;
r.nodes.insert(r.nodes.end(), nodes.begin(), nodes.end());
if (r.remaining == 0)
cb(r.ok, std::move(r.nodes));
} : DoneCallback{});
});
cv.notify_all();
uv_async_send(&uv_async_);
}
void
DhtRunner::bootstrap(const SockAddr& addr, DoneCallbackSimple&& cb)
DhtRunner::ping(const SockAddr& addr, DoneCallback&& cb)
{
std::lock_guard<std::mutex> lck(storage_mtx);
pending_ops_prio.emplace([addr,cb](SecureDht& dht) mutable {
dht.pingNode((const sockaddr*)&addr.first, addr.second, std::move(cb));
dht.pingNode(addr, std::move(cb));
});
cv.notify_all();
uv_async_send(&uv_async_);
}
void
@@ -770,7 +624,7 @@ DhtRunner::bootstrap(const std::vector<NodeExport>& nodes)
dht.insertNode(node);
});
}
cv.notify_all();
uv_async_send(&uv_async_);
}
void
@@ -782,7 +636,7 @@ DhtRunner::connectivityChanged()
dht.connectivityChanged();
});
}
cv.notify_all();
uv_async_send(&uv_async_);
}
void
@@ -793,7 +647,7 @@ DhtRunner::findCertificate(InfoHash hash, std::function<void(const std::shared_p
dht.findCertificate(hash, cb);
});
}
cv.notify_all();
uv_async_send(&uv_async_);
}
}
+29 -2
View File
@@ -65,8 +65,8 @@ InfoHash::getRandom()
InfoHash h;
crypto::random_device rdev;
std::uniform_int_distribution<uint32_t> rand_int;
auto a = reinterpret_cast<uint32_t*>(&(*h.begin()));
auto b = reinterpret_cast<uint32_t*>(&(*h.end()));
auto a = reinterpret_cast<uint32_t*>(h.data());
auto b = reinterpret_cast<uint32_t*>(h.data() + h.size());
std::generate(a, b, std::bind(rand_int, std::ref(rdev)));
return h;
}
@@ -121,4 +121,31 @@ std::istream& operator>> (std::istream& s, InfoHash& h)
return s;
}
void
NodeExport::msgpack_unpack(msgpack::object o)
{
if (o.type != msgpack::type::MAP)
throw msgpack::type_error();
if (o.via.map.size < 2)
throw msgpack::type_error();
if (o.via.map.ptr[0].key.as<std::string>() != "id")
throw msgpack::type_error();
if (o.via.map.ptr[1].key.as<std::string>() != "addr")
throw msgpack::type_error();
const auto& addr = o.via.map.ptr[1].val;
if (addr.type != msgpack::type::BIN)
throw msgpack::type_error();
if (addr.via.bin.size > sizeof(sockaddr_storage))
throw msgpack::type_error();
id.msgpack_unpack(o.via.map.ptr[0].val);
sslen = addr.via.bin.size;
std::copy_n(addr.via.bin.ptr, addr.via.bin.size, (char*)&ss);
}
std::ostream& operator<< (std::ostream& s, const NodeExport& h)
{
msgpack::pack(s, h);
return s;
}
}
+52
View File
@@ -0,0 +1,52 @@
/*
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#pragma once
#include "value.h"
#include "utils.h"
namespace dht {
/**
* Foreign nodes asking for updates about an InfoHash.
*/
struct Listener {
size_t sid;
time_point time;
Query query;
Listener(size_t sid, time_point t, Query&& q) : sid(sid), time(t), query(std::move(q)) {}
void refresh(size_t s, time_point t, Query&& q) {
sid = s;
time = t;
query = std::move(q);
}
};
/**
* A single "listen" operation data
*/
struct LocalListener {
Sp<Query> query;
Value::Filter filter;
GetCallback get_cb;
};
}
+36 -3
View File
@@ -19,6 +19,13 @@
#include "log.h"
#ifndef _WIN32
#include <syslog.h>
#endif
#include <fstream>
#include <chrono>
namespace dht {
namespace log {
@@ -37,9 +44,9 @@ printLog(std::ostream& s, char const *m, va_list args) {
using namespace std::chrono;
using log_precision = microseconds;
constexpr auto den = log_precision::period::den;
auto micro = duration_cast<log_precision>(steady_clock::now().time_since_epoch()).count();
s << "[" << std::setfill('0') << std::setw(6) << micro / den << "."
<< std::setfill('0') << std::setw(6) << micro % den << "]" << " ";
auto num = duration_cast<log_precision>(steady_clock::now().time_since_epoch()).count();
s << "[" << std::setfill('0') << std::setw(6) << num / den << "."
<< std::setfill('0') << std::setw(6) << num % den << "]" << " ";
// write log
s.write(buffer.data(), std::min((size_t) ret, buffer.size()));
@@ -77,6 +84,32 @@ enableFileLogging(dht::DhtRunner &dht, const std::string &path) {
);
}
OPENDHT_PUBLIC void
enableSyslog(dht::DhtRunner &dht, const char* name) {
#ifndef _WIN32
struct Syslog {
Syslog(const char* n) {
openlog(n, LOG_NDELAY, LOG_USER);
}
~Syslog() {
closelog();
}
};
// syslog is global. Existing instance must be reused.
static std::weak_ptr<Syslog> opened_logfile;
auto logfile = opened_logfile.lock();
if (not logfile) {
logfile = std::make_shared<Syslog>(name);
opened_logfile = logfile;
}
dht.setLoggers(
[logfile](char const *m, va_list args) { vsyslog(LOG_ERR, m, args); },
[logfile](char const *m, va_list args) { vsyslog(LOG_WARNING, m, args); },
[logfile](char const *m, va_list args) { vsyslog(LOG_INFO, m, args); }
);
#endif
}
void
disableLogging(dht::DhtRunner &dht) {
dht.setLoggers(dht::NOLOG, dht::NOLOG, dht::NOLOG);
+419 -548
View File
File diff suppressed because it is too large. Load diff
+76 -24
View File
@@ -20,6 +20,7 @@
#include "node.h"
#include "request.h"
#include "rng.h"
#include <sstream>
@@ -29,6 +30,14 @@ constexpr std::chrono::minutes Node::NODE_EXPIRE_TIME;
constexpr std::chrono::minutes Node::NODE_GOOD_TIME;
constexpr std::chrono::seconds Node::MAX_RESPONSE_TIME;
Node::Node(const InfoHash& id, const SockAddr& addr, const Sp<TcpSocket>& s, bool client)
: id(id), addr(addr), sock(s), is_client(client), sockets_()
{
crypto::random_device rd;
transaction_id = std::uniform_int_distribution<SocketId>{1}(rd);
}
/* This is our definition of a known-good node. */
bool
Node::isGood(time_point now) const
@@ -41,11 +50,9 @@ Node::isGood(time_point now) const
bool
Node::isPendingMessage() const
{
for (auto w : requests_) {
if (auto r = w.lock()) {
if (r->pending())
return true;
}
for (auto r : requests_) {
if (r.second->pending())
return true;
}
return false;
}
@@ -54,44 +61,61 @@ size_t
Node::getPendingMessageCount() const
{
size_t count {0};
for (auto w : requests_) {
if (auto r = w.lock()) {
if (r->pending())
count++;
}
for (auto r : requests_) {
if (r.second->pending())
count++;
}
return count;
}
void
Node::update(const SockAddr& new_addr)
Node::update(const SockAddr& new_addr, const Sp<TcpSocket>& s)
{
addr = new_addr;
if (not sock or (s and sock->isClosed() and not s->isClosed()))
sock = s;
}
/** To be called when a message was sent to the node */
void
Node::requested(std::shared_ptr<net::Request>& req)
Node::requested(const Sp<net::Request>& req)
{
requests_.emplace_back(req);
auto e = requests_.emplace(req->getTid(), req);
if (not e.second and req != e.first->second) {
// Should not happen !
// Try to handle this scenario as well as we can
e.first->second->setExpired();
e.first->second = req;
}
}
/** To be called when a message was received from the node.
Req should be true if the message was an aswer to a request we made*/
void
Node::received(time_point now, std::shared_ptr<net::Request> req)
Node::received(time_point now, const Sp<net::Request>& req)
{
time = now;
expired_ = false;
if (req) {
reply_time = now;
for (auto it = requests_.begin(); it != requests_.end();) {
auto r = it->lock();
if (not r or r == req)
it = requests_.erase(it);
else
++it;
}
requests_.erase(req->getTid());
}
}
Sp<net::Request>
Node::getRequest(const net::TransId& tid)
{
auto it = requests_.find(tid);
return it != requests_.end() ? it->second : nullptr;
}
void
Node::cancelRequest(const Sp<net::Request>& req)
{
if (req) {
req->cancel();
closeSocket(req->closeSocket());
requests_.erase(req->getTid());
}
}
@@ -99,13 +123,41 @@ void
Node::setExpired()
{
expired_ = true;
for (auto w : requests_) {
if (auto r = w.lock())
r->setExpired();
for (auto r : requests_) {
r.second->setExpired();
}
requests_.clear();
sockets_.clear();
}
SocketId
Node::openSocket(SocketCb&& cb)
{
if (++transaction_id == 0)
transaction_id = 1;
auto sock = Socket(std::move(cb));
auto s = sockets_.emplace(transaction_id, std::move(sock));
if (not s.second)
s.first->second = std::move(sock);
return transaction_id;
}
Socket*
Node::getSocket(SocketId id)
{
auto it = sockets_.find(id);
return it == sockets_.end() ? nullptr : &it->second;
}
void
Node::closeSocket(SocketId id)
{
if (id) {
sockets_.erase(id);
//DHT_LOG.w("Closing socket (tid: %d), %lu remaining", socket->id, sockets_.size());
}
}
std::string
Node::toString() const
+59 -28
View File
@@ -20,44 +20,47 @@
namespace dht {
std::shared_ptr<Node>
Sp<Node>
NodeCache::getNode(const InfoHash& id, sa_family_t family) {
return (family == AF_INET ? cache_4 : cache_6).getNode(id);
return cache(family).getNode(id);
}
std::shared_ptr<Node>
NodeCache::getNode(const InfoHash& id, const SockAddr& addr, time_point now, bool confirm) {
Sp<Node>
NodeCache::getNode(const InfoHash& id, const SockAddr& addr, const Sp<TcpSocket>& sock, time_point now, bool confirm, bool client) {
if (id == zeroes)
return std::make_shared<Node>(id, addr);
return (addr.getFamily() == AF_INET ? cache_4 : cache_6).getNode(id, addr, now, confirm);
return cache(addr.getFamily()).getNode(id, addr, sock, now, confirm, client);
}
std::vector<std::shared_ptr<Node>>
NodeCache::getCachedNodes(const InfoHash& id, sa_family_t sa_f, size_t count) {
const auto& c = (sa_f == AF_INET ? cache_4 : cache_6);
auto it_p = c.lower_bound(id),
it_n = it_p;
std::vector<Sp<Node>>
NodeCache::getCachedNodes(const InfoHash& id, sa_family_t sa_f, size_t count) const
{
const auto& c = cache(sa_f);
std::vector<std::shared_ptr<Node>> nodes;
std::vector<Sp<Node>> nodes;
nodes.reserve(std::min(c.size(), count));
NodeMap::const_iterator it;
auto dec_it = [&c](NodeMap::const_iterator& it) {
auto ret = it;
it = (it == c.cbegin()) ? c.cend() : std::prev(it);
return ret;
};
if (it_p != c.begin()) /* Create 2 separate iterator if we could */
--it_p;
auto it_p = c.lower_bound(id),
it_n = it_p;
if (not c.empty())
dec_it(it_p); /* Create 2 separate iterator if we could */
while (nodes.size() < count and (it_n != c.end() or it_p != c.end())) {
while (nodes.size() < count and (it_n != c.cend() or it_p != c.cend())) {
/* If one of the iterator is at the end, then take the other one
If they are both in middle of somewhere comapre both and take
the closest to the id. */
if (it_p == c.end()) it = it_n++;
else if (it_n == c.end()) it = it_p--;
else it = id.xorCmp(it_p->first, it_n->first) < 0 ? it_p-- : it_n++;
if (it == c.begin())
it_p = c.end();
if (it_p == c.cend()) it = it_n++;
else if (it_n == c.cend()) it = dec_it(it_p);
else it = id.xorCmp(it_p->first, it_n->first) < 0 ? dec_it(it_p) : it_n++;
if (auto n = it->second.lock())
if ( not n->isExpired() )
if ( not n->isExpired() and not n->isClient() )
nodes.emplace_back(std::move(n));
}
@@ -71,11 +74,22 @@ NodeCache::clearBadNodes(sa_family_t family)
clearBadNodes(AF_INET);
clearBadNodes(AF_INET6);
} else {
(family == AF_INET ? cache_4 : cache_6).clearBadNodes();
cache(family).clearBadNodes();
}
}
std::shared_ptr<Node>
void
NodeCache::closeAll(sa_family_t family)
{
if (family == 0) {
closeAll(AF_INET);
closeAll(AF_INET6);
} else {
(family == AF_INET ? cache_4 : cache_6).closeAll();
}
}
Sp<Node>
NodeCache::NodeMap::getNode(const InfoHash& id)
{
auto wn = find(id);
@@ -87,16 +101,16 @@ NodeCache::NodeMap::getNode(const InfoHash& id)
return {};
}
std::shared_ptr<Node>
NodeCache::NodeMap::getNode(const InfoHash& id, const SockAddr& addr, time_point now, bool confirm)
Sp<Node>
NodeCache::NodeMap::getNode(const InfoHash& id, const SockAddr& addr, const Sp<TcpSocket>& sock, time_point now, bool confirm, bool client)
{
auto it = emplace(id, std::weak_ptr<Node>{});
auto node = it.first->second.lock();
if (not node) {
node = std::make_shared<Node>(id, addr);
node = std::make_shared<Node>(id, addr, sock, client);
it.first->second = node;
} else if (confirm || node->time < now - Node::NODE_EXPIRE_TIME) {
node->update(addr);
} else if (confirm or node->isOld(now)) {
node->update(addr, sock);
}
return node;
}
@@ -113,4 +127,21 @@ NodeCache::NodeMap::clearBadNodes() {
}
}
void
NodeCache::NodeMap::closeAll()
{
for (auto it = cbegin(); it != cend();) {
if (auto n = it->second.lock()) {
if (n->sock) {
n->sock->close();
n->sock.reset();
}
++it;
} else {
erase(it++);
}
}
}
}
+309
View File
@@ -0,0 +1,309 @@
/*
* Copyright (C) 2017 Savoir-faire Linux Inc.
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#pragma once
#include "infohash.h"
#include "sockaddr.h"
#include "net.h"
#include <map>
namespace dht {
namespace net {
enum class MessageType {
Error = 0,
Reply,
Ping,
FindNode,
GetValues,
AnnounceValue,
Refresh,
Listen,
ValueData
};
struct ParsedMessage {
MessageType type;
/* Node ID of the sender */
InfoHash id;
/* Network id */
NetId network {0};
/** Is a client node */
bool is_client {false};
/* hash for which values are requested */
InfoHash info_hash;
/* target id around which to find nodes */
InfoHash target;
/* transaction id */
TransId tid;
/* tid for packets going through request socket */
TransId socket_id;
/* security token */
Blob token;
/* the value id (announce confirmation) */
Value::Id value_id;
/* time when value was first created */
time_point created { time_point::max() };
/* IPv4 nodes in response to a 'find' request */
Blob nodes4_raw, nodes6_raw;
std::vector<Sp<Node>> nodes4, nodes6;
/* values to store or retreive request */
std::vector<Sp<Value>> values;
/* index for fields values */
std::vector<Sp<FieldValueIndex>> fields;
/** When part of the message header: {index -> (total size, {})}
* When part of partial value data: {index -> (offset, part_data)} */
std::map<unsigned, std::pair<unsigned, Blob>> value_parts;
/* query describing a filter to apply on values. */
Query query;
/* states if ipv4 or ipv6 request */
want_t want;
/* error code in case of error */
uint16_t error_code;
/* reported address by the distant node */
std::string ua;
SockAddr addr;
void msgpack_unpack(msgpack::object o);
bool append(const ParsedMessage& block);
bool complete();
};
bool
ParsedMessage::append(const ParsedMessage& block)
{
bool ret(false);
for (const auto& ve : block.value_parts) {
auto part_val = value_parts.find(ve.first);
if (part_val == value_parts.end()
|| part_val->second.second.size() >= part_val->second.first)
continue;
// TODO: handle out-of-order packets
if (ve.second.first != part_val->second.second.size()) {
//std::cout << "skipping out-of-order packet" << std::endl;
continue;
}
ret = true;
part_val->second.second.insert(part_val->second.second.end(),
ve.second.second.begin(),
ve.second.second.end());
}
return ret;
}
bool
ParsedMessage::complete()
{
for (auto& e : value_parts) {
//std::cout << "part " << e.first << ": " << e.second.second.size() << "/" << e.second.first << std::endl;
if (e.second.first > e.second.second.size())
return false;
}
for (auto& e : value_parts) {
msgpack::unpacked msg;
msgpack::unpack(msg, (const char*)e.second.second.data(), e.second.second.size());
values.emplace_back(std::make_shared<Value>(msg.get()));
}
return true;
}
void
ParsedMessage::msgpack_unpack(msgpack::object msg)
{
auto y = findMapValue(msg, "y");
auto r = findMapValue(msg, "r");
auto e = findMapValue(msg, "e");
auto v = findMapValue(msg, "p");
if (auto t = findMapValue(msg, "t"))
tid = unpackTid(*t);
if (auto rv = findMapValue(msg, "v"))
ua = rv->as<std::string>();
if (auto netid = findMapValue(msg, "n"))
network = netid->as<NetId>();
if (auto is_client_v = findMapValue(msg, "s"))
is_client = is_client_v->as<bool>();
std::string q;
if (auto rq = findMapValue(msg, "q")) {
if (rq->type != msgpack::type::STR)
throw msgpack::type_error();
q = rq->as<std::string>();
}
if (e)
type = MessageType::Error;
else if (r)
type = MessageType::Reply;
else if (v)
type = MessageType::ValueData;
else if (y and y->as<std::string>() != "q")
throw msgpack::type_error();
else if (q == "ping")
type = MessageType::Ping;
else if (q == "find")
type = MessageType::FindNode;
else if (q == "get")
type = MessageType::GetValues;
else if (q == "listen")
type = MessageType::Listen;
else if (q == "put")
type = MessageType::AnnounceValue;
else if (q == "refresh")
type = MessageType::Refresh;
else
throw msgpack::type_error();
if (type == MessageType::ValueData) {
if (v->type != msgpack::type::MAP)
throw msgpack::type_error();
for (size_t i = 0; i < v->via.map.size; ++i) {
auto& vdat = v->via.map.ptr[i];
auto o = findMapValue(vdat.val, "o");
auto d = findMapValue(vdat.val, "d");
if (not o or not d)
continue;
value_parts.emplace(vdat.key.as<unsigned>(), std::pair<size_t, Blob>(o->as<size_t>(), unpackBlob(*d)));
}
return;
}
auto a = findMapValue(msg, "a");
if (!a && !r && !e)
throw msgpack::type_error();
auto& req = a ? *a : (r ? *r : *e);
if (e) {
if (e->type != msgpack::type::ARRAY)
throw msgpack::type_error();
error_code = e->via.array.ptr[0].as<uint16_t>();
}
if (auto t = findMapValue(req, "sid"))
socket_id = unpackTid(*t);
if (auto rid = findMapValue(req, "id"))
id = {*rid};
if (auto rh = findMapValue(req, "h"))
info_hash = {*rh};
if (auto rtarget = findMapValue(req, "target"))
target = {*rtarget};
if (auto rquery = findMapValue(req, "q"))
query.msgpack_unpack(*rquery);
if (auto otoken = findMapValue(req, "token"))
token = unpackBlob(*otoken);
if (auto vid = findMapValue(req, "vid"))
value_id = vid->as<Value::Id>();
if (auto rnodes4 = findMapValue(req, "n4"))
nodes4_raw = unpackBlob(*rnodes4);
if (auto rnodes6 = findMapValue(req, "n6"))
nodes6_raw = unpackBlob(*rnodes6);
if (auto sa = findMapValue(req, "sa")) {
if (sa->type != msgpack::type::BIN)
throw msgpack::type_error();
auto l = sa->via.bin.size;
if (l == sizeof(in_addr)) {
addr.setFamily(AF_INET);
auto& a = addr.getIPv4();
std::fill_n((uint8_t*)&a, sizeof(a), 0);
a.sin_port = 0;
std::copy_n(sa->via.bin.ptr, l, (char*)&a.sin_addr);
} else if (l == sizeof(in6_addr)) {
addr.setFamily(AF_INET6);
auto& a = addr.getIPv6();
std::fill_n((uint8_t*)&a, sizeof(a), 0);
a.sin6_port = 0;
std::copy_n(sa->via.bin.ptr, l, (char*)&a.sin6_addr);
}
} else
addr = {};
if (auto rcreated = findMapValue(req, "c"))
created = from_time_t(rcreated->as<std::time_t>());
if (auto rvalues = findMapValue(req, "values")) {
if (rvalues->type != msgpack::type::ARRAY)
throw msgpack::type_error();
for (size_t i = 0; i < rvalues->via.array.size; i++) {
auto& packed_v = rvalues->via.array.ptr[i];
if (packed_v.type == msgpack::type::POSITIVE_INTEGER) {
// Skip oversize values with a small margin for header overhead
if (packed_v.via.u64 > MAX_VALUE_SIZE + 32)
continue;
value_parts.emplace(i, std::make_pair(packed_v.via.u64, Blob{}));
} else {
try {
values.emplace_back(std::make_shared<Value>(rvalues->via.array.ptr[i]));
} catch (const std::exception& e) {
//DHT_LOG_WARN("Error reading value: %s", e.what());
}
}
}
} else if (auto raw_fields = findMapValue(req, "fields")) {
if (auto rfields = findMapValue(*raw_fields, "f")) {
auto vfields = rfields->as<std::set<Value::Field>>();
if (auto rvalues = findMapValue(*raw_fields, "v")) {
if (rvalues->type != msgpack::type::ARRAY)
throw msgpack::type_error();
size_t val_num = rvalues->via.array.size / vfields.size();
for (size_t i = 0; i < val_num; ++i) {
try {
auto v = std::make_shared<FieldValueIndex>();
v->msgpack_unpack_fields(vfields, *rvalues, i*vfields.size());
fields.emplace_back(std::move(v));
} catch (const std::exception& e) { }
}
}
} else {
throw msgpack::type_error();
}
}
if (auto w = findMapValue(req, "w")) {
if (w->type != msgpack::type::ARRAY)
throw msgpack::type_error();
want = 0;
for (unsigned i=0; i<w->via.array.size; i++) {
auto& val = w->via.array.ptr[i];
try {
auto w = val.as<sa_family_t>();
if (w == AF_INET)
want |= WANT4;
else if(w == AF_INET6)
want |= WANT6;
} catch (const std::exception& e) {};
}
} else {
want = -1;
}
}
} /* namespace net */
} /* namespace dht */
+12 -26
View File
@@ -20,34 +20,15 @@
#pragma once
#include "net.h"
#include "value.h"
namespace dht {
struct Node;
namespace net {
class NetworkEngine;
struct ParsedMessage;
/*!
* @class Socket
* @brief Open route to a node for continous incoming packets.
* @details
* A socket lets a remote node send us continuous packets treated using a
* given callback. This is intended to provide an easy management of
* specific updates nodes can send. For e.g, this is used in the case of the
* "listen" operation for treating updates a node has for a given storage.
*/
struct Socket {
Socket() {}
Socket(std::shared_ptr<Node> node,
TransId id,
std::function<void(const std::shared_ptr<Node>&, ParsedMessage&&)> on_receive) :
node(node), id(id), on_receive(on_receive) {}
std::shared_ptr<Node> node;
TransId id;
std::function<void(const std::shared_ptr<Node>&, ParsedMessage&&)> on_receive {};
};
/*!
* @class Request
* @brief An atomic request destined to a node.
@@ -59,7 +40,7 @@ struct Socket {
struct Request {
friend class dht::net::NetworkEngine;
std::shared_ptr<Node> node {}; /* the node to whom the request is destined. */
Sp<Node> node {}; /* the node to whom the request is destined. */
time_point reply_time {time_point::min()}; /* time when we received the response to the request. */
enum class State
@@ -78,14 +59,19 @@ struct Request {
State getState() const { return state_; }
Request(State state = State::PENDING) : state_(state) {}
Request(TransId tid,
std::shared_ptr<Node> node,
Request(const TransId& tid,
Sp<Node> node,
Blob&& msg,
std::function<void(const Request&, ParsedMessage&&)> on_done,
std::function<void(const Request&, bool)> on_expired,
std::shared_ptr<Socket> socket = {}) :
uint32_t socket = 0) :
node(node), on_done(on_done), on_expired(on_expired), tid(tid), msg(std::move(msg)), socket(socket) { }
TransId getTid() const { return tid; }
uint32_t getSocket() { return socket; }
uint32_t closeSocket() { auto ret = socket; socket = 0; return ret; }
void setExpired() {
if (pending()) {
state_ = Request::State::EXPIRED;
@@ -132,7 +118,7 @@ private:
const TransId tid; /* the request id. */
Blob msg {}; /* the serialized message. */
std::shared_ptr<Socket> socket; /* the socket used for further reponses. */
uint32_t socket; /* the socket used for further reponses. */
};
} /* namespace net */
+95 -7
View File
@@ -1,4 +1,24 @@
/*
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#include "routing_table.h"
#include "network_engine.h"
#include "rng.h"
#include <memory>
@@ -35,6 +55,15 @@ Bucket::randomNode()
return nodes.back();
}
void Bucket::sendCachedPing(net::NetworkEngine& ne)
{
if (not cached)
return;
//DHT_LOG.d(b.cached->id, "[node %s] sending ping to cached node", cached->toString().c_str());
ne.sendPing(cached, nullptr, nullptr);
cached = {};
}
InfoHash
RoutingTable::randomId(const RoutingTable::const_iterator& it) const
{
@@ -80,7 +109,8 @@ RoutingTable::depth(const RoutingTable::const_iterator& it) const
std::vector<Sp<Node>>
RoutingTable::findClosestNodes(const InfoHash id, time_point now, size_t count) const
{
std::vector<Sp<Node>> nodes {};
std::vector<Sp<Node>> nodes;
nodes.reserve(count);
auto bucket = findBucket(id);
if (bucket == end()) { return nodes; }
@@ -100,7 +130,7 @@ RoutingTable::findClosestNodes(const InfoHash id, time_point now, size_t count)
};
auto itn = bucket;
auto itp = std::prev(bucket);
auto itp = (bucket == begin()) ? end() : std::prev(bucket);
while (nodes.size() < count && (itn != end() || itp != end())) {
if (itn != end()) {
sortedBucketInsert(*itn);
@@ -108,11 +138,7 @@ RoutingTable::findClosestNodes(const InfoHash id, time_point now, size_t count)
}
if (itp != end()) {
sortedBucketInsert(*itp);
if (itp == begin()) {
itp = end();
continue;
}
itp = std::prev(itp);
itp = (itp == begin()) ? end() : std::prev(itp);
}
}
@@ -175,4 +201,66 @@ RoutingTable::split(const RoutingTable::iterator& b)
return true;
}
bool
RoutingTable::onNewNode(const Sp<Node>& node, int confirm, const time_point& now, const InfoHash& myid, net::NetworkEngine& ne) {
auto b = findBucket(node->id);
if (b == end()) return false;
if (confirm == 2)
b->time = now;
for (auto& n : b->nodes) {
if (n == node)
return false;
}
bool mybucket = contains(b, myid);
if (mybucket) {
grow_time = now;
//scheduler.edit(nextNodesConfirmation, now);
}
if (b->nodes.size() >= TARGET_NODES) {
/* Try to get rid of an expired node. */
for (auto& n : b->nodes)
if (n->isExpired()) {
n = node;
return true;
}
/* Bucket full. Ping a dubious node */
bool dubious = false;
for (auto& n : b->nodes) {
/* Pick the first dubious node that we haven't pinged in the
last 9 seconds. This gives nodes the time to reply, but
tends to concentrate on the same nodes, so that we get rid
of bad nodes fast. */
if (not n->isGood(now)) {
dubious = true;
if (not n->isPendingMessage()) {
//DHT_LOG.d(n->id, "[node %s] sending ping to dubious node", n->toString().c_str());
ne.sendPing(n, nullptr, nullptr);
break;
}
}
}
if ((mybucket || (is_client and depth(b) < 6)) && (!dubious || size() == 1)) {
//DHT_LOG.d("Splitting from depth %u", depth(b));
b->sendCachedPing(ne);
split(b);
return onNewNode(node, confirm, now, myid, ne);
}
/* No space for this node. Cache it away for later. */
if (confirm or not b->cached)
b->cached = node;
} else {
/* Create a new node. */
b->nodes.emplace_front(node);
}
return true;
}
}
+867
View File
@@ -0,0 +1,867 @@
/*
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#pragma once
#include "value.h"
#include "request.h"
#include "listener.h"
namespace dht {
/**
* A single "get" operation data
*/
struct Dht::Get {
time_point start;
Value::Filter filter;
Sp<Query> query;
QueryCallback query_cb;
GetCallback get_cb;
DoneCallback done_cb;
};
/**
* A single "put" operation data
*/
struct Dht::Announce {
bool permanent;
Sp<Value> value;
time_point created;
DoneCallback callback;
};
struct Dht::SearchNode {
/**
* Foreach value id, we keep track of a pair (net::Request, time_point) where the
* request is the request returned by the network engine and the time_point
* is the next time at which the value must be refreshed.
*/
using AnnounceStatus = std::map<Value::Id, std::pair<Sp<net::Request>, time_point>>;
/**
* Foreach Query, we keep track of the request returned by the network
* engine when we sent the "get".
*/
using SyncStatus = std::map<Sp<Query>, Sp<net::Request>>;
Sp<Node> node {}; /* the node info */
/* queries sent for finding out values hosted by the node */
Sp<Query> probe_query {};
/* queries substituting formal 'get' requests */
std::map<Sp<Query>, std::vector<Sp<Query>>> pagination_queries {};
SyncStatus getStatus {}; /* get/sync status */
SyncStatus listenStatus {}; /* listen status */
AnnounceStatus acked {}; /* announcement status for a given value id */
Blob token {}; /* last token the node sent to us after a get request */
time_point last_get_reply {time_point::min()}; /* last time received valid token */
bool candidate {false}; /* A search node is candidate if the search is/was synced and this
node is a new candidate for inclusion. */
SearchNode() : node() {}
SearchNode(const SearchNode&) = delete;
SearchNode(SearchNode&&) = default;
SearchNode& operator=(const SearchNode&) = delete;
SearchNode& operator=(SearchNode&&) = default;
SearchNode(const Sp<Node>& node) : node(node) {}
~SearchNode() {
if (node) {
cancelGet();
cancelListen();
cancelAnnounce();
}
}
/**
* Can we use this node to listen/announce now ?
*/
bool isSynced(time_point now) const {
return not node->isExpired() and
not token.empty() and last_get_reply >= now - Node::NODE_EXPIRE_TIME;
}
/**
* Could a particular "get" request be sent to this node now ?
*
* A 'get' request can be sent when all of the following requirements are
* met:
*
* - The node is not expired;
* - The pagination process for this particular 'get' must not have begun;
* - There hasn't been any response for a request, satisfying the initial
* request, anytime following the initial request.
* - No other request satisfying the request must be pending;
*
* @param now The time reference to now.
* @param update The time of the last 'get' op satisfying this request.
* @param q The query defining the "get" operation we're referring to.
*
* @return true if we can send get, else false.
*/
bool canGet(time_point now, time_point update, Sp<Query> q = {}) const {
if (node->isExpired())
return false;
bool pending {false},
completed_sq_status {false},
pending_sq_status {false};
for (const auto& s : getStatus) {
if (s.second and s.second->pending())
pending = true;
if (s.first and q and q->isSatisfiedBy(*s.first) and s.second) {
if (s.second->pending() and not pending_sq_status)
pending_sq_status = true;
if (s.second->completed() and not (update > s.second->reply_time) and not completed_sq_status)
completed_sq_status = true;
if (completed_sq_status and pending_sq_status)
break;
}
}
return (not pending and now > last_get_reply + Node::NODE_EXPIRE_TIME) or
not (hasStartedPagination(q) or completed_sq_status or pending_sq_status);
}
/**
* Tells if we have started sending a 'get' request in paginated form.
*
* @param q The query as an id for a given 'get' request.
*
* @return true if pagination process has started, else false.
*/
bool hasStartedPagination(const Sp<Query>& q) const {
const auto& pqs = pagination_queries.find(q);
if (pqs == pagination_queries.cend() or pqs->second.empty())
return false;
return std::find_if(pqs->second.cbegin(), pqs->second.cend(),
[this](const Sp<Query>& query) {
const auto& req = getStatus.find(query);
return req != getStatus.cend() and req->second;
}) != pqs->second.cend();
};
/**
* Tell if the node has finished responding to a given 'get' request.
*
* A 'get' request can be divided in multiple requests called "pagination
* requests". If this is the case, we have to check if they're all finished.
* Otherwise, we only check for the single request.
*
* @param get The 'get' request data structure;
*
* @return true if it has finished, else false.
*/
bool isDone(const Get& get) const {
if (hasStartedPagination(get.query)) {
const auto& pqs = pagination_queries.find(get.query);
auto paginationPending = std::find_if(pqs->second.cbegin(), pqs->second.cend(),
[this](const Sp<Query>& query) {
const auto& req = getStatus.find(query);
return req != getStatus.cend() and req->second and req->second->pending();
}) != pqs->second.cend();
return not paginationPending;
} else { /* no pagination yet */
const auto& gs = get.query ? getStatus.find(get.query) : getStatus.cend();
return gs != getStatus.end() and gs->second and not gs->second->pending();
}
}
void cancelGet() {
for (const auto& status : getStatus) {
if (status.second->pending()) {
node->cancelRequest(status.second);
}
}
getStatus.clear();
}
/**
* Tells if a request in the status map is expired.
*
* @param status A SyncStatus reference.
*
* @return true if there exists an expired request, else false.
*/
/*static bool expired(const SyncStatus& status) const {
return std::find_if(status.begin(), status.end(),
[](const SyncStatus::value_type& r){
return r.second and r.second->expired();
}) != status.end();
}*/
/**
* Tells if a request in the status map is pending.
*
* @param status A SyncStatus reference.
*
* @return true if there exists an expired request, else false.
*/
static bool pending(const SyncStatus& status) {
return std::find_if(status.begin(), status.end(),
[](const SyncStatus::value_type& r){
return r.second and r.second->pending();
}) != status.end();
}
bool pendingGet() const { return pending(getStatus); }
bool isAnnounced(Value::Id vid) const {
auto ack = acked.find(vid);
if (ack == acked.end() or not ack->second.first)
return false;
return ack->second.first->completed();
}
void cancelAnnounce() {
for (const auto& status : acked) {
const auto& req = status.second.first;
if (req and req->pending()) {
node->cancelRequest(req);
}
}
acked.clear();
}
bool isListening(time_point now) const {
auto ls = listenStatus.begin();
for ( ; ls != listenStatus.end() ; ++ls) {
if (isListening(now, ls)) {
break;
}
}
return ls != listenStatus.end();
}
bool isListening(time_point now, const Sp<Query>& q) const {
const auto& ls = listenStatus.find(q);
if (ls == listenStatus.end())
return false;
else
return isListening(now, ls);
}
bool isListening(time_point now, SyncStatus::const_iterator listen_status) const {
if (listen_status == listenStatus.end())
return false;
return listen_status->second->reply_time + LISTEN_EXPIRE_TIME > now;
}
void cancelListen() {
for (const auto& status : listenStatus)
node->cancelRequest(status.second);
listenStatus.clear();
}
void cancelListen(const Sp<Query>& query) {
auto it = listenStatus.find(query);
if (it != listenStatus.end()) {
node->cancelRequest(it->second);
listenStatus.erase(it);
}
}
/**
* Assumng the node is synced, should a "put" request be sent to this node now ?
*/
time_point getAnnounceTime(Value::Id vid) const {
const auto& ack = acked.find(vid);
const auto& gs = probe_query ? getStatus.find(probe_query) : getStatus.cend();
if ((ack == acked.cend() or not ack->second.first) and (gs == getStatus.cend()
or not gs->second or not gs->second->pending()))
return time_point::min();
return ((gs != getStatus.cend() and gs->second and gs->second->pending())
or ack == acked.cend() or not ack->second.first or ack->second.first->pending())
? time_point::max()
: ack->second.second - REANNOUNCE_MARGIN;
}
/**
* Assumng the node is synced, should the "listen" request with Query q be
* sent to this node now ?
*/
time_point getListenTime(const Sp<Query>& q) const {
auto listen_status = listenStatus.find(q);
if (listen_status == listenStatus.end() or not listen_status->second)
return time_point::min();
return listen_status->second->pending() ? time_point::max() :
listen_status->second->reply_time + LISTEN_EXPIRE_TIME - REANNOUNCE_MARGIN;
}
/**
* Is this node expired or candidate
*/
bool isBad() const {
return not node or node->isExpired() or candidate;
}
};
/**
* A search is a list of the nodes we think are responsible
* for storing values for a given hash.
*/
struct Dht::Search {
InfoHash id {};
sa_family_t af;
uint16_t tid;
time_point refill_time {time_point::min()};
time_point step_time {time_point::min()}; /* the time of the last search step */
Sp<Job> nextSearchStep {};
bool expired {false}; /* no node, or all nodes expired */
bool done {false}; /* search is over, cached for later */
std::vector<SearchNode> nodes {};
/* pending puts */
std::vector<Announce> announce {};
/* pending gets */
std::multimap<time_point, Get> callbacks {};
/* listeners */
std::map<size_t, LocalListener> listeners {};
size_t listener_token = 1;
~Search() {
for (auto& get : callbacks) {
get.second.done_cb(false, {});
get.second.done_cb = {};
}
for (auto& put : announce) {
put.callback(false, {});
put.callback = {};
}
}
/**
* @returns true if the node was not present and added to the search
*/
bool insertNode(const Sp<Node>& n, time_point now, const Blob& token={});
SearchNode* getNode(const Sp<Node>& n) {
auto srn = std::find_if(nodes.begin(), nodes.end(), [&](SearchNode& sn) {
return n == sn.node;
});
return (srn == nodes.end()) ? nullptr : &(*srn);
}
/* number of concurrent sync requests */
unsigned currentlySolicitedNodeCount() const {
unsigned count = 0;
for (const auto& n : nodes)
if (not n.isBad() and n.pendingGet())
count++;
return count;
}
/**
* Can we use this search to announce ?
*/
bool isSynced(time_point now) const;
/**
* Get the time of the last "get" operation performed on this search,
* or time_point::min() if no such operation have been performed.
*
* @param query The query identifying a 'get' request.
*/
time_point getLastGetTime(Sp<Query> query = {}) const;
/**
* Is this get operation done ?
*/
bool isDone(const Get& get) const;
/**
* Sets a consistent state of the search after a given 'get' operation as
* been completed.
*
* This will also make sure to call the associated 'done callback'.
*
* @param get The 'get' operation which is now over.
*/
void setDone(const Get& get) {
for (auto& n : nodes) {
auto pqs = n.pagination_queries.find(get.query);
if (pqs != n.pagination_queries.cend()) {
for (auto& pq : pqs->second)
n.getStatus.erase(pq);
}
n.getStatus.erase(get.query);
}
if (get.done_cb)
get.done_cb(true, getNodes());
}
/**
* Set the search in a consistent state after the search is done. This is
* the opportunity we have to clear some entries in the SearchNodes status
* maps.
*/
void setDone() {
for (auto& n : nodes) {
n.getStatus.clear();
n.listenStatus.clear();
n.acked.clear();
}
done = true;
}
time_point getUpdateTime(time_point now) const;
bool isAnnounced(Value::Id id) const;
bool isListening(time_point now) const;
void cancelListen(size_t token) {
Sp<Query> query;
const auto& ll = listeners.find(token);
if (ll != listeners.cend()) {
query = ll->second.query;
listeners.erase(ll);
}
for (auto& sn : nodes) {
if (listeners.empty())
sn.cancelListen();
else if (query)
sn.cancelListen(query);
}
}
/**
* @return The number of non-good search nodes.
*/
unsigned getNumberOfBadNodes() const {
return std::count_if(nodes.begin(), nodes.end(), [](const SearchNode& sn) {
return sn.isBad();
});
}
unsigned getNumberOfConsecutiveBadNodes() const {
unsigned count = 0;
std::find_if(nodes.begin(), nodes.end(), [&count](const SearchNode& sn) {
if (not sn.isBad())
return true;
++count;
return false;
});
return count;
}
/**
* Returns the time of the next "announce" event for this search,
* or time_point::max() if no such event is planned.
* Only makes sense when the search is synced.
*/
time_point getAnnounceTime(time_point now) const;
/**
* Returns the time of the next "listen" event for this search,
* or time_point::max() if no such event is planned.
* Only makes sense when the search is synced.
*/
time_point getListenTime(time_point now) const;
/**
* Returns the time of the next event for this search,
* or time_point::max() if no such event is planned.
*/
time_point getNextStepTime(time_point now) const;
/**
* Removes a node which have been expired for at least
* NODE::NODE_EXPIRE_TIME minutes. The search for an expired node starts
* from the end.
*
* @param now The reference to now.
*
* @return true if a node has been removed, else false.
*/
bool removeExpiredNode(const time_point& now) {
for (auto e = nodes.cend(); e != nodes.cbegin();) {
const Node& n = *(--e)->node;
if (n.isRemovable(now)) {
//std::cout << "Removing expired node " << n.id << " from IPv" << (af==AF_INET?'4':'6') << " search " << id << std::endl;
nodes.erase(e);
return true;
}
}
return false;
}
/**
* This method is called when we have discovered that the search is expired.
* We have to
*
* - remove all nodes from the search;
* - clear (non-permanent) callbacks;
*/
void expire() {
// no nodes or all expired nodes. This is most likely a connectivity change event.
expired = true;
nodes.clear();
if (announce.empty() && listeners.empty())
// Listening or announcing requires keeping the cluster up to date.
setDone();
{
auto get_cbs = std::move(callbacks);
for (const auto& g : get_cbs) {
if (g.second.done_cb)
g.second.done_cb(false, {});
}
}
{
std::vector<DoneCallback> a_cbs;
a_cbs.reserve(announce.size());
for (auto ait = announce.begin() ; ait != announce.end(); ) {
if (ait->callback)
a_cbs.emplace_back(std::move(ait->callback));
if (not ait->permanent)
ait = announce.erase(ait);
else
ait++;
}
for (const auto& a : a_cbs)
a(false, {});
}
}
/**
* If the value was just successfully announced, call the callback and erase it if not permanent.
*
* @param vid The id of the announced value.
* @param types The sequence of existing types.
* @param now The time reference to now.
*/
void checkAnnounced(Value::Id vid = Value::INVALID_ID) {
auto announced = std::partition(announce.begin(), announce.end(),
[this,&vid](Announce& a) {
if (vid != Value::INVALID_ID and (!a.value || a.value->id != vid))
return true;
if (isAnnounced(a.value->id)) {
if (a.callback) {
a.callback(true, getNodes());
a.callback = nullptr;
}
if (not a.permanent)
return false;
}
return true;
});
// remove acked for cleared annouces
for (auto it = announced; it != announce.end(); ++it) {
for (auto& n : nodes)
n.acked.erase(it->value->id);
}
announce.erase(announced, announce.end());
}
std::vector<Sp<Node>> getNodes() const;
void clear() {
announce.clear();
callbacks.clear();
listeners.clear();
nodes.clear();
nextSearchStep.reset();
}
};
/* A search contains a list of nodes, sorted by decreasing distance to the
target. We just got a new candidate, insert it at the right spot or
discard it. */
bool
Dht::Search::insertNode(const Sp<Node>& snode, time_point now, const Blob& token)
{
auto& node = *snode;
const auto& nid = node.id;
if (node.getFamily() != af)
return false;
bool found = false;
auto n = nodes.end();
while (n != nodes.begin()) {
--n;
if (n->node == snode) {
found = true;
break;
}
/* Node not found. We could insert it after this one. */
if (id.xorCmp(nid, n->node->id) > 0) {
++n;
break;
}
}
bool new_search_node = false;
if (not found) {
// find if and where to trim excessive nodes
auto t = nodes.cend();
size_t bad = 0; // number of bad nodes (if search is not expired)
bool full {false}; // is the search full (has the maximum nodes)
if (expired) {
// if the search is expired, trim to SEARCH_NODES nodes
if (nodes.size() >= SEARCH_NODES) {
full = true;
t = nodes.begin() + SEARCH_NODES;
}
} else {
// otherwise, trim to SEARCH_NODES nodes, not counting bad nodes
bad = getNumberOfBadNodes();
full = nodes.size() - bad >= SEARCH_NODES;
while (std::distance(nodes.cbegin(), t) - bad > SEARCH_NODES) {
--t;
if (t->isBad())
bad--;
}
}
if (full) {
if (t != nodes.cend())
nodes.resize(std::distance(nodes.cbegin(), t));
if (n >= t)
return false;
}
// Reset search timer if the search is empty
if (nodes.empty()) {
step_time = time_point::min();
}
n = nodes.insert(n, SearchNode(snode));
node.setTime(now);
new_search_node = true;
if (node.isExpired()) {
if (not expired)
bad++;
} else if (expired) {
bad = nodes.size() - 1;
expired = false;
}
while (nodes.size() - bad > SEARCH_NODES) {
if (not expired and nodes.back().isBad())
bad--;
nodes.pop_back();
}
}
if (not token.empty()) {
n->candidate = false;
n->last_get_reply = now;
if (token.size() <= 64)
n->token = token;
expired = false;
}
if (new_search_node)
removeExpiredNode(now);
return new_search_node;
}
std::vector<Sp<Node>>
Dht::Search::getNodes() const
{
std::vector<Sp<Node>> ret {};
ret.reserve(nodes.size());
for (const auto& sn : nodes)
ret.emplace_back(sn.node);
return ret;
}
bool
Dht::Search::isSynced(time_point now) const
{
unsigned i = 0;
for (const auto& n : nodes) {
if (n.isBad())
continue;
if (not n.isSynced(now))
return false;
if (++i == TARGET_NODES)
break;
}
return i > 0;
}
time_point
Dht::Search::getLastGetTime(Sp<Query> q) const
{
time_point last = time_point::min();
for (const auto& g : callbacks)
last = std::max(last, (not q or q->isSatisfiedBy(*g.second.query) ? g.second.start : time_point::min()));
return last;
}
bool
Dht::Search::isDone(const Get& get) const
{
unsigned i = 0;
for (const auto& sn : nodes) {
if (sn.isBad())
continue;
if (not sn.isDone(get))
return false;
if (++i == TARGET_NODES)
break;
}
return true;
}
time_point
Dht::Search::getUpdateTime(time_point now) const
{
time_point ut = time_point::max();
const auto last_get = getLastGetTime();
unsigned i = 0, t = 0, d = 0;
const auto solicited_nodes = currentlySolicitedNodeCount();
for (const auto& sn : nodes) {
if (sn.node->isExpired() or (sn.candidate and t >= TARGET_NODES))
continue;
auto pending = sn.pendingGet();
if (sn.last_get_reply < std::max(now - Node::NODE_EXPIRE_TIME, last_get) or pending) {
// not isSynced
if (not pending and solicited_nodes < MAX_REQUESTED_SEARCH_NODES)
ut = std::min(ut, now);
if (not sn.candidate)
d++;
} else
ut = std::min(ut, sn.last_get_reply + Node::NODE_EXPIRE_TIME);
t++;
if (not sn.candidate and ++i == TARGET_NODES)
break;
}
if (not callbacks.empty() and d == 0)
// If all synced/updated but some callbacks remain, step now to clear them
return now;
return ut;
}
bool
Dht::Search::isAnnounced(Value::Id id) const
{
if (nodes.empty())
return false;
unsigned i = 0;
for (const auto& n : nodes) {
if (n.isBad())
continue;
if (not n.isAnnounced(id))
return false;
if (++i == TARGET_NODES)
return true;
}
return i;
}
bool
Dht::Search::isListening(time_point now) const
{
if (nodes.empty() or listeners.empty())
return false;
unsigned i = 0;
for (const auto& n : nodes) {
if (n.isBad())
continue;
SearchNode::SyncStatus::const_iterator ls {};
for (ls = n.listenStatus.begin(); ls != n.listenStatus.end() ; ++ls) {
if (n.isListening(now, ls))
break;
}
if (ls == n.listenStatus.end())
return false;
if (++i == LISTEN_NODES)
break;
}
return i;
}
time_point
Dht::Search::getAnnounceTime(time_point now) const
{
if (nodes.empty())
return time_point::max();
time_point ret {time_point::max()};
for (const auto& a : announce) {
if (!a.value) continue;
unsigned i = 0, t = 0;
for (const auto& n : nodes) {
if (not n.isSynced(now) or (n.candidate and t >= TARGET_NODES))
continue;
ret = std::min(ret, n.getAnnounceTime(a.value->id));
t++;
if (not n.candidate and ++i == TARGET_NODES)
break;
}
}
return ret;
}
time_point
Dht::Search::getListenTime(time_point now) const
{
if (listeners.empty())
return time_point::max();
time_point listen_time {time_point::max()};
unsigned i = 0, t = 0;
for (const auto& sn : nodes) {
if (not sn.isSynced(now) or (sn.candidate and t >= LISTEN_NODES))
continue;
for (auto& l : listeners)
listen_time = std::min(listen_time, sn.getListenTime(l.second.query));
t++;
if (not sn.candidate and ++i == LISTEN_NODES)
break;
}
return listen_time;
}
time_point
Dht::Search::getNextStepTime(time_point now) const
{
auto next_step = time_point::max();
if (expired or done)
return next_step;
auto ut = getUpdateTime(now);
if (ut != time_point::max()) {
//std::cout << id.toString() << " IPv" << (af==AF_INET?"4":"6") << " update time in " << print_dt(ut - now) << " s" << std::endl;
next_step = std::min(next_step, ut);
}
if (isSynced(now))
{
auto at = getAnnounceTime(now);
if (at != time_point::max()) {
//std::cout << id.toString() << " IPv" << (af==AF_INET?"4":"6") << " announce time in " << print_dt(at - now) << " s" << std::endl;
next_step = std::min(next_step, at);
}
auto lt = getListenTime(now);
if (lt != time_point::max()) {
//std::cout << id.toString() << " IPv" << (af==AF_INET?"4":"6") << " listen time in " << print_dt(lt - now) << " s" << std::endl;
next_step = std::min(next_step, lt);
}
}
return next_step;
}
}
+7 -7
View File
@@ -40,10 +40,10 @@ Config& getConfig(SecureDht::Config& conf)
return c;
}
SecureDht::SecureDht(int s, int s6, SecureDht::Config conf)
: Dht(s, s6, getConfig(conf)), key_(conf.id.first), certificate_(conf.id.second)
SecureDht::SecureDht(uv_loop_t* loop, SecureDht::Config conf)
: Dht(loop, getConfig(conf)), key_(conf.id.first), certificate_(conf.id.second)
{
if (s < 0 && s6 < 0)
if (not loop)
return;
for (const auto& type : DEFAULT_TYPES)
@@ -76,7 +76,7 @@ SecureDht::~SecureDht()
ValueType
SecureDht::secureType(ValueType&& type)
{
type.storePolicy = [this,type](InfoHash id, Sp<Value>& v, InfoHash nid, const sockaddr* a, socklen_t al) {
type.storePolicy = [this,type](InfoHash id, Sp<Value>& v, const InfoHash& nid, const SockAddr& a) {
if (v->isSigned()) {
if (!v->owner or !v->owner->checkSignature(v->getToSign(), v->signature)) {
DHT_LOG.WARN("Signature verification failed");
@@ -85,11 +85,11 @@ SecureDht::secureType(ValueType&& type)
else
DHT_LOG.WARN("Signature verification succeeded");
}
return type.storePolicy(id, v, nid, a, al);
return type.storePolicy(id, v, nid, a);
};
type.editPolicy = [this,type](InfoHash id, const Sp<Value>& o, Sp<Value>& n, InfoHash nid, const sockaddr* a, socklen_t al) {
type.editPolicy = [this,type](InfoHash id, const Sp<Value>& o, Sp<Value>& n, const InfoHash& nid, const SockAddr& a) {
if (!o->isSigned())
return type.editPolicy(id, o, n, nid, a, al);
return type.editPolicy(id, o, n, nid, a);
if (o->owner != n->owner) {
DHT_LOG.WARN("Edition forbidden: owner changed.");
return false;
+286
View File
@@ -0,0 +1,286 @@
/*
* Copyright (C) 2014-2017 Savoir-faire Linux Inc.
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#pragma once
#include "infohash.h"
#include "value.h"
#include "listener.h"
#include <map>
#include <utility>
namespace dht {
/**
* Tracks storage usage per IP or IP range
*/
class StorageBucket {
public:
void insert(const InfoHash& id, const Value& value, time_point expiration) {
totalSize_ += value.size();
storedValues_.emplace(expiration, std::pair<InfoHash, Value::Id>(id, value.id));
}
void erase(const InfoHash& id, const Value& value, time_point expiration) {
auto size = value.size();
totalSize_ -= size;
auto range = storedValues_.equal_range(expiration);
for (auto rit = range.first; rit != range.second;) {
if (rit->second.first == id && rit->second.second == value.id) {
storedValues_.erase(rit);
break;
} else
++rit;
}
}
size_t size() const { return totalSize_; }
std::pair<InfoHash, Value::Id> getOldest() const { return storedValues_.begin()->second; }
private:
std::multimap<time_point, std::pair<InfoHash, Value::Id>> storedValues_;
size_t totalSize_ {0};
};
struct ValueStorage {
Sp<Value> data {};
time_point created {};
time_point expiration {};
StorageBucket* store_bucket {nullptr};
ValueStorage() {}
ValueStorage(const Sp<Value>& v, time_point t, time_point e)
: data(v), created(t), expiration(e) {}
};
struct Storage {
time_point maintenance_time {};
std::map<Sp<Node>, std::map<size_t, Listener>> listeners;
std::map<size_t, LocalListener> local_listeners {};
size_t listener_token {1};
/* The maximum number of values we store for a given hash. */
static constexpr unsigned MAX_VALUES {1024};
/**
* Changes caused by an operation on the storage.
*/
struct StoreDiff {
/** Difference in stored size caused by the op */
ssize_t size_diff;
/** Difference in number of values */
ssize_t values_diff;
/** Difference in number of listeners */
ssize_t listeners_diff;
};
Storage() {}
Storage(time_point t) : maintenance_time(t) {}
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 || defined(_WIN32)
// GCC-bug: remove me when support of GCC < 4.9.2 is abandoned
Storage(Storage&& o) noexcept
: maintenance_time(std::move(o.maintenance_time))
, listeners(std::move(o.listeners))
, local_listeners(std::move(o.local_listeners))
, listener_token(std::move(o.listener_token))
, values(std::move(o.values))
, total_size(std::move(o.total_size)) {}
#else
Storage(Storage&& o) noexcept = default;
#endif
Storage& operator=(Storage&& o) = default;
bool empty() const {
return values.empty();
}
StoreDiff clear();
size_t valueCount() const {
return values.size();
}
size_t totalSize() const {
return total_size;
}
const std::vector<ValueStorage>& getValues() const { return values; }
Sp<Value> getById(Value::Id vid) const {
for (auto& v : values)
if (v.data->id == vid) return v.data;
return {};
}
std::vector<Sp<Value>> get(Value::Filter f = {}) const {
std::vector<Sp<Value>> newvals {};
if (not f) newvals.reserve(values.size());
for (auto& v : values) {
if (not f || f(*v.data))
newvals.push_back(v.data);
}
return newvals;
}
/**
* Stores a new value in this storage, or replace a previous value
*
* @return <storage, change_size, change_value_num>
* storage: set if a change happened
* change_size: size difference
* change_value_num: change of value number (0 or 1)
*/
std::pair<ValueStorage*, StoreDiff>
store(const InfoHash& id, const Sp<Value>&, time_point created, time_point expiration, StorageBucket*);
/**
* Refreshes the time point of the value's lifetime begining.
*
* @param now The reference to now
* @param vid The value id
* @return true if a value storage was updated, false otherwise
*/
bool refresh(const time_point& now, const Value::Id& vid) {
for (auto& vs : values)
if (vs.data->id == vid) {
vs.created = now;
return true;
}
return false;
}
StoreDiff remove(const InfoHash& id, Value::Id);
StoreDiff expire(const InfoHash& id, time_point now);
private:
Storage(const Storage&) = delete;
Storage& operator=(const Storage&) = delete;
std::vector<ValueStorage> values {};
size_t total_size {};
};
std::pair<ValueStorage*, Storage::StoreDiff>
Storage::store(const InfoHash& id, const Sp<Value>& value, time_point created, time_point expiration, StorageBucket* sb)
{
auto it = std::find_if (values.begin(), values.end(), [&](const ValueStorage& vr) {
return vr.data == value || vr.data->id == value->id;
});
ssize_t size_new = value->size();
if (it != values.end()) {
/* Already there, only need to refresh */
it->created = created;
size_t size_old = it->data->size();
ssize_t size_diff = size_new - (ssize_t)size_old;
if (it->data != value) {
//DHT_LOG.DEBUG("Updating %s -> %s", id.toString().c_str(), value->toString().c_str());
// clear quota for previous value
if (it->store_bucket)
it->store_bucket->erase(id, *value, it->expiration);
it->expiration = expiration;
// update quota for new value
it->store_bucket = sb;
if (sb)
sb->insert(id, *value, expiration);
it->data = value;
total_size += size_diff;
return std::make_pair(&(*it), StoreDiff{size_diff, 0, 0});
}
return std::make_pair(nullptr, StoreDiff{});
} else {
//DHT_LOG.DEBUG("Storing %s -> %s", id.toString().c_str(), value->toString().c_str());
if (values.size() < MAX_VALUES) {
total_size += size_new;
values.emplace_back(value, created, expiration);
values.back().store_bucket = sb;
if (sb)
sb->insert(id, *value, expiration);
return std::make_pair(&values.back(), StoreDiff{size_new, 1, 0});
}
return std::make_pair(nullptr, StoreDiff{});
}
}
Storage::StoreDiff
Storage::remove(const InfoHash& id, Value::Id vid)
{
auto it = std::find_if (values.begin(), values.end(), [&](const ValueStorage& vr) {
return vr.data->id == vid;
});
if (it == values.end())
return {};
ssize_t size = it->data->size();
if (it->store_bucket)
it->store_bucket->erase(id, *it->data, it->expiration);
total_size -= size;
values.erase(it);
return {-size, -1, 0};
}
Storage::StoreDiff
Storage::clear()
{
ssize_t num_values = values.size();
ssize_t tot_size = total_size;
values.clear();
total_size = 0;
return {-tot_size, -num_values, 0};
}
Storage::StoreDiff
Storage::expire(const InfoHash& id, time_point now)
{
// expire listeners
ssize_t del_listen {0};
for (auto nl_it = listeners.begin(); nl_it != listeners.end();) {
auto& node_listeners = nl_it->second;
for (auto l = node_listeners.cbegin(); l != node_listeners.cend();) {
bool expired = l->second.time + Node::NODE_EXPIRE_TIME < now;
if (expired)
l = node_listeners.erase(l);
else
++l;
}
if (node_listeners.empty()) {
nl_it = listeners.erase(nl_it);
del_listen--;
}
else
++nl_it;
}
// expire values
auto r = std::partition(values.begin(), values.end(), [&](const ValueStorage& v) {
return v.expiration > now;
});
ssize_t del_num = -std::distance(r, values.end());
ssize_t size_diff {};
std::for_each(r, values.end(), [&](const ValueStorage& v) {
size_diff -= v.data->size();
if (v.store_bucket)
v.store_bucket->erase(id, *v.data, v.expiration);
});
total_size += size_diff;
values.erase(r, values.end());
return {size_diff, del_num, del_listen};
}
}
+70 -6
View File
@@ -19,12 +19,16 @@
#include "utils.h"
#include "sockaddr.h"
#include "default_types.h"
#include "net.h"
#include "uv_utils.h"
/* An IPv4 equivalent to IN6_IS_ADDR_UNSPECIFIED */
#ifndef IN_IS_ADDR_UNSPECIFIED
#define IN_IS_ADDR_UNSPECIFIED(a) (((long int) (a)->s_addr) == 0x00000000)
#endif /* IN_IS_ADDR_UNSPECIFIED */
static constexpr std::array<uint8_t, 12> MAPPED_IPV4_PREFIX {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff}};
namespace dht {
std::string
@@ -38,7 +42,7 @@ print_addr(const sockaddr* sa, socklen_t slen)
out << "[" << hbuf << "]";
else
out << hbuf;
if (strcmp(sbuf, "0"))
if (std::strcmp(sbuf, "0"))
out << ":" << sbuf;
} else
out << "[invalid address]";
@@ -51,11 +55,6 @@ print_addr(const sockaddr_storage& ss, socklen_t sslen)
return print_addr((const sockaddr*)&ss, sslen);
}
std::string
printAddr(const SockAddr& addr) {
return print_addr((const sockaddr*)&addr.first, addr.second);
}
bool
SockAddr::isUnspecified() const
{
@@ -116,6 +115,57 @@ SockAddr::isPrivate() const
}
}
bool
SockAddr::isMappedIPv4() const
{
if (getFamily() != AF_INET6)
return false;
const uint8_t* addr6 = reinterpret_cast<const uint8_t*>(&getIPv6().sin6_addr);
return std::equal(MAPPED_IPV4_PREFIX.begin(), MAPPED_IPV4_PREFIX.end(), addr6);
}
SockAddr
SockAddr::getMappedIPv4() const
{
if (getFamily() != AF_INET6)
return *this;
SockAddr ret;
ret.setFamily(AF_INET);
ret.setPort(getPort());
auto addr6 = reinterpret_cast<const uint8_t*>(&getIPv6().sin6_addr);
auto addr4 = reinterpret_cast<uint8_t*>(&ret.getIPv4().sin_addr);
addr6 += MAPPED_IPV4_PREFIX.size();
std::copy_n(addr6, sizeof(in_addr), addr4);
return ret;
}
void get_addr_info_cb(uv_getaddrinfo_t* req, int status, struct addrinfo* res) {
if (auto cb = static_cast<GetAddrInfoCb*>(req->data)) {
size_t count {0};
for (auto addr = res; addr; addr = addr->ai_next)
count++;
std::vector<SockAddr> ret;
ret.reserve(count);
for (auto addr = res; addr; addr = addr->ai_next)
ret.emplace_back(addr->ai_addr, addr->ai_addrlen);
(*cb)(std::move(ret));
delete cb;
}
delete req;
uv_freeaddrinfo(res);
}
void getAddrInfo(uv_loop_t* loop, const char* node, const char* service, GetAddrInfoCb&& cb)
{
auto req = new uv_getaddrinfo_t;
req->data = new GetAddrInfoCb(std::move(cb));
addrinfo hints {};
hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM;
uv_getaddrinfo(loop, req, get_addr_info_cb, node, service, &hints);
}
bool operator==(const SockAddr& a, const SockAddr& b) {
return a.equals(b);
}
@@ -165,4 +215,18 @@ findMapValue(msgpack::object& map, const std::string& key) {
return nullptr;
}
namespace net {
TransId
unpackTid(msgpack::object& o) {
switch (o.type) {
case msgpack::type::POSITIVE_INTEGER:
return o.as<uint32_t>();
default:
return o.as<std::array<char, 4>>();
}
}
}
}
+17 -6
View File
@@ -26,8 +26,16 @@ namespace dht {
const std::string Query::QUERY_PARSE_ERROR {"Error parsing query."};
Value::Filter bindFilterRaw(FilterRaw raw_filter, void* user_data) {
if (not raw_filter) return {};
return [=](const Value& value) {
return raw_filter(value, user_data);
};
}
std::ostream& operator<< (std::ostream& s, const Value& v)
{
auto flags(s.flags());
s << "Value[id:" << std::hex << v.id << std::dec << " ";
if (v.isEncrypted())
s << "encrypted ";
@@ -56,13 +64,14 @@ std::ostream& operator<< (std::ostream& s, const Value& v)
}
}
s << "]";
s.flags(flags);
return s;
}
const ValueType ValueType::USER_DATA = {0, "User Data"};
bool
ValueType::DEFAULT_STORE_POLICY(InfoHash, std::shared_ptr<Value>& v, InfoHash, const sockaddr*, socklen_t)
ValueType::DEFAULT_STORE_POLICY(InfoHash, std::shared_ptr<Value>& v, const InfoHash&, const SockAddr&)
{
return v->size() <= MAX_VALUE_SIZE;
}
@@ -250,9 +259,12 @@ std::ostream& operator<<(std::ostream& os, const FieldValueIndex& fvi) {
os << "Index[";
for (auto v = fvi.index.begin(); v != fvi.index.end(); ++v) {
switch (v->first) {
case Value::Field::Id:
case Value::Field::Id: {
auto flags(os.flags());
os << "Id:" << std::hex << v->second.getInt();
os.flags(flags);
break;
}
case Value::Field::ValueType:
os << "ValueType:" << v->second.getInt();
break;
@@ -353,10 +365,10 @@ Where::Where(const std::string& q_str) {
std::string s {};
std::istringstream convert {value_str};
convert >> v;
if (convert.failbit
if (not convert
and value_str.size() > 1
and value_str[0] == '\"'
and value_str[value_str.size()-1] == '\"')
and value_str[0] == '"'
and value_str[value_str.size()-1] == '"')
s = value_str.substr(1, value_str.size()-2);
else
s = value_str;
@@ -483,4 +495,3 @@ std::ostream& operator<<(std::ostream& s, const dht::Where& where) {
}
+24 -4
View File
@@ -17,8 +17,28 @@ else ()
target_link_libraries (dhtchat LINK_PUBLIC opendht-static)
endif ()
if (NOT DEFINED CMAKE_INSTALL_BINDIR)
set(CMAKE_INSTALL_BINDIR bin)
endif ()
install (TARGETS dhtnode dhtscanner dhtchat RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
if (OPENDHT_SYSTEMD)
execute_process(COMMAND ${PKG_CONFIG_EXECUTABLE} systemd --variable=systemdsystemunitdir
OUTPUT_VARIABLE SYSTEMD_UNIT_INSTALL_DIR)
string(REGEX REPLACE "[ \t\n]+" "" SYSTEMD_UNIT_INSTALL_DIR "${SYSTEMD_UNIT_INSTALL_DIR}")
set (systemdunitdir "${SYSTEMD_UNIT_INSTALL_DIR}")
configure_file (
systemd/dhtnode.service.in
systemd/dhtnode.service
@ONLY
)
install (FILES ${CMAKE_CURRENT_BINARY_DIR}/systemd/dhtnode.service DESTINATION ${systemdunitdir})
install (FILES systemd/dhtnode.conf DESTINATION ${sysconfdir})
if (OPENDHT_PYTHON)
configure_file (
systemd/dhtcluster.service.in
systemd/dhtcluster.service
@ONLY
)
install (FILES ${CMAKE_CURRENT_BINARY_DIR}/systemd/dhtcluster.service DESTINATION ${systemdunitdir})
install (FILES systemd/dhtcluster.conf DESTINATION ${sysconfdir})
endif()
endif ()
+13 -7
View File
@@ -39,7 +39,7 @@ void print_id_req() {
}
void print_node_info(const std::shared_ptr<DhtRunner>& dht, const dht_params& params) {
std::cout << "OpenDht node " << dht->getNodeId() << " running on port " << dht->getBoundPort() << std::endl;
std::cout << "OpenDht node " << dht->getNodeId() << " running on port " << dht->getBound().toString() << std::endl;
if (params.generate_identity)
std::cout << "Public key ID " << dht->getId() << std::endl;
}
@@ -359,18 +359,25 @@ main(int argc, char **argv)
}
if (params.daemonize) {
daemonize();
} else if (params.service) {
setupSignals();
}
dht::crypto::Identity crt {};
if (params.generate_identity) {
auto ca_tmp = dht::crypto::generateIdentity("DHT Node CA");
crt = dht::crypto::generateIdentity("DHT Node", ca_tmp);
auto ca_tmp = dht::crypto::generateEcIdentity("DHT Node CA");
crt = dht::crypto::generateEcIdentity("DHT Node", ca_tmp);
}
dht->run(params.port, crt, true, params.network);
dht->setOnStatusChanged([](dht::NodeStatus status4, dht::NodeStatus status6){
std::cout << "Node status changed : IPv4:" << (unsigned)status4 << " IPv6:" << (unsigned)status6 << std::endl;
});
if (params.log) {
if (not params.logfile.empty())
if (params.syslog or (params.daemonize and params.logfile.empty()))
log::enableSyslog(*dht, "dhtnode");
else if (not params.logfile.empty())
log::enableFileLogging(*dht, params.logfile);
else
log::enableLogging(*dht);
@@ -381,9 +388,8 @@ main(int argc, char **argv)
dht->bootstrap(params.bootstrap.first.c_str(), params.bootstrap.second.c_str());
}
if (params.daemonize) {
while (true)
std::this_thread::sleep_for(std::chrono::seconds(30));
if (params.daemonize or params.service) {
while (runner.wait());
} else {
cmd_loop(dht, params);
}
+11 -2
View File
@@ -56,8 +56,8 @@ step(DhtRunner& dht, std::atomic_uint& done, std::shared_ptr<NodeSet> all_nodes,
all_nodes->insert(nodes.begin(), nodes.end());
NodeSet sbuck {nodes.begin(), nodes.end()};
if (not sbuck.empty()) {
unsigned bdepth = sbuck.size()==1 ? 0u : InfoHash::commonBits((*sbuck.begin())->id, (*std::prev(sbuck.end()))->id);
unsigned target_depth = std::min(159u, bdepth+3u);
unsigned bdepth = sbuck.size()==1 ? 0u : InfoHash::commonBits((*sbuck.begin())->id, (*sbuck.rbegin())->id);
unsigned target_depth = std::min(8u, bdepth+6u);
std::cout << cur_h << " : " << nodes.size() << " nodes; target is " << target_depth << " bits deep (cur " << cur_depth << ")" << std::endl;
for (unsigned b = cur_depth ; b < target_depth; b++) {
auto new_h = cur_h;
@@ -87,6 +87,15 @@ main(int argc, char **argv)
try {
dht.run(params.port, {}, true, params.network);
if (params.log) {
if (params.syslog)
log::enableSyslog(dht, "dhtnode");
else if (not params.logfile.empty())
log::enableFileLogging(dht, params.logfile);
else
log::enableLogging(dht);
}
if (not params.bootstrap.first.empty())
dht.bootstrap(params.bootstrap.first.c_str(), params.bootstrap.second.c_str());
+1
View File
@@ -0,0 +1 @@
DHT_ARGS=-b bootstrap.ring.cx -p 4222 -n 16
+20
View File
@@ -0,0 +1,20 @@
[Unit]
Description=OpenDHT node cluster
After=network.target
[Service]
EnvironmentFile=@sysconfdir@/dhtcluster.conf
ExecStart=@bindir@/dhtcluster -s $DHT_ARGS
KillMode=process
Restart=on-failure
Type=simple
ProtectSystem=strict
ProtectHome=yes
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectControlGroups=yes
PrivateDevices=yes
PrivateUsers=yes
[Install]
WantedBy=multi-user.target
+1
View File
@@ -0,0 +1 @@
DHT_ARGS=-b bootstrap.ring.cx -p 4222 -v
+14
View File
@@ -0,0 +1,14 @@
[Unit]
Description=OpenDHT standalone node
After=network.target
[Service]
EnvironmentFile=@sysconfdir@/dhtnode.conf
ExecStart=@bindir@/dhtnode -d $DHT_ARGS
KillMode=process
Restart=on-failure
Type=forking
DynamicUser=yes
[Install]
WantedBy=multi-user.target
+51 -11
View File
@@ -18,6 +18,7 @@
*/
// Common utility methods used by C++ OpenDHT tools.
#pragma once
#include <opendht.h>
#ifndef WIN32_NATIVE
@@ -37,6 +38,8 @@
#include <string>
#include <vector>
#include <chrono>
#include <mutex>
#include <condition_variable>
#include <iostream>
#include <sstream>
#include <fstream>
@@ -111,10 +114,12 @@ struct dht_params {
bool help {false}; // print help and exit
bool log {false};
std::string logfile {};
bool syslog {false};
in_port_t port {0};
dht::NetId network {0};
bool generate_identity {false};
bool daemonize {false};
bool service {false};
std::pair<std::string, std::string> bootstrap {};
};
@@ -126,7 +131,9 @@ static const constexpr struct option long_options[] = {
{"identity", no_argument , nullptr, 'i'},
{"verbose", no_argument , nullptr, 'v'},
{"daemonize", no_argument , nullptr, 'd'},
{"service", no_argument , nullptr, 's'},
{"logfile", required_argument, nullptr, 'l'},
{"syslog", no_argument , nullptr, 'L'},
{nullptr, 0 , nullptr, 0}
};
@@ -134,7 +141,7 @@ dht_params
parseArgs(int argc, char **argv) {
dht_params params;
int opt;
while ((opt = getopt_long(argc, argv, "hidvp:n:b:l:", long_options, nullptr)) != -1) {
while ((opt = getopt_long(argc, argv, "hidsvp:n:b:l:", long_options, nullptr)) != -1) {
switch (opt) {
case 'p': {
int port_arg = atoi(optarg);
@@ -150,9 +157,7 @@ parseArgs(int argc, char **argv) {
case 'b':
params.bootstrap = splitPort((optarg[0] == '=') ? optarg+1 : optarg);
if (not params.bootstrap.first.empty() and params.bootstrap.second.empty()) {
std::stringstream ss;
ss << DHT_DEFAULT_PORT;
params.bootstrap.second = ss.str();
params.bootstrap.second = std::to_string(DHT_DEFAULT_PORT);
}
break;
case 'h':
@@ -161,6 +166,10 @@ parseArgs(int argc, char **argv) {
case 'l':
params.logfile = optarg;
break;
case 'L':
params.log = true;
params.syslog = true;
break;
case 'v':
params.log = true;
break;
@@ -170,6 +179,9 @@ parseArgs(int argc, char **argv) {
case 'd':
params.daemonize = true;
break;
case 's':
params.service = true;
break;
default:
break;
}
@@ -195,17 +207,50 @@ readLine(const char* prefix = PROMPT)
return line_read ? std::string(line_read) : std::string("\0", 1);
}
struct ServiceRunner {
bool wait() {
std::unique_lock<std::mutex> lock(m);
cv.wait(lock, [&]{return terminate;});
return !terminate;
}
void kill() {
std::lock_guard<std::mutex> lock(m);
terminate = true;
cv.notify_all();
}
private:
std::condition_variable cv;
std::mutex m;
bool terminate = false;
};
ServiceRunner runner;
void signal_handler(int sig)
{
switch(sig) {
case SIGHUP:
break;
case SIGINT:
case SIGTERM:
exit(EXIT_SUCCESS);
runner.kill();
break;
}
}
void setupSignals()
{
#ifndef WIN32_NATIVE
signal(SIGCHLD,SIG_IGN); /* ignore child */
signal(SIGTSTP,SIG_IGN); /* ignore tty signals */
signal(SIGTTOU,SIG_IGN);
signal(SIGTTIN,SIG_IGN);
signal(SIGHUP,signal_handler); /* catch hangup signal */
signal(SIGINT,signal_handler); /* catch interrupt signal */
signal(SIGTERM,signal_handler); /* catch kill signal */
#endif
}
void daemonize()
{
#ifndef WIN32_NATIVE
@@ -228,11 +273,6 @@ void daemonize()
close(STDOUT_FILENO);
close(STDERR_FILENO);
signal(SIGCHLD,SIG_IGN); /* ignore child */
signal(SIGTSTP,SIG_IGN); /* ignore tty signals */
signal(SIGTTOU,SIG_IGN);
signal(SIGTTIN,SIG_IGN);
signal(SIGHUP,signal_handler); /* catch hangup signal */
signal(SIGTERM,signal_handler); /* catch kill signal */
setupSignals();
#endif
}