mirror of
https://github.com/outbackdingo/sysadm.git
synced 2026-01-27 10:20:26 +00:00
{
namespace : rpc,
name : identify,
id : junk,
args : junk
}
Return args:
args : {
type : [server/client/bridge],
hostname : <hostname>
}
216 lines
8.1 KiB
C++
216 lines
8.1 KiB
C++
// ===============================
|
|
// PC-BSD SysAdm Bridge Server
|
|
// Available under the 3-clause BSD License
|
|
// Written by: Ken Moore <ken@pcbsd.org> May 2016
|
|
// =================================
|
|
#include "BridgeServer.h"
|
|
|
|
#define DEBUG 0
|
|
|
|
//=======================
|
|
// PUBLIC
|
|
//=======================
|
|
BridgeServer::BridgeServer() : QWebSocketServer("sysadm-bridge", QWebSocketServer::SecureMode){
|
|
//Setup all the various settings
|
|
connect(AUTHSYSTEM, SIGNAL(BlockHost(QHostAddress)), this, SLOT(BlackListConnection(QHostAddress)) );
|
|
}
|
|
|
|
BridgeServer::~BridgeServer(){
|
|
|
|
}
|
|
|
|
bool BridgeServer::startServer(quint16 port){
|
|
if(!QSslSocket::supportsSsl()){ qDebug() << "No SSL Support on this system!!!"; return false; }
|
|
else{
|
|
qDebug() << "Using SSL Library:";
|
|
qDebug() << " - Version:" << QSslSocket::sslLibraryVersionString();
|
|
}
|
|
bool ok = setupWebSocket(port);
|
|
|
|
if(ok){
|
|
//QCoreApplication::processEvents();
|
|
qDebug() << " URL:" << this->serverUrl().toString();
|
|
}else{
|
|
qCritical() << "Could not start server - exiting...";
|
|
}
|
|
|
|
return ok;
|
|
}
|
|
|
|
//===================
|
|
// PUBLIC SLOTS
|
|
//===================
|
|
void BridgeServer::sendMessage(QString toID, QString msg){
|
|
//qDebug() << "Try to forward message:" << toID;
|
|
for(int i=0; i<OpenSockets.length(); i++){
|
|
if(OpenSockets[i]->ID()==toID){ OpenSockets[i]->forwardMessage(msg); }
|
|
}
|
|
}
|
|
|
|
//===================
|
|
// PRIVATE
|
|
//===================
|
|
bool BridgeServer::setupWebSocket(quint16 port){
|
|
//SSL Configuration
|
|
qDebug() << "SSL Files:" << SSLFILEDIR+"/["+SSLCERTFILE+", "+SSLKEYFILE+"]";
|
|
QSslConfiguration config = QSslConfiguration::defaultConfiguration();
|
|
QFile CF( SSLFILEDIR +"/"+SSLCERTFILE );
|
|
if(CF.open(QIODevice::ReadOnly) ){
|
|
QSslCertificate CERT(&CF,QSsl::Pem);
|
|
config.setLocalCertificate( CERT );
|
|
CF.close();
|
|
}else{
|
|
qWarning() << "Could not read WS certificate file:" << CF.fileName();
|
|
}
|
|
QFile KF( SSLFILEDIR +"/"+SSLKEYFILE );
|
|
if(KF.open(QIODevice::ReadOnly) ){
|
|
QSslKey KEY(&KF, QSsl::Rsa, QSsl::Pem);
|
|
config.setPrivateKey( KEY );
|
|
KF.close();
|
|
}else{
|
|
qWarning() << "Could not read WS key file:" << KF.fileName();
|
|
}
|
|
config.setPeerVerifyMode(QSslSocket::VerifyNone);
|
|
config.setProtocol(SSLVERSION);
|
|
this->setSslConfiguration(config);
|
|
//Setup Connections
|
|
connect(this, SIGNAL(newConnection()), this, SLOT(NewSocketConnection()) );
|
|
connect(this, SIGNAL(acceptError(QAbstractSocket::SocketError)), this, SLOT(NewConnectError(QAbstractSocket::SocketError)) );
|
|
connect(this, SIGNAL(closed()), this, SLOT(ServerClosed()) );
|
|
connect(this, SIGNAL(serverError(QWebSocketProtocol::CloseCode)), this, SLOT(ServerError(QWebSocketProtocol::CloseCode)) );
|
|
connect(this, SIGNAL(originAuthenticationRequired(QWebSocketCorsAuthenticator*)), this, SLOT(OriginAuthRequired(QWebSocketCorsAuthenticator*)) );
|
|
connect(this, SIGNAL(peerVerifyError(const QSslError&)), this, SLOT(PeerVerifyError(const QSslError&)) );
|
|
connect(this, SIGNAL(sslErrors(const QList<QSslError>&)), this, SLOT(SslErrors(const QList<QSslError>&)) );
|
|
connect(this, SIGNAL(acceptError(QAbstractSocket::SocketError)), this, SLOT(ConnectError(QAbstractSocket::SocketError)) );
|
|
//Now start the server
|
|
return this->listen(QHostAddress::Any, port);
|
|
}
|
|
|
|
//Server Blacklist / DDOS mitigator
|
|
bool BridgeServer::allowConnection(QHostAddress addr){
|
|
//Check if this addr is on the blacklist
|
|
QString key = "blacklist/"+addr.toString();
|
|
if(!CONFIG->contains(key) ){ return true; } //not in the list
|
|
//Address on the list - see if the timeout has expired
|
|
QDateTime dt = CONFIG->value(key,QDateTime()).toDateTime();
|
|
if(dt.addSecs(CONFIG->value("blacklist_settings/block_minutes",60).toInt()*60) < QDateTime::currentDateTime()){
|
|
//This entry has timed out - go ahead and allow it
|
|
CONFIG->remove(key); //make the next connection check for this IP faster again
|
|
return true;
|
|
}else{
|
|
return false; //blacklist block is still in effect
|
|
}
|
|
|
|
}
|
|
|
|
QString BridgeServer::generateID(QString name){
|
|
return name+"::"+QUuid::createUuid().toString();
|
|
}
|
|
|
|
//=======================
|
|
// PRIVATE SLOTS
|
|
//=======================
|
|
//GENERIC SERVER SIGNALS
|
|
// New Connection Signals
|
|
void BridgeServer::NewSocketConnection(){
|
|
BridgeConnection *sock = 0;
|
|
if(this->hasPendingConnections()){
|
|
//qDebug() << "New incoming connection..";
|
|
QWebSocket *ws = this->nextPendingConnection();
|
|
if(allowConnection(ws->peerAddress()) ){
|
|
sock = new BridgeConnection( this, ws, generateID("") );
|
|
}else{
|
|
ws->abort();
|
|
}
|
|
}
|
|
if(sock==0){ return; } //no new connection
|
|
//qDebug() << "New Socket Connection";
|
|
connect(sock, SIGNAL(SocketClosed(QString)), this, SLOT(SocketClosed(QString)) );
|
|
connect(sock, SIGNAL(SocketMessage(QString, QString)), this, SLOT(sendMessage(QString, QString)) );
|
|
connect(sock, SIGNAL(keysChanged(QString, bool, QStringList)), this, SLOT(announceKeyChange(QString, bool, QStringList)) );
|
|
OpenSockets << sock;
|
|
}
|
|
|
|
void BridgeServer::NewConnectError(QAbstractSocket::SocketError err){
|
|
qWarning() << "New Connection Error["+QString::number(err)+"]:" << this->errorString();
|
|
QTimer::singleShot(0,this, SLOT(NewSocketConnection()) ); //check for a new connection
|
|
}
|
|
|
|
//Socket Blacklist function
|
|
void BridgeServer::BlackListConnection(QHostAddress addr){
|
|
//Make sure this is not the localhost (never block that)
|
|
if(addr!=QHostAddress(QHostAddress::LocalHost) && addr!=QHostAddress(QHostAddress::LocalHostIPv6) && addr.toString()!="::ffff:127.0.0.1" ){
|
|
//Block this remote host
|
|
qDebug() << "Blacklisting IP Temporarily: " << addr.toString();
|
|
CONFIG->setValue("blacklist/"+addr.toString(), QDateTime::currentDateTime());
|
|
}
|
|
}
|
|
|
|
//WEBSOCKET SERVER SIGNALS
|
|
// Overall Server signals
|
|
void BridgeServer::ServerClosed(){
|
|
QCoreApplication::exit(0);
|
|
}
|
|
|
|
void BridgeServer::ServerError(QWebSocketProtocol::CloseCode code){
|
|
qWarning() << "Server Error["+QString::number(code)+"]:" << this->errorString();
|
|
}
|
|
|
|
// - SSL/Authentication Signals (still websocket only)
|
|
void BridgeServer::OriginAuthRequired(QWebSocketCorsAuthenticator *auth){
|
|
//This just provides the ability to check the URL/app which is trying to connect from
|
|
// - this is not really useful right now since anything could be set there (accurate or not)
|
|
auth->setAllowed(true);
|
|
}
|
|
|
|
void BridgeServer::ConnectError(QAbstractSocket::SocketError err){
|
|
qDebug() << "Connection Error" << err;
|
|
}
|
|
|
|
void BridgeServer::PeerVerifyError(const QSslError &err){
|
|
qDebug() << "Peer Verification Error:" << err.errorString();
|
|
|
|
}
|
|
|
|
void BridgeServer::SslErrors(const QList<QSslError> &list){
|
|
qWarning() << "SSL Errors:";
|
|
for(int i=0; i<list.length(); i++){
|
|
qWarning() << " - " << list[i].errorString();
|
|
}
|
|
}
|
|
|
|
// - More Functions for all socket interactions
|
|
void BridgeServer::SocketClosed(QString ID){
|
|
qDebug() << "Socket Closed:" << ID;
|
|
for(int i=0; i<OpenSockets.length(); i++){
|
|
if(OpenSockets[i]->ID()==ID){ delete OpenSockets.takeAt(i); break; }
|
|
}
|
|
QTimer::singleShot(0,this, SLOT(NewSocketConnection()) ); //check for a new connection
|
|
}
|
|
|
|
// Connection Keys Changed
|
|
void BridgeServer::announceKeyChange(QString ID, bool isServer, QStringList keys){
|
|
//qDebug() << "Key Change:" << ID << isServer << keys;
|
|
for(int c = 0; c<OpenSockets.length(); c++){
|
|
bool server = OpenSockets[c]->isServer();
|
|
QStringList keys = OpenSockets[c]->validKeySums();
|
|
keys.removeDuplicates();
|
|
QStringList IDs;
|
|
for(int i=0; i<OpenSockets.length(); i++){
|
|
if(i==c){ continue; } //current socket
|
|
if(!OpenSockets[i]->isActive()){ continue; } //skip right now (probably waiting on a signal that the connection closed)
|
|
else if(OpenSockets[i]->isServer() != server){ //look for a server/client pair
|
|
//compare keys to look for matches
|
|
QStringList chkkeys = OpenSockets[i ]->validKeySums();
|
|
chkkeys.removeDuplicates();
|
|
//qDebug() << "Known Keys for ID:" << OpenSockets[i]->ID() << chkkeys;
|
|
chkkeys << keys;
|
|
if(chkkeys.removeDuplicates() > 0){
|
|
IDs << OpenSockets[i]->ID();
|
|
}
|
|
}
|
|
}//end inner loop of sockets
|
|
OpenSockets[c]->announceIDAvailability(IDs);
|
|
} //end loop over sockets to check
|
|
}
|