#include #include #include #include #include TunnelEndpoint::TunnelEndpoint(QObject* parent) : QObject(parent), tcpServer(new QTcpServer(this)){} TunnelEndpoint::~TunnelEndpoint() = default; bool TunnelEndpoint::StartTunnel(AuthProfile* profile, const QString &type, const QByteArray &jsonData) { this->profile = profile; QString urlTemplate = "wss://%1:%2%3/channel"; QString sUrl = urlTemplate.arg( profile->GetHost() ).arg( profile->GetPort() ).arg( profile->GetEndpoint() ); this->wsUrl = QUrl(sUrl); QJsonDocument doc = QJsonDocument::fromJson(jsonData); if (doc.isNull() || !doc.isObject()) return false; QJsonObject obj = doc.object(); this->tunnelType = type; if (type == "socks5") { this->useAuth = obj["use_auth"].toBool(); this->username = obj["username"].toString(); this->password = obj["password"].toString(); connect(tcpServer, &QTcpServer::newConnection, this, &TunnelEndpoint::onStartSocksChannel); return Listen(obj); } else if (type == "socks4") { connect(tcpServer, &QTcpServer::newConnection, this, &TunnelEndpoint::onStartSocksChannel); return Listen(obj); } else if (type == "lportfwd") { connect(tcpServer, &QTcpServer::newConnection, this, &TunnelEndpoint::onStartLpfChannel); return Listen(obj); } else if (type == "rportfwd") { } return false; } bool TunnelEndpoint::Listen(const QJsonObject &obj) { lHost = obj["l_host"].toString(); lPort = obj["l_port"].toInt(); if (!tcpServer->listen(QHostAddress(lHost), lPort)) { MessageError(tcpServer->errorString()); return false; } return true; } void TunnelEndpoint::SetTunnelId(const QString &tunnelId) { this->tunnelId = tunnelId; } void TunnelEndpoint::StopChannel(const QString& channelId) { auto it = tunnelChannels.find(channelId); if (it == tunnelChannels.end()) return; tunnelChannels.erase(it); } void TunnelEndpoint::Stop() { if (tcpServer && tcpServer->isListening()) tcpServer->close(); for (auto it = tunnelChannels.begin(); it != tunnelChannels.end(); ) { ChannelHandle handle = it.value(); it = tunnelChannels.erase(it); if (handle.worker && handle.thread) { QMetaObject::invokeMethod(handle.worker, "stop", Qt::QueuedConnection); handle.thread->quit(); handle.thread->wait(3000); } } } void TunnelEndpoint::startWorker(QTcpSocket* clientSock, const QJsonObject& otpData, const QString& channelId) { QString otp; bool otpResult = HttpReqGetOTP("channel_tunnel", otpData, profile->GetURL(), profile->GetAccessToken(), &otp); if (!otpResult) { clientSock->deleteLater(); return; } QThread* thread = new QThread; TunnelWorker* worker = new TunnelWorker(clientSock, otp, this->wsUrl); clientSock->setParent(nullptr); clientSock->moveToThread(thread); worker->moveToThread(thread); clientSock->setParent(worker); connect(thread, &QThread::started, worker, &TunnelWorker::start); connect(worker, &TunnelWorker::finished, thread, &QThread::quit); connect(worker, &TunnelWorker::finished, worker, &TunnelWorker::deleteLater); connect(thread, &QThread::finished, thread, &QThread::deleteLater); connect(worker, &TunnelWorker::finished, this, [this, channelId]() {StopChannel(channelId);}); tunnelChannels[channelId] = {thread, worker, channelId}; thread->start(); } void TunnelEndpoint::onStartLpfChannel() { if (this->tunnelId.isEmpty()) return; while (tcpServer->hasPendingConnections()) { QTcpSocket* clientSock = tcpServer->nextPendingConnection(); QString channelId = GenerateRandomString(8, "hex"); QJsonObject otpData; otpData["tunnel_id"] = this->tunnelId; otpData["channel_id"] = channelId; startWorker(clientSock, otpData, channelId); } } void TunnelEndpoint::startHandshakeWorker(QTcpSocket* clientSock, const QString& type) { QThread* thread = new QThread; SocksHandshakeWorker* worker = new SocksHandshakeWorker(clientSock, this->tunnelId, type, this->useAuth, this->username, this->password, this->profile->GetAccessToken(), this->profile->GetURL(), this->wsUrl); clientSock->setParent(nullptr); clientSock->moveToThread(thread); worker->moveToThread(thread); connect(thread, &QThread::started, worker, &SocksHandshakeWorker::process); connect(worker, &SocksHandshakeWorker::workerReady, this, &TunnelEndpoint::onWorkerReady, Qt::QueuedConnection); connect(worker, &SocksHandshakeWorker::handshakeFailed, this, &TunnelEndpoint::onHandshakeFailed, Qt::QueuedConnection); connect(worker, &SocksHandshakeWorker::workerReady, worker, &SocksHandshakeWorker::deleteLater); connect(worker, &SocksHandshakeWorker::handshakeFailed, worker, &SocksHandshakeWorker::deleteLater); connect(worker, &SocksHandshakeWorker::handshakeFailed, thread, &QThread::quit); connect(thread, &QThread::finished, thread, &QThread::deleteLater); thread->start(); } void TunnelEndpoint::onStartSocksChannel() { if (this->tunnelId.isEmpty()) return; while (tcpServer->hasPendingConnections()) { QTcpSocket* clientSock = tcpServer->nextPendingConnection(); startHandshakeWorker(clientSock, this->tunnelType); } } void TunnelEndpoint::onWorkerReady(TunnelWorker* worker, const QString& channelId) { QThread* thread = worker->thread(); connect(worker, &TunnelWorker::finished, thread, &QThread::quit); connect(worker, &TunnelWorker::finished, worker, &TunnelWorker::deleteLater); connect(thread, &QThread::finished, thread, &QThread::deleteLater); connect(worker, &TunnelWorker::finished, this, [this, channelId]() { StopChannel(channelId); }); tunnelChannels[channelId] = {thread, worker, channelId}; QMetaObject::invokeMethod(worker, "start", Qt::QueuedConnection); } void TunnelEndpoint::onHandshakeFailed() { }