diff --git a/.vscode/tasks.json b/.vscode/tasks.json index ac3f100..168ca95 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -12,7 +12,10 @@ "ENABLE_DEBUG=1", "USE_GPSD=1" ], - "group": "build", + "group": { + "kind": "build", + "isDefault": true + }, "problemMatcher": [] }, { @@ -25,10 +28,7 @@ "ENABLE_DEBUG=1", "USE_GPSD=1" ], - "group": { - "kind": "build", - "isDefault": true - }, + "group": "build", "problemMatcher": [] } ] diff --git a/DPlusHandler.cpp b/DPlusHandler.cpp index aa78395..178eefd 100644 --- a/DPlusHandler.cpp +++ b/DPlusHandler.cpp @@ -357,8 +357,9 @@ void CDPlusHandler::process(CConnectData& connect) } } -void CDPlusHandler::link(IReflectorCallback* handler, const std::string& repeater, const std::string &gateway, const in_addr& address) +void CDPlusHandler::link(IReflectorCallback* handler, const std::string& repeater, const std::string &gateway, const in_addr& address, unsigned int& localPort) { + localPort = 0U; CDPlusProtocolHandler* protoHandler = m_pool->getHandler(); if (protoHandler == NULL) return; @@ -377,6 +378,7 @@ void CDPlusHandler::link(IReflectorCallback* handler, const std::string& repeate if (found) { CConnectData connect(CT_LINK1, address, DPLUS_PORT); + localPort = protoHandler->getPort(); protoHandler->writeConnect(connect); m_stateChange = true; } else { diff --git a/DPlusHandler.h b/DPlusHandler.h index 2e6ea83..78b5b7a 100644 --- a/DPlusHandler.h +++ b/DPlusHandler.h @@ -58,7 +58,7 @@ public: static void startAuthenticator(const std::string& address, CCacheManager* cache); - static void link(IReflectorCallback* handler, const std::string& repeater, const std::string& reflector, const in_addr& address); + static void link(IReflectorCallback* handler, const std::string& repeater, const std::string& reflector, const in_addr& address, unsigned int& localPort); static void relink(IReflectorCallback* handler, const std::string& reflector); static void unlink(IReflectorCallback* handler, const std::string& reflector = "", bool exclude = true); static void unlink(); diff --git a/DPlusProtocolHandler.cpp b/DPlusProtocolHandler.cpp index 7414aa0..41e1e21 100644 --- a/DPlusProtocolHandler.cpp +++ b/DPlusProtocolHandler.cpp @@ -18,7 +18,7 @@ */ #include "DPlusProtocolHandler.h" - +#include "Log.h" #include "DStarDefines.h" #include "Utils.h" @@ -107,6 +107,17 @@ bool CDPlusProtocolHandler::writeConnect(const CConnectData& connect) return m_socket.write(buffer, length, connect.getYourAddress(), connect.getYourPort()); } +void CDPlusProtocolHandler::traverseNat(const std::string& address, unsigned int remotePort) +{ + unsigned char buffer = 0x00U; + + in_addr addr = CUDPReaderWriter::lookup(address); + + CLog::logInfo("DPlus Punching hole to %s:%u", address.c_str(), remotePort); + + m_socket.write(&buffer, 1U, addr, remotePort); +} + DPLUS_TYPE CDPlusProtocolHandler::read() { bool res = true; diff --git a/DPlusProtocolHandler.h b/DPlusProtocolHandler.h index 85b51f6..144dcf6 100644 --- a/DPlusProtocolHandler.h +++ b/DPlusProtocolHandler.h @@ -27,6 +27,7 @@ #include "PollData.h" #include +#include enum DPLUS_TYPE { DP_NONE, @@ -49,6 +50,7 @@ public: bool writeAMBE(const CAMBEData& data); bool writeConnect(const CConnectData& connect); bool writePoll(const CPollData& poll); + void traverseNat(const std::string& address, unsigned int remotePort); DPLUS_TYPE read(); CHeaderData* readHeader(); diff --git a/DStarGatewayThread.cpp b/DStarGatewayThread.cpp index 765179a..5229ceb 100644 --- a/DStarGatewayThread.cpp +++ b/DStarGatewayThread.cpp @@ -799,7 +799,6 @@ void CDStarGatewayThread::processIrcDDB() } break; case IDRT_NATTRAVERSAL_DEXTRA: { - std::string address, remotePort; bool res = m_irc->receiveNATTraversalDextra(address, remotePort); if(!res) @@ -814,6 +813,22 @@ void CDStarGatewayThread::processIrcDDB() CLog::logInfo("%s wants to DExtra connect to us, punching UDP Holes through NAT, remote port %s, but DExtra is Disabled", address.c_str(), remotePort.c_str()); } } + break; + case IDRT_NATTRAVERSAL_DPLUS: { + std::string address, remotePort; + bool res = m_irc->receiveNATTraversalDPlus(address, remotePort); + if(!res) + return; + + if(m_dextraEnabled && m_dextraPool != nullptr && m_dplusPool->getIncomingHandler() != nullptr) { + CLog::logInfo("%s wants to DPlus connect to us, punching UDP Holes through NAT, remote port %s", address.c_str(), remotePort.c_str()); + auto remotePortInt = boost::lexical_cast(remotePort); + m_dplusPool->getIncomingHandler()->traverseNat(address, remotePortInt); + } + else { + CLog::logInfo("%s wants to DPlus connect to us, punching UDP Holes through NAT, remote port %s, but DExtra is Disabled", address.c_str(), remotePort.c_str()); + } + } break; case IDRT_NONE: return; diff --git a/IRCDDB.h b/IRCDDB.h index b285119..6b5534a 100644 --- a/IRCDDB.h +++ b/IRCDDB.h @@ -34,6 +34,7 @@ enum IRCDDB_RESPONSE_TYPE { IDRT_REPEATER, IDRT_NATTRAVERSAL_G2, IDRT_NATTRAVERSAL_DEXTRA, + IDRT_NATTRAVERSAL_DPLUS, }; @@ -117,6 +118,7 @@ public: // notify another repeater for NAT Traversal, a false return implies a network error virtual bool notifyRepeaterG2NatTraversal(const std::string& repeater) = 0; virtual bool notifyRepeaterDextraNatTraversal(const std::string& repeater, unsigned int myport) = 0; + virtual bool notifyRepeaterDPlusNatTraversal(const std::string& repeater, unsigned int myport) = 0; // Support for the Smart Group Server virtual void sendDStarGatewayInfo(const std::string subcommand, const std::vector parms) = 0; @@ -142,6 +144,7 @@ public: virtual bool receiveNATTraversalG2(std::string& address) = 0; virtual bool receiveNATTraversalDextra(std::string& address, std::string& remotePort) = 0; + virtual bool receiveNATTraversalDPlus(std::string& address, std::string& remotePort) = 0; virtual void close() = 0; // Implictely kills any threads in the IRC code }; diff --git a/IRCDDBApp.cpp b/IRCDDBApp.cpp index c9ca209..7599bd8 100644 --- a/IRCDDBApp.cpp +++ b/IRCDDBApp.cpp @@ -262,6 +262,9 @@ IRCDDB_RESPONSE_TYPE IRCDDBApp::getReplyMessageType() if(msgType.compare("NATTRAVERSAL_DEXTRA") == 0) return IDRT_NATTRAVERSAL_DEXTRA; + if(msgType.compare("NATTRAVERSAL_DPLUS") == 0) + return IDRT_NATTRAVERSAL_DPLUS; + CLog::logWarning("IRCDDBApp::getMessageType: unknown msg type: %s\n", msgType.c_str()); return IDRT_NONE; @@ -643,27 +646,10 @@ bool IRCDDBApp::findUser(const std::string& usrCall) bool IRCDDBApp::notifyRepeaterG2NatTraversal(const std::string& repeater) { - auto firstSpacePos = repeater.find_first_of(' '); - if(firstSpacePos == std::string::npos) - return true; - - auto lrepeater = repeater.substr(0, firstSpacePos); - CUtils::ToLower(lrepeater); std::string nick; - std::lock_guard loclUserMap(m_d->m_userMapMutex); - for(unsigned int i = 1; i <= 4U; i++) { - nick = lrepeater + "-" + std::to_string(i); - if(m_d->m_userMap.count(nick) == 1) { - break; - } - nick.clear(); - } - - if(nick.empty()) { - CLog::logDebug("Unable to find IRC nick for repeater %s", repeater.c_str()); - return true; - } + if(!getNickForRepeater(repeater, nick)) + return true; //return true because this return value is handled as a network error, whoch is actually uncleve IRCMessage * ircMessage = new IRCMessage(nick, "NATTRAVERSAL_G2"); m_d->m_sendQ->putMessage(ircMessage); @@ -673,33 +659,53 @@ bool IRCDDBApp::notifyRepeaterG2NatTraversal(const std::string& repeater) bool IRCDDBApp::notifyRepeaterDextraNatTraversal(const std::string& repeater, unsigned int myLocalPort) { + std::string nick; + + if(!getNickForRepeater(repeater, nick)) + return true; //return true because this return value is handled as a network error, whoch is actually uncleve + + IRCMessage * ircMessage = new IRCMessage(nick, "NATTRAVERSAL_DEXTRA"); + ircMessage->addParam(std::to_string(myLocalPort)); + m_d->m_sendQ->putMessage(ircMessage); + + return true; +} + +bool IRCDDBApp::notifyRepeaterDPlusNatTraversal(const std::string& repeater, unsigned int myLocalPort) +{ + std::string nick; + + if(!getNickForRepeater(repeater, nick)) + return true; //return true because this return value is handled as a network error, whoch is actually unclever + + IRCMessage * ircMessage = new IRCMessage(nick, "NATTRAVERSAL_DPLUS"); + ircMessage->addParam(std::to_string(myLocalPort)); + m_d->m_sendQ->putMessage(ircMessage); + + return true; +} + +bool IRCDDBApp::getNickForRepeater(const std::string& repeater, std::string& nick) const +{ + std::lock_guard lockUserMap(m_d->m_userMapMutex); + auto firstSpacePos = repeater.find_first_of(' '); if(firstSpacePos == std::string::npos) - return true; - + return false; + auto lrepeater = repeater.substr(0, firstSpacePos); CUtils::ToLower(lrepeater); - std::string nick; - - std::lock_guard loclUserMap(m_d->m_userMapMutex); + for(unsigned int i = 1; i <= 4U; i++) { nick = lrepeater + "-" + std::to_string(i); if(m_d->m_userMap.count(nick) == 1) { - break; + return true; } - nick.clear(); - } - - if(nick.empty()) { - CLog::logDebug("Unable to find IRC nick for repeater %s", repeater.c_str()); - return true; } - IRCMessage * ircMessage = new IRCMessage(nick, "NATTRAVERSAL_DEXTRA"); - ircMessage->addParam(std::to_string(myLocalPort)); - m_d->m_sendQ->putMessage(ircMessage); - - return true; + nick.clear(); + CLog::logDebug("Unable to find IRC nick for repeater %s", repeater.c_str()); + return false; } void IRCDDBApp::msgChannel(IRCMessage *m) @@ -919,6 +925,13 @@ void IRCDDBApp::msgQuery(IRCMessage *m) m2->addParam(remotePort); m_d->m_replyQ.putMessage(m2); } + else if(m->m_params.size() >= 2U && boost::starts_with(m->m_params[1], "NATTRAVERSAL_DPLUS")) { + IRCMessage * m2 = new IRCMessage(m->m_params[1].substr(0, (std::string("NATTRAVERSAL_DPLUS")).length())); + m2->addParam(m->getPrefixHost()); + std::string remotePort = boost::trim_copy(boost::replace_all_copy(m->m_params[1], "NATTRAVERSAL_DPLUS", "")); + m2->addParam(remotePort); + m_d->m_replyQ.putMessage(m2); + } } } diff --git a/IRCDDBApp.h b/IRCDDBApp.h index 7677e98..c88935e 100644 --- a/IRCDDBApp.h +++ b/IRCDDBApp.h @@ -69,6 +69,7 @@ public: bool notifyRepeaterG2NatTraversal(const std::string& repeater); bool notifyRepeaterDextraNatTraversal(const std::string& repeater, unsigned int myLocalPort); + bool notifyRepeaterDPlusNatTraversal(const std::string& repeater, unsigned int myLocalPort); 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); @@ -93,6 +94,8 @@ private: bool findServerUser(); unsigned int calculateUsn(const std::string& nick); std::string getLastEntryTime(int tableID); + bool getNickForRepeater(const std::string& repeater, std::string& user) const; + IRCDDBAppPrivate *m_d; time_t m_maxTime; std::future m_future; diff --git a/IRCDDBClient.cpp b/IRCDDBClient.cpp index 60c51b0..644ef35 100644 --- a/IRCDDBClient.cpp +++ b/IRCDDBClient.cpp @@ -288,6 +288,11 @@ bool CIRCDDBClient::notifyRepeaterDextraNatTraversal(const std::string& repeater return m_d->m_app->notifyRepeaterDextraNatTraversal(repeater, myPort); } +bool CIRCDDBClient::notifyRepeaterDPlusNatTraversal(const std::string& repeater, unsigned int myPort) +{ + return m_d->m_app->notifyRepeaterDPlusNatTraversal(repeater, myPort); +} + // The following functions are for processing received messages // Get the waiting message type @@ -467,7 +472,7 @@ bool CIRCDDBClient::receiveNATTraversalDextra(std::string& address, std::string& } if (m->getCommand().compare("NATTRAVERSAL_DEXTRA")) { - CLog::logDebug("CIRCDDBClient::receiveNATTraversalDextra: wrong message type, expected 'NATTRAVERSAL_G2', got '%s'\n", m->getCommand().c_str()); + CLog::logDebug("CIRCDDBClient::receiveNATTraversalDextra: wrong message type, expected 'NATTRAVERSAL_DEXTRA', got '%s'\n", m->getCommand().c_str()); delete m; return false; } @@ -485,6 +490,41 @@ bool CIRCDDBClient::receiveNATTraversalDextra(std::string& address, std::string& return true; } +bool CIRCDDBClient::receiveNATTraversalDPlus(std::string& address, std::string& remotePort) +{ + IRCDDB_RESPONSE_TYPE rt = m_d->m_app->getReplyMessageType(); + + if(rt != IDRT_NATTRAVERSAL_DPLUS) { + CLog::logDebug("CIRCDDBClient::receiveNATTraversalDPlus: unexpected response type=%d\n", rt); + return false; + } + + IRCMessage * m = m_d->m_app->getReplyMessage(); + + if (m == NULL) { + CLog::logDebug("CIRCDDBClient::receiveNATTraversalDPlus: no message\n"); + return false; + } + + if (m->getCommand().compare("NATTRAVERSAL_DPLUS")) { + CLog::logDebug("CIRCDDBClient::receiveNATTraversalDPlus: wrong message type, expected 'NATTRAVERSAL_DPLUS', got '%s'\n", m->getCommand().c_str()); + delete m; + return false; + } + + if (2 != m->getParamCount()) { + CLog::logDebug("CIRCDDBClient::receiveNATTraversalDPlus: unexpected number of message parameters, expected 2, got %d\n", m->getParamCount()); + delete m; + return false; + } + + address = m->m_params[0]; + remotePort = m->m_params[1]; + delete m; + + return true; +} + void CIRCDDBClient::close() // Implictely kills any threads in the IRC code { m_d->client -> stopWork(); diff --git a/IRCDDBClient.h b/IRCDDBClient.h index a5c8981..4a0f8d2 100644 --- a/IRCDDBClient.h +++ b/IRCDDBClient.h @@ -109,6 +109,7 @@ public: // notify another repeater for NAT Traversal, a false return implies a network error bool notifyRepeaterG2NatTraversal(const std::string& repeater); bool notifyRepeaterDextraNatTraversal(const std::string& repeater, unsigned int myport); + bool notifyRepeaterDPlusNatTraversal(const std::string& repeater, unsigned int myport); // Support for the Smart Group Server void sendDStarGatewayInfo(const std::string subcommand, const std::vector parms); @@ -134,6 +135,7 @@ public: bool receiveNATTraversalG2(std::string& address); bool receiveNATTraversalDextra(std::string& address, std::string& remotePort); + bool receiveNATTraversalDPlus(std::string& address, std::string& remotePort); void close(); // Implictely kills any threads in the IRC code diff --git a/IRCDDBMultiClient.cpp b/IRCDDBMultiClient.cpp index 96cb913..a7fd1fd 100644 --- a/IRCDDBMultiClient.cpp +++ b/IRCDDBMultiClient.cpp @@ -203,7 +203,16 @@ bool CIRCDDBMultiClient::notifyRepeaterDextraNatTraversal(const std::string& rep return result; } +bool CIRCDDBMultiClient::notifyRepeaterDPlusNatTraversal(const std::string& repeater, unsigned int myPort) +{ + // NAT traversal message does not expect a response over IRC + bool result = true; + for (unsigned int i = 0; i < m_clients.size(); i++) { + result = m_clients[i]->notifyRepeaterDPlusNatTraversal(repeater, myPort) && result; + } + return result; +} bool CIRCDDBMultiClient::receiveNATTraversalG2(std::string& address) { @@ -228,6 +237,18 @@ bool CIRCDDBMultiClient::receiveNATTraversalDextra(std::string& address, std::st return true; } +bool CIRCDDBMultiClient::receiveNATTraversalDPlus(std::string& address, std::string& remotePort) +{ + CIRCDDBMultiClientQuery * item = checkAndGetNextResponse(IDRT_NATTRAVERSAL_DPLUS, "CIRCDDBMultiClient::receiveNATTraversalDextra: unexpected response type"); + if (item == NULL) + return false; + + address = item->getAddress(); + remotePort = item->getRemotePort(); + + return true; +} + IRCDDB_RESPONSE_TYPE CIRCDDBMultiClient::getMessageType() { //process the inner clients at each call diff --git a/IRCDDBMultiClient.h b/IRCDDBMultiClient.h index eff829a..2b47a5f 100644 --- a/IRCDDBMultiClient.h +++ b/IRCDDBMultiClient.h @@ -161,6 +161,7 @@ public: virtual bool findUser(const std::string & userCallsign); virtual bool notifyRepeaterG2NatTraversal(const std::string& repeater); virtual bool notifyRepeaterDextraNatTraversal(const std::string& repeater, unsigned int myPort); + virtual bool notifyRepeaterDPlusNatTraversal(const std::string& repeater, unsigned int myPort); virtual IRCDDB_RESPONSE_TYPE getMessageType(); virtual bool receiveRepeater(std::string & repeaterCallsign, std::string & gatewayCallsign, std::string & address); virtual bool receiveGateway(std::string & gatewayCallsign, std::string & address); @@ -168,6 +169,7 @@ public: virtual bool receiveUser(std::string & userCallsign, std::string & repeaterCallsign, std::string & gatewayCallsign, std::string & address, std::string & timeStamp); virtual bool receiveNATTraversalG2(std::string& address); virtual bool receiveNATTraversalDextra(std::string& address, std::string& remotePort); + virtual bool receiveNATTraversalDPlus(std::string& address, std::string& remotePort); virtual void sendDStarGatewayInfo(const std::string subcommand, const std::vector parms); virtual void close(); diff --git a/RepeaterHandler.cpp b/RepeaterHandler.cpp index cfad808..b88bea2 100644 --- a/RepeaterHandler.cpp +++ b/RepeaterHandler.cpp @@ -1342,8 +1342,9 @@ void CRepeaterHandler::resolveRepeaterInt(const std::string& repeater, const std case DP_DPLUS: if (m_dplusEnabled) { m_linkGateway = gateway; + unsigned int localPort = 0U; addr.s_addr = ::inet_addr(address.c_str()); - CDPlusHandler::link(this, m_rptCallsign, m_linkRepeater, addr); + CDPlusHandler::link(this, m_rptCallsign, m_linkRepeater, addr, localPort); m_linkStatus = LS_LINKING_DPLUS; } else { CLog::logInfo("Require D-Plus for linking to %s, but D-Plus is disabled", repeater.c_str()); @@ -2287,8 +2288,11 @@ void CRepeaterHandler::linkInt(const std::string& callsign) switch (data->getProtocol()) { case DP_DPLUS: if (m_dplusEnabled) { + unsigned int localPort = 0U; m_linkStatus = LS_LINKING_DPLUS; - CDPlusHandler::link(this, m_rptCallsign, m_linkRepeater, data->getAddress()); + CDPlusHandler::link(this, m_rptCallsign, m_linkRepeater, data->getAddress(), localPort); + if(m_irc != nullptr && localPort > 0U) + m_irc->notifyRepeaterDPlusNatTraversal(m_linkRepeater, localPort); writeLinkingTo(m_linkRepeater); triggerInfo(); } else { @@ -2462,8 +2466,11 @@ void CRepeaterHandler::startupInt() switch (protocol) { case DP_DPLUS: if (m_dplusEnabled) { + unsigned int localPort = 0U; m_linkStatus = LS_LINKING_DPLUS; - CDPlusHandler::link(this, m_rptCallsign, m_linkRepeater, data->getAddress()); + CDPlusHandler::link(this, m_rptCallsign, m_linkRepeater, data->getAddress(), localPort); + if(m_irc != nullptr && localPort > 0U) + m_irc->notifyRepeaterDPlusNatTraversal(m_linkRepeater, localPort); writeLinkingTo(m_linkRepeater); triggerInfo(); } else {