Compare commits

..

No commits in common. "master" and "v1.0.1" have entirely different histories.

7 changed files with 115 additions and 641 deletions

View File

@ -72,7 +72,9 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<TargetExt>64.lib</TargetExt>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<TargetExt>.lib</TargetExt>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
@ -128,8 +130,6 @@
<Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<AdditionalLibraryDirectories>$(SolutionDir)libs\win_crypto++\;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>cryptlib.lib;winsqlite3.lib;shlwapi.lib;Crypt32.lib;Ws2_32.lib;Mswsock.lib;AdvApi32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<Lib>
<AdditionalLibraryDirectories>$(SolutionDir)libs\win_crypto++\;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>

View File

@ -7,21 +7,6 @@
namespace Crypto {
static unsigned long x = 123456789, y = 362436069, z = 521288629;
unsigned long xorshf96(void) { //period 2^96-1
unsigned long t;
x ^= x << 16;
x ^= x >> 5;
x ^= x << 1;
t = x;
x = y;
y = z;
z = t ^ x ^ y;
return z;
}
namespace AES {
// -------- AES START --------
@ -53,7 +38,7 @@ namespace Crypto {
(*resultingSize) = t;
char* cipher = (char*)new char[t];
char* cipher = (char*)malloc(t);
memcpy(cipher, ciphertext.c_str(), t);
return cipher;
@ -76,11 +61,11 @@ namespace Crypto {
*resultSize = decryptedtext.size();
char* c = (char*)new char[*resultSize];
char* c = (char*)malloc(*resultSize);
//memset(c, 0, decryptedtext.size());
memcpy(c, decryptedtext.c_str(), decryptedtext.size());
//decryptedtext.~basic_string();
decryptedtext.~basic_string();
return c;
}
@ -89,15 +74,9 @@ namespace Crypto {
Payload aes_auto_encrypt(void* msg, ulong_64b size) {
char* message = (char*)msg;
Payload p;
srand(time(NULL));
// Generate random number 'n stuff
for (volatile unsigned int i = ((unsigned int)rand())%4096; i > 0; --i) xorshf96();
time_t t = (time_t) xorshf96();
if (sizeof(long) < sizeof(time_t)) t ^= xorshf96() << ((sizeof(time_t) / 2) + (sizeof(time_t)%2)); // Fill ALL the bits!
srand(time(NULL)+(signed long long)t);
p.key = (char*)new char[sizeof(AES_KEY)];
p.key = (char*)malloc(sizeof(AES_KEY));
AES_KEY k = (AES_KEY)rand();
p.keySize = sizeof(AES_KEY);
@ -163,15 +142,9 @@ namespace Crypto {
}
namespace RSA {
KeyData::KeyData(CryptoPP::RSA::PrivateKey *priv, CryptoPP::RSA::PublicKey *pub) : privKey(priv), publKey(pub) { }
KeyData::~KeyData() {
delete privKey;
delete publKey;
}
// -------- RSA START --------
KeyData* rsa_gen_keys() {
KeyData rsa_gen_keys() {
KeyData k;
CryptoPP::InvertibleRSAFunction params;
CryptoPP::RandomPool rng;
@ -180,8 +153,8 @@ namespace Crypto {
rng.IncorporateEntropy((const byte*)&t, sizeof(t) * 8);
params.GenerateRandomWithKeySize(rng, 3072);
KeyData *k = new KeyData(new CryptoPP::RSA::PrivateKey(params), new CryptoPP::RSA::PublicKey(params));
k.privKey = CryptoPP::RSA::PrivateKey(params);
k.publKey = CryptoPP::RSA::PublicKey(params);
return k;
}
@ -191,7 +164,7 @@ namespace Crypto {
//func.DEREncodePublicKey(queue);
byte* shortened = (byte*)new byte[*rSize=queue.TotalBytesRetrievable()];
byte* shortened = (byte*)malloc(*rSize=queue.TotalBytesRetrievable());
memset(shortened, 0, *rSize);
std::vector<byte> spk;
@ -205,44 +178,6 @@ namespace Crypto {
return (char*)shortened;
}
char* serializePrivKey(CryptoPP::RSA::PrivateKey& func, ulong_64b* rSize) {
CryptoPP::ByteQueue queue;
func.Save(queue);
//func.DEREncodePublicKey(queue);
byte* shortened = (byte*)new byte[*rSize = queue.TotalBytesRetrievable()];
memset(shortened, 0, *rSize);
std::vector<byte> spk;
spk.resize(queue.TotalBytesRetrievable());
CryptoPP::ArraySink snk(&spk[0], spk.size());
queue.CopyTo(snk);
for (ulong_64b t = 0; t < spk.size(); ++t) shortened[t] = spk.at(t);
return (char*)shortened;
}
CryptoPP::RSA::PublicKey* deserializePublicKey(char* c, ulong_64b size) {
CryptoPP::RSA::PublicKey *pK = new CryptoPP::RSA::PublicKey();
CryptoPP::StringSource src((const byte*)c, size, true);
pK->Load(src);
return pK;
}
CryptoPP::RSA::PrivateKey* deserializePrivateKey(char* c, ulong_64b size) {
CryptoPP::RSA::PrivateKey *pK = new CryptoPP::RSA::PrivateKey();
CryptoPP::StringSource src((const byte*)c, size, true);
pK->Load(src);
return pK;
}
char* rsa_encrypt(void* msg, ulong_64b size, CryptoPP::RSA::PublicKey& pubKey, ulong_64b* resultingSize) {
char* message = (char*)msg;
CryptoPP::RandomPool rng;
@ -256,10 +191,9 @@ namespace Crypto {
*resultingSize = cipher.size();
char* c = (char*)new char[cipher.size()];
//memset(c, 0, cipher.size());
char* c = (char*)malloc(cipher.size());
memset(c, 0, cipher.size());
memcpy(c, cipher.c_str(), cipher.size());
return c;
}
@ -276,11 +210,9 @@ namespace Crypto {
*resultingSize = clear.size();
char* c = (char*)new char[clear.size()];
//memset(c, 0, clear.size());
char* c = (char*)malloc(clear.size());
memset(c, 0, clear.size());
memcpy(c, clear.c_str(), clear.size());
return c;
}
// -------- RSA END --------
@ -288,21 +220,14 @@ namespace Crypto {
char* full_auto_encrypt(void* msg, ulong_64b mSize, CryptoPP::RSA::PublicKey& pk, ulong_64b* rSize) {
AES::Payload p = AES::aes_auto_encrypt(msg, mSize);
char *c = RSA::rsa_encrypt(p.key, p.keySize, pk, &p.keySize);
delete[] p.key;
p.key = c;
p.key = RSA::rsa_encrypt(p.key, p.keySize, pk, &p.keySize);
return p.serialize(rSize);
}
char* full_auto_decrypt(void* msg, CryptoPP::RSA::PrivateKey& pk, ulong_64b* rSize) {
ulong_64b size;
AES::Payload p = AES::deserializePayload(msg, &size);
char *c = RSA::rsa_decrypt(p.key, p.keySize, pk, &p.keySize);
delete[] p.key;
p.key = c;
c = AES::aes_auto_decrypt(p, rSize);
delete[] p.key;
delete[] p.ldPayload;
return c;
p.key = RSA::rsa_decrypt(p.key, p.keySize, pk, &p.keySize);
return AES::aes_auto_decrypt(p, rSize);
}
}

View File

@ -50,18 +50,13 @@ namespace Crypto {
namespace RSA {
struct KeyData {
const CryptoPP::RSA::PrivateKey *privKey;
const CryptoPP::RSA::PublicKey *publKey;
KeyData(CryptoPP::RSA::PrivateKey*, CryptoPP::RSA::PublicKey*);
~KeyData();
CryptoPP::RSA::PrivateKey privKey;
CryptoPP::RSA::PublicKey publKey;
};
char* serializeKey(CryptoPP::RSA::PublicKey&, ulong_64b* rSize);
char* serializePrivKey(CryptoPP::RSA::PrivateKey&, ulong_64b* rSize);
CryptoPP::RSA::PublicKey* deserializePublicKey(char* c, ulong_64b size);
CryptoPP::RSA::PrivateKey* deserializePrivateKey(char* c, ulong_64b size);
KeyData* rsa_gen_keys();
KeyData rsa_gen_keys();
char* rsa_encrypt(void* message, ulong_64b size, CryptoPP::RSA::PublicKey& pubKey, ulong_64b* resultingSize);
char* rsa_decrypt(void* message, ulong_64b size, CryptoPP::RSA::PrivateKey& privKey, ulong_64b* resultingSize);
}

View File

@ -1,73 +1,41 @@
#include "Net.h"
#include "Support.h"
#include <future>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <windows.h>
#include <ws2tcpip.h>
#include <iostream>
namespace IO {
AsyncKeys::AsyncKeys() {
gen = std::async(std::launch::async, [this]() {
Crypto::RSA::KeyData *data = Crypto::RSA::rsa_gen_keys();
done = true;
return data;
});
done = suppressDelete = false;
chain = false;
}
AsyncKeys::AsyncKeys(Crypto::RSA::KeyData* predef) {
done = suppressDelete = true;
keys = predef;
chain = false;
}
AsyncKeys::AsyncKeys(AsyncKeys* chainKeys) {
this->chainKeys = chainKeys;
chain = true;
}
AsyncKeys::~AsyncKeys() {
if (!chain && !suppressDelete) delete keys;
}
Crypto::RSA::KeyData* AsyncKeys::get() {
if (chain) return chainKeys->get();
if (!done) {
keys = gen.get();
bool connected(SOCKET sock)
{
char buf;
int err = recv(sock, &buf, 1, MSG_PEEK);
if (err == SOCKET_ERROR)
{
if (WSAGetLastError() != WSAEWOULDBLOCK)
{
return false;
}
}
return keys;
return true;
}
bool cryptoLevelsAreCompatible(CryptoLevel l1, CryptoLevel l2) {
return !(((l1 == CryptoLevel::None) && (l2 == CryptoLevel::Force)) || ((l2 == CryptoLevel::None) && (l1 == CryptoLevel::Force)));
}
char* __cdecl readSparse(std::vector<char>* sparse, ulong_64b rSize, bool pop = true) {
if (sparse->size() < rSize) throw new std::exception(); // This should never happen if function is used correctly
if (sparse->size() < rSize) throw new _exception(); // This should never happen if function is used correctly
char* c = new char[rSize];
for (ulong_64b b = 0; b < rSize; ++b) c[b] = sparse->at(b);
if (pop) sparse->erase(sparse->begin(), sparse->begin() + rSize);
return c;
}
void flushPrecedingPings(std::vector<char>* sparse) {
while (sparse->size() >= sizeof(ulong_64b)) {
ulong_64b size = 0;
char* c = readSparse(sparse, sizeof(ulong_64b), false);
memcpy(&size, c, sizeof(ulong_64b));
delete[] c;
if (size != FLAG_PING) return;
else delete[] readSparse(sparse, sizeof(ulong_64b)); // If this block is a ping packet, remove it
}
}
bool hasFullMessage(std::vector<char> *sparse) {
if (sparse->size() < sizeof(ulong_64b)) return false;
flushPrecedingPings(sparse);
ulong_64b size = 0;
char* c = readSparse(sparse, sizeof(ulong_64b), false);
memcpy(&size, c, sizeof(ulong_64b));
@ -77,30 +45,27 @@ namespace IO {
void NetClient::sharedSetup(bool setupKeys) {
if (setupKeys && (preferEncrypted != CryptoLevel::None)) keyData = new AsyncKeys();
void NetClient::sharedSetup() {
if (preferEncrypted != CryptoLevel::None) keys = Crypto::RSA::rsa_gen_keys();
packets = new std::vector<Packet>();
sparse = new std::vector<char>();
outPacketBuf = new std::vector<Packet>();
rBuf.resize(1);
_open = true;
canWrite = true;
evt = nullptr;
char cryptoPref = static_cast<char>(preferEncrypted);
commTime = time(nullptr);
if (send(_socket, &cryptoPref, 1, 0) == SOCKET_ERROR) throw new std::exception(); // Cannot establish connection :(
if (send(_socket, &cryptoPref, 1, 0) == SOCKET_ERROR) throw new _exception(); // Cannot establish connection :(
if (!noThread) listener = std::thread([](NetClient& cli) { while (cli._open) { cli.update(); Sleep(25); } }, std::ref(*this)); // Setup separate thread for reading new data
}
NetClient::NetClient(char* ipAddr, char* port, CryptoLevel preferEncrypted) : NetClient(ipAddr, port, preferEncrypted, true) {}
NetClient::NetClient(char* ipAddr, char* port, CryptoLevel preferEncrypted, bool setupKeys) :
preferEncrypted(preferEncrypted), startNegotiate(false)
NetClient::NetClient(char* ipAddr, char* port, CryptoLevel preferEncrypted) :
commTime(time(nullptr)), preferEncrypted(preferEncrypted), startNegotiate(false)
{
_socket = INVALID_SOCKET;
this->noThread = false;
WSADATA wsaData;
int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0) throw new std::exception();
if (iResult != 0) throw new _exception();
struct addrinfo *result = NULL, *ptr = NULL, hints;
@ -112,14 +77,14 @@ namespace IO {
iResult = getaddrinfo(ipAddr, port, &hints, &result);
if (iResult) throw new std::exception();
if (iResult) throw new _exception();
for (ptr = result; ptr != NULL; ptr = ptr->ai_next) {
// Create a SOCKET for connecting to server
_socket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
if (_socket == INVALID_SOCKET) {
throw new std::exception();
throw new _exception();
}
// Connect to server.
@ -134,31 +99,20 @@ namespace IO {
freeaddrinfo(result);
if (_socket == INVALID_SOCKET) throw new std::exception();
if (_socket == INVALID_SOCKET) throw new _exception();
sharedSetup(setupKeys);
sharedSetup();
}
NetClient::NetClient(char* ipAddr, char* port, AsyncKeys *keyData, CryptoLevel level) : NetClient(ipAddr, port, level, false) { this->keyData = keyData; }
NetClient::NetClient(SOCKET wrap, bool noThread, AsyncKeys *keyData, CryptoLevel preferEncrypted, bool startNegotiate) :
preferEncrypted(preferEncrypted), startNegotiate(startNegotiate)
NetClient::NetClient(SOCKET wrap, bool noThread, Crypto::RSA::KeyData& keys, CryptoLevel preferEncrypted, bool startNegotiate) :
commTime(time(nullptr)), preferEncrypted(preferEncrypted), startNegotiate(startNegotiate)
{
_socket = wrap;
this->noThread = noThread;
this->keyData = new AsyncKeys(keyData);
sharedSetup(false);
sharedSetup();
}
NetClient::~NetClient() {
delete keyData;
for (std::pair<char*, std::pair<ulong_64b, char*>*> *p : associatedData) {
delete[] p->first;
delete[] p->second->second;
delete p->second;
delete p;
}
associatedData.clear();
packets->clear();
delete packets;
sparse->clear();
@ -166,10 +120,6 @@ namespace IO {
if (isOpen()) close();
}
bool NetClient::close() {
if (getBOPCount()) {
scheduleTerminate = true;
return true;
}
bool result = !_open;
_open = false;
result &= (SOCKET_ERROR == shutdown(_socket, SD_BOTH));
@ -180,46 +130,18 @@ namespace IO {
shutdown(_socket, SD_SEND);
canWrite = false;
}
bool NetClient::ping() {
int i;
char* c = new char[sizeof(ulong_64b)];
ulong_64b pingValue = FLAG_PING;
memcpy(c, (const char*)&pingValue, sizeof(ulong_64b));
for (ulong_64b wIdx = 0; wIdx < sizeof(ulong_64b); ++wIdx) {
if ((i = send(_socket, c + wIdx, 1, 0)) == SOCKET_ERROR) return false;
else if (i == 0) --wIdx;
}
commTime = time(nullptr);
delete[] c;
return true;
}
size_t NetClient::getBOPCount() { return firstMessage ? outPacketBuf->size() : 0; }
bool NetClient::_write(char* message, ulong_64b size) {
if (size==FLAG_PING) throw new std::exception(); // Max value is reserved for ping packet
int i;
char* c = new char[sizeof(ulong_64b)];
memcpy(c, &size, sizeof(ulong_64b));
for (ulong_64b wIdx = 0; wIdx < sizeof(ulong_64b); ++wIdx) {
if ((i = send(_socket, c + wIdx, 1, 0)) == SOCKET_ERROR) {
delete[] message;
delete[] c;
return false;
}
if ((i = send(_socket, c + wIdx, 1, 0)) == SOCKET_ERROR) return false;
else if (i == 0) --wIdx;
}
for (ulong_64b wIdx = 0; wIdx < size; ++wIdx) {
if ((i = send(_socket, message + wIdx, 1, 0)) == SOCKET_ERROR) {
delete[] message;
delete[] c;
return false;
}
if ((i = send(_socket, message + wIdx, 1, 0)) == SOCKET_ERROR) return false;
else if (i == 0) --wIdx;
}
commTime = time(nullptr);
if(autoDelete) delete[] message;
delete[] c;
return true;
}
bool NetClient::write(void* message, ulong_64b size) {
@ -231,16 +153,9 @@ namespace IO {
return true;
}
if (!canWrite) return false;
char* bMsg = new char[size+1];
bMsg[0] = remotePUID;
++remotePUID;
memcpy(bMsg + 1, message, size);
++size;
char* msg = encrypted ? Crypto::full_auto_encrypt(bMsg, size, pK, &size) : (char*)bMsg;
_write(msg, size);
if (!autoDelete && encrypted) delete[] msg;
delete[] bMsg;
if (autoDelete) delete[] message;
char* msg = encrypted ? Crypto::full_auto_encrypt(message, size, pK, &size) : (char*)message;
_write(msg, size + encrypted);
if (encrypted) delete[] msg;
return true;
}
bool NetClient::write(char* message) { return write(message, strlen(message)+1); } // Send together with the null-terminator
@ -255,7 +170,7 @@ namespace IO {
packets->erase(packets->begin(), packets->begin() + 1); // Delete first buffered packet
return p;
}
throw new std::exception(); // No packets available!
throw new _exception(); // No packets available!
}
void NetClient::setEventHandler(std::function<void(NetClient*, Packet)> _ev) {
evt = _ev;
@ -270,41 +185,29 @@ namespace IO {
}
bool NetClient::isEncrypted() { return encrypted; }
void NetClient::update() {
/*if (!connected(_socket)) { // Check this later...
_open = false;
close();
return;
}*/
int iResult = 0, rdErr;
unsigned long rCount;
rdErr = ioctlsocket(_socket, FIONREAD, &rCount);
if (rdErr == SOCKET_ERROR) throw new _exception(); // Error using socket :(
if (rCount > 0) {
rBuf.resize(rCount);
iResult = recv(_socket, &rBuf[0], rCount, 0);
iResult = recv(_socket, rBuf, BUFSIZE, 0);
if (iResult > 0)
for (int i = 0; i < iResult; ++i)
if (sparse->size() < BUF_2_MAX)
sparse->push_back(rBuf[i]); // Drop anything over the absolute max
commTime = time(nullptr);
if(!firstMessage) flushPrecedingPings(sparse);
}
while (!firstMessage && hasFullMessage(sparse)) {
Packet p;
char* size = readSparse(sparse, sizeof(ulong_64b));
memcpy(&p.size, size, sizeof(ulong_64b));
delete[] size;
p.message = readSparse(sparse, p.size);
if (encrypted) p.message = Crypto::full_auto_decrypt(p.message, (CryptoPP::RSA::PrivateKey&)*keyData->get()->privKey, &p.size);
p.packetUID = p.message[0];
if (p.packetUID != expectedNextPUID) continue; // Detect packet replay/mismatch
else ++expectedNextPUID;
--p.size;
char* c = new char[p.size];
memcpy(c, p.message + 1, p.size);
delete[] p.message;
p.message = c;
if (encrypted) p.message = Crypto::full_auto_decrypt(p.message, keys.privKey, &p.size);
if (evt == nullptr) packets->push_back(p);
else evt(this, p); // Notify event handler of a new packet
}
@ -325,12 +228,12 @@ namespace IO {
}
else {
ulong_64b size;
char* c = Crypto::RSA::serializeKey((CryptoPP::RSA::PublicKey&)*keyData->get()->publKey, &size);
char* c = Crypto::RSA::serializeKey(keys.publKey, &size);
_write(c, size); // This shouldn't be encrypted
if(!autoDelete) delete[] c;
delete[] c;
}
}
else throw new std::exception(); // Incompatible cryptographic requirements!
else throw new _exception(); // Incompatible cryptographic requirements!
}
if (fm_neg_hasLevel && !fm_neg_hasSize && encrypted && sparse->size() >= sizeof(ulong_64b)) {
fm_neg_hasSize = true;
@ -355,73 +258,29 @@ namespace IO {
_open = false;
close();
}
if (autoPing && ((time(nullptr) - commTime) > 1)) if (!ping()) { _open = false; close(); }
if (scheduleTerminate && !getBOPCount()) close();
}
bool NetClient::isOpen() { return _open; }
void NetClient::setOnDestroy(std::function<void()> call) { onDestroy = call; }
std::pair<ulong_64b, char*> NetClient::getValue(const char* name, bool copy) {
for(std::pair<char*, std::pair<ulong_64b, char*>*>* p : associatedData)
if (!strcmp(p->first, name)) {
char* c = copy ? new char[p->second->first] : p->second->second;
if (copy) memcpy(c, p->second->second, p->second->first);
return std::pair<ulong_64b, char*>(p->second->first, c);
}
return std::pair<ulong_64b, char*>(0, nullptr);
}
char* NetClient::getStrValue(const char* name, bool copy) {
return getValue(name, copy).second;
}
void NetClient::setValue(const char* name, std::pair<ulong_64b, char*> value, bool copy, bool del) {
for (std::pair<char*, std::pair<ulong_64b, char*>*>* p : associatedData)
if (!strcmp(p->first, name)) {
p->second->first = value.first;
if (del) delete[] p->second->second;
char* c = copy ? new char[value.first] : value.second;
if (copy) memcpy(c, value.second, value.first);
p->second->second = c;
return;
}
std::pair<char*, std::pair<ulong_64b, char*>*>* p = new std::pair<char*, std::pair<ulong_64b, char*>*>();
p->first = (char*)name;
p->second = new std::pair<ulong_64b, char*>();
p->second->first = value.first;
if (del) delete[] p->second->second;
char* c = copy ? new char[value.first] : value.second;
if (copy) memcpy(c, value.second, value.first);
p->second->second = c;
associatedData.push_back(p);
}
void NetClient::setValue(const char* name, char* value, bool copy, bool del) {
setValue(name, std::pair<ulong_64b, char*>(strlen(value), value), copy, del);
}
bool NetClient::removeValue(const char* name, bool del) {
for (size_t t = associatedData.size(); t>0; --t)
if (!strcmp(associatedData.at(t-1)->first, name)) {
if (del) delete[] associatedData.at(t-1)->second->second;
associatedData.erase(associatedData.begin()+t-1, associatedData.begin()+t);
return true;
}
return false;
}
bool NetClient::containsKey(const char* name) {
for (size_t t = associatedData.size(); t>0; --t)
if (!strcmp(associatedData.at(t - 1)->first, name))
return true;
return false;
}
ulong_64b NetClient::available() { return packets->size(); }
bool NetServer::close() {
if (!_open) return false;
_open = false;
for (ulong_64b t = clients->size(); t > 0; --t) {
NetClient* s = clients->at(t - 1);
s->close();
clients->pop_back();
delete s;
}
return true;
}
void NetServer::sharedSetup(char* port, std::function<bool(NetClient*)> f) {
NetServer::NetServer(char* port, std::function<bool(NetClient*)> f = nullptr, CryptoLevel pref = CryptoLevel::None) : pref(pref) {
if (pref != CryptoLevel::None) keys = Crypto::RSA::rsa_gen_keys();
_open = true;
timeoutHandler = NULL;
onDestroy = NULL;
@ -438,7 +297,7 @@ namespace IO {
// Initialize Winsock
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0) throw new std::exception();
if (iResult != 0) throw new _exception();
ZeroMemory(&hints, sizeof(hints));
@ -450,14 +309,14 @@ namespace IO {
// Resolve the server address and port
iResult = getaddrinfo(NULL, port, &hints, &result);
if (iResult) {
throw new std::exception();
throw new _exception();
}
// Create a SOCKET for connecting to server
_server = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
if (_server == INVALID_SOCKET) {
freeaddrinfo(result);
throw new std::exception();
throw new _exception();
}
// Setup the TCP listening socket
@ -465,31 +324,31 @@ namespace IO {
if (iResult == SOCKET_ERROR) {
freeaddrinfo(result);
closesocket(_server);
throw new std::exception(); // Can't be fucked to deal with errors
throw new _exception(); // Can't be fucked to deal with errors
}
if (listen(_server, 20) == SOCKET_ERROR) { // 20 is the backlog amount, i.e. amount of connections Windows will accept if program is busy and can't accept atm.
closesocket(_server);
throw new std::exception();
throw new _exception();
}
timeval t;
t.tv_sec = 0;
t.tv_usec = 5000;
do {
Next:
if (!_open) break;
fd_set connecting;
connecting.fd_count = 1;
connecting.fd_array[0] = _server;
int i = select(NULL, &connecting, NULL, NULL, &t); // Check for new clients
if (i == SOCKET_ERROR) throw new std::exception();
if (i == SOCKET_ERROR) {
throw new _exception();
}
if (connecting.fd_count > 0) { // This checks if any new clients are tryig to connect. If not, don't block to await one; just continue to update clients
SOCKET client = accept(_server, NULL, NULL);
if (client == INVALID_SOCKET) {
closesocket(_server);
if (_open) throw new std::exception();
if (_open) throw new _exception();
else break;
}
NetClient* cli = new NetClient(client, true, keyData, this->pref, false);
NetClient* cli = new NetClient(client, true, keys, this->pref, false);
clients->push_back(cli);
for (ulong_64b t = 0; t < handlers->size(); ++t)
if (handlers->at(t)(cli))
@ -497,54 +356,18 @@ namespace IO {
}
updateClients();
if (scheduleTerminate) {
for (NetClient* cli : *clients)
if (cli->getBOPCount() > 0)
goto Next;
break;
}
} while (_open);
closesocket(_server);
close();
});
}
bool NetServer::close() {
for (NetClient* cli : *clients) {
if (cli->getBOPCount() > 0) {
scheduleTerminate = true;
return true;
}
}
if (!_open) return false;
_open = false;
for (ulong_64b t = clients->size(); t > 0; --t) {
NetClient* s = clients->at(t - 1);
s->close();
clients->pop_back();
delete s;
}
return true;
}
NetServer::NetServer(char* port, std::function<bool(NetClient*)> f, CryptoLevel pref) : pref(pref) {
if (pref != CryptoLevel::None) keyData = new AsyncKeys();
sharedSetup(port, f);
}
NetServer::NetServer(char* port, std::function<bool(NetClient*)> f, AsyncKeys &keyData, CryptoLevel level) : pref(level) {
this->keyData = new AsyncKeys(keyData.get());
sharedSetup(port, f);
}
NetServer::~NetServer() {
delete keyData;
if (_open) close();
handlers->clear();
delete handlers;
for (NetClient *cli : *clients) delete cli;
clients->clear();
clients->clear();
delete clients;
onDestroy();
}
@ -572,142 +395,4 @@ namespace IO {
bool NetServer::isOpen() { return _open; }
void NetServer::setOnDestroy(std::function<void()> call) { onDestroy = call; }
void NetServer::setAutoPing(bool b) { for (NetClient* cli : *clients) cli->autoPing = b; }
ulong_64b NetServer::getClientCount() { return clients->size(); }
NetClient* NetServer::at(ulong_64b idx) { return clients->at(idx); }
void writeState(NetClient& cli, const char* stateName, char state) {
char* c = cli.getStrValue(stateName, false);
if (c == nullptr) c = new char[0];
c[0] = state;
cli.setValue(stateName, c, false, false); // Write/overwrite
}
char readState(NetClient& cli, const char* stateName) {
char* c = cli.getStrValue(stateName, false);
if (c == nullptr) return 0;
else return *c;
}
PartialNetworkStream::PartialNetworkStream(NetClient& client, bool noBuffer, bool permissive) :
std::ostream(std::_Uninitialized::_Noinit),
client(client),
buffer(noBuffer?nullptr:new std::vector<char>()),
permissive(permissive)
{ /* NOP */}
PartialNetworkStream::~PartialNetworkStream() {
if (client.isOpen() && !stateIs(client, PartialCommState::COMM_FULL)) {
sendState(PartialCommState::COMM_FULL);
writeState(client, STREAM_ATTRIB, PartialCommState::COMM_FULL);
}
client.removeValue(STREAM_ATTRIB); // Cleanup
}
void PartialNetworkStream::write(char* message, std::streamsize size, bool autoFlush) {
bool isPartial = stateIs(client, PartialCommState::COMM_PARTIAL);
if (!isPartial || autoFlush || (size > STREAM_BUFMIN)) {
if(isPartial) flush();
client.write(message, size);
}
else {
for (std::streamsize t = 0; t < size; ++t) buffer->push_back(message[t]);
if (buffer->size() > STREAM_BUFMIN) flush();
}
}
void PartialNetworkStream::writeNonPartial(char* message, std::streamsize size) {
bool b = stateIs(client, PartialCommState::COMM_PARTIAL);
if (b) client.write((char*)&STREAM_PAUSE, sizeof(STREAM_PAUSE));
client.write(message, size);
if (b) client.write((char*)&STREAM_PAUSE, sizeof(STREAM_PAUSE));
}
void PartialNetworkStream::flush() {
if(!check(PartialCommState::COMM_FULL)) return; // Check failed in a permissive state
if (buffer->size() == 0) return;
bool b = stateIs(client, PartialCommState::COMM_PAUSE);
if (b) client.write((char*)&STREAM_PAUSE, sizeof(STREAM_PAUSE)); // Temporarily set the remote read state to PARTIAL
client.write(&buffer->at(0), buffer->size());
if (b) client.write((char*)&STREAM_PAUSE, sizeof(STREAM_PAUSE)); // Set the remote read state back to PAUSE
buffer->clear();
}
bool PartialNetworkStream::check(PartialCommState state) {
if (readState(client, STREAM_ATTRIB) == state) {
if (permissive) return false;
throw new std::exception("Stream is not open!");
}
return true;
}
void PartialNetworkStream::sendState(PartialCommState state) {
switch (getCommState()) {
case PartialCommState::COMM_PAUSE:
if (state == PartialCommState::COMM_FULL) {
client.write((char*)&STREAM_PAUSE, sizeof(STREAM_PAUSE));
client.write((char*)&STREAM_DELIMIT, sizeof(STREAM_DELIMIT));
}
else if (state == PartialCommState::COMM_PARTIAL) client.write((char*)&STREAM_PAUSE, sizeof(STREAM_PAUSE));
break;
case PartialCommState::COMM_PARTIAL:
if (state == PartialCommState::COMM_FULL) client.write((char*)&STREAM_DELIMIT, sizeof(STREAM_DELIMIT));
else if (state == PartialCommState::COMM_PAUSE) client.write((char*)&STREAM_PAUSE, sizeof(STREAM_PAUSE));
break;
case PartialCommState::COMM_FULL:
if (state == PartialCommState::COMM_PARTIAL) client.write((char*)&STREAM_DELIMIT, sizeof(STREAM_DELIMIT));
else if (state == PartialCommState::COMM_PAUSE) {
client.write((char*)&STREAM_DELIMIT, sizeof(STREAM_PAUSE));
client.write((char*)&STREAM_PAUSE, sizeof(STREAM_PAUSE));
}
break;
}
}
void PartialNetworkStream::endPartial() {
flush();
sendState(PartialCommState::COMM_FULL);
writeState(client, STREAM_ATTRIB, PartialCommState::COMM_FULL);
}
void PartialNetworkStream::startPartial() {
sendState(PartialCommState::COMM_PARTIAL);
writeState(client, STREAM_ATTRIB, PartialCommState::COMM_PARTIAL);
}
PartialCommState PartialNetworkStream::getCommState() {
return static_cast<PartialCommState>(readState(client, STREAM_ATTRIB));
}
bool PartialNetworkStream::stateIs(NetClient& cli, PartialCommState state) { return readState(cli, STREAM_ATTRIB) == state; }
PartialDataState PartialNetworkStream::accept(NetClient& cli, Packet& pkt) {
bool toggle_partial = (pkt.size-1) == sizeof(STREAM_DELIMIT) && ((*(ulong_64b*)pkt.message) == STREAM_DELIMIT);
bool toggle_pause = !toggle_partial && ((pkt.size-1) == sizeof(STREAM_PAUSE) && ((*(ulong_64b*)pkt.message) == STREAM_PAUSE));
if (!toggle_partial && !toggle_pause) return PartialDataState::DATA;
else if (toggle_partial) {
if (stateIs(cli, PartialCommState::COMM_FULL)) {
writeState(cli, STREAM_ATTRIB, PartialCommState::COMM_PARTIAL);
return PartialDataState::START;
}
else if (stateIs(cli, PartialCommState::COMM_PAUSE)) {
writeState(cli, STREAM_ATTRIB, PartialCommState::COMM_PARTIAL);
return PartialDataState::RESUME;
}
else {
writeState(cli, STREAM_ATTRIB, PartialCommState::COMM_FULL);
return PartialDataState::END;
}
}
else /* if(toggle_pause) */{
if (stateIs(cli, PartialCommState::COMM_FULL)) {
writeState(cli, STREAM_ATTRIB, PartialCommState::COMM_PAUSE);
return PartialDataState::PAUSE;
}
else if (stateIs(cli, PartialCommState::COMM_PAUSE)) {
writeState(cli, STREAM_ATTRIB, PartialCommState::COMM_PARTIAL);
return PartialDataState::RESUME;
}
else {
writeState(cli, STREAM_ATTRIB, PartialCommState::COMM_PAUSE);
return PartialDataState::PAUSE;
}
}
}
}

View File

@ -8,17 +8,12 @@
#define BUFSIZE 512
#define BUF_2_MAX 2048
#else
#define BUFSIZE 1073741824 // 1 GiB
#define BUF_2_MAX 1073741824 // 1 GiB
#define BUFSIZE 16384
#define BUF_2_MAX 16384
#endif
#define WIN32_LEAN_AND_MEAN
// Ping flag tells the recieving host to drop the current ulong_64b, as it is sent to check if the connection is still alive
#define FLAG_PING (ulong_64b)-1
#define FLAG_PART (ulong_64b)-2
#define FLAG_NPRT (ulong_64b)-3
#include "Crypto.h"
#include "ArchAbstract.h"
@ -26,36 +21,13 @@
#include <vector>
#include <thread>
#include <functional>
#include <future>
namespace IO {
class AsyncKeys {
private:
std::future<Crypto::RSA::KeyData*> gen;
Crypto::RSA::KeyData* keys;
AsyncKeys* chainKeys;
volatile bool done;
bool suppressDelete;
bool chain;
public:
AsyncKeys();
AsyncKeys(Crypto::RSA::KeyData* predef);
AsyncKeys(AsyncKeys*);
~AsyncKeys();
Crypto::RSA::KeyData* get();
};
enum CryptoLevel { None, Prefer, Force };
struct Packet {
ulong_64b size;
char packetUID;
char* message;
};
struct PartialPacket {
ulong_64b size;
char* message;
};
@ -68,44 +40,35 @@ namespace IO {
volatile bool _open; // Whether or not connection is open
bool canWrite; // Whether or not writing to peer is possible
bool noThread; // Whether or not reading incoming data should be / is being done in a separate thread
bool scheduleTerminate = false;
std::vector<char> rBuf;
char rBuf[BUFSIZE]; // Recieve buffer
CryptoLevel preferEncrypted = CryptoLevel::None;// Whether or not the socket should attempt to request an encrypted channel
bool encrypted = false; // Whether or not negotiation determined the use of an encrypted channel
bool firstMessage = true; // Whether or not negotiation has yet ocurred
ulong_64b fm_neg_size; // First message negotiation size
bool fm_neg_hasLevel = false; // First message has crypto level
bool fm_neg_hasSize = false; // Got negotiation size from first message
bool startNegotiate = false; // Whether or not to initiate negotiation
char expectedNextPUID = 0;
char remotePUID = 0;
ulong_64b fm_neg_size;
bool fm_neg_hasLevel = false;
bool fm_neg_hasSize = false;
bool startNegotiate = false;
std::vector<char>* sparse;
std::vector<Packet>* outPacketBuf;
AsyncKeys *keyData; // Client's keysets (if using encryption)
Crypto::RSA::KeyData keys; // Client's keysets (if using encryption)
CryptoPP::RSAFunction pK; // Remote host's public key (if using encryption)
NetClient(char*, char*, CryptoLevel, bool); // Underlying setup for regular constructors
NetClient(SOCKET, bool, CryptoLevel, bool);// Special setup constructor
NetClient(SOCKET, bool, AsyncKeys*, CryptoLevel = CryptoLevel::None, bool = false);// Create wrapper for existing socket
void sharedSetup(bool); // Setup function for all constructor
NetClient(SOCKET, bool, Crypto::RSA::KeyData&, CryptoLevel = CryptoLevel::None, bool = false);// Create wrapper for existing socket
void sharedSetup(); // Setup function for all constructor
bool _write(char*, ulong_64b); // Internal write function. Doesn't do any of the fancy auto encryption: just raw write...
bool writeBufferedPackets(); // Flushes and deletes buffer
void update(); // Read incoming data and store in buffers
protected:
std::vector<std::pair<char*, std::pair<ulong_64b, char*>*>*> associatedData;
std::thread listener; // Incoming data listener (optional)
SOCKET _socket; // Underlying socket used for communication
std::vector<Packet>* packets; // Basically a set containing a backlog of unprocessed data. Will oly be used if event handler doesn't exist
std::function<void(NetClient*, Packet)> evt; // New data event handler
std::function<void()> onDestroy; // Event handler called when NetClient object is destroyed
public:
bool autoPing = true; // Whether or not client should actively check connection state
bool autoDelete = false;
time_t commTime; // Latest time a transaction occurred
std::vector<char*> associatedData;
NetClient(char* ipAddr, char* port, CryptoLevel = CryptoLevel::None);// Standard constructor for creating connection
NetClient(char* ipAddr, char* port, AsyncKeys*, CryptoLevel);// Standard constructor for creating connection with predefined keys
~NetClient();
bool close();
void closeWrite();
@ -114,29 +77,20 @@ namespace IO {
bool write(void* message, ulong_64b size);
bool write(char* message);
Packet read();
void setEventHandler(std::function<void(NetClient*, Packet)>);// Register a callback that is guaranteed to be called when the socket has at least one unprocessed packet
void setEventHandler(std::function<void(NetClient*, Packet)>); // Register a callback that is guaranteed to be called when the socket has at least one unprocessed packet
void setOnDestroy(std::function<void()>);
std::pair<ulong_64b, char*> getValue(const char* name, bool copy = true);
char* getStrValue(const char* name, bool copy = true);
void setValue(const char* name, std::pair<ulong_64b, char*> value, bool copy = true, bool del = true);
void setValue(const char* name, char* data, bool copy = true, bool del = true);
bool removeValue(const char* name, bool del = true);
bool containsKey(const char* name);
bool isOpen();
ulong_64b available();
bool ping(); // Check if connection is alive by pinging remote host
};
class NetServer {
friend class NetClient;
private:
CryptoLevel pref;
AsyncKeys *keyData; // Server's keysets (if using encryption)
Crypto::RSA::KeyData keys; // Server's keysets (if using encryption)
std::function<void()> onDestroy;
volatile bool _open;
bool scheduleTerminate = false;
void sharedSetup(char* port, std::function<bool(NetClient*)> f);
void updateClients();
protected:
std::thread clientListener;
@ -144,8 +98,7 @@ namespace IO {
std::vector<NetClient*>* clients;
public:
std::function<bool(NetClient*)> timeoutHandler;
NetServer(char* port, std::function<bool(NetClient*)> = nullptr, CryptoLevel = CryptoLevel::None);
NetServer(char* port, std::function<bool(NetClient*)>, AsyncKeys&, CryptoLevel);
NetServer(char* port, std::function<bool(NetClient*)>, CryptoLevel);
~NetServer();
bool isOpen();
CryptoLevel getCryptoPreference();
@ -153,60 +106,6 @@ namespace IO {
void clearHandlers();
void setOnDestroy(std::function<void()>);
bool close();
void setAutoPing(bool);
ulong_64b getClientCount();
NetClient* at(ulong_64b);
};
// Partial data stream management
static const auto STREAM_DELIMIT = FLAG_PART;
static const auto STREAM_PAUSE = FLAG_NPRT;
static const auto STREAM_ATTRIB = (const char*) "$PartialNetworkStream$ACTIVE";
static const auto STREAM_BUFMIN = 32;
/*
START represents the beginning of a partial message
PAUSE represents a pause in the partial stream in which a full (unrelated) message is being sent
RESUME tells dev that the partial stream is being resumed (from a full-write state)
END represebts the end of a partial message
DATA represents the the supplied data isn't metadata
*/
enum PartialDataState { START, PAUSE, RESUME, END, DATA };
/*
PARTIAL tells you that the stream is currently accepting partial data packets
PAUSE means that the client is set to accept a partial stream, but has been specifically paused to accept a full message
FULL means that the client is interpreting messages as full message blocks
*/
enum PartialCommState { COMM_FULL, COMM_PARTIAL, COMM_PAUSE };
class PartialNetworkStream : public std::ostream{
protected:
const bool permissive;
bool open;
std::vector<char>* buffer;
NetClient& client;
bool check(PartialCommState state);
void sendState(PartialCommState state);
public:
PartialNetworkStream(NetClient&, bool = false, bool = true);
~PartialNetworkStream();
void endPartial();
void startPartial();
PartialCommState getCommState();
void write(char*, std::streamsize, bool = false);
void writeNonPartial(char*, std::streamsize);
void flush();
static PartialDataState accept(NetClient& cli, Packet& pkt);
static bool stateIs(NetClient& cli, PartialCommState state);
};
}

View File

@ -76,54 +76,27 @@ namespace Tools {
return c;
}
char* toHexString(const void* data, ulong_64b size, bool ignorePreZero) {
char* toHexString(const void* data, ulong_64b size) {
char* c = (char*)data;
ulong_64b lastNonZero = ignorePreZero?0:size-1;
if (ignorePreZero) {
for (ulong_64b t = size; t > 0; --t)
if (c[t - 1] != 0) {
lastNonZero = t - 1;
goto Ayy;
}
}
else goto Ayy;
return new char[2]{ '0', 0 };
ulong_64b lastNonZero = 0;
for (ulong_64b t = 0; t < size; ++t) if (c[t] != 0) lastNonZero = t;
if (lastNonZero == 0) return (char*)memset(malloc(1), '0', 1);
Ayy:
char* c1 = (char*)new char[1 + ((lastNonZero + 1) * 2)];
c1[lastNonZero * 2] = 0;
for (ulong_64b j = lastNonZero + 1; j > 0; --j) {
ulong_64b t = 1 + lastNonZero - j;
c1[2 * t] = (c[j - 1] >> 4) & 15;
if (c1[(2 * t)] < 10) c1[(2 * t)] += 48;
char* c1 = (char*)malloc(lastNonZero * 2);
for (ulong_64b t = 0; t < lastNonZero; ++t) {
c1[2 * t] = (c[t]) & 15;
if (c1[(2 * t)] < 9) c1[(2 * t)] += 48;
else c1[(2 * t)] += 55;
c1[(2 * t) + 1] = (c[j - 1]) & 15;
if (c1[(2 * t) + 1] < 10) c1[(2 * t) + 1] += 48;
c1[(2 * t) + 1] = (c[t] >> 4) & 15;
if (c1[(2 * t) + 1] < 9) c1[(2 * t) + 1] += 48;
else c1[(2 * t) + 1] += 55;
}
return c1;
}
char* toHexString(const void* data, ulong_64b size) { return toHexString(data, size, true); }
char* toHexString(ulong_64b value) { return toHexString(&value, sizeof(value), false); }
void* parseHex(char* c, size_t *rSize) {
size_t len = strlen(c);
size_t rem = (len % 2);
size_t target = (len + rem) / 2;
if (rSize != nullptr) *rSize = target;
char* out = new char[target];
if (rem) out[target - 1] = c[0] - (c[0]>64 ? 55 : 48);
for (size_t t = rem; t < len; ++t) {
out[target - 1 - ((t + rem) / 2)] |= (c[t] - (c[t] > 64 ? 55 : 48)) << (((t + 1) % 2) * 4);
}
return out;
}
ulong_64b parseHexLong(char* c) { return *(ulong_64b*)parseHex(c, nullptr); }
char* toHexString(ulong_64b value) { return toHexString(&value, sizeof(value)); }
bool isDigit(char c) { return (c > 47) && (c < 58); }

View File

@ -20,11 +20,8 @@ namespace Tools {
ulong_64b indexOf(char*, char);
ulong_64b lastIndexOf(char*, char);
char* copydata(const char*, ulong_64b);
char* toHexString(const void* data, ulong_64b size, bool);
char* toHexString(const void* data, ulong_64b size);
char* toHexString(ulong_64b);
void* parseHex(char*, size_t *rSize);
ulong_64b parseHexLong(char*);
char* toHexString(ulong_64b value);
bool isDigit(char c);
bool isNumber(char* c);
bool isIP(char* c);