Compare commits

...

39 Commits
master ... dev

Author SHA1 Message Date
Duke Leto 6006bfc9e9 Add CMessageHeader 3 years ago
Duke Leto fe16c226bd Add CNetMessage 3 years ago
Duke Leto 5b5fb4d34f Make CNode findable from bitcoin.cpp 3 years ago
Duke Leto 25568ac37f More compile fixes 3 years ago
Duke Leto dc60063e08 Comment out all locking code in TLSManager, #yolo 3 years ago
Duke Leto 492a5419ac Merge remote-tracking branch 'origin/dev' into dev 3 years ago
jahway603 d121c604a4 added sha256 checking 3 years ago
Duke Leto 6403d4f751 Port various headers from hush3.git that are needed 3 years ago
Duke Leto 1a47b64f5c Fix multiple declaration error and add members to CNode 3 years ago
Duke Leto 97139c89ae Fix more compile errors 3 years ago
Duke Leto 724463edae Locks gone wild 3 years ago
Duke Leto f961295b8d Add SocketSendData 3 years ago
Duke Leto d7dd704297 Add CloseSocketDisconnect from hushd 3 years ago
Duke Leto 4e51939b00 compile fixes 3 years ago
Duke Leto c776d4d1bc fix more compile errors 3 years ago
Duke Leto 73e60f04b9 add forgotten node.h 3 years ago
Duke Leto 7dd7083e64 Fix GetTimeMillis and MilliSleep errors 3 years ago
Duke Leto 075bee803e Make the compiler gods slightly less angry 3 years ago
Duke Leto 8a2a8b11fc Make it so tlsmanager.cpp can find CNode declaration 3 years ago
Duke Leto 5081cfa5e6 . 3 years ago
Duke Leto 5da558316d Fix more compile errors 3 years ago
Duke Leto 81238bead0 Include wolfssl headers in dnsseed and some needed globals 3 years ago
Duke Leto 7e80cbcaff fd_set fixes 3 years ago
Duke Leto f6b49e37f5 Fix SOCKET 3 years ago
Duke Leto 16d0193cbf Get rid of trivial compile issues for now so we can concentrate on important ones 3 years ago
Duke Leto c8dfdd42e7 note about wolfssl compile flags 3 years ago
Duke Leto a2fb56d315 Look for headers in wolfssl/ and don't compile wolfssl if dir exists 3 years ago
Duke Leto 0edeaa6dc6 Merge remote-tracking branch 'origin/dev' into dev 3 years ago
Duke Leto 0838a3fdde Start porting TLSManager from hush3.git 3 years ago
jahway603 816a9250d8 undid changes which did not work 3 years ago
jahway603 75c475691f minor markdown edit 3 years ago
jahway603 1eff9ebf69 minor markdown edit 3 years ago
jahway603 9c7502be1b minor Makefile and README.md improvements 3 years ago
jahway603 75e06476a3 testing out different things in the Makefile to get wolfSSL working on any Linux system 3 years ago
jahway603 abf390a633 updated Makefile with wolfssl build options 3 years ago
jahway603 99a010cbde reverted wolfssl back to 4.6.0 3 years ago
jahway603 9d0fac99a7 updated wolfssl to 4.7.0 3 years ago
Duke Leto b2b504b630 . 3 years ago
Duke Leto 398e23c2d3 Download and compile wolfssl 4.6.0 3 years ago
  1. 2
      .gitignore
  2. 2
      AUTHORS
  3. 68
      Makefile
  4. 41
      README.md
  5. 283
      bitcoin.cpp
  6. 25
      build_wolfssl.sh
  7. 35
      cleanse.cpp
  8. 15
      cleanse.h
  9. 2
      main.cpp
  10. 6
      netbase.cpp
  11. 2
      netbase.h
  12. 533
      node.h
  13. 7
      pre-req-install.sh
  14. 296
      sync.h
  15. 55
      threadsafety.h
  16. 9
      tlsenums.h
  17. 819
      tlsmanager.cpp
  18. 69
      tlsmanager.h
  19. 18
      utiltls.h
  20. 44
      validate_sha256sum
  21. 48
      zeroafterfree.h

2
.gitignore

@ -1,3 +1,5 @@
*.o
dnsseed*
dnsstats.log
*.sw?
core

2
AUTHORS

@ -1,4 +1,4 @@
# The Hush Developers
Duke Leto https://git.hush.is/duke
Jah Way https://git.hush.is/jahway603

68
Makefile

@ -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

41
README.md

@ -1,7 +1,8 @@
## HUSH Seeder
## HUSH DNS Seeder
hush-seeder is a crawler for the HUSH network, based on [bitcoin-seeder](https://github.com/sipa/bitcoin-seeder), which exposes a list
of reliable nodes via a built-in DNS server.
hush-seeder is a crawler for the HUSH network, based on
[bitcoin-seeder](https://github.com/sipa/bitcoin-seeder),
which exposes a list of reliable nodes via a built-in DNS server.
Features:
* regularly revisits known nodes to check their availability
@ -14,7 +15,13 @@ Features:
## REQUIREMENTS
sudo apt-get install build-essential libboost-all-dev libssl-dev
1. Before doing anything, install these first. Below are the package
names for Debian and Ubuntu based systems:
```sudo apt-get install build-essential libboost-all-dev libboost-dev libssl-dev```
1. Next if you have your Linux package manager's version of wolfSSL installed,
then uninstall it as this Makefile will most likely create some form of a conflict.
## USAGE
@ -27,8 +34,10 @@ to for example vps.example.com:
dig -t NS dnsseed.example.com
```
;; ANSWER SECTION
dnsseed.example.com. 86400 IN NS vps.example.com.
```
On the system vps.example.com, you can now run dnsseed:
@ -39,15 +48,31 @@ e-mail address (with the @ part replaced by .) using -m.
## COMPILING
Compiling will require boost and ssl. On debian systems, these are provided
by `libboost-dev` and `libssl-dev` respectively.
1. Compiling will require you to first install the packages listed under REQUIREMENTS
at the top of this document.
1. Then continue below:
sudo apt-get install libboost-dev libssl-dev
git clone https://git.hush.is/hush/hush-seeder
cd hush-seeder
make
make uninstall-wolfssl
1. This will produce the `dnsseed` binary.
### INSTALL TO SYSTEM
If you want to install dnsseed on your system, then you can run the following:
This will produce the `dnsseed` binary.
cd hush-seeder
make install
### UNINSTALL FROM SYSTEM
If you want to uninstall dnsseed from your system, then you can run the following:
cd hush-seeder
make uninstall
## RUNNING AS NON-ROOT

283
bitcoin.cpp

@ -3,282 +3,17 @@
// file LICENSE or https://www.gnu.org/licenses/gpl-3.0.en.html
#include <algorithm>
#include "coin.h"
#include "db.h"
#include "netbase.h"
#include "protocol.h"
#include "serialize.h"
#include "uint256.h"
#define HUSH_SEED_NONCE 0x0539a019ca550825ULL
//#include "coin.h"
//#include "db.h"
//#include "netbase.h"
//#include "protocol.h"
//#include "serialize.h"
//#include "uint256.h"
#include "node.h"
using namespace std;
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:
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);
}
}
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 {
bool TestNode(const CService &cip, int &ban, int &clientV, std::string &clientSV, int &blocks, vector<CAddress>* vAddr) {
try {
@ -300,6 +35,8 @@ bool TestNode(const CService &cip, int &ban, int &clientV, std::string &clientSV
}
}
}
/*
int main(void) {
CService ip("bitcoin.sipa.be", 8333, true);

25
build_wolfssl.sh

@ -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

35
cleanse.cpp

@ -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
}

15
cleanse.h

@ -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

2
main.cpp

@ -11,6 +11,8 @@
#include <stdlib.h>
#include <getopt.h>
#include <atomic>
//#include <wolfssl/options.h>
//#include <wolfssl/ssl.h>
#include "bitcoin.h"
#include "db.h"

6
netbase.cpp

@ -16,6 +16,11 @@
#define printf my_printf
#include <wolfssl/options.h>
#include <wolfssl/ssl.h>
TLSManager tlsmanager = TLSManager();
using namespace std;
// Settings
@ -24,6 +29,7 @@ static proxyType proxyInfo[NET_MAX];
static proxyType nameproxyInfo;
int nConnectTimeout = 5000;
bool fNameLookup = false;
WOLFSSL_CTX *tls_ctx_server, *tls_ctx_client;
static const unsigned char pchIPv4[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff };

2
netbase.h

@ -29,6 +29,8 @@ enum Network
NET_MAX,
};
static const int DEFAULT_CONNECT_TIMEOUT = 5000;
extern int nConnectTimeout;
extern bool fNameLookup;

533
node.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

7
pre-req-install.sh

@ -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

296
sync.h

@ -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

55
threadsafety.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

9
tlsenums.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;
}

819
tlsmanager.cpp

@ -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;
}
}

69
tlsmanager.h

@ -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

18
utiltls.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

44
validate_sha256sum

@ -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

48
zeroafterfree.h

@ -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…
Cancel
Save