Compare commits

...

13 Commits

Author SHA1 Message Date
Gabriel Tofvesson
afeda2281d Removed a stupid zip that wan't supposed to be there! 2017-10-17 01:12:22 +02:00
Gabriel Tofvesson
21372a9eb3 Fixed issues with auto-deletion of messages
Added key serialization/deserialization functions
Added experimental "soft-close" feature
Added ability to chain AsyncKeys (unstable?)
2017-10-17 01:08:32 +02:00
Gabriel Tofvesson
88928ba722 Fixed fatal bug causing deletion of stack-allocated cryptographic keyset: keyset is no longer stack-allocated :P
AsyncKeys destructor now completely deletes keydata
2017-10-16 21:01:18 +02:00
Gabriel Tofvesson
04ef028372 Added asynchronous key generation to network library
Replaced malloc() calls with new[] calls to make code more consistent
Fixed memory leaks
KeyData is now generated as a pointer
2017-10-16 20:37:06 +02:00
79d518f97e Made ping available for anyone
Added feature to disable autoPing
Server can also disable autoPing
2017-10-12 12:27:50 +02:00
Gabriel Tofvesson
76e0c6b31d Added hex parser
Added support for direct (not shortened) hex parsing
2017-10-12 00:17:43 +02:00
Gabriel Tofvesson
d13e86fc75 Fixed minor bugs
Added support for pre-created keys to be supplied
2017-10-11 23:25:55 +02:00
be17d51c3f Fixed partial stream management
Added proper state managemment
Fixed state posting to peer
Added flushes to ensure data is sent in the proper order
Added permissive mode to error-checks (optional)
Properly implemented full-packet stream interruption
2017-10-07 22:11:37 +02:00
43cea7b40b Started adding Partial stream handling
- Partial stream implements ostream
  - Added state management for starting or stopping partial streams
  - Stream can be paused to write full messages without ending "stream session"
  - Stream supports buffering to prevent sending small packets
Abstracted associatedData away to what is essentially a map
2017-10-06 00:13:41 +02:00
Gabriel Tofvesson
f3c5bdebbb Started implementing data "streams" on top of socket layer, i.e. data packets of undefined size which take an undefined amount of time to generate, but should be sent immediately 2017-10-05 01:43:01 +02:00
d8ee17c011 Converted the read buffer to a vector such that it resizes as necessary
Increased maximum read buffer size to 1 GiB
2017-10-03 16:52:39 +02:00
e399fcb5cb Revert "Added two new packet structure implementations:"
This reverts commit 506b4257a3a49c0b6bd779b0bd720b92f85f64c0.
2017-10-03 16:40:47 +02:00
Gabriel Tofvesson
506b4257a3 Added two new packet structure implementations:
- Standard packet for messages smaller than or precisely 16KiB
  - Sparse packet: separates the message over a set of memory blocks with a maximum size of 16KiB. Maximum theoretical size (reading may not work) 2^128 ish
Started replacing packet management system with new system
2017-10-03 04:43:37 +02:00
7 changed files with 586 additions and 100 deletions

View File

@ -72,9 +72,7 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<TargetExt>64.lib</TargetExt>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<TargetExt>.lib</TargetExt>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" />
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
@ -130,6 +128,8 @@
<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,6 +7,21 @@
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 --------
@ -38,7 +53,7 @@ namespace Crypto {
(*resultingSize) = t;
char* cipher = (char*)malloc(t);
char* cipher = (char*)new char[t];
memcpy(cipher, ciphertext.c_str(), t);
return cipher;
@ -61,11 +76,11 @@ namespace Crypto {
*resultSize = decryptedtext.size();
char* c = (char*)malloc(*resultSize);
char* c = (char*)new char[*resultSize];
//memset(c, 0, decryptedtext.size());
memcpy(c, decryptedtext.c_str(), decryptedtext.size());
decryptedtext.~basic_string();
//decryptedtext.~basic_string();
return c;
}
@ -74,9 +89,15 @@ namespace Crypto {
Payload aes_auto_encrypt(void* msg, ulong_64b size) {
char* message = (char*)msg;
Payload p;
srand(time(NULL));
p.key = (char*)malloc(sizeof(AES_KEY));
// 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)];
AES_KEY k = (AES_KEY)rand();
p.keySize = sizeof(AES_KEY);
@ -142,9 +163,15 @@ 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 k;
KeyData* rsa_gen_keys() {
CryptoPP::InvertibleRSAFunction params;
CryptoPP::RandomPool rng;
@ -153,8 +180,8 @@ namespace Crypto {
rng.IncorporateEntropy((const byte*)&t, sizeof(t) * 8);
params.GenerateRandomWithKeySize(rng, 3072);
k.privKey = CryptoPP::RSA::PrivateKey(params);
k.publKey = CryptoPP::RSA::PublicKey(params);
KeyData *k = new KeyData(new CryptoPP::RSA::PrivateKey(params), new CryptoPP::RSA::PublicKey(params));
return k;
}
@ -164,7 +191,7 @@ namespace Crypto {
//func.DEREncodePublicKey(queue);
byte* shortened = (byte*)malloc(*rSize=queue.TotalBytesRetrievable());
byte* shortened = (byte*)new byte[*rSize=queue.TotalBytesRetrievable()];
memset(shortened, 0, *rSize);
std::vector<byte> spk;
@ -178,6 +205,44 @@ 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;
@ -191,9 +256,10 @@ namespace Crypto {
*resultingSize = cipher.size();
char* c = (char*)malloc(cipher.size());
memset(c, 0, cipher.size());
char* c = (char*)new char[cipher.size()];
//memset(c, 0, cipher.size());
memcpy(c, cipher.c_str(), cipher.size());
return c;
}
@ -210,9 +276,11 @@ namespace Crypto {
*resultingSize = clear.size();
char* c = (char*)malloc(clear.size());
memset(c, 0, clear.size());
char* c = (char*)new char[clear.size()];
//memset(c, 0, clear.size());
memcpy(c, clear.c_str(), clear.size());
return c;
}
// -------- RSA END --------
@ -220,14 +288,21 @@ 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);
p.key = RSA::rsa_encrypt(p.key, p.keySize, pk, &p.keySize);
char *c = RSA::rsa_encrypt(p.key, p.keySize, pk, &p.keySize);
delete[] p.key;
p.key = c;
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);
p.key = RSA::rsa_decrypt(p.key, p.keySize, pk, &p.keySize);
return AES::aes_auto_decrypt(p, rSize);
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;
}
}

View File

@ -50,13 +50,18 @@ namespace Crypto {
namespace RSA {
struct KeyData {
CryptoPP::RSA::PrivateKey privKey;
CryptoPP::RSA::PublicKey publKey;
const CryptoPP::RSA::PrivateKey *privKey;
const CryptoPP::RSA::PublicKey *publKey;
KeyData(CryptoPP::RSA::PrivateKey*, CryptoPP::RSA::PublicKey*);
~KeyData();
};
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,19 +1,53 @@
#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();
}
return keys;
}
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 _exception(); // This should never happen if function is used correctly
if (sparse->size() < rSize) throw new std::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);
@ -43,28 +77,30 @@ namespace IO {
void NetClient::sharedSetup() {
if (preferEncrypted != CryptoLevel::None) keys = Crypto::RSA::rsa_gen_keys();
void NetClient::sharedSetup(bool setupKeys) {
if (setupKeys && (preferEncrypted != CryptoLevel::None)) keyData = new AsyncKeys();
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 _exception(); // Cannot establish connection :(
if (send(_socket, &cryptoPref, 1, 0) == SOCKET_ERROR) throw new std::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) :
commTime(time(nullptr)), preferEncrypted(preferEncrypted), startNegotiate(false)
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)
{
_socket = INVALID_SOCKET;
this->noThread = false;
WSADATA wsaData;
int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0) throw new _exception();
if (iResult != 0) throw new std::exception();
struct addrinfo *result = NULL, *ptr = NULL, hints;
@ -76,14 +112,14 @@ namespace IO {
iResult = getaddrinfo(ipAddr, port, &hints, &result);
if (iResult) throw new _exception();
if (iResult) throw new std::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 _exception();
throw new std::exception();
}
// Connect to server.
@ -98,20 +134,31 @@ namespace IO {
freeaddrinfo(result);
if (_socket == INVALID_SOCKET) throw new _exception();
if (_socket == INVALID_SOCKET) throw new std::exception();
sharedSetup();
sharedSetup(setupKeys);
}
NetClient::NetClient(SOCKET wrap, bool noThread, Crypto::RSA::KeyData& keys, CryptoLevel preferEncrypted, bool startNegotiate) :
commTime(time(nullptr)), preferEncrypted(preferEncrypted), startNegotiate(startNegotiate)
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)
{
_socket = wrap;
this->noThread = noThread;
sharedSetup();
this->keyData = new AsyncKeys(keyData);
sharedSetup(false);
}
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();
@ -119,6 +166,10 @@ 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));
@ -139,24 +190,36 @@ namespace IO {
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 _exception(); // Max value is reserved for ping packet
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) return false;
if ((i = send(_socket, c + wIdx, 1, 0)) == SOCKET_ERROR) {
delete[] message;
delete[] c;
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) return false;
if ((i = send(_socket, message + wIdx, 1, 0)) == SOCKET_ERROR) {
delete[] message;
delete[] c;
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) {
@ -175,8 +238,9 @@ namespace IO {
++size;
char* msg = encrypted ? Crypto::full_auto_encrypt(bMsg, size, pK, &size) : (char*)bMsg;
_write(msg, size);
if (encrypted) delete[] msg;
if (!autoDelete && encrypted) delete[] msg;
delete[] bMsg;
if (autoDelete) delete[] message;
return true;
}
bool NetClient::write(char* message) { return write(message, strlen(message)+1); } // Send together with the null-terminator
@ -191,7 +255,7 @@ namespace IO {
packets->erase(packets->begin(), packets->begin() + 1); // Delete first buffered packet
return p;
}
throw new _exception(); // No packets available!
throw new std::exception(); // No packets available!
}
void NetClient::setEventHandler(std::function<void(NetClient*, Packet)> _ev) {
evt = _ev;
@ -211,7 +275,8 @@ namespace IO {
rdErr = ioctlsocket(_socket, FIONREAD, &rCount);
if (rdErr == SOCKET_ERROR) throw new _exception(); // Error using socket :(
if (rCount > 0) {
iResult = recv(_socket, rBuf, BUFSIZE, 0);
rBuf.resize(rCount);
iResult = recv(_socket, &rBuf[0], rCount, 0);
if (iResult > 0)
for (int i = 0; i < iResult; ++i)
if (sparse->size() < BUF_2_MAX)
@ -227,7 +292,7 @@ namespace IO {
delete[] size;
p.message = readSparse(sparse, p.size);
if (encrypted) p.message = Crypto::full_auto_decrypt(p.message, keys.privKey, &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
@ -260,12 +325,12 @@ namespace IO {
}
else {
ulong_64b size;
char* c = Crypto::RSA::serializeKey(keys.publKey, &size);
char* c = Crypto::RSA::serializeKey((CryptoPP::RSA::PublicKey&)*keyData->get()->publKey, &size);
_write(c, size); // This shouldn't be encrypted
delete[] c;
if(!autoDelete) delete[] c;
}
}
else throw new _exception(); // Incompatible cryptographic requirements!
else throw new std::exception(); // Incompatible cryptographic requirements!
}
if (fm_neg_hasLevel && !fm_neg_hasSize && encrypted && sparse->size() >= sizeof(ulong_64b)) {
fm_neg_hasSize = true;
@ -290,30 +355,73 @@ namespace IO {
_open = false;
close();
}
if ((time(nullptr) - commTime) > 1) if (!ping()) { _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;
}
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();
void NetServer::sharedSetup(char* port, std::function<bool(NetClient*)> f) {
_open = true;
timeoutHandler = NULL;
onDestroy = NULL;
@ -330,7 +438,7 @@ namespace IO {
// Initialize Winsock
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0) throw new _exception();
if (iResult != 0) throw new std::exception();
ZeroMemory(&hints, sizeof(hints));
@ -342,14 +450,14 @@ namespace IO {
// Resolve the server address and port
iResult = getaddrinfo(NULL, port, &hints, &result);
if (iResult) {
throw new _exception();
throw new std::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 _exception();
throw new std::exception();
}
// Setup the TCP listening socket
@ -357,31 +465,31 @@ namespace IO {
if (iResult == SOCKET_ERROR) {
freeaddrinfo(result);
closesocket(_server);
throw new _exception(); // Can't be fucked to deal with errors
throw new std::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 _exception();
throw new std::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 _exception();
}
if (i == SOCKET_ERROR) throw new std::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 _exception();
if (_open) throw new std::exception();
else break;
}
NetClient* cli = new NetClient(client, true, keys, this->pref, false);
NetClient* cli = new NetClient(client, true, keyData, this->pref, false);
clients->push_back(cli);
for (ulong_64b t = 0; t < handlers->size(); ++t)
if (handlers->at(t)(cli))
@ -389,18 +497,54 @@ 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;
clients->clear();
for (NetClient *cli : *clients) delete cli;
clients->clear();
delete clients;
onDestroy();
}
@ -428,4 +572,142 @@ 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,14 +8,16 @@
#define BUFSIZE 512
#define BUF_2_MAX 2048
#else
#define BUFSIZE 16384
#define BUF_2_MAX 16384
#define BUFSIZE 1073741824 // 1 GiB
#define BUF_2_MAX 1073741824 // 1 GiB
#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"
@ -24,10 +26,27 @@
#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 {
@ -36,6 +55,11 @@ namespace IO {
char* message;
};
struct PartialPacket {
ulong_64b size;
char* message;
};
class NetServer;
class NetClient {
friend class NetServer; // Allow NetServer to access all members of NetClient
@ -44,38 +68,44 @@ 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
char rBuf[BUFSIZE]; // Recieve buffer
bool scheduleTerminate = false;
std::vector<char> rBuf;
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;
bool fm_neg_hasLevel = false;
bool fm_neg_hasSize = false;
bool startNegotiate = false;
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;
std::vector<char>* sparse;
std::vector<Packet>* outPacketBuf;
Crypto::RSA::KeyData keys; // Client's keysets (if using encryption)
AsyncKeys *keyData; // 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, Crypto::RSA::KeyData&, CryptoLevel = CryptoLevel::None, bool = false);// Create wrapper for existing socket
void sharedSetup(); // Setup function for all constructor
NetClient(SOCKET, bool, AsyncKeys*, CryptoLevel = CryptoLevel::None, bool = false);// Create wrapper for existing socket
void sharedSetup(bool); // 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
bool ping(); // Check if connection is alive by pinging remote host
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();
@ -84,20 +114,29 @@ 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;
Crypto::RSA::KeyData keys; // Server's keysets (if using encryption)
AsyncKeys *keyData; // 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;
@ -105,7 +144,8 @@ namespace IO {
std::vector<NetClient*>* clients;
public:
std::function<bool(NetClient*)> timeoutHandler;
NetServer(char* port, std::function<bool(NetClient*)>, CryptoLevel);
NetServer(char* port, std::function<bool(NetClient*)> = nullptr, CryptoLevel = CryptoLevel::None);
NetServer(char* port, std::function<bool(NetClient*)>, AsyncKeys&, CryptoLevel);
~NetServer();
bool isOpen();
CryptoLevel getCryptoPreference();
@ -113,6 +153,60 @@ 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,27 +76,54 @@ namespace Tools {
return c;
}
char* toHexString(const void* data, ulong_64b size) {
char* toHexString(const void* data, ulong_64b size, bool ignorePreZero) {
char* c = (char*)data;
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);
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 };
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;
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;
else c1[(2 * t)] += 55;
c1[(2 * t) + 1] = (c[t] >> 4) & 15;
if (c1[(2 * t) + 1] < 9) c1[(2 * t) + 1] += 48;
c1[(2 * t) + 1] = (c[j - 1]) & 15;
if (c1[(2 * t) + 1] < 10) c1[(2 * t) + 1] += 48;
else c1[(2 * t) + 1] += 55;
}
return c1;
}
char* toHexString(ulong_64b value) { return toHexString(&value, sizeof(value)); }
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); }
bool isDigit(char c) { return (c > 47) && (c < 58); }

View File

@ -20,8 +20,11 @@ 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 value);
char* toHexString(ulong_64b);
void* parseHex(char*, size_t *rSize);
ulong_64b parseHexLong(char*);
bool isDigit(char c);
bool isNumber(char* c);
bool isIP(char* c);