Merge remote-tracking branch 'origin/staging' into staging

pull/268/head
Jeff Becker 5 years ago
commit c31c8ce889
No known key found for this signature in database
GPG Key ID: F357B3B42F6F9B05

@ -510,8 +510,12 @@ set(LIB_SRC
llarp/dht/bucket.cpp
llarp/dht/context.cpp
llarp/dht/dht.cpp
llarp/dht/explorenetworkjob.cpp
llarp/dht/kademlia.cpp
llarp/dht/key.cpp
llarp/dht/localtaglookup.cpp
llarp/dht/localrouterlookup.cpp
llarp/dht/localserviceaddresslookup.cpp
llarp/dht/message.cpp
llarp/dht/messages/findintro.cpp
llarp/dht/messages/findrouter.cpp
@ -519,6 +523,12 @@ set(LIB_SRC
llarp/dht/messages/gotrouter.cpp
llarp/dht/messages/pubintro.cpp
llarp/dht/node.cpp
llarp/dht/publishservicejob.cpp
llarp/dht/recursiverouterlookup.cpp
llarp/dht/serviceaddresslookup.cpp
llarp/dht/taglookup.cpp
llarp/dht/tx.cpp
llarp/dht/txholder.cpp
llarp/dht/txowner.cpp
llarp/dns.cpp
llarp/dnsc.cpp
@ -612,13 +622,16 @@ set(DNS_SRC
set(TEST_SRC
# helpers
test/main.cpp
test/dht/mock_context.cpp
test/test_util.cpp
# actual test cases
test/crypto/test_llarp_crypto_types.cpp
test/crypto/test_llarp_crypto.cpp
test/dht/test_llarp_dht_bucket.cpp
test/dht/test_llarp_dht_explorenetworkjob.cpp
test/dht/test_llarp_dht_kademlia.cpp
test/dht/test_llarp_dht_key.cpp
test/dht/test_llarp_dht_tx.cpp
test/dht/test_llarp_dht_txowner.cpp
test/dns/test_llarp_dns_dns.cpp
test/exit/test_llarp_exit_context.cpp
@ -677,11 +690,11 @@ if(USE_LIBABYSS)
set(ALL_SRC ${ALL_SRC} ${ABYSS_SRC} ${ABYSS}/main.cpp)
add_executable(${ABYSS_EXE} ${ABYSS}/main.cpp)
if (NOT WIN32)
target_link_libraries(${ABYSS_EXE} ${UTIL_LIB} ${PLATFORM_LIB})
else()
target_link_libraries(${ABYSS_EXE} ${UTIL_LIB} ${PLATFORM_LIB} ws2_32 iphlpapi)
endif(NOT WIN32)
if (NOT WIN32)
target_link_libraries(${ABYSS_EXE} ${STATIC_LIB} ${UTIL_LIB} ${PLATFORM_LIB} Threads::Threads)
else()
target_link_libraries(${ABYSS_EXE} ${STATIC_LIB} ${UTIL_LIB} ${PLATFORM_LIB} ws2_32 iphlpapi Threads::Threads)
endif(NOT WIN32)
set(TEST_SRC ${TEST_SRC} test/test_libabyss.cpp)
# for freebsd
@ -699,9 +712,6 @@ if(SHADOW)
target_link_libraries(shadow-plugin-${SHARED_LIB} ${LIBS})
install(TARGETS shadow-plugin-${SHARED_LIB} DESTINATION plugins)
else()
#add_executable(${RC_EXE} ${RC_SRC})
#add_executable(${CLIENT_EXE} ${CLIENT_SRC})
#add_executable(${DNS_EXE} ${DNS_SRC})
add_subdirectory(${GTEST_DIR})
if(NOT WIN32)
add_executable(${TEST_EXE} ${TEST_SRC})
@ -745,11 +755,10 @@ else()
target_link_libraries(${STATIC_LIB} ${CRYPTOGRAPHY_LIB} ${LIBS} ${UTIL_LIB} ${PLATFORM_LIB})
target_link_libraries(${EXE} ${STATIC_LINK_LIBS} ${STATIC_LIB} ${UTIL_LIB} ${PLATFORM_LIB})
target_link_libraries(${TEST_EXE} ${STATIC_LINK_LIBS} gtest_main ${STATIC_LIB} ${UTIL_LIB} ${PLATFORM_LIB})
target_link_libraries(${TEST_EXE} ${STATIC_LINK_LIBS} gmock gtest ${STATIC_LIB} ${UTIL_LIB} ${PLATFORM_LIB})
if (WIN32)
target_link_libraries(${EXE} ${STATIC_LINK_LIBS} ${STATIC_LIB} ${UTIL_LIB} ${PLATFORM_LIB} ws2_32 iphlpapi)
target_link_libraries(${TEST_EXE} ${STATIC_LINK_LIBS} gtest_main ${STATIC_LIB} ${UTIL_LIB} ${PLATFORM_LIB} ws2_32 iphlpapi)
target_link_libraries(${TEST_EXE} ${STATIC_LINK_LIBS} gmock gtest ${STATIC_LIB} ${UTIL_LIB} ${PLATFORM_LIB} ws2_32 iphlpapi)
endif(WIN32)
@ -763,4 +772,4 @@ else()
target_link_libraries(${SHARED_LIB} ${CRYPTOGRAPHY_LIB} ${LIBS} ${UTIL_LIB} ${PLATFORM_LIB} Threads::Threads)
install(TARGETS ${SHARED_LIB} LIBRARY DESTINATION lib)
endif(WITH_SHARED)
endif(SHADOW)
endif(SHADOW)

@ -0,0 +1,3 @@
#!/bin/sh
rm -fr loki*/tmp-nodes
rm loki*/profile.dat

@ -0,0 +1,9 @@
#!/bin/sh
# copy a lokinet binary into this cluster
cp ../../lokinet .
# generate default config file
./lokinet -g lokinet.ini
# make seed node
./makenode.sh 1
# establish bootstrap
ln -s loki1/self.signed bootstrap.signed

@ -0,0 +1,7 @@
mkdir loki$1
cd loki$1
ln -s ../lokinet lokinet$1
cp ../lokinet.ini .
nano lokinet.ini
cd ..
echo "killall -9 lokinet$1" >> ../stop.sh

@ -0,0 +1,16 @@
#!/bin/bash
set +x
cd loki1
nohup ./lokinet1 $PWD/lokinet.ini &
# seed node needs some time to write RC to make sure it's not expired on load for the rest
sleep 1
cd ../loki2
nohup ./lokinet2 $PWD/lokinet.ini &
cd ../loki3
nohup ./lokinet3 $PWD/lokinet.ini &
cd ../loki4
nohup ./lokinet4 $PWD/lokinet.ini &
cd ../loki5
nohup ./lokinet5 $PWD/lokinet.ini &
cd ..
tail -f loki*/nohup.out

@ -0,0 +1,7 @@
#!/bin/sh
killall -9 lokinet1
killall -9 lokinet2
killall -9 lokinet3
killall -9 lokinet4
killall -9 lokinet5
killall -9 lokinet6

@ -21,31 +21,43 @@ WINNT_LIBS ?=
# windows target only
.c.o:
$(CC) $(WINNT_INCLUDE) -Ofast -march=core2 -mfpmath=sse $< -c
$(CC) $(WINNT_INCLUDE) -Iinclude -Ofast -march=nocona -mfpmath=sse $< -c
zpipe: zpipe.c miniz.c
$(NATIVE_CC) $(INCLUDE) $(LIBS) $^ -s -static -o $@
$(NATIVE_CC) $(INCLUDE) -Iinclude $(LIBS) $^ -s -static -o $@
base64enc: base64enc.c
$(NATIVE_CC) $(INCLUDE) $(LIBS) $^ -s -static -o $@ -lmbedx509 -lmbedtls -lmbedcrypto
$(NATIVE_CC) $(INCLUDE) -Iinclude $(LIBS) $^ -s -static -o $@ -lmbedx509 -lmbedtls -lmbedcrypto
download:
wget -O ./cacert.pem https://curl.haxx.se/ca/cacert.pem
# I *think* this only work with GNU sed...
prepare: zpipe base64enc download
./zpipe < cacert.pem > data.enc
./base64enc < data.enc > out.bin
sed -ie "s/.\{76\}/&\n/g" out.bin
sed -i 's/.*/\"&\"/g' out.bin
sed -i '49,2192d' bootstrap.c
sed -i '51,2258d' bootstrap.c
echo ';' >> out.bin
sed -i '48r out.bin' bootstrap.c
sed -i '50r out.bin' bootstrap.c
prepare-testnet: zpipe base64enc download
./zpipe < cacert.pem > data.enc
./base64enc < data.enc > out.bin
sed -ie "s/.\{76\}/&\n/g" out.bin
sed -i 's/.*/\"&\"/g' out.bin
sed -i '49,2135d' testnet.c
echo ';' >> out.bin
sed -i '48r out.bin' testnet.c
lokinet-bootstrap: bootstrap.o miniz.o
$(CC) $(WINNT_LIBS) -static -s $^ -o $@.exe -lmbedx509 -lmbedtls -lmbedcrypto -lws2_32
lokinet-bootstrap-testnet: testnet.o miniz.o
$(CC) $(WINNT_LIBS) -static -s $^ -o $@.exe -lmbedx509 -lmbedtls -lmbedcrypto -lws2_32
clean:
-@rm lokinet-bootstrap.exe
-@rm lokinet*.exe
-@rm base64enc
-@rm zpipe
-@rm cacert.pem

@ -18,18 +18,18 @@ native build:
$ export WINNT_INCLUDE=$INCLUDE WINNT_LIBS=$LIBS
$ make prepare;make lokinet-bootstrap
cross-compile build:
cross-compile build (If you have *GNU* sed, you can also update the certificate trust store with `make prepare`):
$ export INCLUDE=/usr/local/include LIBS=/usr/local/lib # or a different path
$ export CC=i686-w64-mingw32-gcc # change these if you use clang, make sure these are in your system $PATH!
$ export NATIVE_CC=cc
$ export WINNT_INCLUDE=/path/to/win32/headers WINNT_LIBS=/path/to/win32/libs
$ make prepare;make lokinet-bootstrap
$ make lokinet-bootstrap
# Usage
C:\>lokinet-bootstrap [uri] [local download path]
C:\>lokinet-bootstrap [uri] [local download path]
this is also included in the lokinet installer package.
-despair86
-rick

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -6,6 +6,7 @@
#include <getopt.h>
#include <signal.h>
#include <wordexp.h>
#include <string>
#include <iostream>
@ -56,6 +57,22 @@ handle_signal_win32(DWORD fdwCtrlType)
}
#endif
/// resolve ~ and symlinks into actual paths (so we know the real path on disk,
/// to remove assumptions and confusion with permissions)
std::string
resolvePath(std::string conffname)
{
wordexp_t exp_result;
wordexp(conffname.c_str(), &exp_result, 0);
char *resolvedPath = realpath(exp_result.we_wordv[0], NULL);
if(!resolvedPath)
{
llarp::LogWarn("Can't resolve path: ", exp_result.we_wordv[0]);
return "";
}
return resolvedPath;
}
int
main(int argc, char *argv[])
{
@ -108,7 +125,7 @@ main(int argc, char *argv[])
}
}
std::string conffname;
std::string conffname; // suggestions: confFName? conf_fname?
if(optind < argc)
{
@ -116,15 +133,33 @@ main(int argc, char *argv[])
fs::path fname = fs::path(argv[optind]);
fs::path basedir = fname.parent_path();
conffname = fname.string();
conffname = resolvePath(conffname);
std::error_code ec;
// llarp::LogDebug("Basedir: ", basedir);
if(basedir.string().empty())
{
if(!llarp_ensure_config(fname.string().c_str(), nullptr, overWrite,
asRouter))
return 1;
// relative path to config
// does this file exist?
if(genconfigOnly)
{
if(!llarp_ensure_config(conffname.c_str(), nullptr, overWrite,
asRouter))
return 1;
}
else
{
if(!fs::exists(fname, ec))
{
llarp::LogError("Config file not found ", conffname);
return 1;
}
}
}
else
{
std::error_code ec;
// absolute path to config
if(!fs::create_directories(basedir, ec))
{
if(ec)
@ -134,9 +169,22 @@ main(int argc, char *argv[])
return 1;
}
}
if(!llarp_ensure_config(fname.string().c_str(), basedir.string().c_str(),
overWrite, asRouter))
return 1;
if(genconfigOnly)
{
// find or create file
if(!llarp_ensure_config(conffname.c_str(), basedir.string().c_str(),
overWrite, asRouter))
return 1;
}
else
{
// does this file exist?
if(!fs::exists(conffname, ec))
{
llarp::LogError("Config file not found ", conffname);
return 1;
}
}
}
}
else
@ -149,6 +197,8 @@ main(int argc, char *argv[])
#endif
fs::path basepath = homedir / fs::path(".lokinet");
fs::path fpath = basepath / "lokinet.ini";
// I don't think this is necessary with this condition
// conffname = resolvePath(conffname);
llarp::LogDebug("Find or create ", basepath.string());
std::error_code ec;
@ -164,6 +214,7 @@ main(int argc, char *argv[])
}
}
// if using default INI file, we're create it even if you don't ask us too
if(!llarp_ensure_config(fpath.string().c_str(), basepath.string().c_str(),
overWrite, asRouter))
return 1;
@ -176,6 +227,7 @@ main(int argc, char *argv[])
}
// this is important, can downgrade from Info though
llarp::LogInfo("Running from: ", cpp17::filesystem::current_path());
llarp::LogInfo("Using config file: ", conffname);
ctx = llarp_main_init(conffname.c_str(), multiThreaded);
int code = 1;

@ -1,6 +1,7 @@
#include <llarp.hpp>
#include <llarp.h>
#include <crypto/crypto_libsodium.hpp>
#include <dht/context.hpp>
#include <dns/dotlokilookup.hpp>
#include <dnsd.hpp>
@ -97,8 +98,7 @@ namespace llarp
int
Context::LoadDatabase()
{
crypto = std::unique_ptr< llarp::Crypto >(
new llarp::Crypto{llarp::Crypto::sodium{}});
crypto = std::make_unique< sodium::CryptoLibSodium >();
nodedb = new llarp_nodedb(crypto.get(), router->disk);
if(!llarp_nodedb::ensure_dir(nodedb_dir.c_str()))

@ -19,8 +19,6 @@
namespace llarp
{
/// label functors
/// PKE(result, publickey, secretkey, nonce)
using path_dh_func = std::function< bool(
SharedSecret &, const PubKey &, const SecretKey &, const TunnelNonce &) >;
@ -29,88 +27,83 @@ namespace llarp
using transport_dh_func = std::function< bool(
SharedSecret &, const PubKey &, const SecretKey &, const TunnelNonce &) >;
/// SD/SE(buffer, key, nonce)
using sym_cipher_func = std::function< bool(
llarp_buffer_t, const SharedSecret &, const TunnelNonce &) >;
/// SD/SE(dst, src, key, nonce)
using sym_cipher_alt_func = std::function< bool(
llarp_buffer_t, llarp_buffer_t, const SharedSecret &, const byte_t *) >;
/// H(result, body)
using hash_func = std::function< bool(byte_t *, llarp_buffer_t) >;
/// SH(result, body)
using shorthash_func = std::function< bool(ShortHash &, llarp_buffer_t) >;
/// MDS(result, body, shared_secret)
using hmac_func =
std::function< bool(byte_t *, llarp_buffer_t, const SharedSecret &) >;
/// S(sig, secretkey, body)
using sign_func =
std::function< bool(Signature &, const SecretKey &, llarp_buffer_t) >;
/// V(pubkey, body, sig)
using verify_func =
std::function< bool(const PubKey &, llarp_buffer_t, const Signature &) >;
/// converts seed to secretkey
using seed_to_secret_func =
std::function< bool(llarp::SecretKey &, const llarp::IdentitySecret &) >;
/// library crypto configuration
struct Crypto
{
virtual ~Crypto() = 0;
/// xchacha symmetric cipher
sym_cipher_func xchacha20;
virtual bool
xchacha20(llarp_buffer_t, const SharedSecret &, const TunnelNonce &) = 0;
/// xchacha symmetric cipher (multibuffer)
sym_cipher_alt_func xchacha20_alt;
virtual bool
xchacha20_alt(llarp_buffer_t, llarp_buffer_t, const SharedSecret &,
const byte_t *) = 0;
/// path dh creator's side
path_dh_func dh_client;
virtual bool
dh_client(SharedSecret &, const PubKey &, const SecretKey &,
const TunnelNonce &) = 0;
/// path dh relay side
path_dh_func dh_server;
virtual bool
dh_server(SharedSecret &, const PubKey &, const SecretKey &,
const TunnelNonce &) = 0;
/// transport dh client side
transport_dh_func transport_dh_client;
virtual bool
transport_dh_client(SharedSecret &, const PubKey &, const SecretKey &,
const TunnelNonce &) = 0;
/// transport dh server side
transport_dh_func transport_dh_server;
virtual bool
transport_dh_server(SharedSecret &, const PubKey &, const SecretKey &,
const TunnelNonce &) = 0;
/// blake2b 512 bit
hash_func hash;
virtual bool
hash(byte_t *, llarp_buffer_t) = 0;
/// blake2b 256 bit
shorthash_func shorthash;
virtual bool
shorthash(ShortHash &, llarp_buffer_t) = 0;
/// blake2s 256 bit hmac
hmac_func hmac;
virtual bool
hmac(byte_t *, llarp_buffer_t, const SharedSecret &) = 0;
/// ed25519 sign
sign_func sign;
virtual bool
sign(Signature &, const SecretKey &, llarp_buffer_t) = 0;
/// ed25519 verify
verify_func verify;
virtual bool
verify(const PubKey &, llarp_buffer_t, const Signature &) = 0;
/// seed to secretkey
seed_to_secret_func seed_to_secretkey;
virtual bool
seed_to_secretkey(llarp::SecretKey &, const llarp::IdentitySecret &) = 0;
/// randomize buffer
std::function< void(llarp_buffer_t) > randomize;
virtual void randomize(llarp_buffer_t) = 0;
/// randomizer memory
std::function< void(void *, size_t) > randbytes;
virtual void
randbytes(void *, size_t) = 0;
/// generate signing keypair
std::function< void(SecretKey &) > identity_keygen;
virtual void
identity_keygen(SecretKey &) = 0;
/// generate encryption keypair
std::function< void(SecretKey &) > encryption_keygen;
virtual void
encryption_keygen(SecretKey &) = 0;
/// generate post quantum encrytion key
std::function< void(PQKeyPair &) > pqe_keygen;
virtual void
pqe_keygen(PQKeyPair &) = 0;
/// post quantum decrypt (buffer, sharedkey_dst, sec)
std::function< bool(const PQCipherBlock &, SharedSecret &, const byte_t *) >
pqe_decrypt;
virtual bool
pqe_decrypt(const PQCipherBlock &, SharedSecret &, const byte_t *) = 0;
/// post quantum encrypt (buffer, sharedkey_dst, pub)
std::function< bool(PQCipherBlock &, SharedSecret &, const PQPubKey &) >
pqe_encrypt;
// Give a basic type tag for the constructor to pick libsodium
struct sodium
{
};
Crypto(Crypto::sodium tag);
virtual bool
pqe_encrypt(PQCipherBlock &, SharedSecret &, const PQPubKey &) = 0;
};
inline Crypto::~Crypto()
{
}
/// return random 64bit unsigned interger
uint64_t
randint();

@ -1,4 +1,4 @@
#include <crypto/crypto.hpp>
#include <crypto/crypto_libsodium.hpp>
#include <sodium/crypto_generichash.h>
#include <sodium/crypto_sign.h>
@ -18,24 +18,6 @@ namespace llarp
{
namespace sodium
{
static bool
xchacha20(llarp_buffer_t buff, const SharedSecret &k, const TunnelNonce &n)
{
return crypto_stream_xchacha20_xor(buff.base, buff.base, buff.sz,
n.data(), k.data())
== 0;
}
static bool
xchacha20_alt(llarp_buffer_t out, llarp_buffer_t in, const SharedSecret &k,
const byte_t *n)
{
if(in.sz > out.sz)
return false;
return crypto_stream_xchacha20_xor(out.base, in.base, in.sz, n, k.data())
== 0;
}
static bool
dh(llarp::SharedSecret &out, const PubKey &client_pk,
const PubKey &server_pk, const uint8_t *themPub, const SecretKey &usSec)
@ -44,7 +26,9 @@ namespace llarp
crypto_generichash_state h;
if(crypto_scalarmult_curve25519(shared.data(), usSec.data(), themPub))
{
return false;
}
crypto_generichash_blake2b_init(&h, nullptr, 0U, shared.size());
crypto_generichash_blake2b_update(&h, client_pk.data(), 32);
crypto_generichash_blake2b_update(&h, server_pk.data(), 32);
@ -54,8 +38,8 @@ namespace llarp
}
static bool
dh_client(llarp::SharedSecret &shared, const PubKey &pk,
const SecretKey &sk, const TunnelNonce &n)
dh_client_priv(llarp::SharedSecret &shared, const PubKey &pk,
const SecretKey &sk, const TunnelNonce &n)
{
llarp::SharedSecret dh_result;
@ -70,8 +54,8 @@ namespace llarp
}
static bool
dh_server(llarp::SharedSecret &shared, const PubKey &pk,
const SecretKey &sk, const TunnelNonce &n)
dh_server_priv(llarp::SharedSecret &shared, const PubKey &pk,
const SecretKey &sk, const TunnelNonce &n)
{
llarp::SharedSecret dh_result;
if(dh(dh_result, pk, sk.toPublic(), pk.data(), sk))
@ -84,113 +68,184 @@ namespace llarp
return false;
}
static bool
hash(uint8_t *result, llarp_buffer_t buff)
CryptoLibSodium::CryptoLibSodium()
{
if(sodium_init() == -1)
{
throw std::runtime_error("sodium_init() returned -1");
}
char *avx2 = std::getenv("AVX2_FORCE_DISABLE");
if(avx2 && std::string(avx2) == "1")
{
ntru_init(1);
}
else
{
ntru_init(0);
}
int seed = 0;
this->randbytes(&seed, sizeof(seed));
srand(seed);
}
bool
CryptoLibSodium::xchacha20(llarp_buffer_t buff, const SharedSecret &k,
const TunnelNonce &n)
{
return crypto_stream_xchacha20_xor(buff.base, buff.base, buff.sz,
n.data(), k.data())
== 0;
}
bool
CryptoLibSodium::xchacha20_alt(llarp_buffer_t out, llarp_buffer_t in,
const SharedSecret &k, const byte_t *n)
{
if(in.sz > out.sz)
return false;
return crypto_stream_xchacha20_xor(out.base, in.base, in.sz, n, k.data())
== 0;
}
bool
CryptoLibSodium::dh_client(llarp::SharedSecret &shared, const PubKey &pk,
const SecretKey &sk, const TunnelNonce &n)
{
return dh_client_priv(shared, pk, sk, n);
}
/// path dh relay side
bool
CryptoLibSodium::dh_server(llarp::SharedSecret &shared, const PubKey &pk,
const SecretKey &sk, const TunnelNonce &n)
{
return dh_server_priv(shared, pk, sk, n);
}
/// transport dh client side
bool
CryptoLibSodium::transport_dh_client(llarp::SharedSecret &shared,
const PubKey &pk, const SecretKey &sk,
const TunnelNonce &n)
{
return dh_client_priv(shared, pk, sk, n);
}
/// transport dh server side
bool
CryptoLibSodium::transport_dh_server(llarp::SharedSecret &shared,
const PubKey &pk, const SecretKey &sk,
const TunnelNonce &n)
{
return dh_server_priv(shared, pk, sk, n);
}
bool
CryptoLibSodium::hash(uint8_t *result, llarp_buffer_t buff)
{
return crypto_generichash_blake2b(result, HASHSIZE, buff.base, buff.sz,
nullptr, 0)
!= -1;
}
static bool
shorthash(ShortHash &result, llarp_buffer_t buff)
bool
CryptoLibSodium::shorthash(ShortHash &result, llarp_buffer_t buff)
{
return crypto_generichash_blake2b(result.data(), ShortHash::SIZE,
buff.base, buff.sz, nullptr, 0)
!= -1;
}
static bool
hmac(byte_t *result, llarp_buffer_t buff, const SharedSecret &secret)
bool
CryptoLibSodium::hmac(byte_t *result, llarp_buffer_t buff,
const SharedSecret &secret)
{
return crypto_generichash_blake2b(result, HMACSIZE, buff.base, buff.sz,
secret.data(), HMACSECSIZE)
!= -1;
}
static bool
sign(Signature &result, const SecretKey &secret, llarp_buffer_t buff)
bool
CryptoLibSodium::sign(Signature &result, const SecretKey &secret,
llarp_buffer_t buff)
{
int rc = crypto_sign_detached(result.data(), nullptr, buff.base, buff.sz,
secret.data());
return rc != -1;
}
static bool
verify(const PubKey &pub, llarp_buffer_t buff, const Signature &sig)
bool
CryptoLibSodium::verify(const PubKey &pub, llarp_buffer_t buff,
const Signature &sig)
{
int rc = crypto_sign_verify_detached(sig.data(), buff.base, buff.sz,
pub.data());
return rc != -1;
}
static bool
seed_to_secretkey(llarp::SecretKey &secret,
const llarp::IdentitySecret &seed)
bool
CryptoLibSodium::seed_to_secretkey(llarp::SecretKey &secret,
const llarp::IdentitySecret &seed)
{
byte_t pk[crypto_sign_ed25519_PUBLICKEYBYTES];
return crypto_sign_ed25519_seed_keypair(pk, secret.data(), seed.data())
!= -1;
}
static void
randomize(llarp_buffer_t buff)
void
CryptoLibSodium::randomize(llarp_buffer_t buff)
{
randombytes((unsigned char *)buff.base, buff.sz);
}
static inline void
randbytes(void *ptr, size_t sz)
void
CryptoLibSodium::randbytes(void *ptr, size_t sz)
{
randombytes((unsigned char *)ptr, sz);
}
static void
sigkeygen(llarp::SecretKey &keys)
void
CryptoLibSodium::identity_keygen(llarp::SecretKey &keys)
{
byte_t *d = keys.data();
crypto_sign_keypair(d + 32, d);
}
static void
enckeygen(llarp::SecretKey &keys)
void
CryptoLibSodium::encryption_keygen(llarp::SecretKey &keys)
{
auto d = keys.data();
randombytes(d, 32);
crypto_scalarmult_curve25519_base(d + 32, d);
}
} // namespace sodium
const byte_t *
seckey_topublic(const SecretKey &sec)
{
return sec.data() + 32;
}
namespace pq
{
bool
encrypt(PQCipherBlock &ciphertext, SharedSecret &sharedkey,
const PQPubKey &pubkey)
CryptoLibSodium::pqe_encrypt(PQCipherBlock &ciphertext,
SharedSecret &sharedkey,
const PQPubKey &pubkey)
{
return crypto_kem_enc(ciphertext.data(), sharedkey.data(), pubkey.data())
!= -1;
}
bool
decrypt(const PQCipherBlock &ciphertext, SharedSecret &sharedkey,
const byte_t *secretkey)
CryptoLibSodium::pqe_decrypt(const PQCipherBlock &ciphertext,
SharedSecret &sharedkey,
const byte_t *secretkey)
{
return crypto_kem_dec(sharedkey.data(), ciphertext.data(), secretkey)
!= -1;
}
void
keygen(PQKeyPair &keypair)
CryptoLibSodium::pqe_keygen(PQKeyPair &keypair)
{
auto d = keypair.data();
crypto_kem_keypair(d + PQ_SECRETKEYSIZE, d);
}
} // namespace pq
} // namespace sodium
const byte_t *
seckey_topublic(const SecretKey &sec)
{
return sec.data() + 32;
}
const byte_t *
pq_keypair_to_public(const PQKeyPair &k)
@ -204,40 +259,6 @@ namespace llarp
return k.data();
}
Crypto::Crypto(Crypto::sodium tag)
{
(void)tag;
if(sodium_init() == -1)
throw std::runtime_error("sodium_init() returned -1");
char *avx2 = std::getenv("AVX2_FORCE_DISABLE");
if(avx2 && std::string(avx2) == "1")
ntru_init(1);
else
ntru_init(0);
this->xchacha20 = llarp::sodium::xchacha20;
this->xchacha20_alt = llarp::sodium::xchacha20_alt;
this->dh_client = llarp::sodium::dh_client;
this->dh_server = llarp::sodium::dh_server;
this->transport_dh_client = llarp::sodium::dh_client;
this->transport_dh_server = llarp::sodium::dh_server;
this->hash = llarp::sodium::hash;
this->shorthash = llarp::sodium::shorthash;
this->hmac = llarp::sodium::hmac;
this->sign = llarp::sodium::sign;
this->verify = llarp::sodium::verify;
this->randomize = llarp::sodium::randomize;
this->randbytes = llarp::sodium::randbytes;
this->identity_keygen = llarp::sodium::sigkeygen;
this->encryption_keygen = llarp::sodium::enckeygen;
this->seed_to_secretkey = llarp::sodium::seed_to_secretkey;
this->pqe_encrypt = llarp::pq::encrypt;
this->pqe_decrypt = llarp::pq::decrypt;
this->pqe_keygen = llarp::pq::keygen;
int seed = 0;
this->randbytes(&seed, sizeof(seed));
srand(seed);
}
uint64_t
randint()
{

@ -0,0 +1,89 @@
#ifndef LLARP_CRYPTO_LIBSODIUM_HPP
#define LLARP_CRYPTO_LIBSODIUM_HPP
#include <crypto/crypto.hpp>
namespace llarp
{
namespace sodium
{
struct CryptoLibSodium final : public Crypto
{
CryptoLibSodium();
~CryptoLibSodium()
{
}
/// xchacha symmetric cipher
bool
xchacha20(llarp_buffer_t, const SharedSecret &,
const TunnelNonce &) override;
/// xchacha symmetric cipher (multibuffer)
bool
xchacha20_alt(llarp_buffer_t, llarp_buffer_t, const SharedSecret &,
const byte_t *) override;
/// path dh creator's side
bool
dh_client(SharedSecret &, const PubKey &, const SecretKey &,
const TunnelNonce &) override;
/// path dh relay side
bool
dh_server(SharedSecret &, const PubKey &, const SecretKey &,
const TunnelNonce &) override;
/// transport dh client side
bool
transport_dh_client(SharedSecret &, const PubKey &, const SecretKey &,
const TunnelNonce &) override;
/// transport dh server side
bool
transport_dh_server(SharedSecret &, const PubKey &, const SecretKey &,
const TunnelNonce &) override;
/// blake2b 512 bit
bool
hash(byte_t *, llarp_buffer_t) override;
/// blake2b 256 bit
bool
shorthash(ShortHash &, llarp_buffer_t) override;
/// blake2s 256 bit hmac
bool
hmac(byte_t *, llarp_buffer_t, const SharedSecret &) override;
/// ed25519 sign
bool
sign(Signature &, const SecretKey &, llarp_buffer_t) override;
/// ed25519 verify
bool
verify(const PubKey &, llarp_buffer_t, const Signature &) override;
/// seed to secretkey
bool
seed_to_secretkey(llarp::SecretKey &,
const llarp::IdentitySecret &) override;
/// randomize buffer
void randomize(llarp_buffer_t) override;
/// randomizer memory
void
randbytes(void *, size_t) override;
/// generate signing keypair
void
identity_keygen(SecretKey &) override;
/// generate encryption keypair
void
encryption_keygen(SecretKey &) override;
/// generate post quantum encrytion key
void
pqe_keygen(PQKeyPair &) override;
/// post quantum decrypt (buffer, sharedkey_dst, sec)
bool
pqe_decrypt(const PQCipherBlock &, SharedSecret &,
const byte_t *) override;
/// post quantum encrypt (buffer, sharedkey_dst, pub)
bool
pqe_encrypt(PQCipherBlock &, SharedSecret &, const PQPubKey &) override;
};
} // namespace sodium
} // namespace llarp
#endif

@ -24,10 +24,6 @@ namespace llarp
SharedSecret shared;
auto DH = crypto->dh_client;
auto Encrypt = crypto->xchacha20;
auto MDS = crypto->hmac;
llarp_buffer_t buf;
buf.base = body;
buf.cur = buf.base;
@ -40,14 +36,14 @@ namespace llarp
TunnelNonce nonce(noncePtr);
// derive shared key
if(!DH(shared, otherPubkey, ourSecretKey, nonce))
if(!crypto->dh_client(shared, otherPubkey, ourSecretKey, nonce))
{
llarp::LogError("DH failed");
return false;
}
// encrypt body
if(!Encrypt(buf, shared, nonce))
if(!crypto->xchacha20(buf, shared, nonce))
{
llarp::LogError("encrypt failed");
return false;
@ -58,7 +54,7 @@ namespace llarp
buf.cur = buf.base;
buf.sz = size() - SHORTHASHSIZE;
if(!MDS(hash, buf, shared))
if(!crypto->hmac(hash, buf, shared))
{
llarp::LogError("Failed to generate message auth");
return false;
@ -82,14 +78,10 @@ namespace llarp
TunnelNonce nonce(noncePtr);
PubKey otherPubkey(noncePtr + TUNNONCESIZE);
// use dh_server because we are not the creator of this message
auto DH = crypto->dh_server;
auto Decrypt = crypto->xchacha20;
auto MDS = crypto->hmac;
SharedSecret shared;
if(!DH(shared, otherPubkey, ourSecretKey, nonce))
// use dh_server because we are not the creator of this message
if(!crypto->dh_server(shared, otherPubkey, ourSecretKey, nonce))
{
llarp::LogError("DH failed");
return false;
@ -101,7 +93,7 @@ namespace llarp
buf.sz = size() - SHORTHASHSIZE;
ShortHash digest;
if(!MDS(digest.data(), buf, shared))
if(!crypto->hmac(digest.data(), buf, shared))
{
llarp::LogError("Digest failed");
return false;
@ -117,7 +109,7 @@ namespace llarp
buf.cur = body;
buf.sz = size() - EncryptedFrameOverheadSize;
if(!Decrypt(buf, shared, nonce))
if(!crypto->xchacha20(buf, shared, nonce))
{
llarp::LogError("decrypt failed");
return false;

@ -58,6 +58,24 @@ namespace llarp
}
};
inline bool
operator==(const PubKey &lhs, const PubKey &rhs)
{
return lhs.as_array() == rhs.as_array();
}
inline bool
operator==(const PubKey &lhs, const RouterID &rhs)
{
return lhs.as_array() == rhs.as_array();
}
inline bool
operator==(const RouterID &lhs, const PubKey &rhs)
{
return lhs.as_array() == rhs.as_array();
}
struct SecretKey final : public AlignedBuffer< SECKEYSIZE >
{
SecretKey() : AlignedBuffer< SECKEYSIZE >(){};

@ -1,10 +1,18 @@
#include <dht/context.hpp>
#include <dht/explorenetworkjob.hpp>
#include <dht/localrouterlookup.hpp>
#include <dht/localserviceaddresslookup.hpp>
#include <dht/localtaglookup.hpp>
#include <dht/messages/findrouter.hpp>
#include <dht/messages/gotintro.hpp>
#include <dht/messages/gotrouter.hpp>
#include <dht/messages/pubintro.hpp>
#include <dht/node.hpp>
#include <dht/publishservicejob.hpp>
#include <dht/recursiverouterlookup.hpp>
#include <dht/serviceaddresslookup.hpp>
#include <dht/taglookup.hpp>
#include <messages/dht.hpp>
#include <messages/dht_immediate.hpp>
#include <router/router.hpp>
@ -15,18 +23,11 @@ namespace llarp
{
namespace dht
{
Context::Context()
{
randombytes((byte_t *)&ids, sizeof(uint64_t));
allowTransit = false;
}
AbstractContext::~AbstractContext() {}
Context::~Context()
Context::Context() : router(nullptr), allowTransit(false)
{
if(nodes)
delete nodes;
if(services)
delete services;
randombytes((byte_t *)&ids, sizeof(uint64_t));
}
void
@ -45,53 +46,6 @@ namespace llarp
llarp::LogError("failed to select random nodes for exploration");
}
struct ExploreNetworkJob : public TX< RouterID, RouterID >
{
ExploreNetworkJob(const RouterID &peer, Context *ctx)
: TX< RouterID, RouterID >(TXOwner{}, peer, ctx)
{
}
bool
Validate(const RouterID &) const override
{
// TODO: check with lokid
return true;
}
void
Start(const TXOwner &peer) override
{
parent->DHTSendTo(peer.node.as_array(),
new FindRouterMessage(peer.txid));
}
bool
GetNextPeer(Key_t &, const std::set< Key_t > &) override
{
return false;
}
void
DoNextRequest(const Key_t &) override
{
}
void
SendReply() override
{
llarp::LogInfo("got ", valuesFound.size(), " routers from exploration");
for(const auto &pk : valuesFound)
{
// lookup router
parent->LookupRouter(
pk,
std::bind(&llarp::Router::HandleDHTLookupForExplore,
parent->router, pk, std::placeholders::_1));
}
}
};
void
Context::ExploreNetworkVia(const Key_t &askpeer)
{
@ -272,8 +226,8 @@ namespace llarp
{
router = r;
ourKey = us;
nodes = new Bucket< RCNode >(ourKey, llarp::randint);
services = new Bucket< ISNode >(ourKey, llarp::randint);
nodes = std::make_unique< Bucket< RCNode > >(ourKey, llarp::randint);
services = std::make_unique< Bucket< ISNode > >(ourKey, llarp::randint);
llarp::LogDebug("initialize dht with key ", ourKey);
// start exploring
@ -308,7 +262,7 @@ namespace llarp
llarp::routing::DHTMessage reply;
if(!msg->HandleMessage(router->dht, reply.M))
return false;
if(reply.M.size())
if(!reply.M.empty())
{
auto path = router->paths.GetByUpstream(router->pubkey(), id);
return path && path->SendRoutingMessage(&reply, router);
@ -316,112 +270,6 @@ namespace llarp
return true;
}
struct ServiceAddressLookup
: public TX< service::Address, service::IntroSet >
{
IntroSetLookupHandler handleResult;
uint64_t R;
ServiceAddressLookup(const TXOwner &asker, const service::Address &addr,
Context *ctx, uint64_t r,
IntroSetLookupHandler handler)
: TX< service::Address, service::IntroSet >(asker, addr, ctx)
, handleResult(handler)
, R(r)
{
peersAsked.insert(ctx->OurKey());
}
bool
Validate(const service::IntroSet &value) const override
{
if(!value.Verify(parent->Crypto(), parent->Now()))
{
llarp::LogWarn("Got invalid introset from service lookup");
return false;
}
if(value.A.Addr() != target)
{
llarp::LogWarn("got introset with wrong target from service lookup");
return false;
}
return true;
}
bool
GetNextPeer(Key_t &next, const std::set< Key_t > &exclude) override
{
Key_t k = target.ToKey();
return parent->nodes->FindCloseExcluding(k, next, exclude);
}
void
Start(const TXOwner &peer) override
{
parent->DHTSendTo(peer.node.as_array(),
new FindIntroMessage(peer.txid, target, R));
}
void
DoNextRequest(const Key_t &ask) override
{
if(R)
parent->LookupIntroSetRecursive(target, whoasked.node, whoasked.txid,
ask, R - 1);
else
parent->LookupIntroSetIterative(target, whoasked.node, whoasked.txid,
ask);
}
virtual void
SendReply() override
{
if(handleResult)
handleResult(valuesFound);
parent->DHTSendTo(whoasked.node.as_array(),
new GotIntroMessage(valuesFound, whoasked.txid));
}
};
struct LocalServiceAddressLookup : public ServiceAddressLookup
{
PathID_t localPath;
LocalServiceAddressLookup(const PathID_t &pathid, uint64_t txid,
const service::Address &addr, Context *ctx,
__attribute__((unused)) const Key_t &askpeer)
: ServiceAddressLookup(TXOwner{ctx->OurKey(), txid}, addr, ctx, 5,
nullptr)
, localPath(pathid)
{
}
void
SendReply() override
{
auto path = parent->router->paths.GetByUpstream(
parent->OurKey().as_array(), localPath);
if(!path)
{
llarp::LogWarn(
"did not send reply for relayed dht request, no such local path "
"for pathid=",
localPath);
return;
}
routing::DHTMessage msg;
msg.M.emplace_back(new GotIntroMessage(valuesFound, whoasked.txid));
if(!path->SendRoutingMessage(&msg, parent->router))
{
llarp::LogWarn(
"failed to send routing message when informing result of dht "
"request, pathid=",
localPath);
}
}
};
void
Context::LookupIntroSetForPath(const service::Address &addr, uint64_t txid,
const llarp::PathID_t &path,
@ -434,62 +282,6 @@ namespace llarp
new LocalServiceAddressLookup(path, txid, addr, this, askpeer));
}
struct PublishServiceJob : public TX< service::Address, service::IntroSet >
{
uint64_t S;
std::set< Key_t > dontTell;
service::IntroSet I;
PublishServiceJob(const TXOwner &asker, const service::IntroSet &introset,
Context *ctx, uint64_t s,
const std::set< Key_t > &exclude)
: TX< service::Address, service::IntroSet >(asker, introset.A.Addr(),
ctx)
, S(s)
, dontTell(exclude)
, I(introset)
{
}
bool
Validate(const service::IntroSet &introset) const override
{
if(I.A != introset.A)
{
llarp::LogWarn(
"publish introset acknowledgement acked a different service");
return false;
}
return true;
}
void
Start(const TXOwner &peer) override
{
std::vector< Key_t > exclude;
for(const auto &router : dontTell)
exclude.push_back(router);
parent->DHTSendTo(peer.node.as_array(),
new PublishIntroMessage(I, peer.txid, S, exclude));
}
bool
GetNextPeer(Key_t &, const std::set< Key_t > &) override
{
return false;
}
void
DoNextRequest(const Key_t &) override
{
}
void
SendReply() override
{
// don't need this
}
};
void
Context::PropagateIntroSetTo(const Key_t &from, uint64_t txid,
const service::IntroSet &introset,
@ -508,7 +300,7 @@ namespace llarp
Context::LookupIntroSetRecursive(const service::Address &addr,
const Key_t &whoasked, uint64_t txid,
const Key_t &askpeer, uint64_t R,
IntroSetLookupHandler handler)
service::IntroSetLookupHandler handler)
{
TXOwner asker(whoasked, txid);
TXOwner peer(askpeer, ++ids);
@ -521,7 +313,7 @@ namespace llarp
Context::LookupIntroSetIterative(const service::Address &addr,
const Key_t &whoasked, uint64_t txid,
const Key_t &askpeer,
IntroSetLookupHandler handler)
service::IntroSetLookupHandler handler)
{
TXOwner asker(whoasked, txid);
TXOwner peer(askpeer, ++ids);
@ -530,76 +322,6 @@ namespace llarp
new ServiceAddressLookup(asker, addr, this, 0, handler));
}
struct TagLookup : public TX< service::Tag, service::IntroSet >
{
uint64_t R;
TagLookup(const TXOwner &asker, const service::Tag &tag, Context *ctx,
uint64_t r)
: TX< service::Tag, service::IntroSet >(asker, tag, ctx), R(r)
{
}
bool
Validate(const service::IntroSet &introset) const override
{
if(!introset.Verify(parent->Crypto(), parent->Now()))
{
llarp::LogWarn("got invalid introset from tag lookup");
return false;
}
if(introset.topic != target)
{
llarp::LogWarn("got introset with missmatched topic in tag lookup");
return false;
}
return true;
}
void
Start(const TXOwner &peer) override
{
parent->DHTSendTo(peer.node.as_array(),
new FindIntroMessage(target, peer.txid, R));
}
bool
GetNextPeer(Key_t &, const std::set< Key_t > &) override
{
return false;
}
void
DoNextRequest(const Key_t &) override
{
}
void
SendReply() override
{
std::set< service::IntroSet > found;
for(const auto &remoteTag : valuesFound)
{
found.insert(remoteTag);
}
// collect our local values if we haven't hit a limit
if(found.size() < 2)
{
for(const auto &localTag :
parent->FindRandomIntroSetsWithTagExcluding(target, 1, found))
{
found.insert(localTag);
}
}
std::vector< service::IntroSet > values;
for(const auto &introset : found)
{
values.push_back(introset);
}
parent->DHTSendTo(whoasked.node.as_array(),
new GotIntroMessage(values, whoasked.txid));
}
};
void
Context::LookupTagRecursive(const service::Tag &tag, const Key_t &whoasked,
uint64_t whoaskedTX, const Key_t &askpeer,
@ -613,42 +335,6 @@ namespace llarp
" R=", R);
}
struct LocalTagLookup : public TagLookup
{
PathID_t localPath;
LocalTagLookup(const PathID_t &path, uint64_t txid,
const service::Tag &target, Context *ctx)
: TagLookup(TXOwner{ctx->OurKey(), txid}, target, ctx, 0)
, localPath(path)
{
}
void
SendReply() override
{
auto path = parent->router->paths.GetByUpstream(
parent->OurKey().as_array(), localPath);
if(!path)
{
llarp::LogWarn(
"did not send reply for relayed dht request, no such local path "
"for pathid=",
localPath);
return;
}
routing::DHTMessage msg;
msg.M.emplace_back(new GotIntroMessage(valuesFound, whoasked.txid));
if(!path->SendRoutingMessage(&msg, parent->router))
{
llarp::LogWarn(
"failed to send routing message when informing result of dht "
"request, pathid=",
localPath);
}
}
};
void
Context::LookupTagForPath(const service::Tag &tag, uint64_t txid,
const llarp::PathID_t &path, const Key_t &askpeer)
@ -699,101 +385,6 @@ namespace llarp
return true;
}
struct RecursiveRouterLookup : public TX< RouterID, RouterContact >
{
RouterLookupHandler resultHandler;
RecursiveRouterLookup(const TXOwner &whoasked, const RouterID &target,
Context *ctx, RouterLookupHandler result)
: TX< RouterID, RouterContact >(whoasked, target, ctx)
, resultHandler(result)
{
peersAsked.insert(ctx->OurKey());
}
bool
Validate(const RouterContact &rc) const override
{
if(!rc.Verify(parent->Crypto(), parent->Now()))
{
llarp::LogWarn("rc from lookup result is invalid");
return false;
}
return true;
}
bool
GetNextPeer(Key_t &, const std::set< Key_t > &) override
{
return false;
}
void
DoNextRequest(const Key_t &) override
{
}
void
Start(const TXOwner &peer) override
{
parent->DHTSendTo(peer.node.as_array(),
new FindRouterMessage(peer.txid, target));
}
virtual void
SendReply() override
{
if(resultHandler)
{
resultHandler(valuesFound);
}
else
{
parent->DHTSendTo(
whoasked.node.as_array(),
new GotRouterMessage({}, whoasked.txid, valuesFound, false));
}
}
};
struct LocalRouterLookup : public RecursiveRouterLookup
{
PathID_t localPath;
LocalRouterLookup(const PathID_t &path, uint64_t txid,
const RouterID &target, Context *ctx)
: RecursiveRouterLookup(TXOwner{ctx->OurKey(), txid}, target, ctx,
nullptr)
, localPath(path)
{
}
void
SendReply() override
{
auto path = parent->router->paths.GetByUpstream(
parent->OurKey().as_array(), localPath);
if(!path)
{
llarp::LogWarn(
"did not send reply for relayed dht request, no such local path "
"for pathid=",
localPath);
return;
}
routing::DHTMessage msg;
msg.M.emplace_back(new GotRouterMessage(parent->OurKey(), whoasked.txid,
valuesFound, true));
if(!path->SendRoutingMessage(&msg, parent->router))
{
llarp::LogWarn(
"failed to send routing message when informing result of dht "
"request, pathid=",
localPath);
}
}
};
void
Context::LookupRouterForPath(const RouterID &target, uint64_t txid,
const llarp::PathID_t &path,
@ -824,13 +415,13 @@ namespace llarp
}
llarp::Crypto *
Context::Crypto()
Context::Crypto() const
{
return &router->crypto;
return router->crypto.get();
}
llarp_time_t
Context::Now()
Context::Now() const
{
return llarp_ev_loop_time_now_ms(router->netloop);
}

@ -1,5 +1,5 @@
#ifndef LLARP_DHT_CONTEXT_HPP
#define LLARP_DHT_CONTEXT_HPP
#ifndef LLARP_DHT_CONTEXT
#define LLARP_DHT_CONTEXT
#include <dht/bucket.hpp>
#include <dht/dht.h>
@ -7,6 +7,8 @@
#include <dht/message.hpp>
#include <dht/messages/findintro.hpp>
#include <dht/node.hpp>
#include <dht/tx.hpp>
#include <dht/txholder.hpp>
#include <dht/txowner.hpp>
#include <service/IntroSet.hpp>
#include <util/time.hpp>
@ -19,96 +21,61 @@ namespace llarp
namespace dht
{
struct Context;
template < typename K, typename V >
struct TX
struct AbstractContext
{
TX(const TXOwner& asker, const K& k, Context* p)
: target(k), whoasked(asker)
{
parent = p;
}
virtual ~TX(){};
K target;
Context* parent;
std::set< Key_t > peersAsked;
std::vector< V > valuesFound;
TXOwner whoasked;
virtual ~AbstractContext() = 0;
virtual bool
Validate(const V& value) const = 0;
LookupRouter(const RouterID& target, RouterLookupHandler result) = 0;
void
OnFound(const Key_t askedPeer, const V& value)
{
peersAsked.insert(askedPeer);
if(Validate(value))
valuesFound.push_back(value);
}
/// on behalf of whoasked request introset for target from dht router with
/// key askpeer
virtual void
LookupIntroSetRecursive(const service::Address& target,
const Key_t& whoasked, uint64_t whoaskedTX,
const Key_t& askpeer, uint64_t R,
service::IntroSetLookupHandler result = nullptr) = 0;
virtual void
Start(const TXOwner& peer) = 0;
LookupIntroSetIterative(const service::Address& target,
const Key_t& whoasked, uint64_t whoaskedTX,
const Key_t& askpeer,
service::IntroSetLookupHandler result = nullptr) = 0;
virtual bool
GetNextPeer(Key_t& next, const std::set< Key_t >& exclude) = 0;
virtual std::set< service::IntroSet >
FindRandomIntroSetsWithTagExcluding(
const service::Tag& tag, size_t max = 2,
const std::set< service::IntroSet >& excludes = {}) = 0;
virtual void
DoNextRequest(const Key_t& peer) = 0;
DHTSendTo(const RouterID& peer, IMessage* msg, bool keepalive = true) = 0;
/// return true if we want to persist this tx
bool
AskNextPeer(const Key_t& prevPeer, const std::unique_ptr< Key_t >& next)
{
peersAsked.insert(prevPeer);
Key_t peer;
if(next)
{
// explicit next peer provided
peer = *next;
}
else if(!GetNextPeer(peer, peersAsked))
{
// no more peers
llarp::LogInfo("no more peers for request asking for", target);
return false;
}
virtual llarp_time_t
Now() const = 0;
const Key_t targetKey{target};
if((prevPeer ^ targetKey) < (peer ^ targetKey))
{
// next peer is not closer
llarp::LogInfo("next peer ", peer, " is not closer to ", target,
" than ", prevPeer);
return false;
}
else
{
peersAsked.insert(peer);
}
DoNextRequest(peer);
return true;
}
virtual llarp::Crypto*
Crypto() const = 0;
virtual void
SendReply() = 0;
};
virtual llarp::Router*
GetRouter() const = 0;
using IntroSetLookupHandler =
std::function< void(const std::vector< service::IntroSet >&) >;
virtual const Key_t&
OurKey() const = 0;
using RouterLookupHandler =
std::function< void(const std::vector< RouterContact >&) >;
virtual Bucket< RCNode >* Nodes() const = 0;
};
struct Context
struct Context final : public AbstractContext
{
Context();
~Context();
~Context()
{
}
llarp::Crypto*
Crypto();
Crypto() const override;
/// on behalf of whoasked request introset for target from dht router with
/// key askpeer
@ -116,13 +83,13 @@ namespace llarp
LookupIntroSetRecursive(const service::Address& target,
const Key_t& whoasked, uint64_t whoaskedTX,
const Key_t& askpeer, uint64_t R,
IntroSetLookupHandler result = nullptr);
service::IntroSetLookupHandler result = nullptr) override;
void
LookupIntroSetIterative(const service::Address& target,
const Key_t& whoasked, uint64_t whoaskedTX,
const Key_t& askpeer,
IntroSetLookupHandler result = nullptr);
service::IntroSetLookupHandler result = nullptr) override;
/// on behalf of whoasked request router with public key target from dht
/// router with key askpeer
@ -132,11 +99,13 @@ namespace llarp
RouterLookupHandler result = nullptr);
bool
LookupRouter(const RouterID& target, RouterLookupHandler result)
LookupRouter(const RouterID& target, RouterLookupHandler result) override
{
Key_t askpeer;
if(!nodes->FindClosest(Key_t(target), askpeer))
{
return false;
}
LookupRouterRecursive(target, OurKey(), 0, askpeer, result);
return true;
}
@ -172,7 +141,7 @@ namespace llarp
/// send a dht message to peer, if keepalive is true then keep the session
/// with that peer alive for 10 seconds
void
DHTSendTo(const RouterID& peer, IMessage* msg, bool keepalive = true);
DHTSendTo(const RouterID& peer, IMessage* msg, bool keepalive = true) override;
/// get routers closest to target excluding requester
bool
@ -183,7 +152,7 @@ namespace llarp
std::set< service::IntroSet >
FindRandomIntroSetsWithTagExcluding(
const service::Tag& tag, size_t max = 2,
const std::set< service::IntroSet >& excludes = {});
const std::set< service::IntroSet >& excludes = {}) override;
/// handle rc lookup from requester for target
void
@ -221,143 +190,24 @@ namespace llarp
void
Explore(size_t N = 3);
llarp::Router* router = nullptr;
llarp::Router* router;
// for router contacts
Bucket< RCNode >* nodes = nullptr;
std::unique_ptr< Bucket< RCNode > > nodes;
// for introduction sets
Bucket< ISNode >* services = nullptr;
bool allowTransit = false;
std::unique_ptr< Bucket< ISNode > > services;
bool allowTransit;
Bucket< RCNode >* Nodes() const override { return nodes.get(); }
const Key_t&
OurKey() const
OurKey() const override
{
return ourKey;
}
template < typename K, typename V, typename K_Hash,
llarp_time_t requestTimeoutMS = 5000UL >
struct TXHolder
{
// tx who are waiting for a reply for each key
std::unordered_multimap< K, TXOwner, K_Hash > waiting;
// tx timesouts by key
std::unordered_map< K, llarp_time_t, K_Hash > timeouts;
// maps remote peer with tx to handle reply from them
std::unordered_map< TXOwner, std::unique_ptr< TX< K, V > >,
TXOwner::Hash >
tx;
const TX< K, V >*
GetPendingLookupFrom(const TXOwner& owner) const
{
auto itr = tx.find(owner);
if(itr == tx.end())
return nullptr;
else
return itr->second.get();
}
bool
HasLookupFor(const K& target) const
{
return timeouts.find(target) != timeouts.end();
}
bool
HasPendingLookupFrom(const TXOwner& owner) const
{
return GetPendingLookupFrom(owner) != nullptr;
}
void
NewTX(const TXOwner& askpeer, const TXOwner& whoasked, const K& k,
TX< K, V >* t)
{
(void)whoasked;
tx.emplace(askpeer, std::unique_ptr< TX< K, V > >(t));
auto count = waiting.count(k);
waiting.insert(std::make_pair(k, askpeer));
auto itr = timeouts.find(k);
if(itr == timeouts.end())
{
timeouts.insert(
std::make_pair(k, time_now_ms() + requestTimeoutMS));
}
if(count == 0)
t->Start(askpeer);
}
/// mark tx as not fond
void
NotFound(const TXOwner& from, const std::unique_ptr< Key_t >& next)
{
bool sendReply = true;
auto txitr = tx.find(from);
if(txitr == tx.end())
return;
// ask for next peer
if(txitr->second->AskNextPeer(from.node, next))
sendReply = false;
llarp::LogWarn("Target key ", txitr->second->target);
Inform(from, txitr->second->target, {}, sendReply, sendReply);
}
void
Found(const TXOwner& from, const K& k, const std::vector< V >& values)
{
Inform(from, k, values, true);
}
/// inform all watches for key of values found
void
Inform(TXOwner from, K key, std::vector< V > values,
bool sendreply = false, bool removeTimeouts = true)
{
auto range = waiting.equal_range(key);
auto itr = range.first;
while(itr != range.second)
{
auto txitr = tx.find(itr->second);
if(txitr != tx.end())
{
for(const auto& value : values)
txitr->second->OnFound(from.node, value);
if(sendreply)
{
txitr->second->SendReply();
tx.erase(txitr);
}
}
++itr;
}
if(sendreply)
waiting.erase(key);
if(removeTimeouts)
timeouts.erase(key);
}
void
Expire(llarp_time_t now)
{
auto itr = timeouts.begin();
while(itr != timeouts.end())
{
if(now > itr->second && now - itr->second >= requestTimeoutMS)
{
Inform(TXOwner{}, itr->first, {}, true, false);
itr = timeouts.erase(itr);
}
else
++itr;
}
}
};
llarp::Router*
GetRouter() const override { return router; }
TXHolder< service::Address, service::IntroSet, service::Address::Hash >
pendingIntrosetLookups;
@ -376,7 +226,7 @@ namespace llarp
}
llarp_time_t
Now();
Now() const override;
void
ExploreNetworkVia(const Key_t& peer);

@ -0,0 +1,32 @@
#include <dht/explorenetworkjob.hpp>
#include <dht/context.hpp>
#include <dht/messages/findrouter.hpp>
#include <router/router.hpp>
namespace llarp
{
namespace dht
{
void
ExploreNetworkJob::Start(const TXOwner &peer)
{
parent->DHTSendTo(peer.node.as_array(), new FindRouterMessage(peer.txid));
}
void
ExploreNetworkJob::SendReply()
{
llarp::LogInfo("got ", valuesFound.size(), " routers from exploration");
auto router = parent->GetRouter();
using std::placeholders::_1;
for(const auto &pk : valuesFound)
{
// lookup router
parent->LookupRouter(
pk, std::bind(&Router::HandleDHTLookupForExplore, router, pk, _1));
}
}
} // namespace dht
} // namespace llarp

@ -0,0 +1,45 @@
#ifndef LLARP_DHT_EXPLORENETWORKJOB
#define LLARP_DHT_EXPLORENETWORKJOB
#include <dht/tx.hpp>
#include <router_id.hpp>
namespace llarp
{
namespace dht
{
struct ExploreNetworkJob : public TX< RouterID, RouterID >
{
ExploreNetworkJob(const RouterID &peer, AbstractContext *ctx)
: TX< RouterID, RouterID >(TXOwner{}, peer, ctx)
{
}
bool
Validate(const RouterID &) const override
{
// TODO: check with lokid
return true;
}
void
Start(const TXOwner &peer) override;
bool
GetNextPeer(Key_t &, const std::set< Key_t > &) override
{
return false;
}
void
DoNextRequest(const Key_t &) override
{
}
void
SendReply() override;
};
} // namespace dht
} // namespace llarp
#endif

@ -0,0 +1,46 @@
#include <dht/localrouterlookup.hpp>
#include <dht/context.hpp>
#include <dht/messages/gotrouter.hpp>
#include <messages/dht.hpp>
#include <router/router.hpp>
#include <util/logger.hpp>
namespace llarp
{
namespace dht
{
LocalRouterLookup::LocalRouterLookup(const PathID_t &path, uint64_t txid,
const RouterID &target, AbstractContext *ctx)
: RecursiveRouterLookup(TXOwner{ctx->OurKey(), txid}, target, ctx,
nullptr)
, localPath(path)
{
}
void
LocalRouterLookup::SendReply()
{
auto path = parent->GetRouter()->paths.GetByUpstream(
parent->OurKey().as_array(), localPath);
if(!path)
{
llarp::LogWarn(
"did not send reply for relayed dht request, no such local path "
"for pathid=",
localPath);
return;
}
routing::DHTMessage msg;
msg.M.emplace_back(new GotRouterMessage(parent->OurKey(), whoasked.txid,
valuesFound, true));
if(!path->SendRoutingMessage(&msg, parent->GetRouter()))
{
llarp::LogWarn(
"failed to send routing message when informing result of dht "
"request, pathid=",
localPath);
}
}
} // namespace dht
} // namespace llarp

@ -0,0 +1,27 @@
#ifndef LLARP_DHT_LOCALROUTERLOOKUP
#define LLARP_DHT_LOCALROUTERLOOKUP
#include <dht/recursiverouterlookup.hpp>
#include <path/path_types.hpp>
#include <router_contact.hpp>
#include <router_id.hpp>
namespace llarp
{
namespace dht
{
struct LocalRouterLookup : public RecursiveRouterLookup
{
PathID_t localPath;
LocalRouterLookup(const PathID_t &path, uint64_t txid,
const RouterID &target, AbstractContext *ctx);
void
SendReply() override;
};
} // namespace dht
} // namespace llarp
#endif

@ -0,0 +1,45 @@
#include <dht/localserviceaddresslookup.hpp>
#include <dht/context.hpp>
#include <dht/messages/gotintro.hpp>
#include <router/router.hpp>
#include <util/logger.hpp>
namespace llarp
{
namespace dht
{
LocalServiceAddressLookup::LocalServiceAddressLookup(
const PathID_t &pathid, uint64_t txid, const service::Address &addr,
AbstractContext *ctx, __attribute__((unused)) const Key_t &askpeer)
: ServiceAddressLookup(TXOwner{ctx->OurKey(), txid}, addr, ctx, 5,
nullptr)
, localPath(pathid)
{
}
void
LocalServiceAddressLookup::SendReply()
{
auto path = parent->GetRouter()->paths.GetByUpstream(
parent->OurKey().as_array(), localPath);
if(!path)
{
llarp::LogWarn(
"did not send reply for relayed dht request, no such local path "
"for pathid=",
localPath);
return;
}
routing::DHTMessage msg;
msg.M.emplace_back(new GotIntroMessage(valuesFound, whoasked.txid));
if(!path->SendRoutingMessage(&msg, parent->GetRouter()))
{
llarp::LogWarn(
"failed to send routing message when informing result of dht "
"request, pathid=",
localPath);
}
}
} // namespace dht
} // namespace llarp

@ -0,0 +1,27 @@
#ifndef LLARP_DHT_LOCALSERVICEADDRESSLOOKUP
#define LLARP_DHT_LOCALSERVICEADDRESSLOOKUP
#include <dht/serviceaddresslookup.hpp>
#include <path/path_types.hpp>
namespace llarp
{
namespace dht
{
struct LocalServiceAddressLookup : public ServiceAddressLookup
{
PathID_t localPath;
LocalServiceAddressLookup(const PathID_t &pathid, uint64_t txid,
const service::Address &addr, AbstractContext *ctx,
__attribute__((unused)) const Key_t &askpeer);
void
SendReply() override;
};
} // namespace dht
} // namespace llarp
#endif

@ -0,0 +1,43 @@
#include <dht/localtaglookup.hpp>
#include <dht/context.hpp>
#include <dht/messages/gotintro.hpp>
#include <messages/dht.hpp>
#include <router/router.hpp>
namespace llarp
{
namespace dht
{
LocalTagLookup::LocalTagLookup(const PathID_t &path, uint64_t txid,
const service::Tag &target, AbstractContext *ctx)
: TagLookup(TXOwner{ctx->OurKey(), txid}, target, ctx, 0)
, localPath(path)
{
}
void
LocalTagLookup::SendReply()
{
auto path = parent->GetRouter()->paths.GetByUpstream(
parent->OurKey().as_array(), localPath);
if(!path)
{
llarp::LogWarn(
"did not send reply for relayed dht request, no such local path "
"for pathid=",
localPath);
return;
}
routing::DHTMessage msg;
msg.M.emplace_back(new GotIntroMessage(valuesFound, whoasked.txid));
if(!path->SendRoutingMessage(&msg, parent->GetRouter()))
{
llarp::LogWarn(
"failed to send routing message when informing result of dht "
"request, pathid=",
localPath);
}
}
} // namespace dht
} // namespace llarp

@ -0,0 +1,23 @@
#ifndef LLARP_DHT_LOOKUPTAGLOOKUP
#define LLARP_DHT_LOOKUPTAGLOOKUP
#include <dht/taglookup.hpp>
namespace llarp
{
namespace dht
{
struct LocalTagLookup : public TagLookup
{
PathID_t localPath;
LocalTagLookup(const PathID_t &path, uint64_t txid,
const service::Tag &target, AbstractContext *ctx);
void
SendReply() override;
};
} // namespace dht
} // namespace llarp
#endif

@ -25,7 +25,7 @@ namespace llarp
std::vector< std::unique_ptr< IMessage > > &replies) const
{
auto &dht = ctx->impl;
auto crypto = &dht.router->crypto;
auto crypto = dht.router->crypto.get();
for(const auto &introset : I)
{

@ -52,14 +52,18 @@ namespace llarp
return false;
}
auto &dht = ctx->impl;
if(!I.Verify(&dht.router->crypto, now))
if(!I.Verify(dht.router->crypto.get(), now))
{
llarp::LogWarn("invalid introset: ", I);
// don't propogate or store
replies.emplace_back(new GotIntroMessage({}, txID));
return true;
}
if(I.W && !I.W->IsValid(dht.router->crypto.shorthash, now))
using namespace std::placeholders;
shorthash_func shorthash =
std::bind(&Crypto::shorthash, dht.router->crypto.get(), _1, _2);
if(I.W && !I.W->IsValid(shorthash, now))
{
llarp::LogWarn("proof of work not good enough for IntroSet");
// don't propogate or store

@ -41,9 +41,8 @@ namespace llarp
ID.Zero();
}
ISNode(const service::IntroSet& other)
ISNode(const service::IntroSet& other) : introset(other)
{
introset = other;
introset.A.CalculateAddress(ID.as_array());
}

@ -0,0 +1,46 @@
#include <dht/publishservicejob.hpp>
#include <dht/context.hpp>
#include <dht/messages/pubintro.hpp>
namespace llarp
{
namespace dht
{
PublishServiceJob::PublishServiceJob(const TXOwner &asker,
const service::IntroSet &introset,
AbstractContext *ctx, uint64_t s,
const std::set< Key_t > &exclude)
: TX< service::Address, service::IntroSet >(asker, introset.A.Addr(),
ctx)
, S(s)
, dontTell(exclude)
, I(introset)
{
}
bool
PublishServiceJob::Validate(const service::IntroSet &introset) const
{
if(I.A != introset.A)
{
llarp::LogWarn(
"publish introset acknowledgement acked a different service");
return false;
}
return true;
}
void
PublishServiceJob::Start(const TXOwner &peer)
{
std::vector< Key_t > exclude;
for(const auto &router : dontTell)
{
exclude.push_back(router);
}
parent->DHTSendTo(peer.node.as_array(),
new PublishIntroMessage(I, peer.txid, S, exclude));
}
} // namespace dht
} // namespace llarp

@ -0,0 +1,51 @@
#ifndef LLARP_DHT_PUBLISHSERVICEJOB
#define LLARP_DHT_PUBLISHSERVICEJOB
#include <dht/tx.hpp>
#include <dht/txowner.hpp>
#include <service/address.hpp>
#include <service/IntroSet.hpp>
#include <set>
namespace llarp
{
namespace dht
{
struct PublishServiceJob : public TX< service::Address, service::IntroSet >
{
uint64_t S;
std::set< Key_t > dontTell;
service::IntroSet I;
PublishServiceJob(const TXOwner &asker, const service::IntroSet &introset,
AbstractContext *ctx, uint64_t s,
const std::set< Key_t > &exclude);
bool
Validate(const service::IntroSet &introset) const override;
void
Start(const TXOwner &peer) override;
bool
GetNextPeer(Key_t &, const std::set< Key_t > &) override
{
return false;
}
void
DoNextRequest(const Key_t &) override
{
}
void
SendReply() override
{
// don't need this
}
};
} // namespace dht
} // namespace llarp
#endif

@ -0,0 +1,55 @@
#include <dht/recursiverouterlookup.hpp>
#include <dht/context.hpp>
#include <dht/messages/findrouter.hpp>
#include <dht/messages/gotrouter.hpp>
namespace llarp
{
namespace dht
{
RecursiveRouterLookup::RecursiveRouterLookup(const TXOwner &whoasked,
const RouterID &target,
AbstractContext *ctx,
RouterLookupHandler result)
: TX< RouterID, RouterContact >(whoasked, target, ctx)
, resultHandler(result)
{
peersAsked.insert(ctx->OurKey());
}
bool
RecursiveRouterLookup::Validate(const RouterContact &rc) const
{
if(!rc.Verify(parent->Crypto(), parent->Now()))
{
llarp::LogWarn("rc from lookup result is invalid");
return false;
}
return true;
}
void
RecursiveRouterLookup::Start(const TXOwner &peer)
{
parent->DHTSendTo(peer.node.as_array(),
new FindRouterMessage(peer.txid, target));
}
void
RecursiveRouterLookup::SendReply()
{
if(resultHandler)
{
resultHandler(valuesFound);
}
else
{
parent->DHTSendTo(
whoasked.node.as_array(),
new GotRouterMessage({}, whoasked.txid, valuesFound, false));
}
}
} // namespace dht
} // namespace llarp

@ -0,0 +1,42 @@
#ifndef LLARP_DHT_RECURSIVEROUTERLOOKUP
#define LLARP_DHT_RECURSIVEROUTERLOOKUP
#include <dht/tx.hpp>
#include <router_contact.hpp>
#include <router_id.hpp>
namespace llarp
{
namespace dht
{
struct RecursiveRouterLookup : public TX< RouterID, RouterContact >
{
RouterLookupHandler resultHandler;
RecursiveRouterLookup(const TXOwner &whoasked, const RouterID &target,
AbstractContext *ctx, RouterLookupHandler result);
bool
Validate(const RouterContact &rc) const override;
bool
GetNextPeer(Key_t &, const std::set< Key_t > &) override
{
return false;
}
void
DoNextRequest(const Key_t &) override
{
}
void
Start(const TXOwner &peer) override;
virtual void
SendReply() override;
};
} // namespace dht
} // namespace llarp
#endif

@ -0,0 +1,79 @@
#include <dht/serviceaddresslookup.hpp>
#include <dht/context.hpp>
#include <dht/messages/findintro.hpp>
#include <dht/messages/gotintro.hpp>
namespace llarp
{
namespace dht
{
ServiceAddressLookup::ServiceAddressLookup(
const TXOwner &asker, const service::Address &addr, AbstractContext *ctx,
uint64_t r, service::IntroSetLookupHandler handler)
: TX< service::Address, service::IntroSet >(asker, addr, ctx)
, handleResult(handler)
, R(r)
{
peersAsked.insert(ctx->OurKey());
}
bool
ServiceAddressLookup::Validate(const service::IntroSet &value) const
{
if(!value.Verify(parent->Crypto(), parent->Now()))
{
llarp::LogWarn("Got invalid introset from service lookup");
return false;
}
if(value.A.Addr() != target)
{
llarp::LogWarn("got introset with wrong target from service lookup");
return false;
}
return true;
}
bool
ServiceAddressLookup::GetNextPeer(Key_t &next,
const std::set< Key_t > &exclude)
{
Key_t k = target.ToKey();
return parent->Nodes()->FindCloseExcluding(k, next, exclude);
}
void
ServiceAddressLookup::Start(const TXOwner &peer)
{
parent->DHTSendTo(peer.node.as_array(),
new FindIntroMessage(peer.txid, target, R));
}
void
ServiceAddressLookup::DoNextRequest(const Key_t &ask)
{
if(R)
{
parent->LookupIntroSetRecursive(target, whoasked.node, whoasked.txid,
ask, R - 1);
}
else
{
parent->LookupIntroSetIterative(target, whoasked.node, whoasked.txid,
ask);
}
}
void
ServiceAddressLookup::SendReply()
{
if(handleResult)
{
handleResult(valuesFound);
}
parent->DHTSendTo(whoasked.node.as_array(),
new GotIntroMessage(valuesFound, whoasked.txid));
}
} // namespace dht
} // namespace llarp

@ -0,0 +1,44 @@
#ifndef LLARP_DHT_SERVICEADDRESSLOOKUP
#define LLARP_DHT_SERVICEADDRESSLOOKUP
#include <dht/key.hpp>
#include <dht/tx.hpp>
#include <service/address.hpp>
#include <service/IntroSet.hpp>
namespace llarp
{
namespace dht
{
struct TXOwner;
struct ServiceAddressLookup
: public TX< service::Address, service::IntroSet >
{
service::IntroSetLookupHandler handleResult;
uint64_t R;
ServiceAddressLookup(const TXOwner &asker, const service::Address &addr,
AbstractContext *ctx, uint64_t r,
service::IntroSetLookupHandler handler);
bool
Validate(const service::IntroSet &value) const override;
bool
GetNextPeer(Key_t &next, const std::set< Key_t > &exclude) override;
void
Start(const TXOwner &peer) override;
void
DoNextRequest(const Key_t &ask) override;
virtual void
SendReply() override;
};
} // namespace dht
} // namespace llarp
#endif

@ -0,0 +1,59 @@
#include <dht/taglookup.hpp>
#include <dht/context.hpp>
#include <dht/messages/gotintro.hpp>
namespace llarp
{
namespace dht
{
bool
TagLookup::Validate(const service::IntroSet &introset) const
{
if(!introset.Verify(parent->Crypto(), parent->Now()))
{
llarp::LogWarn("got invalid introset from tag lookup");
return false;
}
if(introset.topic != target)
{
llarp::LogWarn("got introset with missmatched topic in tag lookup");
return false;
}
return true;
}
void
TagLookup::Start(const TXOwner &peer)
{
parent->DHTSendTo(peer.node.as_array(),
new FindIntroMessage(target, peer.txid, R));
}
void
TagLookup::SendReply()
{
std::set< service::IntroSet > found;
for(const auto &remoteTag : valuesFound)
{
found.insert(remoteTag);
}
// collect our local values if we haven't hit a limit
if(found.size() < 2)
{
for(const auto &localTag :
parent->FindRandomIntroSetsWithTagExcluding(target, 1, found))
{
found.insert(localTag);
}
}
std::vector< service::IntroSet > values;
for(const auto &introset : found)
{
values.push_back(introset);
}
parent->DHTSendTo(whoasked.node.as_array(),
new GotIntroMessage(values, whoasked.txid));
}
} // namespace dht
} // namespace llarp

@ -0,0 +1,44 @@
#ifndef LLARP_DHT_TAGLOOKUP
#define LLARP_DHT_TAGLOOKUP
#include <dht/tx.hpp>
#include <service/IntroSet.hpp>
#include <service/tag.hpp>
namespace llarp
{
namespace dht
{
struct TagLookup : public TX< service::Tag, service::IntroSet >
{
uint64_t R;
TagLookup(const TXOwner &asker, const service::Tag &tag, AbstractContext *ctx,
uint64_t r)
: TX< service::Tag, service::IntroSet >(asker, tag, ctx), R(r)
{
}
bool
Validate(const service::IntroSet &introset) const override;
void
Start(const TXOwner &peer) override;
bool
GetNextPeer(Key_t &, const std::set< Key_t > &) override
{
return false;
}
void
DoNextRequest(const Key_t &) override
{
}
void
SendReply() override;
};
} // namespace dht
} // namespace llarp
#endif

@ -0,0 +1 @@
#include <dht/tx.hpp>

@ -0,0 +1,104 @@
#ifndef LLARP_DHT_TX
#define LLARP_DHT_TX
#include <dht/key.hpp>
#include <dht/txowner.hpp>
#include <util/logger.hpp>
#include <set>
#include <vector>
namespace llarp
{
namespace dht
{
struct AbstractContext;
template < typename K, typename V >
struct TX
{
K target;
AbstractContext* parent;
std::set< Key_t > peersAsked;
std::vector< V > valuesFound;
TXOwner whoasked;
TX(const TXOwner& asker, const K& k, AbstractContext* p)
: target(k), parent(p), whoasked(asker)
{
}
virtual ~TX(){};
void
OnFound(const Key_t& askedPeer, const V& value);
/// return true if we want to persist this tx
bool
AskNextPeer(const Key_t& prevPeer, const std::unique_ptr< Key_t >& next);
virtual bool
Validate(const V& value) const = 0;
virtual void
Start(const TXOwner& peer) = 0;
virtual bool
GetNextPeer(Key_t& next, const std::set< Key_t >& exclude) = 0;
virtual void
DoNextRequest(const Key_t& peer) = 0;
virtual void
SendReply() = 0;
};
template < typename K, typename V >
inline void
TX< K, V >::OnFound(const Key_t& askedPeer, const V& value)
{
peersAsked.insert(askedPeer);
if(Validate(value))
{
valuesFound.push_back(value);
}
}
template < typename K, typename V >
inline bool
TX< K, V >::AskNextPeer(const Key_t& prevPeer,
const std::unique_ptr< Key_t >& next)
{
peersAsked.insert(prevPeer);
Key_t peer;
if(next)
{
// explicit next peer provided
peer = *next;
}
else if(!GetNextPeer(peer, peersAsked))
{
// no more peers
llarp::LogInfo("no more peers for request asking for ", target);
return false;
}
const Key_t targetKey{target};
if((prevPeer ^ targetKey) < (peer ^ targetKey))
{
// next peer is not closer
llarp::LogInfo("next peer ", peer, " is not closer to ", target,
" than ", prevPeer);
return false;
}
else
{
peersAsked.insert(peer);
}
DoNextRequest(peer);
return true;
}
} // namespace dht
} // namespace llarp
#endif

@ -0,0 +1 @@
#include <dht/txholder.hpp>

@ -0,0 +1,190 @@
#ifndef LLARP_DHT_TXHOLDER
#define LLARP_DHT_TXHOLDER
#include <dht/tx.hpp>
#include <dht/txowner.hpp>
#include <util/logger.hpp>
#include <util/time.hpp>
#include <memory>
#include <unordered_map>
namespace llarp
{
namespace dht
{
template < typename K, typename V, typename K_Hash,
llarp_time_t requestTimeoutMS = 5000UL >
struct TXHolder
{
using TXPtr = std::unique_ptr< TX< K, V > >;
// tx who are waiting for a reply for each key
std::unordered_multimap< K, TXOwner, K_Hash > waiting;
// tx timesouts by key
std::unordered_map< K, llarp_time_t, K_Hash > timeouts;
// maps remote peer with tx to handle reply from them
std::unordered_map< TXOwner, TXPtr, TXOwner::Hash > tx;
const TX< K, V >*
GetPendingLookupFrom(const TXOwner& owner) const;
bool
HasLookupFor(const K& target) const
{
return timeouts.find(target) != timeouts.end();
}
bool
HasPendingLookupFrom(const TXOwner& owner) const
{
return GetPendingLookupFrom(owner) != nullptr;
}
void
NewTX(const TXOwner& askpeer, const TXOwner& whoasked, const K& k,
TX< K, V >* t);
/// mark tx as not fond
void
NotFound(const TXOwner& from, const std::unique_ptr< Key_t >& next);
void
Found(const TXOwner& from, const K& k, const std::vector< V >& values)
{
Inform(from, k, values, true);
}
/// inform all watches for key of values found
void
Inform(TXOwner from, K key, std::vector< V > values,
bool sendreply = false, bool removeTimeouts = true);
void
Expire(llarp_time_t now);
};
template < typename K, typename V, typename K_Hash,
llarp_time_t requestTimeoutMS >
const TX< K, V >*
TXHolder< K, V, K_Hash, requestTimeoutMS >::GetPendingLookupFrom(
const TXOwner& owner) const
{
auto itr = tx.find(owner);
if(itr == tx.end())
{
return nullptr;
}
else
{
return itr->second.get();
}
}
template < typename K, typename V, typename K_Hash,
llarp_time_t requestTimeoutMS >
void
TXHolder< K, V, K_Hash, requestTimeoutMS >::NewTX(const TXOwner& askpeer,
const TXOwner& whoasked,
const K& k, TX< K, V >* t)
{
(void)whoasked;
tx.emplace(askpeer, std::unique_ptr< TX< K, V > >(t));
auto count = waiting.count(k);
waiting.emplace(k, askpeer);
auto itr = timeouts.find(k);
if(itr == timeouts.end())
{
timeouts.emplace(k, time_now_ms() + requestTimeoutMS);
}
if(count == 0)
{
t->Start(askpeer);
}
}
template < typename K, typename V, typename K_Hash,
llarp_time_t requestTimeoutMS >
void
TXHolder< K, V, K_Hash, requestTimeoutMS >::NotFound(
const TXOwner& from, const std::unique_ptr< Key_t >& next)
{
bool sendReply = true;
auto txitr = tx.find(from);
if(txitr == tx.end())
{
return;
}
// ask for next peer
if(txitr->second->AskNextPeer(from.node, next))
{
sendReply = false;
}
llarp::LogWarn("Target key ", txitr->second->target);
Inform(from, txitr->second->target, {}, sendReply, sendReply);
}
template < typename K, typename V, typename K_Hash,
llarp_time_t requestTimeoutMS >
void
TXHolder< K, V, K_Hash, requestTimeoutMS >::Inform(TXOwner from, K key,
std::vector< V > values,
bool sendreply,
bool removeTimeouts)
{
auto range = waiting.equal_range(key);
auto itr = range.first;
while(itr != range.second)
{
auto txitr = tx.find(itr->second);
if(txitr != tx.end())
{
for(const auto& value : values)
{
txitr->second->OnFound(from.node, value);
}
if(sendreply)
{
txitr->second->SendReply();
tx.erase(txitr);
}
}
++itr;
}
if(sendreply)
{
waiting.erase(key);
}
if(removeTimeouts)
{
timeouts.erase(key);
}
}
template < typename K, typename V, typename K_Hash,
llarp_time_t requestTimeoutMS >
void
TXHolder< K, V, K_Hash, requestTimeoutMS >::Expire(llarp_time_t now)
{
auto itr = timeouts.begin();
while(itr != timeouts.end())
{
if(now > itr->second && now - itr->second >= requestTimeoutMS)
{
Inform(TXOwner{}, itr->first, {}, true, false);
itr = timeouts.erase(itr);
}
else
{
++itr;
}
}
}
} // namespace dht
} // namespace llarp
#endif

@ -7,6 +7,7 @@ namespace llarp
{
namespace dns
{
constexpr uint16_t qTypeAAAA = 28;
constexpr uint16_t qTypeTXT = 16;
constexpr uint16_t qTypeMX = 15;
constexpr uint16_t qTypePTR = 12;

@ -247,14 +247,17 @@ namespace llarp
{
hdr_fields |= (1 << 15) | (1 << 3);
const auto& question = questions[0];
answers.emplace_back();
auto& nx = answers.back();
nx.rr_name = question.qname;
nx.rr_type = question.qtype;
nx.rr_class = question.qclass;
nx.ttl = ttl;
nx.rData.resize(1);
nx.rData.data()[0] = 0;
if(question.qtype != qTypeAAAA)
{
answers.emplace_back();
auto& nx = answers.back();
nx.rr_name = question.qname;
nx.rr_type = question.qtype;
nx.rr_class = question.qclass;
nx.ttl = ttl;
nx.rData.resize(1);
nx.rData.data()[0] = 0;
}
}
}

@ -74,6 +74,7 @@ llarp_ev_loop_run_single_process(struct llarp_ev_loop *ev,
ev->tick(EV_TICK_INTERVAL);
if(ev->running())
{
ev->_now = llarp::time_now_ms();
logic->tick_async(ev->_now);
llarp_threadpool_tick(tp);
}
@ -148,10 +149,11 @@ llarp_ev_udp_sendto(struct llarp_udp_io *udp, const sockaddr *to,
bool
llarp_ev_add_tun(struct llarp_ev_loop *loop, struct llarp_tun_io *tun)
{
if(strcmp(tun->ifaddr, "") == 0 || strcmp(tun->ifaddr, "auto"))
// llarp::LogInfo("ev creating tunnel ", tun->ifaddr, " on ", tun->ifname);
if(strcmp(tun->ifaddr, "") == 0 || strcmp(tun->ifaddr, "auto") == 0)
{
std::string ifaddr = llarp::findFreePrivateRange();
auto pos = ifaddr.find("/");
auto pos = ifaddr.find("/");
if(pos == std::string::npos)
{
llarp::LogWarn("Auto ifaddr didn't return a netmask: ", ifaddr);
@ -169,15 +171,17 @@ llarp_ev_add_tun(struct llarp_ev_loop *loop, struct llarp_tun_io *tun)
llarp::LogError("bad ifaddr netmask value: ", ifaddr);
return false;
}
tun->netmask = num;
tun->netmask = num;
const std::string addr = ifaddr.substr(0, pos);
std::copy_n(addr.begin(), std::min(sizeof(tun->ifaddr), addr.size()), tun->ifaddr);
std::copy_n(addr.begin(), std::min(sizeof(tun->ifaddr), addr.size()),
tun->ifaddr);
llarp::LogInfo("IfAddr autodetect: ", tun->ifaddr, "/", tun->netmask);
}
if(strcmp(tun->ifname, "") == 0 || strcmp(tun->ifname, "auto"))
if(strcmp(tun->ifname, "") == 0 || strcmp(tun->ifname, "auto") == 0)
{
std::string ifname = llarp::findFreeLokiTunIfName();
std::copy_n(ifname.begin(), std::min(sizeof(tun->ifname), ifname.size()), tun->ifname);
std::copy_n(ifname.begin(), std::min(sizeof(tun->ifname), ifname.size()),
tun->ifname);
llarp::LogInfo("IfName autodetect: ", tun->ifname);
}
llarp::LogDebug("Tun Interface will use the following settings:");

@ -672,11 +672,20 @@ struct llarp_ev_loop
{
byte_t readbuf[EV_READ_BUF_SZ] = {0};
llarp_time_t _now = 0;
virtual bool
init() = 0;
virtual int
run() = 0;
virtual bool
running() const = 0;
/// return false on socket error (non blocking)
virtual bool
tcp_connect(llarp_tcp_connecter* tcp, const sockaddr* addr) = 0;
virtual int
tick(int ms) = 0;
@ -701,17 +710,10 @@ struct llarp_ev_loop
virtual llarp::ev_io*
bind_tcp(llarp_tcp_acceptor* tcp, const sockaddr* addr) = 0;
/// return false on socket error (non blocking)
virtual bool
tcp_connect(llarp_tcp_connecter* tcp, const sockaddr* addr) = 0;
/// register event listener
virtual bool
add_ev(llarp::ev_io* ev, bool write) = 0;
virtual bool
running() const = 0;
virtual ~llarp_ev_loop(){};
std::list< std::unique_ptr< llarp::ev_io > > handlers;

@ -374,13 +374,13 @@ llarp_kqueue_loop::tick(int ms)
llarp::ev_io* ev = static_cast< llarp::ev_io* >(events[idx].udata);
if(ev)
{
if(events[idx].filter & EVFILT_READ)
ev->read(readbuf,
std::min(sizeof(readbuf), size_t(events[idx].data)));
if(events[idx].filter & EVFILT_WRITE)
{
ev->flush_write_buffers(events[idx].data);
}
if(events[idx].filter & EVFILT_READ)
ev->read(readbuf,
std::min(sizeof(readbuf), size_t(events[idx].data)));
}
++idx;
}
@ -580,4 +580,4 @@ llarp_kqueue_loop::stop()
::close(kqueuefd);
kqueuefd = -1;
}
}

@ -77,7 +77,7 @@ namespace llarp
} // namespace llarp
struct llarp_kqueue_loop : public llarp_ev_loop
struct llarp_kqueue_loop final : public llarp_ev_loop
{
int kqueuefd;
@ -85,51 +85,51 @@ struct llarp_kqueue_loop : public llarp_ev_loop
{
}
llarp::ev_io*
bind_tcp(llarp_tcp_acceptor* tcp, const sockaddr* bindaddr);
~llarp_kqueue_loop()
virtual ~llarp_kqueue_loop()
{
}
llarp::ev_io*
create_tun(llarp_tun_io* tun);
bool
init();
init() override;
bool
running() const;
int
run() override;
bool
tcp_connect(llarp_tcp_connecter* tcp, const sockaddr* addr);
running() const override;
int
tick(int ms);
bool
tcp_connect(llarp_tcp_connecter* tcp, const sockaddr* addr) override;
int
run();
tick(int ms) override;
int
udp_bind(const sockaddr* addr);
virtual bool
udp_listen(llarp_udp_io* l, const sockaddr* src);
udp_listen(llarp_udp_io* l, const sockaddr* src) override;
bool
close_ev(llarp::ev_io* ev);
close_ev(llarp::ev_io* ev) override;
llarp::ev_io*
create_tun(llarp_tun_io* tun) override;
llarp::ev_io*
bind_tcp(llarp_tcp_acceptor* tcp, const sockaddr* bindaddr) override;
llarp::ev_io*
create_udp(llarp_udp_io* l, const sockaddr* src);
create_udp(llarp_udp_io* l, const sockaddr* src) override;
bool
add_ev(llarp::ev_io* ev, bool w);
add_ev(llarp::ev_io* ev, bool w) override;
bool
udp_close(llarp_udp_io* l);
udp_close(llarp_udp_io* l) override;
void
stop();
stop() override;
};
#endif

@ -16,7 +16,7 @@ namespace llarp
, m_Counter(0)
, m_LastUse(0)
{
r->crypto.identity_keygen(m_ExitIdentity);
r->crypto->identity_keygen(m_ExitIdentity);
}
BaseSession::~BaseSession()
@ -78,7 +78,7 @@ namespace llarp
obtain.S = p->NextSeqNo();
obtain.T = llarp::randint();
PopulateRequest(obtain);
if(!obtain.Sign(&router->crypto, m_ExitIdentity))
if(!obtain.Sign(router->crypto.get(), m_ExitIdentity))
{
llarp::LogError("Failed to sign exit request");
return;
@ -107,7 +107,7 @@ namespace llarp
{
llarp::LogInfo(p->Name(), " closing exit path");
llarp::routing::CloseExitMessage msg;
if(!(msg.Sign(&router->crypto, m_ExitIdentity)
if(!(msg.Sign(router->crypto.get(), m_ExitIdentity)
&& p->SendExitClose(&msg, router)))
llarp::LogWarn(p->Name(), " failed to send exit close message");
}

@ -246,7 +246,7 @@ namespace llarp
Crypto *
ExitEndpoint::GetCrypto()
{
return &m_Router->crypto;
return m_Router->crypto.get();
}
huint32_t

@ -9,8 +9,9 @@ namespace llarp
{
struct NullEndpoint final : public llarp::service::Endpoint
{
NullEndpoint(const std::string &name, llarp::Router *r)
: llarp::service::Endpoint(name, r){};
NullEndpoint(const std::string &name, llarp::Router *r,
llarp::service::Context *parent)
: llarp::service::Endpoint(name, r, parent){};
bool HandleWriteIPPacket(llarp_buffer_t,
std::function< huint32_t(void) >) override

@ -29,8 +29,9 @@ namespace llarp
self->Flush();
}
TunEndpoint::TunEndpoint(const std::string &nickname, llarp::Router *r)
: service::Endpoint(nickname, r)
TunEndpoint::TunEndpoint(const std::string &nickname, llarp::Router *r,
service::Context *parent)
: service::Endpoint(nickname, r, parent)
, m_UserToNetworkPktQueue(nickname + "_sendq", r->netloop, r->netloop)
, m_NetworkToUserPktQueue(nickname + "_recvq", r->netloop, r->netloop)
, m_Resolver(r->netloop, this)
@ -204,10 +205,7 @@ namespace llarp
return false;
}
std::string qname = msg.questions[0].qname;
if(msg.questions[0].qtype == dns::qTypeCNAME)
{
}
else if(msg.questions[0].qtype == dns::qTypeMX)
if(msg.questions[0].qtype == dns::qTypeMX)
{
// mx record
llarp::service::Address addr;
@ -228,19 +226,44 @@ namespace llarp
else
msg.AddNXReply();
}
else if(msg.questions[0].qname == "localhost.loki"
|| msg.questions[0].qname == "localhost.loki.")
{
size_t counter = 0;
context->ForEachService(
[&](const std::string &,
const std::unique_ptr< service::Endpoint > &service) -> bool {
service::Address addr = service->GetIdentity().pub.Addr();
msg.AddCNAMEReply(addr.ToString(), 1);
++counter;
return true;
});
if(counter == 0)
msg.AddNXReply();
}
else
msg.AddNXReply();
}
else if(msg.questions[0].qtype == dns::qTypeA)
{
// forward dns
llarp::service::Address addr;
if(qname == "random.snode" || qname == "random.snode.")
// forward dns
if(msg.questions[0].qname == "localhost.loki"
|| msg.questions[0].qname == "localhost.loki.")
{
RouterID random;
if(Router()->GetRandomGoodRouter(random))
msg.AddCNAMEReply(random.ToString(), 1);
else
size_t counter = 0;
context->ForEachService(
[&](const std::string &,
const std::unique_ptr< service::Endpoint > &service) -> bool {
if(service->HasIfAddr())
{
huint32_t ip = service->GetIfAddr();
msg.AddINReply(ip);
++counter;
}
return true;
});
if(counter == 0)
msg.AddNXReply();
}
else if(addr.FromString(qname, ".loki"))
@ -317,14 +340,18 @@ namespace llarp
// always hook mx records
if(msg.questions[0].qtype == llarp::dns::qTypeMX)
return true;
// always hook random.snode
// hook random.snode for CNAME
if(msg.questions[0].qname == "random.snode"
|| msg.questions[0].qname == "random.snode.")
return true;
// always hook .loki
// hook localhost.loki
if(msg.questions[0].qname == "localhost.loki"
|| msg.questions[0].qname == "localhost.loki.")
return true;
// hook .loki A records
if(addr.FromString(msg.questions[0].qname, ".loki"))
return true;
// always hook .snode
// hook .snode A records
if(addr.FromString(msg.questions[0].qname, ".snode"))
return true;
// hook any ranges we own
@ -476,8 +503,9 @@ namespace llarp
}
if(!m_Resolver.Start(m_LocalResolverAddr, m_UpstreamResolvers))
{
llarp::LogError(Name(), " failed to start dns server");
return false;
// downgrade DNS server failure to a warning
llarp::LogWarn(Name(), " failed to start dns server");
// return false;
}
return true;
}

@ -20,7 +20,8 @@ namespace llarp
struct TunEndpoint : public service::Endpoint, public dns::IQueryHandler
{
TunEndpoint(const std::string& nickname, llarp::Router* r);
TunEndpoint(const std::string& nickname, llarp::Router* r,
llarp::service::Context* parent);
~TunEndpoint();
virtual bool
@ -70,9 +71,16 @@ namespace llarp
bool
QueueOutboundTraffic(llarp::net::IPv4Packet&& pkt);
/// we have a resolvable ip address
bool
HasIfAddr() const override
{
return true;
}
/// get the local interface's address
huint32_t
GetIfAddr() const;
GetIfAddr() const override;
bool
HasLocalIP(const huint32_t& ip) const;

@ -28,6 +28,8 @@ namespace llarp
{
namespace utp
{
using namespace std::placeholders;
bool
InboundMessage::IsExpired(llarp_time_t now) const
{
@ -549,7 +551,7 @@ namespace llarp
NewServerFromRouter(llarp::Router* r)
{
return NewServer(
&r->crypto, r->encryption, std::bind(&llarp::Router::rc, r),
r->crypto.get(), r->encryption, std::bind(&llarp::Router::rc, r),
std::bind(&llarp::Router::HandleRecvLinkMessageBuffer, r,
std::placeholders::_1, std::placeholders::_2),
std::bind(&llarp::Router::OnSessionEstablished, r,
@ -665,8 +667,10 @@ namespace llarp
remoteRC = msg->rc;
Crypto()->shorthash(txKey, remoteRC.pubkey.as_buffer());
if(!DoKeyExchange(Crypto()->transport_dh_server, rxKey, msg->N,
remoteRC.enckey, parent->TransportSecretKey()))
if(!DoKeyExchange(std::bind(&Crypto::transport_dh_server, Crypto(), _1,
_2, _3, _4),
rxKey, msg->N, remoteRC.enckey,
parent->TransportSecretKey()))
return false;
byte_t tmp[LinkIntroMessage::MaxSize];
@ -706,8 +710,10 @@ namespace llarp
Close();
return false;
}
if(!DoKeyExchange(Crypto()->transport_dh_client, txKey, replymsg.N,
remoteRC.enckey, parent->RouterEncryptionSecret()))
if(!DoKeyExchange(std::bind(&Crypto::transport_dh_client, Crypto(), _1,
_2, _3, _4),
txKey, replymsg.N, remoteRC.enckey,
parent->RouterEncryptionSecret()))
return false;
llarp::LogDebug("Sent reply LIM");
@ -761,8 +767,10 @@ namespace llarp
}
remoteRC = msg->rc;
gotLIM = true;
if(!DoKeyExchange(Crypto()->transport_dh_server, rxKey, msg->N,
remoteRC.enckey, parent->RouterEncryptionSecret()))
if(!DoKeyExchange(
std::bind(&Crypto::transport_dh_server, Crypto(), _1, _2, _3, _4),
rxKey, msg->N, remoteRC.enckey, parent->RouterEncryptionSecret()))
{
Close();
return false;
@ -814,9 +822,11 @@ namespace llarp
Close();
return;
}
if(!DoKeyExchange(Crypto()->transport_dh_client, txKey, msg.N,
remoteTransportPubKey,
parent->RouterEncryptionSecret()))
if(!DoKeyExchange(
std::bind(&Crypto::transport_dh_client, Crypto(), _1, _2, _3, _4),
txKey, msg.N, remoteTransportPubKey,
parent->RouterEncryptionSecret()))
{
llarp::LogError("failed to mix keys for outbound session to ",
remoteAddr);
@ -988,8 +998,9 @@ namespace llarp
// set remote rc
remoteRC = msg->rc;
// recalcuate rx key
return DoKeyExchange(Crypto()->transport_dh_server, rxKey, msg->N,
remoteRC.enckey, parent->RouterEncryptionSecret());
return DoKeyExchange(
std::bind(&Crypto::transport_dh_server, Crypto(), _1, _2, _3, _4),
rxKey, msg->N, remoteRC.enckey, parent->RouterEncryptionSecret());
}
bool
@ -1012,8 +1023,9 @@ namespace llarp
if(!SendMessageBuffer(buf))
return false;
// regen our tx Key
return DoKeyExchange(Crypto()->transport_dh_client, txKey, lim.N,
remoteRC.enckey, parent->RouterEncryptionSecret());
return DoKeyExchange(
std::bind(&Crypto::transport_dh_client, Crypto(), _1, _2, _3, _4),
txKey, lim.N, remoteRC.enckey, parent->RouterEncryptionSecret());
}
bool
@ -1080,7 +1092,9 @@ namespace llarp
// get message
if(m_RecvMsgs.find(msgid) == m_RecvMsgs.end())
{
m_RecvMsgs.emplace(msgid, InboundMessage{});
}
auto itr = m_RecvMsgs.find(msgid);
// add message activity

@ -117,7 +117,7 @@ namespace llarp
bool
LinkIntroMessage::HandleMessage(llarp::Router* router) const
{
if(!Verify(&router->crypto))
if(!Verify(router->crypto.get()))
return false;
return session->GotLIM(this);
}

@ -269,19 +269,21 @@ namespace llarp
return;
}
// generate path key as we are in a worker thread
auto DH = self->context->Crypto()->dh_server;
if(!DH(self->hop->pathKey, self->record.commkey,
self->context->EncryptionSecretKey(), self->record.tunnelNonce))
auto crypto = self->context->Crypto();
if(!crypto->dh_server(self->hop->pathKey, self->record.commkey,
self->context->EncryptionSecretKey(),
self->record.tunnelNonce))
{
llarp::LogError("LRCM DH Failed ", info);
delete self;
return;
}
// generate hash of hop key for nonce mutation
self->context->Crypto()->shorthash(self->hop->nonceXOR,
self->hop->pathKey.as_buffer());
crypto->shorthash(self->hop->nonceXOR, self->hop->pathKey.as_buffer());
using namespace std::placeholders;
if(self->record.work
&& self->record.work->IsValid(self->context->Crypto()->shorthash, now))
&& self->record.work->IsValid(
std::bind(&Crypto::shorthash, crypto, _1, _2), now))
{
llarp::LogDebug("LRCM extended lifetime by ",
self->record.work->extendedLifetime, " seconds for ",

@ -23,9 +23,9 @@ namespace llarp
struct in6_addr netmask;
PubKey pubkey;
ExitInfo(const PubKey &pk, const nuint32_t &ipv4_exit) : IBEncodeMessage()
ExitInfo(const PubKey &pk, const nuint32_t &ipv4_exit) : IBEncodeMessage(),
pubkey(pk)
{
pubkey = pk;
memset(address.s6_addr, 0, 16);
address.s6_addr[11] = 0xff;
address.s6_addr[10] = 0xff;
@ -37,12 +37,11 @@ namespace llarp
{
}
ExitInfo(const ExitInfo &other) : IBEncodeMessage()
ExitInfo(const ExitInfo &other) : IBEncodeMessage(other.version),
pubkey(other.pubkey)
{
pubkey = other.pubkey;
memcpy(address.s6_addr, other.address.s6_addr, 16);
memcpy(netmask.s6_addr, other.netmask.s6_addr, 16);
version = other.version;
}
~ExitInfo();

@ -43,7 +43,7 @@ namespace llarp
llarp::Crypto*
PathContext::Crypto()
{
return &m_Router->crypto;
return m_Router->crypto.get();
}
llarp::Logic*
@ -155,24 +155,26 @@ namespace llarp
IHopHandler*
PathContext::GetByUpstream(const RouterID& remote, const PathID_t& id)
{
auto own = MapGet(m_OurPaths, id,
[](__attribute__((unused)) const PathSet* s) -> bool {
// TODO: is this right?
return true;
},
[remote, id](PathSet* p) -> IHopHandler* {
return p->GetByUpstream(remote, id);
});
auto own = MapGet(
m_OurPaths, id,
[](__attribute__((unused)) const PathSet* s) -> bool {
// TODO: is this right?
return true;
},
[remote, id](PathSet* p) -> IHopHandler* {
return p->GetByUpstream(remote, id);
});
if(own)
return own;
return MapGet(m_TransitPaths, id,
[remote](const std::shared_ptr< TransitHop >& hop) -> bool {
return hop->info.upstream == remote;
},
[](const std::shared_ptr< TransitHop >& h) -> IHopHandler* {
return h.get();
});
return MapGet(
m_TransitPaths, id,
[remote](const std::shared_ptr< TransitHop >& hop) -> bool {
return hop->info.upstream == remote;
},
[](const std::shared_ptr< TransitHop >& h) -> IHopHandler* {
return h.get();
});
}
bool
@ -189,13 +191,14 @@ namespace llarp
IHopHandler*
PathContext::GetByDownstream(const RouterID& remote, const PathID_t& id)
{
return MapGet(m_TransitPaths, id,
[remote](const std::shared_ptr< TransitHop >& hop) -> bool {
return hop->info.downstream == remote;
},
[](const std::shared_ptr< TransitHop >& h) -> IHopHandler* {
return h.get();
});
return MapGet(
m_TransitPaths, id,
[remote](const std::shared_ptr< TransitHop >& hop) -> bool {
return hop->info.downstream == remote;
},
[](const std::shared_ptr< TransitHop >& h) -> IHopHandler* {
return h.get();
});
}
PathSet*
@ -506,7 +509,7 @@ namespace llarp
TunnelNonce n = Y;
for(const auto& hop : hops)
{
r->crypto.xchacha20(buf, hop.shared, n);
r->crypto->xchacha20(buf, hop.shared, n);
n ^= hop.nonceXOR;
}
RelayUpstreamMessage msg;
@ -546,7 +549,7 @@ namespace llarp
for(const auto& hop : hops)
{
n ^= hop.nonceXOR;
r->crypto.xchacha20(buf, hop.shared, n);
r->crypto->xchacha20(buf, hop.shared, n);
}
return HandleRoutingMessage(buf, r);
}
@ -605,7 +608,7 @@ namespace llarp
if(buf.sz < MESSAGE_PAD_SIZE)
{
// randomize padding
r->crypto.randbytes(buf.cur, MESSAGE_PAD_SIZE - buf.sz);
r->crypto->randbytes(buf.cur, MESSAGE_PAD_SIZE - buf.sz);
buf.sz = MESSAGE_PAD_SIZE;
}
buf.cur = buf.base;
@ -716,7 +719,7 @@ namespace llarp
/// allows exits to close from their end
if(SupportsAnyRoles(ePathRoleExit | ePathRoleSVC))
{
if(msg->Verify(&r->crypto, EndpointPubKey()))
if(msg->Verify(r->crypto.get(), EndpointPubKey()))
{
llarp::LogInfo(Name(), " had its exit closed");
_role &= ~ePathRoleExit;
@ -775,7 +778,7 @@ namespace llarp
{
if(m_ExitObtainTX && msg->T == m_ExitObtainTX)
{
if(!msg->Verify(&r->crypto, EndpointPubKey()))
if(!msg->Verify(r->crypto.get(), EndpointPubKey()))
{
llarp::LogError(Name(), "RXM invalid signature");
return false;
@ -794,7 +797,7 @@ namespace llarp
{
if(m_ExitObtainTX && msg->T == m_ExitObtainTX)
{
if(!msg->Verify(&r->crypto, EndpointPubKey()))
if(!msg->Verify(r->crypto.get(), EndpointPubKey()))
{
llarp::LogError(Name(), " GXM signature failed");
return false;

@ -167,7 +167,7 @@ namespace llarp
, numHops(hops)
{
p_router->paths.AddPathBuilder(this);
p_router->crypto.encryption_keygen(enckey);
p_router->crypto->encryption_keygen(enckey);
_run.store(true);
keygens.store(0);
}
@ -281,7 +281,7 @@ namespace llarp
lastBuild = Now();
// async generate keys
AsyncPathKeyExchangeContext< Builder >* ctx =
new AsyncPathKeyExchangeContext< Builder >(&router->crypto);
new AsyncPathKeyExchangeContext< Builder >(router->crypto.get());
ctx->router = router;
ctx->pathset = this;
auto path = new llarp::path::Path(hops, this, roles);

@ -75,7 +75,7 @@ namespace llarp
{
dlt = MESSAGE_PAD_SIZE - dlt;
// randomize padding
r->crypto.randbytes(buf.cur, dlt);
r->crypto->randbytes(buf.cur, dlt);
buf.sz += dlt;
}
buf.cur = buf.base;
@ -89,7 +89,7 @@ namespace llarp
RelayDownstreamMessage msg;
msg.pathid = info.rxID;
msg.Y = Y ^ nonceXOR;
r->crypto.xchacha20(buf, pathKey, Y);
r->crypto->xchacha20(buf, pathKey, Y);
msg.X = buf;
llarp::LogDebug("relay ", msg.X.size(), " bytes downstream from ",
info.upstream, " to ", info.downstream);
@ -100,7 +100,7 @@ namespace llarp
TransitHop::HandleUpstream(llarp_buffer_t buf, const TunnelNonce& Y,
llarp::Router* r)
{
r->crypto.xchacha20(buf, pathKey, Y);
r->crypto->xchacha20(buf, pathKey, Y);
if(IsEndpoint(r->pubkey()))
{
m_LastActivity = r->Now();
@ -157,13 +157,13 @@ namespace llarp
TransitHop::HandleObtainExitMessage(
const llarp::routing::ObtainExitMessage* msg, llarp::Router* r)
{
if(msg->Verify(&r->crypto)
if(msg->Verify(r->crypto.get())
&& r->exitContext.ObtainNewExit(msg->I, info.rxID, msg->E != 0))
{
llarp::routing::GrantExitMessage grant;
grant.S = NextSeqNo();
grant.T = msg->T;
if(!grant.Sign(&r->crypto, r->identity))
if(!grant.Sign(r->crypto.get(), r->identity))
{
llarp::LogError("Failed to sign grant exit message");
return false;
@ -175,7 +175,7 @@ namespace llarp
llarp::routing::RejectExitMessage reject;
reject.S = NextSeqNo();
reject.T = msg->T;
if(!reject.Sign(&r->crypto, r->identity))
if(!reject.Sign(r->crypto.get(), r->identity))
{
llarp::LogError("Failed to sign reject exit message");
return false;
@ -189,13 +189,13 @@ namespace llarp
{
llarp::routing::DataDiscardMessage discard(info.rxID, msg->S);
auto ep = r->exitContext.FindEndpointForPath(info.rxID);
if(ep && msg->Verify(&r->crypto, ep->PubKey()))
if(ep && msg->Verify(r->crypto.get(), ep->PubKey()))
{
ep->Close();
// ep is now gone af
llarp::routing::CloseExitMessage reply;
reply.S = NextSeqNo();
if(reply.Sign(&r->crypto, r->identity))
if(reply.Sign(r->crypto.get(), r->identity))
return SendRoutingMessage(&reply, r);
}
return SendRoutingMessage(&discard, r);
@ -218,7 +218,7 @@ namespace llarp
auto ep = r->exitContext.FindEndpointForPath(msg->P);
if(ep)
{
if(!msg->Verify(&r->crypto, ep->PubKey()))
if(!msg->Verify(r->crypto.get(), ep->PubKey()))
return false;
if(ep->UpdateLocalPath(info.rxID))

@ -2,6 +2,7 @@
#include <constants/proto.hpp>
#include <crypto/crypto.hpp>
#include <crypto/crypto_libsodium.hpp>
#include <dht/context.hpp>
#include <dht/node.hpp>
#include <link/iwp.hpp>
@ -196,7 +197,7 @@ namespace llarp
Router::OnSessionEstablished(llarp::RouterContact rc)
{
async_verify_RC(rc, nullptr);
llarp::LogInfo("session with ", rc.pubkey, "established");
llarp::LogInfo("session with ", rc.pubkey, " established");
}
Router::Router(struct llarp_threadpool *_tp, struct llarp_ev_loop *_netloop,
@ -205,7 +206,7 @@ namespace llarp
, netloop(_netloop)
, tp(_tp)
, logic(_logic)
, crypto(llarp::Crypto::sodium{})
, crypto(std::make_unique< sodium::CryptoLibSodium >())
, paths(this)
, exitContext(this)
, dht(llarp_dht_context_new(this))
@ -343,7 +344,7 @@ namespace llarp
{
return;
}
if(results[0].Verify(&crypto, Now()))
if(results[0].Verify(crypto.get(), Now()))
{
nodedb->Insert(results[0]);
TryConnectAsync(results[0], 10);
@ -391,7 +392,7 @@ namespace llarp
llarp::LogError("failure to decode or verify of remote RC");
return;
}
if(remote.Verify(&crypto, Now()))
if(remote.Verify(crypto.get(), Now()))
{
llarp::LogDebug("verified signature");
// store into filesystem
@ -415,15 +416,16 @@ namespace llarp
if(!EnsureEncryptionKey())
return false;
if(usingSNSeed)
return llarp_loadServiceNodeIdentityKey(&crypto, ident_keyfile, identity);
return llarp_loadServiceNodeIdentityKey(crypto.get(), ident_keyfile,
identity);
else
return llarp_findOrCreateIdentity(&crypto, ident_keyfile, identity);
return llarp_findOrCreateIdentity(crypto.get(), ident_keyfile, identity);
}
bool
Router::EnsureEncryptionKey()
{
return llarp_findOrCreateEncryption(&crypto, encryption_keyfile,
return llarp_findOrCreateEncryption(crypto.get(), encryption_keyfile,
this->encryption);
}
@ -459,7 +461,7 @@ namespace llarp
Router::SaveRC()
{
llarp::LogDebug("verify RC signature");
if(!_rc.Verify(&crypto, Now()))
if(!_rc.Verify(crypto.get(), Now()))
{
rc().Dump< MAX_RC_SIZE >();
llarp::LogError("RC is invalid, not saving");
@ -585,7 +587,7 @@ namespace llarp
return;
for(const auto &rc : results)
{
if(rc.Verify(&crypto, Now()))
if(rc.Verify(crypto.get(), Now()))
nodedb->Insert(rc);
else
return;
@ -681,7 +683,7 @@ namespace llarp
}
}
nextRC.last_updated = Now();
if(!nextRC.Sign(&crypto, identity))
if(!nextRC.Sign(crypto.get(), identity))
return false;
_rc = nextRC;
// propagate RC by renegotiating sessions
@ -818,7 +820,7 @@ namespace llarp
bool
Router::Sign(llarp::Signature &sig, llarp_buffer_t buf) const
{
return crypto.sign(sig, identity, buf);
return crypto->sign(sig, identity, buf);
}
void
@ -968,7 +970,7 @@ namespace llarp
job->nodedb = nodedb;
job->logic = logic;
// job->crypto = &crypto; // we already have this
// job->crypto = crypto.get(); // we already have this
job->cryptoworker = tp;
job->diskworker = disk;
if(rc.IsPublicRouter())
@ -1004,6 +1006,21 @@ namespace llarp
}
llarp::LogInfo("Bound RPC server to ", rpcBindAddr);
}
if(whitelistRouters)
{
rpcCaller = std::make_unique<llarp::rpc::Caller>(this);
rpcCaller->SetBasicAuth(lokidRPCUser, lokidRPCPassword);
while(!rpcCaller->Start(lokidRPCAddr))
{
llarp::LogError("failed to start jsonrpc caller to ", lokidRPCAddr);
#if defined(ANDROID) || defined(RPI)
sleep(1);
#else
std::this_thread::sleep_for(std::chrono::seconds(1));
#endif
}
llarp::LogInfo("RPC Caller to ", lokidRPCAddr, " started");
}
llarp_threadpool_start(tp);
llarp_threadpool_start(disk);
@ -1043,8 +1060,7 @@ namespace llarp
if(ExitEnabled())
{
llarp::nuint32_t a = publicAddr.xtonl();
// TODO: enable this once the network can serialize xi
//_rc.exits.emplace_back(_rc.pubkey, a);
_rc.exits.emplace_back(_rc.pubkey, a);
llarp::LogInfo(
"Neato tehl33toh, You are a freaking exit relay. w00t!!!!! your "
"exit "
@ -1052,7 +1068,7 @@ namespace llarp
a);
}
llarp::LogInfo("Signing rc...");
if(!_rc.Sign(&crypto, identity))
if(!_rc.Sign(crypto.get(), identity))
{
llarp::LogError("failed to sign rc");
return false;
@ -1105,11 +1121,11 @@ namespace llarp
{
// we are a client
// regenerate keys and resign rc before everything else
crypto.identity_keygen(identity);
crypto.encryption_keygen(encryption);
crypto->identity_keygen(identity);
crypto->encryption_keygen(encryption);
_rc.pubkey = llarp::seckey_topublic(identity);
_rc.enckey = llarp::seckey_topublic(encryption);
if(!_rc.Sign(&crypto, identity))
if(!_rc.Sign(crypto.get(), identity))
{
llarp::LogError("failed to regenerate keys and sign RC");
return false;
@ -1409,11 +1425,13 @@ namespace llarp
{
// fallback defaults
// To NeuroScr: why run findFree* here instead of in tun.cpp?
// I think it should be in tun.cpp, better to closer to time of usage
// that way new tun may have grab a range we may have also grabbed here
static const std::unordered_map< std::string,
std::function< std::string(void) > >
netConfigDefaults = {
{"ifname", llarp::findFreeLokiTunIfName},
{"ifaddr", llarp::findFreePrivateRange},
{"ifname", []() -> std::string { return "auto"; }},
{"ifaddr", []() -> std::string { return "auto"; }},
{"local-dns", []() -> std::string { return "127.0.0.1:53"; }},
{"upstream-dns", []() -> std::string { return "1.1.1.1:53"; }}};
// populate with fallback defaults if values not present
@ -1592,6 +1610,14 @@ namespace llarp
{
self->lokidRPCAddr = val;
}
if(StrEq(key, "username"))
{
self->lokidRPCUser = val;
}
if(StrEq(key, "password"))
{
self->lokidRPCPassword = val;
}
}
else if(StrEq(section, "dns"))
{
@ -1609,21 +1635,31 @@ namespace llarp
else if(StrEq(section, "connect")
|| (StrEq(section, "bootstrap") && StrEq(key, "add-node")))
{
//llarp::LogDebug("connect section has ", key, "=", val);
self->bootstrapRCList.emplace_back();
auto &rc = self->bootstrapRCList.back();
if(rc.Read(val) && rc.Verify(&self->crypto, self->Now()))
if(!rc.Read(val))
{
llarp::LogInfo("Added bootstrap node ", RouterID(rc.pubkey));
llarp::LogWarn("failed to decode bootstrap RC, file='", val,
"' rc=", rc);
self->bootstrapRCList.pop_back();
return;
}
else if(self->Now() - rc.last_updated > RouterContact::Lifetime)
if(rc.Verify(self->crypto.get(), self->Now()))
{
llarp::LogWarn("Bootstrap node ", RouterID(rc.pubkey),
" is too old and needs to be refreshed");
self->bootstrapRCList.pop_back();
llarp::LogInfo("Added bootstrap node ", RouterID(rc.pubkey));
}
else
{
llarp::LogError("malformed rc file: ", val);
if(rc.IsExpired(self->Now()))
{
llarp::LogWarn("Bootstrap node ", RouterID(rc.pubkey),
" is too old and needs to be refreshed");
}
else
{
llarp::LogError("malformed rc file='", val, "' rc=", rc);
}
self->bootstrapRCList.pop_back();
}
}
@ -1635,8 +1671,10 @@ namespace llarp
{
llarp::LogWarn("!!!! you have manually set netid to be '", val,
"' which does not equal '", Version::LLARP_NET_ID,
"' you will run as a different network, good luck and "
"don't forget: something something MUH traffic shape "
"' you will run as a different network, good luck "
"and "
"don't forget: something something MUH traffic "
"shape "
"correlation !!!!");
llarp::NetID::DefaultValue() =
llarp::NetID(reinterpret_cast< const byte_t * >(strdup(val)));

@ -26,9 +26,10 @@
#include <functional>
#include <list>
#include <map>
#include <vector>
#include <unordered_map>
#include <memory>
#include <set>
#include <unordered_map>
#include <vector>
namespace llarp
{
@ -105,7 +106,7 @@ namespace llarp
llarp_ev_loop *netloop;
llarp_threadpool *tp;
llarp::Logic *logic;
llarp::Crypto crypto;
std::unique_ptr< llarp::Crypto > crypto;
llarp::path::PathContext paths;
llarp::exit::Context exitContext;
llarp::SecretKey identity;
@ -174,7 +175,9 @@ namespace llarp
/// lokid caller
const std::string DefaultLokidRPCAddr = "127.0.0.1:22023";
std::unique_ptr< llarp::rpc::Caller > rpcCaller;
std::string lokidRPCAddr = DefaultLokidRPCAddr;
std::string lokidRPCAddr = DefaultLokidRPCAddr;
std::string lokidRPCUser = "";
std::string lokidRPCPassword = "";
std::set< std::unique_ptr< llarp::ILinkLayer >,
CompareLinks< llarp::ILinkLayer > >

@ -337,7 +337,9 @@ namespace llarp
return false;
}
f.seekg(0, std::ios::end);
auto l = f.tellg();
size_t l = f.tellg();
if(l > sizeof(tmp))
return false;
f.seekg(0, std::ios::beg);
f.read((char *)tmp, l);
auto buf = llarp::StackBuffer< decltype(tmp) >(tmp);

@ -8,6 +8,7 @@
#include <util/aligned.hpp>
#include <util/bencode.hpp>
#include <functional>
#include <vector>
#define MAX_RC_SIZE (1024)
@ -188,6 +189,9 @@ namespace llarp
bool
VerifySignature(llarp::Crypto *crypto) const;
};
using RouterLookupHandler =
std::function< void(const std::vector< RouterContact >&) >;
} // namespace llarp
#endif

@ -44,6 +44,13 @@ namespace llarp
using Hash = AlignedBuffer< SIZE >::Hash;
};
inline bool
operator==(const RouterID& lhs, const RouterID& rhs)
{
return lhs.as_array() == rhs.as_array();
}
} // namespace llarp
#endif

@ -3,6 +3,7 @@
#include <router/router.hpp>
#ifdef USE_ABYSS
#include <util/encode.hpp>
#include <libabyss.hpp>
#endif
@ -14,8 +15,9 @@ namespace llarp
struct CallerHandler : public ::abyss::http::IRPCClientHandler
{
CallerHandler(::abyss::http::ConnImpl* impl)
: ::abyss::http::IRPCClientHandler(impl)
CallerImpl* m_Parent;
CallerHandler(::abyss::http::ConnImpl* impl, CallerImpl* parent)
: ::abyss::http::IRPCClientHandler(impl), m_Parent(parent)
{
}
@ -44,16 +46,12 @@ namespace llarp
}
void
PopulateReqHeaders(abyss::http::Headers_t& hdr)
{
(void)hdr;
// TODO: add http auth (?)
}
PopulateReqHeaders(abyss::http::Headers_t& hdr);
};
struct GetServiceNodeListHandler final : public CallerHandler
{
using PubkeyList_t = std::vector< PubKey >;
using PubkeyList_t = std::vector< RouterID >;
using Callback_t = std::function< void(const PubkeyList_t&, bool) >;
~GetServiceNodeListHandler()
@ -61,8 +59,9 @@ namespace llarp
}
Callback_t handler;
GetServiceNodeListHandler(::abyss::http::ConnImpl* impl, Callback_t h)
: CallerHandler(impl), handler(h)
GetServiceNodeListHandler(::abyss::http::ConnImpl* impl,
CallerImpl* parent, Callback_t h)
: CallerHandler(impl, parent), handler(h)
{
}
@ -92,8 +91,12 @@ namespace llarp
if(key_itr->IsString())
{
keys.emplace_back();
if(!HexDecode(key_itr->GetString(), keys.back().begin(),
decltype(keys)::value_type::SIZE))
std::string str = key_itr->GetString();
if(str.size() != Base32DecodeSize(keys.back().size()))
{
keys.pop_back();
}
else if(!Base32Decode(str, keys.back()))
{
keys.pop_back();
}
@ -114,10 +117,13 @@ namespace llarp
struct CallerImpl : public ::abyss::http::JSONRPC
{
Router* router;
llarp_time_t m_NextKeyUpdate;
llarp_time_t m_NextKeyUpdate = 0;
const llarp_time_t KeyUpdateInterval = 1000 * 60 * 2;
using PubkeyList_t = GetServiceNodeListHandler::PubkeyList_t;
std::string username;
std::string password;
CallerImpl(Router* r) : ::abyss::http::JSONRPC(), router(r)
{
}
@ -133,13 +139,20 @@ namespace llarp
Flush();
}
void
SetBasicAuth(const std::string& user, const std::string& passwd)
{
username = user;
password = passwd;
}
void
AsyncUpdatePubkeyList()
{
LogInfo("Updating service node list");
::abyss::json::Value params;
params.SetObject();
QueueRPC("/get_all_service_node_keys", std::move(params),
QueueRPC("get_all_service_nodes_keys", std::move(params),
std::bind(&CallerImpl::NewAsyncUpdatePubkeyListConn, this,
std::placeholders::_1));
}
@ -154,7 +167,7 @@ namespace llarp
NewAsyncUpdatePubkeyListConn(abyss::http::ConnImpl* impl)
{
return new GetServiceNodeListHandler(
impl,
impl, this,
std::bind(&CallerImpl::HandleServiceNodeListUpdated, this,
std::placeholders::_1, std::placeholders::_2));
}
@ -180,6 +193,18 @@ namespace llarp
}
};
void
CallerHandler::PopulateReqHeaders(abyss::http::Headers_t& hdr)
{
if(m_Parent->username.empty() || m_Parent->password.empty())
return;
std::stringstream ss;
ss << "Basic ";
std::string cred = m_Parent->username + ":" + m_Parent->password;
llarp::Base64Encode(ss, (const byte_t*)cred.c_str(), cred.size());
hdr.emplace("Authorization", ss.str());
}
struct Handler : public ::abyss::httpd::IRPCHandler
{
Router* router;
@ -354,6 +379,11 @@ namespace llarp
{
(void)now;
}
void
SetBasicAuth(const std::string&, const std::string&)
{
}
};
#endif
@ -385,6 +415,12 @@ namespace llarp
m_Impl->Tick(now);
}
void
Caller::SetBasicAuth(const std::string& user, const std::string& passwd)
{
m_Impl->SetBasicAuth(user, passwd);
}
Server::Server(Router* r) : m_Impl(new ServerImpl(r))
{
}

@ -40,6 +40,10 @@ namespace llarp
Caller(Router* r);
~Caller();
/// set http basic auth for use with remote rpc endpoint
void
SetBasicAuth(const std::string& user, const std::string& password);
/// start with jsonrpc endpoint address
bool
Start(const std::string& remote);
@ -48,10 +52,6 @@ namespace llarp
void
Stop();
/// test if a router is valid
bool
VerifyRouter(const PubKey& pk);
/// do per second tick
void
Tick(llarp_time_t now);

@ -122,7 +122,8 @@ namespace llarp
if(!A.Verify(crypto, buf, Z))
return false;
// validate PoW
if(W && !W->IsValid(crypto->shorthash, now))
using namespace std::placeholders;
if(W && !W->IsValid(std::bind(&Crypto::shorthash, crypto, _1, _2), now))
return false;
// valid timestamps
// add max clock skew

@ -10,6 +10,7 @@
#include <util/time.hpp>
#include <algorithm>
#include <functional>
#include <iostream>
#include <vector>
@ -153,6 +154,10 @@ namespace llarp
bool
Verify(llarp::Crypto* crypto, llarp_time_t now) const;
};
using IntroSetLookupHandler =
std::function< void(const std::vector< IntroSet >&) >;
} // namespace service
} // namespace llarp

@ -30,6 +30,22 @@ namespace llarp
return true;
}
void
Context::ForEachService(
std::function< bool(const std::string &,
const std::unique_ptr< Endpoint > &) >
visit)
{
auto itr = m_Endpoints.begin();
while(itr != m_Endpoints.end())
{
if(visit(itr->first, itr->second))
++itr;
else
return;
}
}
bool
Context::RemoveEndpoint(const std::string &name)
{
@ -274,17 +290,18 @@ namespace llarp
static std::map< std::string,
std::function< llarp::service::Endpoint *(
const std::string &, llarp::Router *) > >
const std::string &, llarp::Router *,
llarp::service::Context *) > >
endpointConstructors = {
{"tun",
[](const std::string &nick,
llarp::Router *r) -> llarp::service::Endpoint * {
return new llarp::handlers::TunEndpoint(nick, r);
[](const std::string &nick, llarp::Router *r,
llarp::service::Context *c) -> llarp::service::Endpoint * {
return new llarp::handlers::TunEndpoint(nick, r, c);
}},
{"null",
[](const std::string &nick,
llarp::Router *r) -> llarp::service::Endpoint * {
return new llarp::handlers::NullEndpoint(nick, r);
[](const std::string &nick, llarp::Router *r,
llarp::service::Context *c) -> llarp::service::Endpoint * {
return new llarp::handlers::NullEndpoint(nick, r, c);
}}};
{
@ -297,7 +314,7 @@ namespace llarp
}
// construct
service.reset(itr->second(conf.first, m_Router));
service.reset(itr->second(conf.first, m_Router, this));
// if ephemeral, then we need to regen key
// if privkey file, then set it and load it

@ -61,6 +61,12 @@ namespace llarp
bool
iterate(struct endpoint_iter &i);
/// function visitor returns false to prematurely break iteration
void
ForEachService(std::function< bool(const std::string &,
const std::unique_ptr< Endpoint > &) >
visit);
/// hint at possible path usage and trigger building early
bool
Prefetch(const llarp::service::Address &addr);

@ -16,8 +16,10 @@ namespace llarp
{
namespace service
{
Endpoint::Endpoint(const std::string& name, llarp::Router* r)
Endpoint::Endpoint(const std::string& name, llarp::Router* r,
Context* parent)
: path::Builder(r, r->dht, 6, DEFAULT_HOP_LENGTH)
, context(parent)
, m_Router(r)
, m_Name(name)
{
@ -116,7 +118,7 @@ namespace llarp
return;
}
m_IntroSet.topic = m_Tag;
if(!m_Identity.SignIntroSet(m_IntroSet, &m_Router->crypto, now))
if(!m_Identity.SignIntroSet(m_IntroSet, m_Router->crypto.get(), now))
{
llarp::LogWarn("failed to sign introset for endpoint ", Name());
return;
@ -352,7 +354,7 @@ namespace llarp
bool
Endpoint::HandleGotIntroMessage(const llarp::dht::GotIntroMessage* msg)
{
auto crypto = &m_Router->crypto;
auto crypto = m_Router->crypto.get();
std::set< IntroSet > remote;
for(const auto& introset : msg->I)
{
@ -492,7 +494,7 @@ namespace llarp
bool
Endpoint::LoadKeyFile()
{
auto crypto = &m_Router->crypto;
auto crypto = m_Router->crypto.get();
if(m_Keyfile.size())
{
if(!m_Identity.EnsureKeys(m_Keyfile, crypto))
@ -512,7 +514,7 @@ namespace llarp
Endpoint::Start()
{
// how can I tell if a m_Identity isn't loaded?
//this->LoadKeyFile();
// this->LoadKeyFile();
if(!m_DataHandler)
{
m_DataHandler = this;
@ -1233,7 +1235,7 @@ namespace llarp
f.C.Zero();
transfer.Y.Randomize();
transfer.P = remoteIntro.pathID;
if(!f.EncryptAndSign(&Router()->crypto, m, K, m_Identity))
if(!f.EncryptAndSign(Router()->crypto.get(), m, K, m_Identity))
{
llarp::LogError("failed to encrypt and sign");
return false;
@ -1260,12 +1262,13 @@ namespace llarp
}
}
// no converstation
return EnsurePathToService(remote,
[](Address, OutboundContext* c) {
if(c)
c->UpdateIntroSet(true);
},
5000, false);
return EnsurePathToService(
remote,
[](Address, OutboundContext* c) {
if(c)
c->UpdateIntroSet(true);
},
5000, false);
}
bool
@ -1491,9 +1494,11 @@ namespace llarp
// compure post handshake session key
// PKE (A, B, N)
SharedSecret sharedSecret;
if(!self->m_LocalIdentity.KeyExchange(self->crypto->dh_client,
sharedSecret, self->remote,
self->frame.N))
using namespace std::placeholders;
path_dh_func dh_client =
std::bind(&Crypto::dh_client, self->crypto, _1, _2, _3, _4);
if(!self->m_LocalIdentity.KeyExchange(dh_client, sharedSecret,
self->remote, self->frame.N))
{
llarp::LogError("failed to derive x25519 shared key component");
}
@ -1726,7 +1731,7 @@ namespace llarp
Endpoint::SendContext::EncryptAndSendTo(llarp_buffer_t payload,
ProtocolType t)
{
auto crypto = m_Endpoint->Router()->crypto;
auto crypto = m_Endpoint->Router()->crypto.get();
SharedSecret shared;
routing::PathTransferMessage msg;
ProtocolFrame& f = msg.T;
@ -1762,7 +1767,7 @@ namespace llarp
m.PutBuffer(payload);
m.tag = f.T;
if(!f.EncryptAndSign(&crypto, m, shared, m_Endpoint->m_Identity))
if(!f.EncryptAndSign(crypto, m, shared, m_Endpoint->m_Identity))
{
llarp::LogError("failed to sign");
return;
@ -1804,7 +1809,7 @@ namespace llarp
llarp::Crypto*
Endpoint::Crypto()
{
return &m_Router->crypto;
return m_Router->crypto.get();
}
llarp_threadpool*

@ -20,6 +20,8 @@ namespace llarp
{
namespace service
{
// foward declare
struct Context;
// forward declare
struct AsyncKeyExchange;
@ -35,7 +37,7 @@ namespace llarp
static const size_t MAX_OUTBOUND_CONTEXT_COUNT = 4;
Endpoint(const std::string& nickname, llarp::Router* r);
Endpoint(const std::string& nickname, llarp::Router* r, Context* parent);
~Endpoint();
void
@ -47,6 +49,20 @@ namespace llarp
virtual void
Tick(llarp_time_t now);
/// return true if we have a resolvable ip address
virtual bool
HasIfAddr() const
{
return false;
}
/// get our ifaddr if it is set
virtual huint32_t
GetIfAddr() const
{
return huint32_t{0};
}
/// router's logic
llarp::Logic*
RouterLogic();
@ -381,6 +397,9 @@ namespace llarp
IntroSetPublished();
protected:
/// parent context that owns this endpoint
Context* const context;
void
RegenAndPublishIntroSet(llarp_time_t now, bool forceRebuild = false);

@ -269,7 +269,11 @@ namespace llarp
}
// PKE (A, B, N)
SharedSecret sharedSecret;
if(!self->m_LocalIdentity.KeyExchange(crypto->dh_server, sharedSecret,
using namespace std::placeholders;
path_dh_func dh_server =
std::bind(&Crypto::dh_server, self->crypto, _1, _2, _3, _4);
if(!self->m_LocalIdentity.KeyExchange(dh_server, sharedSecret,
self->msg->sender, self->frame.N))
{
llarp::LogError("x25519 key exchange failed");

@ -50,8 +50,8 @@ llarp_buffer_read_until(llarp_buffer_t* buff, char delim, byte_t* result,
{
size_t read = 0;
while(*buff->cur != delim && resultsize
&& (buff->cur != buff->base + buff->sz))
// do the bound check first, to avoid over running
while((buff->cur != buff->base + buff->sz) && *buff->cur != delim && resultsize)
{
*result = *buff->cur;
buff->cur++;

@ -31,13 +31,19 @@ namespace llarp
return b * d.quot;
}
static size_t
Base32DecodeSize(size_t sz)
{
return DecodeSize<5, 8>(sz);
}
template < typename Stack, typename V >
bool
Base32Decode(const Stack& stack, V& value)
{
int tmp = 0, bits = 0;
size_t ret = 0;
size_t len = DecodeSize< 5, 8 >(value.size());
size_t len = Base32DecodeSize(value.size());
size_t outLen = value.size();
for(size_t i = 0; i < len; i++)
{
@ -146,6 +152,68 @@ namespace llarp
return sz == 0;
}
static const char base64_table[] = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
template < typename OStream_t >
void
Base64Encode(OStream_t& out, const uint8_t* src, size_t len)
{
size_t i = 0;
size_t j = 0;
uint8_t buf[4] = {0};
uint8_t tmp[3] = {0};
while(len--)
{
tmp[i++] = *(src++);
if(3 == i)
{
buf[0] = (tmp[0] & 0xfc) >> 2;
buf[1] = ((tmp[0] & 0x03) << 4) + ((tmp[1] & 0xf0) >> 4);
buf[2] = ((tmp[1] & 0x0f) << 2) + ((tmp[2] & 0xc0) >> 6);
buf[3] = tmp[2] & 0x3f;
// encode
for(i = 0; i < 4; ++i)
{
out << base64_table[buf[i]];
}
// reset
i = 0;
}
}
// remainder
if(i > 0)
{
// fill `tmp' with `\0' at most 3 times
for(j = i; j < 3; ++j)
{
tmp[j] = 0;
}
// encode remainder
buf[0] = (tmp[0] & 0xfc) >> 2;
buf[1] = ((tmp[0] & 0x03) << 4) + ((tmp[1] & 0xf0) >> 4);
buf[2] = ((tmp[1] & 0x0f) << 2) + ((tmp[2] & 0xc0) >> 6);
buf[3] = tmp[2] & 0x3f;
for(j = 0; (j < i + 1); ++j)
{
out << base64_table[buf[j]];
}
// pad
while((i++ < 3))
{
out << '=';
}
}
}
} // namespace llarp
#endif

Binary file not shown.

Binary file not shown.

@ -1,4 +1,4 @@
#include <crypto/crypto.hpp>
#include <crypto/crypto_libsodium.hpp>
#include <iostream>
@ -6,16 +6,15 @@
namespace llarp
{
struct IdentityKeyTest : public ::testing::Test
struct IdentityKeyTest : public ::testing::Test
{
llarp::Crypto crypto;
llarp::sodium::CryptoLibSodium crypto;
llarp::IdentitySecret seed;
IdentityKeyTest() : crypto(llarp::Crypto::sodium{})
IdentityKeyTest()
{
}
llarp::Crypto*
Crypto()
{
@ -27,15 +26,13 @@ namespace llarp
{
seed.Randomize();
}
};
TEST_F(IdentityKeyTest, TestSeedToSecretKey)
{
SecretKey secret;
ASSERT_TRUE(crypto.seed_to_secretkey(secret, seed));
AlignedBuffer<128> random;
AlignedBuffer< 128 > random;
random.Randomize();
Signature sig;
ASSERT_TRUE(crypto.sign(sig, secret, random.as_buffer()));
@ -47,10 +44,10 @@ namespace llarp
struct PQCryptoTest : public ::testing::Test
{
llarp::Crypto crypto;
llarp::sodium::CryptoLibSodium crypto;
PQKeyPair keys;
PQCryptoTest() : crypto(llarp::Crypto::sodium{})
PQCryptoTest()
{
}

@ -35,10 +35,10 @@ TEST_P(PubKeyString, fromstring)
}
llarp::PubKey::Data empty = {};
llarp::PubKey::Data full = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
llarp::PubKey::Data full = {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}};
// clang-format off
ToStringData toStringData[] = {

@ -0,0 +1 @@
#include <dht/mock_context.hpp>

@ -0,0 +1,46 @@
#ifndef TEST_LLARP_MOCK_CONTEXT
#define TEST_LLARP_MOCK_CONTEXT
#include <dht/context.hpp>
#include <gmock/gmock.h>
namespace llarp
{
namespace test
{
struct MockContext final : public dht::AbstractContext
{
MOCK_METHOD2(LookupRouter, bool(const RouterID&, RouterLookupHandler));
MOCK_METHOD6(LookupIntroSetRecursive,
void(const service::Address&, const dht::Key_t&, uint64_t,
const dht::Key_t&, uint64_t,
service::IntroSetLookupHandler));
MOCK_METHOD5(LookupIntroSetIterative,
void(const service::Address&, const dht::Key_t&, uint64_t,
const dht::Key_t&, service::IntroSetLookupHandler));
MOCK_METHOD3(
FindRandomIntroSetsWithTagExcluding,
std::set< service::IntroSet >(const service::Tag&, size_t,
const std::set< service::IntroSet >&));
MOCK_METHOD3(DHTSendTo, void(const RouterID&, dht::IMessage*, bool));
MOCK_CONST_METHOD0(Now, llarp_time_t());
MOCK_CONST_METHOD0(Crypto, llarp::Crypto*());
MOCK_CONST_METHOD0(GetRouter, llarp::Router*());
MOCK_CONST_METHOD0(OurKey, const dht::Key_t&());
MOCK_CONST_METHOD0(Nodes, dht::Bucket< dht::RCNode >*());
};
} // namespace test
} // namespace llarp
#endif

@ -0,0 +1,105 @@
#include <dht/explorenetworkjob.hpp>
#include <dht/messages/findrouter.hpp>
#include <dht/mock_context.hpp>
#include <test_util.hpp>
#include <gtest/gtest.h>
using namespace llarp;
using namespace ::testing;
using test::makeBuf;
struct TestDhtExploreNetworkJob : public ::testing::Test
{
RouterID peer;
test::MockContext context;
dht::ExploreNetworkJob exploreNetworkJob;
TestDhtExploreNetworkJob()
: peer(makeBuf< RouterID >(0x01)), exploreNetworkJob(peer, &context)
{
}
};
TEST_F(TestDhtExploreNetworkJob, validate)
{
const RouterID other = makeBuf< RouterID >(0x02);
ASSERT_TRUE(exploreNetworkJob.Validate(other));
}
TEST_F(TestDhtExploreNetworkJob, get_next_peer)
{
dht::Key_t key = makeBuf< dht::Key_t >(0x02);
std::set< dht::Key_t > exclude;
ASSERT_FALSE(exploreNetworkJob.GetNextPeer(key, exclude));
}
TEST_F(TestDhtExploreNetworkJob, do_next)
{
const dht::Key_t key = makeBuf< dht::Key_t >(0x02);
ASSERT_NO_THROW(exploreNetworkJob.DoNextRequest(key));
}
TEST_F(TestDhtExploreNetworkJob, start)
{
// Verify input arguments are passed correctly.
// The actual logic is inside the `dht::AbstractContext` implementation.
const auto txKey = makeBuf< dht::Key_t >(0x02);
uint64_t txId = 4;
dht::TXOwner txOwner(txKey, txId);
// clang-format off
EXPECT_CALL(context, DHTSendTo(
Eq(txKey.as_array()),
WhenDynamicCastTo< dht::FindRouterMessage* >(NotNull()),
true)
).Times(1);
// clang-format off
ASSERT_NO_THROW(exploreNetworkJob.Start(txOwner));
}
TEST_F(TestDhtExploreNetworkJob, send_reply)
{
// Concerns:
// - Empty collection
// - Lookup router fails (returns false)
// - Number of calls matches collection size
{
exploreNetworkJob.valuesFound.clear();
EXPECT_CALL(context, LookupRouter(_, _)).Times(0);
EXPECT_CALL(context, GetRouter()).WillOnce(Return(nullptr));
ASSERT_NO_THROW(exploreNetworkJob.SendReply());
}
{
exploreNetworkJob.valuesFound.clear();
exploreNetworkJob.valuesFound.push_back(makeBuf<RouterID>(0x00));
exploreNetworkJob.valuesFound.push_back(makeBuf<RouterID>(0x01));
exploreNetworkJob.valuesFound.push_back(makeBuf<RouterID>(0x02));
EXPECT_CALL(context, GetRouter()).WillOnce(Return(nullptr));
EXPECT_CALL(context, LookupRouter(Ne(makeBuf<RouterID>(0x01)), _)).Times(2).WillRepeatedly(Return(true));
EXPECT_CALL(context, LookupRouter(Eq(makeBuf<RouterID>(0x01)), _)).WillOnce(Return(false));
ASSERT_NO_THROW(exploreNetworkJob.SendReply());
}
{
exploreNetworkJob.valuesFound.clear();
exploreNetworkJob.valuesFound.push_back(makeBuf<RouterID>(0x00));
exploreNetworkJob.valuesFound.push_back(makeBuf<RouterID>(0x01));
exploreNetworkJob.valuesFound.push_back(makeBuf<RouterID>(0x02));
EXPECT_CALL(context, GetRouter()).WillOnce(Return(nullptr));
EXPECT_CALL(context, LookupRouter(_, _)).Times(3).WillRepeatedly(Return(true));
ASSERT_NO_THROW(exploreNetworkJob.SendReply());
}
}

@ -0,0 +1,170 @@
#include <dht/tx.hpp>
#include <test_util.hpp>
#include <gtest/gtest.h>
#include <gmock/gmock.h>
using namespace llarp;
using namespace ::testing;
using llarp::test::makeBuf;
// Mock implementation of TX.
struct TestTx final : public dht::TX< dht::Key_t, std::string >
{
TestTx(const dht::TXOwner& asker, const dht::Key_t& k,
dht::AbstractContext* p)
: dht::TX< dht::Key_t, std::string >(asker, k, p)
{
}
MOCK_CONST_METHOD1(Validate, bool(const std::string&));
MOCK_METHOD1(Start, void(const dht::TXOwner&));
MOCK_METHOD2(GetNextPeer, bool(dht::Key_t&, const std::set< dht::Key_t >&));
MOCK_METHOD1(DoNextRequest, void(const dht::Key_t&));
MOCK_METHOD0(SendReply, void());
};
struct TestDhtTx : public Test
{
dht::TXOwner asker;
dht::Key_t key;
TestTx tx;
TestDhtTx() : tx(asker, key, nullptr)
{
}
};
TEST_F(TestDhtTx, on_found)
{
// Concerns
// - Validate returns true
// - Repeated call on success
// - Validate returns false
// - Repeated call on failure
// - Repeated call on success after failure
const auto key = makeBuf< dht::Key_t >(0x00);
std::string val("good value");
// Validate returns true
{
EXPECT_CALL(tx, Validate(val)).WillOnce(Return(true));
tx.OnFound(key, val);
ASSERT_THAT(tx.peersAsked, Contains(key));
ASSERT_THAT(tx.valuesFound, Contains(val));
}
// Repeated call on success
{
EXPECT_CALL(tx, Validate(val)).WillOnce(Return(true));
tx.OnFound(key, val);
ASSERT_THAT(tx.peersAsked, Contains(key));
ASSERT_THAT(tx.valuesFound, Contains(val));
}
const auto key1 = makeBuf< dht::Key_t >(0x01);
std::string badVal("bad value");
// Validate returns false
{
EXPECT_CALL(tx, Validate(badVal)).WillOnce(Return(false));
tx.OnFound(key1, badVal);
ASSERT_THAT(tx.peersAsked, Contains(key1));
ASSERT_THAT(tx.valuesFound, Not(Contains(badVal)));
}
// Repeated call on failure
{
EXPECT_CALL(tx, Validate(badVal)).WillOnce(Return(false));
tx.OnFound(key1, badVal);
ASSERT_THAT(tx.peersAsked, Contains(key1));
ASSERT_THAT(tx.valuesFound, Not(Contains(badVal)));
}
// Repeated call on success after failure
{
EXPECT_CALL(tx, Validate(badVal)).WillOnce(Return(true));
tx.OnFound(key1, badVal);
ASSERT_THAT(tx.peersAsked, Contains(key1));
ASSERT_THAT(tx.valuesFound, Contains(badVal));
}
}
TEST_F(TestDhtTx, ask_next_peer)
{
// Concerns:
// - GetNextPeer fails
// - Next Peer is not closer
// - next ptr is null
// - next ptr is not null
const auto key0 = makeBuf< dht::Key_t >(0x00);
const auto key1 = makeBuf< dht::Key_t >(0x01);
const auto key2 = makeBuf< dht::Key_t >(0x02);
{
// GetNextPeer fails
EXPECT_CALL(tx, GetNextPeer(_, _)).WillOnce(Return(false));
EXPECT_CALL(tx, DoNextRequest(key1)).Times(0);
ASSERT_FALSE(tx.AskNextPeer(key0, {}));
ASSERT_THAT(tx.peersAsked, Contains(key0));
tx.peersAsked.clear();
}
{
// Next Peer is not closer
EXPECT_CALL(tx, GetNextPeer(_, _))
.WillOnce(DoAll(SetArgReferee< 0 >(key1), Return(true)));
EXPECT_CALL(tx, DoNextRequest(key1)).Times(0);
ASSERT_FALSE(tx.AskNextPeer(key0, {}));
ASSERT_THAT(tx.peersAsked, Contains(key0));
tx.peersAsked.clear();
}
{
// next ptr is null
EXPECT_CALL(tx, GetNextPeer(_, _))
.WillOnce(DoAll(SetArgReferee< 0 >(key1), Return(true)));
EXPECT_CALL(tx, DoNextRequest(key1)).Times(1);
ASSERT_TRUE(tx.AskNextPeer(key2, {}));
ASSERT_THAT(tx.peersAsked, Contains(key2));
tx.peersAsked.clear();
}
{
// next ptr is not null
EXPECT_CALL(tx, GetNextPeer(_, _)).Times(0);
EXPECT_CALL(tx, DoNextRequest(key1)).Times(1);
auto ptr = std::make_unique< dht::Key_t >(key1);
ASSERT_TRUE(tx.AskNextPeer(key2, ptr));
ASSERT_THAT(tx.peersAsked, Contains(key2));
tx.peersAsked.clear();
}
}

@ -0,0 +1,4 @@
# Run manually to reformat a file:
# clang-format -i --style=file <file>
Language: Cpp
BasedOnStyle: Google

@ -1,2 +1,57 @@
# Ignore CI build directory
build/
xcuserdata
cmake-build-debug/
.idea/
bazel-bin
bazel-genfiles
bazel-googletest
bazel-out
bazel-testlogs
# python
*.pyc
# Visual Studio files
.vs
*.sdf
*.opensdf
*.VC.opendb
*.suo
*.user
_ReSharper.Caches/
Win32-Debug/
Win32-Release/
x64-Debug/
x64-Release/
# Ignore autoconf / automake files
Makefile.in
aclocal.m4
configure
build-aux/
autom4te.cache/
googletest/m4/libtool.m4
googletest/m4/ltoptions.m4
googletest/m4/ltsugar.m4
googletest/m4/ltversion.m4
googletest/m4/lt~obsolete.m4
# Ignore generated directories.
googlemock/fused-src/
googletest/fused-src/
# macOS files
.DS_Store
googletest/.DS_Store
googletest/xcode/.DS_Store
# Ignore cmake generated directories and files.
CMakeFiles
CTestTestfile.cmake
Makefile
cmake_install.cmake
googlemock/CMakeFiles
googlemock/CTestTestfile.cmake
googlemock/Makefile
googlemock/cmake_install.cmake
googlemock/gtest

@ -0,0 +1,78 @@
# Build matrix / environment variable are explained on:
# https://docs.travis-ci.com/user/customizing-the-build/
# This file can be validated on:
# http://lint.travis-ci.org/
sudo: false
language: cpp
# Define the matrix explicitly, manually expanding the combinations of (os, compiler, env).
# It is more tedious, but grants us far more flexibility.
matrix:
include:
- os: linux
dist: trusty
sudo: required
group: deprecated-2017Q3
before_install: chmod -R +x ./ci/*platformio.sh
install: ./ci/install-platformio.sh
script: ./ci/build-platformio.sh
- os: linux
compiler: gcc
sudo : true
install: ./ci/install-linux.sh && ./ci/log-config.sh
script: ./ci/build-linux-bazel.sh
- os: linux
compiler: clang
sudo : true
install: ./ci/install-linux.sh && ./ci/log-config.sh
script: ./ci/build-linux-bazel.sh
- os: linux
group: deprecated-2017Q4
compiler: gcc
install: ./ci/install-linux.sh && ./ci/log-config.sh
script: ./ci/build-linux-autotools.sh
env: VERBOSE=1 CXXFLAGS=-std=c++11
- os: linux
group: deprecated-2017Q4
compiler: gcc
env: BUILD_TYPE=Debug VERBOSE=1 CXX_FLAGS=-std=c++11
- os: linux
group: deprecated-2017Q4
compiler: clang
env: BUILD_TYPE=Release VERBOSE=1 CXX_FLAGS=-std=c++11
- os: linux
compiler: clang
env: BUILD_TYPE=Release VERBOSE=1 CXX_FLAGS=-std=c++11 NO_EXCEPTION=ON NO_RTTI=ON COMPILER_IS_GNUCXX=ON
- os: osx
compiler: gcc
env: BUILD_TYPE=Release VERBOSE=1 CXX_FLAGS=-std=c++11 HOMEBREW_LOGS=~/homebrew-logs HOMEBREW_TEMP=~/homebrew-temp
- os: osx
env: BUILD_TYPE=Release VERBOSE=1 CXX_FLAGS=-std=c++11 HOMEBREW_LOGS=~/homebrew-logs HOMEBREW_TEMP=~/homebrew-temp
if: type != pull_request
# These are the install and build (script) phases for the most common entries in the matrix. They could be included
# in each entry in the matrix, but that is just repetitive.
install:
- ./ci/install-${TRAVIS_OS_NAME}.sh
- . ./ci/env-${TRAVIS_OS_NAME}.sh
- ./ci/log-config.sh
script: ./ci/travis.sh
# For sudo=false builds this section installs the necessary dependencies.
addons:
apt:
# List of whitelisted in travis packages for ubuntu-precise can be found here:
# https://github.com/travis-ci/apt-package-whitelist/blob/master/ubuntu-precise
# List of whitelisted in travis apt-sources:
# https://github.com/travis-ci/apt-source-whitelist/blob/master/ubuntu.json
sources:
- ubuntu-toolchain-r-test
- llvm-toolchain-precise-3.9
packages:
- g++-4.9
- clang-3.9
notifications:
email: false

@ -0,0 +1,166 @@
# Copyright 2017 Google Inc.
# All Rights Reserved.
#
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# Author: misterg@google.com (Gennadiy Civil)
#
# Bazel Build for Google C++ Testing Framework(Google Test)
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
config_setting(
name = "windows",
constraint_values = ["@bazel_tools//platforms:windows"],
)
config_setting(
name = "has_absl",
values = {"define": "absl=1"},
)
# Library that defines the FRIEND_TEST macro.
cc_library(
name = "gtest_prod",
hdrs = ["googletest/include/gtest/gtest_prod.h"],
includes = ["googletest/include"],
)
# Google Test including Google Mock
cc_library(
name = "gtest",
srcs = glob(
include = [
"googletest/src/*.cc",
"googletest/src/*.h",
"googletest/include/gtest/**/*.h",
"googlemock/src/*.cc",
"googlemock/include/gmock/**/*.h",
],
exclude = [
"googletest/src/gtest-all.cc",
"googletest/src/gtest_main.cc",
"googlemock/src/gmock-all.cc",
"googlemock/src/gmock_main.cc",
],
),
hdrs = glob([
"googletest/include/gtest/*.h",
"googlemock/include/gmock/*.h",
]),
copts = select({
":windows": [],
"//conditions:default": ["-pthread"],
}),
defines = select({
":has_absl": ["GTEST_HAS_ABSL=1"],
"//conditions:default": [],
}),
includes = [
"googlemock",
"googlemock/include",
"googletest",
"googletest/include",
],
linkopts = select({
":windows": [],
"//conditions:default": ["-pthread"],
}),
deps = select({
":has_absl": [
"@com_google_absl//absl/debugging:failure_signal_handler",
"@com_google_absl//absl/debugging:stacktrace",
"@com_google_absl//absl/debugging:symbolize",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/types:optional",
"@com_google_absl//absl/types:variant",
],
"//conditions:default": [],
}),
)
cc_library(
name = "gtest_main",
srcs = ["googlemock/src/gmock_main.cc"],
deps = [":gtest"],
)
# The following rules build samples of how to use gTest.
cc_library(
name = "gtest_sample_lib",
srcs = [
"googletest/samples/sample1.cc",
"googletest/samples/sample2.cc",
"googletest/samples/sample4.cc",
],
hdrs = [
"googletest/samples/prime_tables.h",
"googletest/samples/sample1.h",
"googletest/samples/sample2.h",
"googletest/samples/sample3-inl.h",
"googletest/samples/sample4.h",
],
)
cc_test(
name = "gtest_samples",
size = "small",
# All Samples except:
# sample9 (main)
# sample10 (main and takes a command line option and needs to be separate)
srcs = [
"googletest/samples/sample1_unittest.cc",
"googletest/samples/sample2_unittest.cc",
"googletest/samples/sample3_unittest.cc",
"googletest/samples/sample4_unittest.cc",
"googletest/samples/sample5_unittest.cc",
"googletest/samples/sample6_unittest.cc",
"googletest/samples/sample7_unittest.cc",
"googletest/samples/sample8_unittest.cc",
],
deps = [
"gtest_sample_lib",
":gtest_main",
],
)
cc_test(
name = "sample9_unittest",
size = "small",
srcs = ["googletest/samples/sample9_unittest.cc"],
deps = [":gtest"],
)
cc_test(
name = "sample10_unittest",
size = "small",
srcs = ["googletest/samples/sample10_unittest.cc"],
deps = [":gtest"],
)

@ -1,157 +0,0 @@
Changes for 1.7.0:
* New feature: death tests are supported on OpenBSD and in iOS
simulator now.
* New feature: Google Test now implements a protocol to allow
a test runner to detect that a test program has exited
prematurely and report it as a failure (before it would be
falsely reported as a success if the exit code is 0).
* New feature: Test::RecordProperty() can now be used outside of the
lifespan of a test method, in which case it will be attributed to
the current test case or the test program in the XML report.
* New feature (potentially breaking): --gtest_list_tests now prints
the type parameters and value parameters for each test.
* Improvement: char pointers and char arrays are now escaped properly
in failure messages.
* Improvement: failure summary in XML reports now includes file and
line information.
* Improvement: the <testsuites> XML element now has a timestamp attribute.
* Improvement: When --gtest_filter is specified, XML report now doesn't
contain information about tests that are filtered out.
* Fixed the bug where long --gtest_filter flag values are truncated in
death tests.
* Potentially breaking change: RUN_ALL_TESTS() is now implemented as a
function instead of a macro in order to work better with Clang.
* Compatibility fixes with C++ 11 and various platforms.
* Bug/warning fixes.
Changes for 1.6.0:
* New feature: ADD_FAILURE_AT() for reporting a test failure at the
given source location -- useful for writing testing utilities.
* New feature: the universal value printer is moved from Google Mock
to Google Test.
* New feature: type parameters and value parameters are reported in
the XML report now.
* A gtest_disable_pthreads CMake option.
* Colored output works in GNU Screen sessions now.
* Parameters of value-parameterized tests are now printed in the
textual output.
* Failures from ad hoc test assertions run before RUN_ALL_TESTS() are
now correctly reported.
* Arguments of ASSERT_XY and EXPECT_XY no longer need to support << to
ostream.
* More complete handling of exceptions.
* GTEST_ASSERT_XY can be used instead of ASSERT_XY in case the latter
name is already used by another library.
* --gtest_catch_exceptions is now true by default, allowing a test
program to continue after an exception is thrown.
* Value-parameterized test fixtures can now derive from Test and
WithParamInterface<T> separately, easing conversion of legacy tests.
* Death test messages are clearly marked to make them more
distinguishable from other messages.
* Compatibility fixes for Android, Google Native Client, MinGW, HP UX,
PowerPC, Lucid autotools, libCStd, Sun C++, Borland C++ Builder (Code Gear),
IBM XL C++ (Visual Age C++), and C++0x.
* Bug fixes and implementation clean-ups.
* Potentially incompatible changes: disables the harmful 'make install'
command in autotools.
Changes for 1.5.0:
* New feature: assertions can be safely called in multiple threads
where the pthreads library is available.
* New feature: predicates used inside EXPECT_TRUE() and friends
can now generate custom failure messages.
* New feature: Google Test can now be compiled as a DLL.
* New feature: fused source files are included.
* New feature: prints help when encountering unrecognized Google Test flags.
* Experimental feature: CMake build script (requires CMake 2.6.4+).
* Experimental feature: the Pump script for meta programming.
* double values streamed to an assertion are printed with enough precision
to differentiate any two different values.
* Google Test now works on Solaris and AIX.
* Build and test script improvements.
* Bug fixes and implementation clean-ups.
Potentially breaking changes:
* Stopped supporting VC++ 7.1 with exceptions disabled.
* Dropped support for 'make install'.
Changes for 1.4.0:
* New feature: the event listener API
* New feature: test shuffling
* New feature: the XML report format is closer to junitreport and can
be parsed by Hudson now.
* New feature: when a test runs under Visual Studio, its failures are
integrated in the IDE.
* New feature: /MD(d) versions of VC++ projects.
* New feature: elapsed time for the tests is printed by default.
* New feature: comes with a TR1 tuple implementation such that Boost
is no longer needed for Combine().
* New feature: EXPECT_DEATH_IF_SUPPORTED macro and friends.
* New feature: the Xcode project can now produce static gtest
libraries in addition to a framework.
* Compatibility fixes for Solaris, Cygwin, minGW, Windows Mobile,
Symbian, gcc, and C++Builder.
* Bug fixes and implementation clean-ups.
Changes for 1.3.0:
* New feature: death tests on Windows, Cygwin, and Mac.
* New feature: ability to use Google Test assertions in other testing
frameworks.
* New feature: ability to run disabled test via
--gtest_also_run_disabled_tests.
* New feature: the --help flag for printing the usage.
* New feature: access to Google Test flag values in user code.
* New feature: a script that packs Google Test into one .h and one
.cc file for easy deployment.
* New feature: support for distributing test functions to multiple
machines (requires support from the test runner).
* Bug fixes and implementation clean-ups.
Changes for 1.2.1:
* Compatibility fixes for Linux IA-64 and IBM z/OS.
* Added support for using Boost and other TR1 implementations.
* Changes to the build scripts to support upcoming release of Google C++
Mocking Framework.
* Added Makefile to the distribution package.
* Improved build instructions in README.
Changes for 1.2.0:
* New feature: value-parameterized tests.
* New feature: the ASSERT/EXPECT_(NON)FATAL_FAILURE(_ON_ALL_THREADS)
macros.
* Changed the XML report format to match JUnit/Ant's.
* Added tests to the Xcode project.
* Added scons/SConscript for building with SCons.
* Added src/gtest-all.cc for building Google Test from a single file.
* Fixed compatibility with Solaris and z/OS.
* Enabled running Python tests on systems with python 2.3 installed,
e.g. Mac OS X 10.4.
* Bug fixes.
Changes for 1.1.0:
* New feature: type-parameterized tests.
* New feature: exception assertions.
* New feature: printing elapsed time of tests.
* Improved the robustness of death tests.
* Added an Xcode project and samples.
* Adjusted the output format on Windows to be understandable by Visual Studio.
* Minor bug fixes.
Changes for 1.0.1:
* Added project files for Visual Studio 7.1.
* Fixed issues with compiling on Mac OS X.
* Fixed issues with compiling on Cygwin.
Changes for 1.0.0:
* Initial Open Source release of Google Test

@ -1,290 +1,31 @@
########################################################################
# CMake build script for Google Test.
#
# To run the tests for Google Test itself on Linux, use 'make test' or
# ctest. You can select which tests to run using 'ctest -R regex'.
# For more options, run 'ctest --help'.
cmake_minimum_required(VERSION 2.8.8)
# BUILD_SHARED_LIBS is a standard CMake variable, but we declare it here to
# make it prominent in the GUI.
option(BUILD_SHARED_LIBS "Build shared libraries (DLLs)." OFF)
# When other libraries are using a shared version of runtime libraries,
# Google Test also has to use one.
option(
gtest_force_shared_crt
"Use shared (DLL) run-time lib even when Google Test is built as static lib."
OFF)
option(gtest_build_tests "Build all of gtest's own tests." OFF)
option(gtest_build_samples "Build gtest's sample programs." OFF)
if (NOT WIN32)
option(gtest_disable_pthreads "Disable uses of pthreads in gtest." OFF)
if (CMAKE_VERSION VERSION_LESS "3.1")
add_definitions(-std=c++11)
else()
option(gtest_disable_pthreads "Disable uses of pthreads in gtest." ON)
endif(NOT WIN32)
option(
gtest_hide_internal_symbols
"Build gtest with internal symbols hidden in shared libraries."
OFF)
# Defines pre_project_set_up_hermetic_build() and set_up_hermetic_build().
include(cmake/hermetic_build.cmake OPTIONAL)
if (COMMAND pre_project_set_up_hermetic_build)
pre_project_set_up_hermetic_build()
endif()
########################################################################
#
# Project-wide settings
# Name of the project.
#
# CMake files in this project can refer to the root source directory
# as ${gtest_SOURCE_DIR} and to the root binary directory as
# ${gtest_BINARY_DIR}.
# Language "C" is required for find_package(Threads).
project(gtest CXX C)
cmake_minimum_required(VERSION 2.6.2)
if (COMMAND set_up_hermetic_build)
set_up_hermetic_build()
endif()
if (gtest_hide_internal_symbols)
set(CMAKE_CXX_VISIBILITY_PRESET hidden)
set(CMAKE_VISIBILITY_INLINES_HIDDEN 1)
endif()
# Define helper functions and macros used by Google Test.
include(cmake/internal_utils.cmake)
config_compiler_and_linker() # Defined in internal_utils.cmake.
# Where Google Test's .h files can be found.
include_directories(
${gtest_SOURCE_DIR}/include
${gtest_SOURCE_DIR})
# Where Google Test's libraries can be found.
link_directories(${gtest_BINARY_DIR}/src)
# Summary of tuple support for Microsoft Visual Studio:
# Compiler version(MS) version(cmake) Support
# ---------- ----------- -------------- -----------------------------
# <= VS 2010 <= 10 <= 1600 Use Google Tests's own tuple.
# VS 2012 11 1700 std::tr1::tuple + _VARIADIC_MAX=10
# VS 2013 12 1800 std::tr1::tuple
if (MSVC AND MSVC_VERSION EQUAL 1700)
add_definitions(/D _VARIADIC_MAX=10)
endif()
########################################################################
#
# Defines the gtest & gtest_main libraries. User tests should link
# with one of them.
# Google Test libraries. We build them using more strict warnings than what
# are used for other targets, to ensure that gtest can be compiled by a user
# aggressive about warnings.
cxx_library(gtest "${cxx_strict}" src/gtest-all.cc)
cxx_library(gtest_main "${cxx_strict}" src/gtest_main.cc)
target_link_libraries(gtest_main gtest)
# If the CMake version supports it, attach header directory information
# to the targets for when we are part of a parent build (ie being pulled
# in via add_subdirectory() rather than being a standalone build).
if (DEFINED CMAKE_VERSION AND NOT "${CMAKE_VERSION}" VERSION_LESS "2.8.11")
target_include_directories(gtest INTERFACE "${gtest_SOURCE_DIR}/include")
target_include_directories(gtest_main INTERFACE "${gtest_SOURCE_DIR}/include")
endif()
########################################################################
#
# Install rules
#install(TARGETS gtest gtest_main
# DESTINATION lib)
#install(DIRECTORY ${gtest_SOURCE_DIR}/include/gtest
# DESTINATION include)
########################################################################
#
# Samples on how to link user tests with gtest or gtest_main.
#
# They are not built by default. To build them, set the
# gtest_build_samples option to ON. You can do it by running ccmake
# or specifying the -Dgtest_build_samples=ON flag when running cmake.
if (gtest_build_samples)
cxx_executable(sample1_unittest samples gtest_main samples/sample1.cc)
cxx_executable(sample2_unittest samples gtest_main samples/sample2.cc)
cxx_executable(sample3_unittest samples gtest_main)
cxx_executable(sample4_unittest samples gtest_main samples/sample4.cc)
cxx_executable(sample5_unittest samples gtest_main samples/sample1.cc)
cxx_executable(sample6_unittest samples gtest_main)
cxx_executable(sample7_unittest samples gtest_main)
cxx_executable(sample8_unittest samples gtest_main)
cxx_executable(sample9_unittest samples gtest)
cxx_executable(sample10_unittest samples gtest)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
endif()
########################################################################
#
# Google Test's own tests.
#
# You can skip this section if you aren't interested in testing
# Google Test itself.
#
# The tests are not built by default. To build them, set the
# gtest_build_tests option to ON. You can do it by running ccmake
# or specifying the -Dgtest_build_tests=ON flag when running cmake.
if (gtest_build_tests)
# This must be set in the root directory for the tests to be run by
# 'make test' or ctest.
enable_testing()
############################################################
# C++ tests built with standard compiler flags.
cxx_test(gtest-death-test_test gtest_main)
cxx_test(gtest_environment_test gtest)
cxx_test(gtest-filepath_test gtest_main)
cxx_test(gtest-linked_ptr_test gtest_main)
cxx_test(gtest-listener_test gtest_main)
cxx_test(gtest_main_unittest gtest_main)
cxx_test(gtest-message_test gtest_main)
cxx_test(gtest_no_test_unittest gtest)
cxx_test(gtest-options_test gtest_main)
cxx_test(gtest-param-test_test gtest
test/gtest-param-test2_test.cc)
cxx_test(gtest-port_test gtest_main)
cxx_test(gtest_pred_impl_unittest gtest_main)
cxx_test(gtest_premature_exit_test gtest
test/gtest_premature_exit_test.cc)
cxx_test(gtest-printers_test gtest_main)
cxx_test(gtest_prod_test gtest_main
test/production.cc)
cxx_test(gtest_repeat_test gtest)
cxx_test(gtest_sole_header_test gtest_main)
cxx_test(gtest_stress_test gtest)
cxx_test(gtest-test-part_test gtest_main)
cxx_test(gtest_throw_on_failure_ex_test gtest)
cxx_test(gtest-typed-test_test gtest_main
test/gtest-typed-test2_test.cc)
cxx_test(gtest_unittest gtest_main)
cxx_test(gtest-unittest-api_test gtest)
############################################################
# C++ tests built with non-standard compiler flags.
# MSVC 7.1 does not support STL with exceptions disabled.
if (NOT MSVC OR MSVC_VERSION GREATER 1310)
cxx_library(gtest_no_exception "${cxx_no_exception}"
src/gtest-all.cc)
cxx_library(gtest_main_no_exception "${cxx_no_exception}"
src/gtest-all.cc src/gtest_main.cc)
endif()
cxx_library(gtest_main_no_rtti "${cxx_no_rtti}"
src/gtest-all.cc src/gtest_main.cc)
cxx_test_with_flags(gtest-death-test_ex_nocatch_test
"${cxx_exception} -DGTEST_ENABLE_CATCH_EXCEPTIONS_=0"
gtest test/gtest-death-test_ex_test.cc)
cxx_test_with_flags(gtest-death-test_ex_catch_test
"${cxx_exception} -DGTEST_ENABLE_CATCH_EXCEPTIONS_=1"
gtest test/gtest-death-test_ex_test.cc)
cxx_test_with_flags(gtest_no_rtti_unittest "${cxx_no_rtti}"
gtest_main_no_rtti test/gtest_unittest.cc)
cxx_shared_library(gtest_dll "${cxx_default}"
src/gtest-all.cc src/gtest_main.cc)
cxx_executable_with_flags(gtest_dll_test_ "${cxx_default}"
gtest_dll test/gtest_all_test.cc)
set_target_properties(gtest_dll_test_
PROPERTIES
COMPILE_DEFINITIONS "GTEST_LINKED_AS_SHARED_LIBRARY=1")
if (NOT MSVC OR MSVC_VERSION LESS 1600) # 1600 is Visual Studio 2010.
# Visual Studio 2010, 2012, and 2013 define symbols in std::tr1 that
# conflict with our own definitions. Therefore using our own tuple does not
# work on those compilers.
cxx_library(gtest_main_use_own_tuple "${cxx_use_own_tuple}"
src/gtest-all.cc src/gtest_main.cc)
cxx_test_with_flags(gtest-tuple_test "${cxx_use_own_tuple}"
gtest_main_use_own_tuple test/gtest-tuple_test.cc)
if (POLICY CMP0048)
cmake_policy(SET CMP0048 NEW)
endif (POLICY CMP0048)
cxx_test_with_flags(gtest_use_own_tuple_test "${cxx_use_own_tuple}"
gtest_main_use_own_tuple
test/gtest-param-test_test.cc test/gtest-param-test2_test.cc)
endif()
project(googletest-distribution)
set(GOOGLETEST_VERSION 1.9.0)
############################################################
# Python tests.
enable_testing()
cxx_executable(gtest_break_on_failure_unittest_ test gtest)
py_test(gtest_break_on_failure_unittest)
include(CMakeDependentOption)
include(GNUInstallDirs)
# Visual Studio .NET 2003 does not support STL with exceptions disabled.
if (NOT MSVC OR MSVC_VERSION GREATER 1310) # 1310 is Visual Studio .NET 2003
cxx_executable_with_flags(
gtest_catch_exceptions_no_ex_test_
"${cxx_no_exception}"
gtest_main_no_exception
test/gtest_catch_exceptions_test_.cc)
endif()
#Note that googlemock target already builds googletest
option(BUILD_GMOCK "Builds the googlemock subproject" ON)
option(INSTALL_GTEST "Enable installation of googletest. (Projects embedding googletest may want to turn this OFF.)" ON)
cxx_executable_with_flags(
gtest_catch_exceptions_ex_test_
"${cxx_exception}"
gtest_main
test/gtest_catch_exceptions_test_.cc)
py_test(gtest_catch_exceptions_test)
cxx_executable(gtest_color_test_ test gtest)
py_test(gtest_color_test)
cxx_executable(gtest_env_var_test_ test gtest)
py_test(gtest_env_var_test)
cxx_executable(gtest_filter_unittest_ test gtest)
py_test(gtest_filter_unittest)
cxx_executable(gtest_help_test_ test gtest_main)
py_test(gtest_help_test)
cxx_executable(gtest_list_tests_unittest_ test gtest)
py_test(gtest_list_tests_unittest)
cxx_executable(gtest_output_test_ test gtest)
py_test(gtest_output_test)
cxx_executable(gtest_shuffle_test_ test gtest)
py_test(gtest_shuffle_test)
# MSVC 7.1 does not support STL with exceptions disabled.
if (NOT MSVC OR MSVC_VERSION GREATER 1310)
cxx_executable(gtest_throw_on_failure_test_ test gtest_no_exception)
set_target_properties(gtest_throw_on_failure_test_
PROPERTIES
COMPILE_FLAGS "${cxx_no_exception}")
py_test(gtest_throw_on_failure_test)
endif()
cxx_executable(gtest_uninitialized_test_ test gtest)
py_test(gtest_uninitialized_test)
cxx_executable(gtest_xml_outfile1_test_ test gtest_main)
cxx_executable(gtest_xml_outfile2_test_ test gtest_main)
py_test(gtest_xml_outfiles_test)
cxx_executable(gtest_xml_output_unittest_ test gtest)
py_test(gtest_xml_output_unittest)
if(BUILD_GMOCK)
add_subdirectory( googlemock )
else()
add_subdirectory( googletest )
endif()

@ -0,0 +1,161 @@
# How to become a contributor and submit your own code
## Contributor License Agreements
We'd love to accept your patches! Before we can take them, we
have to jump a couple of legal hurdles.
Please fill out either the individual or corporate Contributor License Agreement
(CLA).
* If you are an individual writing original source code and you're sure you
own the intellectual property, then you'll need to sign an
[individual CLA](https://developers.google.com/open-source/cla/individual).
* If you work for a company that wants to allow you to contribute your work,
then you'll need to sign a
[corporate CLA](https://developers.google.com/open-source/cla/corporate).
Follow either of the two links above to access the appropriate CLA and
instructions for how to sign and return it. Once we receive it, we'll be able to
accept your pull requests.
## Are you a Googler?
If you are a Googler, you can either create an internal change or work on GitHub directly.
## Contributing A Patch
1. Submit an issue describing your proposed change to the
[issue tracker](https://github.com/google/googletest).
1. Please don't mix more than one logical change per submittal,
because it makes the history hard to follow. If you want to make a
change that doesn't have a corresponding issue in the issue
tracker, please create one.
1. Also, coordinate with team members that are listed on the issue in
question. This ensures that work isn't being duplicated and
communicating your plan early also generally leads to better
patches.
1. If your proposed change is accepted, and you haven't already done so, sign a
Contributor License Agreement (see details above).
1. Fork the desired repo, develop and test your code changes.
1. Ensure that your code adheres to the existing style in the sample to which
you are contributing.
1. Ensure that your code has an appropriate set of unit tests which all pass.
1. Submit a pull request.
## The Google Test and Google Mock Communities ##
The Google Test community exists primarily through the
[discussion group](http://groups.google.com/group/googletestframework)
and the GitHub repository.
Likewise, the Google Mock community exists primarily through their own
[discussion group](http://groups.google.com/group/googlemock).
You are definitely encouraged to contribute to the
discussion and you can also help us to keep the effectiveness of the
group high by following and promoting the guidelines listed here.
### Please Be Friendly ###
Showing courtesy and respect to others is a vital part of the Google
culture, and we strongly encourage everyone participating in Google
Test development to join us in accepting nothing less. Of course,
being courteous is not the same as failing to constructively disagree
with each other, but it does mean that we should be respectful of each
other when enumerating the 42 technical reasons that a particular
proposal may not be the best choice. There's never a reason to be
antagonistic or dismissive toward anyone who is sincerely trying to
contribute to a discussion.
Sure, C++ testing is serious business and all that, but it's also
a lot of fun. Let's keep it that way. Let's strive to be one of the
friendliest communities in all of open source.
As always, discuss Google Test in the official GoogleTest discussion group.
You don't have to actually submit code in order to sign up. Your participation
itself is a valuable contribution.
## Style
To keep the source consistent, readable, diffable and easy to merge,
we use a fairly rigid coding style, as defined by the [google-styleguide](https://github.com/google/styleguide) project. All patches will be expected
to conform to the style outlined [here](https://google.github.io/styleguide/cppguide.html).
Use [.clang-format](https://github.com/google/googletest/blob/master/.clang-format) to check your formatting
## Requirements for Contributors ###
If you plan to contribute a patch, you need to build Google Test,
Google Mock, and their own tests from a git checkout, which has
further requirements:
* [Python](https://www.python.org/) v2.3 or newer (for running some of
the tests and re-generating certain source files from templates)
* [CMake](https://cmake.org/) v2.6.4 or newer
* [GNU Build System](https://en.wikipedia.org/wiki/GNU_Build_System)
including automake (>= 1.9), autoconf (>= 2.59), and
libtool / libtoolize.
## Developing Google Test ##
This section discusses how to make your own changes to Google Test.
### Testing Google Test Itself ###
To make sure your changes work as intended and don't break existing
functionality, you'll want to compile and run Google Test's own tests.
For that you can use CMake:
mkdir mybuild
cd mybuild
cmake -Dgtest_build_tests=ON ${GTEST_DIR}
Make sure you have Python installed, as some of Google Test's tests
are written in Python. If the cmake command complains about not being
able to find Python (`Could NOT find PythonInterp (missing:
PYTHON_EXECUTABLE)`), try telling it explicitly where your Python
executable can be found:
cmake -DPYTHON_EXECUTABLE=path/to/python -Dgtest_build_tests=ON ${GTEST_DIR}
Next, you can build Google Test and all of its own tests. On \*nix,
this is usually done by 'make'. To run the tests, do
make test
All tests should pass.
### Regenerating Source Files ##
Some of Google Test's source files are generated from templates (not
in the C++ sense) using a script.
For example, the
file include/gtest/internal/gtest-type-util.h.pump is used to generate
gtest-type-util.h in the same directory.
You don't need to worry about regenerating the source files
unless you need to modify them. You would then modify the
corresponding `.pump` files and run the '[pump.py](googletest/scripts/pump.py)'
generator script. See the [Pump Manual](googletest/docs/PumpManual.md).
## Developing Google Mock ###
This section discusses how to make your own changes to Google Mock.
#### Testing Google Mock Itself ####
To make sure your changes work as intended and don't break existing
functionality, you'll want to compile and run Google Test's own tests.
For that you'll need Autotools. First, make sure you have followed
the instructions above to configure Google Mock.
Then, create a build output directory and enter it. Next,
${GMOCK_DIR}/configure # try --help for more info
Once you have successfully configured Google Mock, the build steps are
standard for GNU-style OSS packages.
make # Standard makefile following GNU conventions
make check # Builds and runs all tests - all should pass.
Note that when building your project against Google Mock, you are building
against Google Test as well. There is no need to configure Google Test
separately.

@ -1,310 +1,14 @@
# Automake file
## Process this file with automake to produce Makefile.in
ACLOCAL_AMFLAGS = -I m4
# Nonstandard package files for distribution
EXTRA_DIST = \
CHANGES \
CONTRIBUTORS \
LICENSE \
include/gtest/gtest-param-test.h.pump \
include/gtest/internal/gtest-param-util-generated.h.pump \
include/gtest/internal/gtest-tuple.h.pump \
include/gtest/internal/gtest-type-util.h.pump \
make/Makefile \
scripts/fuse_gtest_files.py \
scripts/gen_gtest_pred_impl.py \
scripts/pump.py \
scripts/test/Makefile
# gtest source files that we don't compile directly. They are
# #included by gtest-all.cc.
GTEST_SRC = \
src/gtest-death-test.cc \
src/gtest-filepath.cc \
src/gtest-internal-inl.h \
src/gtest-port.cc \
src/gtest-printers.cc \
src/gtest-test-part.cc \
src/gtest-typed-test.cc \
src/gtest.cc
EXTRA_DIST += $(GTEST_SRC)
# Sample files that we don't compile.
EXTRA_DIST += \
samples/prime_tables.h \
samples/sample2_unittest.cc \
samples/sample3_unittest.cc \
samples/sample4_unittest.cc \
samples/sample5_unittest.cc \
samples/sample6_unittest.cc \
samples/sample7_unittest.cc \
samples/sample8_unittest.cc \
samples/sample9_unittest.cc
AUTOMAKE_OPTIONS = foreign
# C++ test files that we don't compile directly.
EXTRA_DIST += \
test/gtest-death-test_ex_test.cc \
test/gtest-death-test_test.cc \
test/gtest-filepath_test.cc \
test/gtest-linked_ptr_test.cc \
test/gtest-listener_test.cc \
test/gtest-message_test.cc \
test/gtest-options_test.cc \
test/gtest-param-test2_test.cc \
test/gtest-param-test2_test.cc \
test/gtest-param-test_test.cc \
test/gtest-param-test_test.cc \
test/gtest-param-test_test.h \
test/gtest-port_test.cc \
test/gtest_premature_exit_test.cc \
test/gtest-printers_test.cc \
test/gtest-test-part_test.cc \
test/gtest-tuple_test.cc \
test/gtest-typed-test2_test.cc \
test/gtest-typed-test_test.cc \
test/gtest-typed-test_test.h \
test/gtest-unittest-api_test.cc \
test/gtest_break_on_failure_unittest_.cc \
test/gtest_catch_exceptions_test_.cc \
test/gtest_color_test_.cc \
test/gtest_env_var_test_.cc \
test/gtest_environment_test.cc \
test/gtest_filter_unittest_.cc \
test/gtest_help_test_.cc \
test/gtest_list_tests_unittest_.cc \
test/gtest_main_unittest.cc \
test/gtest_no_test_unittest.cc \
test/gtest_output_test_.cc \
test/gtest_pred_impl_unittest.cc \
test/gtest_prod_test.cc \
test/gtest_repeat_test.cc \
test/gtest_shuffle_test_.cc \
test/gtest_sole_header_test.cc \
test/gtest_stress_test.cc \
test/gtest_throw_on_failure_ex_test.cc \
test/gtest_throw_on_failure_test_.cc \
test/gtest_uninitialized_test_.cc \
test/gtest_unittest.cc \
test/gtest_unittest.cc \
test/gtest_xml_outfile1_test_.cc \
test/gtest_xml_outfile2_test_.cc \
test/gtest_xml_output_unittest_.cc \
test/production.cc \
test/production.h
# Build . before src so that our all-local and clean-local hooks kicks in at
# the right time.
SUBDIRS = googletest googlemock
# Python tests that we don't run.
EXTRA_DIST += \
test/gtest_break_on_failure_unittest.py \
test/gtest_catch_exceptions_test.py \
test/gtest_color_test.py \
test/gtest_env_var_test.py \
test/gtest_filter_unittest.py \
test/gtest_help_test.py \
test/gtest_list_tests_unittest.py \
test/gtest_output_test.py \
test/gtest_output_test_golden_lin.txt \
test/gtest_shuffle_test.py \
test/gtest_test_utils.py \
test/gtest_throw_on_failure_test.py \
test/gtest_uninitialized_test.py \
test/gtest_xml_outfiles_test.py \
test/gtest_xml_output_unittest.py \
test/gtest_xml_test_utils.py
# CMake script
EXTRA_DIST += \
EXTRA_DIST = \
BUILD.bazel \
CMakeLists.txt \
cmake/internal_utils.cmake
# MSVC project files
EXTRA_DIST += \
msvc/gtest-md.sln \
msvc/gtest-md.vcproj \
msvc/gtest.sln \
msvc/gtest.vcproj \
msvc/gtest_main-md.vcproj \
msvc/gtest_main.vcproj \
msvc/gtest_prod_test-md.vcproj \
msvc/gtest_prod_test.vcproj \
msvc/gtest_unittest-md.vcproj \
msvc/gtest_unittest.vcproj
# xcode project files
EXTRA_DIST += \
xcode/Config/DebugProject.xcconfig \
xcode/Config/FrameworkTarget.xcconfig \
xcode/Config/General.xcconfig \
xcode/Config/ReleaseProject.xcconfig \
xcode/Config/StaticLibraryTarget.xcconfig \
xcode/Config/TestTarget.xcconfig \
xcode/Resources/Info.plist \
xcode/Scripts/runtests.sh \
xcode/Scripts/versiongenerate.py \
xcode/gtest.xcodeproj/project.pbxproj
# xcode sample files
EXTRA_DIST += \
xcode/Samples/FrameworkSample/Info.plist \
xcode/Samples/FrameworkSample/WidgetFramework.xcodeproj/project.pbxproj \
xcode/Samples/FrameworkSample/runtests.sh \
xcode/Samples/FrameworkSample/widget.cc \
xcode/Samples/FrameworkSample/widget.h \
xcode/Samples/FrameworkSample/widget_test.cc
# C++Builder project files
EXTRA_DIST += \
codegear/gtest.cbproj \
codegear/gtest.groupproj \
codegear/gtest_all.cc \
codegear/gtest_link.cc \
codegear/gtest_main.cbproj \
codegear/gtest_unittest.cbproj
# Distribute and install M4 macro
m4datadir = $(datadir)/aclocal
m4data_DATA = m4/gtest.m4
EXTRA_DIST += $(m4data_DATA)
# We define the global AM_CPPFLAGS as everything we compile includes from these
# directories.
AM_CPPFLAGS = -I$(srcdir) -I$(srcdir)/include
# Modifies compiler and linker flags for pthreads compatibility.
if HAVE_PTHREADS
AM_CXXFLAGS = @PTHREAD_CFLAGS@ -DGTEST_HAS_PTHREAD=1
AM_LIBS = @PTHREAD_LIBS@
else
AM_CXXFLAGS = -DGTEST_HAS_PTHREAD=0
endif
# Build rules for libraries.
lib_LTLIBRARIES = lib/libgtest.la lib/libgtest_main.la
lib_libgtest_la_SOURCES = src/gtest-all.cc
pkginclude_HEADERS = \
include/gtest/gtest-death-test.h \
include/gtest/gtest-message.h \
include/gtest/gtest-param-test.h \
include/gtest/gtest-printers.h \
include/gtest/gtest-spi.h \
include/gtest/gtest-test-part.h \
include/gtest/gtest-typed-test.h \
include/gtest/gtest.h \
include/gtest/gtest_pred_impl.h \
include/gtest/gtest_prod.h
pkginclude_internaldir = $(pkgincludedir)/internal
pkginclude_internal_HEADERS = \
include/gtest/internal/gtest-death-test-internal.h \
include/gtest/internal/gtest-filepath.h \
include/gtest/internal/gtest-internal.h \
include/gtest/internal/gtest-linked_ptr.h \
include/gtest/internal/gtest-param-util-generated.h \
include/gtest/internal/gtest-param-util.h \
include/gtest/internal/gtest-port.h \
include/gtest/internal/gtest-port-arch.h \
include/gtest/internal/gtest-string.h \
include/gtest/internal/gtest-tuple.h \
include/gtest/internal/gtest-type-util.h \
include/gtest/internal/custom/gtest.h \
include/gtest/internal/custom/gtest-port.h \
include/gtest/internal/custom/gtest-printers.h
lib_libgtest_main_la_SOURCES = src/gtest_main.cc
lib_libgtest_main_la_LIBADD = lib/libgtest.la
# Bulid rules for samples and tests. Automake's naming for some of
# these variables isn't terribly obvious, so this is a brief
# reference:
#
# TESTS -- Programs run automatically by "make check"
# check_PROGRAMS -- Programs built by "make check" but not necessarily run
noinst_LTLIBRARIES = samples/libsamples.la
samples_libsamples_la_SOURCES = \
samples/sample1.cc \
samples/sample1.h \
samples/sample2.cc \
samples/sample2.h \
samples/sample3-inl.h \
samples/sample4.cc \
samples/sample4.h
TESTS=
TESTS_ENVIRONMENT = GTEST_SOURCE_DIR="$(srcdir)/test" \
GTEST_BUILD_DIR="$(top_builddir)/test"
check_PROGRAMS=
# A simple sample on using gtest.
TESTS += samples/sample1_unittest
check_PROGRAMS += samples/sample1_unittest
samples_sample1_unittest_SOURCES = samples/sample1_unittest.cc
samples_sample1_unittest_LDADD = lib/libgtest_main.la \
lib/libgtest.la \
samples/libsamples.la
# Another sample. It also verifies that libgtest works.
TESTS += samples/sample10_unittest
check_PROGRAMS += samples/sample10_unittest
samples_sample10_unittest_SOURCES = samples/sample10_unittest.cc
samples_sample10_unittest_LDADD = lib/libgtest.la
# This tests most constructs of gtest and verifies that libgtest_main
# and libgtest work.
TESTS += test/gtest_all_test
check_PROGRAMS += test/gtest_all_test
test_gtest_all_test_SOURCES = test/gtest_all_test.cc
test_gtest_all_test_LDADD = lib/libgtest_main.la \
lib/libgtest.la
# Tests that fused gtest files compile and work.
FUSED_GTEST_SRC = \
fused-src/gtest/gtest-all.cc \
fused-src/gtest/gtest.h \
fused-src/gtest/gtest_main.cc
if HAVE_PYTHON
TESTS += test/fused_gtest_test
check_PROGRAMS += test/fused_gtest_test
test_fused_gtest_test_SOURCES = $(FUSED_GTEST_SRC) \
samples/sample1.cc samples/sample1_unittest.cc
test_fused_gtest_test_CPPFLAGS = -I"$(srcdir)/fused-src"
# Build rules for putting fused Google Test files into the distribution
# package. The user can also create those files by manually running
# scripts/fuse_gtest_files.py.
$(test_fused_gtest_test_SOURCES): fused-gtest
fused-gtest: $(pkginclude_HEADERS) $(pkginclude_internal_HEADERS) \
$(GTEST_SRC) src/gtest-all.cc src/gtest_main.cc \
scripts/fuse_gtest_files.py
mkdir -p "$(srcdir)/fused-src"
chmod -R u+w "$(srcdir)/fused-src"
rm -f "$(srcdir)/fused-src/gtest/gtest-all.cc"
rm -f "$(srcdir)/fused-src/gtest/gtest.h"
"$(srcdir)/scripts/fuse_gtest_files.py" "$(srcdir)/fused-src"
cp -f "$(srcdir)/src/gtest_main.cc" "$(srcdir)/fused-src/gtest/"
maintainer-clean-local:
rm -rf "$(srcdir)/fused-src"
endif
# Death tests may produce core dumps in the build directory. In case
# this happens, clean them to keep distcleancheck happy.
CLEANFILES = core
# Disables 'make install' as installing a compiled version of Google
# Test can lead to undefined behavior due to violation of the
# One-Definition Rule.
install-exec-local:
echo "'make install' is dangerous and not supported. Instead, see README for how to integrate Google Test into your build system."
false
install-data-local:
echo "'make install' is dangerous and not supported. Instead, see README for how to integrate Google Test into your build system."
false
README.md \
WORKSPACE

@ -1,280 +1,132 @@
### Generic Build Instructions ###
# Google Test #
#### Setup ####
[![Build Status](https://api.travis-ci.org/google/googletest.svg?branch=master)](https://travis-ci.org/google/googletest)
[![Build status](https://ci.appveyor.com/api/projects/status/4o38plt0xbo1ubc8/branch/master?svg=true)](https://ci.appveyor.com/project/GoogleTestAppVeyor/googletest/branch/master)
To build Google Test and your tests that use it, you need to tell your
build system where to find its headers and source files. The exact
way to do it depends on which build system you use, and is usually
straightforward.
**PR FREEZE COMING SOON**
#### Build ####
We are working on a large refactoring that would make it hard to accept external PRs. *Really Soon Now* we will not be accepting new PRs until the refactoring has been completed.
Suppose you put Google Test in directory `${GTEST_DIR}`. To build it,
create a library build target (or a project as called by Visual Studio
and Xcode) to compile
**Future Plans**:
* 1.8.x Release - [the 1.8.x](https://github.com/google/googletest/releases/tag/release-1.8.1) is the last release that works with pre-C++11 compilers. The 1.8.x will not accept any requests for any new features and any bugfix requests will only be accepted if proven "critical"
* Post 1.8.x - work to improve/cleanup/pay technical debt. When this work is completed there will be a 1.9.x tagged release
* Post 1.9.x googletest will follow [Abseil Live at Head philosophy](https://abseil.io/about/philosophy)
${GTEST_DIR}/src/gtest-all.cc
with `${GTEST_DIR}/include` in the system header search path and `${GTEST_DIR}`
in the normal header search path. Assuming a Linux-like system and gcc,
something like the following will do:
Welcome to **Google Test**, Google's C++ test framework!
g++ -isystem ${GTEST_DIR}/include -I${GTEST_DIR} \
-pthread -c ${GTEST_DIR}/src/gtest-all.cc
ar -rv libgtest.a gtest-all.o
This repository is a merger of the formerly separate GoogleTest and
GoogleMock projects. These were so closely related that it makes sense to
maintain and release them together.
(We need `-pthread` as Google Test uses threads.)
Please subscribe to the mailing list at googletestframework@googlegroups.com for questions, discussions, and development.
There is also an IRC channel on [OFTC](https://webchat.oftc.net/) (irc.oftc.net) #gtest available.
Next, you should compile your test source file with
`${GTEST_DIR}/include` in the system header search path, and link it
with gtest and any other necessary libraries:
Getting started information for **Google Test** is available in the
[Google Test Primer](googletest/docs/primer.md) documentation.
g++ -isystem ${GTEST_DIR}/include -pthread path/to/your_test.cc libgtest.a \
-o your_test
**Google Mock** is an extension to Google Test for writing and using C++ mock
classes. See the separate [Google Mock documentation](googlemock/README.md).
As an example, the make/ directory contains a Makefile that you can
use to build Google Test on systems where GNU make is available
(e.g. Linux, Mac OS X, and Cygwin). It doesn't try to build Google
Test's own tests. Instead, it just builds the Google Test library and
a sample test. You can use it as a starting point for your own build
script.
More detailed documentation for googletest (including build instructions) are
in its interior [googletest/README.md](googletest/README.md) file.
If the default settings are correct for your environment, the
following commands should succeed:
## Features ##
cd ${GTEST_DIR}/make
make
./sample1_unittest
* An [xUnit](https://en.wikipedia.org/wiki/XUnit) test framework.
* Test discovery.
* A rich set of assertions.
* User-defined assertions.
* Death tests.
* Fatal and non-fatal failures.
* Value-parameterized tests.
* Type-parameterized tests.
* Various options for running the tests.
* XML test report generation.
If you see errors, try to tweak the contents of `make/Makefile` to make
them go away. There are instructions in `make/Makefile` on how to do
it.
## Platforms ##
### Using CMake ###
Google test has been used on a variety of platforms:
Google Test comes with a CMake build script (
[CMakeLists.txt](CMakeLists.txt)) that can be used on a wide range of platforms ("C" stands for
cross-platform.). If you don't have CMake installed already, you can
download it for free from <http://www.cmake.org/>.
* Linux
* Mac OS X
* Windows
* Cygwin
* MinGW
* Windows Mobile
* Symbian
CMake works by generating native makefiles or build projects that can
be used in the compiler environment of your choice. The typical
workflow starts with:
## Who Is Using Google Test? ##
mkdir mybuild # Create a directory to hold the build output.
cd mybuild
cmake ${GTEST_DIR} # Generate native build scripts.
In addition to many internal projects at Google, Google Test is also used by
the following notable projects:
If you want to build Google Test's samples, you should replace the
last command with
* The [Chromium projects](http://www.chromium.org/) (behind the Chrome
browser and Chrome OS).
* The [LLVM](http://llvm.org/) compiler.
* [Protocol Buffers](https://github.com/google/protobuf), Google's data
interchange format.
* The [OpenCV](http://opencv.org/) computer vision library.
* [tiny-dnn](https://github.com/tiny-dnn/tiny-dnn): header only, dependency-free deep learning framework in C++11.
cmake -Dgtest_build_samples=ON ${GTEST_DIR}
## Related Open Source Projects ##
If you are on a \*nix system, you should now see a Makefile in the
current directory. Just type 'make' to build gtest.
[GTest Runner](https://github.com/nholthaus/gtest-runner) is a Qt5 based automated test-runner and Graphical User Interface with powerful features for Windows and Linux platforms.
If you use Windows and have Visual Studio installed, a `gtest.sln` file
and several `.vcproj` files will be created. You can then build them
using Visual Studio.
[Google Test UI](https://github.com/ospector/gtest-gbar) is test runner that runs
your test binary, allows you to track its progress via a progress bar, and
displays a list of test failures. Clicking on one shows failure text. Google
Test UI is written in C#.
On Mac OS X with Xcode installed, a `.xcodeproj` file will be generated.
[GTest TAP Listener](https://github.com/kinow/gtest-tap-listener) is an event
listener for Google Test that implements the
[TAP protocol](https://en.wikipedia.org/wiki/Test_Anything_Protocol) for test
result output. If your test runner understands TAP, you may find it useful.
### Legacy Build Scripts ###
[gtest-parallel](https://github.com/google/gtest-parallel) is a test runner that
runs tests from your binary in parallel to provide significant speed-up.
Before settling on CMake, we have been providing hand-maintained build
projects/scripts for Visual Studio, Xcode, and Autotools. While we
continue to provide them for convenience, they are not actively
maintained any more. We highly recommend that you follow the
instructions in the previous two sections to integrate Google Test
with your existing build system.
[GoogleTest Adapter](https://marketplace.visualstudio.com/items?itemName=DavidSchuldenfrei.gtest-adapter) is a VS Code extension allowing to view Google Tests in a tree view, and run/debug your tests.
If you still need to use the legacy build scripts, here's how:
## Requirements ##
The msvc\ folder contains two solutions with Visual C++ projects.
Open the `gtest.sln` or `gtest-md.sln` file using Visual Studio, and you
are ready to build Google Test the same way you build any Visual
Studio project. Files that have names ending with -md use DLL
versions of Microsoft runtime libraries (the /MD or the /MDd compiler
option). Files without that suffix use static versions of the runtime
libraries (the /MT or the /MTd option). Please note that one must use
the same option to compile both gtest and the test code. If you use
Visual Studio 2005 or above, we recommend the -md version as /MD is
the default for new projects in these versions of Visual Studio.
Google Test is designed to have fairly minimal requirements to build
and use with your projects, but there are some. Currently, we support
Linux, Windows, Mac OS X, and Cygwin. We will also make our best
effort to support other platforms (e.g. Solaris, AIX, and z/OS).
However, since core members of the Google Test project have no access
to these platforms, Google Test may have outstanding issues there. If
you notice any problems on your platform, please notify
[googletestframework@googlegroups.com](https://groups.google.com/forum/#!forum/googletestframework). Patches for fixing them are
even more welcome!
On Mac OS X, open the `gtest.xcodeproj` in the `xcode/` folder using
Xcode. Build the "gtest" target. The universal binary framework will
end up in your selected build directory (selected in the Xcode
"Preferences..." -> "Building" pane and defaults to xcode/build).
Alternatively, at the command line, enter:
### Linux Requirements ###
xcodebuild
These are the base requirements to build and use Google Test from a source
package (as described below):
This will build the "Release" configuration of gtest.framework in your
default build location. See the "xcodebuild" man page for more
information about building different configurations and building in
different locations.
* GNU-compatible Make or gmake
* POSIX-standard shell
* POSIX(-2) Regular Expressions (regex.h)
* A C++11-standard-compliant compiler
If you wish to use the Google Test Xcode project with Xcode 4.x and
above, you need to either:
### Windows Requirements ###
* update the SDK configuration options in xcode/Config/General.xconfig.
Comment options `SDKROOT`, `MACOS_DEPLOYMENT_TARGET`, and `GCC_VERSION`. If
you choose this route you lose the ability to target earlier versions
of MacOS X.
* Install an SDK for an earlier version. This doesn't appear to be
supported by Apple, but has been reported to work
(http://stackoverflow.com/questions/5378518).
* Microsoft Visual C++ 2015 or newer
### Tweaking Google Test ###
### Cygwin Requirements ###
Google Test can be used in diverse environments. The default
configuration may not work (or may not work well) out of the box in
some environments. However, you can easily tweak Google Test by
defining control macros on the compiler command line. Generally,
these macros are named like `GTEST_XYZ` and you define them to either 1
or 0 to enable or disable a certain feature.
* Cygwin v1.5.25-14 or newer
We list the most frequently used macros below. For a complete list,
see file [include/gtest/internal/gtest-port.h](include/gtest/internal/gtest-port.h).
### Mac OS X Requirements ###
### Choosing a TR1 Tuple Library ###
* Mac OS X v10.4 Tiger or newer
* Xcode Developer Tools
Some Google Test features require the C++ Technical Report 1 (TR1)
tuple library, which is not yet available with all compilers. The
good news is that Google Test implements a subset of TR1 tuple that's
enough for its own need, and will automatically use this when the
compiler doesn't provide TR1 tuple.
## Contributing change
Usually you don't need to care about which tuple library Google Test
uses. However, if your project already uses TR1 tuple, you need to
tell Google Test to use the same TR1 tuple library the rest of your
project uses, or the two tuple implementations will clash. To do
that, add
Please read the [`CONTRIBUTING.md`](CONTRIBUTING.md) for details on
how to contribute to this project.
-DGTEST_USE_OWN_TR1_TUPLE=0
to the compiler flags while compiling Google Test and your tests. If
you want to force Google Test to use its own tuple library, just add
-DGTEST_USE_OWN_TR1_TUPLE=1
to the compiler flags instead.
If you don't want Google Test to use tuple at all, add
-DGTEST_HAS_TR1_TUPLE=0
and all features using tuple will be disabled.
### Multi-threaded Tests ###
Google Test is thread-safe where the pthread library is available.
After `#include "gtest/gtest.h"`, you can check the `GTEST_IS_THREADSAFE`
macro to see whether this is the case (yes if the macro is `#defined` to
1, no if it's undefined.).
If Google Test doesn't correctly detect whether pthread is available
in your environment, you can force it with
-DGTEST_HAS_PTHREAD=1
or
-DGTEST_HAS_PTHREAD=0
When Google Test uses pthread, you may need to add flags to your
compiler and/or linker to select the pthread library, or you'll get
link errors. If you use the CMake script or the deprecated Autotools
script, this is taken care of for you. If you use your own build
script, you'll need to read your compiler and linker's manual to
figure out what flags to add.
### As a Shared Library (DLL) ###
Google Test is compact, so most users can build and link it as a
static library for the simplicity. You can choose to use Google Test
as a shared library (known as a DLL on Windows) if you prefer.
To compile *gtest* as a shared library, add
-DGTEST_CREATE_SHARED_LIBRARY=1
to the compiler flags. You'll also need to tell the linker to produce
a shared library instead - consult your linker's manual for how to do
it.
To compile your *tests* that use the gtest shared library, add
-DGTEST_LINKED_AS_SHARED_LIBRARY=1
to the compiler flags.
Note: while the above steps aren't technically necessary today when
using some compilers (e.g. GCC), they may become necessary in the
future, if we decide to improve the speed of loading the library (see
<http://gcc.gnu.org/wiki/Visibility> for details). Therefore you are
recommended to always add the above flags when using Google Test as a
shared library. Otherwise a future release of Google Test may break
your build script.
### Avoiding Macro Name Clashes ###
In C++, macros don't obey namespaces. Therefore two libraries that
both define a macro of the same name will clash if you `#include` both
definitions. In case a Google Test macro clashes with another
library, you can force Google Test to rename its macro to avoid the
conflict.
Specifically, if both Google Test and some other code define macro
FOO, you can add
-DGTEST_DONT_DEFINE_FOO=1
to the compiler flags to tell Google Test to change the macro's name
from `FOO` to `GTEST_FOO`. Currently `FOO` can be `FAIL`, `SUCCEED`,
or `TEST`. For example, with `-DGTEST_DONT_DEFINE_TEST=1`, you'll
need to write
GTEST_TEST(SomeTest, DoesThis) { ... }
instead of
TEST(SomeTest, DoesThis) { ... }
in order to define a test.
## Developing Google Test ##
This section discusses how to make your own changes to Google Test.
### Testing Google Test Itself ###
To make sure your changes work as intended and don't break existing
functionality, you'll want to compile and run Google Test's own tests.
For that you can use CMake:
mkdir mybuild
cd mybuild
cmake -Dgtest_build_tests=ON ${GTEST_DIR}
Make sure you have Python installed, as some of Google Test's tests
are written in Python. If the cmake command complains about not being
able to find Python (`Could NOT find PythonInterp (missing:
PYTHON_EXECUTABLE)`), try telling it explicitly where your Python
executable can be found:
cmake -DPYTHON_EXECUTABLE=path/to/python -Dgtest_build_tests=ON ${GTEST_DIR}
Next, you can build Google Test and all of its own tests. On \*nix,
this is usually done by 'make'. To run the tests, do
make test
All tests should pass.
Normally you don't need to worry about regenerating the source files,
unless you need to modify them. In that case, you should modify the
corresponding .pump files instead and run the pump.py Python script to
regenerate them. You can find pump.py in the [scripts/](scripts/) directory.
Read the [Pump manual](docs/PumpManual.md) for how to use it.
Happy testing!

@ -0,0 +1,10 @@
workspace(name = "com_google_googletest")
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
# Abseil
http_archive(
name = "com_google_absl",
urls = ["https://github.com/abseil/abseil-cpp/archive/master.zip"],
strip_prefix = "abseil-cpp-master",
)

@ -0,0 +1,103 @@
version: '{build}'
os: Visual Studio 2015
environment:
matrix:
- compiler: msvc-15-seh
generator: "Visual Studio 15 2017"
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
- compiler: msvc-15-seh
generator: "Visual Studio 15 2017 Win64"
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
enabled_on_pr: yes
- compiler: msvc-14-seh
generator: "Visual Studio 14 2015"
enabled_on_pr: yes
- compiler: msvc-14-seh
generator: "Visual Studio 14 2015 Win64"
- compiler: gcc-6.3.0-posix
generator: "MinGW Makefiles"
cxx_path: 'C:\mingw-w64\i686-6.3.0-posix-dwarf-rt_v5-rev1\mingw32\bin'
enabled_on_pr: yes
configuration:
- Debug
build:
verbosity: minimal
install:
- ps: |
Write-Output "Compiler: $env:compiler"
Write-Output "Generator: $env:generator"
Write-Output "Env:Configuation: $env:configuration"
Write-Output "Env: $env"
if (-not (Test-Path env:APPVEYOR_PULL_REQUEST_NUMBER)) {
Write-Output "This is *NOT* a pull request build"
} else {
Write-Output "This is a pull request build"
if (-not (Test-Path env:enabled_on_pr) -or $env:enabled_on_pr -ne "yes") {
Write-Output "PR builds are *NOT* explicitly enabled"
}
}
# git bash conflicts with MinGW makefiles
if ($env:generator -eq "MinGW Makefiles") {
$env:path = $env:path.replace("C:\Program Files\Git\usr\bin;", "")
if ($env:cxx_path -ne "") {
$env:path += ";$env:cxx_path"
}
}
build_script:
- ps: |
# Only enable some builds for pull requests, the AppVeyor queue is too long.
if ((Test-Path env:APPVEYOR_PULL_REQUEST_NUMBER) -And (-not (Test-Path env:enabled_on_pr) -or $env:enabled_on_pr -ne "yes")) {
return
}
md _build -Force | Out-Null
cd _build
$conf = if ($env:generator -eq "MinGW Makefiles") {"-DCMAKE_BUILD_TYPE=$env:configuration"} else {"-DCMAKE_CONFIGURATION_TYPES=Debug;Release"}
# Disable test for MinGW (gtest tests fail, gmock tests can not build)
$gtest_build_tests = if ($env:generator -eq "MinGW Makefiles") {"-Dgtest_build_tests=OFF"} else {"-Dgtest_build_tests=ON"}
$gmock_build_tests = if ($env:generator -eq "MinGW Makefiles") {"-Dgmock_build_tests=OFF"} else {"-Dgmock_build_tests=ON"}
& cmake -G "$env:generator" $conf -Dgtest_build_samples=ON $gtest_build_tests $gmock_build_tests ..
if ($LastExitCode -ne 0) {
throw "Exec: $ErrorMessage"
}
$cmake_parallel = if ($env:generator -eq "MinGW Makefiles") {"-j2"} else {"/m"}
& cmake --build . --config $env:configuration -- $cmake_parallel
if ($LastExitCode -ne 0) {
throw "Exec: $ErrorMessage"
}
skip_commits:
files:
- '**/*.md'
test_script:
- ps: |
# Only enable some builds for pull requests, the AppVeyor queue is too long.
if ((Test-Path env:APPVEYOR_PULL_REQUEST_NUMBER) -And (-not (Test-Path env:enabled_on_pr) -or $env:enabled_on_pr -ne "yes")) {
return
}
if ($env:generator -eq "MinGW Makefiles") {
return # No test available for MinGW
}
& ctest -C $env:configuration --timeout 600 --output-on-failure
if ($LastExitCode -ne 0) {
throw "Exec: $ErrorMessage"
}
artifacts:
- path: '_build/CMakeFiles/*.log'
name: logs
- path: '_build/Testing/**/*.xml'
name: test_results

@ -0,0 +1,44 @@
#!/usr/bin/env bash
# Copyright 2017 Google Inc.
# All Rights Reserved.
#
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
set -e
. ci/get-nprocessors.sh
# Create the configuration script
autoreconf -i
# Run in a subdirectory to keep the sources clean
mkdir build || true
cd build
../configure
make -j ${NPROCESSORS:-2}

@ -0,0 +1,36 @@
#!/usr/bin/env bash
# Copyright 2017 Google Inc.
# All Rights Reserved.
#
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
set -e
bazel build --curses=no //...:all
bazel test --curses=no //...:all
bazel test --curses=no //...:all --define absl=1

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save