mirror of
https://github.com/CalcProgrammer1/OpenRGB.git
synced 2025-12-23 23:37:48 -05:00
1179 lines
43 KiB
C++
1179 lines
43 KiB
C++
/*---------------------------------------------------------*\
|
|
| NetworkServer.cpp |
|
|
| |
|
|
| OpenRGB SDK network server |
|
|
| |
|
|
| Adam Honse (CalcProgrammer1) 09 May 2020 |
|
|
| |
|
|
| This file is part of the OpenRGB project |
|
|
| SPDX-License-Identifier: GPL-2.0-only |
|
|
\*---------------------------------------------------------*/
|
|
|
|
#include <cstring>
|
|
#include "NetworkServer.h"
|
|
#include "LogManager.h"
|
|
|
|
#ifndef WIN32
|
|
#include <sys/ioctl.h>
|
|
#include <netinet/tcp.h>
|
|
#include <sys/types.h>
|
|
#include <arpa/inet.h>
|
|
#else
|
|
#include <ws2tcpip.h>
|
|
#endif
|
|
#include <memory.h>
|
|
#include <errno.h>
|
|
#include <stdlib.h>
|
|
#include <iostream>
|
|
|
|
const char yes = 1;
|
|
|
|
#ifdef WIN32
|
|
#include <Windows.h>
|
|
#else
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
using namespace std::chrono_literals;
|
|
|
|
NetworkClientInfo::NetworkClientInfo()
|
|
{
|
|
client_string = "Client";
|
|
client_ip = OPENRGB_SDK_HOST;
|
|
client_sock = INVALID_SOCKET;
|
|
client_listen_thread = nullptr;
|
|
client_protocol_version = 0;
|
|
}
|
|
|
|
NetworkClientInfo::~NetworkClientInfo()
|
|
{
|
|
if (client_sock != INVALID_SOCKET) {
|
|
LOG_INFO("[NetworkServer] Closing server connection: %s", client_ip.c_str());
|
|
delete client_listen_thread;
|
|
shutdown(client_sock, SD_RECEIVE);
|
|
closesocket(client_sock);
|
|
}
|
|
}
|
|
|
|
NetworkServer::NetworkServer(std::vector<RGBController *> &control)
|
|
: controllers(control)
|
|
{
|
|
host = OPENRGB_SDK_HOST;
|
|
port_num = OPENRGB_SDK_PORT;
|
|
server_online = false;
|
|
server_listening = false;
|
|
legacy_workaround_enabled = false;
|
|
|
|
for (int i = 0; i < MAXSOCK; i++) {
|
|
ConnectionThread[i] = nullptr;
|
|
}
|
|
|
|
profile_manager = nullptr;
|
|
}
|
|
|
|
NetworkServer::~NetworkServer()
|
|
{
|
|
StopServer();
|
|
}
|
|
|
|
void NetworkServer::ClientInfoChanged()
|
|
{
|
|
ClientInfoChangeMutex.lock();
|
|
|
|
/*---------------------------------------------------------*\
|
|
| Client info has changed, call the callbacks |
|
|
\*---------------------------------------------------------*/
|
|
for (unsigned int callback_idx = 0; callback_idx < ClientInfoChangeCallbacks.size();
|
|
callback_idx++) {
|
|
ClientInfoChangeCallbacks[callback_idx](ClientInfoChangeCallbackArgs[callback_idx]);
|
|
}
|
|
|
|
ClientInfoChangeMutex.unlock();
|
|
}
|
|
|
|
void NetworkServer::DeviceListChanged()
|
|
{
|
|
/*---------------------------------------------------------*\
|
|
| Indicate to the clients that the controller list has |
|
|
| changed |
|
|
\*---------------------------------------------------------*/
|
|
for (unsigned int client_idx = 0; client_idx < ServerClients.size(); client_idx++) {
|
|
SendRequest_DeviceListChanged(ServerClients[client_idx]->client_sock);
|
|
}
|
|
}
|
|
|
|
void NetworkServer::ServerListeningChanged()
|
|
{
|
|
ServerListeningChangeMutex.lock();
|
|
|
|
/*---------------------------------------------------------*\
|
|
| Server state has changed, call the callbacks |
|
|
\*---------------------------------------------------------*/
|
|
for (unsigned int callback_idx = 0; callback_idx < ServerListeningChangeCallbacks.size();
|
|
callback_idx++) {
|
|
ServerListeningChangeCallbacks[callback_idx](
|
|
ServerListeningChangeCallbackArgs[callback_idx]);
|
|
}
|
|
|
|
ServerListeningChangeMutex.unlock();
|
|
}
|
|
|
|
std::string NetworkServer::GetHost()
|
|
{
|
|
return host;
|
|
}
|
|
|
|
unsigned short NetworkServer::GetPort()
|
|
{
|
|
return port_num;
|
|
}
|
|
|
|
bool NetworkServer::GetOnline()
|
|
{
|
|
return server_online;
|
|
}
|
|
|
|
bool NetworkServer::GetListening()
|
|
{
|
|
return server_listening;
|
|
}
|
|
|
|
unsigned int NetworkServer::GetNumClients()
|
|
{
|
|
return (unsigned int) ServerClients.size();
|
|
}
|
|
|
|
const char *NetworkServer::GetClientString(unsigned int client_num)
|
|
{
|
|
const char *result;
|
|
|
|
ServerClientsMutex.lock();
|
|
|
|
if (client_num < ServerClients.size()) {
|
|
result = ServerClients[client_num]->client_string.c_str();
|
|
} else {
|
|
result = "";
|
|
}
|
|
|
|
ServerClientsMutex.unlock();
|
|
|
|
return result;
|
|
}
|
|
|
|
const char *NetworkServer::GetClientIP(unsigned int client_num)
|
|
{
|
|
const char *result;
|
|
|
|
ServerClientsMutex.lock();
|
|
|
|
if (client_num < ServerClients.size()) {
|
|
result = ServerClients[client_num]->client_ip.c_str();
|
|
} else {
|
|
result = "";
|
|
}
|
|
|
|
ServerClientsMutex.unlock();
|
|
|
|
return result;
|
|
}
|
|
|
|
unsigned int NetworkServer::GetClientProtocolVersion(unsigned int client_num)
|
|
{
|
|
unsigned int result;
|
|
|
|
ServerClientsMutex.lock();
|
|
|
|
if (client_num < ServerClients.size()) {
|
|
result = ServerClients[client_num]->client_protocol_version;
|
|
} else {
|
|
result = 0;
|
|
}
|
|
|
|
ServerClientsMutex.unlock();
|
|
|
|
return result;
|
|
}
|
|
|
|
void NetworkServer::RegisterClientInfoChangeCallback(NetServerCallback new_callback,
|
|
void *new_callback_arg)
|
|
{
|
|
ClientInfoChangeCallbacks.push_back(new_callback);
|
|
ClientInfoChangeCallbackArgs.push_back(new_callback_arg);
|
|
}
|
|
|
|
void NetworkServer::RegisterServerListeningChangeCallback(NetServerCallback new_callback,
|
|
void *new_callback_arg)
|
|
{
|
|
ServerListeningChangeCallbacks.push_back(new_callback);
|
|
ServerListeningChangeCallbackArgs.push_back(new_callback_arg);
|
|
}
|
|
|
|
void NetworkServer::SetHost(std::string new_host)
|
|
{
|
|
if (server_online == false) {
|
|
host = new_host;
|
|
}
|
|
}
|
|
|
|
void NetworkServer::SetLegacyWorkaroundEnable(bool enable)
|
|
{
|
|
legacy_workaround_enabled = enable;
|
|
}
|
|
|
|
void NetworkServer::SetPort(unsigned short new_port)
|
|
{
|
|
if (server_online == false) {
|
|
port_num = new_port;
|
|
}
|
|
}
|
|
|
|
void NetworkServer::StartServer()
|
|
{
|
|
int err;
|
|
struct addrinfo hints, *res, *result;
|
|
|
|
/*---------------------------------------------------------*\
|
|
| Start a TCP server and launch threads |
|
|
\*---------------------------------------------------------*/
|
|
char port_str[6];
|
|
snprintf(port_str, 6, "%d", port_num);
|
|
|
|
socket_count = 0;
|
|
|
|
/*---------------------------------------------------------*\
|
|
| Windows requires WSAStartup before using sockets |
|
|
\*---------------------------------------------------------*/
|
|
#ifdef WIN32
|
|
if (WSAStartup(MAKEWORD(2, 2), &wsa) != NO_ERROR) {
|
|
WSACleanup();
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
memset(&hints, 0, sizeof(hints));
|
|
hints.ai_family = AF_UNSPEC;
|
|
hints.ai_socktype = SOCK_STREAM;
|
|
hints.ai_flags = AI_PASSIVE;
|
|
err = getaddrinfo(host.c_str(), port_str, &hints, &result);
|
|
|
|
if (err) {
|
|
LOG_ERROR("[NetworkServer] Unable to get address.");
|
|
WSACleanup();
|
|
return;
|
|
}
|
|
|
|
/*---------------------------------------------------------*\
|
|
| Create a server socket for each address returned. |
|
|
\*---------------------------------------------------------*/
|
|
for (res = result; res && socket_count < MAXSOCK; res = res->ai_next) {
|
|
server_sock[socket_count] = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
|
|
|
|
if (server_sock[socket_count] == INVALID_SOCKET) {
|
|
LOG_ERROR("[NetworkServer] Network socket could not be created.");
|
|
WSACleanup();
|
|
return;
|
|
}
|
|
|
|
/*---------------------------------------------------------*\
|
|
| Bind the server socket |
|
|
\*---------------------------------------------------------*/
|
|
if (bind(server_sock[socket_count], res->ai_addr, res->ai_addrlen) == SOCKET_ERROR) {
|
|
if (errno == EADDRINUSE) {
|
|
LOG_ERROR("[NetworkServer] Could not bind network socket. Is port %hu already "
|
|
"being used?",
|
|
GetPort());
|
|
} else if (errno == EACCES) {
|
|
LOG_ERROR(
|
|
"[NetworkServer] Could not bind network socket. Access to socket was denied.");
|
|
} else if (errno == EBADF) {
|
|
LOG_ERROR("[NetworkServer] Could not bind network socket. sockfd is not a valid "
|
|
"file descriptor.");
|
|
} else if (errno == EINVAL) {
|
|
LOG_ERROR("[NetworkServer] Could not bind network socket. The socket is already "
|
|
"bound to an address, or addrlen is wrong, or addr is not a valid "
|
|
"address for this socket's domain.");
|
|
} else if (errno == ENOTSOCK) {
|
|
LOG_ERROR("[NetworkServer] Could not bind network socket. The file descriptor "
|
|
"sockfd does not refer to a socket.");
|
|
} else {
|
|
/*---------------------------------------------------------*\
|
|
| errno could be a Linux specific error, see: |
|
|
| https://man7.org/linux/man-pages/man2/bind.2.html |
|
|
\*---------------------------------------------------------*/
|
|
LOG_ERROR("[NetworkServer] Could not bind network socket. Error code: %d.", errno);
|
|
}
|
|
|
|
WSACleanup();
|
|
return;
|
|
}
|
|
|
|
/*---------------------------------------------------------*\
|
|
| Set socket options - no delay |
|
|
\*---------------------------------------------------------*/
|
|
setsockopt(server_sock[socket_count], IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes));
|
|
|
|
socket_count += 1;
|
|
}
|
|
|
|
freeaddrinfo(result);
|
|
server_online = true;
|
|
|
|
/*---------------------------------------------------------*\
|
|
| Start the connection thread |
|
|
\*---------------------------------------------------------*/
|
|
for (int curr_socket = 0; curr_socket < socket_count; curr_socket++) {
|
|
ConnectionThread[curr_socket] = new std::thread(&NetworkServer::ConnectionThreadFunction,
|
|
this,
|
|
curr_socket);
|
|
ConnectionThread[curr_socket]->detach();
|
|
}
|
|
}
|
|
|
|
void NetworkServer::StopServer()
|
|
{
|
|
int curr_socket;
|
|
server_online = false;
|
|
|
|
ServerClientsMutex.lock();
|
|
|
|
for (unsigned int client_idx = 0; client_idx < ServerClients.size(); client_idx++) {
|
|
delete ServerClients[client_idx];
|
|
}
|
|
|
|
ServerClients.clear();
|
|
|
|
for (curr_socket = 0; curr_socket < socket_count; curr_socket++) {
|
|
shutdown(server_sock[curr_socket], SD_RECEIVE);
|
|
closesocket(server_sock[curr_socket]);
|
|
}
|
|
|
|
ServerClientsMutex.unlock();
|
|
|
|
for (curr_socket = 0; curr_socket < socket_count; curr_socket++) {
|
|
if (ConnectionThread[curr_socket]) {
|
|
delete ConnectionThread[curr_socket];
|
|
ConnectionThread[curr_socket] = nullptr;
|
|
}
|
|
}
|
|
|
|
socket_count = 0;
|
|
|
|
/*---------------------------------------------------------*\
|
|
| Client info has changed, call the callbacks |
|
|
\*---------------------------------------------------------*/
|
|
ClientInfoChanged();
|
|
}
|
|
|
|
void NetworkServer::ConnectionThreadFunction(int socket_idx)
|
|
{
|
|
/*---------------------------------------------------------*\
|
|
| This thread handles client connections |
|
|
\*---------------------------------------------------------*/
|
|
LOG_INFO("[NetworkServer] Network connection thread started on port %hu", GetPort());
|
|
|
|
while (server_online == true) {
|
|
/*---------------------------------------------------------*\
|
|
| Create new socket for client connection |
|
|
\*---------------------------------------------------------*/
|
|
NetworkClientInfo *client_info = new NetworkClientInfo();
|
|
|
|
/*---------------------------------------------------------*\
|
|
| Listen for incoming client connection on the server |
|
|
| socket. This call blocks until a connection is |
|
|
| established |
|
|
\*---------------------------------------------------------*/
|
|
if (listen(server_sock[socket_idx], 10) < 0) {
|
|
LOG_INFO("[NetworkServer] Connection thread closed");
|
|
server_online = false;
|
|
|
|
return;
|
|
}
|
|
|
|
server_listening = true;
|
|
ServerListeningChanged();
|
|
|
|
/*---------------------------------------------------------*\
|
|
| Accept the client connection |
|
|
\*---------------------------------------------------------*/
|
|
client_info->client_sock = accept_select((int) server_sock[socket_idx]);
|
|
|
|
if (client_info->client_sock < 0) {
|
|
LOG_INFO("[NetworkServer] Connection thread closed");
|
|
server_online = false;
|
|
|
|
server_listening = false;
|
|
ServerListeningChanged();
|
|
|
|
return;
|
|
}
|
|
|
|
/*---------------------------------------------------------*\
|
|
| Get the new client socket and store it in the clients |
|
|
| vector |
|
|
\*---------------------------------------------------------*/
|
|
u_long arg = 0;
|
|
ioctlsocket(client_info->client_sock, FIONBIO, &arg);
|
|
setsockopt(client_info->client_sock, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes));
|
|
|
|
/*---------------------------------------------------------*\
|
|
| Discover the remote hosts IP |
|
|
\*---------------------------------------------------------*/
|
|
struct sockaddr_storage tmp_addr;
|
|
char ipstr[INET6_ADDRSTRLEN];
|
|
socklen_t len;
|
|
len = sizeof(tmp_addr);
|
|
getpeername(client_info->client_sock, (struct sockaddr *) &tmp_addr, &len);
|
|
|
|
if (tmp_addr.ss_family == AF_INET) {
|
|
struct sockaddr_in *s_4 = (struct sockaddr_in *) &tmp_addr;
|
|
inet_ntop(AF_INET, &s_4->sin_addr, ipstr, sizeof(ipstr));
|
|
client_info->client_ip = ipstr;
|
|
} else {
|
|
struct sockaddr_in6 *s_6 = (struct sockaddr_in6 *) &tmp_addr;
|
|
inet_ntop(AF_INET6, &s_6->sin6_addr, ipstr, sizeof(ipstr));
|
|
client_info->client_ip = ipstr;
|
|
}
|
|
|
|
/*---------------------------------------------------------*\
|
|
| We need to lock before the thread could possibly finish |
|
|
\*---------------------------------------------------------*/
|
|
ServerClientsMutex.lock();
|
|
|
|
/*---------------------------------------------------------*\
|
|
| Start a listener thread for the new client socket |
|
|
\*---------------------------------------------------------*/
|
|
client_info->client_listen_thread = new std::thread(&NetworkServer::ListenThreadFunction,
|
|
this,
|
|
client_info);
|
|
client_info->client_listen_thread->detach();
|
|
|
|
ServerClients.push_back(client_info);
|
|
ServerClientsMutex.unlock();
|
|
|
|
/*---------------------------------------------------------*\
|
|
| Client info has changed, call the callbacks |
|
|
\*---------------------------------------------------------*/
|
|
ClientInfoChanged();
|
|
}
|
|
|
|
LOG_INFO("[NetworkServer] Connection thread closed");
|
|
server_online = false;
|
|
server_listening = false;
|
|
ServerListeningChanged();
|
|
}
|
|
|
|
int NetworkServer::accept_select(int sockfd)
|
|
{
|
|
fd_set set;
|
|
struct timeval timeout;
|
|
|
|
while (1) {
|
|
timeout.tv_sec = TCP_TIMEOUT_SECONDS;
|
|
timeout.tv_usec = 0;
|
|
|
|
FD_ZERO(&set);
|
|
FD_SET(sockfd, &set);
|
|
|
|
int rv = select(sockfd + 1, &set, NULL, NULL, &timeout);
|
|
|
|
if (rv == SOCKET_ERROR || server_online == false) {
|
|
return -1;
|
|
} else if (rv == 0) {
|
|
continue;
|
|
} else {
|
|
return (accept((int) sockfd, NULL, NULL));
|
|
}
|
|
}
|
|
}
|
|
|
|
int NetworkServer::recv_select(SOCKET s, char *buf, int len, int flags)
|
|
{
|
|
fd_set set;
|
|
struct timeval timeout;
|
|
|
|
while (1) {
|
|
timeout.tv_sec = TCP_TIMEOUT_SECONDS;
|
|
timeout.tv_usec = 0;
|
|
|
|
FD_ZERO(&set);
|
|
FD_SET(s, &set);
|
|
|
|
int rv = select((int) s + 1, &set, NULL, NULL, &timeout);
|
|
|
|
if (rv == SOCKET_ERROR || server_online == false) {
|
|
return 0;
|
|
} else if (rv == 0) {
|
|
continue;
|
|
} else {
|
|
return (recv(s, buf, len, flags));
|
|
}
|
|
}
|
|
}
|
|
|
|
void NetworkServer::ListenThreadFunction(NetworkClientInfo *client_info)
|
|
{
|
|
SOCKET client_sock = client_info->client_sock;
|
|
|
|
LOG_INFO("[NetworkServer] Network server started");
|
|
|
|
/*---------------------------------------------------------*\
|
|
| This thread handles messages received from clients |
|
|
\*---------------------------------------------------------*/
|
|
while (server_online == true) {
|
|
NetPacketHeader header;
|
|
int bytes_read = 0;
|
|
char *data = NULL;
|
|
|
|
for (unsigned int i = 0; i < 4; i++) {
|
|
/*---------------------------------------------------------*\
|
|
| Read byte of magic |
|
|
\*---------------------------------------------------------*/
|
|
bytes_read = recv_select(client_sock, &header.pkt_magic[i], 1, 0);
|
|
|
|
if (bytes_read <= 0) {
|
|
LOG_ERROR("[NetworkServer] recv_select failed receiving magic, closing listener");
|
|
goto listen_done;
|
|
}
|
|
|
|
/*---------------------------------------------------------*\
|
|
| Test characters of magic "ORGB" |
|
|
\*---------------------------------------------------------*/
|
|
if (header.pkt_magic[i] != openrgb_sdk_magic[i]) {
|
|
LOG_ERROR("[NetworkServer] Invalid magic received");
|
|
continue;
|
|
}
|
|
}
|
|
|
|
/*---------------------------------------------------------*\
|
|
| If we get to this point, the magic is correct. Read the |
|
|
| rest of the header |
|
|
\*---------------------------------------------------------*/
|
|
bytes_read = 0;
|
|
do {
|
|
int tmp_bytes_read = 0;
|
|
|
|
tmp_bytes_read = recv_select(client_sock,
|
|
(char *) &header.pkt_dev_idx + bytes_read,
|
|
sizeof(header) - sizeof(header.pkt_magic) - bytes_read,
|
|
0);
|
|
|
|
bytes_read += tmp_bytes_read;
|
|
|
|
if (tmp_bytes_read <= 0) {
|
|
LOG_ERROR("[NetworkServer] recv_select failed receiving header, closing listener");
|
|
goto listen_done;
|
|
}
|
|
|
|
} while (bytes_read != sizeof(header) - sizeof(header.pkt_magic));
|
|
|
|
/*---------------------------------------------------------*\
|
|
| Header received, now receive the data |
|
|
\*---------------------------------------------------------*/
|
|
bytes_read = 0;
|
|
if (header.pkt_size > 0) {
|
|
data = new char[header.pkt_size];
|
|
|
|
do {
|
|
int tmp_bytes_read = 0;
|
|
|
|
tmp_bytes_read = recv_select(client_sock,
|
|
&data[(unsigned int) bytes_read],
|
|
header.pkt_size - bytes_read,
|
|
0);
|
|
|
|
if (tmp_bytes_read <= 0) {
|
|
LOG_ERROR(
|
|
"[NetworkServer] recv_select failed receiving data, closing listener");
|
|
goto listen_done;
|
|
}
|
|
bytes_read += tmp_bytes_read;
|
|
|
|
} while ((unsigned int) bytes_read < header.pkt_size);
|
|
}
|
|
|
|
/*---------------------------------------------------------*\
|
|
| Entire request received, select functionality based on |
|
|
| request ID |
|
|
\*---------------------------------------------------------*/
|
|
switch (header.pkt_id) {
|
|
case NET_PACKET_ID_REQUEST_CONTROLLER_COUNT:
|
|
SendReply_ControllerCount(client_sock);
|
|
break;
|
|
|
|
case NET_PACKET_ID_REQUEST_CONTROLLER_DATA: {
|
|
unsigned int protocol_version = 0;
|
|
|
|
if (header.pkt_size == sizeof(unsigned int)) {
|
|
memcpy(&protocol_version, data, sizeof(unsigned int));
|
|
}
|
|
|
|
SendReply_ControllerData(client_sock, header.pkt_dev_idx, protocol_version);
|
|
} break;
|
|
|
|
case NET_PACKET_ID_REQUEST_PROTOCOL_VERSION:
|
|
SendReply_ProtocolVersion(client_sock);
|
|
ProcessRequest_ClientProtocolVersion(client_sock, header.pkt_size, data);
|
|
break;
|
|
|
|
case NET_PACKET_ID_SET_CLIENT_NAME:
|
|
if (data == NULL) {
|
|
break;
|
|
}
|
|
|
|
ProcessRequest_ClientString(client_sock, header.pkt_size, data);
|
|
break;
|
|
|
|
case NET_PACKET_ID_RGBCONTROLLER_RESIZEZONE:
|
|
if (data == NULL) {
|
|
break;
|
|
}
|
|
|
|
if ((header.pkt_dev_idx < controllers.size())
|
|
&& (header.pkt_size == (2 * sizeof(int)))) {
|
|
int zone;
|
|
int new_size;
|
|
|
|
memcpy(&zone, data, sizeof(int));
|
|
memcpy(&new_size, data + sizeof(int), sizeof(int));
|
|
|
|
controllers[header.pkt_dev_idx]->ResizeZone(zone, new_size);
|
|
profile_manager->SaveProfile("sizes", true);
|
|
}
|
|
break;
|
|
|
|
case NET_PACKET_ID_RGBCONTROLLER_UPDATELEDS:
|
|
if (data == NULL) {
|
|
break;
|
|
}
|
|
|
|
/*---------------------------------------------------------*\
|
|
| Verify the color description size (first 4 bytes of data) |
|
|
| matches the packet size in the header |
|
|
| |
|
|
| If protocol version is 4 or below and the legacy SDK |
|
|
| compatibility workaround is enabled, ignore this check. |
|
|
| This allows backwards compatibility with old versions of |
|
|
| SDK applications that didn't properly implement the size |
|
|
| field. |
|
|
\*---------------------------------------------------------*/
|
|
if ((header.pkt_size == *((unsigned int *) data))
|
|
|| ((client_info->client_protocol_version <= 4) && (legacy_workaround_enabled))) {
|
|
if (header.pkt_dev_idx < controllers.size()) {
|
|
controllers[header.pkt_dev_idx]->SetColorDescription((unsigned char *) data);
|
|
controllers[header.pkt_dev_idx]->UpdateLEDs();
|
|
}
|
|
} else {
|
|
LOG_ERROR("[NetworkServer] UpdateLEDs packet has invalid size. Packet size: %d, "
|
|
"Data size: %d",
|
|
header.pkt_size,
|
|
*((unsigned int *) data));
|
|
goto listen_done;
|
|
}
|
|
break;
|
|
|
|
case NET_PACKET_ID_RGBCONTROLLER_UPDATEZONELEDS:
|
|
if (data == NULL) {
|
|
break;
|
|
}
|
|
|
|
/*---------------------------------------------------------*\
|
|
| Verify the color description size (first 4 bytes of data) |
|
|
| matches the packet size in the header |
|
|
| |
|
|
| If protocol version is 4 or below and the legacy SDK |
|
|
| compatibility workaround is enabled, ignore this check. |
|
|
| This allows backwards compatibility with old versions of |
|
|
| SDK applications that didn't properly implement the size |
|
|
| field. |
|
|
\*---------------------------------------------------------*/
|
|
if ((header.pkt_size == *((unsigned int *) data))
|
|
|| ((client_info->client_protocol_version <= 4) && (legacy_workaround_enabled))) {
|
|
if (header.pkt_dev_idx < controllers.size()) {
|
|
int zone;
|
|
|
|
memcpy(&zone, &data[sizeof(unsigned int)], sizeof(int));
|
|
|
|
controllers[header.pkt_dev_idx]->SetZoneColorDescription((unsigned char *) data);
|
|
controllers[header.pkt_dev_idx]->UpdateZoneLEDs(zone);
|
|
}
|
|
} else {
|
|
LOG_ERROR("[NetworkServer] UpdateZoneLEDs packet has invalid size. Packet size: "
|
|
"%d, Data size: %d",
|
|
header.pkt_size,
|
|
*((unsigned int *) data));
|
|
goto listen_done;
|
|
}
|
|
break;
|
|
|
|
case NET_PACKET_ID_RGBCONTROLLER_UPDATESINGLELED:
|
|
if (data == NULL) {
|
|
break;
|
|
}
|
|
|
|
/*---------------------------------------------------------*\
|
|
| Verify the single LED color description size (8 bytes) |
|
|
| matches the packet size in the header |
|
|
\*---------------------------------------------------------*/
|
|
if (header.pkt_size == (sizeof(int) + sizeof(RGBColor))) {
|
|
if (header.pkt_dev_idx < controllers.size()) {
|
|
int led;
|
|
|
|
memcpy(&led, data, sizeof(int));
|
|
|
|
controllers[header.pkt_dev_idx]->SetSingleLEDColorDescription(
|
|
(unsigned char *) data);
|
|
controllers[header.pkt_dev_idx]->UpdateSingleLED(led);
|
|
}
|
|
} else {
|
|
LOG_ERROR("[NetworkServer] UpdateSingleLED packet has invalid size. Packet size: "
|
|
"%d, Data size: %d",
|
|
header.pkt_size,
|
|
(sizeof(int) + sizeof(RGBColor)));
|
|
goto listen_done;
|
|
}
|
|
break;
|
|
|
|
case NET_PACKET_ID_RGBCONTROLLER_SETCUSTOMMODE:
|
|
if (header.pkt_dev_idx < controllers.size()) {
|
|
controllers[header.pkt_dev_idx]->SetCustomMode();
|
|
}
|
|
break;
|
|
|
|
case NET_PACKET_ID_RGBCONTROLLER_UPDATEMODE:
|
|
if (data == NULL) {
|
|
break;
|
|
}
|
|
|
|
/*---------------------------------------------------------*\
|
|
| Verify the mode description size (first 4 bytes of data) |
|
|
| matches the packet size in the header |
|
|
| |
|
|
| If protocol version is 4 or below and the legacy SDK |
|
|
| compatibility workaround is enabled, ignore this check. |
|
|
| This allows backwards compatibility with old versions of |
|
|
| SDK applications that didn't properly implement the size |
|
|
| field. |
|
|
\*---------------------------------------------------------*/
|
|
if ((header.pkt_size == *((unsigned int *) data))
|
|
|| ((client_info->client_protocol_version <= 4) && (legacy_workaround_enabled))) {
|
|
if (header.pkt_dev_idx < controllers.size()) {
|
|
controllers[header.pkt_dev_idx]
|
|
->SetModeDescription((unsigned char *) data,
|
|
client_info->client_protocol_version);
|
|
controllers[header.pkt_dev_idx]->UpdateMode();
|
|
}
|
|
} else {
|
|
LOG_ERROR("[NetworkServer] UpdateMode packet has invalid size. Packet size: %d, "
|
|
"Data size: %d",
|
|
header.pkt_size,
|
|
*((unsigned int *) data));
|
|
goto listen_done;
|
|
}
|
|
break;
|
|
|
|
case NET_PACKET_ID_RGBCONTROLLER_SAVEMODE:
|
|
if (data == NULL) {
|
|
break;
|
|
}
|
|
|
|
/*---------------------------------------------------------*\
|
|
| Verify the mode description size (first 4 bytes of data) |
|
|
| matches the packet size in the header |
|
|
| |
|
|
| If protocol version is 4 or below and the legacy SDK |
|
|
| compatibility workaround is enabled, ignore this check. |
|
|
| This allows backwards compatibility with old versions of |
|
|
| SDK applications that didn't properly implement the size |
|
|
| field. |
|
|
\*---------------------------------------------------------*/
|
|
if ((header.pkt_size == *((unsigned int *) data))
|
|
|| ((client_info->client_protocol_version <= 4) && (legacy_workaround_enabled))) {
|
|
if (header.pkt_dev_idx < controllers.size()) {
|
|
controllers[header.pkt_dev_idx]
|
|
->SetModeDescription((unsigned char *) data,
|
|
client_info->client_protocol_version);
|
|
controllers[header.pkt_dev_idx]->SaveMode();
|
|
}
|
|
}
|
|
break;
|
|
|
|
case NET_PACKET_ID_REQUEST_PROFILE_LIST:
|
|
SendReply_ProfileList(client_sock);
|
|
break;
|
|
|
|
case NET_PACKET_ID_REQUEST_SAVE_PROFILE:
|
|
if (data == NULL) {
|
|
break;
|
|
}
|
|
|
|
if (profile_manager) {
|
|
std::string profile_name;
|
|
profile_name.assign(data, header.pkt_size);
|
|
|
|
profile_manager->SaveProfile(profile_name);
|
|
}
|
|
|
|
break;
|
|
|
|
case NET_PACKET_ID_REQUEST_LOAD_PROFILE:
|
|
if (data == NULL) {
|
|
break;
|
|
}
|
|
|
|
if (profile_manager) {
|
|
std::string profile_name;
|
|
profile_name.assign(data, header.pkt_size);
|
|
|
|
profile_manager->LoadProfile(profile_name);
|
|
}
|
|
|
|
for (RGBController *controller : controllers) {
|
|
controller->UpdateLEDs();
|
|
}
|
|
|
|
break;
|
|
|
|
case NET_PACKET_ID_REQUEST_DELETE_PROFILE:
|
|
if (data == NULL) {
|
|
break;
|
|
}
|
|
|
|
if (profile_manager) {
|
|
std::string profile_name;
|
|
profile_name.assign(data, header.pkt_size);
|
|
|
|
profile_manager->DeleteProfile(profile_name);
|
|
}
|
|
|
|
break;
|
|
|
|
case NET_PACKET_ID_REQUEST_PLUGIN_LIST:
|
|
SendReply_PluginList(client_sock);
|
|
break;
|
|
|
|
case NET_PACKET_ID_PLUGIN_SPECIFIC: {
|
|
unsigned int plugin_pkt_type = *((unsigned int *) (data));
|
|
unsigned int plugin_pkt_size = header.pkt_size - (sizeof(unsigned int));
|
|
unsigned char *plugin_data = (unsigned char *) (data + sizeof(unsigned int));
|
|
|
|
if (header.pkt_dev_idx < plugins.size()) {
|
|
NetworkPlugin plugin = plugins[header.pkt_dev_idx];
|
|
unsigned char *output = plugin.callback(plugin.callback_arg,
|
|
plugin_pkt_type,
|
|
plugin_data,
|
|
&plugin_pkt_size);
|
|
if (output != nullptr) {
|
|
SendReply_PluginSpecific(client_sock, plugin_pkt_type, output, plugin_pkt_size);
|
|
}
|
|
}
|
|
break;
|
|
} break;
|
|
|
|
case NET_PACKET_ID_RGBCONTROLLER_CLEARSEGMENTS:
|
|
if (data == NULL) {
|
|
break;
|
|
}
|
|
|
|
if ((header.pkt_dev_idx < controllers.size()) && (header.pkt_size == sizeof(int))) {
|
|
int zone;
|
|
|
|
memcpy(&zone, data, sizeof(int));
|
|
|
|
controllers[header.pkt_dev_idx]->ClearSegments(zone);
|
|
profile_manager->SaveProfile("sizes", true);
|
|
}
|
|
break;
|
|
|
|
case NET_PACKET_ID_RGBCONTROLLER_ADDSEGMENT: {
|
|
/*---------------------------------------------------------*\
|
|
| Verify the segment description size (first 4 bytes of |
|
|
| data) matches the packet size in the header |
|
|
\*---------------------------------------------------------*/
|
|
if (header.pkt_size == *((unsigned int *) data)) {
|
|
if (header.pkt_dev_idx < controllers.size()) {
|
|
controllers[header.pkt_dev_idx]->SetSegmentDescription((unsigned char *) data);
|
|
profile_manager->SaveProfile("sizes", true);
|
|
}
|
|
}
|
|
} break;
|
|
}
|
|
|
|
delete[] data;
|
|
}
|
|
|
|
listen_done:
|
|
|
|
ServerClientsMutex.lock();
|
|
|
|
for (unsigned int this_idx = 0; this_idx < ServerClients.size(); this_idx++) {
|
|
if (ServerClients[this_idx] == client_info) {
|
|
delete client_info;
|
|
ServerClients.erase(ServerClients.begin() + this_idx);
|
|
break;
|
|
}
|
|
}
|
|
|
|
client_info = nullptr;
|
|
|
|
ServerClientsMutex.unlock();
|
|
|
|
/*---------------------------------------------------------*\
|
|
| Client info has changed, call the callbacks |
|
|
\*---------------------------------------------------------*/
|
|
ClientInfoChanged();
|
|
}
|
|
|
|
void NetworkServer::ProcessRequest_ClientProtocolVersion(SOCKET client_sock,
|
|
unsigned int data_size,
|
|
char *data)
|
|
{
|
|
unsigned int protocol_version = 0;
|
|
|
|
if (data_size == sizeof(unsigned int) && (data != NULL)) {
|
|
memcpy(&protocol_version, data, sizeof(unsigned int));
|
|
}
|
|
|
|
if (protocol_version > OPENRGB_SDK_PROTOCOL_VERSION) {
|
|
protocol_version = OPENRGB_SDK_PROTOCOL_VERSION;
|
|
}
|
|
|
|
ServerClientsMutex.lock();
|
|
for (unsigned int this_idx = 0; this_idx < ServerClients.size(); this_idx++) {
|
|
if (ServerClients[this_idx]->client_sock == client_sock) {
|
|
ServerClients[this_idx]->client_protocol_version = protocol_version;
|
|
break;
|
|
}
|
|
}
|
|
ServerClientsMutex.unlock();
|
|
|
|
/*---------------------------------------------------------*\
|
|
| Client info has changed, call the callbacks |
|
|
\*---------------------------------------------------------*/
|
|
ClientInfoChanged();
|
|
}
|
|
|
|
void NetworkServer::ProcessRequest_ClientString(SOCKET client_sock,
|
|
unsigned int data_size,
|
|
char *data)
|
|
{
|
|
ServerClientsMutex.lock();
|
|
for (unsigned int this_idx = 0; this_idx < ServerClients.size(); this_idx++) {
|
|
if (ServerClients[this_idx]->client_sock == client_sock) {
|
|
ServerClients[this_idx]->client_string.assign(data, data_size);
|
|
break;
|
|
}
|
|
}
|
|
ServerClientsMutex.unlock();
|
|
|
|
/*---------------------------------------------------------*\
|
|
| Client info has changed, call the callbacks |
|
|
\*---------------------------------------------------------*/
|
|
ClientInfoChanged();
|
|
}
|
|
|
|
void NetworkServer::SendReply_ControllerCount(SOCKET client_sock)
|
|
{
|
|
NetPacketHeader reply_hdr;
|
|
unsigned int reply_data;
|
|
|
|
InitNetPacketHeader(&reply_hdr, 0, NET_PACKET_ID_REQUEST_CONTROLLER_COUNT, sizeof(unsigned int));
|
|
|
|
reply_data = (unsigned int) controllers.size();
|
|
|
|
send(client_sock, (const char *) &reply_hdr, sizeof(NetPacketHeader), 0);
|
|
send(client_sock, (const char *) &reply_data, sizeof(unsigned int), 0);
|
|
}
|
|
|
|
void NetworkServer::SendReply_ControllerData(SOCKET client_sock,
|
|
unsigned int dev_idx,
|
|
unsigned int protocol_version)
|
|
{
|
|
if (dev_idx < controllers.size()) {
|
|
NetPacketHeader reply_hdr;
|
|
unsigned char *reply_data = controllers[dev_idx]->GetDeviceDescription(protocol_version);
|
|
unsigned int reply_size;
|
|
|
|
memcpy(&reply_size, reply_data, sizeof(reply_size));
|
|
|
|
InitNetPacketHeader(&reply_hdr, dev_idx, NET_PACKET_ID_REQUEST_CONTROLLER_DATA, reply_size);
|
|
|
|
send(client_sock, (const char *) &reply_hdr, sizeof(NetPacketHeader), 0);
|
|
send(client_sock, (const char *) reply_data, reply_size, 0);
|
|
|
|
delete[] reply_data;
|
|
}
|
|
}
|
|
|
|
void NetworkServer::SendReply_ProtocolVersion(SOCKET client_sock)
|
|
{
|
|
NetPacketHeader reply_hdr;
|
|
unsigned int reply_data;
|
|
|
|
InitNetPacketHeader(&reply_hdr, 0, NET_PACKET_ID_REQUEST_PROTOCOL_VERSION, sizeof(unsigned int));
|
|
|
|
reply_data = OPENRGB_SDK_PROTOCOL_VERSION;
|
|
|
|
send(client_sock, (const char *) &reply_hdr, sizeof(NetPacketHeader), 0);
|
|
send(client_sock, (const char *) &reply_data, sizeof(unsigned int), 0);
|
|
}
|
|
|
|
void NetworkServer::SendRequest_DeviceListChanged(SOCKET client_sock)
|
|
{
|
|
NetPacketHeader pkt_hdr;
|
|
|
|
InitNetPacketHeader(&pkt_hdr, 0, NET_PACKET_ID_DEVICE_LIST_UPDATED, 0);
|
|
|
|
send(client_sock, (char *) &pkt_hdr, sizeof(NetPacketHeader), 0);
|
|
}
|
|
|
|
void NetworkServer::SendReply_ProfileList(SOCKET client_sock)
|
|
{
|
|
if (!profile_manager) {
|
|
return;
|
|
}
|
|
|
|
NetPacketHeader reply_hdr;
|
|
unsigned char *reply_data = profile_manager->GetProfileListDescription();
|
|
unsigned int reply_size;
|
|
|
|
memcpy(&reply_size, reply_data, sizeof(reply_size));
|
|
|
|
InitNetPacketHeader(&reply_hdr, 0, NET_PACKET_ID_REQUEST_PROFILE_LIST, reply_size);
|
|
|
|
send(client_sock, (const char *) &reply_hdr, sizeof(NetPacketHeader), 0);
|
|
send(client_sock, (const char *) reply_data, reply_size, 0);
|
|
}
|
|
|
|
void NetworkServer::SendReply_PluginList(SOCKET client_sock)
|
|
{
|
|
unsigned int data_size = 0;
|
|
unsigned int data_ptr = 0;
|
|
|
|
/*---------------------------------------------------------*\
|
|
| Calculate data size |
|
|
\*---------------------------------------------------------*/
|
|
unsigned short num_plugins = (unsigned short) plugins.size();
|
|
|
|
data_size += sizeof(data_size);
|
|
data_size += sizeof(num_plugins);
|
|
|
|
for (unsigned int i = 0; i < num_plugins; i++) {
|
|
data_size += sizeof(unsigned short) * 3;
|
|
data_size += (unsigned int) strlen(plugins[i].name.c_str()) + 1;
|
|
data_size += (unsigned int) strlen(plugins[i].description.c_str()) + 1;
|
|
data_size += (unsigned int) strlen(plugins[i].version.c_str()) + 1;
|
|
data_size += sizeof(unsigned int) * 2;
|
|
}
|
|
|
|
/*---------------------------------------------------------*\
|
|
| Create data buffer |
|
|
\*---------------------------------------------------------*/
|
|
unsigned char *data_buf = new unsigned char[data_size];
|
|
|
|
/*---------------------------------------------------------*\
|
|
| Copy in data size |
|
|
\*---------------------------------------------------------*/
|
|
memcpy(&data_buf[data_ptr], &data_size, sizeof(data_size));
|
|
data_ptr += sizeof(data_size);
|
|
|
|
/*---------------------------------------------------------*\
|
|
| Copy in num_plugins |
|
|
\*---------------------------------------------------------*/
|
|
memcpy(&data_buf[data_ptr], &num_plugins, sizeof(num_plugins));
|
|
data_ptr += sizeof(num_plugins);
|
|
|
|
for (unsigned int i = 0; i < num_plugins; i++) {
|
|
/*---------------------------------------------------------*\
|
|
| Copy in plugin name (size+data) |
|
|
\*---------------------------------------------------------*/
|
|
unsigned short str_len = (unsigned short) strlen(plugins[i].name.c_str()) + 1;
|
|
|
|
memcpy(&data_buf[data_ptr], &str_len, sizeof(unsigned short));
|
|
data_ptr += sizeof(unsigned short);
|
|
|
|
strcpy((char *) &data_buf[data_ptr], plugins[i].name.c_str());
|
|
data_ptr += str_len;
|
|
|
|
/*---------------------------------------------------------*\
|
|
| Copy in plugin description (size+data) |
|
|
\*---------------------------------------------------------*/
|
|
str_len = (unsigned short) strlen(plugins[i].description.c_str()) + 1;
|
|
|
|
memcpy(&data_buf[data_ptr], &str_len, sizeof(unsigned short));
|
|
data_ptr += sizeof(unsigned short);
|
|
|
|
strcpy((char *) &data_buf[data_ptr], plugins[i].description.c_str());
|
|
data_ptr += str_len;
|
|
|
|
/*---------------------------------------------------------*\
|
|
| Copy in plugin version (size+data) |
|
|
\*---------------------------------------------------------*/
|
|
str_len = (unsigned short) strlen(plugins[i].version.c_str()) + 1;
|
|
|
|
memcpy(&data_buf[data_ptr], &str_len, sizeof(unsigned short));
|
|
data_ptr += sizeof(unsigned short);
|
|
|
|
strcpy((char *) &data_buf[data_ptr], plugins[i].version.c_str());
|
|
data_ptr += str_len;
|
|
|
|
/*---------------------------------------------------------*\
|
|
| Copy in plugin index (data) |
|
|
\*---------------------------------------------------------*/
|
|
memcpy(&data_buf[data_ptr], &i, sizeof(unsigned int));
|
|
data_ptr += sizeof(unsigned int);
|
|
|
|
/*---------------------------------------------------------*\
|
|
| Copy in plugin sdk version (data) |
|
|
\*---------------------------------------------------------*/
|
|
memcpy(&data_buf[data_ptr], &plugins[i].protocol_version, sizeof(unsigned int));
|
|
data_ptr += sizeof(unsigned int);
|
|
}
|
|
|
|
NetPacketHeader reply_hdr;
|
|
unsigned int reply_size;
|
|
|
|
memcpy(&reply_size, data_buf, sizeof(reply_size));
|
|
|
|
InitNetPacketHeader(&reply_hdr, 0, NET_PACKET_ID_REQUEST_PLUGIN_LIST, reply_size);
|
|
|
|
send(client_sock, (const char *) &reply_hdr, sizeof(NetPacketHeader), 0);
|
|
send(client_sock, (const char *) data_buf, reply_size, 0);
|
|
|
|
delete[] data_buf;
|
|
}
|
|
|
|
void NetworkServer::SendReply_PluginSpecific(SOCKET client_sock,
|
|
unsigned int pkt_type,
|
|
unsigned char *data,
|
|
unsigned int data_size)
|
|
{
|
|
NetPacketHeader reply_hdr;
|
|
|
|
InitNetPacketHeader(&reply_hdr, 0, NET_PACKET_ID_PLUGIN_SPECIFIC, data_size + sizeof(pkt_type));
|
|
|
|
send(client_sock, (const char *) &reply_hdr, sizeof(NetPacketHeader), 0);
|
|
send(client_sock, (const char *) &pkt_type, sizeof(pkt_type), 0);
|
|
send(client_sock, (const char *) data, data_size, 0);
|
|
delete[] data;
|
|
}
|
|
|
|
void NetworkServer::SetProfileManager(ProfileManagerInterface *profile_manager_pointer)
|
|
{
|
|
profile_manager = profile_manager_pointer;
|
|
}
|
|
|
|
void NetworkServer::RegisterPlugin(NetworkPlugin plugin)
|
|
{
|
|
plugins.push_back(plugin);
|
|
}
|
|
|
|
void NetworkServer::UnregisterPlugin(std::string plugin_name)
|
|
{
|
|
for (std::vector<NetworkPlugin>::iterator it = plugins.begin(); it != plugins.end(); it++) {
|
|
if (it->name == plugin_name) {
|
|
plugins.erase(it);
|
|
break;
|
|
}
|
|
}
|
|
}
|