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/.vscode/settings.json b/.vscode/settings.json index 98a8b2f..95ddebc 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,68 +1,6 @@ { - "files.associations": { - "*.tcc": "cpp", - "optional": "cpp", - "future": "cpp", - "numeric": "cpp", - "cmath": "cpp", - "*.h++": "cpp", - "array": "cpp", - "atomic": "cpp", - "hash_map": "cpp", - "bit": "cpp", - "bitset": "cpp", - "cctype": "cpp", - "chrono": "cpp", - "clocale": "cpp", - "codecvt": "cpp", - "complex": "cpp", - "condition_variable": "cpp", - "csignal": "cpp", - "cstdarg": "cpp", - "cstddef": "cpp", - "cstdint": "cpp", - "cstdio": "cpp", - "cstdlib": "cpp", - "cstring": "cpp", - "ctime": "cpp", - "cwchar": "cpp", - "cwctype": "cpp", - "deque": "cpp", - "list": "cpp", - "map": "cpp", - "set": "cpp", - "unordered_map": "cpp", - "vector": "cpp", - "exception": "cpp", - "algorithm": "cpp", - "functional": "cpp", - "iterator": "cpp", - "memory": "cpp", - "memory_resource": "cpp", - "random": "cpp", - "ratio": "cpp", - "regex": "cpp", - "string": "cpp", - "string_view": "cpp", - "system_error": "cpp", - "tuple": "cpp", - "type_traits": "cpp", - "utility": "cpp", - "fstream": "cpp", - "initializer_list": "cpp", - "iomanip": "cpp", - "iosfwd": "cpp", - "iostream": "cpp", - "istream": "cpp", - "limits": "cpp", - "mutex": "cpp", - "new": "cpp", - "ostream": "cpp", - "shared_mutex": "cpp", - "sstream": "cpp", - "stdexcept": "cpp", - "streambuf": "cpp", - "thread": "cpp", - "typeinfo": "cpp" + "files.exclude": { + "*.d":true, + "*.o":true } } \ No newline at end of file diff --git a/GroupHandler.cpp b/GroupHandler.cpp index 38c803c..121fc94 100644 --- a/GroupHandler.cpp +++ b/GroupHandler.cpp @@ -974,8 +974,6 @@ void CGroupHandler::updateReflectorInfo() info.resize(20, '_'); CUtils::ReplaceChar(info, ' ', '_'); parms.push_back(info); - - m_irc->sendSGSInfo(subcommand, parms); } void CGroupHandler::logUser(LOGUSER lu, const std::string channel, const std::string user) @@ -988,7 +986,6 @@ void CGroupHandler::logUser(LOGUSER lu, const std::string channel, const std::st std::vector parms; parms.push_back(chn); parms.push_back(usr); - m_irc->sendSGSInfo(cmd, parms); } void CGroupHandler::sendToRepeaters(CHeaderData& header) const diff --git a/IRCDDB.h b/IRCDDB.h index 83d5742..129ea66 100644 --- a/IRCDDB.h +++ b/IRCDDB.h @@ -111,9 +111,6 @@ public: // Send query for a user, a false return implies a network error virtual bool findUser(const std::string& userCallsign) = 0; - // Support for the Smart Group Server - virtual void sendSGSInfo(const std::string subcommand, const std::vector parms) = 0; - // The following functions are for processing received messages // Get the waiting message type diff --git a/IRCDDBApp.cpp b/IRCDDBApp.cpp index 96064a2..2ce7ec2 100644 --- a/IRCDDBApp.cpp +++ b/IRCDDBApp.cpp @@ -550,22 +550,6 @@ bool IRCDDBApp::findRepeater(const std::string& rptrCall) return true; } -void IRCDDBApp::sendSGSInfo(const std::string &subcommand, const std::vector &pars) -{ - IRCMessageQueue *q = getSendQ(); - std::string srv(d->currentServer); - if (srv.size() && d->state>=6 && q) { - std::string command("SGS "); - command.append(subcommand); - for (auto it=pars.begin(); it!=pars.end(); it++) { - command.push_back(' '); - command.append(*it); - } - IRCMessage *m = new IRCMessage(srv, command); - q->putMessage(m); - } -} - bool IRCDDBApp::sendHeard(const std::string& myCall, const std::string& myCallExt, const std::string& yourCall, const std::string& rpt1, const std::string& rpt2, unsigned char flag1, unsigned char flag2, unsigned char flag3, const std::string& destination, const std::string& tx_msg, const std::string& tx_stats) { diff --git a/IRCDDBApp.h b/IRCDDBApp.h index 8fc77cf..3368fb1 100644 --- a/IRCDDBApp.h +++ b/IRCDDBApp.h @@ -70,8 +70,6 @@ public: bool sendHeard(const std::string& myCall, const std::string& myCallExt, const std::string& yourCall, const std::string& rpt1, const std::string& rpt2, unsigned char flag1, unsigned char flag2, unsigned char flag3, const std::string& destination, const std::string& tx_msg, const std::string& tx_stats); - void sendSGSInfo(const std::string &subcommand, const std::vector &pars); - int getConnectionState(); void rptrQRG(const std::string& callsign, double txFrequency, double duplexShift, double range, double agl); diff --git a/IRCDDBClient.cpp b/IRCDDBClient.cpp index a278905..bf216e3 100644 --- a/IRCDDBClient.cpp +++ b/IRCDDBClient.cpp @@ -30,9 +30,8 @@ struct CIRCDDBClientPrivate IRCDDBApp *app; }; -CIRCDDBClient::CIRCDDBClient(const std::string& hostName, unsigned int port, const std::string& callsign, const std::string& password, const std::string& versionInfo, const std::string& localAddr, bool isQuadNet ) : -d(new CIRCDDBClientPrivate), -m_isQuadNet(isQuadNet) +CIRCDDBClient::CIRCDDBClient(const std::string& hostName, unsigned int port, const std::string& callsign, const std::string& password, const std::string& versionInfo, const std::string& localAddr) : +d(new CIRCDDBClientPrivate) { std::string update_channel("#dstar"); d->app = new IRCDDBApp(update_channel); @@ -114,17 +113,6 @@ bool CIRCDDBClient::sendHeard( const std::string& myCall, const std::string& myC return d->app->sendHeard(myCall, myCallExt, yourCall, rpt1, rpt2, flag1, flag2, flag3, std::string(" "), std::string(""), std::string("")); } -void CIRCDDBClient::sendSGSInfo(const std::string subcommand, const std::vector parms) -{ - printf("CIRCDDBClient::sendSGSInfo subcommand %s parms", subcommand.c_str()); - for(unsigned int i=0; i < parms.size();i++) - printf(" %s", parms[i].c_str()); - printf("\n"); - - if(m_isQuadNet) { - d->app->sendSGSInfo(subcommand, parms); - } -} // Send heard data, a false return implies a network error bool CIRCDDBClient::sendHeardWithTXMsg(const std::string& myCall, const std::string& myCallExt, const std::string& yourCall, const std::string& rpt1, diff --git a/IRCDDBClient.h b/IRCDDBClient.h index 12666be..0e21eb8 100644 --- a/IRCDDBClient.h +++ b/IRCDDBClient.h @@ -33,7 +33,7 @@ struct CIRCDDBPrivate; class CIRCDDBClient : public CIRCDDB{ public: CIRCDDBClient(const std::string& hostName, unsigned int port, const std::string& callsign, const std::string& password, const std::string& versionInfo, - const std::string& localAddr = std::string(""), bool isQuadNet = false); + const std::string& localAddr = std::string("")); ~CIRCDDBClient(); // A false return implies a network error, or unable to log in @@ -106,9 +106,6 @@ public: // Send query for a user, a false return implies a network error bool findUser(const std::string& userCallsign); - // Support for the Smart Group Server - void sendSGSInfo(const std::string subcommand, const std::vector parms); - // The following functions are for processing received messages // Get the waiting message type @@ -132,7 +129,6 @@ public: private: struct CIRCDDBClientPrivate * const d; - bool m_isQuadNet; }; diff --git a/IRCDDBMultiClient.cpp b/IRCDDBMultiClient.cpp index 54856ae..69f299c 100644 --- a/IRCDDBMultiClient.cpp +++ b/IRCDDBMultiClient.cpp @@ -137,13 +137,6 @@ bool CIRCDDBMultiClient::sendHeardWithTXStats(const std::string & myCall, const return result; } -void CIRCDDBMultiClient::sendSGSInfo(const std::string subcommand, const std::vector parms) -{ - for (unsigned int i = 0; i < m_clients.size(); i++) { - m_clients[i]->sendSGSInfo(subcommand, parms); - } -} - bool CIRCDDBMultiClient::findGateway(const std::string & gatewayCallsign) { pushQuery(IDRT_GATEWAY, gatewayCallsign, new CIRCDDBMultiClientQuery("", "", gatewayCallsign, "", "", IDRT_GATEWAY)); diff --git a/IRCDDBMultiClient.h b/IRCDDBMultiClient.h index 7d7d99e..9c45f80 100644 --- a/IRCDDBMultiClient.h +++ b/IRCDDBMultiClient.h @@ -154,7 +154,6 @@ public: virtual bool receiveGateway(std::string & gatewayCallsign, std::string & address); virtual bool receiveUser(std::string & userCallsign, std::string & repeaterCallsign, std::string & gatewayCallsign, std::string & address); virtual bool receiveUser(std::string & userCallsign, std::string & repeaterCallsign, std::string & gatewayCallsign, std::string & address, std::string & timeStamp); - virtual void sendSGSInfo(const std::string subcommand, const std::vector parms); virtual void close(); // diff --git a/Makefile b/Makefile index b10b364..5011b83 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ 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)\" @@ -16,14 +16,17 @@ 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 + $(RM) GitVersion.h $(OBJS) $(DEPS) sgs-xl sgs-xl.crt sgs-xl.key -include $(DEPS) @@ -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 @@ -63,7 +68,6 @@ removehostfiles : rm -f $(DATADIR)/DExtra_Hosts.txt rm -f $(DATADIR)/DCS_Hosts.txt -.PHONY: GitVersion.h GitVersion.h: force ifneq ("$(wildcard .git/index)","") echo "const char *gitversion = \"$(shell git rev-parse HEAD)\";" > $@ @@ -72,4 +76,4 @@ else endif .PHONY: force -force: \ No newline at end of file +force: diff --git a/README.md b/README.md index d3d2234..9664adc 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. @@ -58,6 +58,11 @@ sudo make install && sudo journalctl -u sgs-xl.service -f This will allow you to view the smart-group-server log file while it's booting up. When you are satisfied it's running okay you can Control-C to end the journalctl session. To uninstall it, type `sudo make uninstall` and `sudo make removehostfiles`. This will stop the server and remove all files. You can then delete the build directory to remove every trace of the smart-group-server. ## Whatsnew +### v1.1 +#### 2021-03-03 +Removed specific SGS IRC messages +Is now compatible with N7TAE's remote https://github.com/n7tae/sgs-remote + ### v1.0 #### 2020-03-10 Groups can share same logoff. Thus users will be logged off at once from every group sharing the same logoff diff --git a/RemoteGroup.cpp b/RemoteGroup.cpp index 9eec349..d967077 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,7 +29,7 @@ m_linkStatus(linkStatus), m_userTimeout(userTimeout), m_users() { - if (m_logoff.compare(" ")) + if (m_logoff.compare(" ") == 0) m_logoff.clear(); } diff --git a/RemoteGroup.h b/RemoteGroup.h index 132488a..74f0902 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 @@ -28,8 +28,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/SGSXLApp.cpp b/SGSXLApp.cpp index 5729bd5..5bb95a0 100644 --- a/SGSXLApp.cpp +++ b/SGSXLApp.cpp @@ -109,10 +109,9 @@ bool CSGSXLApp::createThread() CIRCDDB_Array clients; for(unsigned int i=0; i < config.getIrcDDBCount(); i++) { std::string hostname, username, password; - bool isQuadNet; - config.getIrcDDB(i, hostname, username, password, isQuadNet); - std::cout << "ircDDB " << i + 1 << " set to " << hostname << " username set to " << username << " QuadNet " << isQuadNet << std::endl; - CIRCDDB *ircDDB = new CIRCDDBClient(hostname, 9007U, username, password, std::string("linux_SmartGroupServerXL-") + VERSION, address, isQuadNet); + config.getIrcDDB(i, hostname, username, password); + std::cout << "ircDDB " << i + 1 << " set to " << hostname << " username set to " << username << std::endl; + CIRCDDB *ircDDB = new CIRCDDBClient(hostname, 9007U, username, password, std::string("linux_SmartGroupServerXL-") + VERSION, address); clients.push_back(ircDDB); } diff --git a/SGSXLConfig.cpp b/SGSXLConfig.cpp index 3d0d9c0..ef902db 100644 --- a/SGSXLConfig.cpp +++ b/SGSXLConfig.cpp @@ -85,7 +85,6 @@ CSGSXLConfig::CSGSXLConfig(const std::string &pathname) continue; } - ircddb->isQuadNet = ircddb->hostname.find("openquad.net") != std::string::npos; this->m_ircDDB.push_back(ircddb); std::cout << "IRCDDB: host=" << ircddb->hostname << " user=" << ircddb->username << " password=" << ircddb->password << "\n"; } @@ -95,7 +94,6 @@ CSGSXLConfig::CSGSXLConfig(const std::string &pathname) ircddb->hostname = "rr.openquad.net"; ircddb->password = ""; ircddb->username = m_callsign; - ircddb->isQuadNet = true; this->m_ircDDB.push_back(ircddb); std::cout << "No ircDDB networks configure'd, defaulting to IRCDDB: host=" << ircddb->hostname << " user=" << ircddb->username << " password=" << ircddb->password << "\n"; } @@ -362,12 +360,11 @@ void CSGSXLConfig::getGateway(std::string& callsign, std::string& address) const address = m_address; } -void CSGSXLConfig::getIrcDDB(unsigned int ircddb, std::string& hostname, std::string& username, std::string& password, bool &isQuadNet) const +void CSGSXLConfig::getIrcDDB(unsigned int ircddb, std::string& hostname, std::string& username, std::string& password) const { hostname = m_ircDDB[ircddb]->hostname; username = m_ircDDB[ircddb]->username; password = m_ircDDB[ircddb]->password; - isQuadNet = m_ircDDB[ircddb]->isQuadNet; } void CSGSXLConfig::getGroup(unsigned int mod, std::string& band, std::string& callsign, std::string& logoff, std::string& info, std::string& permanent, unsigned int& userTimeout, CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, std::string& reflector) const diff --git a/SGSXLConfig.h b/SGSXLConfig.h index 9b401ea..246d9cb 100644 --- a/SGSXLConfig.h +++ b/SGSXLConfig.h @@ -40,7 +40,6 @@ struct SircDDB { std::string hostname; std::string username; std::string password; - bool isQuadNet; }; class CSGSXLConfig { @@ -50,7 +49,7 @@ public: void getGateway(std::string &callsign, std::string &address) const; - void getIrcDDB(unsigned int ircddb, std::string &hostname, std::string &username, std::string &password, bool &isQuadNet) const; + void getIrcDDB(unsigned int ircddb, std::string &hostname, std::string &username, std::string &password) const; void getGroup(unsigned int mod, std::string &band, std::string &callsign, std::string &logoff, std::string &info, std::string &permanent, unsigned int &userTimeout, CALLSIGN_SWITCH &callsignSwitch, bool &txMsgSwitch, std::string &reflector) const; 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; +}; diff --git a/Utils.h b/Utils.h index ffcea50..45d6076 100644 --- a/Utils.h +++ b/Utils.h @@ -17,8 +17,7 @@ #include #include #include -#include -#include +#include enum TRISTATE { STATE_FALSE,