Füge Unterstützung für SSL/TLS in die Konfiguration und das Build-System ein
- Integriere die libwebsockets-Bibliothek für SSL/TLS WebSocket-Unterstützung in `CMakeLists.txt`. - Aktualisiere `chatconfig.json`, um SSL-Optionen wie `ssl_enabled`, `ssl_cert_path` und `ssl_key_path` hinzuzufügen. - Ergänze das `deploy.sh`-Skript um einen Schritt zur optionalen Einrichtung von SSL/TLS. - Modifiziere `update_config.sh`, um die SSL-Konfiguration in die Servereinstellungen zu integrieren. - Implementiere eine Überprüfung in `main.cpp`, um den SSL-Status zu prüfen und entsprechende Meldungen auszugeben.
This commit is contained in:
364
src/core/ssl_server.cpp
Normal file
364
src/core/ssl_server.cpp
Normal file
@@ -0,0 +1,364 @@
|
||||
#include "ssl_server.h"
|
||||
#include "config.h"
|
||||
#include "lib/database.h"
|
||||
#include "chat_room.h"
|
||||
#include "chat_user.h"
|
||||
#include "lib/base.h"
|
||||
#include <iostream>
|
||||
#include <json/json.h>
|
||||
#include <cstring>
|
||||
|
||||
namespace Yc {
|
||||
namespace Lib {
|
||||
|
||||
// Static instance pointer
|
||||
SSLServer* SSLServer::_instance = nullptr;
|
||||
|
||||
// Protocols array definition
|
||||
struct lws_protocols SSLServer::_protocols[] = {
|
||||
{
|
||||
"yourchat-protocol",
|
||||
SSLServer::wsCallback,
|
||||
sizeof(WebSocketUserData),
|
||||
4096
|
||||
},
|
||||
{ nullptr, nullptr, 0, 0 }
|
||||
};
|
||||
|
||||
SSLServer::SSLServer(std::shared_ptr<Config> config, std::shared_ptr<Database> database)
|
||||
: _config(std::move(config)), _database(std::move(database)) {
|
||||
_instance = this;
|
||||
|
||||
// Load SSL settings from config
|
||||
_useSSL = _config->value("server", "ssl_enabled").asBool();
|
||||
_certPath = _config->value("server", "ssl_cert_path").asString();
|
||||
_keyPath = _config->value("server", "ssl_key_path").asString();
|
||||
_port = _config->value("server", "port").asInt();
|
||||
|
||||
if (_useSSL && (_certPath.empty() || _keyPath.empty())) {
|
||||
throw std::runtime_error("SSL enabled but certificate or key path not provided");
|
||||
}
|
||||
}
|
||||
|
||||
SSLServer::~SSLServer() {
|
||||
stop();
|
||||
_instance = nullptr;
|
||||
}
|
||||
|
||||
void SSLServer::run() {
|
||||
_running = true;
|
||||
_serverThread = std::thread([this](){ startServer(); });
|
||||
_messageThread = std::thread([this](){ processMessageQueue(); });
|
||||
}
|
||||
|
||||
void SSLServer::stop() {
|
||||
_running = false;
|
||||
if (_context) lws_cancel_service(_context);
|
||||
if (_serverThread.joinable()) _serverThread.join();
|
||||
if (_messageThread.joinable()) _messageThread.join();
|
||||
if (_context) {
|
||||
lws_context_destroy(_context);
|
||||
_context = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void SSLServer::startServer() {
|
||||
struct lws_context_creation_info info;
|
||||
memset(&info, 0, sizeof(info));
|
||||
info.port = _port;
|
||||
info.protocols = _protocols;
|
||||
|
||||
// SSL/TLS Konfiguration
|
||||
if (_useSSL) {
|
||||
info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
|
||||
info.ssl_cert_filepath = _certPath.c_str();
|
||||
info.ssl_private_key_filepath = _keyPath.c_str();
|
||||
std::cout << "[YourChat] SSL WebSocket Server starting on port " << _port
|
||||
<< " with certificates: " << _certPath << " / " << _keyPath << std::endl;
|
||||
} else {
|
||||
std::cout << "[YourChat] WebSocket Server starting on port " << _port << " (no SSL)" << std::endl;
|
||||
}
|
||||
|
||||
// Reduziere Log-Level um weniger Debug-Ausgaben zu haben
|
||||
setenv("LWS_LOG_LEVEL", "0", 1); // 0 = nur Fehler
|
||||
|
||||
_context = lws_create_context(&info);
|
||||
if (!_context) {
|
||||
throw std::runtime_error("Failed to create LWS context");
|
||||
}
|
||||
|
||||
while (_running) {
|
||||
lws_service(_context, 50);
|
||||
}
|
||||
}
|
||||
|
||||
void SSLServer::processMessageQueue() {
|
||||
while (_running) {
|
||||
std::unique_lock<std::mutex> lock(_queueMutex);
|
||||
_queueCV.wait(lock, [this](){ return !_messageQueue.empty() || !_running; });
|
||||
|
||||
while (!_messageQueue.empty()) {
|
||||
std::string msg = std::move(_messageQueue.front());
|
||||
_messageQueue.pop();
|
||||
lock.unlock();
|
||||
|
||||
// Process message here if needed
|
||||
lock.lock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int SSLServer::wsCallback(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) {
|
||||
if (!_instance) return 0;
|
||||
|
||||
auto *ud = reinterpret_cast<WebSocketUserData*>(user);
|
||||
|
||||
switch (reason) {
|
||||
case LWS_CALLBACK_ESTABLISHED:
|
||||
std::cout << "[YourChat] WebSocket-Verbindung hergestellt" << std::endl;
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_RECEIVE: {
|
||||
std::string msg(reinterpret_cast<char*>(in), len);
|
||||
std::cout << "[YourChat] WebSocket-Nachricht empfangen: " << msg << std::endl;
|
||||
|
||||
_instance->handleWebSocketMessage(wsi, msg);
|
||||
break;
|
||||
}
|
||||
|
||||
case LWS_CALLBACK_SERVER_WRITEABLE: {
|
||||
if (ud->pendingMessage.empty()) {
|
||||
// Ping senden
|
||||
unsigned char buf[LWS_PRE + 4];
|
||||
memcpy(buf + LWS_PRE, "ping", 4);
|
||||
lws_write(wsi, buf + LWS_PRE, 4, LWS_WRITE_TEXT);
|
||||
} else {
|
||||
// Nachricht senden
|
||||
unsigned char buf[LWS_PRE + ud->pendingMessage.length()];
|
||||
memcpy(buf + LWS_PRE, ud->pendingMessage.c_str(), ud->pendingMessage.length());
|
||||
lws_write(wsi, buf + LWS_PRE, ud->pendingMessage.length(), LWS_WRITE_TEXT);
|
||||
ud->pendingMessage.clear();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case LWS_CALLBACK_CLOSED:
|
||||
if (!ud->token.empty()) {
|
||||
_instance->removeConnection(ud->token);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SSLServer::handleWebSocketMessage(struct lws *wsi, const std::string& message) {
|
||||
try {
|
||||
Json::Value root;
|
||||
Json::Reader reader;
|
||||
|
||||
if (!reader.parse(message, root)) {
|
||||
std::cerr << "[YourChat] JSON Parse Error: " << reader.getFormattedErrorMessages() << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
std::string type = root.get("type", "").asString();
|
||||
std::string token = root.get("token", "").asString();
|
||||
|
||||
if (type == "init") {
|
||||
// User initialization
|
||||
std::string name = root.get("name", "").asString();
|
||||
std::string room = root.get("room", "").asString();
|
||||
std::string color = root.get("color", "#000000").asString();
|
||||
std::string password = root.get("password", "").asString();
|
||||
|
||||
if (name.empty() || room.empty()) {
|
||||
Json::Value errorJson;
|
||||
errorJson["type"] = "error";
|
||||
errorJson["message"] = "missing_fields";
|
||||
errorJson["detail"] = "'name' und 'room' müssen gesetzt sein.";
|
||||
sendMessage(lws_get_socket_fd(wsi), errorJson);
|
||||
return;
|
||||
}
|
||||
|
||||
if (userExists(name)) {
|
||||
Json::Value errorJson;
|
||||
errorJson["type"] = "error";
|
||||
errorJson["message"] = "loggedin";
|
||||
sendMessage(lws_get_socket_fd(wsi), errorJson);
|
||||
return;
|
||||
}
|
||||
|
||||
// Generate token if not provided
|
||||
if (token.empty()) {
|
||||
token = Base::generateToken();
|
||||
}
|
||||
|
||||
// Store user data
|
||||
auto *ud = reinterpret_cast<WebSocketUserData*>(lws_wsi_user(wsi));
|
||||
ud->token = token;
|
||||
ud->userName = name;
|
||||
ud->userColor = color;
|
||||
ud->currentRoom = room;
|
||||
ud->authenticated = true;
|
||||
|
||||
// Add to connections
|
||||
addConnection(token, wsi);
|
||||
|
||||
// Try to add user to room
|
||||
bool added = false;
|
||||
for (auto &roomObj: _rooms) {
|
||||
if (roomObj->name() == room) {
|
||||
// Create ChatUser and add to room
|
||||
auto chatUser = std::make_shared<ChatUser>(name, color, token, lws_get_socket_fd(wsi));
|
||||
if (roomObj->addUser(name, color, password, lws_get_socket_fd(wsi))) {
|
||||
_users[token] = chatUser;
|
||||
added = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!added) {
|
||||
Json::Value errorJson;
|
||||
errorJson["type"] = "error";
|
||||
errorJson["message"] = "room_not_found_or_join_failed";
|
||||
sendMessage(lws_get_socket_fd(wsi), errorJson);
|
||||
} else {
|
||||
// Send success response
|
||||
Json::Value successJson;
|
||||
successJson["type"] = "init_success";
|
||||
successJson["token"] = token;
|
||||
successJson["message"] = "Erfolgreich verbunden";
|
||||
sendMessage(lws_get_socket_fd(wsi), successJson);
|
||||
}
|
||||
} else if (type == "message") {
|
||||
// Handle chat message
|
||||
if (!token.empty()) {
|
||||
auto user = getUserByToken(token);
|
||||
if (user) {
|
||||
std::string msg = root.get("message", "").asString();
|
||||
// Process message through room
|
||||
for (auto &room: _rooms) {
|
||||
if (room->userIsInRoom(user->name())) {
|
||||
room->addMessage(user, msg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Add more message types as needed
|
||||
|
||||
} catch (const std::exception &e) {
|
||||
std::cerr << "[YourChat] Error processing WebSocket message: " << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void SSLServer::addConnection(const std::string& token, struct lws *wsi) {
|
||||
std::unique_lock<std::shared_mutex> lock(_connectionsMutex);
|
||||
_connections[token] = wsi;
|
||||
std::cout << "[YourChat] Verbindung für Token " << token << " gespeichert" << std::endl;
|
||||
}
|
||||
|
||||
void SSLServer::removeConnection(const std::string& token) {
|
||||
std::unique_lock<std::shared_mutex> lock(_connectionsMutex);
|
||||
_connections.erase(token);
|
||||
_users.erase(token);
|
||||
std::cout << "[YourChat] Verbindung für Token " << token << " entfernt" << std::endl;
|
||||
}
|
||||
|
||||
std::shared_ptr<ChatUser> SSLServer::getUserByToken(const std::string& token) {
|
||||
std::shared_lock<std::shared_mutex> lock(_connectionsMutex);
|
||||
auto it = _users.find(token);
|
||||
return (it != _users.end()) ? it->second : nullptr;
|
||||
}
|
||||
|
||||
void SSLServer::sendMessage(int socket, const Json::Value& message) {
|
||||
Json::StreamWriterBuilder builder;
|
||||
std::string jsonString = Json::writeString(builder, message);
|
||||
sendMessage(socket, jsonString);
|
||||
}
|
||||
|
||||
void SSLServer::sendMessage(int socket, const std::string& message) {
|
||||
// Find the WebSocket connection for this socket
|
||||
std::shared_lock<std::shared_mutex> lock(_connectionsMutex);
|
||||
for (auto& pair : _connections) {
|
||||
if (lws_get_socket_fd(pair.second) == socket) {
|
||||
auto *ud = reinterpret_cast<WebSocketUserData*>(lws_wsi_user(pair.second));
|
||||
if (ud) {
|
||||
ud->pendingMessage = message;
|
||||
lws_callback_on_writable(pair.second);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SSLServer::broadcastToRoom(const std::string& roomName, const std::string& message) {
|
||||
for (auto &room: _rooms) {
|
||||
if (room->name() == roomName) {
|
||||
// This would need to be implemented in ChatRoom to work with WebSockets
|
||||
// For now, just a placeholder
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SSLServer::createRooms() {
|
||||
// Load rooms from database or config
|
||||
// This is a simplified version - would need to be expanded
|
||||
Json::Value roomList = _config->group("rooms");
|
||||
|
||||
if (roomList.isArray()) {
|
||||
for (const auto& room : roomList) {
|
||||
// Create room objects
|
||||
// This would need the actual ChatRoom constructor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> SSLServer::roomList() {
|
||||
std::vector<std::string> list;
|
||||
for (const auto &room: _rooms) {
|
||||
list.push_back(room->name());
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
bool SSLServer::roomAllowed(const std::string& roomName, const std::string& userName, const std::string& password) {
|
||||
for (auto &room: _rooms) {
|
||||
if (room->name() == roomName && room->accessAllowed(userName, password)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SSLServer::changeRoom(std::shared_ptr<ChatUser> user, const std::string& newRoom, const std::string& password) {
|
||||
if (!roomAllowed(newRoom, user->name(), password)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Implementation would go here
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SSLServer::userExists(const std::string& userName) {
|
||||
for (const auto &room: _rooms) {
|
||||
if (room->userNameExists(userName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void SSLServer::initUser(const std::string& token, const std::string& name, const std::string& room, const std::string& color, const std::string& password) {
|
||||
// Implementation would go here
|
||||
}
|
||||
|
||||
} // namespace Lib
|
||||
} // namespace Yc
|
||||
100
src/core/ssl_server.h
Normal file
100
src/core/ssl_server.h
Normal file
@@ -0,0 +1,100 @@
|
||||
#ifndef YC_LIB_SSL_SERVER_H
|
||||
#define YC_LIB_SSL_SERVER_H
|
||||
|
||||
#include <libwebsockets.h>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
#include <shared_mutex>
|
||||
#include <unordered_map>
|
||||
#include <queue>
|
||||
#include <condition_variable>
|
||||
#include <vector>
|
||||
|
||||
namespace Yc {
|
||||
namespace Lib {
|
||||
|
||||
class Config;
|
||||
class Database;
|
||||
class ChatRoom;
|
||||
class ChatUser;
|
||||
|
||||
struct WebSocketUserData {
|
||||
std::string token;
|
||||
std::string userName;
|
||||
std::string userColor;
|
||||
std::string currentRoom;
|
||||
bool authenticated = false;
|
||||
std::string pendingMessage;
|
||||
};
|
||||
|
||||
class SSLServer {
|
||||
public:
|
||||
SSLServer(std::shared_ptr<Config> config, std::shared_ptr<Database> database);
|
||||
~SSLServer();
|
||||
|
||||
void run();
|
||||
void stop();
|
||||
void createRooms();
|
||||
|
||||
// Room management
|
||||
std::vector<std::string> roomList();
|
||||
bool roomAllowed(const std::string& roomName, const std::string& userName, const std::string& password);
|
||||
bool changeRoom(std::shared_ptr<ChatUser> user, const std::string& newRoom, const std::string& password);
|
||||
|
||||
// User management
|
||||
bool userExists(const std::string& userName);
|
||||
void initUser(const std::string& token, const std::string& name, const std::string& room, const std::string& color, const std::string& password);
|
||||
|
||||
// Message handling
|
||||
void sendMessage(int socket, const std::string& message);
|
||||
void broadcastToRoom(const std::string& roomName, const std::string& message);
|
||||
|
||||
// WebSocket callbacks
|
||||
static int wsCallback(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len);
|
||||
|
||||
private:
|
||||
void startServer();
|
||||
void processMessageQueue();
|
||||
void handleWebSocketMessage(struct lws *wsi, const std::string& message);
|
||||
void addConnection(const std::string& token, struct lws *wsi);
|
||||
void removeConnection(const std::string& token);
|
||||
std::shared_ptr<ChatUser> getUserByToken(const std::string& token);
|
||||
|
||||
std::shared_ptr<Config> _config;
|
||||
std::shared_ptr<Database> _database;
|
||||
std::vector<std::shared_ptr<ChatRoom>> _rooms;
|
||||
|
||||
// SSL/TLS settings
|
||||
bool _useSSL;
|
||||
std::string _certPath;
|
||||
std::string _keyPath;
|
||||
int _port;
|
||||
|
||||
// Server state
|
||||
std::atomic<bool> _running{false};
|
||||
struct lws_context* _context = nullptr;
|
||||
std::thread _serverThread;
|
||||
std::thread _messageThread;
|
||||
|
||||
// Message queue
|
||||
std::mutex _queueMutex;
|
||||
std::condition_variable _queueCV;
|
||||
std::queue<std::string> _messageQueue;
|
||||
|
||||
// Connections
|
||||
std::shared_mutex _connectionsMutex;
|
||||
std::unordered_map<std::string, struct lws*> _connections;
|
||||
std::unordered_map<std::string, std::shared_ptr<ChatUser>> _users;
|
||||
|
||||
// Static instance for callbacks
|
||||
static SSLServer* _instance;
|
||||
static struct lws_protocols _protocols[];
|
||||
};
|
||||
|
||||
} // namespace Lib
|
||||
} // namespace Yc
|
||||
|
||||
#endif // YC_LIB_SSL_SERVER_H
|
||||
13
src/main.cpp
13
src/main.cpp
@@ -2,13 +2,26 @@
|
||||
#include "core/config.h"
|
||||
#include "core/server.h"
|
||||
#include "lib/database.h"
|
||||
#include <iostream>
|
||||
|
||||
// main function
|
||||
int main(int, char **) {
|
||||
auto config = std::make_shared<Yc::Lib::Config>();
|
||||
auto database = std::make_shared<Yc::Lib::Database>(config);
|
||||
|
||||
// Check if SSL is enabled (for future implementation)
|
||||
bool sslEnabled = config->value("server", "ssl_enabled").asBool();
|
||||
|
||||
if (sslEnabled) {
|
||||
std::cout << "[YourChat] SSL/TLS support is configured but not yet implemented" << std::endl;
|
||||
std::cout << "[YourChat] Starting without SSL/TLS support" << std::endl;
|
||||
} else {
|
||||
std::cout << "[YourChat] Starting without SSL/TLS support" << std::endl;
|
||||
}
|
||||
|
||||
auto server = std::make_shared<Yc::Lib::Server>(config, database);
|
||||
server->createRooms(config->group("rooms"));
|
||||
server->run();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user