Compare commits

..
1 Commits
Author SHA1 Message Date
Adrien Beraud 934c471d98 ci: install brew packages for macOS 15 2026-07-15 12:04:16 -04:00
16 changed files with 73 additions and 408 deletions

No files matched your search

+1 -1
View File
@@ -23,7 +23,7 @@ jobs:
# Run locally with: pipx run cibuildwheel --platform linux python
- name: Build wheels
uses: pypa/cibuildwheel@v4.1.1
uses: pypa/cibuildwheel@v4.1.0
with:
package-dir: python
+1 -1
View File
@@ -10,7 +10,7 @@ if(POLICY CMP0167)
endif()
set (opendht_VERSION_MAJOR 4)
set (opendht_VERSION_MINOR 2.0)
set (opendht_VERSION_MINOR 1.1)
set (opendht_VERSION ${opendht_VERSION_MAJOR}.${opendht_VERSION_MINOR})
set (PACKAGE_VERSION ${opendht_VERSION})
set (VERSION "${opendht_VERSION}")
+2 -7
View File
@@ -267,14 +267,9 @@ public:
/**
* Sign this revocation list using provided key and certificate.
* Validity_period sets the duration until next update (default to no next update).
* If `number` is non-zero, it is stored verbatim (big-endian) as the CRL Number
* extension. Otherwise the number is initialized randomly on the first signature
* and incremented on subsequent ones.
*/
void sign(const PrivateKey&, const Certificate&, duration validity_period = {}, uint64_t number = 0);
void sign(const Identity& id, duration validity_period = {}, uint64_t number = 0) {
sign(*id.first, *id.second, validity_period, number);
}
void sign(const PrivateKey&, const Certificate&, duration validity_period = {});
void sign(const Identity& id) { sign(*id.first, *id.second); }
bool isSignedBy(const Certificate& issuer) const;
-8
View File
@@ -85,14 +85,6 @@ using system_clock = std::chrono::system_clock;
using time_point = clock::time_point;
using duration = clock::duration;
OPENDHT_PUBLIC time_point from_system_time(system_clock::time_point t,
time_point steady_now,
system_clock::time_point system_now);
OPENDHT_PUBLIC time_point from_system_time(system_clock::time_point t);
OPENDHT_PUBLIC system_clock::time_point to_system_time(time_point t,
time_point steady_now,
system_clock::time_point system_now);
OPENDHT_PUBLIC system_clock::time_point to_system_time(time_point t);
OPENDHT_PUBLIC time_point from_time_t(std::time_t t);
OPENDHT_PUBLIC std::time_t to_time_t(time_point t);
+1 -1
View File
@@ -5,7 +5,7 @@ project(
'cython',
license: 'MIT',
license_files: 'LICENSE',
version: '4.2.0',
version: '4.1.1',
meson_version: '>=1.1.0',
default_options: ['cpp_std=c++20', 'warning_level=3'],
)
+41 -33
View File
@@ -571,12 +571,6 @@ PublicKey::toString() const
}
if (err != GNUTLS_E_SUCCESS)
throw CryptoException(std::string("Could not print public key: ") + gnutls_strerror(err));
// GnuTLS reports the PEM size including the terminating null byte, which
// does not belong to the text and would end up embedded in whatever the
// string is copied into, such as the "owner" field of a serialized value.
ret.resize(sz);
if (not ret.empty() and ret.back() == '\0')
ret.pop_back();
return ret;
}
@@ -1402,11 +1396,8 @@ setRandomSerial(gnutls_x509_crt_t cert)
{
std::random_device rdev;
std::uniform_int_distribution<int64_t> dist {1};
const auto cert_serial = dist(rdev);
std::array<uint8_t, sizeof(cert_serial)> packed_serial;
for (size_t i = 0; i < packed_serial.size(); i++)
packed_serial[i] = cert_serial >> (8 * (packed_serial.size() - 1 - i));
gnutls_x509_crt_set_serial(cert, packed_serial.data(), packed_serial.size());
int64_t cert_serial = dist(rdev);
gnutls_x509_crt_set_serial(cert, &cert_serial, sizeof(cert_serial));
}
Certificate
@@ -1882,8 +1873,34 @@ RevocationList::getUpdateTime() const
return std::chrono::system_clock::from_time_t(t);
}
enum class Endian : uint32_t { LITTLE = 0, BIG = 1 };
template<typename T>
T
endian(T w, Endian endian = Endian::BIG)
{
// this gets optimized out into if (endian == host_endian) return w;
union {
uint64_t quad;
uint32_t islittle;
} t;
t.quad = 1;
if (t.islittle ^ (uint32_t) endian)
return w;
T r = 0;
// decent compilers will unroll this (gcc)
// or even convert straight into single bswap (clang)
for (size_t i = 0; i < sizeof(r); i++) {
r <<= 8;
r |= w & 0xff;
w >>= 8;
}
return r;
}
void
RevocationList::sign(const PrivateKey& key, const Certificate& ca, duration validity, uint64_t crlNumber)
RevocationList::sign(const PrivateKey& key, const Certificate& ca, duration validity)
{
if (auto err = gnutls_x509_crl_set_version(crl, 2))
throw CryptoException(std::string("Unable to set CRL version: ") + gnutls_strerror(err));
@@ -1893,28 +1910,19 @@ RevocationList::sign(const PrivateKey& key, const Certificate& ca, duration vali
throw CryptoException(std::string("Unable to set CRL update time: ") + gnutls_strerror(err));
if (auto err = gnutls_x509_crl_set_next_update(crl, std::chrono::system_clock::to_time_t(next_update)))
throw CryptoException(std::string("Unable to set CRL next update time: ") + gnutls_strerror(err));
uint64_t number {crlNumber};
uint64_t number {0};
size_t number_sz {sizeof(number)};
unsigned critical {0};
gnutls_x509_crl_get_number(crl, &number, &number_sz, &critical);
if (number == 0) {
// The CRL Number extension holds a DER integer of variable length, so it must be
// read as a big-endian byte string rather than straight into an uint64_t.
auto current = getNumber();
for (uint8_t byte : current)
number = (number << 8) | byte;
if (number == 0) {
// Initialize to a random number.
std::random_device rdev;
do {
std::generate_n((uint8_t*) &number, sizeof(number), std::bind(rand_byte, std::ref(rdev)));
} while (number == 0);
} else
number++;
}
// Store the number in network byte order.
uint8_t packedNumber[sizeof(number)];
for (size_t i = 0; i < sizeof(packedNumber); i++)
packedNumber[i] = (uint8_t) ((number >> (8 * (sizeof(packedNumber) - 1 - i))) & 0xff);
if (auto err = gnutls_x509_crl_set_number(crl, packedNumber, sizeof(packedNumber)))
throw CryptoException(std::string("Unable to set CRL number: ") + gnutls_strerror(err));
// initialize to a random number
number_sz = sizeof(number);
std::random_device rdev;
std::generate_n((uint8_t*) &number, sizeof(number), std::bind(rand_byte, std::ref(rdev)));
} else
number = endian(endian(number) + 1);
if (auto err = gnutls_x509_crl_set_number(crl, &number, sizeof(number)))
throw CryptoException(std::string("Unable to set CRL update time: ") + gnutls_strerror(err));
if (auto err = gnutls_x509_crl_sign2(crl, ca.cert, key.x509_key, GNUTLS_DIG_SHA512, 0))
throw CryptoException(std::string("Unable to sign certificate revocation list: ") + gnutls_strerror(err));
// to be able to actually use the CRL we need to serialize/deserialize it
+7 -32
View File
@@ -2270,16 +2270,8 @@ Dht::onDisconnected()
void
Dht::bootstrap()
{
if (dht4.status != NodeStatus::Disconnected || dht6.status != NodeStatus::Disconnected) {
// The job that led here has already run, and the scheduler does not
// clear the handle to a job it has executed. onDisconnected() reads a
// set handle as "a bootstrap is already scheduled" and does nothing,
// so leaving it set here means the node never tries to rejoin the
// network once it has lost it. Clearing it is what says that no
// bootstrap is pending any more.
bootstrapJob.reset();
if (dht4.status != NodeStatus::Disconnected || dht6.status != NodeStatus::Disconnected)
return;
}
if (logger_)
logger_->debug("[{}] Bootstraping", myid.to_view());
bootstrap_pending = false;
@@ -2363,9 +2355,6 @@ Dht::confirmNodes()
std::vector<ValuesExport>
Dht::exportValues() const
{
const auto& now = scheduler.time();
const auto system_now = system_clock::now();
std::vector<ValuesExport> e {};
e.reserve(store.size());
for (const auto& h : store) {
@@ -2378,7 +2367,7 @@ Dht::exportValues() const
pk.pack_array(vals.size());
for (const auto& v : vals) {
pk.pack_array(4);
pk.pack(to_system_time(v.created, now, system_now));
pk.pack(v.created.time_since_epoch().count());
v.data->msgpack_pack(pk);
if (const auto& store_addr = v.store_bucket ? v.store_bucket->getAddr() : SockAddr {}) {
pk.pack_bin(store_addr.getLength());
@@ -2386,7 +2375,7 @@ Dht::exportValues() const
} else {
pk.pack_bin(0);
}
pk.pack(to_system_time(v.expiration, now, system_now));
pk.pack(v.expiration.time_since_epoch().count());
}
ve.second = {buffer.data(), buffer.data() + buffer.size()};
e.push_back(std::move(ve));
@@ -2398,7 +2387,6 @@ void
Dht::importValues(const std::vector<ValuesExport>& import)
{
const auto& now = scheduler.time();
const auto system_now = system_clock::now();
unsigned imported = 0, ignored = 0;
size_t imported_size = 0;
@@ -2423,15 +2411,8 @@ Dht::importValues(const std::vector<ValuesExport>& import)
Value tmp_val;
SockAddr store_addr;
try {
const bool uses_system_time = valel.via.array.ptr[0].type == msgpack::type::EXT;
if (uses_system_time) {
val_time = from_system_time(valel.via.array.ptr[0].as<system_clock::time_point>(),
now,
system_now);
} else {
val_time = time_point {
time_point::duration {valel.via.array.ptr[0].as<time_point::duration::rep>()}};
}
val_time = time_point {
time_point::duration {valel.via.array.ptr[0].as<time_point::duration::rep>()}};
tmp_val.msgpack_unpack(valel.via.array.ptr[1]);
if (valel.via.array.size >= 3) {
auto addr_blob = valel.via.array.ptr[2].as<Blob>();
@@ -2441,14 +2422,8 @@ Dht::importValues(const std::vector<ValuesExport>& import)
}
}
if (valel.via.array.size >= 4) {
if (uses_system_time) {
expiration = from_system_time(valel.via.array.ptr[3].as<system_clock::time_point>(),
now,
system_now);
} else {
expiration = time_point {
time_point::duration {valel.via.array.ptr[3].as<time_point::duration::rep>()}};
}
expiration = time_point {
time_point::duration {valel.via.array.ptr[3].as<time_point::duration::rep>()}};
}
} catch (const std::exception&) {
if (logger_)
+3 -11
View File
@@ -302,7 +302,7 @@ static X509*
issuer_from_chain(STACK_OF(X509) * fullchain)
{
X509 *cert, *issuer;
const X509_NAME* issuer_name;
X509_NAME* issuer_name;
cert = cert_from_chain(fullchain);
if ((issuer_name = X509_get_issuer_name(cert)) == nullptr)
@@ -917,16 +917,8 @@ Resolver::add_callback(ResolverCb cb, sa_family_t family)
else
cb(ec, filter(endpoints, family));
});
else {
// Always dispatch the callback on the io_context thread. Invoking it
// synchronously would run the caller's continuation (e.g. the whole
// connect chain of Request::send) on the calling thread, mutating asio
// objects concurrently with the io thread running the io_context.
asio::post(resolver_.get_executor(),
[cb = std::move(cb),
ec = ec_,
endpoints = family == AF_UNSPEC ? endpoints_ : filter(endpoints_, family)] { cb(ec, endpoints); });
}
else
cb(ec_, family == AF_UNSPEC ? endpoints_ : filter(endpoints_, family));
}
void
+14 -44
View File
@@ -238,58 +238,28 @@ operator==(const SockAddr& a, const SockAddr& b)
return a.equals(b);
}
time_point
from_system_time(system_clock::time_point t, time_point steady_now, system_clock::time_point system_now)
{
if (t == system_clock::time_point::min())
return time_point::min();
if (t == system_clock::time_point::max())
return time_point::max();
auto dt = t - system_now;
if (dt > system_clock::duration(0) and steady_now > time_point::max() - dt)
return time_point::max();
else if (dt < system_clock::duration(0) and steady_now < time_point::min() - dt)
return time_point::min();
return steady_now + dt;
}
time_point
from_system_time(system_clock::time_point t)
{
return from_system_time(t, clock::now(), system_clock::now());
}
system_clock::time_point
to_system_time(time_point t, time_point steady_now, system_clock::time_point system_now)
{
if (t == time_point::min())
return system_clock::time_point::min();
if (t == time_point::max())
return system_clock::time_point::max();
auto dt = t - steady_now;
if (dt > duration(0) and system_now >= system_clock::time_point::max() - dt)
return system_clock::time_point::max();
else if (dt < duration(0) and system_now <= system_clock::time_point::min() - dt)
return system_clock::time_point::min();
return system_now + std::chrono::duration_cast<system_clock::duration>(dt);
}
system_clock::time_point
to_system_time(time_point t)
{
return to_system_time(t, clock::now(), system_clock::now());
}
time_point
from_time_t(std::time_t t)
{
return from_system_time(system_clock::from_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)
return time_point::max();
else if (dt < system_clock::duration(0) and now < time_point::min() - dt)
return time_point::min();
return now + dt;
}
std::time_t
to_time_t(time_point t)
{
return system_clock::to_time_t(to_system_time(t));
auto dt = t - clock::now();
auto now = system_clock::now();
if (dt > duration(0) and now >= system_clock::time_point::max() - dt)
return system_clock::to_time_t(system_clock::time_point::max());
else if (dt < duration(0) and now <= system_clock::time_point::min() - dt)
return system_clock::to_time_t(system_clock::time_point::min());
return system_clock::to_time_t(now + std::chrono::duration_cast<system_clock::duration>(dt));
}
Blob
+2 -178
View File
@@ -59,24 +59,6 @@ CryptoTester::testSignatureEncryption()
}
}
void
CryptoTester::testPublicKeyPemIsPlainText()
{
auto key = dht::crypto::PrivateKey::generate();
auto pem = key.getPublicKey().toString();
// The exported key is PEM text: it must not carry the terminating null
// byte GnuTLS counts in the exported size, which would otherwise be
// embedded in anything the string is serialized into.
CPPUNIT_ASSERT(not pem.empty());
CPPUNIT_ASSERT_EQUAL(std::string::npos, pem.find('\0'));
CPPUNIT_ASSERT_EQUAL('\n', pem.back());
// The exported text still reads back as the same key.
dht::crypto::PublicKey reloaded(dht::Blob(pem.begin(), pem.end()));
CPPUNIT_ASSERT_EQUAL(key.getPublicKey().getId(), reloaded.getId());
}
void
CryptoTester::testCertificateRevocation()
{
@@ -113,159 +95,10 @@ CryptoTester::testCertificateRevocation()
CPPUNIT_ASSERT_MESSAGE(v.toString(), v);
}
namespace {
/** Read a CRL Number extension blob (big-endian, variable length) as an integer. */
uint64_t
crlNumberValue(const dht::Blob& number)
{
uint64_t value {0};
for (uint8_t byte : number)
value = (value << 8) | byte;
return value;
}
} // namespace
void
CryptoTester::testRevocationListNumber()
{
auto ca = dht::crypto::generateIdentity("ca");
auto device = dht::crypto::generateIdentity("dev", ca);
dht::crypto::RevocationList crl;
CPPUNIT_ASSERT_EQUAL(uint64_t(0), crlNumberValue(crl.getNumber()));
crl.revoke(*device.second);
crl.sign(ca);
auto first = crl.getNumber();
// An unnumbered list gets a random number on its first signature.
CPPUNIT_ASSERT(not first.empty());
CPPUNIT_ASSERT(crlNumberValue(first) != 0);
// Re-signing keeps the same lineage and moves the number forward.
crl.sign(ca);
auto second = crl.getNumber();
CPPUNIT_ASSERT_EQUAL(crlNumberValue(first) + 1, crlNumberValue(second));
crl.sign(ca);
CPPUNIT_ASSERT_EQUAL(crlNumberValue(first) + 2, crlNumberValue(crl.getNumber()));
// The number is a DER integer, so it is stored with leading zeros stripped.
// Incrementing must still work for numbers shorter than 8 bytes.
for (uint64_t start : {uint64_t(1), uint64_t(0x7f), uint64_t(0x80), uint64_t(0xffff)}) {
dht::crypto::RevocationList small;
small.revoke(*device.second);
small.sign(ca, {}, start);
CPPUNIT_ASSERT_EQUAL(start, crlNumberValue(small.getNumber()));
small.sign(ca);
CPPUNIT_ASSERT_EQUAL(start + 1, crlNumberValue(small.getNumber()));
small.sign(ca);
CPPUNIT_ASSERT_EQUAL(start + 2, crlNumberValue(small.getNumber()));
}
}
void
CryptoTester::testRevocationListExplicitNumber()
{
auto ca = dht::crypto::generateIdentity("ca");
auto device = dht::crypto::generateIdentity("dev", ca);
// Values chosen to cover leading zero bytes, a full-width number and the largest
// value that still encodes as a positive 8-byte DER integer.
const std::vector<uint64_t> numbers {1,
42,
0xff,
0x100,
0x0000'0001'0000'0000ull,
0x0000'02a1'0000'0001ull,
0x7fff'ffff'ffff'ffffull};
for (auto number : numbers) {
dht::crypto::RevocationList crl;
crl.revoke(*device.second);
crl.sign(ca, {}, number);
CPPUNIT_ASSERT_EQUAL(number, crlNumberValue(crl.getNumber()));
CPPUNIT_ASSERT(crl.isSignedBy(*ca.second));
CPPUNIT_ASSERT(crl.isRevoked(*device.second));
// The number must survive a pack/unpack round-trip.
dht::crypto::RevocationList unpacked(crl.getPacked());
CPPUNIT_ASSERT_EQUAL(number, crlNumberValue(unpacked.getNumber()));
}
// An explicit number overrides the increment, in both directions.
dht::crypto::RevocationList crl;
crl.revoke(*device.second);
crl.sign(ca, {}, 1000);
CPPUNIT_ASSERT_EQUAL(uint64_t(1000), crlNumberValue(crl.getNumber()));
crl.sign(ca, {}, 5);
CPPUNIT_ASSERT_EQUAL(uint64_t(5), crlNumberValue(crl.getNumber()));
// Passing 0 falls back to the default behavior: increment the current number.
crl.sign(ca, {}, 0);
CPPUNIT_ASSERT_EQUAL(uint64_t(6), crlNumberValue(crl.getNumber()));
}
void
CryptoTester::testRevocationListPartitionedNumber()
{
// Two devices holding the same authority issue revocation lists independently.
// RFC 5280 5.2.3 requires CRL numbers to increase monotonically for a given CRL
// scope and issuer; partitioning the low bits by device keeps each sequence
// monotonic while making collisions between devices impossible.
constexpr unsigned TAG_BITS = 24;
constexpr uint64_t TAG_A = 0x0a0b0c;
constexpr uint64_t TAG_B = 0x0a0b0d;
auto ca = dht::crypto::generateIdentity("ca");
auto account = dht::crypto::generateIdentity("acc", ca, 4096, true);
auto deviceA = dht::crypto::generateIdentity("devA", account);
auto deviceB = dht::crypto::generateIdentity("devB", account);
uint64_t previous {0};
dht::crypto::RevocationList crlA;
for (unsigned i = 1; i <= 4; i++) {
crlA.revoke(*deviceA.second);
crlA.sign(account, {}, (uint64_t(i) << TAG_BITS) | TAG_A);
auto number = crlNumberValue(crlA.getNumber());
CPPUNIT_ASSERT_EQUAL(TAG_A, number & ((uint64_t(1) << TAG_BITS) - 1));
CPPUNIT_ASSERT(number > previous);
CPPUNIT_ASSERT_EQUAL(uint64_t(0), number >> 63);
previous = number;
}
// Both devices branch from the same counter but never mint the same number.
dht::crypto::RevocationList branchA;
branchA.revoke(*deviceA.second);
branchA.sign(account, {}, (uint64_t(9) << TAG_BITS) | TAG_A);
dht::crypto::RevocationList branchB;
branchB.revoke(*deviceB.second);
branchB.sign(account, {}, (uint64_t(9) << TAG_BITS) | TAG_B);
CPPUNIT_ASSERT(crlNumberValue(branchA.getNumber()) != crlNumberValue(branchB.getNumber()));
// Both are valid and revoke only their own target.
CPPUNIT_ASSERT(branchA.isSignedBy(*account.second));
CPPUNIT_ASSERT(branchB.isSignedBy(*account.second));
CPPUNIT_ASSERT(branchA.isRevoked(*deviceA.second));
CPPUNIT_ASSERT(not branchA.isRevoked(*deviceB.second));
CPPUNIT_ASSERT(branchB.isRevoked(*deviceB.second));
CPPUNIT_ASSERT(not branchB.isRevoked(*deviceA.second));
// A number minted by an older client is fully random, so bit 63 may be set. Masking
// it out and taking the counter part still yields a strictly greater number.
constexpr uint64_t legacy = 0xdead'beef'0123'4567ull;
dht::crypto::RevocationList legacyCrl;
legacyCrl.revoke(*deviceA.second);
legacyCrl.sign(account, {}, legacy);
auto counter = ((crlNumberValue(legacyCrl.getNumber()) & ~(uint64_t(1) << 63)) >> TAG_BITS) + 1;
legacyCrl.sign(account, {}, (counter << TAG_BITS) | TAG_A);
CPPUNIT_ASSERT(crlNumberValue(legacyCrl.getNumber()) > (legacy & ~(uint64_t(1) << 63)));
CPPUNIT_ASSERT_EQUAL(uint64_t(0), crlNumberValue(legacyCrl.getNumber()) >> 63);
}
void
CryptoTester::testCertificateRequest()
{ // Generate CA
{
// Generate CA
auto ca = dht::crypto::generateIdentity("Test CA");
// Generate signed request
@@ -316,15 +149,6 @@ CryptoTester::testCertificateSerialNumber()
};
auto serial = dht::crypto::Certificate(cert_pem).getSerialNumber();
CPPUNIT_ASSERT(std::equal(SERIAL.begin(), SERIAL.end(), serial.begin(), serial.end()));
auto key = dht::crypto::PrivateKey::generateEC();
for (unsigned i = 0; i < 32; i++) {
auto generated = dht::crypto::Certificate::generate(key, "generated", {});
auto generatedSerial = generated.getSerialNumber();
CPPUNIT_ASSERT(not generatedSerial.empty());
CPPUNIT_ASSERT_EQUAL(uint8_t {0}, uint8_t(generatedSerial.front() & 0x80));
CPPUNIT_ASSERT(std::any_of(generatedSerial.begin(), generatedSerial.end(), [](uint8_t byte) { return byte != 0; }));
}
}
void
-20
View File
@@ -12,11 +12,7 @@ class CryptoTester : public CppUnit::TestFixture
{
CPPUNIT_TEST_SUITE(CryptoTester);
CPPUNIT_TEST(testSignatureEncryption);
CPPUNIT_TEST(testPublicKeyPemIsPlainText);
CPPUNIT_TEST(testCertificateRevocation);
CPPUNIT_TEST(testRevocationListNumber);
CPPUNIT_TEST(testRevocationListExplicitNumber);
CPPUNIT_TEST(testRevocationListPartitionedNumber);
CPPUNIT_TEST(testCertificateRequest);
CPPUNIT_TEST(testCertificateSerialNumber);
CPPUNIT_TEST(testOcsp);
@@ -40,26 +36,10 @@ public:
* Test data signature, encryption and decryption
*/
void testSignatureEncryption();
/**
* Test that an exported public key is text only, with no embedded null byte
*/
void testPublicKeyPemIsPlainText();
/**
* Test certificate generation, validation and revocation
*/
void testCertificateRevocation();
/**
* Test the default CRL Number behavior: random on first signature, incremented after
*/
void testRevocationListNumber();
/**
* Test signing a revocation list with a caller-provided CRL Number
*/
void testRevocationListExplicitNumber();
/**
* Test that issuers sharing an authority can partition the CRL Number space
*/
void testRevocationListPartitionedNumber();
/**
* Test certificate requests
*/
-36
View File
@@ -210,42 +210,6 @@ DhtRunnerTester::testImportValuesPreservesRemoteQuota()
restored.join();
}
void
DhtRunnerTester::testExportValuesUsesSystemTime()
{
auto key = dht::InfoHash::get("export-system-time");
const auto created = dht::clock::now();
msgpack::sbuffer importBuffer;
msgpack::packer<msgpack::sbuffer> pk(&importBuffer);
pk.pack_array(1);
pk.pack_array(2);
pk.pack(created.time_since_epoch().count());
dht::Value {std::string("persisted")}.msgpack_pack(pk);
node1.importValues({
dht::ValuesExport {key, {importBuffer.data(), importBuffer.data() + importBuffer.size()}}
});
auto exported = node1.exportValues();
CPPUNIT_ASSERT_EQUAL(size_t(1), exported.size());
msgpack::unpacked msg;
msgpack::unpack(msg, reinterpret_cast<const char*>(exported.front().second.data()), exported.front().second.size());
const auto values = msg.get();
CPPUNIT_ASSERT(values.type == msgpack::type::ARRAY);
CPPUNIT_ASSERT_EQUAL(uint32_t(1), values.via.array.size);
const auto& persistedValue = values.via.array.ptr[0];
CPPUNIT_ASSERT_EQUAL(uint32_t(4), persistedValue.via.array.size);
CPPUNIT_ASSERT(persistedValue.via.array.ptr[0].type == msgpack::type::EXT);
CPPUNIT_ASSERT(persistedValue.via.array.ptr[3].type == msgpack::type::EXT);
const auto persistedCreated = persistedValue.via.array.ptr[0].as<std::chrono::system_clock::time_point>();
const auto persistedExpiration = persistedValue.via.array.ptr[3].as<std::chrono::system_clock::time_point>();
const auto delta = std::chrono::system_clock::now() - persistedCreated;
CPPUNIT_ASSERT(delta >= std::chrono::system_clock::duration::zero());
CPPUNIT_ASSERT(delta < 5s);
CPPUNIT_ASSERT(persistedExpiration > persistedCreated);
}
void
DhtRunnerTester::testImportValuesPreservesStoredExpiration()
{
-2
View File
@@ -21,7 +21,6 @@ class DhtRunnerTester : public CppUnit::TestFixture
CPPUNIT_TEST(testListenLotOfBytes);
CPPUNIT_TEST(testIdOps);
CPPUNIT_TEST(testImportValuesPreservesRemoteQuota);
CPPUNIT_TEST(testExportValuesUsesSystemTime);
CPPUNIT_TEST(testImportValuesPreservesStoredExpiration);
CPPUNIT_TEST(testImportPermanentValueExpires);
CPPUNIT_TEST(testBootstrapSetsConnectingState);
@@ -68,7 +67,6 @@ public:
*/
void testIdOps();
void testImportValuesPreservesRemoteQuota();
void testExportValuesUsesSystemTime();
void testImportValuesPreservesStoredExpiration();
void testImportPermanentValueExpires();
void testBootstrapSetsConnectingState();
-31
View File
@@ -8,7 +8,6 @@
#include <iostream>
#include <string>
#include <atomic>
#include <chrono>
#include <condition_variable>
@@ -333,36 +332,6 @@ HttpTester::test_parse_url_just_slash()
CPPUNIT_ASSERT_EQUAL(std::string("/"), parsed.target);
}
void
HttpTester::test_completed_resolver_callback_uses_io_thread()
{
asio::io_context ioContext;
const asio::ip::tcp::endpoint endpoint(asio::ip::address_v4::loopback(), 80);
dht::http::Resolver resolver(ioContext, {endpoint});
const auto callerThread = std::this_thread::get_id();
std::thread::id callbackThread;
asio::error_code callbackError;
std::vector<asio::ip::tcp::endpoint> resolvedEndpoints;
std::atomic_bool callbackCalled {false};
resolver.add_callback([&](const asio::error_code& ec, const auto& endpoints) {
callbackError = ec;
callbackThread = std::this_thread::get_id();
resolvedEndpoints = endpoints;
callbackCalled = true;
});
CPPUNIT_ASSERT(!callbackCalled);
std::thread ioThread([&] { ioContext.run(); });
ioThread.join();
CPPUNIT_ASSERT(callbackCalled);
CPPUNIT_ASSERT(!callbackError);
CPPUNIT_ASSERT(callbackThread != callerThread);
CPPUNIT_ASSERT_EQUAL(size_t(1), resolvedEndpoints.size());
CPPUNIT_ASSERT_EQUAL(endpoint, resolvedEndpoints.front());
}
void
HttpTester::test_send_json()
{
-2
View File
@@ -39,7 +39,6 @@ class HttpTester : public CppUnit::TestFixture
CPPUNIT_TEST(test_parse_url_complex_path_query_fragment);
CPPUNIT_TEST(test_parse_url_empty);
CPPUNIT_TEST(test_parse_url_just_slash);
CPPUNIT_TEST(test_completed_resolver_callback_uses_io_thread);
// send
CPPUNIT_TEST(test_send_json);
CPPUNIT_TEST_SUITE_END();
@@ -83,7 +82,6 @@ public:
void test_parse_url_complex_path_query_fragment();
void test_parse_url_empty();
void test_parse_url_just_slash();
void test_completed_resolver_callback_uses_io_thread();
/**
* Test send(json)
*/
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "opendht",
"version-string": "4.2.0",
"version-string": "4.1.1",
"port-version": 1,
"description": "OpenDHT: A lightweight C++17 Distributed Hash Table library.",
"homepage": "https://opendht.net",