diff --git a/config/xlxd.terminal b/config/xlxd.terminal new file mode 100644 index 0000000..e8b0cf5 --- /dev/null +++ b/config/xlxd.terminal @@ -0,0 +1,17 @@ +######################################################################################### +# XLXD terminal option file +# +# one line per entry +# each entry specifies a terminal option +# +# Valid option: +# address - Ip address to be used by the terminal route responder +# By default, the request destination address is used. +# If the system is behind a router, set it to the public IP +# If the system runs on the public IP, leave unset. +# modules - a string with all modules to accept a terminal connection +# Default value is "*", meaning accept all +# +######################################################################################### +#address 193.1.2.3 +#modules BCD diff --git a/src/cg3protocol.cpp b/src/cg3protocol.cpp index cb6da50..768df7d 100644 --- a/src/cg3protocol.cpp +++ b/src/cg3protocol.cpp @@ -24,6 +24,7 @@ #include "main.h" #include +#include #include "cg3client.h" #include "cg3protocol.h" #include "creflector.h" @@ -40,6 +41,8 @@ bool CG3Protocol::Init(void) { bool ok; + ReadOptions(); + // base class ok = CProtocol::Init(); @@ -260,6 +263,7 @@ void CG3Protocol::ConfigTask(void) } else { + std::cout << "Module " << Call << " invalid" << std::endl; Buffer.data()[3] = 0x01; // reject } } @@ -269,10 +273,19 @@ void CG3Protocol::ConfigTask(void) Buffer.data()[3] = 0x01; // reject } + char module = Call.GetModule(); + + if (!strchr(m_Modules.c_str(), module) && !strchr(m_Modules.c_str(), '*')) + { + // restricted + std::cout << "Module " << Call << " restricted by configuration" << std::endl; + Buffer.data()[3] = 0x01; // reject + } + // UR Buffer.resize(8); Buffer.Append((uint8 *)(const char *)Call, CALLSIGN_LEN - 1); - Buffer.Append((uint8)Call.GetModule()); + Buffer.Append((uint8)module); // RPT1 Buffer.Append((uint8 *)(const char *)GetReflectorCallsign(), CALLSIGN_LEN - 1); @@ -293,7 +306,16 @@ void CG3Protocol::ConfigTask(void) if (Buffer.data()[3] == 0x00) { - Buffer.Append(*(uint32 *)m_ConfigSocket.GetLocalAddr()); + std::cout << "External G3 gateway address " << inet_ntoa(*(in_addr *)&m_GwAddress) << std::endl; + + if (m_GwAddress == 0) + { + Buffer.Append(*(uint32 *)m_ConfigSocket.GetLocalAddr()); + } + else + { + Buffer.Append(m_GwAddress); + } } else { @@ -400,14 +422,14 @@ void CG3Protocol::Task(void) if ( (Frame = IsValidDvFramePacket(Buffer)) != NULL ) { //std::cout << "Terminal DV frame" << std::endl; - + // handle it OnDvFramePacketIn(Frame, BaseIp); } else if ( (Header = IsValidDvHeaderPacket(Buffer)) != NULL ) { //std::cout << "Terminal DV header" << std::endl; - + // callsign muted? if ( g_GateKeeper.MayTransmit(Header->GetMyCallsign(), Ip, PROTOCOL_G3, Header->GetRpt2Module()) ) { @@ -452,6 +474,9 @@ void CG3Protocol::Task(void) // update time m_LastKeepaliveTime.Now(); + + // reload option if needed + NeedReload(); } } @@ -566,7 +591,18 @@ bool CG3Protocol::OnDvHeaderPacketIn(CDvHeaderPacket *Header, const CIp &Ip) { if (client->GetReflectorModule() != Header->GetRpt2Callsign().GetModule()) { - client->SetReflectorModule(Header->GetRpt2Callsign().GetModule()); + char new_module = Header->GetRpt2Callsign().GetModule(); + if (strchr(m_Modules.c_str(), '*') || strchr(m_Modules.c_str(), new_module)) + { + client->SetReflectorModule(new_module); + } + else + { + // drop if invalid module + delete Header; + g_Reflector.ReleaseClients(); + return NULL; + } } // get client callsign @@ -724,3 +760,105 @@ bool CG3Protocol::EncodeDvLastFramePacket(const CDvLastFramePacket &Packet, CBuf return true; } +//////////////////////////////////////////////////////////////////////////////////////// +// option helpers + +char *CG3Protocol::TrimWhiteSpaces(char *str) +{ + char *end; + while ((*str == ' ') || (*str == '\t')) str++; + if (*str == 0) + return str; + end = str + strlen(str) - 1; + while ((end > str) && ((*end == ' ') || (*end == '\t') || (*end == '\r'))) end --; + *(end + 1) = 0; + return str; +} + + +bool CG3Protocol::NeedReload(void) +{ + struct stat fileStat; + + if (::stat(TERMINALOPTIONS_PATH, &fileStat) != -1) + { + if (m_LastModTime != fileStat.st_mtime) + { + ReadOptions(); + } + } + + // iterate on clients + CClients *clients = g_Reflector.GetClients(); + int index = -1; + CClient *client = NULL; + while ( (client = clients->FindNextClient(PROTOCOL_G3, &index)) != NULL ) + { + char module = client->GetReflectorModule(); + if (!strchr(m_Modules.c_str(), module) && !strchr(m_Modules.c_str(), '*')) + { + clients->RemoveClient(client); + } + } + g_Reflector.ReleaseClients(); +} + +void CG3Protocol::ReadOptions(void) +{ + char sz[256]; + int opts = 0; + + + std::ifstream file(TERMINALOPTIONS_PATH); + if (file.is_open()) + { + m_GwAddress = 0u; + m_Modules = "*"; + + while (file.getline(sz, sizeof(sz)).good()) + { + char *szt = TrimWhiteSpaces(sz); + char *szval; + + if ((::strlen(szt) > 0) && szt[0] != '#') + { + if ((szt = ::strtok(szt, " ,\t")) != NULL) + { + if ((szval = ::strtok(NULL, " ,\t")) != NULL) + { + if (::strncmp(szt, "address", 7) == 0) + { + in_addr addr = { .s_addr = inet_addr(szval) }; + if (addr.s_addr) + { + std::cout << "G3 handler address set to " << inet_ntoa(addr) << std::endl; + m_GwAddress = addr.s_addr; + opts++; + } + } + else if (strncmp(szt, "modules", 7) == 0) + { + std::cout << "G3 handler module list set to " << szval << std::endl; + m_Modules = szval; + opts++; + } + else + { + // unknown option - ignore + } + } + } + } + } + std::cout << "G3 handler loaded " << opts << " options from file " << TERMINALOPTIONS_PATH << std::endl; + file.close(); + + struct stat fileStat; + + if (::stat(TERMINALOPTIONS_PATH, &fileStat) != -1) + { + m_LastModTime = fileStat.st_mtime; + } + } +} + diff --git a/src/cg3protocol.h b/src/cg3protocol.h index b289502..f372385 100644 --- a/src/cg3protocol.h +++ b/src/cg3protocol.h @@ -25,6 +25,7 @@ #ifndef cg3protocol_h #define cg3protocol_h +#include #include "ctimepoint.h" #include "cprotocol.h" #include "cdvheaderpacket.h" @@ -59,7 +60,7 @@ class CG3Protocol : public CProtocol { public: // constructor - CG3Protocol() {}; + CG3Protocol() : m_GwAddress(0u), m_Modules("*"), m_LastModTime(0) {}; // destructor virtual ~CG3Protocol() {}; @@ -84,6 +85,13 @@ protected: void ConfigTask(void); void IcmpTask(void); + // config + void ReadOptions(void); + + // helper + char *TrimWhiteSpaces(char *); + bool NeedReload(void); + // queue helper void HandleQueue(void); @@ -97,12 +105,12 @@ protected: CDvHeaderPacket *IsValidDvHeaderPacket(const CBuffer &); CDvFramePacket *IsValidDvFramePacket(const CBuffer &); CDvLastFramePacket *IsValidDvLastFramePacket(const CBuffer &); - + // packet encoding helpers bool EncodeDvHeaderPacket(const CDvHeaderPacket &, CBuffer *) const; bool EncodeDvFramePacket(const CDvFramePacket &, CBuffer *) const; bool EncodeDvLastFramePacket(const CDvLastFramePacket &, CBuffer *) const; - + protected: std::thread *m_pPresenceThread; std::thread *m_pConfigThread; @@ -116,6 +124,11 @@ protected: CUdpSocket m_PresenceSocket; CUdpSocket m_ConfigSocket; int m_IcmpRawSocket; + + // optional params + uint32 m_GwAddress; + std::string m_Modules; + time_t m_LastModTime; }; //////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/main.h b/src/main.h index 138a3d8..19853dc 100644 --- a/src/main.h +++ b/src/main.h @@ -153,6 +153,7 @@ #define WHITELIST_PATH "/xlxd/xlxd.whitelist" #define BLACKLIST_PATH "/xlxd/xlxd.blacklist" #define INTERLINKLIST_PATH "/xlxd/xlxd.interlink" +#define TERMINALOPTIONS_PATH "/xlxd/xlxd.terminal" #define DEBUGDUMP_PATH "/var/log/xlxd.debug" // system constants ---------------------------------------------