Compare commits
39 Commits
21 changed files with 2092 additions and 287 deletions
@ -1,3 +1,5 @@ |
|||
*.o |
|||
dnsseed* |
|||
dnsstats.log |
|||
*.sw? |
|||
core |
|||
|
@ -1,4 +1,4 @@ |
|||
# The Hush Developers |
|||
|
|||
Duke Leto https://git.hush.is/duke |
|||
|
|||
Jah Way https://git.hush.is/jahway603 |
|||
|
@ -1,13 +1,71 @@ |
|||
# Copyright (c) 2018-2021 The Hush developers
|
|||
# Distributed under the GPLv3 software license, see the accompanying
|
|||
# file LICENSE or https://www.gnu.org/licenses/gpl-3.0.en.html
|
|||
CXX = g++ |
|||
CXXFLAGS = -O3 -g0 -march=native |
|||
LDFLAGS = $(CXXFLAGS) |
|||
CC = gcc |
|||
|
|||
dnsseed: dns.o bitcoin.o netbase.o protocol.o db.o main.o util.o |
|||
g++ -pthread $(LDFLAGS) -o dnsseed dns.o bitcoin.o netbase.o protocol.o db.o main.o util.o -lcrypto |
|||
$wolfssl_version=4.6.0 |
|||
# set to sha256sum of dnsseed binary release
|
|||
#$compiled_dnsseed_sha256sum=4a6ef85876712ae2a0018401de2485da4196ed1926a094ef2eea969dfd121076
|
|||
|
|||
%.o: %.cpp bitcoin.h netbase.h protocol.h db.h serialize.h uint256.h util.h coin.h |
|||
g++ -std=c++11 -pthread $(CXXFLAGS) -Wall -Wno-unused -Wno-sign-compare -Wno-reorder -Wno-comment -c -o $@ $< |
|||
## TODO: this is from the Hush build system but the hush-seeder build system doesn't know to look for this stuff
|
|||
## should be used in build_wolfssl.sh
|
|||
define $(package)_set_vars |
|||
$(package)_config_env=AR="$($(package)_ar)" RANLIB="$($(package)_ranlib)" CC="$($(package)_cc)" |
|||
$(package)_config_opts=--prefix=$(host_prefix) |
|||
$(package)_config_opts+=--host=$(host) |
|||
$(package)_config_opts+=--enable-debug |
|||
$(package)_config_opts+=--enable-tls13 |
|||
$(package)_config_opts+=--enable-ecc |
|||
$(package)_config_opts+=--enable-static |
|||
$(package)_config_opts+=--enable-sha3 |
|||
$(package)_config_opts+=--enable-sha512 |
|||
$(package)_config_opts+=--disable-shared |
|||
$(package)_config_opts+=--disable-examples |
|||
$(package)_config_opts+=--disable-crypttests |
|||
$(package)_config_opts+=--enable-keygen |
|||
$(package)_config_opts+=--enable-certgen |
|||
$(package)_config_opts+=--enable-bigcache |
|||
# TODO: can we reduce down to only the normal openssl compat, without these options?
|
|||
$(package)_config_opts+=--enable-opensslall |
|||
$(package)_config_opts+=--enable-opensslextra |
|||
$(package)_config_opts+=C_EXTRA_FLAGS="-DSPEAK_AND_TRANSACT_FREELY" |
|||
|
|||
endef |
|||
|
|||
.phony: |
|||
echo "Starting to compile HUSH DNS Seeder... get ready pup" |
|||
echo "==> Building Pre-req: wolfssl..." && ./build_wolfssl.sh |
|||
|
|||
dnsseed: .phony dns.o bitcoin.o netbase.o protocol.o db.o main.o util.o tlsmanager.o cleanse.o |
|||
g++ -Iwolfssl/ -pthread $(LDFLAGS) -o dnsseed dns.o bitcoin.o netbase.o protocol.o db.o main.o util.o tlsmanager.o -lcrypto |
|||
|
|||
%.o: %.cpp bitcoin.h netbase.h protocol.h db.h serialize.h uint256.h util.h coin.h tlsmanager.h zeroafterfree.h cleanse.h |
|||
$(CXX) -Iwolfssl/ -std=c++11 -pthread $(CXXFLAGS) -Wall -Wno-unused -Wno-sign-compare -Wno-reorder -Wno-comment -c -o $@ $< |
|||
|
|||
dns.o: dns.c |
|||
gcc -pthread -std=c99 $(CXXFLAGS) dns.c -Wall -c -o dns.o |
|||
$(CC) -pthread -std=c99 $(CXXFLAGS) dns.c -Wall -c -o dns.o |
|||
|
|||
%.o: %.cpp |
|||
|
|||
#sha256check:
|
|||
# ./validate_sha256sum dnsseed $($compiled_dnsseed_sha256sum)
|
|||
|
|||
# will install symlink /usr/bin/dnsseed to /opt/hush-seeder/dnsseed
|
|||
install: |
|||
echo "Installing Hush DNS Seeder on your system" |
|||
sudo mkdir /opt/hush-seeder |
|||
sudo cp dnsseed /opt/hush-seeder/ |
|||
cd /usr/bin && sudo ln -s /opt/hush-seeder/dnsseed |
|||
echo "DONE! Thanks for being a great Hush puppy!" |
|||
|
|||
uninstall: |
|||
echo "Removing Hush DNS Seeder on your system..." |
|||
sudo rm /usr/bin/dnsseed |
|||
sudo rm -rf /opt/hush-seeder |
|||
echo "Seeder is removed... hope you come back for more fun." |
|||
|
|||
clean: |
|||
rm -rf wolfssl* v$(wolfssl_version)*.tar.gz* *.o |
|||
|
@ -0,0 +1,25 @@ |
|||
#!/usr/bin/env bash |
|||
# Copyright (c) 2016-2021 The Hush developers |
|||
# Distributed under the GPLv3 software license, see the accompanying |
|||
# file LICENSE or https://www.gnu.org/licenses/gpl-3.0.en.html |
|||
|
|||
wolfssl_version=4.6.0 |
|||
wolfssl_download_path=https://github.com/wolfSSL/wolfssl/archive |
|||
wolfssl_download_file=v$wolfssl_version-stable.tar.gz |
|||
wolfssl_sha256_hash=053aefbb02d0b06b27c5e2df6875b4b587318755b7db9d6aa8d72206b310a848 |
|||
|
|||
echo "Building WolfSSL $wolfssl_version..." |
|||
|
|||
if [ -d wolfssl ]; then |
|||
echo "WolfSSL directory already exists" |
|||
else |
|||
wget $wolfssl_download_path/$wolfssl_download_file |
|||
|
|||
if [ $wolfssl_sha256_hash == $(cat $wolfssl_download_file | sha256sum | head -c 64 ) ]; then |
|||
tar zxvpf $wolfssl_download_file |
|||
mv wolfssl-4.6.0-stable wolfssl |
|||
cd wolfssl && ./autogen.sh && ./configure && make -j2 |
|||
else |
|||
echo "The file $wolfssl_download_file did not pass sha256 checksum. Please try again." |
|||
fi |
|||
fi |
@ -0,0 +1,35 @@ |
|||
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
|||
// Copyright (c) 2009-2015 The Bitcoin Core developers
|
|||
// Distributed under the GPLv3 software license, see the accompanying
|
|||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
|||
|
|||
#include <support/cleanse.h> |
|||
|
|||
#include <cstring> |
|||
|
|||
#if defined(_MSC_VER) |
|||
#include <Windows.h> // For SecureZeroMemory. |
|||
#endif |
|||
|
|||
void memory_cleanse(void *ptr, size_t len) |
|||
{ |
|||
#if defined(_MSC_VER) |
|||
/* SecureZeroMemory is guaranteed not to be optimized out by MSVC. */ |
|||
SecureZeroMemory(ptr, len); |
|||
#else |
|||
std::memset(ptr, 0, len); |
|||
|
|||
/* Memory barrier that scares the compiler away from optimizing out the memset.
|
|||
* |
|||
* Quoting Adam Langley <agl@google.com> in commit ad1907fe73334d6c696c8539646c21b11178f20f |
|||
* in BoringSSL (ISC License): |
|||
* As best as we can tell, this is sufficient to break any optimisations that |
|||
* might try to eliminate "superfluous" memsets. |
|||
* This method is used in memzero_explicit() the Linux kernel, too. Its advantage is that it |
|||
* is pretty efficient because the compiler can still implement the memset() efficiently, |
|||
* just not remove it entirely. See "Dead Store Elimination (Still) Considered Harmful" by |
|||
* Yang et al. (USENIX Security 2017) for more background. |
|||
*/ |
|||
__asm__ __volatile__("" : : "r"(ptr) : "memory"); |
|||
#endif |
|||
} |
@ -0,0 +1,15 @@ |
|||
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
|||
// Copyright (c) 2009-2015 The Bitcoin Core developers
|
|||
// Distributed under the GPLv3 software license, see the accompanying
|
|||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
|||
|
|||
#ifndef HUSH_SUPPORT_CLEANSE_H |
|||
#define HUSH_SUPPORT_CLEANSE_H |
|||
|
|||
#include <stdlib.h> |
|||
|
|||
/** Secure overwrite a buffer (possibly containing secret data) with zero-bytes. The write
|
|||
* operation will not be optimized out by the compiler. */ |
|||
void memory_cleanse(void *ptr, size_t len); |
|||
|
|||
#endif // HUSH_SUPPORT_CLEANSE_H
|
@ -0,0 +1,533 @@ |
|||
// Copyright (c) 2018-2021 The Hush developers
|
|||
// Distributed under the GPLv3 software license, see the accompanying
|
|||
// file LICENSE or https://www.gnu.org/licenses/gpl-3.0.en.html
|
|||
#ifndef HUSH_NODE_H |
|||
#define HUSH_NODE_H |
|||
|
|||
#include <algorithm> |
|||
#include "coin.h" |
|||
#include "db.h" |
|||
#include "netbase.h" |
|||
#include "protocol.h" |
|||
#include "serialize.h" |
|||
#include "uint256.h" |
|||
#include <wolfssl/options.h> |
|||
#include <wolfssl/ssl.h> |
|||
#include <wolfssl/openssl/dh.h> |
|||
#include <wolfssl/wolfcrypt/asn.h> |
|||
//#include "sync.h"
|
|||
#include "zeroafterfree.h" |
|||
#include "tlsmanager.cpp" |
|||
#include "tlsmanager.h" |
|||
|
|||
typedef std::vector<char, zero_after_free_allocator<char> > CSerializeData; |
|||
/*
|
|||
typedef CMutexLock<CCriticalSection> CCriticalBlock; |
|||
#define LOCK(cs) CCriticalBlock criticalblock(cs, #cs, __FILE__, __LINE__) |
|||
#define LOCK2(cs1, cs2) CCriticalBlock criticalblock1(cs1, #cs1, __FILE__, __LINE__), criticalblock2(cs2, #cs2, __FILE__, __LINE__) |
|||
#define TRY_LOCK(cs, name) CCriticalBlock name(cs, #cs, __FILE__, __LINE__, true) |
|||
|
|||
|
|||
// TODO: hacks to get things to compile
|
|||
void LogPrint(...) { } |
|||
void LogPrintf(...) { } |
|||
void LogPrintStr(...) { } |
|||
*/ |
|||
|
|||
|
|||
using namespace std; |
|||
|
|||
#define MESSAGE_START_SIZE 4 |
|||
#define HUSH_SEED_NONCE 0x0539a019ca550825ULL |
|||
|
|||
namespace hush { |
|||
|
|||
/** Message header.
|
|||
* (4) message start. |
|||
* (12) command. |
|||
* (4) size. |
|||
* (4) checksum. |
|||
*/ |
|||
|
|||
|
|||
CMessageHeader::CMessageHeader(const MessageStartChars& pchMessageStartIn) |
|||
{ |
|||
memcpy(pchMessageStart, pchMessageStartIn, MESSAGE_START_SIZE); |
|||
memset(pchCommand, 0, sizeof(pchCommand)); |
|||
nMessageSize = -1; |
|||
nChecksum = 0; |
|||
} |
|||
|
|||
CMessageHeader::CMessageHeader(const MessageStartChars& pchMessageStartIn, const char* pszCommand, unsigned int nMessageSizeIn) |
|||
{ |
|||
memcpy(pchMessageStart, pchMessageStartIn, MESSAGE_START_SIZE); |
|||
memset(pchCommand, 0, sizeof(pchCommand)); |
|||
strncpy(pchCommand, pszCommand, COMMAND_SIZE); |
|||
nMessageSize = nMessageSizeIn; |
|||
nChecksum = 0; |
|||
} |
|||
|
|||
bool CMessageHeader::IsValid(const MessageStartChars& pchMessageStartIn) const |
|||
{ |
|||
// Check start string
|
|||
if (memcmp(pchMessageStart, pchMessageStartIn, MESSAGE_START_SIZE) != 0) |
|||
return false; |
|||
|
|||
// Check the command string for errors
|
|||
for (const char* p1 = pchCommand; p1 < pchCommand + COMMAND_SIZE; p1++) |
|||
{ |
|||
if (*p1 == 0) |
|||
{ |
|||
// Must be all zeros after the first zero
|
|||
for (; p1 < pchCommand + COMMAND_SIZE; p1++) |
|||
if (*p1 != 0) |
|||
return false; |
|||
} |
|||
else if (*p1 < ' ' || *p1 > 0x7E) |
|||
return false; |
|||
} |
|||
|
|||
// Message size
|
|||
if (nMessageSize > MAX_SIZE) |
|||
{ |
|||
LogPrintf("CMessageHeader::IsValid(): (%s, %u bytes) nMessageSize > MAX_SIZE\n", GetCommand(), nMessageSize); |
|||
return false; |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
class CMessageHeader |
|||
{ |
|||
public: |
|||
typedef unsigned char MessageStartChars[MESSAGE_START_SIZE]; |
|||
|
|||
CMessageHeader(const MessageStartChars& pchMessageStartIn); |
|||
CMessageHeader(const MessageStartChars& pchMessageStartIn, const char* pszCommand, unsigned int nMessageSizeIn); |
|||
|
|||
std::string GetCommand() const; |
|||
bool IsValid(const MessageStartChars& messageStart) const; |
|||
|
|||
ADD_SERIALIZE_METHODS; |
|||
|
|||
template <typename Stream, typename Operation> |
|||
inline void SerializationOp(Stream& s, Operation ser_action) |
|||
{ |
|||
READWRITE(FLATDATA(pchMessageStart)); |
|||
READWRITE(FLATDATA(pchCommand)); |
|||
READWRITE(nMessageSize); |
|||
READWRITE(nChecksum); |
|||
} |
|||
|
|||
// TODO: make private (improves encapsulation)
|
|||
public: |
|||
enum { |
|||
COMMAND_SIZE = 12, |
|||
MESSAGE_SIZE_SIZE = sizeof(int), |
|||
CHECKSUM_SIZE = sizeof(int), |
|||
|
|||
MESSAGE_SIZE_OFFSET = MESSAGE_START_SIZE + COMMAND_SIZE, |
|||
CHECKSUM_OFFSET = MESSAGE_SIZE_OFFSET + MESSAGE_SIZE_SIZE, |
|||
HEADER_SIZE = MESSAGE_START_SIZE + COMMAND_SIZE + MESSAGE_SIZE_SIZE + CHECKSUM_SIZE |
|||
}; |
|||
char pchMessageStart[MESSAGE_START_SIZE]; |
|||
char pchCommand[COMMAND_SIZE]; |
|||
unsigned int nMessageSize; |
|||
unsigned int nChecksum; |
|||
}; |
|||
|
|||
const CMessageHeader::MessageStartChars& MessageStart() const { return pchMessageStart; } |
|||
|
|||
/** nServices flags */ |
|||
enum { |
|||
// NODE_NETWORK means that the node is capable of serving the block chain. It is currently
|
|||
// set by all Bitcoin Core nodes, and is unset by SPV clients or other peers that just want
|
|||
// network services but don't provide them.
|
|||
NODE_NETWORK = (1 << 0), |
|||
// NODE_BLOOM means the node is capable and willing to handle bloom-filtered connections.
|
|||
// Zcash nodes used to support this by default, without advertising this bit,
|
|||
// but no longer do as of protocol version 170004 (= NO_BLOOM_VERSION)
|
|||
NODE_BLOOM = (1 << 2), |
|||
|
|||
NODE_NSPV = (1 << 30), |
|||
NODE_ADDRINDEX = (1 << 29), |
|||
NODE_SPENTINDEX = (1 << 28), |
|||
|
|||
// Bits 24-31 are reserved for temporary experiments. Just pick a bit that
|
|||
// isn't getting used, or one not being used much, and notify the
|
|||
// bitcoin-development mailing list. Remember that service bits are just
|
|||
// unauthenticated advertisements, so your code must be robust against
|
|||
// collisions and other cases where nodes may be advertising a service they
|
|||
// do not actually support. Other service bits should be allocated via the
|
|||
// BIP process.
|
|||
}; |
|||
|
|||
class CNetMessage { |
|||
public: |
|||
bool in_data; // parsing header (false) or data (true)
|
|||
|
|||
CDataStream hdrbuf; // partially received header
|
|||
CMessageHeader hdr; // complete header
|
|||
unsigned int nHdrPos; |
|||
|
|||
CDataStream vRecv; // received message data
|
|||
unsigned int nDataPos; |
|||
|
|||
int64_t nTime; // time (in microseconds) of message receipt.
|
|||
|
|||
CNetMessage(const CMessageHeader::MessageStartChars& pchMessageStartIn, int nTypeIn, int nVersionIn) : hdrbuf(nTypeIn, nVersionIn), hdr(pchMessageStartIn), vRecv(nTypeIn, nVersionIn) { |
|||
hdrbuf.resize(24); |
|||
in_data = false; |
|||
nHdrPos = 0; |
|||
nDataPos = 0; |
|||
nTime = 0; |
|||
} |
|||
|
|||
bool complete() const |
|||
{ |
|||
if (!in_data) |
|||
return false; |
|||
return (hdr.nMessageSize == nDataPos); |
|||
} |
|||
|
|||
void SetVersion(int nVersionIn) |
|||
{ |
|||
hdrbuf.SetVersion(nVersionIn); |
|||
vRecv.SetVersion(nVersionIn); |
|||
} |
|||
|
|||
int readHeader(const char *pch, unsigned int nBytes); |
|||
int readData(const char *pch, unsigned int nBytes); |
|||
}; |
|||
|
|||
class CNode { |
|||
|
|||
SOCKET sock; |
|||
CDataStream vSend; |
|||
CDataStream vRecv; |
|||
unsigned int nHeaderStart; |
|||
unsigned int nMessageStart; |
|||
int nVersion; |
|||
string strSubVer; |
|||
int nStartingHeight; |
|||
vector<CAddress> *vAddr; |
|||
int ban; |
|||
int64 doneAfter; |
|||
CAddress you; |
|||
|
|||
|
|||
int GetTimeout() { |
|||
if (you.IsTor()) |
|||
return 120; |
|||
else |
|||
return 30; |
|||
} |
|||
|
|||
void BeginMessage(const char *pszCommand) { |
|||
if (nHeaderStart != -1) AbortMessage(); |
|||
nHeaderStart = vSend.size(); |
|||
vSend << CMessageHeader(pszCommand, 0); |
|||
nMessageStart = vSend.size(); |
|||
// printf("%s: SEND %s\n", ToString(you).c_str(), pszCommand);
|
|||
} |
|||
|
|||
void AbortMessage() { |
|||
if (nHeaderStart == -1) return; |
|||
vSend.resize(nHeaderStart); |
|||
nHeaderStart = -1; |
|||
nMessageStart = -1; |
|||
} |
|||
|
|||
void EndMessage() { |
|||
if (nHeaderStart == -1) return; |
|||
unsigned int nSize = vSend.size() - nMessageStart; |
|||
memcpy((char*)&vSend[nHeaderStart] + offsetof(CMessageHeader, nMessageSize), &nSize, sizeof(nSize)); |
|||
if (vSend.GetVersion() >= INIT_PROTO_VERSION) { |
|||
uint256 hash = Hash(vSend.begin() + nMessageStart, vSend.end()); |
|||
unsigned int nChecksum = 0; |
|||
memcpy(&nChecksum, &hash, sizeof(nChecksum)); |
|||
assert(nMessageStart - nHeaderStart >= offsetof(CMessageHeader, nChecksum) + sizeof(nChecksum)); |
|||
memcpy((char*)&vSend[nHeaderStart] + offsetof(CMessageHeader, nChecksum), &nChecksum, sizeof(nChecksum)); |
|||
} |
|||
nHeaderStart = -1; |
|||
nMessageStart = -1; |
|||
} |
|||
|
|||
void Send() { |
|||
if (sock == INVALID_SOCKET) return; |
|||
if (vSend.empty()) return; |
|||
int nBytes = send(sock, &vSend[0], vSend.size(), 0); |
|||
if (nBytes > 0) { |
|||
vSend.erase(vSend.begin(), vSend.begin() + nBytes); |
|||
} else { |
|||
close(sock); |
|||
sock = INVALID_SOCKET; |
|||
} |
|||
} |
|||
|
|||
void PushVersion() { |
|||
int64 nTime = time(NULL); |
|||
uint64 nLocalNonce = HUSH_SEED_NONCE; |
|||
int64 nLocalServices = 0; |
|||
CAddress me(CService("0.0.0.0")); |
|||
BeginMessage("version"); |
|||
int nBestHeight = GetRequireHeight(); |
|||
string ver = "/hush-seeder:0.03/"; |
|||
vSend << PROTOCOL_VERSION << nLocalServices << nTime << you << me << nLocalNonce << ver << nBestHeight; |
|||
EndMessage(); |
|||
} |
|||
|
|||
void GotVersion() { |
|||
// printf("\n%s: version %i\n", ToString(you).c_str(), nVersion);
|
|||
if (vAddr) { |
|||
BeginMessage("getaddr"); |
|||
EndMessage(); |
|||
doneAfter = time(NULL) + GetTimeout(); |
|||
} else { |
|||
doneAfter = time(NULL) + 1; |
|||
} |
|||
} |
|||
|
|||
bool ProcessMessage(string strCommand, CDataStream& vRecv) { |
|||
// printf("%s: RECV %s\n", ToString(you).c_str(), strCommand.c_str());
|
|||
if (strCommand == "version") { |
|||
int64 nTime; |
|||
CAddress addrMe; |
|||
CAddress addrFrom; |
|||
uint64 nNonce = 1; |
|||
vRecv >> nVersion >> you.nServices >> nTime >> addrMe; |
|||
if (nVersion == 10300) nVersion = 300; |
|||
if (nVersion >= 106 && !vRecv.empty()) |
|||
vRecv >> addrFrom >> nNonce; |
|||
if (nVersion >= 106 && !vRecv.empty()) |
|||
vRecv >> strSubVer; |
|||
if (nVersion >= INIT_PROTO_VERSION && !vRecv.empty()) |
|||
vRecv >> nStartingHeight; |
|||
|
|||
if (nVersion >= INIT_PROTO_VERSION) { |
|||
BeginMessage("verack"); |
|||
EndMessage(); |
|||
} |
|||
vSend.SetVersion(min(nVersion, PROTOCOL_VERSION)); |
|||
if (nVersion < INIT_PROTO_VERSION) { |
|||
this->vRecv.SetVersion(min(nVersion, PROTOCOL_VERSION)); |
|||
GotVersion(); |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
if (strCommand == "verack") { |
|||
this->vRecv.SetVersion(min(nVersion, PROTOCOL_VERSION)); |
|||
GotVersion(); |
|||
return false; |
|||
} |
|||
|
|||
if (strCommand == "addr" && vAddr) { |
|||
vector<CAddress> vAddrNew; |
|||
vRecv >> vAddrNew; |
|||
// printf("%s: got %i addresses\n", ToString(you).c_str(), (int)vAddrNew.size());
|
|||
int64 now = time(NULL); |
|||
vector<CAddress>::iterator it = vAddrNew.begin(); |
|||
if (vAddrNew.size() > 1) { |
|||
if (doneAfter == 0 || doneAfter > now + 1) doneAfter = now + 1; |
|||
} |
|||
while (it != vAddrNew.end()) { |
|||
CAddress &addr = *it; |
|||
// printf("%s: got address %s\n", ToString(you).c_str(), addr.ToString().c_str(), (int)(vAddr->size()));
|
|||
it++; |
|||
if (addr.nTime <= 100000000 || addr.nTime > now + 600) |
|||
addr.nTime = now - 5 * 86400; |
|||
if (addr.nTime > now - 604800) |
|||
vAddr->push_back(addr); |
|||
// printf("%s: added address %s (#%i)\n", ToString(you).c_str(), addr.ToString().c_str(), (int)(vAddr->size()));
|
|||
if (vAddr->size() > 1000) {doneAfter = 1; return true; } |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
bool ProcessMessages() { |
|||
if (vRecv.empty()) return false; |
|||
do { |
|||
CDataStream::iterator pstart = search(vRecv.begin(), vRecv.end(), BEGIN(pchMessageStart), END(pchMessageStart)); |
|||
int nHeaderSize = vRecv.GetSerializeSize(CMessageHeader()); |
|||
if (vRecv.end() - pstart < nHeaderSize) { |
|||
if (vRecv.size() > nHeaderSize) { |
|||
vRecv.erase(vRecv.begin(), vRecv.end() - nHeaderSize); |
|||
} |
|||
break; |
|||
} |
|||
vRecv.erase(vRecv.begin(), pstart); |
|||
vector<char> vHeaderSave(vRecv.begin(), vRecv.begin() + nHeaderSize); |
|||
CMessageHeader hdr; |
|||
vRecv >> hdr; |
|||
if (!hdr.IsValid()) { |
|||
// printf("%s: BAD (invalid header)\n", ToString(you).c_str());
|
|||
ban = 100000; return true; |
|||
} |
|||
string strCommand = hdr.GetCommand(); |
|||
unsigned int nMessageSize = hdr.nMessageSize; |
|||
if (nMessageSize > MAX_SIZE) { |
|||
// printf("%s: BAD (message too large)\n", ToString(you).c_str());
|
|||
ban = 100000; |
|||
return true; |
|||
} |
|||
if (nMessageSize > vRecv.size()) { |
|||
vRecv.insert(vRecv.begin(), vHeaderSave.begin(), vHeaderSave.end()); |
|||
break; |
|||
} |
|||
if (vRecv.GetVersion() >= INIT_PROTO_VERSION) { |
|||
uint256 hash = Hash(vRecv.begin(), vRecv.begin() + nMessageSize); |
|||
unsigned int nChecksum = 0; |
|||
memcpy(&nChecksum, &hash, sizeof(nChecksum)); |
|||
if (nChecksum != hdr.nChecksum) continue; |
|||
} |
|||
CDataStream vMsg(vRecv.begin(), vRecv.begin() + nMessageSize, vRecv.nType, vRecv.nVersion); |
|||
vRecv.ignore(nMessageSize); |
|||
if (ProcessMessage(strCommand, vMsg)) |
|||
return true; |
|||
// printf("%s: done processing %s\n", ToString(you).c_str(), strCommand.c_str());
|
|||
} while(1); |
|||
return false; |
|||
} |
|||
|
|||
public: |
|||
SOCKET hSocket; |
|||
|
|||
// TLS via WolfSSL
|
|||
WOLFSSL *ssl; |
|||
std::string tls_cipher; |
|||
|
|||
bool fNetworkNode; |
|||
bool fSuccessfullyConnected; |
|||
bool fDisconnect; |
|||
|
|||
CDataStream ssSend; |
|||
size_t nSendSize; // total size of all vSendMsg entries
|
|||
size_t nSendOffset; // offset inside the first vSendMsg already sent
|
|||
uint64_t nSendBytes; |
|||
std::deque<CSerializeData> vSendMsg; |
|||
CCriticalSection cs_vSend; |
|||
|
|||
std::deque<CInv> vRecvGetData; |
|||
std::deque<CNetMessage> vRecvMsg; |
|||
CCriticalSection cs_vRecvMsg; |
|||
uint64_t nRecvBytes; |
|||
int nRecvVersion; |
|||
|
|||
CNode(const CService& ip, vector<CAddress>* vAddrIn) : you(ip), nHeaderStart(-1), nMessageStart(-1), vAddr(vAddrIn), ban(0), doneAfter(0), nVersion(0) { |
|||
vSend.SetType(SER_NETWORK); |
|||
vSend.SetVersion(0); |
|||
vRecv.SetType(SER_NETWORK); |
|||
vRecv.SetVersion(0); |
|||
if (time(NULL) > 1329696000) { |
|||
vSend.SetVersion(INIT_PROTO_VERSION); |
|||
vRecv.SetVersion(INIT_PROTO_VERSION); |
|||
} |
|||
} |
|||
|
|||
|
|||
void CloseSocketDisconnect() |
|||
{ |
|||
fDisconnect = true; |
|||
|
|||
{ |
|||
//LOCK(cs_hSocket);
|
|||
|
|||
if (hSocket != INVALID_SOCKET) |
|||
{ |
|||
try |
|||
{ |
|||
LogPrint("net", "disconnecting peer\n"); // =%d\n", id);
|
|||
} |
|||
catch(std::bad_alloc&) |
|||
{ |
|||
// when the node is shutting down, the call above might use invalid memory resulting in a
|
|||
// std::bad_alloc exception when instantiating internal objs for handling log category
|
|||
LogPrintf("(node is probably shutting down) disconnecting peer\n"); // =%d\n", id);
|
|||
} |
|||
|
|||
if (ssl) { |
|||
unsigned long err_code = 0; |
|||
tlsmanager.waitFor(SSL_SHUTDOWN, hSocket, ssl, (DEFAULT_CONNECT_TIMEOUT / 1000), err_code); |
|||
wolfSSL_free(ssl); |
|||
ssl = NULL; |
|||
} |
|||
CloseSocket(hSocket); |
|||
} |
|||
} |
|||
|
|||
// in case this fails, we'll empty the recv buffer when the CNode is deleted
|
|||
//TRY_LOCK(cs_vRecvMsg, lockRecv);
|
|||
//if (lockRecv)
|
|||
vRecvMsg.clear(); |
|||
} |
|||
|
|||
bool Run() { |
|||
bool res = true; |
|||
if (!ConnectSocket(you, sock)) return false; |
|||
PushVersion(); |
|||
Send(); |
|||
int64 now; |
|||
while (now = time(NULL), ban == 0 && (doneAfter == 0 || doneAfter > now) && sock != INVALID_SOCKET) { |
|||
char pchBuf[0x10000]; |
|||
fd_set set; |
|||
FD_ZERO(&set); |
|||
FD_SET(sock,&set); |
|||
struct timeval wa; |
|||
if (doneAfter) { |
|||
wa.tv_sec = doneAfter - now; |
|||
wa.tv_usec = 0; |
|||
} else { |
|||
wa.tv_sec = GetTimeout(); |
|||
wa.tv_usec = 0; |
|||
} |
|||
int ret = select(sock+1, &set, NULL, &set, &wa); |
|||
if (ret != 1) { |
|||
if (!doneAfter) res = false; |
|||
break; |
|||
} |
|||
int nBytes = recv(sock, pchBuf, sizeof(pchBuf), 0); |
|||
int nPos = vRecv.size(); |
|||
if (nBytes > 0) { |
|||
vRecv.resize(nPos + nBytes); |
|||
memcpy(&vRecv[nPos], pchBuf, nBytes); |
|||
} else if (nBytes == 0) { |
|||
// printf("%s: BAD (connection closed prematurely)\n", ToString(you).c_str());
|
|||
res = false; |
|||
break; |
|||
} else { |
|||
// printf("%s: BAD (connection error)\n", ToString(you).c_str());
|
|||
res = false; |
|||
break; |
|||
} |
|||
ProcessMessages(); |
|||
Send(); |
|||
} |
|||
if (sock == INVALID_SOCKET) res = false; |
|||
close(sock); |
|||
sock = INVALID_SOCKET; |
|||
return (ban == 0) && res; |
|||
} |
|||
|
|||
int GetBan() { |
|||
return ban; |
|||
} |
|||
|
|||
int GetClientVersion() { |
|||
return nVersion; |
|||
} |
|||
|
|||
std::string GetClientSubVersion() { |
|||
return strSubVer; |
|||
} |
|||
|
|||
int GetStartingHeight() { |
|||
return nStartingHeight; |
|||
} |
|||
}; |
|||
|
|||
} // namespace hush
|
|||
#endif // HUSH_NODE_H
|
@ -0,0 +1,7 @@ |
|||
#!/usr/bin/env bash |
|||
# Copyright (c) 2021 The Hush developers |
|||
# Distributed under the GPLv3 software license, see the accompanying |
|||
# file LICENSE or https://www.gnu.org/licenses/gpl-3.0.en.html |
|||
|
|||
echo "For Debian and Ubuntu users" |
|||
sudo apt install build-essential libboost-all-dev libboost-dev libssl-dev -y |
@ -0,0 +1,296 @@ |
|||
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
|||
// Copyright (c) 2009-2013 The Bitcoin Core developers
|
|||
// Distributed under the GPLv3 software license, see the accompanying
|
|||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
|||
|
|||
/******************************************************************************
|
|||
* Copyright © 2014-2019 The SuperNET Developers. * |
|||
* * |
|||
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * |
|||
* the top-level directory of this distribution for the individual copyright * |
|||
* holder information and the developer policies on copyright and licensing. * |
|||
* * |
|||
* Unless otherwise agreed in a custom licensing agreement, no part of the * |
|||
* SuperNET software, including this file may be copied, modified, propagated * |
|||
* or distributed except according to the terms contained in the LICENSE file * |
|||
* * |
|||
* Removal or modification of this copyright notice is prohibited. * |
|||
* * |
|||
******************************************************************************/ |
|||
|
|||
#ifndef HUSH_SYNC_H |
|||
#define HUSH_SYNC_H |
|||
|
|||
#include "threadsafety.h" |
|||
|
|||
#undef __cpuid |
|||
#include <boost/thread/condition_variable.hpp> |
|||
#include <boost/thread/locks.hpp> |
|||
#include <boost/thread/mutex.hpp> |
|||
#include <boost/thread/recursive_mutex.hpp> |
|||
|
|||
|
|||
////////////////////////////////////////////////
|
|||
// //
|
|||
// THE SIMPLE DEFINITION, EXCLUDING DEBUG CODE //
|
|||
// //
|
|||
////////////////////////////////////////////////
|
|||
|
|||
/*
|
|||
CCriticalSection mutex; |
|||
boost::recursive_mutex mutex; |
|||
|
|||
LOCK(mutex); |
|||
boost::unique_lock<boost::recursive_mutex> criticalblock(mutex); |
|||
|
|||
LOCK2(mutex1, mutex2); |
|||
boost::unique_lock<boost::recursive_mutex> criticalblock1(mutex1); |
|||
boost::unique_lock<boost::recursive_mutex> criticalblock2(mutex2); |
|||
|
|||
TRY_LOCK(mutex, name); |
|||
boost::unique_lock<boost::recursive_mutex> name(mutex, boost::try_to_lock_t); |
|||
|
|||
ENTER_CRITICAL_SECTION(mutex); // no RAII
|
|||
mutex.lock(); |
|||
|
|||
LEAVE_CRITICAL_SECTION(mutex); // no RAII
|
|||
mutex.unlock(); |
|||
*/ |
|||
|
|||
///////////////////////////////
|
|||
// //
|
|||
// THE ACTUAL IMPLEMENTATION //
|
|||
// //
|
|||
///////////////////////////////
|
|||
|
|||
/**
|
|||
* Template mixin that adds -Wthread-safety locking |
|||
* annotations to a subset of the mutex API. |
|||
*/ |
|||
template <typename PARENT> |
|||
class LOCKABLE AnnotatedMixin : public PARENT |
|||
{ |
|||
public: |
|||
void lock() EXCLUSIVE_LOCK_FUNCTION() |
|||
{ |
|||
PARENT::lock(); |
|||
} |
|||
|
|||
void unlock() UNLOCK_FUNCTION() |
|||
{ |
|||
PARENT::unlock(); |
|||
} |
|||
|
|||
bool try_lock() EXCLUSIVE_TRYLOCK_FUNCTION(true) |
|||
{ |
|||
return PARENT::try_lock(); |
|||
} |
|||
}; |
|||
|
|||
/**
|
|||
* Wrapped boost mutex: supports recursive locking, but no waiting |
|||
* TODO: We should move away from using the recursive lock by default. |
|||
*/ |
|||
typedef AnnotatedMixin<boost::recursive_mutex> CCriticalSection; |
|||
|
|||
/** Wrapped boost mutex: supports waiting but not recursive locking */ |
|||
typedef AnnotatedMixin<boost::mutex> CWaitableCriticalSection; |
|||
|
|||
/** Just a typedef for boost::condition_variable, can be wrapped later if desired */ |
|||
typedef boost::condition_variable CConditionVariable; |
|||
|
|||
#ifdef DEBUG_LOCKORDER |
|||
void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false); |
|||
void LeaveCritical(); |
|||
std::string LocksHeld(); |
|||
void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs); |
|||
#else |
|||
void static inline EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false) {} |
|||
void static inline LeaveCritical() {} |
|||
void static inline AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs) {} |
|||
#endif |
|||
#define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) |
|||
|
|||
#ifdef DEBUG_LOCKCONTENTION |
|||
void PrintLockContention(const char* pszName, const char* pszFile, int nLine); |
|||
#endif |
|||
|
|||
/** Wrapper around boost::unique_lock<Mutex> */ |
|||
template <typename Mutex> |
|||
class SCOPED_LOCKABLE CMutexLock |
|||
{ |
|||
private: |
|||
boost::unique_lock<Mutex> lock; |
|||
|
|||
void Enter(const char* pszName, const char* pszFile, int nLine) |
|||
{ |
|||
EnterCritical(pszName, pszFile, nLine, (void*)(lock.mutex())); |
|||
#ifdef DEBUG_LOCKCONTENTION |
|||
if (!lock.try_lock()) { |
|||
PrintLockContention(pszName, pszFile, nLine); |
|||
#endif |
|||
lock.lock(); |
|||
#ifdef DEBUG_LOCKCONTENTION |
|||
} |
|||
#endif |
|||
} |
|||
|
|||
bool TryEnter(const char* pszName, const char* pszFile, int nLine) |
|||
{ |
|||
EnterCritical(pszName, pszFile, nLine, (void*)(lock.mutex()), true); |
|||
lock.try_lock(); |
|||
if (!lock.owns_lock()) |
|||
LeaveCritical(); |
|||
return lock.owns_lock(); |
|||
} |
|||
|
|||
public: |
|||
CMutexLock(Mutex& mutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(mutexIn) : lock(mutexIn, boost::defer_lock) |
|||
{ |
|||
if (fTry) |
|||
TryEnter(pszName, pszFile, nLine); |
|||
else |
|||
Enter(pszName, pszFile, nLine); |
|||
} |
|||
|
|||
CMutexLock(Mutex* pmutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(pmutexIn) |
|||
{ |
|||
if (!pmutexIn) return; |
|||
|
|||
lock = boost::unique_lock<Mutex>(*pmutexIn, boost::defer_lock); |
|||
if (fTry) |
|||
TryEnter(pszName, pszFile, nLine); |
|||
else |
|||
Enter(pszName, pszFile, nLine); |
|||
} |
|||
|
|||
~CMutexLock() UNLOCK_FUNCTION() |
|||
{ |
|||
if (lock.owns_lock()) |
|||
LeaveCritical(); |
|||
} |
|||
|
|||
operator bool() |
|||
{ |
|||
return lock.owns_lock(); |
|||
} |
|||
}; |
|||
|
|||
typedef CMutexLock<CCriticalSection> CCriticalBlock; |
|||
|
|||
#define LOCK(cs) CCriticalBlock criticalblock(cs, #cs, __FILE__, __LINE__) |
|||
#define LOCK2(cs1, cs2) CCriticalBlock criticalblock1(cs1, #cs1, __FILE__, __LINE__), criticalblock2(cs2, #cs2, __FILE__, __LINE__) |
|||
#define TRY_LOCK(cs, name) CCriticalBlock name(cs, #cs, __FILE__, __LINE__, true) |
|||
|
|||
#define ENTER_CRITICAL_SECTION(cs) \ |
|||
{ \ |
|||
EnterCritical(#cs, __FILE__, __LINE__, (void*)(&cs)); \ |
|||
(cs).lock(); \ |
|||
} |
|||
|
|||
#define LEAVE_CRITICAL_SECTION(cs) \ |
|||
{ \ |
|||
(cs).unlock(); \ |
|||
LeaveCritical(); \ |
|||
} |
|||
|
|||
class CSemaphore |
|||
{ |
|||
private: |
|||
boost::condition_variable condition; |
|||
boost::mutex mutex; |
|||
int value; |
|||
|
|||
public: |
|||
CSemaphore(int init) : value(init) {} |
|||
|
|||
void wait() |
|||
{ |
|||
boost::unique_lock<boost::mutex> lock(mutex); |
|||
while (value < 1) { |
|||
condition.wait(lock); |
|||
} |
|||
value--; |
|||
} |
|||
|
|||
bool try_wait() |
|||
{ |
|||
boost::unique_lock<boost::mutex> lock(mutex); |
|||
if (value < 1) |
|||
return false; |
|||
value--; |
|||
return true; |
|||
} |
|||
|
|||
void post() |
|||
{ |
|||
{ |
|||
boost::unique_lock<boost::mutex> lock(mutex); |
|||
value++; |
|||
} |
|||
condition.notify_one(); |
|||
} |
|||
}; |
|||
|
|||
/** RAII-style semaphore lock */ |
|||
class CSemaphoreGrant |
|||
{ |
|||
private: |
|||
CSemaphore* sem; |
|||
bool fHaveGrant; |
|||
|
|||
public: |
|||
void Acquire() |
|||
{ |
|||
if (fHaveGrant) |
|||
return; |
|||
sem->wait(); |
|||
fHaveGrant = true; |
|||
} |
|||
|
|||
void Release() |
|||
{ |
|||
if (!fHaveGrant) |
|||
return; |
|||
sem->post(); |
|||
fHaveGrant = false; |
|||
} |
|||
|
|||
bool TryAcquire() |
|||
{ |
|||
if (!fHaveGrant && sem->try_wait()) |
|||
fHaveGrant = true; |
|||
return fHaveGrant; |
|||
} |
|||
|
|||
void MoveTo(CSemaphoreGrant& grant) |
|||
{ |
|||
grant.Release(); |
|||
grant.sem = sem; |
|||
grant.fHaveGrant = fHaveGrant; |
|||
sem = NULL; |
|||
fHaveGrant = false; |
|||
} |
|||
|
|||
CSemaphoreGrant() : sem(NULL), fHaveGrant(false) {} |
|||
|
|||
CSemaphoreGrant(CSemaphore& sema, bool fTry = false) : sem(&sema), fHaveGrant(false) |
|||
{ |
|||
if (fTry) |
|||
TryAcquire(); |
|||
else |
|||
Acquire(); |
|||
} |
|||
|
|||
~CSemaphoreGrant() |
|||
{ |
|||
Release(); |
|||
} |
|||
|
|||
operator bool() |
|||
{ |
|||
return fHaveGrant; |
|||
} |
|||
}; |
|||
|
|||
#endif // HUSH_SYNC_H
|
@ -0,0 +1,55 @@ |
|||
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
|||
// Copyright (c) 2009-2012 The Bitcoin Core developers
|
|||
// Distributed under the GPLv3 software license, see the accompanying
|
|||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
|||
|
|||
#ifndef HUSH_THREADSAFETY_H |
|||
#define HUSH_THREADSAFETY_H |
|||
|
|||
#ifdef __clang__ |
|||
// TL;DR Add GUARDED_BY(mutex) to member variables. The others are
|
|||
// rarely necessary. Ex: int nFoo GUARDED_BY(cs_foo);
|
|||
//
|
|||
// See http://clang.llvm.org/docs/LanguageExtensions.html#threadsafety
|
|||
// for documentation. The clang compiler can do advanced static analysis
|
|||
// of locking when given the -Wthread-safety option.
|
|||
#define LOCKABLE __attribute__((lockable)) |
|||
#define SCOPED_LOCKABLE __attribute__((scoped_lockable)) |
|||
#define GUARDED_BY(x) __attribute__((guarded_by(x))) |
|||
#define GUARDED_VAR __attribute__((guarded_var)) |
|||
#define PT_GUARDED_BY(x) __attribute__((pt_guarded_by(x))) |
|||
#define PT_GUARDED_VAR __attribute__((pt_guarded_var)) |
|||
#define ACQUIRED_AFTER(...) __attribute__((acquired_after(__VA_ARGS__))) |
|||
#define ACQUIRED_BEFORE(...) __attribute__((acquired_before(__VA_ARGS__))) |
|||
#define EXCLUSIVE_LOCK_FUNCTION(...) __attribute__((exclusive_lock_function(__VA_ARGS__))) |
|||
#define SHARED_LOCK_FUNCTION(...) __attribute__((shared_lock_function(__VA_ARGS__))) |
|||
#define EXCLUSIVE_TRYLOCK_FUNCTION(...) __attribute__((exclusive_trylock_function(__VA_ARGS__))) |
|||
#define SHARED_TRYLOCK_FUNCTION(...) __attribute__((shared_trylock_function(__VA_ARGS__))) |
|||
#define UNLOCK_FUNCTION(...) __attribute__((unlock_function(__VA_ARGS__))) |
|||
#define LOCK_RETURNED(x) __attribute__((lock_returned(x))) |
|||
#define LOCKS_EXCLUDED(...) __attribute__((locks_excluded(__VA_ARGS__))) |
|||
#define EXCLUSIVE_LOCKS_REQUIRED(...) __attribute__((exclusive_locks_required(__VA_ARGS__))) |
|||
#define SHARED_LOCKS_REQUIRED(...) __attribute__((shared_locks_required(__VA_ARGS__))) |
|||
#define NO_THREAD_SAFETY_ANALYSIS __attribute__((no_thread_safety_analysis)) |
|||
#else |
|||
#define LOCKABLE |
|||
#define SCOPED_LOCKABLE |
|||
#define GUARDED_BY(x) |
|||
#define GUARDED_VAR |
|||
#define PT_GUARDED_BY(x) |
|||
#define PT_GUARDED_VAR |
|||
#define ACQUIRED_AFTER(...) |
|||
#define ACQUIRED_BEFORE(...) |
|||
#define EXCLUSIVE_LOCK_FUNCTION(...) |
|||
#define SHARED_LOCK_FUNCTION(...) |
|||
#define EXCLUSIVE_TRYLOCK_FUNCTION(...) |
|||
#define SHARED_TRYLOCK_FUNCTION(...) |
|||
#define UNLOCK_FUNCTION(...) |
|||
#define LOCK_RETURNED(x) |
|||
#define LOCKS_EXCLUDED(...) |
|||
#define EXCLUSIVE_LOCKS_REQUIRED(...) |
|||
#define SHARED_LOCKS_REQUIRED(...) |
|||
#define NO_THREAD_SAFETY_ANALYSIS |
|||
#endif // __GNUC__
|
|||
|
|||
#endif // HUSH_THREADSAFETY_H
|
@ -0,0 +1,9 @@ |
|||
// Copyright (c) 2016-2021 The Hush developers
|
|||
// Distributed under the GPLv3 software license, see the accompanying
|
|||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
|||
|
|||
namespace hush |
|||
{ |
|||
typedef enum { SSL_ACCEPT, SSL_CONNECT, SSL_SHUTDOWN } SSLConnectionRoutine; |
|||
typedef enum { CLIENT_CONTEXT, SERVER_CONTEXT } TLSContextType; |
|||
} |
@ -0,0 +1,819 @@ |
|||
// Copyright (c) 2016-2021 The Hush developers
|
|||
// Distributed under the GPLv3 software license, see the accompanying
|
|||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
|||
#include <wolfssl/options.h> |
|||
#include <wolfssl/ssl.h> |
|||
#include <wolfssl/openssl/dh.h> |
|||
#include <wolfssl/wolfcrypt/asn.h> |
|||
#include <boost/filesystem.hpp> |
|||
#include <boost/thread.hpp> |
|||
#include "tlsmanager.h" |
|||
#include "utiltls.h" |
|||
|
|||
#include <chrono> |
|||
#include <boost/date_time/posix_time/posix_time.hpp> |
|||
#include "node.h" |
|||
|
|||
typedef std::vector<char, zero_after_free_allocator<char> > CSerializeData; |
|||
|
|||
// TODO: hacks to get things to compile
|
|||
void LogPrint(...) { } |
|||
void LogPrintf(...) { } |
|||
void LogPrintStr(...) { } |
|||
|
|||
using namespace std; |
|||
// store our preferred cipherlist so we can use it for debug/etc later on
|
|||
std::string TLS_CIPHERLIST; |
|||
|
|||
|
|||
std::string NetworkErrorString(int err) |
|||
{ |
|||
char buf[256]; |
|||
const char *s = buf; |
|||
buf[0] = 0; |
|||
/* Too bad there are two incompatible implementations of the
|
|||
* thread-safe strerror. */ |
|||
#ifdef STRERROR_R_CHAR_P /* GNU variant can return a pointer outside the passed buffer */ |
|||
s = strerror_r(err, buf, sizeof(buf)); |
|||
#else /* POSIX variant always returns message in buffer */ |
|||
if (strerror_r(err, buf, sizeof(buf))) |
|||
buf[0] = 0; |
|||
#endif |
|||
return strprintf("%s (%d)", s, err); |
|||
} |
|||
|
|||
int64_t GetTimeMillis() |
|||
{ |
|||
return std::chrono::duration_cast<std::chrono::milliseconds>( |
|||
std::chrono::system_clock::now().time_since_epoch()).count(); |
|||
} |
|||
|
|||
int64_t GetTimeMicros() |
|||
{ |
|||
return std::chrono::duration_cast<std::chrono::microseconds>( |
|||
std::chrono::system_clock::now().time_since_epoch()).count(); |
|||
} |
|||
|
|||
void MilliSleep(int64_t n) |
|||
{ |
|||
boost::this_thread::sleep_for(boost::chrono::milliseconds(n)); |
|||
} |
|||
|
|||
//static const int DEFAULT_CONNECT_TIMEOUT = 5000;
|
|||
|
|||
fd_set fdsetSend, fdsetError, fdsetRecv; |
|||
|
|||
extern WOLFSSL_CTX *tls_ctx_client, *tls_ctx_server; |
|||
|
|||
namespace hush |
|||
{ |
|||
static WOLFSSL_EVP_PKEY *mykey; |
|||
static WOLFSSL_X509 *mycert; |
|||
|
|||
// this is the 'dh crypto environment' to be shared between two peers and it is meant to be public, therefore
|
|||
// it is OK to hard code it (or as an alternative to read it from a file)
|
|||
// ----
|
|||
// generated via: openssl dhparam -C 2048
|
|||
static WOLFSSL_DH *get_dh2048(void) |
|||
{ |
|||
static unsigned char dhp_2048[] = { |
|||
0xFF, 0x4A, 0xA8, 0x6C, 0x68, 0xD4, 0x4C, 0x41, 0x73, 0x8D, |
|||
0xD8, 0x14, 0x57, 0xF9, 0x1C, 0x35, 0x72, 0x5F, 0xCD, 0x24, |
|||
0xCB, 0xD1, 0x77, 0x30, 0xC2, 0x9A, 0x69, 0x01, 0xCF, 0x01, |
|||
0xDE, 0xD4, 0x67, 0xD4, 0xEE, 0x9A, 0x03, 0x1C, 0x27, 0x42, |
|||
0x06, 0x3D, 0x1D, 0x91, 0x27, 0xCF, 0x1C, 0x17, 0xB3, 0xDC, |
|||
0x9F, 0x6F, 0x12, 0xC8, 0x03, 0x5C, 0x01, 0xF3, 0x27, 0x7F, |
|||
0x34, 0x58, 0xAE, 0xB9, 0xA7, 0xA9, 0xCE, 0x5E, 0x25, 0x7D, |
|||
0x46, 0x84, 0xDD, 0xEE, 0x55, 0xFB, 0xEA, 0x1C, 0xCD, 0x9B, |
|||
0x96, 0xC4, 0x22, 0x8C, 0x33, 0x8B, 0xC7, 0xE6, 0xCC, 0x4C, |
|||
0x77, 0x1B, 0x7A, 0x46, 0xDE, 0x33, 0xAD, 0xBB, 0xFD, 0x2D, |
|||
0xAD, 0x26, 0xE1, 0x27, 0x48, 0x94, 0xA3, 0x59, 0xC5, 0x10, |
|||
0x5A, 0x86, 0x71, 0x8D, 0xAA, 0x15, 0x8B, 0xB2, 0xCB, 0x70, |
|||
0xBE, 0x1F, 0x17, 0xBD, 0xEB, 0x51, 0xB1, 0x76, 0x0E, 0x24, |
|||
0x43, 0xAA, 0x06, 0xC0, 0x97, 0x01, 0x25, 0x52, 0x30, 0x7A, |
|||
0x56, 0x92, 0x3D, 0x8A, 0x3A, 0xBC, 0xFA, 0x98, 0x51, 0x04, |
|||
0x1D, 0x9B, 0x05, 0xB8, 0x84, 0x8C, 0x2F, 0x7A, 0x94, 0x1E, |
|||
0xAA, 0x51, 0xF2, 0x5D, 0x48, 0x50, 0x58, 0x8D, 0x7E, 0xBA, |
|||
0xD3, 0xCC, 0xF2, 0x92, 0x28, 0xB1, 0x1C, 0x4B, 0x50, 0x10, |
|||
0xFA, 0x7E, 0xDF, 0x8D, 0x23, 0x1C, 0x8C, 0x65, 0xE3, 0x86, |
|||
0x16, 0x67, 0x88, 0x9E, 0xFC, 0x8B, 0xC8, 0x55, 0x38, 0x6E, |
|||
0x79, 0x06, 0x6A, 0x6D, 0x72, 0x75, 0xA6, 0xAC, 0x77, 0x98, |
|||
0xDD, 0xB2, 0x0B, 0xAA, 0x48, 0x54, 0xA9, 0x07, 0x7E, 0x8C, |
|||
0x4C, 0x39, 0x08, 0x26, 0x6D, 0x53, 0xC2, 0xDF, 0xE2, 0xF0, |
|||
0xD6, 0x8A, 0x4F, 0xB5, 0x7A, 0x32, 0xEE, 0x93, 0x0E, 0x2A, |
|||
0x81, 0x2F, 0x3B, 0x1E, 0xE6, 0x38, 0xF8, 0x3C, 0xF5, 0x84, |
|||
0xB4, 0xFB, 0x92, 0x12, 0x28, 0xA3 |
|||
}; |
|||
static unsigned char dhg_2048[] = { |
|||
0x02 |
|||
}; |
|||
|
|||
WOLFSSL_DH *dh = wolfSSL_DH_new(); |
|||
|
|||
if (dh == NULL) { |
|||
return NULL; |
|||
} |
|||
|
|||
if (wc_DhSetKey((DhKey*)dh->internal, dhp_2048, sizeof(dhp_2048), dhg_2048, sizeof(dhg_2048)) != 0) { |
|||
wolfSSL_DH_free(dh); |
|||
return NULL; |
|||
} |
|||
|
|||
return dh; |
|||
} |
|||
|
|||
DH *tmp_dh_callback(WOLFSSL *ssl, int is_export, int keylength) { |
|||
LogPrint("tls", "TLS: %s: %s():%d - Using Diffie-Hellman param for PFS: is_export=%d, keylength=%d\n", __FILE__, __func__, __LINE__, is_export, keylength); |
|||
|
|||
return get_dh2048(); |
|||
} |
|||
|
|||
int TLSManager::waitFor(SSLConnectionRoutine eRoutine, SOCKET hSocket, WOLFSSL* ssl, int timeoutSec, unsigned long& err_code) { |
|||
int retOp = 0; |
|||
err_code = 0; |
|||
char err_buffer[1024]; |
|||
std::string disconnectedPeer("no info"); |
|||
|
|||
while (true) |
|||
{ |
|||
// clear the current thread's error queue
|
|||
wolfSSL_ERR_clear_error(); |
|||
|
|||
switch (eRoutine) { |
|||
case SSL_CONNECT: |
|||
{ |
|||
retOp = wolfSSL_connect(ssl); |
|||
if (retOp == 0) { |
|||
err_code = wolfSSL_ERR_get_error(); |
|||
const char* error_str = wolfSSL_ERR_error_string(err_code, err_buffer); |
|||
LogPrint("tls", "TLS: WARNING: %s: %s():%d - SSL_CONNECT err: %s\n", |
|||
__FILE__, __func__, __LINE__, err_buffer); |
|||
return -1; |
|||
} |
|||
} |
|||
break; |
|||
|
|||
case SSL_ACCEPT: |
|||
{ |
|||
retOp = wolfSSL_accept(ssl); |
|||
if (retOp == 0) { |
|||
err_code = wolfSSL_ERR_get_error(); |
|||
const char* error_str = wolfSSL_ERR_error_string(err_code, err_buffer); |
|||
LogPrint("tls", "TLS: WARNING: %s: %s():%d - SSL_ACCEPT err: %s\n", |
|||
__FILE__, __func__, __LINE__, err_buffer); |
|||
return -1; |
|||
} |
|||
} |
|||
break; |
|||
|
|||
case SSL_SHUTDOWN: |
|||
{ |
|||
if (hSocket != INVALID_SOCKET) { |
|||
disconnectedPeer = "no info"; |
|||
struct sockaddr_in addr; |
|||
socklen_t serv_len = sizeof(addr); |
|||
int ret = getpeername(hSocket, (struct sockaddr *)&addr, &serv_len); |
|||
if (ret == 0) { |
|||
disconnectedPeer = std::string(inet_ntoa(addr.sin_addr)) + ":" + std::to_string(ntohs(addr.sin_port)); |
|||
} |
|||
LogPrint("tls", "TLS: shutting down fd=%d, peer=%s\n", hSocket, disconnectedPeer); |
|||
} |
|||
retOp = wolfSSL_shutdown(ssl); |
|||
} |
|||
break; |
|||
|
|||
default: |
|||
return -1; |
|||
} |
|||
|
|||
if (eRoutine == SSL_SHUTDOWN) { |
|||
if (retOp == 0) { |
|||
LogPrint("tls", "TLS: WARNING: %s: %s():%d - SSL_SHUTDOWN: The close_notify was sent but the peer did not send it back yet. Whatevz\n", |
|||
__FILE__, __func__, __LINE__); |
|||
// do not call SSL_get_error() because it may misleadingly indicate an error even though no error occurred.
|
|||
break; |
|||
} else if (retOp == 1) { |
|||
LogPrint("tls", "TLS: %s: %s():%d - SSL_SHUTDOWN completed from peer %s\n", __FILE__, __func__, __LINE__, disconnectedPeer.c_str()); |
|||
break; |
|||
} else { |
|||
LogPrint("tls", "TLS: %s: %s():%d - SSL_SHUTDOWN failed to %s with ret=%d\n", __FILE__, __func__, __LINE__, disconnectedPeer.c_str(), retOp); |
|||
} |
|||
} else { |
|||
if (retOp == 1) { |
|||
std::string goodPeer = ""; |
|||
struct sockaddr_in addr; |
|||
socklen_t serv_len = sizeof(addr); |
|||
int ret = getpeername(hSocket, (struct sockaddr *)&addr, &serv_len); |
|||
if (ret == 0) { |
|||
goodPeer = std::string(inet_ntoa(addr.sin_addr)) + ":" + std::to_string(ntohs(addr.sin_port)); |
|||
} |
|||
LogPrint("tls", "TLS: %s: %s():%d - %s completed to %s\n", __FILE__, __func__, __LINE__, |
|||
eRoutine == SSL_CONNECT ? "SSL_CONNECT" : "SSL_ACCEPT", goodPeer); |
|||
break; |
|||
} |
|||
} |
|||
|
|||
int sslErr = wolfSSL_get_error(ssl, retOp); |
|||
|
|||
if (sslErr != WOLFSSL_ERROR_WANT_READ && sslErr != WOLFSSL_ERROR_WANT_WRITE) { |
|||
err_code = wolfSSL_ERR_get_error(); |
|||
const char* error_str = NULL; |
|||
// calling this with err_code=0 generates more warnings, lulz
|
|||
if(err_code) { |
|||
error_str = wolfSSL_ERR_error_string(err_code, err_buffer); |
|||
} |
|||
|
|||
LogPrint("tls", "TLS: WARNING: %s: %s():%d - routine(%d), sslErr[0x%x], retOp[%d], errno[0x%x], lib[0x%x], func[0x%x], reas[0x%x]-> err: %s\n", |
|||
__FILE__, __func__, __LINE__, |
|||
eRoutine, sslErr, retOp, errno, wolfSSL_ERR_GET_LIB(err_code), ERR_GET_FUNC(err_code), wolfSSL_ERR_GET_REASON(err_code), error_str); |
|||
retOp = -1; |
|||
break; |
|||
} |
|||
|
|||
fd_set socketSet; |
|||
FD_ZERO(&socketSet); |
|||
FD_SET(hSocket, &socketSet); |
|||
|
|||
struct timeval timeout = {timeoutSec, 0}; |
|||
|
|||
if (sslErr == WOLFSSL_ERROR_WANT_READ) { |
|||
int result = select(hSocket + 1, &socketSet, NULL, NULL, &timeout); |
|||
if (result == 0) { |
|||
LogPrint("tls", "TLS: ERROR: %s: %s():%d - WANT_READ timeout on %s\n", __FILE__, __func__, __LINE__, |
|||
(eRoutine == SSL_CONNECT ? "SSL_CONNECT" : |
|||
(eRoutine == SSL_ACCEPT ? "SSL_ACCEPT" : "SSL_SHUTDOWN" ))); |
|||
err_code = SELECT_TIMEDOUT; |
|||
retOp = -1; |
|||
break; |
|||
} else if (result == -1) { |
|||
LogPrint("tls", "TLS: ERROR: %s: %s: WANT_READ ssl_err_code: 0x%x; errno: %s\n", |
|||
__FILE__, __func__, sslErr, strerror(errno)); |
|||
retOp = -1; |
|||
break; |
|||
} |
|||
} else { |
|||
int result = select(hSocket + 1, NULL, &socketSet, NULL, &timeout); |
|||
if (result == 0) { |
|||
LogPrint("tls", "TLS: ERROR: %s: %s():%d - WANT_WRITE timeout on %s\n", __FILE__, __func__, __LINE__, |
|||
(eRoutine == SSL_CONNECT ? "SSL_CONNECT" : |
|||
(eRoutine == SSL_ACCEPT ? "SSL_ACCEPT" : "SSL_SHUTDOWN" ))); |
|||
err_code = SELECT_TIMEDOUT; |
|||
retOp = -1; |
|||
break; |
|||
} else if (result == -1) { |
|||
LogPrint("tls", "TLS: ERROR: %s: %s: WANT_WRITE ssl_err_code: 0x%x; errno: %s\n", |
|||
__FILE__, __func__, sslErr, strerror(errno)); |
|||
retOp = -1; |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
return retOp; |
|||
} |
|||
|
|||
/**
|
|||
* @brief establish TLS connection to an address |
|||
* |
|||
* @param hSocket socket |
|||
* @param addrConnect the outgoing address |
|||
* @param tls_ctx_client TLS Client context |
|||
* @return WOLFSSL* returns a ssl* if successful, otherwise returns NULL. |
|||
*/ |
|||
WOLFSSL* TLSManager::connect(SOCKET hSocket, const CAddress& addrConnect, unsigned long& err_code) |
|||
{ |
|||
LogPrint("tls", "TLS: establishing connection (tid = %X), (peerid = %s)\n", pthread_self(), addrConnect.ToString()); |
|||
|
|||
err_code = 0; |
|||
char err_buffer[1024]; |
|||
WOLFSSL* ssl = NULL; |
|||
bool bConnectedTLS = false; |
|||
|
|||
if ((ssl = wolfSSL_new(tls_ctx_client))) { |
|||
if (wolfSSL_set_fd(ssl, hSocket)) { |
|||
int ret = TLSManager::waitFor(SSL_CONNECT, hSocket, ssl, (DEFAULT_CONNECT_TIMEOUT / 1000), err_code); |
|||
if (ret == 1) { |
|||
bConnectedTLS = true; |
|||
} else { |
|||
err_code = wolfSSL_ERR_get_error(); |
|||
LogPrint("tls", "%s: timed out waiting for %s\n", __func__, addrConnect.ToString()); |
|||
} |
|||
} else { |
|||
LogPrint("tls", "TLS: %s: failed to set file descriptor for socket!\n", __func__, addrConnect.ToString()); |
|||
} |
|||
} else { |
|||
err_code = wolfSSL_ERR_get_error(); |
|||
const char* error_str = wolfSSL_ERR_error_string(err_code, err_buffer); |
|||
LogPrint("tls", "TLS: %s: %s():%d - SSL_new failed err: %s\n", __FILE__, __func__, __LINE__, err_buffer); |
|||
} |
|||
|
|||
if (bConnectedTLS) { |
|||
LogPrintf("TLS: connection to %s has been established (tlsv = %s 0x%04x / ssl = %s 0x%x ). Using cipher: %s\n", |
|||
addrConnect.ToString(), wolfSSL_get_version(ssl), wolfSSL_version(ssl), wolfSSL_OpenSSL_version(), wolfSSL_lib_version_hex(), wolfSSL_get_cipher_name(ssl)); |
|||
} else { |
|||
if(err_code) { |
|||
LogPrintf("TLS: %s: %s():%d - TLS connection to %s failed with err_code=0x%X\n", __FILE__, __func__, __LINE__, addrConnect.ToString(), err_code); |
|||
} else { |
|||
LogPrintf("TLS: %s: %s():%d - TLS connection to %s timed out\n", __FILE__, __func__, __LINE__, addrConnect.ToString()); |
|||
} |
|||
|
|||
if (ssl) { |
|||
wolfSSL_free(ssl); |
|||
ssl = NULL; |
|||
} |
|||
} |
|||
|
|||
return ssl; |
|||
} |
|||
/**
|
|||
* @brief Initialize TLS Context |
|||
* |
|||
* @param ctxType context type |
|||
* @param privateKeyFile private key file path |
|||
* @param certificateFile certificate key file path |
|||
* @param trustedDirs trusted directories |
|||
* @return WOLSSL_CTX* returns the context. |
|||
*/ |
|||
WOLFSSL_CTX* TLSManager::initCtx(TLSContextType ctxType) |
|||
{ |
|||
LogPrintf("TLS: %s: %s():%d - Initializing %s context\n", |
|||
__FILE__, __func__, __LINE__, ctxType == SERVER_CONTEXT ? "server" : "client"); |
|||
|
|||
if (!mykey || !mycert) { |
|||
return NULL; |
|||
} |
|||
|
|||
bool bInitialized = false; |
|||
WOLFSSL_CTX* tlsCtx = NULL; |
|||
|
|||
byte *pem; |
|||
int plen = 0; |
|||
|
|||
if ((tlsCtx = wolfSSL_CTX_new(ctxType == SERVER_CONTEXT ? wolfTLSv1_3_server_method() : wolfTLSv1_3_client_method()))) { |
|||
wolfSSL_CTX_set_mode(tlsCtx, SSL_MODE_AUTO_RETRY); |
|||
|
|||
// Disable TLS < 1.3, just in case
|
|||
int ret = wolfSSL_CTX_set_min_proto_version(tlsCtx, TLS1_3_VERSION); |
|||
if (ret == 0) { |
|||
LogPrintf("TLS: WARNING: %s: %s():%d - failed to set min TLS version\n", __FILE__, __func__, __LINE__); |
|||
} |
|||
|
|||
LogPrintf("TLS: %s: %s():%d - setting cipher list\n", __FILE__, __func__, __LINE__); |
|||
|
|||
// Default TLSv1.3 cipher list is "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256"
|
|||
// Nodes will randomly choose to prefer first cipher or the second, to create diversity on the network
|
|||
// and not be in the situation where all nodes have the same list so the first is always used
|
|||
if(GetRand(100) > 50) { |
|||
if (wolfSSL_CTX_set_cipher_list(tlsCtx, "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256")) { |
|||
LogPrintf("%s: Preferring TLS_AES256-GCM-SHA384\n", __func__); |
|||
TLS_CIPHERLIST = "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256"; |
|||
} else { |
|||
LogPrintf("%s: Setting preferred cipher failed !!!\n", __func__); |
|||
} |
|||
} else { |
|||
if (wolfSSL_CTX_set_cipher_list(tlsCtx, "TLS_CHACHA20_POLY1305_SHA256:TLS_AES_256_GCM_SHA384")) { |
|||
LogPrintf("%s: Preferring TLS_XCHACHA20_POLY1305\n", __func__); |
|||
// WolfSSL 4.6.0 added xchacha but calls it the same ciphersuite, which causes compatibility issues
|
|||
TLS_CIPHERLIST = "TLS_CHACHA20_POLY1305_SHA256:TLS_AES_256_GCM_SHA384"; |
|||
} else { |
|||
LogPrintf("%s: Setting preferred cipher failed !!!\n", __func__); |
|||
} |
|||
} |
|||
|
|||
if (ctxType == SERVER_CONTEXT) { |
|||
// In case server and client prefered ciphers are different, server preference has priority
|
|||
wolfSSL_CTX_set_options(tlsCtx, SSL_OP_CIPHER_SERVER_PREFERENCE); |
|||
|
|||
LogPrintf("TLS: %s: %s():%d - setting dh callback\n", __FILE__, __func__, __LINE__); |
|||
SSL_CTX_set_tmp_dh_callback(tlsCtx, tmp_dh_callback); |
|||
} |
|||
|
|||
// No certificate verification, all should be self-signed
|
|||
wolfSSL_CTX_set_verify(tlsCtx, WOLFSSL_VERIFY_NONE, NULL); |
|||
|
|||
WOLFSSL_EC_KEY *ec_key = NULL; |
|||
|
|||
ec_key = wolfSSL_EVP_PKEY_get0_EC_KEY(mykey); |
|||
|
|||
if (ec_key != NULL && wolfSSL_PEM_write_mem_ECPrivateKey(ec_key, NULL, NULL, 0, &pem, &plen)) { |
|||
if (wolfSSL_CTX_use_certificate(tlsCtx, mycert) > 0) { |
|||
if (wolfSSL_CTX_use_PrivateKey_buffer(tlsCtx, pem, plen, SSL_FILETYPE_PEM) > 0) { |
|||
|
|||
free(pem); |
|||
|
|||
if (wolfSSL_CTX_check_private_key(tlsCtx)) { |
|||
bInitialized = true; |
|||
} else { |
|||
LogPrintf("TLS: ERROR: %s: %s: private key does not match the certificate public key\n", __FILE__, __func__); |
|||
} |
|||
} else { |
|||
LogPrintf("TLS: ERROR: %s: %s: failed to use private key file\n", __FILE__, __func__); |
|||
} |
|||
} else { |
|||
LogPrintf("TLS: ERROR: %s: %s: failed to use certificate file\n", __FILE__, __func__); |
|||
wolfSSL_ERR_dump_errors_fp(stderr); |
|||
} |
|||
} |
|||
} else { |
|||
LogPrintf("TLS: ERROR: %s: %s: failed to create TLS context\n", __FILE__, __func__); |
|||
} |
|||
|
|||
if (!bInitialized) { |
|||
if (tlsCtx) { |
|||
wolfSSL_CTX_free(tlsCtx); |
|||
tlsCtx = NULL; |
|||
} |
|||
} |
|||
|
|||
return tlsCtx; |
|||
} |
|||
/**
|
|||
* @brief generates certificate credentials. |
|||
* |
|||
* @return true returns true is successful. |
|||
* @return false returns false if an error has occured. |
|||
*/ |
|||
bool TLSManager::prepareCredentials() |
|||
{ |
|||
mykey = NULL; |
|||
mycert = NULL; |
|||
|
|||
// Generating key and the self-signed certificate for it
|
|||
mykey = GenerateEcKey(); |
|||
if (mykey) { |
|||
mycert = GenerateCertificate(mykey); |
|||
if (mycert) { |
|||
if (CheckKeyCert()) { |
|||
LogPrintStr("TLS: New private key and self-signed certificate were generated successfully\n"); |
|||
|
|||
return true; |
|||
} |
|||
//wolfSSL_X509_free(mycert);
|
|||
} |
|||
//wolfSSL_EVP_PKEY_free(mykey);
|
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
bool TLSManager::CheckKeyCert() |
|||
{ |
|||
if (!mykey) { |
|||
LogPrintf("Key is not generated!!!\n"); |
|||
return false; |
|||
} |
|||
|
|||
if (!mycert) { |
|||
LogPrintf("Certificate is not generated!!!\n"); |
|||
return false; |
|||
} |
|||
|
|||
WOLFSSL_EC_KEY *eccKey = wolfSSL_EVP_PKEY_get1_EC_KEY(mykey); |
|||
if (eccKey && wc_ecc_check_key((ecc_key*)eccKey->internal) == 0) { |
|||
wolfSSL_EC_KEY_free(eccKey); |
|||
} else { |
|||
LogPrintf("Generated ECC key check failed!!!\n"); |
|||
return false; |
|||
} |
|||
|
|||
int err = wolfSSL_X509_verify(mycert, mykey); |
|||
if (err == WOLFSSL_SUCCESS) { |
|||
return true; |
|||
} else { |
|||
LogPrintf("%s: x509 verification error: %d = %s\n", __func__, err); |
|||
} |
|||
|
|||
LogPrintf("Generated key and certificate do not match!!!\n"); |
|||
|
|||
return false; |
|||
} |
|||
|
|||
|
|||
/**
|
|||
* @brief accept a TLS connection |
|||
* |
|||
* @param hSocket the TLS socket. |
|||
* @param addr incoming address. |
|||
* @param tls_ctx_server TLS server context. |
|||
* @return WOLFSSL* returns pointer to the ssl object if successful, otherwise returns NULL |
|||
*/ |
|||
WOLFSSL* TLSManager::accept(SOCKET hSocket, const CAddress& addr, unsigned long& err_code) |
|||
{ |
|||
LogPrint("tls", "TLS: accepting connection from %s (tid = %X)\n", addr.ToString(), pthread_self()); |
|||
|
|||
char err_buffer[1024]; |
|||
err_code = 0; |
|||
WOLFSSL* ssl = NULL; |
|||
bool bAcceptedTLS = false; |
|||
|
|||
if ((ssl = wolfSSL_new(tls_ctx_server))) { |
|||
if (wolfSSL_set_fd(ssl, hSocket)) { |
|||
int ret = TLSManager::waitFor(SSL_ACCEPT, hSocket, ssl, (DEFAULT_CONNECT_TIMEOUT / 1000), err_code); |
|||
if (ret == 1) { |
|||
bAcceptedTLS = true; |
|||
} else { |
|||
err_code = wolfSSL_ERR_get_error(); |
|||
} |
|||
} else { |
|||
LogPrint("tls", "TLS: %s: failed to set file descriptor for socket!\n", __func__, addr.ToString()); |
|||
} |
|||
} else { |
|||
err_code = wolfSSL_ERR_get_error(); |
|||
const char* error_str = wolfSSL_ERR_error_string(err_code, err_buffer); |
|||
LogPrint("tls", "TLS: %s: %s():%d - SSL_new failed err: %s\n", __FILE__, __func__, __LINE__, err_buffer); |
|||
} |
|||
|
|||
if (bAcceptedTLS) { |
|||
LogPrintf("TLS: connection from %s has been accepted (tlsv = %s 0x%04x / ssl = %s 0x%x ). Using cipher: %s\n", |
|||
addr.ToString(), wolfSSL_get_version(ssl), wolfSSL_version(ssl), wolfSSL_OpenSSL_version(), wolfSSL_lib_version_hex(), wolfSSL_get_cipher(ssl)); |
|||
|
|||
WOLFSSL_STACK *sk = wolfSSL_get_ciphers_compat(ssl); |
|||
for (int i = 0; i < wolfSSL_sk_SSL_CIPHER_num(sk); i++) { |
|||
const WOLFSSL_CIPHER *c = wolfSSL_sk_SSL_CIPHER_value(sk, i); |
|||
LogPrint("tls", "TLS: supporting cipher: %s\n", wolfSSL_CIPHER_get_name(c)); |
|||
} |
|||
} else { |
|||
LogPrintf("TLS: %s: %s():%d - TLS connection from %s failed (err_code 0x%X)\n", __FILE__, __func__, __LINE__, addr.ToString(), err_code); |
|||
|
|||
if (ssl) { |
|||
SSL_free(ssl); |
|||
ssl = NULL; |
|||
} |
|||
} |
|||
|
|||
return ssl; |
|||
} |
|||
|
|||
/**
|
|||
* @brief Determines whether a string exists in the non-TLS address pool. |
|||
* |
|||
* @param strAddr The address. |
|||
* @param vPool Pool to search in. |
|||
* @param cs reference to the corresponding CCriticalSection. |
|||
* @return true returns true if address exists in the given pool. |
|||
* @return false returns false if address doesnt exist in the given pool. |
|||
*/ |
|||
bool TLSManager::isNonTLSAddr(const string& strAddr, const vector<NODE_ADDR>& vPool, CCriticalSection& cs) |
|||
{ |
|||
//LOCK(cs);
|
|||
return (find(vPool.begin(), vPool.end(), NODE_ADDR(strAddr)) != vPool.end()); |
|||
} |
|||
|
|||
/**
|
|||
* @brief Removes non-TLS node addresses based on timeout. |
|||
* |
|||
* @param vPool |
|||
* @param cs |
|||
*/ |
|||
void TLSManager::cleanNonTLSPool(std::vector<NODE_ADDR>& vPool, CCriticalSection& cs) |
|||
{ |
|||
//LOCK(cs);
|
|||
|
|||
vector<NODE_ADDR> vDeleted; |
|||
|
|||
BOOST_FOREACH (NODE_ADDR nodeAddr, vPool) { |
|||
if ((GetTimeMillis() - nodeAddr.time) >= 900000) { |
|||
vDeleted.push_back(nodeAddr); |
|||
LogPrint("tls", "TLS: Node %s is deleted from the non-TLS pool\n", nodeAddr.ipAddr); |
|||
} |
|||
} |
|||
|
|||
BOOST_FOREACH (NODE_ADDR nodeAddrDeleted, vDeleted) { |
|||
vPool.erase( |
|||
remove( |
|||
vPool.begin(), |
|||
vPool.end(), |
|||
nodeAddrDeleted), |
|||
vPool.end()); |
|||
} |
|||
} |
|||
|
|||
// requires LOCK(cs_vSend)
|
|||
void SocketSendData(CNode *pnode) |
|||
{ |
|||
std::deque<CSerializeData>::iterator it = pnode->vSendMsg.begin(); |
|||
|
|||
while (it != pnode->vSendMsg.end()) { |
|||
const CSerializeData &data = *it; |
|||
assert(data.size() > pnode->nSendOffset); |
|||
|
|||
bool bIsSSL = false; |
|||
int nBytes = 0, nRet = 0; |
|||
{ |
|||
//LOCK(pnode->cs_hSocket);
|
|||
|
|||
if (pnode->hSocket == INVALID_SOCKET) |
|||
{ |
|||
LogPrint("net", "Send: connection with %s is already closed\n", pnode->addr.ToString()); |
|||
break; |
|||
} |
|||
|
|||
bIsSSL = (pnode->ssl != NULL); |
|||
|
|||
if (bIsSSL) |
|||
{ |
|||
wolfSSL_ERR_clear_error(); // clear the error queue, otherwise we may be reading an old error that occurred previously in the current thread
|
|||
nBytes = wolfSSL_write(pnode->ssl, &data[pnode->nSendOffset], data.size() - pnode->nSendOffset); |
|||
nRet = wolfSSL_get_error(pnode->ssl, nBytes); |
|||
} |
|||
else |
|||
{ |
|||
nBytes = send(pnode->hSocket, &data[pnode->nSendOffset], data.size() - pnode->nSendOffset, MSG_NOSIGNAL | MSG_DONTWAIT); |
|||
nRet = WSAGetLastError(); |
|||
} |
|||
} |
|||
if (nBytes > 0) |
|||
{ |
|||
pnode->nLastSend = GetTime(); |
|||
pnode->nSendBytes += nBytes; |
|||
pnode->nSendOffset += nBytes; |
|||
pnode->RecordBytesSent(nBytes); |
|||
if (pnode->nSendOffset == data.size()) { |
|||
pnode->nSendOffset = 0; |
|||
pnode->nSendSize -= data.size(); |
|||
it++; |
|||
} else { |
|||
// could not send full message; stop sending more
|
|||
break; |
|||
} |
|||
} else { |
|||
if (nBytes <= 0) { |
|||
// error
|
|||
if (bIsSSL) |
|||
{ |
|||
if (nRet != WOLFSSL_ERROR_WANT_READ && nRet != WOLFSSL_ERROR_WANT_WRITE) |
|||
{ |
|||
LogPrintf("ERROR: SSL_write %s; closing connection\n", wolfSSL_ERR_error_string(nRet, NULL)); |
|||
pnode->CloseSocketDisconnect(); |
|||
} else { |
|||
// preventive measure from exhausting CPU usage
|
|||
MilliSleep(1); // 1 msec
|
|||
} |
|||
} else { |
|||
if (nRet != WSAEWOULDBLOCK && nRet != WSAEMSGSIZE && nRet != WSAEINTR && nRet != WSAEINPROGRESS) |
|||
{ |
|||
LogPrintf("ERROR: send %s; closing connection\n", NetworkErrorString(nRet)); |
|||
pnode->CloseSocketDisconnect(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
// couldn't send anything at all
|
|||
break; |
|||
} |
|||
} |
|||
|
|||
if (it == pnode->vSendMsg.end()) { |
|||
assert(pnode->nSendOffset == 0); |
|||
assert(pnode->nSendSize == 0); |
|||
} |
|||
pnode->vSendMsg.erase(pnode->vSendMsg.begin(), it); |
|||
} |
|||
|
|||
/**
|
|||
* @brief Handles send and recieve functionality in TLS Sockets. |
|||
* |
|||
* @param pnode reference to the CNode object. |
|||
* @param fdsetRecv |
|||
* @param fdsetSend |
|||
* @param fdsetError |
|||
* @return int returns -1 when socket is invalid. returns 0 otherwise. |
|||
*/ |
|||
int TLSManager::threadSocketHandler(CNode* pnode, fd_set& fdsetRecv, fd_set& fdsetSend, fd_set& fdsetError) |
|||
{ |
|||
//
|
|||
// Receive
|
|||
//
|
|||
bool recvSet = false, sendSet = false, errorSet = false; |
|||
|
|||
{ |
|||
//LOCK(pnode->cs_hSocket);
|
|||
|
|||
if (pnode->hSocket == INVALID_SOCKET) |
|||
return -1; |
|||
|
|||
recvSet = FD_ISSET(pnode->hSocket, &fdsetRecv); |
|||
sendSet = FD_ISSET(pnode->hSocket, &fdsetSend); |
|||
errorSet = FD_ISSET(pnode->hSocket, &fdsetError); |
|||
} |
|||
|
|||
if (recvSet || errorSet) { |
|||
//TRY_LOCK(pnode->cs_vRecvMsg, lockRecv);
|
|||
//if (lockRecv) {
|
|||
{ |
|||
// typical socket buffer is 8K-64K
|
|||
// maximum record size is 16kB for SSLv3/TLSv1
|
|||
char pchBuf[0x10000]; |
|||
bool bIsSSL = false; |
|||
int nBytes = 0, nRet = 0; |
|||
|
|||
{ |
|||
//LOCK(pnode->cs_hSocket);
|
|||
|
|||
if (pnode->hSocket == INVALID_SOCKET) { |
|||
LogPrint("tls", "Receive: connection with %s is already closed\n", pnode->addr.ToString()); |
|||
return -1; |
|||
} |
|||
|
|||
bIsSSL = (pnode->ssl != NULL); |
|||
|
|||
if (bIsSSL) { |
|||
wolfSSL_ERR_clear_error(); // clear the error queue, otherwise we may be reading an old error that occurred previously in the current thread
|
|||
nBytes = wolfSSL_read(pnode->ssl, pchBuf, sizeof(pchBuf)); |
|||
nRet = wolfSSL_get_error(pnode->ssl, nBytes); |
|||
} else { |
|||
nBytes = recv(pnode->hSocket, pchBuf, sizeof(pchBuf), MSG_DONTWAIT); |
|||
nRet = WSAGetLastError(); |
|||
} |
|||
} |
|||
|
|||
if (nBytes > 0) { |
|||
if (!pnode->ReceiveMsgBytes(pchBuf, nBytes)) |
|||
pnode->CloseSocketDisconnect(); |
|||
pnode->nLastRecv = GetTime(); |
|||
pnode->nRecvBytes += nBytes; |
|||
pnode->RecordBytesRecv(nBytes); |
|||
} else if (nBytes == 0) { |
|||
|
|||
if (bIsSSL) { |
|||
unsigned long error = ERR_get_error(); |
|||
const char* error_str = wolfSSL_ERR_error_string(error, NULL); |
|||
LogPrint("tls", "TLS: WARNING: %s: %s():%d - SSL_read err: %s\n", |
|||
__FILE__, __func__, __LINE__, error_str); |
|||
} |
|||
// socket closed gracefully (peer disconnected)
|
|||
if (!pnode->fDisconnect) |
|||
LogPrint("tls", "socket closed (%s)\n", pnode->addr.ToString()); |
|||
pnode->CloseSocketDisconnect(); |
|||
|
|||
} else if (nBytes < 0) { |
|||
// error
|
|||
if (bIsSSL) { |
|||
if (nRet != WOLFSSL_ERROR_WANT_READ && nRet != WOLFSSL_ERROR_WANT_WRITE) |
|||
{ |
|||
if (!pnode->fDisconnect) |
|||
LogPrintf("TSL: ERROR: SSL_read %s\n", wolfSSL_ERR_error_string(nRet, NULL)); |
|||
pnode->CloseSocketDisconnect(); |
|||
|
|||
unsigned long error = wolfSSL_ERR_get_error(); |
|||
const char* error_str = wolfSSL_ERR_error_string(error, NULL); |
|||
LogPrint("tls", "TLS: WARNING: %s: %s():%d - SSL_read - code[0x%x], err: %s\n", |
|||
__FILE__, __func__, __LINE__, nRet, error_str); |
|||
|
|||
} else { |
|||
// preventive measure from exhausting CPU usage
|
|||
MilliSleep(1); // 1 msec
|
|||
} |
|||
} else { |
|||
if (nRet != WSAEWOULDBLOCK && nRet != WSAEMSGSIZE && nRet != WSAEINTR && nRet != WSAEINPROGRESS) { |
|||
if (!pnode->fDisconnect) |
|||
LogPrintf("TSL: ERROR: socket recv %s\n", NetworkErrorString(nRet)); |
|||
pnode->CloseSocketDisconnect(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
//}
|
|||
} |
|||
|
|||
// Send
|
|||
if (sendSet) { |
|||
//TRY_LOCK(pnode->cs_vSend, lockSend);
|
|||
//if (lockSend)
|
|||
SocketSendData(pnode); |
|||
} |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
/**
|
|||
* @brief Initialization of the server and client contexts |
|||
* |
|||
* @return true returns True if successful. |
|||
* @return false returns False if an error has occured. |
|||
*/ |
|||
bool TLSManager::initialize() |
|||
{ |
|||
bool bInitializationStatus = false; |
|||
|
|||
// Initialization routines for the WolfSSL library
|
|||
wolfSSL_load_error_strings(); |
|||
wolfSSL_ERR_load_crypto_strings(); |
|||
wolfSSL_library_init(); |
|||
|
|||
// Initialization of the server and client contexts
|
|||
if ((tls_ctx_server = TLSManager::initCtx(SERVER_CONTEXT))) { |
|||
if ((tls_ctx_client = TLSManager::initCtx(CLIENT_CONTEXT))) { |
|||
LogPrint("tls", "TLS: contexts are initialized\n"); |
|||
bInitializationStatus = true; |
|||
} else { |
|||
LogPrintf("TLS: ERROR: %s: %s: failed to initialize TLS client context\n", __FILE__, __func__); |
|||
wolfSSL_CTX_free (tls_ctx_server); |
|||
} |
|||
} else { |
|||
LogPrintf("TLS: ERROR: %s: %s: failed to initialize TLS server context\n", __FILE__, __func__); |
|||
} |
|||
|
|||
return bInitializationStatus; |
|||
} |
|||
} |
@ -0,0 +1,69 @@ |
|||
// Copyright (c) 2016-2021 The Hush developers
|
|||
// Distributed under the GPLv3 software license, see the accompanying
|
|||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
|||
|
|||
#ifndef HUSH_TLSMANAGER_H |
|||
#define HUSH_TLSMANAGER_H |
|||
|
|||
#include <wolfssl/options.h> |
|||
#include <wolfssl/ssl.h> |
|||
#include "tlsenums.h" |
|||
#include <boost/filesystem.hpp> |
|||
#include <boost/thread.hpp> |
|||
#include "node.h" |
|||
//#include "../util.h"
|
|||
//#include "../net.h"
|
|||
//#include "sync.h"
|
|||
#include <boost/filesystem/path.hpp> |
|||
#include <boost/foreach.hpp> |
|||
#include <boost/signals2/signal.hpp> |
|||
#ifdef WIN32 |
|||
#include <string.h> |
|||
#else |
|||
#include <fcntl.h> |
|||
#endif |
|||
|
|||
#include "node.h" |
|||
|
|||
typedef u_int SOCKET; |
|||
|
|||
using namespace std; |
|||
|
|||
namespace hush |
|||
{ |
|||
typedef struct _NODE_ADDR { |
|||
std::string ipAddr; |
|||
int64_t time; // time in msec, of an attempt to connect via TLS
|
|||
|
|||
_NODE_ADDR(std::string _ipAddr, int64_t _time = 0) : ipAddr(_ipAddr), time(_time) {} |
|||
bool operator==(const _NODE_ADDR b) const |
|||
{ |
|||
return (ipAddr == b.ipAddr); |
|||
} |
|||
} NODE_ADDR, *PNODE_ADDR; |
|||
|
|||
// A class to wrap some of hush specific TLS functionalities used in the net.cpp
|
|||
class TLSManager |
|||
{ |
|||
public: |
|||
/* This is set as a custom error number which is not an error in SSL protocol.
|
|||
A true (not null) SSL error returned by ERR_get_error() consists of a library number, |
|||
function code and reason code. */ |
|||
static const long SELECT_TIMEDOUT = 0xFFFFFFFF; |
|||
|
|||
int waitFor(SSLConnectionRoutine eRoutine, SOCKET hSocket, WOLFSSL* ssl, int timeoutSec, unsigned long& err_code); |
|||
|
|||
WOLFSSL* connect(SOCKET hSocket, const CAddress& addrConnect, unsigned long& err_code); |
|||
WOLFSSL_CTX* initCtx(TLSContextType ctxType); |
|||
bool prepareCredentials(); |
|||
WOLFSSL* accept(SOCKET hSocket, const CAddress& addr, unsigned long& err_code); |
|||
bool isNonTLSAddr(const string& strAddr, const vector<NODE_ADDR>& vPool, CCriticalSection& cs); |
|||
void cleanNonTLSPool(std::vector<NODE_ADDR>& vPool, CCriticalSection& cs); |
|||
//TODO
|
|||
//int threadSocketHandler(CNode* pnode, fd_set& fdsetRecv, fd_set& fdsetSend, fd_set& fdsetError);
|
|||
bool initialize(); |
|||
bool CheckKeyCert(); |
|||
}; |
|||
} |
|||
|
|||
#endif // HUSH_TLSMANAGER_H
|
@ -0,0 +1,18 @@ |
|||
// Copyright (c) 2016-2021 The Hush developers
|
|||
// Copyright (c) 2017 The Zen Core developers
|
|||
// Distributed under the GPLv3 software license, see the accompanying
|
|||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
|||
|
|||
#ifndef UTILTLS_H |
|||
#define UTILTLS_H |
|||
|
|||
namespace hush { |
|||
|
|||
#define CERT_VALIDITY_DAYS (365 * 10) // period of validity, in days, for a self-signed certificate
|
|||
|
|||
WOLFSSL_EVP_PKEY* GenerateEcKey(int nid = NID_X9_62_prime256v1); |
|||
WOLFSSL_X509* GenerateCertificate(WOLFSSL_EVP_PKEY *keypair); |
|||
|
|||
} |
|||
|
|||
#endif // UTILTLS_H
|
@ -0,0 +1,44 @@ |
|||
#!/bin/sh |
|||
## |
|||
# This script contains helper for sha256 validating your downloads |
|||
# |
|||
# Source: https://gist.github.com/onnimonni/b49779ebc96216771a6be3de46449fa1 |
|||
# Author: Onni Hakala |
|||
# License: MIT |
|||
## |
|||
|
|||
# Stop program and give error message |
|||
# $1 - error message (string) |
|||
error () { |
|||
echo "ERROR: $1" |
|||
exit 1 |
|||
} |
|||
|
|||
# return sha256sum for file |
|||
# $1 - filename (string) |
|||
get_sha256sum() { |
|||
cat $1 | sha256sum | head -c 64 |
|||
} |
|||
|
|||
# Good variable names pls |
|||
filename=$1 |
|||
file_hash=$2 |
|||
|
|||
# Check input |
|||
if [ -z "$filename" ]; then |
|||
error "You need to provide filename in first parameter" |
|||
fi |
|||
|
|||
if [ -z "$file_hash" ]; then |
|||
error "You need to provide sha256sum in second parameter" |
|||
fi |
|||
|
|||
# Check if the file is valid |
|||
if [ ! -f $filename ]; |
|||
then |
|||
error "File $filename doesn't exist" |
|||
elif [ "$(get_sha256sum $filename)" = "$file_hash" ]; then |
|||
echo "Success: $filename matches provided sha256sum" |
|||
else |
|||
error "$filename doesn't match provided sha256sum" |
|||
fi |
@ -0,0 +1,48 @@ |
|||
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
|||
// Copyright (c) 2009-2013 The Bitcoin Core developers
|
|||
// Copyright (c) 2016-2021 The Hush developers
|
|||
// Distributed under the GPLv3 software license, see the accompanying
|
|||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
|||
|
|||
#ifndef HUSH_SUPPORT_ALLOCATORS_ZEROAFTERFREE_H |
|||
#define HUSH_SUPPORT_ALLOCATORS_ZEROAFTERFREE_H |
|||
|
|||
#include "cleanse.h" |
|||
#include <memory> |
|||
#include <vector> |
|||
|
|||
template <typename T> |
|||
struct zero_after_free_allocator : public std::allocator<T> { |
|||
// MSVC8 default copy constructor is broken
|
|||
typedef std::allocator<T> base; |
|||
typedef typename base::size_type size_type; |
|||
typedef typename base::difference_type difference_type; |
|||
typedef typename base::pointer pointer; |
|||
typedef typename base::const_pointer const_pointer; |
|||
typedef typename base::reference reference; |
|||
typedef typename base::const_reference const_reference; |
|||
typedef typename base::value_type value_type; |
|||
zero_after_free_allocator() throw() {} |
|||
zero_after_free_allocator(const zero_after_free_allocator& a) throw() : base(a) {} |
|||
template <typename U> |
|||
zero_after_free_allocator(const zero_after_free_allocator<U>& a) throw() : base(a) |
|||
{ |
|||
} |
|||
~zero_after_free_allocator() throw() {} |
|||
template <typename _Other> |
|||
struct rebind { |
|||
typedef zero_after_free_allocator<_Other> other; |
|||
}; |
|||
|
|||
void deallocate(T* p, std::size_t n) |
|||
{ |
|||
if (p != NULL) |
|||
memory_cleanse(p, sizeof(T) * n); |
|||
std::allocator<T>::deallocate(p, n); |
|||
} |
|||
}; |
|||
|
|||
// Byte-vector that clears its contents before deletion.
|
|||
typedef std::vector<char, zero_after_free_allocator<char> > CSerializeData; |
|||
|
|||
#endif // HUSH_SUPPORT_ALLOCATORS_ZEROAFTERFREE_H
|
Loading…
Reference in new issue