diff --git a/osal/windows/CMakeLists.txt b/osal/windows/CMakeLists.txt index a3a719e0a..889ea3396 100644 --- a/osal/windows/CMakeLists.txt +++ b/osal/windows/CMakeLists.txt @@ -1,6 +1,7 @@ target_sources(${LIB_FSFW_NAME} PRIVATE TcWinUdpPollingTask.cpp TmTcWinUdpBridge.cpp + TcWinTcpServer.cpp ) target_link_libraries(${LIB_FSFW_NAME} PRIVATE diff --git a/osal/windows/TcWinTcpServer.cpp b/osal/windows/TcWinTcpServer.cpp new file mode 100644 index 000000000..f85147f04 --- /dev/null +++ b/osal/windows/TcWinTcpServer.cpp @@ -0,0 +1,177 @@ +#include "TcWinTcpServer.h" +#include "../../serviceinterface/ServiceInterface.h" + +#include +#include + +const std::string TcWinTcpServer::DEFAULT_TCP_SERVER_PORT = "7301"; +const std::string TcWinTcpServer::DEFAULT_TCP_CLIENT_PORT = "7302"; + +TcWinTcpServer::TcWinTcpServer(object_id_t objectId, object_id_t tmtcUnixUdpBridge, + std::string customTcpServerPort): + SystemObject(objectId), tcpPort(customTcpServerPort) { + if(tcpPort == "") { + tcpPort = DEFAULT_TCP_SERVER_PORT; + } +} + +ReturnValue_t TcWinTcpServer::initialize() { + int retval = 0; + struct addrinfo *addrResult = nullptr; + struct addrinfo hints; + /* Initiates Winsock DLL. */ + WSAData wsaData; + WORD wVersionRequested = MAKEWORD(2, 2); + int err = WSAStartup(wVersionRequested, &wsaData); + if (err != 0) { + /* Tell the user that we could not find a usable Winsock DLL. */ +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::error << "TmTcWinUdpBridge::TmTcWinUdpBridge: WSAStartup failed with error: " << + err << std::endl; +#endif + return HasReturnvaluesIF::RETURN_FAILED; + } + + ZeroMemory(&hints, sizeof (hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + hints.ai_flags = AI_PASSIVE; + + retval = getaddrinfo(nullptr, tcpPort.c_str(), &hints, &addrResult); + if (retval != 0) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::warning << "TcWinTcpServer::TcWinTcpServer: Retrieving address info failed!" << + std::endl; +#endif + handleError(ErrorSources::GETADDRINFO_CALL); + return HasReturnvaluesIF::RETURN_FAILED; + } + + /* Open TCP (stream) socket */ + listenerTcpSocket = socket(addrResult->ai_family, addrResult->ai_socktype, + addrResult->ai_protocol); + if(listenerTcpSocket == INVALID_SOCKET) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::warning << "TcWinTcpServer::TcWinTcpServer: Socket creation failed!" << std::endl; +#endif + freeaddrinfo(addrResult); + handleError(ErrorSources::SOCKET_CALL); + return HasReturnvaluesIF::RETURN_FAILED; + } + +// retval = setsockopt(listenerTcpSocket, SOL_SOCKET, SO_REUSEADDR | SO_BROADCAST, +// reinterpret_cast(&tcpSockOpt), sizeof(tcpSockOpt)); +// if(retval != 0) { +// sif::warning << "TcWinTcpServer::TcWinTcpServer: Setting socket options failed!" << +// std::endl; +// handleError(ErrorSources::SETSOCKOPT_CALL); +// return HasReturnvaluesIF::RETURN_FAILED; +// } +// tcpAddress.sin_family = AF_INET; +// tcpAddress.sin_addr.s_addr = htonl(INADDR_ANY); + + retval = bind(listenerTcpSocket, reinterpret_cast(&tcpAddress), + tcpAddrLen); + if(retval == SOCKET_ERROR) { + sif::warning << "TcWinTcpServer::TcWinTcpServer: Binding socket failed!" << + std::endl; + freeaddrinfo(addrResult); + handleError(ErrorSources::BIND_CALL); + } + + freeaddrinfo(addrResult); + return HasReturnvaluesIF::RETURN_OK; +} + + +TcWinTcpServer::~TcWinTcpServer() { + closesocket(listenerTcpSocket); + WSACleanup(); +} + +ReturnValue_t TcWinTcpServer::performOperation(uint8_t opCode) { + /* If a connection is accepted, the corresponding socket will be assigned to the new socket */ + SOCKET clientSocket; + sockaddr_in clientSockAddr; + int connectorSockAddrLen = 0; + int retval = 0; + /* Listen for connection requests permanently for lifetime of program */ + while(true) { + retval = listen(listenerTcpSocket, currentBacklog); + if(retval == SOCKET_ERROR) { + handleError(ErrorSources::LISTEN_CALL); + continue; + } + + clientSocket = accept(listenerTcpSocket, reinterpret_cast(&clientSockAddr), + &connectorSockAddrLen); + + if(clientSocket == INVALID_SOCKET) { + handleError(ErrorSources::ACCEPT_CALL); + continue; + }; + + retval = recv(clientSocket, reinterpret_cast(receptionBuffer.data()), + receptionBuffer.size(), 0); + if(retval > 0) { +#if FSFW_TCP_SERVER_WIRETAPPING_ENABLED == 1 + sif::info << "TcWinTcpServer::performOperation: Received " << retval << " bytes." + std::endl; +#endif + } + else if(retval == 0) { + + } + else { + + } + + /* Done, shut down connection */ + retval = shutdown(clientSocket, SD_SEND); + } + return HasReturnvaluesIF::RETURN_OK; +} + +void TcWinTcpServer::handleError(ErrorSources errorSrc) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + int errCode = WSAGetLastError(); + std::string errorSrcString; + if(errorSrc == ErrorSources::SETSOCKOPT_CALL) { + errorSrcString = "setsockopt call"; + } + else if(errorSrc == ErrorSources::SOCKET_CALL) { + errorSrcString = "socket call"; + } + else if(errorSrc == ErrorSources::LISTEN_CALL) { + errorSrcString = "listen call"; + } + else if(errorSrc == ErrorSources::ACCEPT_CALL) { + errorSrcString = "accept call"; + } + else if(errorSrc == ErrorSources::GETADDRINFO_CALL) { + errorSrcString = "getaddrinfo call"; + } + + switch(errCode) { + case(WSANOTINITIALISED): { + sif::warning << "TmTcWinUdpBridge::handleError: " << errorSrcString << " | " + "WSANOTINITIALISED: WSAStartup call necessary" << std::endl; + break; + } + case(WSAEINVAL): { + sif::warning << "TmTcWinUdpBridge::handleError: " << errorSrcString << " | " + "WSAEINVAL: Invalid parameters" << std::endl; + break; + } + default: { + /* + https://docs.microsoft.com/en-us/windows/win32/winsock/ + windows-sockets-error-codes-2 + */ + sif::warning << "TmTcWinUdpBridge::handleSocketError: Error code: " << errCode << std::endl; + break; + } + } +#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */ +} diff --git a/osal/windows/TcWinTcpServer.h b/osal/windows/TcWinTcpServer.h new file mode 100644 index 000000000..f8aebc530 --- /dev/null +++ b/osal/windows/TcWinTcpServer.h @@ -0,0 +1,56 @@ +#ifndef FSFW_OSAL_WINDOWS_TCWINTCPSERVER_H_ +#define FSFW_OSAL_WINDOWS_TCWINTCPSERVER_H_ + +#include "../../objectmanager/SystemObject.h" +#include "../../tasks/ExecutableObjectIF.h" + +#include +#include + +//! Debugging preprocessor define. +#define FSFW_TCP_SERVER_WIRETAPPING_ENABLED 0 + +/** + * @brief Windows TCP server used to receive telecommands on a Windows Host + * @details + * Based on: https://docs.microsoft.com/en-us/windows/win32/winsock/complete-server-code + */ +class TcWinTcpServer: + public SystemObject, + public ExecutableObjectIF { +public: + /* The ports chosen here should not be used by any other process. */ + static const std::string DEFAULT_TCP_SERVER_PORT; + static const std::string DEFAULT_TCP_CLIENT_PORT; + + TcWinTcpServer(object_id_t objectId, object_id_t tmtcUnixUdpBridge, + std::string customTcpServerPort = ""); + virtual~ TcWinTcpServer(); + + ReturnValue_t initialize() override; + ReturnValue_t performOperation(uint8_t opCode) override; + +private: + + std::string tcpPort; + SOCKET listenerTcpSocket = 0; + struct sockaddr_in tcpAddress; + int tcpAddrLen = sizeof(tcpAddress); + int currentBacklog = 3; + + std::vector receptionBuffer; + int tcpSockOpt = 0; + + enum class ErrorSources { + GETADDRINFO_CALL, + SOCKET_CALL, + SETSOCKOPT_CALL, + BIND_CALL, + LISTEN_CALL, + ACCEPT_CALL + }; + + void handleError(ErrorSources errorSrc); +}; + +#endif /* FSFW_OSAL_WINDOWS_TCWINTCPSERVER_H_ */