Heart/ClientProtocol/abstractnode.cpp

542 lines
13 KiB
C++
Raw Normal View History

2019-10-10 17:56:57 +03:00
#include "abstractdata.h"
2019-10-09 17:58:58 +03:00
#include "abstractnode.h"
2019-10-14 18:20:29 +03:00
#include <QSslCertificate>
#include <QSslKey>
#include <QSslKey>
2019-10-11 18:30:09 +03:00
#include <QSslSocket>
2019-10-10 17:56:57 +03:00
#include <quasarapp.h>
2019-10-14 18:20:29 +03:00
#include <openssl/rsa.h>
#include <openssl/evp.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
2019-10-10 17:56:57 +03:00
2019-10-09 17:58:58 +03:00
namespace ClientProtocol {
2019-10-15 17:27:27 +03:00
AbstractNode::AbstractNode(SslMode mode, QObject *ptr):
2019-10-10 17:56:57 +03:00
QTcpServer(ptr) {
2019-10-15 17:27:27 +03:00
_mode = mode;
setMode(_mode);
2019-10-10 17:56:57 +03:00
}
bool AbstractNode::run(const QString &addres, unsigned short port) {
if (!listen(QHostAddress(addres), port)) {
QuasarAppUtils::Params::verboseLog("Run fail " + this->errorString(),
QuasarAppUtils::Error);
return false;
}
return true;
}
void AbstractNode::stop() {
close();
for (auto &&i : _connections) {
i.info.disconnect();
}
}
AbstractNodeInfo *AbstractNode::getInfoPtr(quint32 id) {
if (!_connections.contains(id)) {
return nullptr;
}
return &_connections[id].info;
}
AbstractNodeInfo AbstractNode::getInfo(quint32 id) const{
return _connections.value(id).info;
}
2019-10-11 18:30:09 +03:00
AbstractNodeInfo *AbstractNode::getInfoPtr(const QHostAddress &id) {
return getInfoPtr(qHash(id));
}
AbstractNodeInfo AbstractNode::getInfo(const QHostAddress &id) const {
return getInfo(qHash(id));
}
2019-10-10 17:56:57 +03:00
void AbstractNode::ban(quint32 target) {
auto info = getInfoPtr(target);
if (!info)
_connections[target] = NodeInfoData{};
_connections[target].info.ban();
}
void AbstractNode::unBan(quint32 target) {
if (!_connections.contains(target)) {
return;
}
_connections[target].info.unBan();
}
2019-10-15 18:14:04 +03:00
void AbstractNode::connectToHost(const QHostAddress &ip, short port, SslMode mode) {
QAbstractSocket *socket;
if (mode == SslMode::NoSSL) {
socket = new QTcpSocket(nullptr);
} else {
socket = new QSslSocket(nullptr);
}
registerSocket(socket);
socket->connectToHost(ip, port);
}
2019-10-10 17:56:57 +03:00
unsigned short AbstractNode::port() const {
return serverPort();
}
QHostAddress AbstractNode::address() const {
return serverAddress();
}
2019-10-14 18:20:29 +03:00
2019-10-15 17:27:27 +03:00
AbstractNode::~AbstractNode() {
stop();
}
QSslConfiguration AbstractNode::getSslConfig() const {
return _ssl;
}
bool AbstractNode::generateRSAforSSL(EVP_PKEY *pkey) const {
2019-10-14 18:20:29 +03:00
RSA * rsa = nullptr;
2019-10-15 17:27:27 +03:00
if (!pkey) {
return false;
}
if (!RSA_generate_key_ex(rsa, 2048, nullptr, nullptr)) {
return false;
}
q_check_ptr(rsa);
if (EVP_PKEY_assign_RSA(pkey, rsa) <= 0)
return false;
return true;
}
bool AbstractNode::generateSslDataPrivate(const SslSrtData &data, QSslCertificate& r_srt, QSslKey& r_key) {
EVP_PKEY *pkey = EVP_PKEY_new();
if (!generateRSAforSSL(pkey)) {
return false;
}
2019-10-14 18:20:29 +03:00
X509 * x509 = nullptr;
X509_NAME * name = nullptr;
BIO * bp_public = nullptr, * bp_private = nullptr;
2019-10-15 17:27:27 +03:00
const char *buffer = nullptr;
2019-10-14 18:20:29 +03:00
int size;
x509 = X509_new();
q_check_ptr(x509);
ASN1_INTEGER_set(X509_get_serialNumber(x509), 1);
X509_gmtime_adj(X509_get_notBefore(x509), 0); // not before current time
2019-10-15 17:27:27 +03:00
X509_gmtime_adj(X509_get_notAfter(x509), data.endTime); // not after a year from this point
2019-10-14 18:20:29 +03:00
X509_set_pubkey(x509, pkey);
name = X509_get_subject_name(x509);
q_check_ptr(name);
2019-10-15 17:27:27 +03:00
unsigned char *C = reinterpret_cast<unsigned char *>(data.country.toLatin1().data());
X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC, C, -1, -1, 0);
unsigned char *O = reinterpret_cast<unsigned char *>(data.organization.toLatin1().data());
X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC, O, -1, -1, 0);
unsigned char *CN = reinterpret_cast<unsigned char *>(data.commonName.toLatin1().data());
X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, CN, -1, -1, 0);
2019-10-14 18:20:29 +03:00
X509_set_issuer_name(x509, name);
2019-10-15 17:27:27 +03:00
X509_sign(x509, pkey, EVP_sha256());
2019-10-14 18:20:29 +03:00
bp_private = BIO_new(BIO_s_mem());
q_check_ptr(bp_private);
2019-10-15 17:27:27 +03:00
if(PEM_write_bio_PrivateKey(bp_private, pkey, nullptr, nullptr, 0, nullptr, nullptr) != 1) {
2019-10-14 18:20:29 +03:00
EVP_PKEY_free(pkey);
X509_free(x509);
BIO_free_all(bp_private);
qCritical("PEM_write_bio_PrivateKey");
return false;
}
2019-10-15 17:27:27 +03:00
2019-10-14 18:20:29 +03:00
bp_public = BIO_new(BIO_s_mem());
q_check_ptr(bp_public);
2019-10-15 17:27:27 +03:00
if(PEM_write_bio_X509(bp_public, x509) != 1){
2019-10-14 18:20:29 +03:00
EVP_PKEY_free(pkey);
X509_free(x509);
BIO_free_all(bp_public);
BIO_free_all(bp_private);
qCritical("PEM_write_bio_PrivateKey");
return false;
}
2019-10-15 17:27:27 +03:00
2019-10-14 18:20:29 +03:00
size = static_cast<int>(BIO_get_mem_data(bp_public, &buffer));
q_check_ptr(buffer);
2019-10-15 17:27:27 +03:00
r_srt = QSslCertificate(QByteArray(buffer, size));
2019-10-14 18:20:29 +03:00
2019-10-15 17:27:27 +03:00
if(r_srt.isNull()) {
EVP_PKEY_free(pkey);
X509_free(x509);
BIO_free_all(bp_public);
BIO_free_all(bp_private);
2019-10-14 18:20:29 +03:00
qCritical("Failed to generate a random client certificate");
return false;
}
size = static_cast<int>(BIO_get_mem_data(bp_private, &buffer));
q_check_ptr(buffer);
2019-10-15 17:27:27 +03:00
r_key = QSslKey(QByteArray(buffer, size), QSsl::Rsa);
if(r_key.isNull()) {
EVP_PKEY_free(pkey);
X509_free(x509);
BIO_free_all(bp_public);
BIO_free_all(bp_private);
2019-10-14 18:20:29 +03:00
qCritical("Failed to generate a random private key");
return false;
}
EVP_PKEY_free(pkey); // this will also free the rsa key
X509_free(x509);
BIO_free_all(bp_public);
BIO_free_all(bp_private);
return true;
}
2019-10-15 17:27:27 +03:00
QSslConfiguration AbstractNode::selfSignedSslConfiguration() {
QSslConfiguration res = QSslConfiguration::defaultConfiguration();
QSslKey pkey;
QSslCertificate crt;
SslSrtData sslData;
if (!generateSslDataPrivate(sslData, crt, pkey)) {
QuasarAppUtils::Params::verboseLog("fail to create ssl certificate. node svitch to InitFromSystem mode",
QuasarAppUtils::Warning);
return res;
}
res.setPrivateKey(pkey);
res.setLocalCertificate(crt);
return res;
2019-10-10 17:56:57 +03:00
}
bool AbstractNode::registerSocket(QAbstractSocket *socket) {
2019-10-14 20:56:40 +03:00
if (connectionsCount() >= maxPendingConnections()) {
return false;
}
2019-10-10 17:56:57 +03:00
auto info = AbstractNodeInfo(socket);
_connections[info.id()] = {info, {}};
connect(socket, &QAbstractSocket::readyRead, this, &AbstractNode::avelableBytes);
connect(socket, &QAbstractSocket::disconnected, this, &AbstractNode::handleDisconnected);
return true;
}
2019-10-11 18:30:09 +03:00
bool AbstractNode::parsePackage(const BasePackage &pkg, AbstractNodeInfo *sender) {
2019-10-10 17:56:57 +03:00
if (!pkg.isValid()) {
QuasarAppUtils::Params::verboseLog("incomming package is not valid!",
QuasarAppUtils::Error);
2019-10-11 18:30:09 +03:00
changeTrust(sender->id(), CRITICAL_ERROOR);
2019-10-10 17:56:57 +03:00
return false;
}
if (!sender->isValid()) {
QuasarAppUtils::Params::verboseLog("sender socket is not valid!",
QuasarAppUtils::Error);
2019-10-11 18:30:09 +03:00
changeTrust(sender->id(), LOGICK_ERROOR);
2019-10-10 17:56:57 +03:00
return false;
}
2019-10-11 18:30:09 +03:00
emit incomingReques(pkg, sender->id());
2019-10-10 17:56:57 +03:00
return true;
}
bool AbstractNode::sendPackage(const BasePackage &pkg, QAbstractSocket *target) {
if (!pkg.isValid()) {
return false;
}
if (!target || !target->isValid()) {
QuasarAppUtils::Params::verboseLog("destination server not valid!",
QuasarAppUtils::Error);
return false;
}
if (!target->waitForConnected()) {
QuasarAppUtils::Params::verboseLog("no connected to server! " + target->errorString(),
QuasarAppUtils::Error);
return false;
}
auto bytes = pkg.toBytes();
bool sendet = bytes.size() == target->write(bytes);
return sendet;
}
bool AbstractNode::sendResponse(const AbstractData &resp, quint32 id, const BaseHeader *req) {
auto client = getInfoPtr(id);
if (!client) {
QuasarAppUtils::Params::verboseLog("Response not sent because client == null",
QuasarAppUtils::Error);
return false;
}
BasePackage pkg;
if (!resp.toPackage(pkg, req->command)) {
QuasarAppUtils::Params::verboseLog("Response not sent because dont create package from object",
QuasarAppUtils::Error);
return false;
}
if (!sendPackage(pkg, client->sct())) {
QuasarAppUtils::Params::verboseLog("Response not sent!",
QuasarAppUtils::Error);
return false;
}
return true;
}
QString AbstractNode::getWorkState() const {
if (isListening()) {
2019-10-14 20:56:40 +03:00
if (connectionsCount() >= maxPendingConnections())
2019-10-10 17:56:57 +03:00
return "overload";
else {
return "Work";
}
}
return "Not running";
}
QString AbstractNode::connectionState() const {
return QString("%0 / %1").arg(connectionsCount()).arg(maxPendingConnections());
}
QStringList AbstractNode::baned() const {
QStringList list = {};
for (auto i = _connections.begin(); i != _connections.end(); ++i) {
2019-10-11 18:30:09 +03:00
if (i.value().info.isBaned()) {
2019-10-10 17:56:57 +03:00
list.push_back(QHostAddress(i.key()).toString());
}
}
return list;
}
int AbstractNode::connectionsCount() const {
int count = 0;
for (auto i : _connections) {
2019-10-11 18:30:09 +03:00
if (i.info.sct()) {
if (!i.info.sct()->isValid()) {
2019-10-10 17:56:57 +03:00
QuasarAppUtils::Params::verboseLog("connection count, findet not valid socket",
QuasarAppUtils::Warning);
}
count++;
}
}
return count;
}
bool AbstractNode::isBaned(QAbstractSocket *socket) const {
2019-10-11 18:30:09 +03:00
auto info = getInfo(socket->peerAddress());
2019-10-10 17:56:57 +03:00
2019-10-11 18:30:09 +03:00
if (!info.isValid()) {
2019-10-10 17:56:57 +03:00
return false;
}
2019-10-11 18:30:09 +03:00
return info.isBaned();
2019-10-10 17:56:57 +03:00
}
void AbstractNode::incomingConnection(qintptr handle) {
2019-10-15 17:27:27 +03:00
if (_mode == SslMode::NoSSL) {
2019-10-11 18:30:09 +03:00
incomingSsl(handle);
} else {
incomingTcp(handle);
}
2019-10-10 17:56:57 +03:00
}
bool AbstractNode::changeTrust(quint32 id, int diff) {
auto ptr = getInfoPtr(id);
if (!ptr) {
return false;
}
auto objTrust = ptr->trust();
if (objTrust >= static_cast<int>(TrustNode::Undefined)) {
return false;
}
if (objTrust <= static_cast<int>(TrustNode::Baned)) {
return false;
}
ptr->setTrust(objTrust + diff);
return true;
}
2019-10-11 18:30:09 +03:00
void AbstractNode::incomingSsl(qintptr socketDescriptor) {
2019-10-14 20:56:40 +03:00
QSslSocket *socket = new QSslSocket;
2019-10-11 18:30:09 +03:00
2019-10-15 17:27:27 +03:00
socket->setSslConfiguration(_ssl);
2019-10-11 18:30:09 +03:00
2019-10-14 20:56:40 +03:00
if (!isBaned(socket) && socket->setSocketDescriptor(socketDescriptor)) {
connect(socket, &QSslSocket::encrypted, [this, socket](){
if (!registerSocket(socket)) {
socket->deleteLater();
}
2019-10-11 18:30:09 +03:00
});
2019-10-14 20:56:40 +03:00
connect(socket, QOverload<const QList<QSslError> &>::of(&QSslSocket::sslErrors),
[socket](const QList<QSslError> &errors){
2019-10-11 18:30:09 +03:00
for (auto &error : errors) {
QuasarAppUtils::Params::verboseLog(error.errorString(), QuasarAppUtils::Error);
}
2019-10-14 20:56:40 +03:00
socket->deleteLater();
2019-10-11 18:30:09 +03:00
});
2019-10-14 20:56:40 +03:00
socket->startServerEncryption();
2019-10-11 18:30:09 +03:00
} else {
2019-10-14 20:56:40 +03:00
delete socket;
2019-10-11 18:30:09 +03:00
}
}
void AbstractNode::incomingTcp(qintptr socketDescriptor) {
2019-10-14 20:56:40 +03:00
QTcpSocket *socket = new QTcpSocket;
if (!isBaned(socket) && socket->setSocketDescriptor(socketDescriptor)) {
if (!registerSocket(socket)) {
delete socket;
}
2019-10-11 18:30:09 +03:00
} else {
2019-10-14 20:56:40 +03:00
delete socket;
2019-10-11 18:30:09 +03:00
}
}
2019-10-10 17:56:57 +03:00
void AbstractNode::avelableBytes() {
2019-10-11 18:30:09 +03:00
auto client = dynamic_cast<QAbstractSocket*>(sender());
2019-10-10 17:56:57 +03:00
if (!client) {
return;
}
2019-10-11 18:30:09 +03:00
auto id = qHash(client->peerAddress());
if (!_connections.contains(id)) {
return;
}
2019-10-10 17:56:57 +03:00
2019-10-11 18:30:09 +03:00
auto &val = _connections[id];
auto array = client->readAll();
if (val.pkg.hdr.isValid()) {
val.pkg.data.append(array);
2019-10-10 17:56:57 +03:00
} else {
2019-10-11 18:30:09 +03:00
val.pkg.reset();
2019-10-10 17:56:57 +03:00
2019-10-11 18:30:09 +03:00
memcpy(&val.pkg.hdr,
2019-10-10 17:56:57 +03:00
array.data(), sizeof(BaseHeader));
2019-10-11 18:30:09 +03:00
val.pkg.data.append(array.mid(sizeof(BaseHeader)));
2019-10-10 17:56:57 +03:00
}
2019-10-11 18:30:09 +03:00
if (val.pkg.isValid()) {
parsePackage(val.pkg, &val.info);
2019-10-10 17:56:57 +03:00
}
2019-10-11 18:30:09 +03:00
if (val.pkg.data.size() >= val.pkg.hdr.size) {
val.pkg.reset();
2019-10-10 17:56:57 +03:00
}
}
void AbstractNode::handleDisconnected() {
auto _sender = dynamic_cast<QTcpSocket*>(sender());
if (_sender) {
// log error
2019-10-11 18:30:09 +03:00
auto ptr = getInfoPtr(_sender->peerAddress());
2019-10-10 17:56:57 +03:00
if (ptr) {
ptr->disconnect();
} else {
QuasarAppUtils::Params::verboseLog("system error in void Server::handleDisconected()"
" address not valid",
QuasarAppUtils::Error);
}
return;
}
2019-10-09 17:58:58 +03:00
2019-10-10 17:56:57 +03:00
QuasarAppUtils::Params::verboseLog("system error in void Server::handleDisconected()"
"dynamic_cast fail!",
QuasarAppUtils::Error);
2019-10-09 17:58:58 +03:00
}
2019-10-15 17:27:27 +03:00
SslMode AbstractNode::getMode() const {
return _mode;
}
bool AbstractNode::setMode(const SslMode &mode) {
if (_mode == mode) {
return true;
}
if (isListening()) {
return false;
}
_mode = mode;
switch (_mode) {
case SslMode::InitFromSystem: {
_ssl = QSslConfiguration::defaultConfiguration();
break;
}
case SslMode::InitSelfSigned: {
_ssl = selfSignedSslConfiguration();
break;
}
default: {
break;
}
}
return true;
}
2019-10-09 17:58:58 +03:00
}