Compare commits

...
16 Commits
Author SHA1 Message Date
Adrien Beraud c60c6b8af2 update version to 3.6.2 2025-12-10 12:13:28 -05:00
Adrien Beraud 9f7e4aaa96 Revert "log: add tag and filter mechanism"
This reverts commit afce0fb0b4.
2025-12-10 12:12:05 -05:00
Adrien Béraud 0bfd3cd9fc network engine: check on_expired 2025-12-09 15:46:57 -05:00
Adrien Béraud 442e5501d8 build/meson: update 2025-12-09 15:22:45 -05:00
dependabot[bot] 0203bd8318 build(deps): bump alpine from 3.22 to 3.23 in /docker
Bumps alpine from 3.22 to 3.23.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: '3.23'
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-09 10:44:45 -05:00
Adrien Béraud cd07d68bef crypto: only support webPush with GnuTLS 3.8.2+ 2025-12-09 10:39:58 -05:00
Adrien Béraud 88f3870f20 dhtrunner: add registerInsecureType method and header file 2025-12-09 09:43:30 -05:00
Adrien Béraud d9d2c44e0f format 2025-12-09 09:43:19 -05:00
Adrien Béraud f427f36d9c Add exceptions for missing owner or recipient in unpackValue methods 2025-12-09 09:43:19 -05:00
Adrien Béraud a1c881d914 network engine: replace lambda with direct on_expired callback 2025-12-09 09:42:52 -05:00
Adrien Béraud fbc489863f http: use string_view for resolver, url 2025-12-02 22:28:32 -05:00
Adrien Béraud 504a3638d9 proxy server: add support for encrypted WebPush 2025-12-02 22:28:32 -05:00
Adrien Béraud a85abccd62 proxy server: improve unifiedpush support 2025-12-01 22:20:37 -05:00
Adrien Béraud afce0fb0b4 log: add tag and filter mechanism 2025-12-01 22:20:37 -05:00
Adrien Béraud 99f0334229 http: improve Url parsing 2025-11-30 12:40:17 -05:00
Lucas Fryzek 55ac11bf5c Modify UnifiedPush post request to be AND_3 compliant 2025-11-30 10:54:18 -05:00
27 changed files with 2702 additions and 1570 deletions

No files matched your search

+1 -1
View File
@@ -10,7 +10,7 @@ if(POLICY CMP0167)
endif()
set (opendht_VERSION_MAJOR 3)
set (opendht_VERSION_MINOR 6.0)
set (opendht_VERSION_MINOR 6.2)
set (opendht_VERSION ${opendht_VERSION_MAJOR}.${opendht_VERSION_MINOR})
set (PACKAGE_VERSION ${opendht_VERSION})
set (VERSION "${opendht_VERSION}")
+1 -1
View File
@@ -1,7 +1,7 @@
dnl define macros
m4_define([opendht_major_version], 3)
m4_define([opendht_minor_version], 6)
m4_define([opendht_patch_version], 0)
m4_define([opendht_patch_version], 2)
m4_define([opendht_version],
[opendht_major_version.opendht_minor_version.opendht_patch_version])
+1 -1
View File
@@ -19,7 +19,7 @@ RUN cd opendht && mkdir build && cd build \
-DOPENDHT_SYSTEMD=On \
&& make -j8 && make install
FROM alpine:3.22 AS install
FROM alpine:3.23 AS install
COPY --from=build /install /
RUN apk add --no-cache \
libstdc++ \
+1 -1
View File
@@ -1,4 +1,4 @@
FROM alpine:3.22
FROM alpine:3.23
LABEL maintainer="Adrien Béraud <adrien.beraud@savoirfairelinux.com>"
LABEL org.opencontainers.image.source=https://github.com/savoirfairelinux/opendht
+190 -89
View File
@@ -1,7 +1,5 @@
/*
* Copyright (C) 2014-2025 Savoir-faire Linux Inc.
* Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
* Vsevolod Ivanov <vsevolod.ivanov@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
@@ -47,10 +45,13 @@ namespace dht {
*/
namespace crypto {
class OPENDHT_PUBLIC CryptoException : public std::runtime_error {
class OPENDHT_PUBLIC CryptoException : public std::runtime_error
{
public:
explicit CryptoException(const std::string& str) : std::runtime_error(str) {};
explicit CryptoException(const char* str) : std::runtime_error(str) {};
explicit CryptoException(const std::string& str)
: std::runtime_error(str) {};
explicit CryptoException(const char* str)
: std::runtime_error(str) {};
CryptoException(const CryptoException& e) noexcept = default;
CryptoException& operator=(const CryptoException&) noexcept = default;
};
@@ -58,10 +59,13 @@ public:
/**
* Exception thrown when a decryption error happened.
*/
class OPENDHT_PUBLIC DecryptError : public CryptoException {
class OPENDHT_PUBLIC DecryptError : public CryptoException
{
public:
explicit DecryptError(const std::string& str) : CryptoException(str) {};
explicit DecryptError(const char* str) : CryptoException(str) {};
explicit DecryptError(const std::string& str)
: CryptoException(str) {};
explicit DecryptError(const char* str)
: CryptoException(str) {};
DecryptError(const DecryptError& e) noexcept = default;
DecryptError& operator=(const DecryptError&) noexcept = default;
};
@@ -82,29 +86,35 @@ struct OPENDHT_PUBLIC PublicKey
/**
* Takes ownership of an existing gnutls_pubkey.
*/
PublicKey(gnutls_pubkey_t k) : pk(k) {}
PublicKey(gnutls_pubkey_t k)
: pk(k)
{}
/** Import public key from serialized data */
PublicKey(const uint8_t* dat, size_t dat_size);
PublicKey(const Blob& pk) : PublicKey(pk.data(), pk.size()) {}
PublicKey(std::string_view pk) : PublicKey((const uint8_t*)pk.data(), pk.size()) {}
PublicKey(PublicKey&& o) noexcept : pk(o.pk) { o.pk = nullptr; }
PublicKey(const Blob& pk)
: PublicKey(pk.data(), pk.size())
{}
PublicKey(std::string_view pk)
: PublicKey((const uint8_t*) pk.data(), pk.size())
{}
PublicKey(PublicKey&& o) noexcept
: pk(o.pk)
{
o.pk = nullptr;
}
~PublicKey();
explicit operator bool() const { return pk; }
bool operator ==(const PublicKey& o) const {
return pk == o.pk || getLongId() == o.getLongId();
}
bool operator !=(const PublicKey& o) const {
return !(*this == o);
}
bool operator==(const PublicKey& o) const { return pk == o.pk || getLongId() == o.getLongId(); }
bool operator!=(const PublicKey& o) const { return !(*this == o); }
PublicKey& operator=(PublicKey&& o) noexcept;
/**
* Get public key fingerprint
*/
const InfoHash& getId() const;
const InfoHash& getId() const;
/**
* Get public key long fingerprint
@@ -112,30 +122,27 @@ struct OPENDHT_PUBLIC PublicKey
const PkId& getLongId() const;
bool checkSignature(const uint8_t* data, size_t data_len, const uint8_t* signature, size_t signature_len) const;
inline bool checkSignature(const Blob& data, const Blob& signature) const {
inline bool checkSignature(const Blob& data, const Blob& signature) const
{
return checkSignature(data.data(), data.size(), signature.data(), signature.size());
}
Blob encrypt(const uint8_t* data, size_t data_len) const;
inline Blob encrypt(const Blob& data) const {
return encrypt(data.data(), data.size());
}
inline Blob encrypt(std::string_view data) const {
return encrypt((const uint8_t*)data.data(), data.size());
}
inline Blob encrypt(const Blob& data) const { return encrypt(data.data(), data.size()); }
inline Blob encrypt(std::string_view data) const { return encrypt((const uint8_t*) data.data(), data.size()); }
void pack(Blob& b) const;
int pack(uint8_t* out, size_t* out_len) const;
void unpack(const uint8_t* dat, size_t dat_size);
std::string toString() const;
template <typename Packer>
template<typename Packer>
void msgpack_pack(Packer& p) const
{
Blob b;
pack(b);
p.pack_bin(b.size());
p.pack_bin_body((const char*)b.data(), b.size());
p.pack_bin_body((const char*) b.data(), b.size());
}
void msgpack_unpack(const msgpack::object& o);
@@ -143,6 +150,7 @@ struct OPENDHT_PUBLIC PublicKey
gnutls_digest_algorithm_t getPreferredDigest() const;
gnutls_pubkey_t pk {nullptr};
private:
mutable InfoHash cachedId_ {};
mutable PkId cachedLongId_ {};
@@ -160,7 +168,7 @@ private:
struct OPENDHT_PUBLIC PrivateKey
{
PrivateKey();
//PrivateKey(gnutls_privkey_t k) : key(k) {}
// PrivateKey(gnutls_privkey_t k) : key(k) {}
/**
* Takes ownership of an existing gnutls_x509_privkey.
@@ -171,8 +179,12 @@ struct OPENDHT_PUBLIC PrivateKey
PrivateKey& operator=(PrivateKey&& o) noexcept;
PrivateKey(const uint8_t* src, size_t src_size, const char* password = nullptr);
PrivateKey(const Blob& src, const std::string& password = {}) : PrivateKey(src.data(), src.size(), password.c_str()) {}
PrivateKey(std::string_view src, const std::string& password = {}) : PrivateKey((const uint8_t*)src.data(), src.size(), password.c_str()) {}
PrivateKey(const Blob& src, const std::string& password = {})
: PrivateKey(src.data(), src.size(), password.c_str())
{}
PrivateKey(std::string_view src, const std::string& password = {})
: PrivateKey((const uint8_t*) src.data(), src.size(), password.c_str())
{}
~PrivateKey();
explicit operator bool() const { return key; }
@@ -188,7 +200,7 @@ struct OPENDHT_PUBLIC PrivateKey
* @returns the signature data.
*/
Blob sign(const uint8_t* data, size_t data_len) const;
inline Blob sign(std::string_view dat) const { return sign((const uint8_t*)dat.data(), dat.size()); }
inline Blob sign(std::string_view dat) const { return sign((const uint8_t*) dat.data(), dat.size()); }
inline Blob sign(const Blob& dat) const { return sign(dat.data(), dat.size()); }
/**
@@ -211,6 +223,7 @@ struct OPENDHT_PUBLIC PrivateKey
gnutls_privkey_t key {};
gnutls_x509_privkey_t x509_key {};
private:
PrivateKey(const PrivateKey&) = delete;
PrivateKey& operator=(const PrivateKey&) = delete;
@@ -225,28 +238,39 @@ class OPENDHT_PUBLIC RevocationList
using clock = std::chrono::system_clock;
using time_point = clock::time_point;
using duration = clock::duration;
public:
RevocationList();
RevocationList(const Blob& b);
RevocationList(RevocationList&& o) noexcept : crl(o.crl) { o.crl = nullptr; }
RevocationList(RevocationList&& o) noexcept
: crl(o.crl)
{
o.crl = nullptr;
}
~RevocationList();
RevocationList& operator=(RevocationList&& o) { crl = o.crl; o.crl = nullptr; return *this; }
RevocationList& operator=(RevocationList&& o)
{
crl = o.crl;
o.crl = nullptr;
return *this;
}
void pack(Blob& b) const;
void unpack(const uint8_t* dat, size_t dat_size);
Blob getPacked() const {
Blob getPacked() const
{
Blob b;
pack(b);
return b;
}
template <typename Packer>
template<typename Packer>
void msgpack_pack(Packer& p) const
{
Blob b = getPacked();
p.pack_bin(b.size());
p.pack_bin_body((const char*)b.data(), b.size());
p.pack_bin_body((const char*) b.data(), b.size());
}
void msgpack_unpack(const msgpack::object& o);
@@ -281,7 +305,8 @@ public:
time_point getNextUpdateTime() const;
gnutls_x509_crl_t get() { return crl; }
gnutls_x509_crl_t getCopy() const {
gnutls_x509_crl_t getCopy() const
{
if (not crl)
return nullptr;
auto copy = RevocationList(getPacked());
@@ -298,14 +323,21 @@ private:
enum class NameType { UNKNOWN = 0, RFC822, DNS, URI, IP };
class OPENDHT_PUBLIC CertificateRequest {
class OPENDHT_PUBLIC CertificateRequest
{
public:
CertificateRequest();
CertificateRequest(const uint8_t* data, size_t size);
CertificateRequest(std::string_view src) : CertificateRequest((const uint8_t*)src.data(), src.size()) {}
CertificateRequest(const Blob& data) : CertificateRequest(data.data(), data.size()) {}
CertificateRequest(std::string_view src)
: CertificateRequest((const uint8_t*) src.data(), src.size())
{}
CertificateRequest(const Blob& data)
: CertificateRequest(data.data(), data.size())
{}
CertificateRequest(CertificateRequest&& o) noexcept : request(std::move(o.request)) {
CertificateRequest(CertificateRequest&& o) noexcept
: request(std::move(o.request))
{
o.request = nullptr;
}
CertificateRequest& operator=(CertificateRequest&& o) noexcept;
@@ -327,6 +359,7 @@ public:
std::string toString() const;
gnutls_x509_crq_t get() const { return request; }
private:
CertificateRequest(const CertificateRequest& o) = delete;
CertificateRequest& operator=(const CertificateRequest& o) = delete;
@@ -336,9 +369,13 @@ private:
class OPENDHT_PUBLIC OcspRequest
{
public:
OcspRequest(gnutls_ocsp_req_t r) : request(r) {}
OcspRequest(gnutls_ocsp_req_t r)
: request(r)
{}
OcspRequest(const uint8_t* dat_ptr, size_t dat_size);
OcspRequest(std::string_view dat): OcspRequest((const uint8_t*)dat.data(), dat.size()) {}
OcspRequest(std::string_view dat)
: OcspRequest((const uint8_t*) dat.data(), dat.size())
{}
~OcspRequest();
/*
@@ -348,6 +385,7 @@ public:
Blob pack() const;
Blob getNonce() const;
private:
gnutls_ocsp_req_t request;
};
@@ -356,7 +394,9 @@ class OPENDHT_PUBLIC OcspResponse
{
public:
OcspResponse(const uint8_t* dat_ptr, size_t dat_size);
OcspResponse(std::string_view response) : OcspResponse((const uint8_t*)response.data(), response.size()) {}
OcspResponse(std::string_view response)
: OcspResponse((const uint8_t*) response.data(), response.size())
{}
~OcspResponse();
Blob pack() const;
@@ -383,36 +423,46 @@ private:
gnutls_ocsp_resp_t response;
};
struct OPENDHT_PUBLIC Certificate {
struct OPENDHT_PUBLIC Certificate
{
Certificate() noexcept {}
/**
* Take ownership of existing gnutls structure
*/
Certificate(gnutls_x509_crt_t crt) noexcept : cert(crt) {}
Certificate(gnutls_x509_crt_t crt) noexcept
: cert(crt)
{}
Certificate(Certificate&& o) noexcept
: cert(o.cert)
, issuer(std::move(o.issuer))
, publicKey_(std::move(o.publicKey_))
{ o.cert = nullptr; };
{
o.cert = nullptr;
};
/**
* Import certificate (PEM or DER) or certificate chain (PEM),
* ordered from subject to issuer
*/
Certificate(const Blob& crt);
Certificate(const uint8_t* dat, size_t dat_size) : cert(nullptr) {
Certificate(const uint8_t* dat, size_t dat_size)
: cert(nullptr)
{
unpack(dat, dat_size);
}
Certificate(std::string_view pem) : Certificate((const uint8_t*)pem.data(), pem.size()) {}
Certificate(std::string_view pem)
: Certificate((const uint8_t*) pem.data(), pem.size())
{}
/**
* Import certificate chain (PEM or DER),
* ordered from subject to issuer
*/
template<typename Iterator>
Certificate(const Iterator& begin, const Iterator& end) {
Certificate(const Iterator& begin, const Iterator& end)
{
unpack(begin, end);
}
@@ -421,7 +471,8 @@ struct OPENDHT_PUBLIC Certificate {
* ordered from subject to issuer
*/
template<typename Iterator>
Certificate(const std::vector<std::pair<Iterator, Iterator>>& certs) {
Certificate(const std::vector<std::pair<Iterator, Iterator>>& certs)
{
unpack(certs);
}
@@ -430,7 +481,8 @@ struct OPENDHT_PUBLIC Certificate {
void pack(Blob& b) const;
void unpack(const uint8_t* dat, size_t dat_size);
Blob getPacked() const {
Blob getPacked() const
{
Blob b;
pack(b);
return b;
@@ -479,7 +531,7 @@ struct OPENDHT_PUBLIC Certificate {
for (auto li = certs.rbegin(); li != certs.rend(); ++li) {
Certificate tmp_crt;
gnutls_x509_crt_init(&tmp_crt.cert);
const gnutls_datum_t crt_dt {(uint8_t*)&(*li->first), (unsigned)(li->second-li->first)};
const gnutls_datum_t crt_dt {(uint8_t*) &(*li->first), (unsigned) (li->second - li->first)};
int err = gnutls_x509_crt_import(tmp_crt.cert, &crt_dt, GNUTLS_X509_FMT_PEM);
if (err != GNUTLS_E_SUCCESS)
err = gnutls_x509_crt_import(tmp_crt.cert, &crt_dt, GNUTLS_X509_FMT_DER);
@@ -491,13 +543,13 @@ struct OPENDHT_PUBLIC Certificate {
*this = tmp_issuer ? std::move(*tmp_issuer) : Certificate();
}
template <typename Packer>
template<typename Packer>
void msgpack_pack(Packer& p) const
{
Blob b;
pack(b);
p.pack_bin(b.size());
p.pack_bin_body((const char*)b.data(), b.size());
p.pack_bin_body((const char*) b.data(), b.size());
}
void msgpack_unpack(const msgpack::object& o);
@@ -513,7 +565,7 @@ struct OPENDHT_PUBLIC Certificate {
Blob getSerialNumber() const;
/** Read certificate full DN as described in RFC4514 */
/** Read certificate full DN as described in RFC4514 */
std::string getDN() const;
/** Read certificate Common Name (CN) */
@@ -568,10 +620,15 @@ struct OPENDHT_PUBLIC Certificate {
void addRevocationList(RevocationList&&);
void addRevocationList(std::shared_ptr<RevocationList>);
static Certificate generate(const PrivateKey& key, const std::string& name = "dhtnode", const Identity& ca = {}, bool is_ca = false, int64_t validity = 0);
static Certificate generate(const PrivateKey& key,
const std::string& name = "dhtnode",
const Identity& ca = {},
bool is_ca = false,
int64_t validity = 0);
static Certificate generate(const CertificateRequest& request, const Identity& ca, int64_t validity = 0);
gnutls_x509_crt_t getCopy() const {
gnutls_x509_crt_t getCopy() const
{
if (not cert)
return nullptr;
auto copy = Certificate(getPacked());
@@ -580,8 +637,7 @@ struct OPENDHT_PUBLIC Certificate {
return ret;
}
std::vector<gnutls_x509_crt_t>
getChain(bool copy = false) const
std::vector<gnutls_x509_crt_t> getChain(bool copy = false) const
{
if (not cert)
return {};
@@ -591,11 +647,8 @@ struct OPENDHT_PUBLIC Certificate {
return crts;
}
std::pair<
std::vector<gnutls_x509_crt_t>,
std::vector<gnutls_x509_crl_t>
>
getChainWithRevocations(bool copy = false) const
std::pair<std::vector<gnutls_x509_crt_t>, std::vector<gnutls_x509_crl_t>> getChainWithRevocations(
bool copy = false) const
{
if (not cert)
return {};
@@ -628,6 +681,7 @@ struct OPENDHT_PUBLIC Certificate {
gnutls_x509_crt_t cert {nullptr};
std::shared_ptr<Certificate> issuer {};
std::shared_ptr<OcspResponse> ocspResponse;
private:
Certificate(const Certificate&) = delete;
Certificate& operator=(const Certificate&) = delete;
@@ -636,8 +690,10 @@ private:
mutable std::atomic_bool idCached_ {false};
mutable std::atomic_bool longIdCached_ {false};
struct crlNumberCmp {
bool operator() (const std::shared_ptr<RevocationList>& lhs, const std::shared_ptr<RevocationList>& rhs) const {
struct crlNumberCmp
{
bool operator()(const std::shared_ptr<RevocationList>& lhs, const std::shared_ptr<RevocationList>& rhs) const
{
return lhs->getNumber() < rhs->getNumber();
}
};
@@ -650,18 +706,21 @@ private:
struct OPENDHT_PUBLIC TrustList
{
struct VerifyResult {
struct VerifyResult
{
int ret;
unsigned result;
bool hasError() const { return ret < 0; }
bool isValid() const { return !hasError() and !(result & GNUTLS_CERT_INVALID); }
explicit operator bool() const { return isValid(); }
OPENDHT_PUBLIC std::string toString() const;
OPENDHT_PUBLIC friend std::ostream& operator<< (std::ostream& s, const VerifyResult& h);
OPENDHT_PUBLIC friend std::ostream& operator<<(std::ostream& s, const VerifyResult& h);
};
TrustList();
TrustList(TrustList&& o) noexcept : trust(std::move(o.trust)) {
TrustList(TrustList&& o) noexcept
: trust(std::move(o.trust))
{
o.trust = nullptr;
}
TrustList& operator=(TrustList&& o) noexcept;
@@ -685,13 +744,15 @@ private:
* @param key_length stength of the generated private key (bits).
*/
OPENDHT_PUBLIC Identity generateIdentity(const std::string& name, const Identity& ca, unsigned key_length, bool is_ca);
OPENDHT_PUBLIC Identity generateIdentity(const std::string& name = "dhtnode", const Identity& ca = {}, unsigned key_length = 4096);
OPENDHT_PUBLIC Identity generateIdentity(const std::string& name = "dhtnode",
const Identity& ca = {},
unsigned key_length = 4096);
OPENDHT_PUBLIC Identity generateEcIdentity(const std::string& name, const Identity& ca, bool is_ca);
OPENDHT_PUBLIC Identity generateEcIdentity(const std::string& name = "dhtnode", const Identity& ca = {});
OPENDHT_PUBLIC void saveIdentity(const Identity& id, const std::string& path, const std::string& privkey_password = {});
OPENDHT_PUBLIC Identity loadIdentity(const std::string &path,const std::string &privkey_password = {});
OPENDHT_PUBLIC Identity loadIdentity(const std::string& path, const std::string& privkey_password = {});
/**
* Performs SHA512, SHA256 or SHA1, depending on hash_length.
@@ -701,7 +762,7 @@ OPENDHT_PUBLIC Identity loadIdentity(const std::string &path,const std::string &
* will use SHA256 for hash_length up to 32 bytes,
* will use SHA512 for hash_length of 33 bytes and more.
*/
OPENDHT_PUBLIC Blob hash(const Blob& data, size_t hash_length = 512/8);
OPENDHT_PUBLIC Blob hash(const Blob& data, size_t hash_length = 512 / 8);
OPENDHT_PUBLIC void hash(const uint8_t* data, size_t data_length, uint8_t* hash, size_t hash_length);
@@ -712,20 +773,23 @@ OPENDHT_PUBLIC void hash(const uint8_t* data, size_t data_length, uint8_t* hash,
* that can be transmitted in clear, and will be generated if
* not provided (32 bytes).
*/
OPENDHT_PUBLIC Blob stretchKey(std::string_view password, Blob& salt, size_t key_length = 512/8);
OPENDHT_PUBLIC Blob stretchKey(std::string_view 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 uint8_t* data, size_t data_length, const Blob& key);
OPENDHT_PUBLIC inline Blob aesEncrypt(const Blob& data, const Blob& key) {
OPENDHT_PUBLIC inline Blob
aesEncrypt(const Blob& data, const Blob& key)
{
return aesEncrypt(data.data(), data.size(), key);
}
/**
* AES-GCM encryption with argon2 key derivation.
* This function uses `stretchKey` to generate an AES key from the password and a random salt.
* The result is a bundle including the salt that can be decrypted with `aesDecrypt(data, password)`.
* If needed, the salt or encrypted data can be individually extracted from the bundle with `aesGetSalt` and `aesGetEncrypted`.
* If needed, the salt or encrypted data can be individually extracted from the bundle with `aesGetSalt` and
* `aesGetEncrypted`.
* @param data: data to encrypt
* @param password: password to encrypt the data with
* @param salt: optional salt to use for key derivation. If not provided, a random salt will be generated.
@@ -736,28 +800,47 @@ OPENDHT_PUBLIC Blob aesEncrypt(const Blob& data, std::string_view password, cons
* AES-GCM decryption.
*/
OPENDHT_PUBLIC Blob aesDecrypt(const uint8_t* data, size_t data_length, const Blob& key);
OPENDHT_PUBLIC inline Blob aesDecrypt(const Blob& data, const Blob& key) { return aesDecrypt(data.data(), data.size(), key); }
OPENDHT_PUBLIC inline Blob aesDecrypt(std::string_view data, const Blob& key) { return aesDecrypt((uint8_t*)data.data(), data.size(), key); }
OPENDHT_PUBLIC inline Blob
aesDecrypt(const Blob& data, const Blob& key)
{
return aesDecrypt(data.data(), data.size(), key);
}
OPENDHT_PUBLIC inline Blob
aesDecrypt(std::string_view data, const Blob& key)
{
return aesDecrypt((uint8_t*) data.data(), data.size(), key);
}
OPENDHT_PUBLIC Blob aesDecrypt(const uint8_t* data, size_t data_length, std::string_view password);
OPENDHT_PUBLIC inline Blob aesDecrypt(const Blob& data, std::string_view password) { return aesDecrypt(data.data(), data.size(), password); }
OPENDHT_PUBLIC inline Blob aesDecrypt(std::string_view data, std::string_view password) { return aesDecrypt((uint8_t*)data.data(), data.size(), password); }
OPENDHT_PUBLIC inline Blob
aesDecrypt(const Blob& data, std::string_view password)
{
return aesDecrypt(data.data(), data.size(), password);
}
OPENDHT_PUBLIC inline Blob
aesDecrypt(std::string_view data, std::string_view password)
{
return aesDecrypt((uint8_t*) data.data(), data.size(), password);
}
/**
* Get raw AES key from password and salt stored with the encrypted data.
*/
OPENDHT_PUBLIC Blob aesGetKey(const uint8_t* data, size_t data_length, std::string_view password);
OPENDHT_PUBLIC Blob inline aesGetKey(const Blob& data, std::string_view password) {
OPENDHT_PUBLIC Blob inline aesGetKey(const Blob& data, std::string_view password)
{
return aesGetKey(data.data(), data.size(), password);
}
/** Get the salt part of data password-encrypted with `aesEncrypt(data, password)` */
OPENDHT_PUBLIC Blob aesGetSalt(const uint8_t* data, size_t data_length);
OPENDHT_PUBLIC Blob inline aesGetSalt(const Blob& data) {
OPENDHT_PUBLIC Blob inline aesGetSalt(const Blob& data)
{
return aesGetSalt(data.data(), data.size());
}
/** Get the encrypted data (ciphertext) part of data password-encrypted with `aesEncrypt(data, password)` */
OPENDHT_PUBLIC std::string_view aesGetEncrypted(const uint8_t* data, size_t data_length);
OPENDHT_PUBLIC std::string_view inline aesGetEncrypted(const Blob& data) {
OPENDHT_PUBLIC std::string_view inline aesGetEncrypted(const Blob& data)
{
return aesGetEncrypted(data.data(), data.size());
}
@@ -767,12 +850,30 @@ OPENDHT_PUBLIC std::string_view inline aesGetEncrypted(const Blob& data) {
* Can be obtained from an existing bundle with `aesGetSalt`.
**/
OPENDHT_PUBLIC Blob aesBuildEncrypted(const uint8_t* encryptedData, size_t data_length, const Blob& salt);
OPENDHT_PUBLIC Blob inline aesBuildEncrypted(const Blob& encryptedData, const Blob& salt) {
OPENDHT_PUBLIC Blob inline aesBuildEncrypted(const Blob& encryptedData, const Blob& salt)
{
return aesBuildEncrypted(encryptedData.data(), encryptedData.size(), salt);
}
OPENDHT_PUBLIC Blob inline aesBuildEncrypted(std::string_view encryptedData, const Blob& salt) {
return aesBuildEncrypted((const uint8_t*)encryptedData.data(), encryptedData.size(), salt);
OPENDHT_PUBLIC Blob inline aesBuildEncrypted(std::string_view encryptedData, const Blob& salt)
{
return aesBuildEncrypted((const uint8_t*) encryptedData.data(), encryptedData.size(), salt);
}
}
}
/**
* Encrypts a payload for WebPush (RFC 8291).
* @param p256dh: The user's P-256 public key (uncompressed, 65 bytes).
* @param auth: The user's authentication secret (16 bytes).
* @param payload: The data to encrypt.
* @param privKey: (Optional) The sender's private key (for testing).
* @param salt: (Optional) The salt to use (for testing).
* @return The encrypted body (including salt, record size, keyid).
*/
OPENDHT_PUBLIC Blob webPushEncrypt(const Blob& p256dh,
const Blob& auth,
const uint8_t* payload,
size_t payload_length,
const PrivateKey& privKey = {},
const Blob& salt = {});
} // namespace crypto
} // namespace dht
+5 -3
View File
@@ -69,6 +69,8 @@ public:
if (v.owner) {
owner = v.owner;
from = v.owner->getId();
} else {
throw std::runtime_error("Value has no owner");
}
BaseClass::unpackValue(v);
}
@@ -89,6 +91,9 @@ public:
public:
virtual void unpackValue(const Value& v) override {
if (!v.recipient) {
throw std::runtime_error("Value has no recipient");
}
to = v.recipient;
BaseClass::unpackValue(v);
}
@@ -103,9 +108,6 @@ public:
dht::InfoHash to;
};
class OPENDHT_PUBLIC ImMessage : public SignedValue<ImMessage>
{
private:
+1
View File
@@ -366,6 +366,7 @@ public:
void setLogFilter(const InfoHash& f = {});
void registerType(const ValueType& type);
void registerInsecureType(const ValueType& type);
void importValues(const std::vector<ValuesExport>& values);
+64 -58
View File
@@ -25,8 +25,8 @@
// some libraries may try to redefine snprintf
// but restinio will use it in std namespace
#ifdef _MSC_VER
# undef snprintf
# define snprintf snprintf
#undef snprintf
#define snprintf snprintf
#endif
#include <asio/ip/tcp.hpp>
@@ -47,7 +47,7 @@ namespace restinio {
namespace impl {
class tls_socket_t;
}
}
} // namespace restinio
namespace dht {
namespace log {
@@ -62,8 +62,7 @@ namespace http {
using HandlerCb = std::function<void(const asio::error_code& ec)>;
using BytesHandlerCb = std::function<void(const asio::error_code& ec, size_t bytes)>;
using ConnectHandlerCb = std::function<void(const asio::error_code& ec,
const asio::ip::tcp::endpoint& endpoint)>;
using ConnectHandlerCb = std::function<void(const asio::error_code& ec, const asio::ip::tcp::endpoint& endpoint)>;
using ssl_socket_t = restinio::impl::tls_socket_t;
using socket_t = asio::ip::tcp::socket;
@@ -75,11 +74,13 @@ public:
Url(std::string_view url);
std::string url;
std::string protocol {"http"};
std::string host;
std::string service;
std::string_view user;
std::string_view password;
std::string_view host;
std::string_view service;
std::string target;
std::string query;
std::string fragment;
std::string_view query;
std::string_view fragment;
std::string toString() const;
};
@@ -88,11 +89,13 @@ class OPENDHT_PUBLIC Connection : public std::enable_shared_from_this<Connection
{
public:
Connection(asio::io_context& ctx, const bool ssl = true, std::shared_ptr<log::Logger> l = {});
Connection(asio::io_context& ctx, std::shared_ptr<dht::crypto::Certificate> server_ca,
const dht::crypto::Identity& identity, std::shared_ptr<log::Logger> l = {});
Connection(asio::io_context& ctx,
std::shared_ptr<dht::crypto::Certificate> server_ca,
const dht::crypto::Identity& identity,
std::shared_ptr<log::Logger> l = {});
~Connection();
inline unsigned int id() const { return id_; };
inline unsigned int id() const { return id_; };
bool is_open() const;
bool is_ssl() const;
void checkOcsp(bool check = true) { checkOcsp_ = check; }
@@ -122,10 +125,10 @@ public:
void close();
private:
template<typename T>
T wrapCallback(T cb) const {
return [t=shared_from_this(),cb=std::move(cb)](auto ...params) {
T wrapCallback(T cb) const
{
return [t = shared_from_this(), cb = std::move(cb)](auto... params) {
cb(params...);
};
}
@@ -159,35 +162,38 @@ private:
class OPENDHT_PUBLIC Resolver
{
public:
using ResolverCb = std::function<void(const asio::error_code& ec,
const std::vector<asio::ip::tcp::endpoint>& endpoints)>;
using ResolverCb
= std::function<void(const asio::error_code& ec, const std::vector<asio::ip::tcp::endpoint>& endpoints)>;
Resolver(asio::io_context& ctx, const std::string& url, std::shared_ptr<log::Logger> logger = {});
Resolver(asio::io_context& ctx, const std::string& host, const std::string& service,
const bool ssl = false, std::shared_ptr<log::Logger> logger = {});
Resolver(asio::io_context& ctx,
std::string_view host,
std::string_view service,
const bool ssl = false,
std::shared_ptr<log::Logger> logger = {});
// use already resolved endpoints with classes using this resolver
Resolver(asio::io_context& ctx, std::vector<asio::ip::tcp::endpoint> endpoints,
const bool ssl = false, std::shared_ptr<log::Logger> logger = {});
Resolver(asio::io_context& ctx, const std::string& url, std::vector<asio::ip::tcp::endpoint> endpoints,
std::shared_ptr<log::Logger> logger = {});
Resolver(asio::io_context& ctx,
std::vector<asio::ip::tcp::endpoint> endpoints,
const bool ssl = false,
std::shared_ptr<log::Logger> logger = {});
Resolver(asio::io_context& ctx,
const std::string& url,
std::vector<asio::ip::tcp::endpoint> endpoints,
std::shared_ptr<log::Logger> logger = {});
~Resolver();
inline const Url& get_url() const {
return url_;
}
inline const Url& get_url() const { return url_; }
void add_callback(ResolverCb cb, sa_family_t family = AF_UNSPEC);
std::shared_ptr<log::Logger> getLogger() const {
return logger_;
}
std::shared_ptr<log::Logger> getLogger() const { return logger_; }
void cancel();
private:
void resolve(const std::string& host, const std::string& service);
void resolve(std::string_view host, std::string_view service);
mutable std::mutex mutex_;
@@ -217,13 +223,7 @@ struct Response
class OPENDHT_PUBLIC Request : public std::enable_shared_from_this<Request>
{
public:
enum class State {
CREATED,
SENDING,
HEADER_RECEIVED,
RECEIVING,
DONE
};
enum class State { CREATED, SENDING, HEADER_RECEIVED, RECEIVING, DONE };
using OnStatusCb = std::function<void(unsigned status_code)>;
using OnDataCb = std::function<void(const char* at, size_t length)>;
using OnStateChangeCb = std::function<void(State state, const Response& response)>;
@@ -231,46 +231,51 @@ public:
using OnDoneCb = std::function<void(const Response& response)>;
// resolves implicitly
Request(asio::io_context& ctx, const std::string& url, const Json::Value& json, OnJsonCb jsoncb,
std::shared_ptr<log::Logger> logger = {});
Request(asio::io_context& ctx, const std::string& url, OnJsonCb jsoncb,
Request(asio::io_context& ctx,
const std::string& url,
const Json::Value& json,
OnJsonCb jsoncb,
std::shared_ptr<log::Logger> logger = {});
Request(asio::io_context& ctx, const std::string& url, OnJsonCb jsoncb, std::shared_ptr<log::Logger> logger = {});
Request(asio::io_context& ctx, const std::string& url, std::shared_ptr<log::Logger> logger = {});
Request(asio::io_context& ctx, const std::string& host, const std::string& service,
const bool ssl = false, std::shared_ptr<log::Logger> logger = {});
Request(asio::io_context& ctx,
std::string_view host,
std::string_view service,
const bool ssl = false,
std::shared_ptr<log::Logger> logger = {});
Request(asio::io_context& ctx, const std::string& url, OnDoneCb onDone, std::shared_ptr<log::Logger> logger = {});
// user defined resolver
Request(asio::io_context& ctx, std::shared_ptr<Resolver> resolver, sa_family_t family = AF_UNSPEC);
Request(asio::io_context& ctx, std::shared_ptr<Resolver> resolver, const std::string& target, sa_family_t family = AF_UNSPEC);
Request(asio::io_context& ctx,
std::shared_ptr<Resolver> resolver,
const std::string& target,
sa_family_t family = AF_UNSPEC);
// user defined resolved endpoints
Request(asio::io_context& ctx, std::vector<asio::ip::tcp::endpoint>&& endpoints,
const bool ssl = false, std::shared_ptr<log::Logger> logger = {});
Request(asio::io_context& ctx,
std::vector<asio::ip::tcp::endpoint>&& endpoints,
const bool ssl = false,
std::shared_ptr<log::Logger> logger = {});
~Request();
inline unsigned int id() const { return id_; };
inline unsigned int id() const { return id_; };
void set_connection(std::shared_ptr<Connection> connection);
std::shared_ptr<Connection> get_connection() const;
inline const Url& get_url() const {
return resolver_->get_url();
};
inline const Url& get_url() const { return resolver_->get_url(); };
void timeout(const std::chrono::seconds& timeout, HandlerCb cb = {}) {
void timeout(const std::chrono::seconds& timeout, HandlerCb cb = {})
{
timeout_ = timeout;
timeoutCb_ = cb;
}
/** The previous request in case of redirect following */
std::shared_ptr<Request> getPrevious() const {
return prev_.lock();
}
std::shared_ptr<Request> getPrevious() const { return prev_.lock(); }
inline std::string& to_string() {
return request_;
}
inline std::string& to_string() { return request_; }
void set_certificate_authority(std::shared_ptr<dht::crypto::Certificate> certificate);
void set_identity(const dht::crypto::Identity& identity);
@@ -285,6 +290,7 @@ public:
void set_header_field(restinio::http_field_t field, std::string value);
void set_connection_type(restinio::http_connection_header_t connection);
void set_body(std::string body);
void set_body(const uint8_t* data, size_t length);
void set_auth(const std::string& username, const std::string& password);
void add_on_status_callback(OnStatusCb cb);
@@ -306,7 +312,8 @@ public:
private:
using OnCompleteCb = std::function<void()>;
struct Callbacks {
struct Callbacks
{
OnStatusCb on_status;
OnDataCb on_header_field;
OnDataCb on_header_value;
@@ -381,4 +388,3 @@ private:
} // namespace http
} // namespace dht
+30 -15
View File
@@ -52,14 +52,16 @@ bool setNonblocking(int fd, bool nonblocking = true);
#ifdef _WIN32
void udpPipe(int fds[2]);
#endif
struct ReceivedPacket {
struct ReceivedPacket
{
Blob data;
SockAddr from;
time_point received;
};
using PacketList = std::list<ReceivedPacket>;
class OPENDHT_PUBLIC DatagramSocket {
class OPENDHT_PUBLIC DatagramSocket
{
public:
/** A function that takes a list of new received packets and
* optionally returns consumed packets for recycling.
@@ -69,7 +71,8 @@ public:
virtual int sendTo(const SockAddr& dest, const uint8_t* data, size_t size, bool replied) = 0;
inline void setOnReceive(OnReceive&& cb) {
inline void setOnReceive(OnReceive&& cb)
{
std::lock_guard<std::mutex> lk(lock);
rx_callback = std::move(cb);
}
@@ -77,11 +80,13 @@ public:
virtual bool hasIPv4() const = 0;
virtual bool hasIPv6() const = 0;
SockAddr getBound(sa_family_t family = AF_UNSPEC) const {
SockAddr getBound(sa_family_t family = AF_UNSPEC) const
{
std::lock_guard<std::mutex> lk(lock);
return getBoundRef(family);
}
in_port_t getPort(sa_family_t family = AF_UNSPEC) const {
in_port_t getPort(sa_family_t family = AF_UNSPEC) const
{
std::lock_guard<std::mutex> lk(lock);
return getBoundRef(family).getPort();
}
@@ -89,14 +94,16 @@ public:
virtual const SockAddr& getBoundRef(sa_family_t family = AF_UNSPEC) const = 0;
/** Virtual resolver mothod allows to implement custom resolver */
virtual std::vector<SockAddr> resolve(const std::string& host, const std::string& service = {}) {
virtual std::vector<SockAddr> resolve(const std::string& host, const std::string& service = {})
{
return SockAddr::resolve(host, service);
}
virtual void stop() = 0;
protected:
PacketList getNewPacket() {
protected:
PacketList getNewPacket()
{
PacketList pkts;
if (toRecycle_.empty()) {
pkts.emplace_back();
@@ -108,7 +115,8 @@ protected:
return pkts;
}
inline void onReceived(PacketList&& packets) {
inline void onReceived(PacketList&& packets)
{
std::lock_guard<std::mutex> lk(lock);
if (rx_callback) {
auto r = rx_callback(std::move(packets));
@@ -116,14 +124,17 @@ protected:
toRecycle_.splice(toRecycle_.end(), std::move(r));
}
}
protected:
mutable std::mutex lock;
private:
OnReceive rx_callback;
PacketList toRecycle_;
};
class OPENDHT_PUBLIC UdpSocket : public DatagramSocket {
class OPENDHT_PUBLIC UdpSocket : public DatagramSocket
{
public:
UdpSocket(in_port_t port, const std::shared_ptr<Logger>& l = {});
UdpSocket(const SockAddr& bind4, const SockAddr& bind6, const std::shared_ptr<Logger>& l = {});
@@ -131,20 +142,24 @@ public:
int sendTo(const SockAddr& dest, const uint8_t* data, size_t size, bool replied) override;
const SockAddr& getBoundRef(sa_family_t family = AF_UNSPEC) const override {
const SockAddr& getBoundRef(sa_family_t family = AF_UNSPEC) const override
{
return (family == AF_INET6) ? bound6 : bound4;
}
bool hasIPv4() const override {
bool hasIPv4() const override
{
std::lock_guard<std::mutex> lk(lock);
return s4 != -1;
}
bool hasIPv6() const override {
bool hasIPv6() const override
{
std::lock_guard<std::mutex> lk(lock);
return s6 != -1;
}
void stop() override;
private:
std::shared_ptr<Logger> logger;
int s4 {-1};
@@ -157,5 +172,5 @@ private:
void openSockets(const SockAddr& bind4, const SockAddr& bind6);
};
}
}
} // namespace net
} // namespace dht
+12 -1
View File
@@ -62,10 +62,21 @@ erase_if(std::map<Key, Item>& map, const Condition& condition)
}
}
template<typename... Args>
std::string
concat(Args&&... args)
{
static_assert((std::is_constructible_v<std::string_view, Args&&> && ...));
std::string s;
s.reserve((std::string_view {args}.size() + ...));
(s.append(std::forward<Args>(args)), ...);
return s;
}
/**
* Split "[host]:port" or "host:port" to pair<"host", "port">.
*/
OPENDHT_PUBLIC std::pair<std::string, std::string> splitPort(std::string_view s);
OPENDHT_PUBLIC std::pair<std::string_view, std::string_view> splitPort(std::string_view s);
class OPENDHT_PUBLIC DhtException : public std::runtime_error
{
+6 -3
View File
@@ -3,7 +3,7 @@ project(
'c',
'cpp',
'cython',
version: '3.6.0',
version: '3.6.2',
meson_version: '>=1.1.0',
default_options: ['cpp_std=c++17', 'warning_level=3'],
)
@@ -29,8 +29,11 @@ deps = [
jsoncpp,
simdutf,
llhttp,
io_uring,
]
if host_machine.system() == 'linux' and io_uring.found()
deps += io_uring
endif
conf_data = configuration_data()
add_project_arguments('-DMSGPACK_NO_BOOST', '-DASIO_STANDALONE', language: 'cpp')
@@ -245,7 +248,7 @@ if get_option('tests').enabled()
'tests/tests_runner.cpp',
include_directories: opendht_interface_inc,
link_with: opendht,
dependencies: [cppunit, jsoncpp, fmt, openssl, msgpack],
dependencies: [cppunit, jsoncpp, fmt, openssl, msgpack, gnutls],
)
test('Crypto', test_crypto)
+14 -7
View File
@@ -16,31 +16,38 @@
*/
#include "base64.h"
#include "utils.h"
#include <simdutf.h>
#include <vector>
#include <cstdint>
#include <cstdlib>
#include <stdexcept>
namespace dht {
using namespace std::literals;
std::string
base64_encode(const std::vector<unsigned char>& str)
{
std::string buffer(simdutf::base64_length_from_binary(str.size()), '\0');
simdutf::binary_to_base64((const char*)str.data(), str.size(), buffer.data());
simdutf::binary_to_base64((const char*) str.data(), str.size(), buffer.data());
return buffer;
}
std::vector<unsigned char>
base64_decode(const std::string& str)
base64_decode(std::string_view str)
{
std::vector<unsigned char> buffer(simdutf::maximal_binary_length_from_base64(str.data(), str.size()));
simdutf::result r = simdutf::base64_to_binary(str.data(), str.size(), (char *)buffer.data());
if(r.error) {
// Decoding error, return an empty vector
return std::vector<unsigned char>();
simdutf::result r = simdutf::base64_to_binary(str.data(), str.size(), (char*) buffer.data());
if (r.error) {
throw std::invalid_argument(concat("Invalid base64 input: "sv, simdutf::error_to_string(r.error)));
} else {
buffer.resize(r.count); // resize the buffer according to actual number of bytes
buffer.resize(r.count);
}
return buffer;
}
} // namespace dht
+5 -1
View File
@@ -20,6 +20,8 @@
#include <string>
#include <vector>
namespace dht {
/**
* Encode a buffer in base64.
*
@@ -33,4 +35,6 @@ std::string base64_encode(const std::vector<unsigned char>& str);
* @param str the input buffer
* @return a base64-decoded buffer
*/
std::vector<unsigned char> base64_decode(const std::string& str);
std::vector<unsigned char> base64_decode(std::string_view str);
} // namespace dht
+450 -185
View File
File diff suppressed because it is too large. Load diff
+293 -256
View File
File diff suppressed because it is too large. Load diff
+417 -285
View File
File diff suppressed because it is too large. Load diff
+184 -143
View File
@@ -40,18 +40,20 @@ using namespace std::literals;
constexpr auto PEER_DISCOVERY_DHT_SERVICE = "dht"sv;
struct NodeInsertionPack {
struct NodeInsertionPack
{
dht::InfoHash nodeId;
in_port_t port;
dht::NetId net;
MSGPACK_DEFINE(nodeId, port, net)
};
DhtRunner::DhtRunner() : dht_()
DhtRunner::DhtRunner()
: dht_()
{
#ifdef _WIN32
WSADATA wsd;
if (WSAStartup(MAKEWORD(2,2), &wsd) != 0)
if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
throw DhtException("Can't initialize Winsock2");
#endif
}
@@ -95,7 +97,8 @@ DhtRunner::run(const Config& config, Context&& context)
auto expected = State::Idle;
if (not running.compare_exchange_strong(expected, State::Running)) {
if (context.logger)
context.logger->w("[runner %p] Node is already running. Call join() first before calling run() again.", fmt::ptr(this));
context.logger->w("[runner %p] Node is already running. Call join() first before calling run() again.",
fmt::ptr(this));
return;
}
@@ -104,7 +107,9 @@ DhtRunner::run(const Config& config, Context&& context)
auto local6 = config.bind6;
if (not local4 and not local6) {
if (context.logger)
context.logger->w("[runner %p] No address to bind specified in the configuration, using default addresses", fmt::ptr(this));
context.logger
->w("[runner %p] No address to bind specified in the configuration, using default addresses",
fmt::ptr(this));
local4.setFamily(AF_INET);
local6.setFamily(AF_INET6);
}
@@ -118,14 +123,18 @@ DhtRunner::run(const Config& config, Context&& context)
if (inConfig >> port) {
if (local4.getPort() == 0) {
if (context.logger)
context.logger->d("[runner %p] Using IPv4 port %hu from saved configuration", fmt::ptr(this), port);
context.logger->d("[runner %p] Using IPv4 port %hu from saved configuration",
fmt::ptr(this),
port);
local4.setPort(port);
}
}
if (inConfig >> port) {
if (local6.getPort() == 0) {
if (context.logger)
context.logger->d("[runner %p] Using IPv6 port %hu from saved configuration", fmt::ptr(this), port);
context.logger->d("[runner %p] Using IPv6 port %hu from saved configuration",
fmt::ptr(this),
port);
local6.setPort(port);
}
}
@@ -146,7 +155,7 @@ DhtRunner::run(const Config& config, Context&& context)
if (not context.sock) {
context.sock.reset(new net::UdpSocket(local4, local6, context.logger));
}
context.sock->setOnReceive([&] (net::PacketList&& pkts) {
context.sock->setOnReceive([&](net::PacketList&& pkts) {
net::PacketList ret;
{
std::lock_guard<std::mutex> lck(sock_mtx);
@@ -169,12 +178,18 @@ DhtRunner::run(const Config& config, Context&& context)
outConfig << context.sock->getBoundRef(AF_INET).getPort() << std::endl;
outConfig << context.sock->getBoundRef(AF_INET6).getPort() << std::endl;
}
auto dht = std::make_unique<Dht>(std::move(context.sock), SecureDht::getConfig(config.dht_config), context.logger, std::move(context.rng));
dht_ = std::make_unique<SecureDht>(std::move(dht), config.dht_config, std::move(context.identityAnnouncedCb), context.logger);
auto dht = std::make_unique<Dht>(std::move(context.sock),
SecureDht::getConfig(config.dht_config),
context.logger,
std::move(context.rng));
dht_ = std::make_unique<SecureDht>(std::move(dht),
config.dht_config,
std::move(context.identityAnnouncedCb),
context.logger);
} else {
enableProxy(true);
}
} catch(const std::exception& e) {
} catch (const std::exception& e) {
config_ = {};
identityAnnouncedCb_ = {};
dht_.reset();
@@ -215,7 +230,8 @@ DhtRunner::run(const Config& config, Context&& context)
if (not pending_ops_prio.empty())
return true;
auto s = getStatus();
if (not pending_ops.empty() and (s == NodeStatus::Connected or s == NodeStatus::Disconnected or running == State::Stopping))
if (not pending_ops.empty()
and (s == NodeStatus::Connected or s == NodeStatus::Disconnected or running == State::Stopping))
return true;
}
return false;
@@ -230,9 +246,8 @@ DhtRunner::run(const Config& config, Context&& context)
if (config.proxy_server.empty()) {
if (config.peer_discovery or config.peer_publish) {
#ifdef OPENDHT_PEER_DISCOVERY
peerDiscovery_ = context.peerDiscovery ?
std::move(context.peerDiscovery) :
std::make_shared<PeerDiscovery>();
peerDiscovery_ = context.peerDiscovery ? std::move(context.peerDiscovery)
: std::make_shared<PeerDiscovery>();
#else
std::cerr << "Peer discovery requested but OpenDHT built without peer discovery support." << std::endl;
#endif
@@ -241,12 +256,13 @@ DhtRunner::run(const Config& config, Context&& context)
#ifdef OPENDHT_PEER_DISCOVERY
auto netId = config.dht_config.node_config.network;
if (config.peer_discovery) {
peerDiscovery_->startDiscovery<NodeInsertionPack>(PEER_DISCOVERY_DHT_SERVICE, [this, netId](NodeInsertionPack&& v, SockAddr&& addr){
addr.setPort(v.port);
if (v.nodeId != dht_->getNodeId() && netId == v.net){
bootstrap(v.nodeId, addr);
}
});
peerDiscovery_->startDiscovery<NodeInsertionPack>(PEER_DISCOVERY_DHT_SERVICE,
[this, netId](NodeInsertionPack&& v, SockAddr&& addr) {
addr.setPort(v.port);
if (v.nodeId != dht_->getNodeId() && netId == v.net) {
bootstrap(v.nodeId, addr);
}
});
}
if (config.peer_publish) {
msgpack::sbuffer sbuf_node;
@@ -273,11 +289,9 @@ DhtRunner::run(const Config& config, Context&& context)
statusCbs.emplace_back([this](NodeStatus status4, NodeStatus status6) {
if (status4 == NodeStatus::Disconnected && status6 == NodeStatus::Disconnected) {
peerDiscovery_->connectivityChanged();
}
else if (status4 != NodeStatus::Connected || status6 != NodeStatus::Connected) {
} else if (status4 != NodeStatus::Connected || status6 != NodeStatus::Connected) {
peerDiscovery_->stopConnectivityChanged();
}
});
}
#endif
@@ -285,15 +299,15 @@ DhtRunner::run(const Config& config, Context&& context)
}
void
DhtRunner::shutdown(ShutdownCallback cb, bool stop) {
DhtRunner::shutdown(ShutdownCallback cb, bool stop)
{
std::unique_lock<std::mutex> lck(storage_mtx);
auto expected = State::Running;
if (not running.compare_exchange_strong(expected, State::Stopping)) {
if (expected == State::Stopping and ongoing_ops) {
if (cb)
shutdownCallbacks_.emplace_back(std::move(cb));
}
else if (cb) {
} else if (cb) {
lck.unlock();
cb();
}
@@ -304,7 +318,9 @@ DhtRunner::shutdown(ShutdownCallback cb, bool stop) {
ongoing_ops++;
shutdownCallbacks_.emplace_back(std::move(cb));
pending_ops.emplace([=](SecureDht&) mutable {
auto onShutdown = [this]{ opEnded(); };
auto onShutdown = [this] {
opEnded();
};
if (dht_)
dht_->shutdown(onShutdown, stop);
else
@@ -314,29 +330,35 @@ DhtRunner::shutdown(ShutdownCallback cb, bool stop) {
}
void
DhtRunner::opEnded() {
DhtRunner::opEnded()
{
if (--ongoing_ops == 0)
checkShutdown();
}
DoneCallback
DhtRunner::bindOpDoneCallback(DoneCallback&& cb) {
return [this, cb = std::move(cb)](bool ok, const std::vector<std::shared_ptr<Node>>& nodes){
if (cb) cb(ok, nodes);
DhtRunner::bindOpDoneCallback(DoneCallback&& cb)
{
return [this, cb = std::move(cb)](bool ok, const std::vector<std::shared_ptr<Node>>& nodes) {
if (cb)
cb(ok, nodes);
opEnded();
};
}
DoneCallbackSimple
DhtRunner::bindOpDoneCallback(DoneCallbackSimple&& cb) {
return [this, cb = std::move(cb)](bool ok){
if (cb) cb(ok);
DhtRunner::bindOpDoneCallback(DoneCallbackSimple&& cb)
{
return [this, cb = std::move(cb)](bool ok) {
if (cb)
cb(ok);
opEnded();
};
}
bool
DhtRunner::checkShutdown() {
DhtRunner::checkShutdown()
{
decltype(shutdownCallbacks_) cbs;
{
std::lock_guard<std::mutex> lck(storage_mtx);
@@ -345,7 +367,8 @@ DhtRunner::checkShutdown() {
cbs = std::move(shutdownCallbacks_);
}
for (auto& cb : cbs)
if (cb) cb();
if (cb)
cb();
return true;
}
@@ -390,16 +413,18 @@ DhtRunner::join()
}
SockAddr
DhtRunner::getBound(sa_family_t af) const {
DhtRunner::getBound(sa_family_t af) const
{
std::lock_guard<std::mutex> lck(dht_mtx);
if (dht_)
if (auto sock = dht_->getSocket())
return sock->getBound(af);
return SockAddr{};
return SockAddr {};
}
in_port_t
DhtRunner::getBoundPort(sa_family_t af) const {
DhtRunner::getBoundPort(sa_family_t af) const
{
std::lock_guard<std::mutex> lck(dht_mtx);
if (dht_)
if (auto sock = dht_->getSocket())
@@ -417,24 +442,24 @@ DhtRunner::dumpTables() const
InfoHash
DhtRunner::getId() const
{
return dht_ ? dht_->getId() : InfoHash{};
return dht_ ? dht_->getId() : InfoHash {};
}
std::shared_ptr<crypto::PublicKey>
DhtRunner::getPublicKey() const
{
return dht_ ? dht_->getPublicKey() : std::shared_ptr<crypto::PublicKey>{};
return dht_ ? dht_->getPublicKey() : std::shared_ptr<crypto::PublicKey> {};
}
InfoHash
DhtRunner::getNodeId() const
{
return dht_ ? dht_->getNodeId() : InfoHash{};
return dht_ ? dht_->getNodeId() : InfoHash {};
}
std::pair<size_t, size_t>
DhtRunner::getStoreSize() const {
DhtRunner::getStoreSize() const
{
std::lock_guard<std::mutex> lck(dht_mtx);
if (!dht_)
return {};
@@ -442,7 +467,8 @@ DhtRunner::getStoreSize() const {
}
void
DhtRunner::setStorageLimit(size_t limit) {
DhtRunner::setStorageLimit(size_t limit)
{
std::lock_guard<std::mutex> lck(dht_mtx);
if (!dht_)
throw std::runtime_error("dht is not running");
@@ -450,7 +476,8 @@ DhtRunner::setStorageLimit(size_t limit) {
}
std::vector<NodeExport>
DhtRunner::exportNodes() const {
DhtRunner::exportNodes() const
{
std::lock_guard<std::mutex> lck(dht_mtx);
if (!dht_)
return {};
@@ -458,7 +485,8 @@ DhtRunner::exportNodes() const {
}
std::vector<ValuesExport>
DhtRunner::exportValues() const {
DhtRunner::exportValues() const
{
std::lock_guard<std::mutex> lck(dht_mtx);
if (!dht_)
return {};
@@ -466,7 +494,8 @@ DhtRunner::exportValues() const {
}
void
DhtRunner::setLogger(const Sp<Logger>& logger) {
DhtRunner::setLogger(const Sp<Logger>& logger)
{
std::lock_guard<std::mutex> lck(dht_mtx);
logger_ = logger;
if (dht_)
@@ -474,26 +503,40 @@ DhtRunner::setLogger(const Sp<Logger>& logger) {
}
void
DhtRunner::setLogFilter(const InfoHash& f) {
DhtRunner::setLogFilter(const InfoHash& f)
{
std::lock_guard<std::mutex> lck(dht_mtx);
if (dht_)
dht_->setLogFilter(f);
}
void
DhtRunner::registerType(const ValueType& type) {
DhtRunner::registerType(const ValueType& type)
{
std::lock_guard<std::mutex> lck(dht_mtx);
dht_->registerType(type);
}
void
DhtRunner::importValues(const std::vector<ValuesExport>& values) {
DhtRunner::registerInsecureType(const ValueType& type)
{
std::lock_guard<std::mutex> lck(dht_mtx);
dht_->registerInsecureType(type);
}
void
DhtRunner::importValues(const std::vector<ValuesExport>& values)
{
std::lock_guard<std::mutex> lck(dht_mtx);
dht_->importValues(values);
}
unsigned
DhtRunner::getNodesStats(sa_family_t af, unsigned *good_return, unsigned *dubious_return, unsigned *cached_return, unsigned *incoming_return) const
DhtRunner::getNodesStats(sa_family_t af,
unsigned* good_return,
unsigned* dubious_return,
unsigned* cached_return,
unsigned* incoming_return) const
{
std::lock_guard<std::mutex> lck(dht_mtx);
const auto stats = dht_->getNodesStats(af);
@@ -516,7 +559,8 @@ DhtRunner::getNodesStats(sa_family_t af) const
}
NodeInfo
DhtRunner::getNodeInfo() const {
DhtRunner::getNodeInfo() const
{
std::lock_guard<std::mutex> lck(dht_mtx);
NodeInfo info {};
if (dht_) {
@@ -538,7 +582,7 @@ DhtRunner::getNodeInfo(std::function<void(std::shared_ptr<NodeInfo>)> cb)
{
std::lock_guard<std::mutex> lck(storage_mtx);
ongoing_ops++;
pending_ops_prio.emplace([cb = std::move(cb), this](SecureDht& dht){
pending_ops_prio.emplace([cb = std::move(cb), this](SecureDht& dht) {
auto sinfo = std::make_shared<NodeInfo>();
auto& info = *sinfo;
info.id = dht.getId();
@@ -616,7 +660,7 @@ DhtRunner::getPublicAddress(std::function<void(std::vector<SockAddr>&&)> cb, sa_
{
std::lock_guard<std::mutex> lck(storage_mtx);
ongoing_ops++;
pending_ops_prio.emplace([cb = std::move(cb), this, af](SecureDht& dht){
pending_ops_prio.emplace([cb = std::move(cb), this, af](SecureDht& dht) {
cb(dht.getPublicAddress(af));
opEnded();
});
@@ -624,19 +668,22 @@ DhtRunner::getPublicAddress(std::function<void(std::vector<SockAddr>&&)> cb, sa_
}
void
DhtRunner::registerCertificate(const std::shared_ptr<crypto::Certificate>& cert) {
DhtRunner::registerCertificate(const std::shared_ptr<crypto::Certificate>& cert)
{
std::lock_guard<std::mutex> lck(dht_mtx);
dht_->registerCertificate(cert);
}
void
DhtRunner::setLocalCertificateStore(CertificateStoreQueryLegacy&& query_method) {
DhtRunner::setLocalCertificateStore(CertificateStoreQueryLegacy&& query_method)
{
std::lock_guard<std::mutex> lck(dht_mtx);
if (dht_)
dht_->setLocalCertificateStore(std::forward<CertificateStoreQueryLegacy>(query_method));
}
void DhtRunner::setLocalCertificateStore(CertificateStoreQuery&& query_method)
void
DhtRunner::setLocalCertificateStore(CertificateStoreQuery&& query_method)
{
std::lock_guard<std::mutex> lck(dht_mtx);
if (dht_)
@@ -653,8 +700,10 @@ DhtRunner::loop_()
{
std::lock_guard<std::mutex> lck(storage_mtx);
auto s = getStatus();
ops = (pending_ops_prio.empty() && (s == NodeStatus::Connected or s == NodeStatus::Disconnected or running == State::Stopping)) ?
std::move(pending_ops) : std::move(pending_ops_prio);
ops = (pending_ops_prio.empty()
&& (s == NodeStatus::Connected or s == NodeStatus::Disconnected or running == State::Stopping))
? std::move(pending_ops)
: std::move(pending_ops_prio);
}
while (not ops.empty()) {
ops.front()(*dht_);
@@ -713,7 +762,7 @@ DhtRunner::loop_()
if (nstatus4 != status4 || nstatus6 != status6) {
status4 = nstatus4;
status6 = nstatus6;
for (auto& cb : statusCbs){
for (auto& cb : statusCbs) {
cb(status4, status6);
}
}
@@ -727,7 +776,8 @@ DhtRunner::get(InfoHash hash, GetCallback vcb, DoneCallback dcb, Value::Filter f
std::unique_lock<std::mutex> lck(storage_mtx);
if (running != State::Running) {
lck.unlock();
if (dcb) dcb(false, {});
if (dcb)
dcb(false, {});
return;
}
ongoing_ops++;
@@ -743,11 +793,13 @@ DhtRunner::get(const std::string& key, GetCallback vcb, DoneCallbackSimple dcb,
get(InfoHash::get(key), std::move(vcb), std::move(dcb), std::move(f), std::move(w));
}
void
DhtRunner::query(const InfoHash& hash, QueryCallback cb, DoneCallback done_cb, Query q) {
DhtRunner::query(const InfoHash& hash, QueryCallback cb, DoneCallback done_cb, Query q)
{
std::unique_lock<std::mutex> lck(storage_mtx);
if (running != State::Running) {
lck.unlock();
if (done_cb) done_cb(false, {});
if (done_cb)
done_cb(false, {});
return;
}
ongoing_ops++;
@@ -814,14 +866,12 @@ DhtRunner::put(InfoHash hash, Value&& value, DoneCallback cb, time_point created
std::unique_lock<std::mutex> lck(storage_mtx);
if (running != State::Running) {
lck.unlock();
if (cb) cb(false, {});
if (cb)
cb(false, {});
return;
}
ongoing_ops++;
pending_ops.emplace([=,
cb = std::move(cb),
sv = std::make_shared<Value>(std::move(value))
] (SecureDht& dht) mutable {
pending_ops.emplace([=, cb = std::move(cb), sv = std::make_shared<Value>(std::move(value))](SecureDht& dht) mutable {
dht.put(hash, sv, bindOpDoneCallback(std::move(cb)), created, permanent);
});
cv.notify_all();
@@ -833,7 +883,8 @@ DhtRunner::put(InfoHash hash, std::shared_ptr<Value> value, DoneCallback cb, tim
std::unique_lock<std::mutex> lck(storage_mtx);
if (running != State::Running) {
lck.unlock();
if (cb) cb(false, {});
if (cb)
cb(false, {});
return;
}
ongoing_ops++;
@@ -853,9 +904,7 @@ void
DhtRunner::cancelPut(const InfoHash& h, Value::Id id)
{
std::lock_guard<std::mutex> lck(storage_mtx);
pending_ops.emplace([=](SecureDht& dht) {
dht.cancelPut(h, id);
});
pending_ops.emplace([=](SecureDht& dht) { dht.cancelPut(h, id); });
cv.notify_all();
}
@@ -863,9 +912,7 @@ void
DhtRunner::cancelPut(const InfoHash& h, const std::shared_ptr<Value>& value)
{
std::lock_guard<std::mutex> lck(storage_mtx);
pending_ops.emplace([=](SecureDht& dht) {
dht.cancelPut(h, value->id);
});
pending_ops.emplace([=](SecureDht& dht) { dht.cancelPut(h, value->id); });
cv.notify_all();
}
@@ -875,14 +922,12 @@ DhtRunner::putSigned(InfoHash hash, std::shared_ptr<Value> value, DoneCallback c
std::unique_lock<std::mutex> lck(storage_mtx);
if (running != State::Running) {
lck.unlock();
if (cb) cb(false, {});
if (cb)
cb(false, {});
return;
}
ongoing_ops++;
pending_ops.emplace([=,
cb = std::move(cb),
value = std::move(value)
](SecureDht& dht) mutable {
pending_ops.emplace([=, cb = std::move(cb), value = std::move(value)](SecureDht& dht) mutable {
dht.putSigned(hash, value, bindOpDoneCallback(std::move(cb)), permanent);
});
cv.notify_all();
@@ -906,14 +951,12 @@ DhtRunner::putEncrypted(InfoHash hash, const PkId& to, std::shared_ptr<Value> va
std::unique_lock<std::mutex> lck(storage_mtx);
if (running != State::Running) {
lck.unlock();
if (cb) cb(false, {});
if (cb)
cb(false, {});
return;
}
ongoing_ops++;
pending_ops.emplace([=,
cb = std::move(cb),
value = std::move(value)
] (SecureDht& dht) mutable {
pending_ops.emplace([=, cb = std::move(cb), value = std::move(value)](SecureDht& dht) mutable {
dht.putEncrypted(hash, to, value, bindOpDoneCallback(std::move(cb)), permanent);
});
cv.notify_all();
@@ -925,14 +968,12 @@ DhtRunner::putEncrypted(InfoHash hash, InfoHash to, std::shared_ptr<Value> value
std::unique_lock<std::mutex> lck(storage_mtx);
if (running != State::Running) {
lck.unlock();
if (cb) cb(false, {});
if (cb)
cb(false, {});
return;
}
ongoing_ops++;
pending_ops.emplace([=,
cb = std::move(cb),
value = std::move(value)
] (SecureDht& dht) mutable {
pending_ops.emplace([=, cb = std::move(cb), value = std::move(value)](SecureDht& dht) mutable {
dht.putEncrypted(hash, to, value, bindOpDoneCallback(std::move(cb)), permanent);
});
cv.notify_all();
@@ -951,26 +992,29 @@ DhtRunner::putEncrypted(const std::string& key, InfoHash to, Value&& value, Done
}
void
DhtRunner::putEncrypted(InfoHash hash, const std::shared_ptr<crypto::PublicKey>& to, std::shared_ptr<Value> value, DoneCallback cb, bool permanent)
DhtRunner::putEncrypted(InfoHash hash,
const std::shared_ptr<crypto::PublicKey>& to,
std::shared_ptr<Value> value,
DoneCallback cb,
bool permanent)
{
std::unique_lock<std::mutex> lck(storage_mtx);
if (running != State::Running) {
lck.unlock();
if (cb) cb(false, {});
if (cb)
cb(false, {});
return;
}
ongoing_ops++;
pending_ops.emplace([=,
cb = std::move(cb),
value = std::move(value)
] (SecureDht& dht) mutable {
pending_ops.emplace([=, cb = std::move(cb), value = std::move(value)](SecureDht& dht) mutable {
dht.putEncrypted(hash, *to, value, bindOpDoneCallback(std::move(cb)), permanent);
});
cv.notify_all();
}
void
DhtRunner::putEncrypted(InfoHash hash, const std::shared_ptr<crypto::PublicKey>& to, Value&& value, DoneCallback cb, bool permanent)
DhtRunner::putEncrypted(
InfoHash hash, const std::shared_ptr<crypto::PublicKey>& to, Value&& value, DoneCallback cb, bool permanent)
{
putEncrypted(hash, to, std::make_shared<Value>(std::move(value)), std::move(cb), permanent);
}
@@ -979,9 +1023,7 @@ void
DhtRunner::bootstrap(const std::string& host, const std::string& service)
{
std::lock_guard<std::mutex> lck(storage_mtx);
pending_ops_prio.emplace([host, service] (SecureDht& dht) mutable {
dht.addBootstrap(host, service);
});
pending_ops_prio.emplace([host, service](SecureDht& dht) mutable { dht.addBootstrap(host, service); });
cv.notify_all();
}
@@ -989,9 +1031,9 @@ void
DhtRunner::bootstrap(const std::string& hostService)
{
std::lock_guard<std::mutex> lck(storage_mtx);
pending_ops_prio.emplace([host_service = splitPort(hostService)] (SecureDht& dht) mutable {
dht.addBootstrap(host_service.first, host_service.second);
});
auto [h, s] = splitPort(hostService);
pending_ops_prio.emplace(
[host = std::string(h), service = std::string(s)](SecureDht& dht) { dht.addBootstrap(host, service); });
cv.notify_all();
}
@@ -999,9 +1041,7 @@ void
DhtRunner::clearBootstrap()
{
std::lock_guard<std::mutex> lck(storage_mtx);
pending_ops_prio.emplace([] (SecureDht& dht) mutable {
dht.clearBootstrap();
});
pending_ops_prio.emplace([](SecureDht& dht) mutable { dht.clearBootstrap(); });
cv.notify_all();
}
@@ -1014,15 +1054,12 @@ DhtRunner::bootstrap(std::vector<SockAddr> nodes, DoneCallbackSimple cb)
}
std::lock_guard<std::mutex> lck(storage_mtx);
ongoing_ops++;
pending_ops_prio.emplace([
cb = bindOpDoneCallback(std::move(cb)),
nodes = std::move(nodes)
] (SecureDht& dht) mutable {
pending_ops_prio.emplace([cb = bindOpDoneCallback(std::move(cb)), nodes = std::move(nodes)](SecureDht& dht) mutable {
auto rem = cb ? std::make_shared<std::pair<size_t, bool>>(nodes.size(), false) : nullptr;
for (auto& node : nodes) {
if (node.getPort() == 0)
node.setPort(net::DHT_DEFAULT_PORT);
dht.pingNode(std::move(node), [rem,cb](bool ok) {
dht.pingNode(std::move(node), [rem, cb](bool ok) {
auto& r = *rem;
r.first--;
r.second |= ok;
@@ -1041,7 +1078,8 @@ DhtRunner::bootstrap(SockAddr addr, DoneCallbackSimple cb)
std::unique_lock<std::mutex> lck(storage_mtx);
if (running != State::Running) {
lck.unlock();
if (cb) cb(false);
if (cb)
cb(false);
return;
}
ongoing_ops++;
@@ -1057,9 +1095,7 @@ DhtRunner::bootstrap(const InfoHash& id, const SockAddr& address)
std::lock_guard<std::mutex> lck(storage_mtx);
if (running != State::Running)
return;
pending_ops_prio.emplace([id, address](SecureDht& dht) mutable {
dht.insertNode(id, address);
});
pending_ops_prio.emplace([id, address](SecureDht& dht) mutable { dht.insertNode(id, address); });
cv.notify_all();
}
@@ -1091,7 +1127,8 @@ DhtRunner::connectivityChanged()
}
void
DhtRunner::findCertificate(InfoHash hash, std::function<void(const Sp<crypto::Certificate>&)> cb) {
DhtRunner::findCertificate(InfoHash hash, std::function<void(const Sp<crypto::Certificate>&)> cb)
{
std::unique_lock<std::mutex> lck(storage_mtx);
if (running != State::Running) {
lck.unlock();
@@ -1099,8 +1136,8 @@ DhtRunner::findCertificate(InfoHash hash, std::function<void(const Sp<crypto::Ce
return;
}
ongoing_ops++;
pending_ops.emplace([this, hash, cb = std::move(cb)] (SecureDht& dht) {
dht.findCertificate(hash, [this, cb = std::move(cb)](const Sp<crypto::Certificate>& crt){
pending_ops.emplace([this, hash, cb = std::move(cb)](SecureDht& dht) {
dht.findCertificate(hash, [this, cb = std::move(cb)](const Sp<crypto::Certificate>& crt) {
cb(crt);
opEnded();
});
@@ -1108,7 +1145,8 @@ DhtRunner::findCertificate(InfoHash hash, std::function<void(const Sp<crypto::Ce
cv.notify_all();
}
void DhtRunner::findCertificate(PkId hash, std::function<void(const std::shared_ptr<crypto::Certificate>&)> cb)
void
DhtRunner::findCertificate(PkId hash, std::function<void(const std::shared_ptr<crypto::Certificate>&)> cb)
{
std::unique_lock<std::mutex> lck(storage_mtx);
if (running != State::Running) {
@@ -1116,8 +1154,8 @@ void DhtRunner::findCertificate(PkId hash, std::function<void(const std::shared_
return;
}
ongoing_ops++;
pending_ops.emplace([this, hash, cb = std::move(cb)] (SecureDht& dht) {
dht.findCertificate(hash, [this, cb = std::move(cb)](const Sp<crypto::Certificate>& crt){
pending_ops.emplace([this, hash, cb = std::move(cb)](SecureDht& dht) {
dht.findCertificate(hash, [this, cb = std::move(cb)](const Sp<crypto::Certificate>& crt) {
cb(crt);
opEnded();
});
@@ -1159,22 +1197,22 @@ DhtRunner::enableProxy(bool proxify)
if (proxify) {
// Init the proxy client
auto dht_via_proxy = std::make_unique<DhtProxyClient>(
config_.server_ca,
config_.client_identity,
[this]{
if (config_.threaded) {
std::lock_guard<std::mutex> lck(storage_mtx);
pending_ops_prio.emplace([=](SecureDht&) mutable {});
cv.notify_all();
}
},
config_.proxy_server,
config_.proxy_user_agent,
config_.push_node_id,
config_.push_token,
config_.push_topic,
config_.push_platform,
logger_);
config_.server_ca,
config_.client_identity,
[this] {
if (config_.threaded) {
std::lock_guard<std::mutex> lck(storage_mtx);
pending_ops_prio.emplace([=](SecureDht&) mutable {});
cv.notify_all();
}
},
config_.proxy_server,
config_.proxy_user_agent,
config_.push_node_id,
config_.push_token,
config_.push_topic,
config_.push_platform,
logger_);
dht_ = std::make_unique<SecureDht>(std::move(dht_via_proxy), config_.dht_config, identityAnnouncedCb_, logger_);
// and use it
use_proxy = proxify;
@@ -1199,7 +1237,8 @@ DhtRunner::forwardAllMessages(bool forward)
* Updates the push notification device token
*/
void
DhtRunner::setPushNotificationToken(const std::string& token) {
DhtRunner::setPushNotificationToken(const std::string& token)
{
std::lock_guard<std::mutex> lck(dht_mtx);
#if defined(OPENDHT_PROXY_CLIENT) && defined(OPENDHT_PUSH_NOTIFICATIONS)
config_.push_token = token;
@@ -1211,7 +1250,8 @@ DhtRunner::setPushNotificationToken(const std::string& token) {
}
void
DhtRunner::setPushNotificationTopic(const std::string& topic) {
DhtRunner::setPushNotificationTopic(const std::string& topic)
{
std::lock_guard<std::mutex> lck(dht_mtx);
#if defined(OPENDHT_PROXY_CLIENT) && defined(OPENDHT_PUSH_NOTIFICATIONS)
config_.push_topic = topic;
@@ -1223,7 +1263,8 @@ DhtRunner::setPushNotificationTopic(const std::string& topic) {
}
void
DhtRunner::setPushNotificationPlatform(const std::string& platform) {
DhtRunner::setPushNotificationPlatform(const std::string& platform)
{
std::lock_guard<std::mutex> lck(dht_mtx);
#if defined(OPENDHT_PROXY_CLIENT) && defined(OPENDHT_PUSH_NOTIFICATIONS)
config_.push_platform = platform;
@@ -1256,4 +1297,4 @@ DhtRunner::pushNotificationReceived([[maybe_unused]] const std::map<std::string,
#endif
}
}
} // namespace dht
+509 -336
View File
File diff suppressed because it is too large. Load diff
+9 -31
View File
@@ -294,7 +294,8 @@ NetworkEngine::requestStep(Sp<Request> sreq)
requests.erase(req.tid);
return;
} else if (req.attempt_count == 1) {
req.on_expired(req, false);
if (req.on_expired)
req.on_expired(req, false);
}
auto err = send(node.getAddr(), (char*)req.msg.data(), req.msg.size(), node.getReplyTime() < now - UDP_REPLY_TIME);
@@ -749,11 +750,7 @@ NetworkEngine::sendPing(const Sp<Node>& node, RequestCb&& on_done, RequestExpire
on_done(req_status, {});
}
},
[=](const Request& req_status, bool done) { /* on expired */
if (on_expired) {
on_expired(req_status, done);
}
}
std::move(on_expired)
);
sendRequest(req);
++out_stats.ping;
@@ -819,11 +816,7 @@ NetworkEngine::sendFindNode(const Sp<Node>& n, const InfoHash& target, want_t wa
on_done(req_status, {std::forward<ParsedMessage>(msg)});
}
},
[=](const Request& req_status, bool done) { /* on expired */
if (on_expired) {
on_expired(req_status, done);
}
}
std::move(on_expired)
);
sendRequest(req);
++out_stats.find;
@@ -875,11 +868,7 @@ NetworkEngine::sendGetValues(const Sp<Node>& n, const InfoHash& info_hash, const
on_done(req_status, {std::forward<ParsedMessage>(msg)});
}
},
[=](const Request& req_status, bool done) { /* on expired */
if (on_expired) {
on_expired(req_status, done);
}
}
std::move(on_expired)
);
sendRequest(req);
++out_stats.get;
@@ -1157,10 +1146,7 @@ NetworkEngine::sendListen(const Sp<Node>& n,
if (on_done)
on_done(req_status, {std::forward<ParsedMessage>(msg)});
},
[=](const Request& req_status, bool done) { /* on expired */
if (on_expired)
on_expired(req_status, done);
}
std::move(on_expired)
);
sendRequest(req);
++out_stats.listen;
@@ -1240,11 +1226,7 @@ NetworkEngine::sendAnnounceValue(const Sp<Node>& n,
}
}
},
[=](const Request& req_status, bool done) { /* on expired */
if (on_expired) {
on_expired(req_status, done);
}
}
std::move(on_expired)
);
req->parts = std::move(v);
sendRequest(req);
@@ -1372,12 +1354,8 @@ NetworkEngine::sendRefreshValue(const Sp<Node>& n,
}
}
},
on_error,
[=](const Request& req_status, bool done) { /* on expired */
if (on_expired) {
on_expired(req_status, done);
}
}
std::move(on_error),
std::move(on_expired)
);
sendRequest(req);
++out_stats.refresh;
+2 -1
View File
@@ -89,7 +89,8 @@ struct Request {
void setExpired() {
if (pending()) {
state_ = Request::State::EXPIRED;
on_expired(*this, true);
if (on_expired)
on_expired(*this, true);
clear();
}
}
+47 -31
View File
@@ -20,7 +20,7 @@
#include "config.h"
#endif
#if defined(_WIN32) && (_WIN32_WINNT < 0x0502) && (NTDDI_VERSION >= NTDDI_VISTA)
#if defined(_WIN32) && (_WIN32_WINNT < 0x0502) && (NTDDI_VERSION >= NTDDI_VISTA)
#define _WIN32_WINNT _WIN32_WINNT_WIN10
#include <winsock2.h>
#include <ws2tcpip.h>
@@ -42,30 +42,35 @@
namespace dht {
const char* version() {
const char*
version()
{
return PACKAGE_VERSION;
}
static constexpr std::array<uint8_t, 12> MAPPED_IPV4_PREFIX {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff}};
static constexpr std::array<uint8_t, 12> MAPPED_IPV4_PREFIX {
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff}
};
std::pair<std::string, std::string>
splitPort(std::string_view s) {
std::pair<std::string_view, std::string_view>
splitPort(std::string_view s)
{
if (s.empty())
return {};
if (s[0] == '[') {
std::size_t closure = s.find_first_of(']');
std::size_t found = s.find_last_of(':');
if (closure == std::string_view::npos)
return {std::string(s), ""};
return {s, ""};
if (found == std::string_view::npos or found < closure)
return {std::string(s.substr(1,closure-1)), ""};
return {std::string(s.substr(1,closure-1)), std::string(s.substr(found+1))};
return {s.substr(1, closure - 1), ""};
return {s.substr(1, closure - 1), s.substr(found + 1)};
}
std::size_t found = s.find_last_of(':');
std::size_t first = s.find_first_of(':');
if (found == std::string_view::npos or found != first)
return {std::string(s), ""};
return {std::string(s.substr(0,found)), std::string(s.substr(found+1))};
return {s, ""};
return {s.substr(0, found), s.substr(found + 1)};
}
std::vector<SockAddr>
@@ -80,7 +85,7 @@ SockAddr::resolve(const std::string& host, const std::string& service)
hints.ai_socktype = SOCK_DGRAM;
addrinfo* info = nullptr;
int rc = getaddrinfo(host.c_str(), service.empty() ? nullptr : service.c_str(), &hints, &info);
if(rc != 0)
if (rc != 0)
throw std::invalid_argument(std::string("Error: `") + host + ":" + service + "`: " + gai_strerror(rc));
for (addrinfo* infop = info; infop; infop = infop->ai_next)
@@ -108,11 +113,13 @@ SockAddr::setAddress(const char* address)
throw std::runtime_error(std::string("Can't parse IP address: ") + strerror(errno));
}
void print_addr(std::ostream& out, const sockaddr* sa, socklen_t slen)
void
print_addr(std::ostream& out, const sockaddr* sa, socklen_t slen)
{
char hbuf[NI_MAXHOST];
char sbuf[NI_MAXSERV];
if (sa and slen and !getnameinfo(sa, slen, hbuf, sizeof(hbuf), sbuf, sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV)) {
if (sa and slen
and !getnameinfo(sa, slen, hbuf, sizeof(hbuf), sbuf, sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV)) {
if (sa->sa_family == AF_INET6)
out << '[' << hbuf << ']';
else
@@ -134,7 +141,7 @@ print_addr(const sockaddr* sa, socklen_t slen)
std::string
print_addr(const sockaddr_storage& ss, socklen_t sslen)
{
return print_addr((const sockaddr*)&ss, sslen);
return print_addr((const sockaddr*) &ss, sslen);
}
bool
@@ -156,7 +163,7 @@ SockAddr::isLoopback() const
switch (getFamily()) {
case AF_INET: {
auto addr_host = ntohl(getIPv4().sin_addr.s_addr);
uint8_t b1 = (uint8_t)(addr_host >> 24);
uint8_t b1 = (uint8_t) (addr_host >> 24);
return b1 == 127;
}
case AF_INET6:
@@ -176,8 +183,8 @@ SockAddr::isPrivate() const
case AF_INET: {
auto addr_host = ntohl(getIPv4().sin_addr.s_addr);
uint8_t b1, b2;
b1 = (uint8_t)(addr_host >> 24);
b2 = (uint8_t)((addr_host >> 16) & 0x0ff);
b1 = (uint8_t) (addr_host >> 24);
b2 = (uint8_t) ((addr_host >> 16) & 0x0ff);
// 10.x.y.z
if (b1 == 10)
return true;
@@ -240,11 +247,15 @@ SockAddr::getMappedIPv6()
return ret;
}
bool operator==(const SockAddr& a, const SockAddr& b) {
bool
operator==(const SockAddr& a, const SockAddr& b)
{
return a.equals(b);
}
time_point from_time_t(std::time_t t) {
time_point
from_time_t(std::time_t t)
{
auto dt = system_clock::from_time_t(t) - system_clock::now();
auto now = clock::now();
if (dt > system_clock::duration(0) and now > time_point::max() - dt)
@@ -254,7 +265,9 @@ time_point from_time_t(std::time_t t) {
return now + dt;
}
std::time_t to_time_t(time_point t) {
std::time_t
to_time_t(time_point t)
{
auto dt = t - clock::now();
auto now = system_clock::now();
if (dt > duration(0) and now >= system_clock::time_point::max() - dt)
@@ -265,15 +278,16 @@ std::time_t to_time_t(time_point t) {
}
Blob
unpackBlob(const msgpack::object& o) {
unpackBlob(const msgpack::object& o)
{
switch (o.type) {
case msgpack::type::BIN:
return {o.via.bin.ptr, o.via.bin.ptr+o.via.bin.size};
return {o.via.bin.ptr, o.via.bin.ptr + o.via.bin.size};
case msgpack::type::STR:
return {o.via.str.ptr, o.via.str.ptr+o.via.str.size};
return {o.via.str.ptr, o.via.str.ptr + o.via.str.size};
case msgpack::type::ARRAY: {
Blob ret(o.via.array.size);
std::transform(o.via.array.ptr, o.via.array.ptr+o.via.array.size, ret.begin(), [](const msgpack::object& b) {
std::transform(o.via.array.ptr, o.via.array.ptr + o.via.array.size, ret.begin(), [](const msgpack::object& b) {
return b.as<uint8_t>();
});
return ret;
@@ -284,21 +298,23 @@ unpackBlob(const msgpack::object& o) {
}
msgpack::unpacked
unpackMsg(Blob b) {
return msgpack::unpack((const char*)b.data(), b.size());
unpackMsg(Blob b)
{
return msgpack::unpack((const char*) b.data(), b.size());
}
msgpack::object*
findMapValue(const msgpack::object& map, const char* key, size_t key_length) {
if (map.type != msgpack::type::MAP) throw msgpack::type_error();
findMapValue(const msgpack::object& map, const char* key, size_t key_length)
{
if (map.type != msgpack::type::MAP)
throw msgpack::type_error();
for (unsigned i = 0; i < map.via.map.size; i++) {
auto& o = map.via.map.ptr[i];
if (o.key.type == msgpack::type::STR
&& key_length == o.key.via.str.size
if (o.key.type == msgpack::type::STR && key_length == o.key.via.str.size
&& std::strncmp(o.key.via.str.ptr, key, o.key.via.str.size) == 0)
return &o.val;
}
return nullptr;
}
}
} // namespace dht
+71 -49
View File
@@ -30,19 +30,22 @@
#include "base64.h"
#endif
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 {};
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)
std::ostream&
operator<<(std::ostream& s, const Value& v)
{
auto flags(s.flags());
s << "Value[id:" << std::hex << v.id << std::dec << ' ';
@@ -240,7 +243,8 @@ Value::toJson() const
}
uint64_t
unpackId(const Json::Value& json, const std::string& key) {
unpackId(const Json::Value& json, const std::string& key)
{
uint64_t ret = 0;
try {
const auto& t = json[key];
@@ -249,7 +253,8 @@ unpackId(const Json::Value& json, const std::string& key) {
} else {
ret = t.asLargestUInt();
}
} catch (...) {}
} catch (...) {
}
return ret;
}
#endif
@@ -297,7 +302,7 @@ Value::decrypt(const crypto::PrivateKey& key)
decrypted = true;
if (isEncrypted()) {
auto decryptedBlob = key.decrypt(cypher);
auto msg = msgpack::unpack((const char*)decryptedBlob.data(), decryptedBlob.size());
auto msg = msgpack::unpack((const char*) decryptedBlob.data(), decryptedBlob.size());
auto v = std::make_unique<Value>(id);
v->msgpack_unpack_body(msg.get());
if (v->recipient != key.getPublicKey().getId())
@@ -355,13 +360,13 @@ FieldValueIndex::FieldValueIndex(const Value& v, const Select& s)
{
if (not s.empty()) {
auto selection = s.getSelection();
std::transform(selection.begin(), selection.end(), std::inserter(index, index.end()),
[](const std::set<Value::Field>::value_type& f) {
return std::make_pair(f, FieldValue {});
});
std::transform(selection.begin(),
selection.end(),
std::inserter(index, index.end()),
[](const std::set<Value::Field>::value_type& f) { return std::make_pair(f, FieldValue {}); });
} else {
index.clear();
for (size_t f = 1 ; f < static_cast<int>(Value::Field::COUNT) ; ++f)
for (size_t f = 1; f < static_cast<int>(Value::Field::COUNT); ++f)
index[static_cast<Value::Field>(f)] = {};
}
for (const auto& fvp : index) {
@@ -374,13 +379,15 @@ FieldValueIndex::FieldValueIndex(const Value& v, const Select& s)
index[f] = {f, v.type};
break;
case Value::Field::OwnerPk:
index[f] = {f, v.owner ? v.owner->getId() : InfoHash() };
index[f] = {f, v.owner ? v.owner->getId() : InfoHash()};
break;
case Value::Field::SeqNum:
index[f] = {f, v.seq};
break;
case Value::Field::UserType:
index[f] = {f, Blob {v.user_type.begin(), v.user_type.end()}};
index[f] = {
f, Blob {v.user_type.begin(), v.user_type.end()}
};
break;
default:
break;
@@ -388,7 +395,9 @@ FieldValueIndex::FieldValueIndex(const Value& v, const Select& s)
}
}
bool FieldValueIndex::containedIn(const FieldValueIndex& other) const {
bool
FieldValueIndex::containedIn(const FieldValueIndex& other) const
{
if (index.size() > other.index.size())
return false;
for (const auto& field : index) {
@@ -399,7 +408,9 @@ bool FieldValueIndex::containedIn(const FieldValueIndex& other) const {
return true;
}
std::ostream& operator<<(std::ostream& os, const FieldValueIndex& fvi) {
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) {
@@ -438,7 +449,7 @@ FieldValueIndex::msgpack_unpack_fields(const std::set<Value::Field>& fields, con
unsigned j = 0;
for (const auto& field : fields) {
auto& field_value = o.via.array.ptr[offset+(j++)];
auto& field_value = o.via.array.ptr[offset + (j++)];
switch (field) {
case Value::Field::Id:
case Value::Field::ValueType:
@@ -457,13 +468,16 @@ FieldValueIndex::msgpack_unpack_fields(const std::set<Value::Field>& fields, con
}
}
void trim_str(std::string& str) {
void
trim_str(std::string& str)
{
auto first = std::min(str.size(), str.find_first_not_of(' '));
auto last = std::min(str.size(), str.find_last_not_of(' '));
str = str.substr(first, last - first + 1);
}
Select::Select(std::string_view q_str) {
Select::Select(std::string_view q_str)
{
std::istringstream q_iss {std::string(q_str)};
std::string token {};
q_iss >> token;
@@ -488,7 +502,8 @@ Select::Select(std::string_view q_str) {
}
}
Where::Where(std::string_view q_str) {
Where::Where(std::string_view q_str)
{
std::istringstream q_iss {std::string(q_str)};
std::string token {};
q_iss >> token;
@@ -509,11 +524,9 @@ Where::Where(std::string_view q_str) {
std::string s {};
std::istringstream convert {value_str};
convert >> v;
if (not convert
and value_str.size() > 1
and value_str[0] == '"'
and value_str[value_str.size()-1] == '"')
s = value_str.substr(1, value_str.size()-2);
if (not convert and 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;
if (field_str == VALUE_KEY_ID)
@@ -527,7 +540,8 @@ Where::Where(std::string_view q_str) {
else if (field_str == "user_type")
userType(s);
else
throw std::invalid_argument(Query::QUERY_PARSE_ERROR + " (WHERE) incorrect token near: " + field_str);
throw std::invalid_argument(Query::QUERY_PARSE_ERROR
+ " (WHERE) incorrect token near: " + field_str);
}
}
}
@@ -536,24 +550,25 @@ Where::Where(std::string_view q_str) {
void
Query::msgpack_unpack(const msgpack::object& o)
{
if (o.type != msgpack::type::MAP)
throw msgpack::type_error();
if (o.type != msgpack::type::MAP)
throw msgpack::type_error();
auto rfilters = findMapValue(o, "w"sv); /* unpacking filters */
if (rfilters)
auto rfilters = findMapValue(o, "w"sv); /* unpacking filters */
if (rfilters)
where.msgpack_unpack(*rfilters);
else
throw msgpack::type_error();
else
throw msgpack::type_error();
auto rfield_selector = findMapValue(o, "s"sv); /* unpacking field selectors */
if (rfield_selector)
auto rfield_selector = findMapValue(o, "s"sv); /* unpacking field selectors */
if (rfield_selector)
select.msgpack_unpack(*rfield_selector);
else
throw msgpack::type_error();
else
throw msgpack::type_error();
}
template <typename T>
bool subset(const std::vector<T>& fds, const std::vector<T>& qfds)
template<typename T>
bool
subset(const std::vector<T>& fds, const std::vector<T>& qfds)
{
for (const auto& fd : fds) {
if (std::find_if(qfds.begin(), qfds.end(), [&fd](const T& _vfd) { return fd == _vfd; }) == qfds.end())
@@ -562,22 +577,28 @@ bool subset(const std::vector<T>& fds, const std::vector<T>& qfds)
return true;
}
bool Select::isSatisfiedBy(const Select& os) const {
bool
Select::isSatisfiedBy(const Select& os) const
{
/* empty, means all values are selected. */
return fieldSelection_.empty() ?
os.fieldSelection_.empty() :
subset(fieldSelection_, os.fieldSelection_);
return fieldSelection_.empty() ? os.fieldSelection_.empty() : subset(fieldSelection_, os.fieldSelection_);
}
bool Where::isSatisfiedBy(const Where& ow) const {
bool
Where::isSatisfiedBy(const Where& ow) const
{
return subset(ow.filters_, filters_);
}
bool Query::isSatisfiedBy(const Query& q) const {
bool
Query::isSatisfiedBy(const Query& q) const
{
return none or (where.isSatisfiedBy(q.where) and select.isSatisfiedBy(q.select));
}
std::ostream& operator<<(std::ostream& s, const dht::Select& select) {
std::ostream&
operator<<(std::ostream& s, const dht::Select& select)
{
s << "SELECT ";
if (select.fieldSelection_.empty())
s << '*';
@@ -608,10 +629,12 @@ std::ostream& operator<<(std::ostream& s, const dht::Select& select) {
return s;
}
std::ostream& operator<<(std::ostream& s, const dht::Where& where) {
std::ostream&
operator<<(std::ostream& s, const dht::Where& where)
{
if (not where.filters_.empty()) {
s << "WHERE ";
for (auto f = where.filters_.begin() ; f != where.filters_.end() ; ++f) {
for (auto f = where.filters_.begin(); f != where.filters_.end(); ++f) {
switch (f->getField()) {
case Value::Field::Id:
s << VALUE_KEY_ID << '=' << f->getInt();
@@ -639,5 +662,4 @@ std::ostream& operator<<(std::ostream& s, const dht::Where& where) {
return s;
}
}
} // namespace dht
+193 -29
View File
@@ -26,12 +26,12 @@ namespace test {
CPPUNIT_TEST_SUITE_REGISTRATION(CryptoTester);
void
CryptoTester::setUp() {
}
CryptoTester::setUp()
{}
void
CryptoTester::testSignatureEncryption() {
CryptoTester::testSignatureEncryption()
{
auto key = dht::crypto::PrivateKey::generate();
const auto& public_key = key.getPublicKey();
@@ -140,40 +140,47 @@ CryptoTester::testCertificateRequest()
CPPUNIT_ASSERT_MESSAGE(v.toString(), v);
}
void CryptoTester::testCertificateSerialNumber()
void
CryptoTester::testCertificateSerialNumber()
{
static const std::string cert_pem = "-----BEGIN CERTIFICATE-----"
"MIICDjCCAZSgAwIBAgIIS90uAKp+u/swCgYIKoZIzj0EAwMwTDEQMA4GA1UEAxMH"
"ZGh0bm9kZTE4MDYGCgmSJomT8ixkAQETKDBlNDQxZTA4YWJmYTQzYTc3ZTVjZDBm"
"Y2QzMzAzMTc4MjYxMTk0MzIwHhcNMTkxMTA3MDA0MTMwWhcNMjkxMTA0MDA0MTMw"
"WjBMMRAwDgYDVQQDEwdkaHRub2RlMTgwNgYKCZImiZPyLGQBARMoMGU0NDFlMDhh"
"YmZhNDNhNzdlNWNkMGZjZDMzMDMxNzgyNjExOTQzMjB2MBAGByqGSM49AgEGBSuB"
"BAAiA2IABCZ7sBp0Pu+b5yIifoNXchU9crv9won0in++COWynvM4GCLF2Gk6QGhh"
"YLDxNGsyQjGR7z5AGibvYhNLU0JA4RbmxYWHw4g3wBrPA1jm9hGZ8y5Y8R97d0Hl"
"VpyreEMjRKNDMEEwHQYDVR0OBBYEFA5EHgir+kOnflzQ/NMwMXgmEZQyMA8GA1Ud"
"EwEB/wQFMAMBAf8wDwYDVR0PAQH/BAUDAwcGADAKBggqhkjOPQQDAwNoADBlAjEA"
"kKF/6WReNytwSrJ8JSTToc7xWS5SvAa23Fnla4mywZUxUFS4VSxCMQTjQCknO3iZ"
"AjBgxXyXYqn0d7vz7S6oAY5TdaD5YFT5MD2c1MAAp8pxQSwdPa9k0ZSoGIEn31Z0"
"GxU="
"-----END CERTIFICATE-----";
"MIICDjCCAZSgAwIBAgIIS90uAKp+u/swCgYIKoZIzj0EAwMwTDEQMA4GA1UEAxMH"
"ZGh0bm9kZTE4MDYGCgmSJomT8ixkAQETKDBlNDQxZTA4YWJmYTQzYTc3ZTVjZDBm"
"Y2QzMzAzMTc4MjYxMTk0MzIwHhcNMTkxMTA3MDA0MTMwWhcNMjkxMTA0MDA0MTMw"
"WjBMMRAwDgYDVQQDEwdkaHRub2RlMTgwNgYKCZImiZPyLGQBARMoMGU0NDFlMDhh"
"YmZhNDNhNzdlNWNkMGZjZDMzMDMxNzgyNjExOTQzMjB2MBAGByqGSM49AgEGBSuB"
"BAAiA2IABCZ7sBp0Pu+b5yIifoNXchU9crv9won0in++COWynvM4GCLF2Gk6QGhh"
"YLDxNGsyQjGR7z5AGibvYhNLU0JA4RbmxYWHw4g3wBrPA1jm9hGZ8y5Y8R97d0Hl"
"VpyreEMjRKNDMEEwHQYDVR0OBBYEFA5EHgir+kOnflzQ/NMwMXgmEZQyMA8GA1Ud"
"EwEB/wQFMAMBAf8wDwYDVR0PAQH/BAUDAwcGADAKBggqhkjOPQQDAwNoADBlAjEA"
"kKF/6WReNytwSrJ8JSTToc7xWS5SvAa23Fnla4mywZUxUFS4VSxCMQTjQCknO3iZ"
"AjBgxXyXYqn0d7vz7S6oAY5TdaD5YFT5MD2c1MAAp8pxQSwdPa9k0ZSoGIEn31Z0"
"GxU="
"-----END CERTIFICATE-----";
/*
* $ openssl x509 -in cert.pem -noout -serial
* serial=4BDD2E00AA7EBBFB
*/
static constexpr std::array<uint8_t, 8> SERIAL {{0x4b,0xdd,0x2e,0x00,0xaa,0x7e,0xbb,0xfb}};
static constexpr std::array<uint8_t, 8> SERIAL {
{0x4b, 0xdd, 0x2e, 0x00, 0xaa, 0x7e, 0xbb, 0xfb}
};
auto serial = dht::crypto::Certificate(cert_pem).getSerialNumber();
CPPUNIT_ASSERT(std::equal(SERIAL.begin(), SERIAL.end(), serial.begin(), serial.end()));
}
void CryptoTester::testOcsp() {
void
CryptoTester::testOcsp()
{
auto ca = dht::crypto::generateIdentity("Test CA");
auto device = dht::crypto::generateIdentity("Test Device", ca);
auto ocspRequest = device.second->generateOcspRequest(ca.second->cert);
auto req = dht::crypto::OcspRequest((const uint8_t*)ocspRequest.first.data(), ocspRequest.first.size());
auto req = dht::crypto::OcspRequest((const uint8_t*) ocspRequest.first.data(), ocspRequest.first.size());
CPPUNIT_ASSERT(ocspRequest.second == req.getNonce());
}
void CryptoTester::testAesEncryption() {
void
CryptoTester::testAesEncryption()
{
auto password = "this is a password 123414!@#%@#$?" + std::to_string(rand());
std::vector<uint8_t> data1 {5, 10};
@@ -214,8 +221,8 @@ void CryptoTester::testAesEncryption() {
CPPUNIT_ASSERT(encrypted11 == encrypted1);
CPPUNIT_ASSERT(encrypted22 == encrypted2);
auto key12 = dht::crypto::stretchKey(password, salt1, 256/8);
auto key22 = dht::crypto::stretchKey(password, salt2, 256/8);
auto key12 = dht::crypto::stretchKey(password, salt1, 256 / 8);
auto key22 = dht::crypto::stretchKey(password, salt2, 256 / 8);
CPPUNIT_ASSERT(key1 == key12);
CPPUNIT_ASSERT(key2 == key22);
@@ -239,7 +246,9 @@ void CryptoTester::testAesEncryption() {
CPPUNIT_ASSERT(data2 == decrypted2);
}
void CryptoTester::testAesEncryptionWithMultipleKeySizes() {
void
CryptoTester::testAesEncryptionWithMultipleKeySizes()
{
auto data = std::vector<uint8_t>(rand(), rand());
// Valid key sizes
@@ -260,7 +269,9 @@ void CryptoTester::testAesEncryptionWithMultipleKeySizes() {
}
}
void CryptoTester::testOaep() {
void
CryptoTester::testOaep()
{
#if GNUTLS_VERSION_NUMBER >= 0x030804 && !defined(_WIN32)
auto data = std::vector<uint8_t>(446, 10);
auto ca = dht::crypto::generateIdentity("n1");
@@ -275,7 +286,7 @@ void CryptoTester::testOaep() {
auto crt = dht::crypto::Certificate::generate(key, "dhtnode", ca);
auto pk2 = crt.getSharedPublicKey();
auto encrypted2 = pk2->encrypt(data);
auto decrypted2 = key.decrypt(encrypted2);
CPPUNIT_ASSERT(data == decrypted2);
@@ -303,7 +314,160 @@ void CryptoTester::testOaep() {
}
void
CryptoTester::tearDown() {
CryptoTester::testWebPushEncryption()
{
#if GNUTLS_VERSION_NUMBER >= 0x030802
// 1. Generate Receiver P-256 Key Pair
gnutls_privkey_t receiver_key;
CPPUNIT_ASSERT_EQUAL(GNUTLS_E_SUCCESS, gnutls_privkey_init(&receiver_key));
CPPUNIT_ASSERT_EQUAL(GNUTLS_E_SUCCESS,
gnutls_privkey_generate(receiver_key,
GNUTLS_PK_ECC,
GNUTLS_CURVE_TO_BITS(GNUTLS_ECC_CURVE_SECP256R1),
0));
gnutls_datum_t x, y, k;
CPPUNIT_ASSERT_EQUAL(GNUTLS_E_SUCCESS, gnutls_privkey_export_ecc_raw(receiver_key, NULL, &x, &y, &k));
// Construct receiver public key (uncompressed)
std::vector<uint8_t> p256dh(65);
p256dh[0] = 0x04;
auto copy_coord = [](uint8_t* dest, const gnutls_datum_t& src) {
if (src.size > 32 && (src.size != 33 || src.data[0] != 0))
throw std::runtime_error("Invalid key coordinate size: " + std::to_string(src.size));
size_t offset = src.size > 32 ? 1 : 0;
size_t len = src.size - offset;
std::memset(dest, 0, 32 - len);
std::memcpy(dest + (32 - len), src.data + offset, len);
};
copy_coord(p256dh.data() + 1, x);
copy_coord(p256dh.data() + 33, y);
gnutls_free(x.data);
gnutls_free(y.data);
gnutls_free(k.data);
gnutls_privkey_deinit(receiver_key);
// 2. Generate Auth Secret
std::vector<uint8_t> auth(16, 'a'); // Dummy auth
// 3. Payload
std::string payloadStr = "{\"test\":\"data\"}";
std::vector<uint8_t> payload(payloadStr.begin(), payloadStr.end());
std::vector<uint8_t> longPayload(3 * 4096 + 17, 'b');
// 4. Encrypt
auto encrypted = dht::crypto::webPushEncrypt(p256dh, auth, payload.data(), payload.size());
auto encryptedLong = dht::crypto::webPushEncrypt(p256dh, auth, longPayload.data(), longPayload.size());
// 5. Verify Structure
// Salt (16) + RS (4) + IDLEN (1) + Key (65) + Ciphertext (payload + 1 + 16)
size_t expectedSize = 16 + 4 + 1 + 65 + payload.size() + 1 + 16;
CPPUNIT_ASSERT_EQUAL(expectedSize, encrypted.size());
expectedSize = 16 + 4 + 1 + 65 + longPayload.size() + 1 + 16;
CPPUNIT_ASSERT_EQUAL(expectedSize, encryptedLong.size());
// Check IDLEN
CPPUNIT_ASSERT_EQUAL((uint8_t) 65, encrypted[20]);
CPPUNIT_ASSERT_EQUAL((uint8_t) 65, encryptedLong[20]);
// Check Key Header
CPPUNIT_ASSERT_EQUAL((uint8_t) 0x04, encrypted[21]);
CPPUNIT_ASSERT_EQUAL((uint8_t) 0x04, encryptedLong[21]);
#else
std::cerr << "Skipping Web Push Encryption test, not supported by the current GnuTLS version" << std::endl;
#endif
}
} // namespace test
void
CryptoTester::testWebPushRFC8291()
{
#if GNUTLS_VERSION_NUMBER >= 0x030802
// RFC 8291 Example
// https://www.rfc-editor.org/rfc/rfc8291.html#section-5
// Plaintext
std::string plaintextStr = "When I grow up, I want to be a watermelon";
dht::Blob payload(plaintextStr.begin(), plaintextStr.end());
// Receiver (UA) Public Key (P-256 uncompressed)
// Base64URL: BCVxsr7N_eNgVRqvHtD0zTZsEc6-VV-JvLexhqUzORcxaOzi6-AYWXvTBHm4bjyPjs7Vd8pZGH6SRpkNtoIAiw4
dht::Blob uaPub = {0x04, 0x25, 0x71, 0xb2, 0xbe, 0xcd, 0xfd, 0xe3, 0x60, 0x55, 0x1a, 0xaf, 0x1e,
0xd0, 0xf4, 0xcd, 0x36, 0x6c, 0x11, 0xce, 0xbe, 0x55, 0x5f, 0x89, 0xbc, 0xb7,
0xb1, 0x86, 0xa5, 0x33, 0x39, 0x17, 0x31, 0x68, 0xec, 0xe2, 0xeb, 0xe0, 0x18,
0x59, 0x7b, 0xd3, 0x04, 0x79, 0xb8, 0x6e, 0x3c, 0x8f, 0x8e, 0xce, 0xd5, 0x77,
0xca, 0x59, 0x18, 0x7e, 0x92, 0x46, 0x99, 0x0d, 0xb6, 0x82, 0x00, 0x8b, 0x0e};
// Auth Secret
// BTBZMqHH6r4Tts7J_aSIgg
dht::Blob auth = {0x05, 0x30, 0x59, 0x32, 0xa1, 0xc7, 0xea, 0xbe, 0x13, 0xb6, 0xce, 0xc9, 0xfd, 0xa4, 0x88, 0x82};
// Salt
// DGv6ra1nlYgDCS1FRnbzlw
dht::Blob salt = {0x0c, 0x6b, 0xfa, 0xad, 0xad, 0x67, 0x95, 0x88, 0x03, 0x09, 0x2d, 0x45, 0x46, 0x76, 0xf3, 0x97};
// AS Private Key (Sender)
// yfWPiYE-n46HLnH0KqZOF1fJJU3MYrct3AELtAQ-oRw
dht::Blob asPrivRaw = {0xc9, 0xf5, 0x8f, 0x89, 0x81, 0x3e, 0x9f, 0x8e, 0x87, 0x2e, 0x71,
0xf4, 0x2a, 0xa6, 0x4e, 0x17, 0x57, 0xc9, 0x25, 0x4d, 0xcc, 0x62,
0xb7, 0x2d, 0xdc, 0x01, 0x0b, 0xb4, 0x04, 0x3e, 0xa1, 0x1c};
// AS Public Key (Sender) - for verification
// BP4z9KsN6nGRTbVYI_c7VJSPQTBtkgcy27mlmlMoZIIgDll6e3vCYLocInmYWAmS6TlzAC8wEqKK6PBru3jl7A8
dht::Blob asPubRaw = {0x04, 0xfe, 0x33, 0xf4, 0xab, 0x0d, 0xea, 0x71, 0x91, 0x4d, 0xb5, 0x58, 0x23,
0xf7, 0x3b, 0x54, 0x94, 0x8f, 0x41, 0x30, 0x6d, 0x92, 0x07, 0x32, 0xdb, 0xb9,
0xa5, 0x9a, 0x53, 0x28, 0x64, 0x82, 0x20, 0x0e, 0x59, 0x7a, 0x7b, 0x7b, 0xc2,
0x60, 0xba, 0x1c, 0x22, 0x79, 0x98, 0x58, 0x09, 0x92, 0xe9, 0x39, 0x73, 0x00,
0x2f, 0x30, 0x12, 0xa2, 0x8a, 0xe8, 0xf0, 0x6b, 0xbb, 0x78, 0xe5, 0xec, 0x0f};
gnutls_privkey_t asPrivKeyGnutls;
gnutls_privkey_init(&asPrivKeyGnutls);
gnutls_datum_t k = {(unsigned char*) asPrivRaw.data(), (unsigned int) asPrivRaw.size()};
gnutls_datum_t x = {(unsigned char*) asPubRaw.data() + 1, 32};
gnutls_datum_t y = {(unsigned char*) asPubRaw.data() + 33, 32};
int ret = gnutls_privkey_import_ecc_raw(asPrivKeyGnutls, GNUTLS_ECC_CURVE_SECP256R1, &x, &y, &k);
CPPUNIT_ASSERT_EQUAL(0, ret);
dht::crypto::PrivateKey asPrivKey;
asPrivKey.key = asPrivKeyGnutls;
// Expected Ciphertext (from RFC)
// 8pfeW0KbunFT06SuDKoJH9Ql87S1QUrdirN6GcG7sFz1y1sqLgVi1VhjVkHsUoEsbI_0LpXMuGvnzQ
dht::Blob expectedCiphertext = {0xf2, 0x97, 0xde, 0x5b, 0x42, 0x9b, 0xba, 0x71, 0x53, 0xd3, 0xa4, 0xae,
0x0c, 0xaa, 0x09, 0x1f, 0xd4, 0x25, 0xf3, 0xb4, 0xb5, 0x41, 0x4a, 0xdd,
0x8a, 0xb3, 0x7a, 0x19, 0xc1, 0xbb, 0xb0, 0x5c, 0xf5, 0xcb, 0x5b, 0x2a,
0x2e, 0x05, 0x62, 0xd5, 0x58, 0x63, 0x56, 0x41, 0xec, 0x52, 0x81, 0x2c,
0x6c, 0x8f, 0xf4, 0x2e, 0x95, 0xcc, 0xb8, 0x6b, 0xe7, 0xcd};
// Perform Encryption
auto output = dht::crypto::webPushEncrypt(uaPub, auth, payload.data(), payload.size(), asPrivKey, salt);
// Verify Output
size_t headerSize = 16 + 4 + 1 + 65;
CPPUNIT_ASSERT(output.size() == headerSize + expectedCiphertext.size());
// Check Salt
dht::Blob outSalt(output.begin(), output.begin() + 16);
CPPUNIT_ASSERT(outSalt == salt);
// Check Ciphertext
dht::Blob outCiphertext(output.begin() + headerSize, output.end());
CPPUNIT_ASSERT(outCiphertext == expectedCiphertext);
// Check AS Public Key in header
dht::Blob outASPub(output.begin() + 16 + 4 + 1, output.begin() + 16 + 4 + 1 + 65);
CPPUNIT_ASSERT(outASPub == asPubRaw);
#else
std::cerr << "Skipping Web Push RFC8291 test, not supported by the current GnuTLS version" << std::endl;
#endif
}
void
CryptoTester::tearDown()
{}
} // namespace test
+8 -3
View File
@@ -26,7 +26,8 @@
namespace test {
class CryptoTester : public CppUnit::TestFixture {
class CryptoTester : public CppUnit::TestFixture
{
CPPUNIT_TEST_SUITE(CryptoTester);
CPPUNIT_TEST(testSignatureEncryption);
CPPUNIT_TEST(testCertificateRevocation);
@@ -36,9 +37,11 @@ class CryptoTester : public CppUnit::TestFixture {
CPPUNIT_TEST(testAesEncryption);
CPPUNIT_TEST(testAesEncryptionWithMultipleKeySizes);
CPPUNIT_TEST(testOaep);
CPPUNIT_TEST(testWebPushEncryption);
CPPUNIT_TEST(testWebPushRFC8291);
CPPUNIT_TEST_SUITE_END();
public:
public:
/**
* Method automatically called before each test by CppUnit
*/
@@ -74,6 +77,8 @@ class CryptoTester : public CppUnit::TestFixture {
void testAesEncryptionWithMultipleKeySizes();
void testOaep();
void testWebPushEncryption();
void testWebPushRFC8291();
};
} // namespace test
} // namespace test
+171 -39
View File
@@ -31,12 +31,13 @@ namespace test {
CPPUNIT_TEST_SUITE_REGISTRATION(HttpTester);
void
HttpTester::setUp() {
HttpTester::setUp()
{
nodePeer = std::make_shared<dht::DhtRunner>();
nodePeer->run(0);
auto nodeProxy = std::make_shared<dht::DhtRunner>();
nodeProxy->run(0, /*identity*/{}, /*threaded*/true);
nodeProxy->run(0, /*identity*/ {}, /*threaded*/ true);
auto bound = nodePeer->getBound();
if (bound.isUnspecified())
bound.setLoopback();
@@ -52,17 +53,19 @@ HttpTester::setUp() {
}
void
HttpTester::tearDown() {
HttpTester::tearDown()
{
serverProxy.reset();
nodePeer->join();
}
void
HttpTester::test_parse_url() {
HttpTester::test_parse_url()
{
// Arrange
std::string url = "http://google.com/";
// Act
dht::http::Url parsed (url);
dht::http::Url parsed(url);
// Assert
CPPUNIT_ASSERT(parsed.url == url);
CPPUNIT_ASSERT(parsed.protocol == "http");
@@ -71,11 +74,12 @@ HttpTester::test_parse_url() {
}
void
HttpTester::test_parse_https_url_no_service() {
HttpTester::test_parse_https_url_no_service()
{
// Arrange
std::string url = "https://jami.net/";
// Act
dht::http::Url parsed (url);
dht::http::Url parsed(url);
// Assert
CPPUNIT_ASSERT(parsed.url == url);
CPPUNIT_ASSERT(parsed.protocol == "https");
@@ -84,11 +88,12 @@ HttpTester::test_parse_https_url_no_service() {
}
void
HttpTester::test_parse_url_no_prefix_no_target() {
HttpTester::test_parse_url_no_prefix_no_target()
{
// Arrange
std::string url = "google.com";
// Act
dht::http::Url parsed (url);
dht::http::Url parsed(url);
// Assert
CPPUNIT_ASSERT(parsed.url == url);
CPPUNIT_ASSERT(parsed.protocol == "http");
@@ -97,11 +102,12 @@ HttpTester::test_parse_url_no_prefix_no_target() {
}
void
HttpTester::test_parse_url_target() {
HttpTester::test_parse_url_target()
{
// Arrange
std::string url = "https://www.google.com:666/going/under";
// Act
dht::http::Url parsed (url);
dht::http::Url parsed(url);
// Assert
CPPUNIT_ASSERT(parsed.url == url);
CPPUNIT_ASSERT(parsed.protocol == "https");
@@ -111,11 +117,12 @@ HttpTester::test_parse_url_target() {
}
void
HttpTester::test_parse_url_query() {
HttpTester::test_parse_url_query()
{
// Arrange
std::string url = "http://google.com/?key=1";
// Act
dht::http::Url parsed (url);
dht::http::Url parsed(url);
// Assert
CPPUNIT_ASSERT(parsed.url == url);
CPPUNIT_ASSERT(parsed.protocol == "http");
@@ -125,11 +132,12 @@ HttpTester::test_parse_url_query() {
}
void
HttpTester::test_parse_url_fragment() {
HttpTester::test_parse_url_fragment()
{
// Arrange
std::string url = "http://google.com/?key=1#some-important-id";
// Act
dht::http::Url parsed (url);
dht::http::Url parsed(url);
// Assert
CPPUNIT_ASSERT(parsed.url == url);
CPPUNIT_ASSERT(parsed.protocol == "http");
@@ -140,11 +148,12 @@ HttpTester::test_parse_url_fragment() {
}
void
HttpTester::test_parse_url_ipv4() {
HttpTester::test_parse_url_ipv4()
{
// Arrange
std::string url = "http://172.217.13.132/";
// Act
dht::http::Url parsed (url);
dht::http::Url parsed(url);
// Assert
CPPUNIT_ASSERT(parsed.url == url);
CPPUNIT_ASSERT(parsed.protocol == "http");
@@ -153,11 +162,12 @@ HttpTester::test_parse_url_ipv4() {
}
void
HttpTester::test_parse_url_no_prefix_no_target_ipv4() {
HttpTester::test_parse_url_no_prefix_no_target_ipv4()
{
// Arrange
std::string url = "172.217.13.132";
// Act
dht::http::Url parsed (url);
dht::http::Url parsed(url);
// Assert
CPPUNIT_ASSERT(parsed.url == url);
CPPUNIT_ASSERT(parsed.protocol == "http");
@@ -165,11 +175,12 @@ HttpTester::test_parse_url_no_prefix_no_target_ipv4() {
}
void
HttpTester::test_parse_url_target_ipv4() {
HttpTester::test_parse_url_target_ipv4()
{
// Arrange
std::string url = "https://172.217.13.132:666/going/under";
// Act
dht::http::Url parsed (url);
dht::http::Url parsed(url);
// Assert
CPPUNIT_ASSERT(parsed.url == url);
CPPUNIT_ASSERT(parsed.protocol == "https");
@@ -179,11 +190,12 @@ HttpTester::test_parse_url_target_ipv4() {
}
void
HttpTester::test_parse_url_ipv6() {
HttpTester::test_parse_url_ipv6()
{
// Arrange
std::string url = "http://[2607:f8b0:4006:804::2004]/";
// Act
dht::http::Url parsed (url);
dht::http::Url parsed(url);
// Assert
CPPUNIT_ASSERT(parsed.url == url);
CPPUNIT_ASSERT(parsed.protocol == "http");
@@ -192,11 +204,12 @@ HttpTester::test_parse_url_ipv6() {
}
void
HttpTester::test_parse_url_no_prefix_no_target_ipv6() {
HttpTester::test_parse_url_no_prefix_no_target_ipv6()
{
// Arrange
std::string url = "2607:f8b0:4006:804::2004";
// Act
dht::http::Url parsed (url);
dht::http::Url parsed(url);
// Assert
CPPUNIT_ASSERT(parsed.url == url);
CPPUNIT_ASSERT(parsed.protocol == "http");
@@ -204,11 +217,12 @@ HttpTester::test_parse_url_no_prefix_no_target_ipv6() {
}
void
HttpTester::test_parse_url_target_ipv6() {
HttpTester::test_parse_url_target_ipv6()
{
// Arrange
std::string url = "https://[2607:f8b0:4006:804::2004]:666/going/under";
// Act
dht::http::Url parsed (url);
dht::http::Url parsed(url);
// Assert
CPPUNIT_ASSERT(parsed.url == url);
CPPUNIT_ASSERT(parsed.protocol == "https");
@@ -218,7 +232,125 @@ HttpTester::test_parse_url_target_ipv6() {
}
void
HttpTester::test_send_json() {
HttpTester::test_parse_url_user_pass()
{
// Arrange
std::string url = "http://user:pass@example.com/";
// Act
dht::http::Url parsed(url);
// Assert
CPPUNIT_ASSERT_EQUAL(url, parsed.toString());
CPPUNIT_ASSERT_EQUAL(std::string("http"), parsed.protocol);
CPPUNIT_ASSERT_EQUAL(std::string_view("user"), parsed.user);
CPPUNIT_ASSERT_EQUAL(std::string_view("pass"), parsed.password);
CPPUNIT_ASSERT_EQUAL(std::string_view("example.com"), parsed.host);
CPPUNIT_ASSERT_EQUAL(std::string("/"), parsed.target);
}
void
HttpTester::test_parse_url_user_only()
{
// Arrange
std::string url = "http://user@example.com/";
// Act
dht::http::Url parsed(url);
// Assert
CPPUNIT_ASSERT_EQUAL(url, parsed.toString());
CPPUNIT_ASSERT_EQUAL(std::string("http"), parsed.protocol);
CPPUNIT_ASSERT_EQUAL(std::string_view("user"), parsed.user);
CPPUNIT_ASSERT(parsed.password.empty());
CPPUNIT_ASSERT_EQUAL(std::string_view("example.com"), parsed.host);
CPPUNIT_ASSERT_EQUAL(std::string("/"), parsed.target);
}
void
HttpTester::test_parse_url_ipv6_brackets()
{
// Arrange
std::string url = "http://[2001:db8::1]/";
// Act
dht::http::Url parsed(url);
// Assert
CPPUNIT_ASSERT_EQUAL(url, parsed.toString());
CPPUNIT_ASSERT_EQUAL(std::string("http"), parsed.protocol);
CPPUNIT_ASSERT_EQUAL(std::string_view("2001:db8::1"), parsed.host);
CPPUNIT_ASSERT_EQUAL(std::string("/"), parsed.target);
}
void
HttpTester::test_parse_url_ipv6_brackets_port()
{
// Arrange
std::string url = "http://[2001:db8::1]:8080/";
// Act
dht::http::Url parsed(url);
// Assert
CPPUNIT_ASSERT_EQUAL(url, parsed.toString());
CPPUNIT_ASSERT_EQUAL(std::string("http"), parsed.protocol);
CPPUNIT_ASSERT_EQUAL(std::string_view("2001:db8::1"), parsed.host);
CPPUNIT_ASSERT_EQUAL(std::string_view("8080"), parsed.service);
CPPUNIT_ASSERT_EQUAL(std::string("/"), parsed.target);
}
void
HttpTester::test_parse_url_mixed_case_protocol()
{
// Arrange
std::string url = "HtTp://example.com/";
// Act
dht::http::Url parsed(url);
// Assert
CPPUNIT_ASSERT_EQUAL(std::string("http://example.com/"), parsed.toString());
CPPUNIT_ASSERT_EQUAL(std::string("http"), parsed.protocol);
CPPUNIT_ASSERT_EQUAL(std::string_view("example.com"), parsed.host);
}
void
HttpTester::test_parse_url_complex_path_query_fragment()
{
// Arrange
std::string url = "https://example.com/path/to/resource?query=param&q2=v2#fragment";
// Act
dht::http::Url parsed(url);
// Assert
CPPUNIT_ASSERT_EQUAL(url, parsed.toString());
CPPUNIT_ASSERT_EQUAL(std::string("https"), parsed.protocol);
CPPUNIT_ASSERT_EQUAL(std::string_view("example.com"), parsed.host);
CPPUNIT_ASSERT_EQUAL(std::string("/path/to/resource?query=param&q2=v2"), parsed.target);
CPPUNIT_ASSERT_EQUAL(std::string_view("query=param&q2=v2"), parsed.query);
CPPUNIT_ASSERT_EQUAL(std::string_view("#fragment"), parsed.fragment);
}
void
HttpTester::test_parse_url_empty()
{
// Arrange
std::string url = "";
// Act
dht::http::Url parsed(url);
// Assert
CPPUNIT_ASSERT(parsed.url.empty());
CPPUNIT_ASSERT_EQUAL(std::string("http"), parsed.protocol); // Default
CPPUNIT_ASSERT(parsed.host.empty());
}
void
HttpTester::test_parse_url_just_slash()
{
// Arrange
std::string url = "/";
// Act
dht::http::Url parsed(url);
// Assert
CPPUNIT_ASSERT_EQUAL(std::string("http:///"), parsed.toString());
CPPUNIT_ASSERT_EQUAL(std::string("http"), parsed.protocol);
CPPUNIT_ASSERT(parsed.host.empty());
CPPUNIT_ASSERT_EQUAL(std::string("/"), parsed.target);
}
void
HttpTester::test_send_json()
{
// Arrange
std::condition_variable cv;
std::mutex cv_m;
@@ -231,19 +363,19 @@ HttpTester::test_send_json() {
// Act
auto request = std::make_shared<dht::http::Request>(serverProxy->io_context(),
"http://127.0.0.1:8080/key/key",
json,
[&](Json::Value value, const dht::http::Response& response) {
std::lock_guard<std::mutex> lk(cv_m);
resp_val = std::move(value);
status = response.status_code;
done = true;
cv.notify_all();
});
"http://127.0.0.1:8080/key/key",
json,
[&](Json::Value value, const dht::http::Response& response) {
std::lock_guard<std::mutex> lk(cv_m);
resp_val = std::move(value);
status = response.status_code;
done = true;
cv.notify_all();
});
request->send();
// Assert
CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(10), [&]{ return done; }));
CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(10), [&] { return done; }));
CPPUNIT_ASSERT_EQUAL(200u, status);
CPPUNIT_ASSERT_EQUAL(json["data"].asString(), resp_val["data"].asString());
done = false;
@@ -292,4 +424,4 @@ HttpTester::test_send_json() {
#endif
}
} // namespace test
} // namespace test
+16
View File
@@ -47,6 +47,14 @@ class HttpTester : public CppUnit::TestFixture {
CPPUNIT_TEST(test_parse_url_ipv6);
CPPUNIT_TEST(test_parse_url_no_prefix_no_target_ipv6);
CPPUNIT_TEST(test_parse_url_target_ipv6);
CPPUNIT_TEST(test_parse_url_user_pass);
CPPUNIT_TEST(test_parse_url_user_only);
CPPUNIT_TEST(test_parse_url_ipv6_brackets);
CPPUNIT_TEST(test_parse_url_ipv6_brackets_port);
CPPUNIT_TEST(test_parse_url_mixed_case_protocol);
CPPUNIT_TEST(test_parse_url_complex_path_query_fragment);
CPPUNIT_TEST(test_parse_url_empty);
CPPUNIT_TEST(test_parse_url_just_slash);
// send
CPPUNIT_TEST(test_send_json);
CPPUNIT_TEST_SUITE_END();
@@ -82,6 +90,14 @@ class HttpTester : public CppUnit::TestFixture {
void test_parse_url_ipv6();
void test_parse_url_no_prefix_no_target_ipv6();
void test_parse_url_target_ipv6();
void test_parse_url_user_pass();
void test_parse_url_user_only();
void test_parse_url_ipv6_brackets();
void test_parse_url_ipv6_brackets_port();
void test_parse_url_mixed_case_protocol();
void test_parse_url_complex_path_query_fragment();
void test_parse_url_empty();
void test_parse_url_just_slash();
/**
* Test send(json)
*/
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "opendht",
"version-string": "3.6.0",
"version-string": "3.6.2",
"port-version": 1,
"description": "OpenDHT: A lightweight C++17 Distributed Hash Table library.",
"homepage": "https://opendht.net",