From 74b8d30bdae03f27f2e7e8632a81d0e3316d8193 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Tue, 2 Mar 2021 20:37:52 +0100 Subject: [PATCH] Add Remote (credits N7TAE) --- .gitignore | 4 + Makefile | 15 ++- README.md | 2 +- RemoteGroup.cpp | 9 +- RemoteGroup.h | 5 +- RemoteHandler.cpp | 298 ++++++++++++++++++++++++++-------------------- RemoteHandler.h | 29 ++--- SGSXLThread.cpp | 7 +- TLSServer.cpp | 230 +++++++++++++++++++++++++++++++++++ TLSServer.h | 45 +++++++ 10 files changed, 480 insertions(+), 164 deletions(-) create mode 100644 TLSServer.cpp create mode 100644 TLSServer.h diff --git a/.gitignore b/.gitignore index c85b971..f602bff 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,7 @@ sgs-xl #Generated source files /GitVersion.h /sgs-xl.cfg + +#certs +sgs-xl.crt +sgs-xl.key diff --git a/Makefile b/Makefile index b10b364..9a9de02 100644 --- a/Makefile +++ b/Makefile @@ -7,20 +7,23 @@ CFGDIR=/usr/local/etc/sgs-xl/ DATADIR=/usr/local/sgs-xl/data/ # choose this if you want debugging help -# CPPFLAGS=-g -ggdb -W -Wall -std=c++17 -DCFG_DIR=\"$(CFGDIR)\" -DDATA_DIR=\"$(DATADIR)\" +CPPFLAGS=-g -ggdb -W -Wall -std=c++17 -DCFG_DIR=\"$(CFGDIR)\" -DDATA_DIR=\"$(DATADIR)\" # or, you can choose this for a much smaller executable without debugging help -CPPFLAGS= -W -Wall -O3 -std=c++17 -DCFG_DIR=\"$(CFGDIR)\" -DDATA_DIR=\"$(DATADIR)\" +#CPPFLAGS= -W -Wall -O3 -std=c++17 -DCFG_DIR=\"$(CFGDIR)\" -DDATA_DIR=\"$(DATADIR)\" SRCS = $(wildcard *.cpp) OBJS = $(SRCS:.cpp=.o) DEPS = $(SRCS:.cpp=.d) sgs-xl : GitVersion.h $(OBJS) - g++ $(CPPFLAGS) -o sgs-xl $(OBJS) -lconfig++ -pthread + g++ $(CPPFLAGS) -o sgs-xl $(OBJS) -lconfig++ -lssl -lcrypto -pthread %.o : %.cpp g++ $(CPPFLAGS) -MMD -MD -c $< -o $@ +sgs-xl.crt sgs-xl.key : + openssl req -new -newkey rsa:4096 -days 36500 -nodes -x509 -subj "/CN=Smart Group Server XL" -keyout sgs-xl.key -out sgs-xl.crt + .PHONY: clean clean: $(RM) GitVersion.h $(OBJS) $(DEPS) sgs-xl @@ -36,11 +39,13 @@ newhostfiles : wget -O $(DATADIR)/DCS_Hosts.txt http://www.pistar.uk/downloads/DCS_Hosts.txt .PHONY: install -install : newhostfiles sgs-xl +install : newhostfiles sgs-xl.key sgs-xl.crt sgs-xl mkdir -p $(CFGDIR) mkdir -p $(DATADIR) cp -rf data/* $(DATADIR) cp -f sgs-xl.cfg $(CFGDIR) + cp -f sgs-xl.crt $(DATADIR) + cp -f sgs-xl.key $(DATADIR) cp -f sgs-xl $(BINDIR) cp -f sgs-xl.service /lib/systemd/system sed -i "s|REPLACEME|$(BINDIR)sgs-xl $(CFGDIR)sgs-xl.cfg|g" /lib/systemd/system/sgs-xl.service @@ -72,4 +77,4 @@ else endif .PHONY: force -force: \ No newline at end of file +force: diff --git a/README.md b/README.md index d3d2234..f217c39 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ git clone git://github.com/F4FXL/smart-group-server-xl.git ``` Install the only needed development library: ``` -sudo apt-get install build-essential libconfig++-dev +sudo apt-get install build-essential libconfig++-dev openssl libssl-dev ``` Change to the smart-group-server directory and type `make`. This should make the executable, `sgs-xl` without errors or warnings. By default, you will have a group server that can link groups to X-Reflectors or DCS-Reflectors. Of course you can declare an unlinked channel by simply not defining a *reflector* parameter for that channel. diff --git a/RemoteGroup.cpp b/RemoteGroup.cpp index 9eec349..493aa55 100644 --- a/RemoteGroup.cpp +++ b/RemoteGroup.cpp @@ -1,6 +1,6 @@ /* * Copyright (C) 2011 by Jonathan Naylor G4KLX - * Copyright (c) 2017,2018 by Thomas A. Early N7TAE + * Copyright (c) 2017,2018, 2020 by Thomas A. Early N7TAE * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,8 +19,7 @@ #include "RemoteGroup.h" -CRemoteGroup::CRemoteGroup(const std::string& callsign, const std::string& logoff, const std::string &repeater, const std::string &infoText, - const std::string &linkReflector, LINK_STATUS linkStatus, unsigned int userTimeout) : +CRemoteGroup::CRemoteGroup(const std::string &callsign, const std::string &logoff, const std::string &repeater, const std::string &infoText, const std::string &linkReflector, LINK_STATUS linkStatus, unsigned int userTimeout) : m_callsign(callsign), m_logoff(logoff), m_repeater(repeater), @@ -30,8 +29,8 @@ m_linkStatus(linkStatus), m_userTimeout(userTimeout), m_users() { - if (m_logoff.compare(" ")) - m_logoff.clear(); + if (logoff.compare(" ")) + logoff.empty(); } CRemoteGroup::~CRemoteGroup() diff --git a/RemoteGroup.h b/RemoteGroup.h index bd6bfa0..23c8658 100644 --- a/RemoteGroup.h +++ b/RemoteGroup.h @@ -1,6 +1,6 @@ /* * Copyright (C) 2011 by Jonathan Naylor G4KLX - * Copyright (c) 2017,2018 by Thomas A. Early N7TAE + * Copyright (c) 2017,2018,2020 by Thomas A. Early N7TAE * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -27,8 +27,7 @@ class CRemoteGroup { public: - CRemoteGroup(const std::string& callsign, const std::string& logoff, const std::string &repeater, const std::string &infoText, const std::string &linkReflector, - LINK_STATUS linkStatus, unsigned int userTimeout); + CRemoteGroup(const std::string& callsign, const std::string& logoff, const std::string &repeater, const std::string &infoText, const std::string &linkReflector, LINK_STATUS linkStatus, unsigned int userTimeout); ~CRemoteGroup(); void addUser(const std::string& callsign, uint32_t timer, uint32_t timeout); diff --git a/RemoteHandler.cpp b/RemoteHandler.cpp index b62967e..f0fb28f 100644 --- a/RemoteHandler.cpp +++ b/RemoteHandler.cpp @@ -1,6 +1,6 @@ /* * Copyright (C) 2011,2012,2013 by Jonathan Naylor G4KLX - * Copyright (c) 2017-2018 by Thomas A. Early N7TAE + * Copyright (c) 2017-2018,2020 by Thomas A. Early N7TAE * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,165 +20,203 @@ #include #include #include +#include +#include +#include -#include "GroupHandler.h" #include "RemoteHandler.h" #include "DExtraHandler.h" #include "DStarDefines.h" #include "DCSHandler.h" #include "Utils.h" -CRemoteHandler::CRemoteHandler(const std::string &password, unsigned int port, const std::string &address) : -m_password(password), -m_handler(port, address), -m_random(0U) +bool CRemoteHandler::open(const std::string &password, const unsigned short port, const bool isIPV6) { - assert(port > 0U); - assert(password.size()); + return m_tlsserver.OpenSocket(password, isIPV6 ? "::" : "0.0.0.0", port); } -CRemoteHandler::~CRemoteHandler() +bool CRemoteHandler::process() { -} + std::string command; + if (m_tlsserver.GetCommand(command)) + return false; // nothing to do... + // parse the command into words + std::stringstream ss(command); + std::istream_iterator begin(ss); + std::istream_iterator end; + std::vector cwords(begin, end); + + + if (cwords.size() == 0) { + return false; + } -bool CRemoteHandler::open() -{ - return m_handler.open(); + if (0 == cwords[0].compare("halt")) { + printf("Received halt command from remote client, shutting down...\n"); + //auto groups = CGroupHandler::listGroups(); + //for (auto it=groups.begin(); it!=groups.end(); it++) { + // CGroupHandler *group = CGroupHandler::findGroup(*it); + // logoff(group, "ALL "); + //} + return true; + } + + if (cwords.size() < 2) { + fprintf(stderr, "Not enough words in the command: [%s]\n", command.c_str()); + return false; + } + + CUtils::ReplaceChar(cwords[1], '_', ' '); // this is the subscribe callsign + cwords[1].resize(8, ' '); + + CGroupHandler *group = CGroupHandler::findGroup(cwords[1]); + if (0 == cwords[0].compare("list")) { + sendGroup(group); + } else if (NULL == group) { + char emsg[128]; + snprintf(emsg, 128, "Smart Group [%s] not found", cwords[1].c_str()); + m_tlsserver.Write(emsg); + } else { + if (cwords.size() > 2 && 0 == cwords[0].compare("link")) { + CUtils::ReplaceChar(cwords[2], '_', ' '); + cwords[2].resize(8, ' '); + printf("Remote control user has linked \"%s\" to \"%s\"\n", cwords[1].c_str(), cwords[2].c_str()); + link(group, cwords[2]); + } + else if (cwords.size() > 1 && 0 == cwords[0].compare("unlink")) { + printf("Remote control user has unlinked \"%s\"\n", cwords[1].c_str()); + unlink(group); + } + else if (cwords.size() > 2 && 0 == cwords[0].compare("drop")) { + CUtils::ReplaceChar(cwords[2], '_', ' '); + cwords[2].resize(8, ' '); + printf("Remote control user has logged off \"%s\" from \"%s\"\n", cwords[2].c_str(), cwords[1].c_str()); + logoff(group, cwords[2]); + } + else { + printf("The command \"%s\" is bad\n", command.c_str()); + } + } + m_tlsserver.CloseClient(); + return false; } -void CRemoteHandler::process() + +void CRemoteHandler::sendGroup(CGroupHandler *group) { - RPH_TYPE type = m_handler.readType(); - switch (type) { - case RPHT_LOGOUT: - m_handler.setLoggedIn(false); - printf("Remote control user has logged out\n"); - break; - case RPHT_LOGIN: - m_random = (uint32_t)rand(); - m_handler.sendRandom(m_random); - break; - case RPHT_HASH: { - bool valid = m_handler.readHash(m_password, m_random); - if (valid) { - printf("Remote control user has logged in\n"); - m_handler.setLoggedIn(true); - m_handler.sendACK(); - } else { - printf("Remote control user has failed login authentication\n"); - m_handler.setLoggedIn(false); - m_handler.sendNAK("Invalid password"); - } - } - break; - case RPHT_SMARTGROUP: { - std::string callsign = m_handler.readGroup(); - sendGroup(callsign); - } - break; - case RPHT_LINK: { - std::string callsign, reflector; - m_handler.readLink(callsign, reflector); - printf("Remote control user has linked \"%s\" to \"%s\"\n", callsign.c_str(), reflector.c_str()); - link(callsign, reflector); + char msg[128]; + if (group) { + CRemoteGroup *data = group->getInfo(); + if (data) { + snprintf(msg, 128, "Subscribe = %s", data->getCallsign().c_str()); + m_tlsserver.Write(msg); + snprintf(msg, 128, "Unsubscribe = %s", data->getLogoff().c_str()); + m_tlsserver.Write(msg); + snprintf(msg, 128, "Module = %s", data->getRepeater().c_str()); + m_tlsserver.Write(msg); + snprintf(msg, 128, "Description = %s", data->getInfoText().c_str()); + m_tlsserver.Write(msg); + snprintf(msg, 128, "Reflector = %s", data->getReflector().c_str()); + m_tlsserver.Write(msg); + switch (data->getLinkStatus()) { + case LS_LINKING_DCS: + case LS_LINKING_DEXTRA: + m_tlsserver.Write("Link Status = Linking"); + break; + case LS_LINKED_DCS: + case LS_LINKED_DEXTRA: + m_tlsserver.Write("Link Status = Linked"); + break; + default: + m_tlsserver.Write("Link Status = Unlinked"); + break; } - break; - case RPHT_UNLINK: { - std::string callsign; - m_handler.readUnlink(callsign); - printf("Remote control user has unlinked \"%s\"\n", callsign.c_str()); - unlink(callsign); + snprintf(msg, 128, "User Timeout = %u min", data->getUserTimeout()); + m_tlsserver.Write(msg); + for (uint32_t i=0; igetUserCount(); i++) { + CRemoteUser *user = data->getUser(i); + snprintf(msg, 128, " User = %s, timer = %u min, timeout = %u min", user->getCallsign().c_str(), user->getTimer()/60U, user->getTimeout()/60U); + m_tlsserver.Write(msg); } - break; - case RPHT_LOGOFF: { - std::string callsign, user; - m_handler.readLogoff(callsign, user); - printf("Remote control user has logged off \"%s\" from \"%s\"\n", user.c_str(), callsign.c_str()); - logoff(callsign, user); + } + delete data; + } else { + // no group, so let's summarize all groups + auto groups = CGroupHandler::listGroups(); + m_tlsserver.Write("Logon Logoff Channel Description Status Reflector Timeout"); + for (auto it=groups.begin(); it!=groups.end(); it++) { + CGroupHandler *group = CGroupHandler::findGroup(*it); + if (group) { + CRemoteGroup *data = group->getInfo(); + if (data) { + std::string linkstat; + switch (data->getLinkStatus()) { + case LS_LINKING_DCS: + case LS_LINKING_DEXTRA: + linkstat.assign("Linking "); + break; + case LS_LINKED_DCS: + case LS_LINKED_DEXTRA: + linkstat.assign("Linked "); + break; + default: + linkstat.assign("Unlinked"); + break; + } + snprintf(msg, 128, "%s %s %s %s %s %8.8s %4u", data->getCallsign().c_str(), data->getLogoff().c_str(), data->getRepeater().c_str(), data->getInfoText().c_str(), linkstat.c_str(), data->getReflector().c_str(), data->getUserTimeout()); + m_tlsserver.Write(msg); + delete data; + } } - break; - default: - break; + } } } -void CRemoteHandler::close() -{ - m_handler.close(); -} - -void CRemoteHandler::sendGroup(const std::string &callsign) +void CRemoteHandler::link(CGroupHandler *group, const std::string &reflector) { - CGroupHandler *group = CGroupHandler::findGroup(callsign); - if (group == NULL) { - m_handler.sendNAK("Invalid Smart Group callsign"); - return; - } - + char msg[128]; CRemoteGroup *data = group->getInfo(); - if (data != NULL) - m_handler.sendGroup(*data); - - delete data; -} - -void CRemoteHandler::link(const std::string &callsign, const std::string &reflector) -{ - CGroupHandler *smartGroup = CGroupHandler::findGroup(callsign); - if (NULL == smartGroup) { - m_handler.sendNAK(std::string("Invalid Smart Group subscribe call ") + callsign); - return; - } - - if (smartGroup->remoteLink(reflector)) - m_handler.sendACK(); + if (group->remoteLink(reflector)) + snprintf(msg, 128, "Smart Group %s linked to %s", data->getCallsign().c_str(), reflector.c_str()); else - m_handler.sendNAK("link failed"); + snprintf(msg, 128, "Failed to link Smart Group %s to %s", data->getCallsign().c_str(), reflector.c_str()); + m_tlsserver.Write(msg); + delete data; } -void CRemoteHandler::unlink(const std::string &callsign) +void CRemoteHandler::unlink(CGroupHandler *group) { - CGroupHandler *smartGroup = CGroupHandler::findGroup(callsign); - if (NULL == smartGroup) { - m_handler.sendNAK(std::string("Invalid Smart Group subscribe call ") + callsign); - return; - } - - CRemoteGroup *data = smartGroup->getInfo(); - if (data) { - switch (smartGroup->getLinkType()) { - case LT_DEXTRA: - CDExtraHandler::unlink(smartGroup, data->getReflector(), false); - break; - case LT_DCS: - CDCSHandler::unlink(smartGroup, data->getReflector(), false); - break; - default: - delete data; - m_handler.sendNAK("alread unlinked"); - return; - } - delete data; - } else { - m_handler.sendNAK("could not get Smart Group info"); - return; + char msg[128]; + CRemoteGroup *data = group->getInfo(); + switch (group->getLinkType()) { + case LT_DEXTRA: + CDExtraHandler::unlink(group, data->getReflector(), false); + snprintf(msg, 128, "Smart Group %s unlinked from %s", data->getCallsign().c_str(), data->getReflector().c_str()); + break; + case LT_DCS: + CDCSHandler::unlink(group, data->getReflector(), false); + snprintf(msg, 128, "Smart Group %s unlinked from %s", data->getCallsign().c_str(), data->getReflector().c_str()); + break; + default: + snprintf(msg, 128, "Smart Group %s is already unlinked", data->getCallsign().c_str()); + m_tlsserver.Write(msg); + delete data; + return; } - smartGroup->setLinkType(LT_NONE); - smartGroup->clearReflector(); - m_handler.sendACK(); + delete data; + group->setLinkType(LT_NONE); + group->clearReflector(); + m_tlsserver.Write(msg); } -void CRemoteHandler::logoff(const std::string &callsign, const std::string &user) +void CRemoteHandler::logoff(CGroupHandler *group, const std::string &user) { - CGroupHandler *pGroup = CGroupHandler::findGroup(callsign); - if (pGroup == NULL) { - m_handler.sendNAK("Invalid Smart Group callsign"); - return; - } - - bool res = pGroup->logoff(user); - if (!res) - m_handler.sendNAK("Invalid Smart Group user callsign"); + char msg[128]; + CRemoteGroup *data = group->getInfo(); + if (group->logoff(user)) + snprintf(msg, 128, "Logging off %s from Smart Group %s", user.c_str(), data->getCallsign().c_str()); else - m_handler.sendACK(); + snprintf(msg, 128, "Could not logoff %s from Smart Group %s", user.c_str(), data->getCallsign().c_str()); + m_tlsserver.Write(msg); + delete data; } diff --git a/RemoteHandler.h b/RemoteHandler.h index b16a0bd..a478372 100644 --- a/RemoteHandler.h +++ b/RemoteHandler.h @@ -22,27 +22,24 @@ #include #include -#include "RemoteProtocolHandler.h" -#include "Timer.h" +#include "GroupHandler.h" +#include "TLSServer.h" class CRemoteHandler { public: - CRemoteHandler(const std::string &password, unsigned int port, const std::string &address = std::string("")); - ~CRemoteHandler(); + CRemoteHandler() {}; + ~CRemoteHandler() {}; - bool open(); + bool open(const std::string &password, const unsigned short port, const bool isIPV6); - void process(); - - void close(); + bool process(); private: - std::string m_password; - CRemoteProtocolHandler m_handler; - uint32_t m_random; - - void sendGroup(const std::string &callsign); - void link(const std::string &callsign, const std::string &reflector); - void unlink(const std::string &callsign); - void logoff(const std::string &callsign, const std::string &user); + std::string m_password; + CTLSServer m_tlsserver; + + void sendGroup(CGroupHandler *group); + void link(CGroupHandler *group, const std::string &reflector); + void unlink(CGroupHandler *group); + void logoff(CGroupHandler *group, const std::string &user); }; diff --git a/SGSXLThread.cpp b/SGSXLThread.cpp index ccdd97d..5d5b8a8 100644 --- a/SGSXLThread.cpp +++ b/SGSXLThread.cpp @@ -115,9 +115,9 @@ void CSGSXLThread::run() CGroupHandler::link(); if (m_remoteEnabled && m_remotePassword.size() && m_remotePort > 0U) { - m_remote = new CRemoteHandler(m_remotePassword, m_remotePort); - bool res = m_remote->open(); - if (!res) { + m_remote = new CRemoteHandler(); + bool res = m_remote->open(m_remotePassword, m_remotePort, false); + if (res) { delete m_remote; m_remote = NULL; } @@ -176,7 +176,6 @@ void CSGSXLThread::run() delete m_irc; if (m_remote != NULL) { - m_remote->close(); delete m_remote; } diff --git a/TLSServer.cpp b/TLSServer.cpp new file mode 100644 index 0000000..ee87f5a --- /dev/null +++ b/TLSServer.cpp @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2020 by Thomas A. Early N7TAE + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include + +#include + +#include "TLSServer.h" + +CTLSServer::~CTLSServer() +{ + CloseClient(); + if (m_sock >= 0) + close(m_sock); + if (m_ctx) + SSL_CTX_free(m_ctx); +} + +bool CTLSServer::CreateContext(const SSL_METHOD *method) +{ + m_ctx = SSL_CTX_new(method); + if (!m_ctx) { + fprintf(stderr, "Unable to create SSL context"); + ERR_print_errors_fp(stderr); + return true; + } + return false; +} + +// Server Class Definitions +#ifndef CFG_DIR +#define CFG_DIR "/usr/local/etc" +#endif + +bool CTLSServer::OpenSocket(const std::string &password, const std::string &address, unsigned short port) +{ + m_password.assign(password); + m_address.assign(address); + m_port = port; + + SSL_load_error_strings(); + OpenSSL_add_ssl_algorithms(); + + const SSL_METHOD *method = TLS_server_method(); + if (NULL == method) { + perror("Can't set SSL method"); + return true; + } + + if (CreateContext(method)) + return true; + + if (0 == SSL_CTX_set_min_proto_version(m_ctx, TLS1_2_VERSION)) { + perror("Can't sent minimum version"); + return true; + } + + std::string path(DATA_DIR); + std::string file(path+"/sgs-xl.crt"); + SSL_CTX_set_ecdh_auto(ctx, 1); + if (0 >= SSL_CTX_use_certificate_file(m_ctx, file.c_str(), SSL_FILETYPE_PEM)) { + ERR_print_errors_fp(stderr); + return true; + } + + file.assign(path+"/sgs-xl.key"); + if (0 >= SSL_CTX_use_PrivateKey_file(m_ctx, file.c_str(), SSL_FILETYPE_PEM)) { + ERR_print_errors_fp(stderr); + return true; + } + + if (CreateSocket()) + return true; + + return false; +} + +bool CTLSServer::CreateSocket() +{ + struct sockaddr_storage addr; + memset(&addr, 0, sizeof(struct sockaddr_storage)); + + int family; + if (m_address.npos != m_address.find(':')) { + struct sockaddr_in6 *a = (struct sockaddr_in6 *)&addr; + a->sin6_family = family = AF_INET6; + a->sin6_port = htons(m_port); + inet_pton(AF_INET6, m_address.c_str(), &(a->sin6_addr)); + } else if (m_address.npos != m_address.find('.')) { + struct sockaddr_in *a = (struct sockaddr_in *)&addr; + a->sin_family = family = AF_INET; + a->sin_port = htons(m_port); + inet_pton(AF_INET, m_address.c_str(), &(a->sin_addr)); + } else { + fprintf(stderr, "Improper addess [%s], remote socket creation failed!\n", m_address.c_str()); + return true; + } + + m_sock = socket(family, SOCK_STREAM, 0); + if (m_sock < 0) { + perror("Unable to create socket"); + return true; + } + + if (0 > bind(m_sock, (struct sockaddr*)&addr, sizeof(addr))) { + perror("Unable to bind"); + close(m_sock); + return true; + } + + if (0 > listen(m_sock, 1)) { + perror("Unable to listen"); + close(m_sock); + return true; + } + + return false; +} + +bool CTLSServer::GetCommand(std::string &command) +{ + struct sockaddr_storage addr; + uint len = sizeof(addr); + memset(&addr, 0, len); + + fd_set readfds; + FD_ZERO(&readfds); + FD_SET(m_sock, &readfds); + + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 0; + + // don't care about writefds and exceptfds: + // and we will return immediately + int ret = select(m_sock+1, &readfds, NULL, NULL, &tv); + + if (ret && FD_ISSET(m_sock, &readfds)) { + // there is someting to read + m_client = accept(m_sock, (struct sockaddr*)&addr, &len); + if (m_client < 0) { + perror("Remote is unable to accept"); + return true; + } + + if (AF_INET6 == addr.ss_family) { + struct sockaddr_in6 *a = (struct sockaddr_in6 *)&addr; + char s[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, &(a->sin6_addr), s, INET6_ADDRSTRLEN); + printf("Remote IPV6 client from %s\n", s); + } else { + struct sockaddr_in *a = (struct sockaddr_in *)&addr; + char s[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, &(a->sin_addr), s, INET_ADDRSTRLEN); + printf("Remote IPV4 client from %s\n", s); + } + + m_ssl = SSL_new(m_ctx); + if (NULL == m_ssl) { + CloseClient(); + perror("Remote can't create a new SSL"); + return true; + } else { + if (0 == SSL_set_fd(m_ssl, m_client)) { + CloseClient(); + perror("Remote can't set fd"); + return true; + } else { + if (SSL_accept(m_ssl) <= 0) { + CloseClient(); + ERR_print_errors_fp(stderr); + return true; + } else { + char buf[256] = { 0 }; + SSL_read(m_ssl, buf, 256); + if (m_password.compare(buf)) { + printf("Password [%s] from remote client failed.\n", buf); + SSL_write(m_ssl, "fail", 4); + CloseClient(); + return true; + } else { + SSL_write(m_ssl, "pass", 4); + char com[1024] = { 0 }; + SSL_read(m_ssl, com, 1024); + command.assign(com); + } + } + } + } + return false; + } else { + // nothing to read + command.clear(); + return true; + } + +} + +int CTLSServer::Write(const char *line) +{ + return SSL_write(m_ssl, line, strlen(line)); +} + +void CTLSServer::CloseClient() +{ + if (m_ssl) + SSL_free(m_ssl); + m_ssl = NULL; + if (m_client >= 0) + close(m_client); + m_client = 0; +} diff --git a/TLSServer.h b/TLSServer.h new file mode 100644 index 0000000..5953729 --- /dev/null +++ b/TLSServer.h @@ -0,0 +1,45 @@ +#pragma once + +/* + * Copyright (C) 2020 by Thomas A. Early N7TAE + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include + +class CTLSServer +{ +public: + CTLSServer() : m_sock(-1) , m_ctx(NULL) , m_ssl(NULL) , m_client(-1) {} + ~CTLSServer(); + virtual bool OpenSocket(const std::string &password, const std::string &address, unsigned short port); + bool GetCommand(std::string &command); + int Write(const char *line); + void CloseClient(); + +private: + bool CreateContext(const SSL_METHOD *method); + virtual bool CreateSocket(); + + int m_sock; + SSL_CTX *m_ctx; + SSL *m_ssl; + int m_client; + std::string m_address, m_password; + unsigned short m_port; +};