diff --git a/CPPTools/CPPTools.vcxproj b/CPPTools/CPPTools.vcxproj index 7629231..dbeee4e 100644 --- a/CPPTools/CPPTools.vcxproj +++ b/CPPTools/CPPTools.vcxproj @@ -139,13 +139,14 @@ + + - diff --git a/CPPTools/CPPTools.vcxproj.filters b/CPPTools/CPPTools.vcxproj.filters index e414768..f69797c 100644 --- a/CPPTools/CPPTools.vcxproj.filters +++ b/CPPTools/CPPTools.vcxproj.filters @@ -27,6 +27,9 @@ Header Files + + Header Files + @@ -35,7 +38,7 @@ Source Files - + Source Files diff --git a/CPPTools/Net.cpp b/CPPTools/Net.cpp new file mode 100644 index 0000000..bab19a3 --- /dev/null +++ b/CPPTools/Net.cpp @@ -0,0 +1,398 @@ +#include "Net.h" + +#include +#include +#include +#include +#include + +namespace IO { + + bool connected(SOCKET sock) + { + char buf; + int err = recv(sock, &buf, 1, MSG_PEEK); + if (err == SOCKET_ERROR) + { + if (WSAGetLastError() != WSAEWOULDBLOCK) + { + return false; + } + } + 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* sparse, ulong_64b rSize, bool pop = true) { + 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; + } + + bool hasFullMessage(std::vector *sparse) { + if (sparse->size() < sizeof(ulong_64b)) return false; + ulong_64b size = 0; + char* c = readSparse(sparse, sizeof(ulong_64b), false); + memcpy(&size, c, sizeof(ulong_64b)); + delete[] c; + return sparse->size() >= (size + sizeof(ulong_64b)); + } + + + + void NetClient::sharedSetup() { + if (preferEncrypted != CryptoLevel::None) keys = Crypto::RSA::rsa_gen_keys(); + packets = new std::vector(); + sparse = new std::vector(); + outPacketBuf = new std::vector(); + _open = true; + canWrite = true; + evt = nullptr; + char cryptoPref = static_cast(preferEncrypted); + 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) : + 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 _exception(); + + + struct addrinfo *result = NULL, *ptr = NULL, hints; + + ZeroMemory(&hints, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + iResult = getaddrinfo(ipAddr, port, &hints, &result); + + 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 _exception(); + } + + // Connect to server. + iResult = connect(_socket, ptr->ai_addr, (int)ptr->ai_addrlen); + if (iResult == SOCKET_ERROR) { + closesocket(_socket); + _socket = INVALID_SOCKET; + continue; + } + break; + } + + freeaddrinfo(result); + + if (_socket == INVALID_SOCKET) throw new _exception(); + + sharedSetup(); + } + + 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; + sharedSetup(); + } + + NetClient::~NetClient() { + packets->clear(); + delete packets; + sparse->clear(); + delete sparse; + if (isOpen()) close(); + } + bool NetClient::close() { + bool result = !_open; + _open = false; + result &= (SOCKET_ERROR == shutdown(_socket, SD_BOTH)); + closesocket(_socket); + return result; + } + void NetClient::closeWrite() { + shutdown(_socket, SD_SEND); + canWrite = false; + } + bool NetClient::_write(char* message, ulong_64b size) { + 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; + 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; + else if (i == 0) --wIdx; + } + return true; + } + bool NetClient::write(void* message, ulong_64b size) { + if (firstMessage) { + Packet p; + p.message = (char*)message; + p.size = size; + outPacketBuf->push_back(p); + return true; + } + if (!canWrite) return false; + char* msg = encrypted ? Crypto::full_auto_encrypt(message, size, pK, &size) : (char*)message; + _write(msg, size); + if (encrypted) delete[] msg; + return true; + } + bool NetClient::write(char* message) { return write(message, strlen(message)); } + bool NetClient::writeBufferedPackets() { + for (size_t t = 0; t < outPacketBuf->size(); ++t) if (!write(outPacketBuf->at(t).message, outPacketBuf->at(t).size)) { delete outPacketBuf; return false; }; + delete outPacketBuf; + return true; + } + Packet NetClient::read() { + if (packets->size() != 0) { + Packet p = packets->at(0); + packets->erase(packets->begin(), packets->begin() + 1); // Delete first buffered packet + return p; + } + throw new _exception(); // No packets available! + } + void NetClient::setEventHandler(std::function _ev) { + evt = _ev; + + // Process unhandled packets + if (evt != nullptr) + for (size_t t = packets->size(); t > 0; --t) { + Packet p = packets->at(t - 1); + packets->pop_back(); + evt(this, p); + } + } + bool NetClient::isEncrypted() { return encrypted; } + void NetClient::update() { + if (!connected(_socket)) { + _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) { + 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 + } + 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, keys.privKey, &p.size); + if (evt == nullptr) packets->push_back(p); + else evt(this, p); // Notify event handler of a new packet + } + if (iResult > 0) { + if (firstMessage) { + if (!fm_neg_hasLevel && sparse->size() >= 1) { + fm_neg_hasLevel = true; + char* readCrypt = readSparse(sparse, 1); + CryptoLevel lvl = static_cast(*readCrypt); + free(readCrypt); + if (cryptoLevelsAreCompatible(lvl, preferEncrypted)) { + // Determine whether or not to use encryption + encrypted = (preferEncrypted == CryptoLevel::Force) || (lvl == CryptoLevel::Force) || ((preferEncrypted == CryptoLevel::Prefer) && (lvl == CryptoLevel::Prefer)); + + if (!encrypted) { + firstMessage = false; // We're done here. No need to try to get a public key for an unencrypted channel + writeBufferedPackets(); + } + else { + ulong_64b size; + char* c = Crypto::RSA::serializeKey(keys.publKey, &size); + _write(c, size); // This shouldn't be encrypted + delete[] c; + } + } + else throw new _exception(); // Incompatible cryptographic requirements! + } + if (fm_neg_hasLevel && !fm_neg_hasSize && encrypted && sparse->size() >= sizeof(ulong_64b)) { + fm_neg_hasSize = true; + char* readSize = readSparse(sparse, sizeof(ulong_64b)); + + fm_neg_size = 0; + memcpy(&fm_neg_size, readSize, sizeof(ulong_64b)); + free(readSize); + } + if (fm_neg_hasSize && sparse->size() >= fm_neg_size) { + char* msg = readSparse(sparse, fm_neg_size); + + CryptoPP::StringSource src((const byte*)msg, fm_neg_size, true); + pK.Load(src); + + firstMessage = false; + writeBufferedPackets(); + } + } + } + else if (iResult < 0 && _open) { + _open = false; + close(); + } + } + bool NetClient::isOpen() { return _open; } + + void NetClient::setOnDestroy(std::function call) { onDestroy = call; } + + 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 f = nullptr, CryptoLevel pref = CryptoLevel::None) : pref(pref) { + if (pref != CryptoLevel::None) keys = Crypto::RSA::rsa_gen_keys(); + _open = true; + timeoutHandler = NULL; + onDestroy = NULL; + handlers = new std::vector >(); + if (f != NULL) handlers->push_back(f); + clients = new std::vector(); + clientListener = std::thread([this, port]() { + SOCKET _server; + WSADATA wsaData; + int iResult; + + struct addrinfo *result = NULL; + struct addrinfo hints; + + // Initialize Winsock + iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); + if (iResult != 0) throw new _exception(); + + + ZeroMemory(&hints, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + hints.ai_flags = AI_PASSIVE; + + // Resolve the server address and port + iResult = getaddrinfo(NULL, port, &hints, &result); + if (iResult) { + 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 _exception(); + } + + // Setup the TCP listening socket + iResult = bind(_server, result->ai_addr, (int)result->ai_addrlen); + if (iResult == SOCKET_ERROR) { + freeaddrinfo(result); + closesocket(_server); + 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 _exception(); + } + timeval t; + t.tv_sec = 0; + t.tv_usec = 5000; + do { + 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 (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(); + else break; + } + 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)) + break; + + } + updateClients(); + } while (_open); + closesocket(_server); + close(); + }); + + } + + NetServer::~NetServer() { + if (_open) close(); + handlers->clear(); + delete handlers; + clients->clear(); + delete clients; + onDestroy(); + } + + void NetServer::addHandler(std::function evtH) { + handlers->push_back(evtH); + } + + void NetServer::clearHandlers() { + handlers->clear(); + } + + void NetServer::updateClients() { + for (ulong_64b t = clients->size(); t > 0; --t) { + NetClient* c = clients->at(t - 1); + if (!c->isOpen() || (timeoutHandler != NULL && !timeoutHandler(c))) { + clients->erase(clients->begin() + t - 1, clients->begin() + t); + c->close(); + } + else c->update(); + } + } + CryptoLevel NetServer::getCryptoPreference() { return pref; } + + bool NetServer::isOpen() { return _open; } + + void NetServer::setOnDestroy(std::function call) { onDestroy = call; } +} \ No newline at end of file diff --git a/CPPTools/Net.h b/CPPTools/Net.h new file mode 100644 index 0000000..223744b --- /dev/null +++ b/CPPTools/Net.h @@ -0,0 +1,112 @@ +#pragma once + + +#ifndef _NET_H +#define _NET_H + +#ifdef _NET_SMALL_BUF +#define BUFSIZE 512 +#define BUF_2_MAX 2048 +#else +#define BUFSIZE 16384 +#define BUF_2_MAX 16384 +#endif + +#define WIN32_LEAN_AND_MEAN + +#include "Crypto.h" +#include "ArchAbstract.h" + +#include +#include +#include +#include + + +namespace IO { + + enum CryptoLevel { None, Prefer, Force }; + + struct Packet { + ulong_64b size; + char* message; + }; + + class NetServer; + class NetClient { + friend class NetServer; // Allow NetServer to access all members of NetClient + + private: + 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 + 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; + std::vector* sparse; + std::vector* outPacketBuf; + Crypto::RSA::KeyData keys; // Client's keysets (if using encryption) + CryptoPP::RSAFunction pK; // Remote host's public key (if using encryption) + + 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 + 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::thread listener; // Incoming data listener (optional) + SOCKET _socket; // Underlying socket used for communication + std::vector* packets; // Basically a set containing a backlog of unprocessed data. Will oly be used if event handler doesn't exist + std::function evt; // New data event handler + std::function onDestroy; // Event handler called when NetClient object is destroyed + public: + time_t commTime; // Latest time a transaction occurred + std::vector associatedData; + NetClient(char* ipAddr, char* port, CryptoLevel = CryptoLevel::None);// Standard constructor for creating connection + ~NetClient(); + bool close(); + void closeWrite(); + bool isEncrypted(); + size_t getBOPCount(); // Should return the amount of buffered packets to be sent to server + bool write(void* message, ulong_64b size); + bool write(char* message); + Packet read(); + void setEventHandler(std::function); // Register a callback that is guaranteed to be called when the socket has at least one unprocessed packet + void setOnDestroy(std::function); + bool isOpen(); + ulong_64b available(); + }; + + class NetServer { + friend class NetClient; + private: + CryptoLevel pref; + Crypto::RSA::KeyData keys; // Server's keysets (if using encryption) + + std::function onDestroy; + volatile bool _open; + void updateClients(); + protected: + std::thread clientListener; + std::vector>* handlers; + std::vector* clients; + public: + std::function timeoutHandler; + NetServer(char* port, std::function, CryptoLevel); + ~NetServer(); + bool isOpen(); + CryptoLevel getCryptoPreference(); + void addHandler(std::function); + void clearHandlers(); + void setOnDestroy(std::function); + bool close(); + }; +} + +#endif \ No newline at end of file diff --git a/CPPTools/Support.cpp b/CPPTools/Support.cpp index aa3104e..75de7ba 100644 --- a/CPPTools/Support.cpp +++ b/CPPTools/Support.cpp @@ -118,376 +118,4 @@ namespace Tools { for (size_t t = strlen(c); t > 0; --t) if (!isDigit(c[t - 1])) return false; return true; } -} - -namespace IO { - - bool cryptoLevelsAreCompatible(CryptoLevel l1, CryptoLevel l2) { - return !(((l1 == CryptoLevel::None) && (l2 == CryptoLevel::Force)) || ((l2 == CryptoLevel::None) && (l1 == CryptoLevel::Force))); - } - - char* __cdecl readSparse(std::vector* sparse, ulong_64b rSize, bool pop = true) { - 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; - } - - bool hasFullMessage(std::vector *sparse) { - if (sparse->size() < sizeof(ulong_64b)) return false; - ulong_64b size = 0; - char* c = readSparse(sparse, sizeof(ulong_64b), false); - memcpy(&size, c, sizeof(ulong_64b)); - delete[] c; - return sparse->size() >= (size + sizeof(ulong_64b)); - } - - - - void NetClient::sharedSetup() { - if (preferEncrypted != CryptoLevel::None) keys = Crypto::RSA::rsa_gen_keys(); - packets = new std::vector(); - sparse = new std::vector(); - outPacketBuf = new std::vector(); - _open = true; - canWrite = true; - evt = nullptr; - char cryptoPref = static_cast(preferEncrypted); - if(send(_socket, &cryptoPref, 1, 0)==SOCKET_ERROR) throw new _exception(); // Cannot establish connection :( - //_write(&cryptoPref, 1); - if (!noThread) listener = std::thread([this]() { while(_open) { update(); Sleep(25); } }); // Setup separate thread for reading new data - } - 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 _exception(); - - - struct addrinfo *result = NULL, *ptr = NULL, hints; - - ZeroMemory(&hints, sizeof(hints)); - hints.ai_family = AF_INET; - hints.ai_socktype = SOCK_STREAM; - hints.ai_protocol = IPPROTO_TCP; - - iResult = getaddrinfo(ipAddr, port, &hints, &result); - - 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 _exception(); - } - - // Connect to server. - iResult = connect(_socket, ptr->ai_addr, (int)ptr->ai_addrlen); - if (iResult == SOCKET_ERROR) { - closesocket(_socket); - _socket = INVALID_SOCKET; - continue; - } - break; - } - - freeaddrinfo(result); - - if (_socket == INVALID_SOCKET) throw new _exception(); - - sharedSetup(); - } - - 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; - sharedSetup(); - } - - NetClient::~NetClient() { - packets->clear(); - delete packets; - sparse->clear(); - delete sparse; - if (isOpen()) close(); - } - bool NetClient::close() { - bool result = !_open; - _open = false; - result &= (SOCKET_ERROR==shutdown(_socket, SD_BOTH)); - closesocket(_socket); - return result; - } - void NetClient::closeWrite() { - shutdown(_socket, SD_SEND); - canWrite = false; - } - bool NetClient::_write(char* message, ulong_64b size) { - 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; - 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; - else if (i == 0) --wIdx; - } - return true; - } - bool NetClient::write(void* message, ulong_64b size) { - if (firstMessage) { - Packet p; - p.message = (char*)message; - p.size = size; - outPacketBuf->push_back(p); - return true; - } - if (!canWrite) return false; - char* msg = encrypted?Crypto::full_auto_encrypt(message, size, pK, &size):(char*)message; - _write(msg, size); - if (encrypted) delete[] msg; - return true; - } - bool NetClient::write(char* message) { return write(message, strlen(message)); } - bool NetClient::writeBufferedPackets() { - for (size_t t = 0; t < outPacketBuf->size(); ++t) if (!write(outPacketBuf->at(t).message, outPacketBuf->at(t).size)) { delete outPacketBuf; return false; }; - delete outPacketBuf; - return true; - } - Packet NetClient::read() { - if (packets->size() != 0) { - Packet p = packets->at(0); - packets->erase(packets->begin(), packets->begin()+1); // Delete first buffered packet - return p; - } - throw new _exception(); // No packets available! - } - void NetClient::setEventHandler(std::function _ev) { - evt = _ev; - - // Process unhandled packets - if (evt != nullptr) - for (size_t t = packets->size(); t > 0; --t) { - Packet p = packets->at(t - 1); - packets->pop_back(); - evt(this, p); - } - } - bool NetClient::isEncrypted() { return encrypted; } - void NetClient::update() { - int iResult = 0; - unsigned long rCount; - int rdErr = ioctlsocket(_socket, FIONREAD, &rCount); - if (rdErr == SOCKET_ERROR) throw new _exception(); // Error using socket :( - if (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 - } - 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, keys.privKey, &p.size); - if(evt==nullptr) packets->push_back(p); - else evt(this, p); // Notify event handler of a new packet - } - if (iResult > 0) { - if (firstMessage) { - if (!fm_neg_hasLevel && sparse->size() >= 1) { - fm_neg_hasLevel = true; - char* readCrypt = readSparse(sparse, 1); - CryptoLevel lvl = static_cast(*readCrypt); - free(readCrypt); - if (cryptoLevelsAreCompatible(lvl, preferEncrypted)) { - // Determine whether or not to use encryption - encrypted = (preferEncrypted == CryptoLevel::Force) || (lvl == CryptoLevel::Force) || ((preferEncrypted == CryptoLevel::Prefer) && (lvl == CryptoLevel::Prefer)); - - if (!encrypted) { - firstMessage = false; // We're done here. No need to try to get a public key for an unencrypted channel - writeBufferedPackets(); - } - else { - ulong_64b size; - char* c = Crypto::RSA::serializeKey(keys.publKey, &size); - _write(c, size); // This shouldn't be encrypted - delete[] c; - } - } - else throw new _exception(); // Incompatible cryptographic requirements! - } - if (fm_neg_hasLevel && !fm_neg_hasSize && encrypted && sparse->size() >= sizeof(ulong_64b)) { - fm_neg_hasSize = true; - char* readSize = readSparse(sparse, sizeof(ulong_64b)); - - fm_neg_size = 0; - memcpy(&fm_neg_size, readSize, sizeof(ulong_64b)); - free(readSize); - } - if (fm_neg_hasSize && sparse->size() >= fm_neg_size) { - char* msg = readSparse(sparse, fm_neg_size); - - CryptoPP::StringSource src((const byte*)msg, fm_neg_size, true); - pK.Load(src); - - firstMessage = false; - writeBufferedPackets(); - } - } - }else if (iResult < 0 && _open) { - _open = false; - close(); - } - } - bool NetClient::isOpen() { return _open; } - - void NetClient::setOnDestroy(std::function call) { onDestroy = call; } - - 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 f=nullptr, CryptoLevel pref=CryptoLevel::None) : pref(pref) { - if (pref != CryptoLevel::None) keys = Crypto::RSA::rsa_gen_keys(); - _open = true; - timeoutHandler = NULL; - onDestroy = NULL; - handlers = new std::vector >(); - if (f != NULL) handlers->push_back(f); - clients = new std::vector(); - clientListener = std::thread([this, port]() { - SOCKET _server; - WSADATA wsaData; - int iResult; - - struct addrinfo *result = NULL; - struct addrinfo hints; - - // Initialize Winsock - iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); - if (iResult != 0) throw new _exception(); - - - ZeroMemory(&hints, sizeof(hints)); - hints.ai_family = AF_INET; - hints.ai_socktype = SOCK_STREAM; - hints.ai_protocol = IPPROTO_TCP; - hints.ai_flags = AI_PASSIVE; - - // Resolve the server address and port - iResult = getaddrinfo(NULL, port, &hints, &result); - if (iResult) { - 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 _exception(); - } - - // Setup the TCP listening socket - iResult = bind(_server, result->ai_addr, (int)result->ai_addrlen); - if (iResult == SOCKET_ERROR) { - freeaddrinfo(result); - closesocket(_server); - 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 _exception(); - } - timeval t; - t.tv_sec = 0; - t.tv_usec = 5000; - do { - 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 (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(); - else break; - } - 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)) - break; - - } - updateClients(); - } while (_open); - closesocket(_server); - close(); - }); - - } - - NetServer::~NetServer() { - if (_open) close(); - handlers->clear(); - delete handlers; - clients->clear(); - delete clients; - onDestroy(); - } - - void NetServer::addHandler(std::function evtH) { - handlers->push_back(evtH); - } - - void NetServer::clearHandlers() { - handlers->clear(); - } - - void NetServer::updateClients() { - for (ulong_64b t = clients->size(); t > 0; --t) { - NetClient* c = clients->at(t-1); - if (!c->isOpen() || (timeoutHandler != NULL && !timeoutHandler(c))) { - clients->erase(clients->begin() + t - 1, clients->begin() + t); - c->close(); - } - else c->update(); - } - } - CryptoLevel NetServer::getCryptoPreference() { return pref; } - - bool NetServer::isOpen() { return _open; } - - void NetServer::setOnDestroy(std::function call) { onDestroy = call; } } \ No newline at end of file diff --git a/CPPTools/Support.h b/CPPTools/Support.h index 1aae9d1..63b24ca 100644 --- a/CPPTools/Support.h +++ b/CPPTools/Support.h @@ -3,27 +3,8 @@ #ifndef _SUPPORT_H #define _SUPPORT_H -#ifdef _SUPPORT_SMALL_BUF -#define BUFSIZE 512 -#define BUF_2_MAX 2048 -#else -#define BUFSIZE 16384 -#define BUF_2_MAX 16384 -#endif - -#define WIN32_LEAN_AND_MEAN - #include "ArchAbstract.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include #include "Crypto.h" @@ -46,89 +27,4 @@ namespace Tools { bool isIP(char* c); } -namespace IO { - - enum CryptoLevel { None, Prefer, Force }; - - struct Packet { - ulong_64b size; - char* message; - }; - - class NetServer; - class NetClient { - friend class NetServer; // Allow NetServer to access all members of NetClient - - private: - 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 - 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; - std::vector* sparse; - std::vector* outPacketBuf; - Crypto::RSA::KeyData keys; // Client's keysets (if using encryption) - CryptoPP::RSAFunction pK; // Remote host's public key (if using encryption) - - 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 - 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::thread listener; // Incoming data listener (optional) - SOCKET _socket; // Underlying socket used for communication - std::vector* packets; // Basically a set containing a backlog of unprocessed data. Will oly be used if event handler doesn't exist - std::function evt; // New data event handler - std::function onDestroy; // Event handler called when NetClient object is destroyed - public: - time_t commTime; // Latest time a transaction occurred - std::vector associatedData; - NetClient(char* ipAddr, char* port, CryptoLevel = CryptoLevel::None);// Standard constructor for creating connection - ~NetClient(); - bool close(); - void closeWrite(); - bool isEncrypted(); - size_t getBOPCount(); // Should return the amount of buffered packets to be sent to server - bool write(void* message, ulong_64b size); - bool write(char* message); - Packet read(); - void setEventHandler(std::function); // Register a callback that is guaranteed to be called when the socket has at least one unprocessed packet - void setOnDestroy(std::function); - bool isOpen(); - ulong_64b available(); - }; - - class NetServer { - friend class NetClient; - private: - CryptoLevel pref; - Crypto::RSA::KeyData keys; // Server's keysets (if using encryption) - - std::function onDestroy; - volatile bool _open; - void updateClients(); - protected: - std::thread clientListener; - std::vector>* handlers; - std::vector* clients; - public: - std::function timeoutHandler; - NetServer(char* port, std::function, CryptoLevel); - ~NetServer(); - bool isOpen(); - CryptoLevel getCryptoPreference(); - void addHandler(std::function); - void clearHandlers(); - void setOnDestroy(std::function); - bool close(); - }; -} #endif \ No newline at end of file diff --git a/CPPTools/Test.cpp b/CPPTools/Test.cpp deleted file mode 100644 index 3c37c46..0000000 --- a/CPPTools/Test.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include "Support.h" -#include - -int main() { - std::vector* sparse = new std::vector(); - - sparse->push_back(1); - sparse->push_back(2); - sparse->push_back(3); - - std::cout << sparse->size() << std::endl; - - sparse->erase(sparse->begin(), sparse->begin()+sparse->size()); - - std::cout << sparse->size() << std::endl; - std::cin.ignore(); -} \ No newline at end of file diff --git a/CPPTools/Tools.h b/CPPTools/Tools.h index eaf0a9e..a3ae697 100644 --- a/CPPTools/Tools.h +++ b/CPPTools/Tools.h @@ -2,4 +2,5 @@ #include "Crypto.h" #include "Support.h" -#include "ArchAbstract.h" \ No newline at end of file +#include "ArchAbstract.h" +#include "Net.h" \ No newline at end of file