/*---------------------------------------------------------*\ | 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-or-later | \*---------------------------------------------------------*/ #include #include "NetworkServer.h" #include "LogManager.h" #ifndef WIN32 #include #include #include #include #else #include #endif #include #include #include #include const char yes = 1; #ifdef WIN32 #include #else #include #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& 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_REQUEST_RESCAN_DEVICES: ProcessRequest_RescanDevices(); 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::ProcessRequest_RescanDevices() { ResourceManager::get()->DetectDevices(); } 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_in_progress.lock(); send(client_sock, (const char *)&reply_hdr, sizeof(NetPacketHeader), 0); send(client_sock, (const char *)&reply_data, sizeof(unsigned int), 0); send_in_progress.unlock(); } 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_in_progress.lock(); send(client_sock, (const char *)&reply_hdr, sizeof(NetPacketHeader), 0); send(client_sock, (const char *)reply_data, reply_size, 0); send_in_progress.unlock(); 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_in_progress.lock(); send(client_sock, (const char *)&reply_hdr, sizeof(NetPacketHeader), 0); send(client_sock, (const char *)&reply_data, sizeof(unsigned int), 0); send_in_progress.unlock(); } void NetworkServer::SendRequest_DeviceListChanged(SOCKET client_sock) { NetPacketHeader pkt_hdr; InitNetPacketHeader(&pkt_hdr, 0, NET_PACKET_ID_DEVICE_LIST_UPDATED, 0); send_in_progress.lock(); send(client_sock, (char *)&pkt_hdr, sizeof(NetPacketHeader), 0); send_in_progress.unlock(); } 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_in_progress.lock(); send(client_sock, (const char *)&reply_hdr, sizeof(NetPacketHeader), 0); send(client_sock, (const char *)reply_data, reply_size, 0); send_in_progress.unlock(); } 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_in_progress.lock(); send(client_sock, (const char *)&reply_hdr, sizeof(NetPacketHeader), 0); send(client_sock, (const char *)data_buf, reply_size, 0); send_in_progress.unlock(); 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_in_progress.lock(); 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); send_in_progress.unlock(); 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::iterator it = plugins.begin(); it != plugins.end(); it++) { if(it->name == plugin_name) { plugins.erase(it); break; } } }