From c53df9f70d568e813aee885d1d42356276a1f48c Mon Sep 17 00:00:00 2001 From: Jan Willamowius Date: Fri, 12 Mar 2021 23:51:30 +0100 Subject: [PATCH] fix H.460.18/.19 on multi-homed servers --- ProxyChannel.cxx | 201 ++++++++++++++++++++++++++++++++++++----------- ProxyChannel.h | 8 +- RasSrv.cxx | 10 ++- RasTbl.cxx | 48 +++++++++-- RasTbl.h | 18 ++++- changes.txt | 1 + h323util.cxx | 7 +- h323util.h | 5 +- yasocket.cxx | 119 ++++++++++++++++++++++++---- yasocket.h | 5 +- 10 files changed, 345 insertions(+), 77 deletions(-) diff --git a/ProxyChannel.cxx b/ProxyChannel.cxx index e9fd540d..74195361 100644 --- a/ProxyChannel.cxx +++ b/ProxyChannel.cxx @@ -3,7 +3,7 @@ // ProxyChannel.cxx // // Copyright (c) Citron Network Inc. 2001-2002 -// Copyright (c) 2002-2020, Jan Willamowius +// Copyright (c) 2002-2021, Jan Willamowius // // This work is published under the GNU Public License version 2 (GPLv2) // see file COPYING for details. @@ -455,7 +455,7 @@ bool IsOldH263(const H245_DataType & type) // the method is highly OS specific #ifdef _WIN32 -ssize_t UDPSendWithSourceIP(int fd, void * data, size_t len, const IPAndPortAddress & toAddress) +ssize_t UDPSendWithSourceIP(int fd, void * data, size_t len, const IPAndPortAddress & toAddress, PIPSocket::Address * gkIP) { #ifdef hasIPV6 struct sockaddr_in6 dest; @@ -474,7 +474,12 @@ ssize_t UDPSendWithSourceIP(int fd, void * data, size_t len, const IPAndPortAddr #if (_WIN32_WINNT >= WINDOWS_VISTA) if (g_pfWSASendMsg && !g_disableSettingUDPSourceIP) { // set source address - PIPSocket::Address src = RasServer::Instance()->GetLocalAddress(toIP); + PIPSocket::Address src; + if (gkIP) { + src = * gkIP; // if we have a specific GK IP to use, use that + } else { + src = RasServer::Instance()->GetLocalAddress(toIP); // pick a source IP from gatekeeper interfaces + } WSABUF wsabuf; wsabuf.buf = (CHAR *)data; @@ -557,7 +562,7 @@ ssize_t UDPSendWithSourceIP(int fd, void * data, size_t len, const IPAndPortAddr #else // Unix -ssize_t UDPSendWithSourceIP(int fd, void * data, size_t len, const IPAndPortAddress & toAddress) +ssize_t UDPSendWithSourceIP(int fd, void * data, size_t len, const IPAndPortAddress & toAddress, PIPSocket::Address * gkIP) { #ifdef hasIPV6 struct sockaddr_in6 dest; @@ -609,7 +614,12 @@ ssize_t UDPSendWithSourceIP(int fd, void * data, size_t len, const IPAndPortAddr msgh.msg_namelen = addr_len; // set source address - PIPSocket::Address src = RasServer::Instance()->GetLocalAddress(toIP); + PIPSocket::Address src; + if (gkIP) { + src = * gkIP; // if we have a specific GK IP to use, use that + } else { + src = RasServer::Instance()->GetLocalAddress(toIP); // pick a source IP from gatekeeper interfaces + } #ifdef hasIPV6 if (Toolkit::Instance()->IsIPv6Enabled() && (src.GetVersion() == 6)) { @@ -689,10 +699,10 @@ ssize_t UDPSendWithSourceIP(int fd, void * data, size_t len, const IPAndPortAddr } #endif -ssize_t UDPSendWithSourceIP(int fd, void * data, size_t len, const PIPSocket::Address & ip, WORD port) +ssize_t UDPSendWithSourceIP(int fd, void * data, size_t len, const PIPSocket::Address & ip, WORD port, PIPSocket::Address * gkIP) { const IPAndPortAddress to(ip, port); - return UDPSendWithSourceIP(fd, data, len, to); + return UDPSendWithSourceIP(fd, data, len, to, gkIP); } @@ -1949,6 +1959,10 @@ void CallSignalSocket::SetRemote(CallSignalSocket * socket) UnmapIPv4Address(localAddr); masqAddr = RasServer::Instance()->GetMasqAddress(peerAddr); UnmapIPv4Address(masqAddr); + PTRACE(0, "JW set masqAddr=" << masqAddr << " for " << peerAddr); // JWXM + if (m_call->GetEndpointIPMapping(peerAddr, masqAddr)) { + PTRACE(0, "JW set masqAddr=" << masqAddr << " for " << peerAddr << " from IPMapping"); + } SetHandler(socket->GetHandler()); // don't rename the existing remote socket of a H.460.17 call @@ -4331,6 +4345,7 @@ void CallSignalSocket::OnSetup(SignalingMsg * msg) WORD _peerPort = 0, _localPort = 0; msg->GetPeerAddr(_peerAddr, _peerPort); msg->GetLocalAddr(_localAddr, _localPort); + PTRACE(0, "JW Setup from " << _peerAddr << " on " << _localAddr); // incompatible with 'explicit' routing if (GkConfig()->GetBoolean(RoutedSec, "RedirectCallsToGkIP", false)) { @@ -4843,6 +4858,10 @@ void CallSignalSocket::OnSetup(SignalingMsg * msg) << " destination set to " << alias ); } + if (!rejectCall) { + PTRACE(0, "JW SetEndpointIPMapping " << _peerAddr << " <=> " << _localAddr); + m_call->SetEndpointIPMapping(_peerAddr, _localAddr); // JWXX + } if (!rejectCall && authData.m_callDurationLimit > 0) m_call->SetDurationLimit(authData.m_callDurationLimit); if (!authData.m_callingStationId) @@ -5200,6 +5219,10 @@ void CallSignalSocket::OnSetup(SignalingMsg * msg) m_call->SetSetupTime(setupTime); CallTable::Instance()->Insert(call); + if (!rejectCall) { + PTRACE(0, "JW SetEndpointIPMapping " << _peerAddr << " <=> " << _localAddr); + m_call->SetEndpointIPMapping(_peerAddr, _localAddr); // JWXX + } if (!rejectCall && authData.m_callDurationLimit > 0) m_call->SetDurationLimit(authData.m_callDurationLimit); if (!authData.m_callingStationId) @@ -5686,6 +5709,15 @@ void CallSignalSocket::OnSetup(SignalingMsg * msg) UnmapIPv4Address(localAddr); masqAddr = RasServer::Instance()->GetMasqAddress(peerAddr); UnmapIPv4Address(masqAddr); + PTRACE(0, "JW set masqAddr=" << masqAddr << " for " << peerAddr); // JWXM + if (m_call && m_call->GetCalledParty() && m_call->GetCalledParty()->GetRasServerIP().IsValid()) { + PTRACE(0, "JW set masqAddr to called rasserverip=" << AsString(m_call->GetCalledParty()->GetRasServerIP())); + masqAddr = m_call->GetCalledParty()->GetRasServerIP(); + } + if (m_call->GetEndpointIPMapping(peerAddr, masqAddr)) { + PTRACE(0, "JW set masqAddr=" << masqAddr << " for " << peerAddr << " from IPMapping"); + } + m_call->SetCallSignalSocketCalling(this); SetConnected(true); // avoid deletion @@ -5772,14 +5804,8 @@ void CallSignalSocket::OnSetup(SignalingMsg * msg) H46018_IncomingCallIndication incoming; incoming.m_callID = setupBody.m_callIdentifier; - // send GK's signal addr on the best interface for this endpoint - if (!m_call->GetDestSignalAddr(peerAddr, peerPort)) { - PTRACE(3, Type() << "\tINVALID DESTINATION ADDRESS for call from " << Name()); - m_call->SetDisconnectCause(Q931::IncompatibleDestination); - m_result = Error; - return; - } - incoming.m_callSignallingAddress = RasServer::Instance()->GetCallSignalAddress(peerAddr); + WORD signalPort = (WORD)GkConfig()->GetInteger(RoutedSec, "CallSignalPort", GK_DEF_CALL_SIGNAL_PORT); + incoming.m_callSignallingAddress = SocketToH225TransportAddr(m_call->GetCalledParty()->GetRasServerIP(), signalPort); H460_FeatureStd feat = H460_FeatureStd(18); PASN_OctetString rawIndication; @@ -5809,7 +5835,6 @@ void CallSignalSocket::OnSetup(SignalingMsg * msg) } #endif - // on a multi-homed server, the SCI may get the wrong source address and the call fails RasSrv->SendRas(sci_ras, m_call->GetCalledParty()->GetRasAddress(), m_call->GetCalledParty()->GetRasServerIP(), m_call->GetCalledParty()->GetH235Authenticators()); // store Setup @@ -5841,6 +5866,14 @@ bool CallSignalSocket::CreateRemote(H225_Setup_UUIE & setupBody) UnmapIPv4Address(localAddr); masqAddr = RasServer::Instance()->GetMasqAddress(peerAddr); UnmapIPv4Address(masqAddr); + PTRACE(0, "JW set masqAddr=" << masqAddr << " for " << peerAddr); // JWXM + if (m_call && m_call->GetCalledParty() && m_call->GetCalledParty()->GetRasServerIP().IsValid()) { + PTRACE(0, "JW set masqAddr to called rasserverip=" << AsString(m_call->GetCalledParty()->GetRasServerIP())); + masqAddr = m_call->GetCalledParty()->GetRasServerIP(); + } + if (m_call->GetEndpointIPMapping(peerAddr, masqAddr)) { + PTRACE(0, "JW set masqAddr=" << masqAddr << " for " << peerAddr << " from IPMapping"); + } } // only rewrite sourceCallSignalAddress if we are proxying, // otherwise leave the receiving endpoint the option to deal with NATed caller itself @@ -6000,6 +6033,10 @@ bool CallSignalSocket::CreateRemote(const H225_TransportAddress & addr) UnmapIPv4Address(localAddr); masqAddr = RasServer::Instance()->GetMasqAddress(peerAddr); UnmapIPv4Address(masqAddr); + PTRACE(0, "JW set masqAddr=" << masqAddr << " for " << peerAddr); // JWXM + if (m_call->GetEndpointIPMapping(peerAddr, masqAddr)) { + PTRACE(0, "JW set masqAddr=" << masqAddr << " for " << peerAddr << " from IPMapping"); + } #ifdef HAS_TLS if (Toolkit::Instance()->IsTLSEnabled() && m_call->ConnectWithTLS()) { @@ -7885,6 +7922,14 @@ void CallSignalSocket::OnFacility(SignalingMsg * msg) UnmapIPv4Address(localAddr); masqAddr = RasServer::Instance()->GetMasqAddress(peerAddr); UnmapIPv4Address(masqAddr); + PTRACE(0, "JW set masqAddr=" << masqAddr << " for " << peerAddr); // JWXM + if (m_call->GetCallingParty() && m_call->GetCallingParty()->GetRasServerIP().IsValid()) { + PTRACE(0, "JW set masqAddr to calling rasserverip=" << AsString(m_call->GetCallingParty()->GetRasServerIP())); + masqAddr = m_call->GetCallingParty()->GetRasServerIP(); + } + if (m_call->GetEndpointIPMapping(peerAddr, masqAddr)) { + PTRACE(0, "JW set masqAddr=" << masqAddr << " for " << peerAddr << " from IPMapping"); + } callingSocket->remote = this; // update localAddr and masqAddr in remote, now that we know their peerAddr Address remote_peerAddr; @@ -7894,6 +7939,16 @@ void CallSignalSocket::OnFacility(SignalingMsg * msg) UnmapIPv4Address(callingSocket->localAddr); callingSocket->masqAddr = RasServer::Instance()->GetMasqAddress(remote_peerAddr); UnmapIPv4Address(callingSocket->masqAddr); + PTRACE(0, "JW set callingSocket->masqAddr=" << callingSocket->masqAddr << " for " << remote_peerAddr); // JWXM + if (m_call->GetCalledParty() && m_call->GetCalledParty()->GetRasServerIP().IsValid()) { + PTRACE(0, "JW set masqAddr to called rasserverip=" << AsString(m_call->GetCalledParty()->GetRasServerIP())); + callingSocket->masqAddr = m_call->GetCalledParty()->GetRasServerIP(); + } else { + PTRACE(0, "JW calling NO rasserverip"); + } + if (m_call->GetEndpointIPMapping(remote_peerAddr, callingSocket->masqAddr)) { + PTRACE(0, "JW set callingSocket->masqAddr=" << masqAddr << " for " << remote_peerAddr << " from IPMapping"); + } callingSocket->SetConnected(true); SetConnected(true); @@ -8288,6 +8343,7 @@ void CallSignalSocket::BuildFacilityPDU(Q931 & FacilityPDU, int reason, const PO } else { PTRACE(2, "Error: " << GetName() << " has no remote party?"); } + #ifdef HAS_H46018 // add H.460.19 indicator if this is sent out to an endpoint that uses it if (m_call && m_call->GetCalledParty() && m_call->H46019Required() @@ -8504,6 +8560,11 @@ void CallSignalSocket::BuildSetupPDU(Q931 & SetupPDU, const H225_CallIdentifier setup.m_conferenceID = callid.m_guid; // generate new: OpalGloballyUniqueID(); setup.m_callIdentifier.m_guid = setup.m_conferenceID; masqAddr = RasServer::Instance()->GetMasqAddress(peerAddr); + PTRACE(0, "JW set masqAddr=" << masqAddr << " for " << peerAddr); // JWXM + if (m_call->GetEndpointIPMapping(peerAddr, masqAddr)) { + PTRACE(0, "JW set masqAddr=" << masqAddr << " for " << peerAddr << " from IPMapping"); + } + setup.IncludeOptionalField(H225_Setup_UUIE::e_sourceCallSignalAddress); setup.m_sourceCallSignalAddress = SocketToH225TransportAddr(masqAddr, GetPort()); // check if destination contain IP to set destCallSigAdr @@ -10804,15 +10865,24 @@ void H46019Session::HandlePacket(DWORD receivedMultiplexID, const IPAndPortAddre } #endif + PIPSocket::Address gkIP; if (receivedMultiplexID == m_multiplexID_fromA) { if (sideBReady(isRTCP)) { - Send(m_multiplexID_toB, (isRTCP ? m_addrB_RTCP : m_addrB), (isRTCP ? m_osSocketToB_RTCP : m_osSocketToB), data, len, true); + if (call->GetEndpointIPMapping((isRTCP ? m_addrB_RTCP.GetIP() : m_addrB.GetIP()), gkIP)) { + Send(m_multiplexID_toB, (isRTCP ? m_addrB_RTCP : m_addrB), (isRTCP ? m_osSocketToB_RTCP : m_osSocketToB), data, len, true, &gkIP); + } else { + Send(m_multiplexID_toB, (isRTCP ? m_addrB_RTCP : m_addrB), (isRTCP ? m_osSocketToB_RTCP : m_osSocketToB), data, len, true, NULL); + } } else { PTRACE(5, "RTPM\tReceiver not ready"); } } else if (receivedMultiplexID == m_multiplexID_fromB) { if (sideAReady(isRTCP)) { - Send(m_multiplexID_toA, (isRTCP ? m_addrA_RTCP : m_addrA), (isRTCP ? m_osSocketToA_RTCP : m_osSocketToA), data, len, true); + if (call->GetEndpointIPMapping((isRTCP ? m_addrA_RTCP.GetIP() : m_addrA.GetIP()), gkIP)) { + Send(m_multiplexID_toA, (isRTCP ? m_addrA_RTCP : m_addrA), (isRTCP ? m_osSocketToA_RTCP : m_osSocketToA), data, len, true, &gkIP); + } else { + Send(m_multiplexID_toA, (isRTCP ? m_addrA_RTCP : m_addrA), (isRTCP ? m_osSocketToA_RTCP : m_osSocketToA), data, len, true, NULL); + } } else { PTRACE(5, "RTPM\tReceiver not ready"); } @@ -10822,7 +10892,7 @@ void H46019Session::HandlePacket(DWORD receivedMultiplexID, const IPAndPortAddre } } -void H46019Session::Send(DWORD sendMultiplexID, const IPAndPortAddress & toAddress, int osSocket, void * data, unsigned len, bool bufferHasRoomForID) +void H46019Session::Send(DWORD sendMultiplexID, const IPAndPortAddress & toAddress, int osSocket, void * data, unsigned len, bool bufferHasRoomForID, PIPSocket::Address * gkIP) { size_t lenToSend = len; size_t sent = 0; @@ -10847,11 +10917,11 @@ void H46019Session::Send(DWORD sendMultiplexID, const IPAndPortAddress & toAddre PUInt32b networkID = sendMultiplexID; // convert multiplex ID to network format *((PUInt32b*)multiplexMsg) = networkID; // set multiplexID - sent = UDPSendWithSourceIP(osSocket, multiplexMsg, lenToSend, toAddress); + sent = UDPSendWithSourceIP(osSocket, multiplexMsg, lenToSend, toAddress, gkIP); if (!bufferHasRoomForID) free(multiplexMsg); } else { - sent = UDPSendWithSourceIP(osSocket, data, lenToSend, toAddress); + sent = UDPSendWithSourceIP(osSocket, data, lenToSend, toAddress, gkIP); } if (sent != lenToSend) { PTRACE(1, "RTPM\tError sending RTP to " << toAddress << ": should send=" << lenToSend << " did send=" << sent << " errno=" << errno); @@ -11133,24 +11203,24 @@ bool MultiplexedRTPHandler::HandlePacket(PINDEX callno, const H46026_UDPFrame & if (!data.m_dataFrame) { if (IsSet(iter->m_addrA_RTCP) && (iter->m_osSocketToA_RTCP != INVALID_OSSOCKET)) { PTRACE(7, "JW send mux packet to " << iter->m_addrA_RTCP << " osSocket=" << iter->m_osSocketToA_RTCP); - iter->Send(iter->m_multiplexID_toA, iter->m_addrA_RTCP, iter->m_osSocketToA_RTCP, bytes.GetPointer(), bytes.GetSize(), false); + iter->Send(iter->m_multiplexID_toA, iter->m_addrA_RTCP, iter->m_osSocketToA_RTCP, bytes.GetPointer(), bytes.GetSize(), false, NULL); // JWX TODO } } else { if (IsSet(iter->m_addrA) && (iter->m_osSocketToA != INVALID_OSSOCKET)) { PTRACE(7, "JW send mux packet to " << iter->m_addrA << " osSocket=" << iter->m_osSocketToA); - iter->Send(iter->m_multiplexID_toA, iter->m_addrA, iter->m_osSocketToA, bytes.GetPointer(), bytes.GetSize(), false); + iter->Send(iter->m_multiplexID_toA, iter->m_addrA, iter->m_osSocketToA, bytes.GetPointer(), bytes.GetSize(), false, NULL); // JWX TODO } } } else if (iter->m_multiplexID_toB != INVALID_MULTIPLEX_ID) { if (!data.m_dataFrame) { if (IsSet(iter->m_addrB_RTCP) && (iter->m_osSocketToB_RTCP != INVALID_OSSOCKET)) { PTRACE(7, "JW send mux packet to " << iter->m_addrB_RTCP << " osSocket=" << iter->m_osSocketToB_RTCP); - iter->Send(iter->m_multiplexID_toB, iter->m_addrB_RTCP, iter->m_osSocketToB_RTCP, bytes.GetPointer(), bytes.GetSize(), false); + iter->Send(iter->m_multiplexID_toB, iter->m_addrB_RTCP, iter->m_osSocketToB_RTCP, bytes.GetPointer(), bytes.GetSize(), false, NULL); // JWX TODO } } else { if (IsSet(iter->m_addrB) && (iter->m_osSocketToB != INVALID_OSSOCKET)) { PTRACE(7, "JW send mux packet to " << iter->m_addrB << " osSocket=" << iter->m_osSocketToB); - iter->Send(iter->m_multiplexID_toB, iter->m_addrB, iter->m_osSocketToB, bytes.GetPointer(), bytes.GetSize(), false); + iter->Send(iter->m_multiplexID_toB, iter->m_addrB, iter->m_osSocketToB, bytes.GetPointer(), bytes.GetSize(), false, NULL); // JWX TODO } } } @@ -11285,7 +11355,7 @@ void H46026Session::Send(void * data, unsigned len, bool isRTCP) toAddress = m_toAddressRTP; } - sent = UDPSendWithSourceIP(osSocket, data, lenToSend, toAddress); + sent = UDPSendWithSourceIP(osSocket, data, lenToSend, toAddress, NULL); if (sent != lenToSend) { PTRACE(1, "H46026\tError sending RTP to " << toAddress << ": should send=" << lenToSend << " did send=" << sent << " errno=" << errno); } @@ -11913,6 +11983,9 @@ ProxySocket::Result UDPProxySocket::ReceiveData() Address localaddr; WORD localport = 0; GetLocalAddress(localaddr, localport); +#ifdef LARGE_FDSET + GetLastDestAddress(localaddr); +#endif UnmapIPv4Address(localaddr); unsigned int seq = 0; unsigned int timestamp = 0; @@ -11920,7 +11993,7 @@ ProxySocket::Result UDPProxySocket::ReceiveData() seq = ((int)wbuffer[2] * 256) + (int)wbuffer[3]; if (buflen >= 8) timestamp = ((int)wbuffer[4] * 16777216) + ((int)wbuffer[5] * 65536) + ((int)wbuffer[6] * 256) + (int)wbuffer[7]; - PTRACE(7, "JW RTP IN on " << localport << " from " << AsString(fromIP, fromPort) << " pType=" << (int)payloadType + PTRACE(7, "JW RTP IN on " << AsString(localaddr, localport) << " from " << AsString(fromIP, fromPort) << " pType=" << (int)payloadType << " seq=" << seq << " timestamp=" << timestamp << " len=" << buflen << " fSrc=" << AsString(fSrcIP, fSrcPort) << " fDest=" << AsString(fDestIP, fDestPort) << " rSrc=" << AsString(rSrcIP, rSrcPort) << " rDest=" << AsString(rDestIP, rDestPort)); @@ -11939,6 +12012,13 @@ ProxySocket::Result UDPProxySocket::ReceiveData() } } + // TODO: save which destination IP on the gatekeeper is used by remote endpoint and use that to send! JWX + if (!m_portDetectionDone) { + if (m_call && (*m_call)) { + (*m_call)->SetEndpointIPMapping(fromIP, localaddr); + } + } + if (m_ignoreSignaledIPs && !m_portDetectionDone) { //// learn from data we already have (eg. from H.239 signaling) // set known destination as assumed source @@ -12238,17 +12318,17 @@ ProxySocket::Result UDPProxySocket::ReceiveData() #ifdef HAS_H46026 // send packets to H.460.26 endpoints via TCP if (Toolkit::Instance()->IsH46026Enabled()) { - if (m_call && (*m_call) && (*m_call)->GetCallingParty() && (*m_call)->GetCallingParty()->UsesH46026()) { - if (m_call && (*m_call) && (*m_call)->GetCallingParty()->GetSocket()) { - (*m_call)->GetCallingParty()->GetSocket()->SendH46026RTP(m_sessionID, isRTP, wbuffer, buflen); - } - return NoData; // already forwarded via TCP - } else if (m_call && (*m_call) && (*m_call)->GetCalledParty() && (*m_call)->GetCalledParty()->UsesH46026()) { - if (m_call && (*m_call) && (*m_call)->GetCalledParty()->GetSocket()) { - (*m_call)->GetCalledParty()->GetSocket()->SendH46026RTP(m_sessionID, isRTP, wbuffer, buflen); - } - return NoData; // already forwarded via TCP - } + if (m_call && (*m_call) && (*m_call)->GetCallingParty() && (*m_call)->GetCallingParty()->UsesH46026()) { + if (m_call && (*m_call) && (*m_call)->GetCallingParty()->GetSocket()) { + (*m_call)->GetCallingParty()->GetSocket()->SendH46026RTP(m_sessionID, isRTP, wbuffer, buflen); + } + return NoData; // already forwarded via TCP + } else if (m_call && (*m_call) && (*m_call)->GetCalledParty() && (*m_call)->GetCalledParty()->UsesH46026()) { + if (m_call && (*m_call) && (*m_call)->GetCalledParty()->GetSocket()) { + (*m_call)->GetCalledParty()->GetSocket()->SendH46026RTP(m_sessionID, isRTP, wbuffer, buflen); + } + return NoData; // already forwarded via TCP + } } #endif @@ -12256,13 +12336,23 @@ ProxySocket::Result UDPProxySocket::ReceiveData() if (IsSet(m_multiplexDestination_A) && (m_multiplexDestination_A != fromAddr)) { if (isRTCP && m_EnableRTCPStats && m_call && (*m_call)) ParseRTCP(*m_call, m_sessionID, fromIP, wbuffer, buflen); - H46019Session::Send(m_multiplexID_A, m_multiplexDestination_A, m_multiplexSocket_A, wbuffer, buflen); + PIPSocket::Address gkIP; + if ((*m_call)->GetEndpointIPMapping(m_multiplexDestination_A.GetIP(), gkIP)) { + H46019Session::Send(m_multiplexID_A, m_multiplexDestination_A, m_multiplexSocket_A, wbuffer, buflen, false, &gkIP); // JWX + } else { + H46019Session::Send(m_multiplexID_A, m_multiplexDestination_A, m_multiplexSocket_A, wbuffer, buflen, false, NULL); // JWX + } return NoData; // already forwarded through multiplex socket } if (IsSet(m_multiplexDestination_B) && (m_multiplexDestination_B != fromAddr)) { if (isRTCP && m_EnableRTCPStats && m_call && (*m_call)) ParseRTCP(*m_call, m_sessionID, fromIP, wbuffer, buflen); - H46019Session::Send(m_multiplexID_B, m_multiplexDestination_B, m_multiplexSocket_B, wbuffer, buflen); + PIPSocket::Address gkIP; + if ((*m_call)->GetEndpointIPMapping(m_multiplexDestination_B.GetIP(), gkIP)) { + H46019Session::Send(m_multiplexID_B, m_multiplexDestination_B, m_multiplexSocket_B, wbuffer, buflen, false, &gkIP); // JWX + } else { + H46019Session::Send(m_multiplexID_B, m_multiplexDestination_B, m_multiplexSocket_B, wbuffer, buflen, false, NULL); // JWX + } return NoData; // already forwarded through multiplex socket } @@ -12369,7 +12459,12 @@ ProxySocket::Result UDPProxySocket::ReceiveData() PIPSocket::Address toIP; WORD toPort = 0; GetSendAddress(toIP, toPort); - UDPSendWithSourceIP(os_handle, wbuffer, buflen, toIP, toPort); + PIPSocket::Address gkIP; + if (m_call && (*m_call) && (*m_call)->GetEndpointIPMapping(toIP, gkIP)) { + UDPSendWithSourceIP(os_handle, wbuffer, buflen, toIP, toPort, &gkIP); // JWX add gkIP + } else { + UDPSendWithSourceIP(os_handle, wbuffer, buflen, toIP, toPort, NULL); + } return NoData; // we just forwarded the data here } @@ -13837,6 +13932,7 @@ bool H245ProxyHandler::OnLogicalChannelParameters(H245_H2250LogicalChannelParame lc->SetMediaControlChannelSource(*addr); *addr << GetMasqAddr() << (lc->GetPort() + 1); // old RTP assumption + PTRACE(0, "JW OnLogicalChannelParameters setting mediaControl to " << AsString(GetMasqAddr())); #ifdef HAS_H46018 if (IsTraversalClient()) { PTRACE(5, "H46018\tSetting control channel to 0"); @@ -13873,6 +13969,7 @@ bool H245ProxyHandler::OnLogicalChannelParameters(H245_H2250LogicalChannelParame if (GetH245Port(*addr) != 0) { lc->SetMediaChannelSource(*addr); *addr << GetMasqAddr() << lc->GetPort(); + PTRACE(0, "JW OnLogicalChannelParameters setting media to " << AsString(GetMasqAddr())); #ifdef HAS_H46018 if (IsTraversalClient()) { PTRACE(5, "H46018\tSetting media channel to 0"); @@ -13900,6 +13997,7 @@ bool H245ProxyHandler::OnLogicalChannelParameters(H245_H2250LogicalChannelParame lc->ZeroMediaChannelSource(); }; } else { + PTRACE(0, "JW OnLogicalChannelParameters setting media to " << AsString(GetMasqAddr())); *addr << GetMasqAddr() << (WORD)0; } changed = true; @@ -14053,7 +14151,7 @@ bool H245ProxyHandler::HandleOpenLogicalChannel(H245_OpenLogicalChannel & olc, c if (h225Params->HasOptionalField(H245_H2250LogicalChannelParameters::e_transportCapability)) { // just remove media channel capabilities h225Params->m_transportCapability.RemoveOptionalField(H245_TransportCapability::e_mediaChannelCapabilities); - // remove transportCapability if it doesn't contain Q0S or nonStandard items + // remove transportCapability if it doesn't contain QOS or nonStandard items if (!h225Params->m_transportCapability.HasOptionalField(H245_TransportCapability::e_qOSCapabilities) && !h225Params->m_transportCapability.HasOptionalField(H245_TransportCapability::e_nonStandard)) { h225Params->RemoveOptionalField(H245_H2250LogicalChannelParameters::e_transportCapability); @@ -14112,8 +14210,23 @@ bool H245ProxyHandler::HandleOpenLogicalChannel(H245_OpenLogicalChannel & olc, c SNMP_TRAP(10, SNMPError, Network, "H.460.19 server didn't provide mediaControlChannel"); } if (keepAliveInterval > 0) { - call->AddRTPKeepAlive(flcn, keepAliveRTPAddr, keepAliveInterval, multiplexID); - call->AddRTCPKeepAlive(flcn, keepAliveRTCPAddr, keepAliveInterval, multiplexID); + PIPSocket::Address gkIP; + SetInvalid(gkIP); // so AddRTPKeepAlive() can detect if it was set + PIPSocket::Address epIP; + if (m_isCaller && call->GetCallingParty()) { + gkIP = call->GetCallingParty()->GetRasServerIP(); + if (GetIPFromTransportAddr(keepAliveRTPAddr, epIP)) { + call->SetEndpointIPMapping(epIP, gkIP); + } + } + if (!m_isCaller && call->GetCalledParty()) { + gkIP = call->GetCalledParty()->GetRasServerIP(); + if (GetIPFromTransportAddr(keepAliveRTPAddr, epIP)) { + call->SetEndpointIPMapping(epIP, gkIP); + } + } + call->AddRTPKeepAlive(flcn, keepAliveRTPAddr, keepAliveInterval, multiplexID, gkIP); + call->AddRTCPKeepAlive(flcn, keepAliveRTCPAddr, keepAliveInterval, multiplexID, gkIP); } m_remoteRequestsRTPMultiplexing = m_isRTPMultiplexingEnabled && (multiplexID != INVALID_MULTIPLEX_ID); if (m_requestRTPMultiplexing || m_remoteRequestsRTPMultiplexing) { diff --git a/ProxyChannel.h b/ProxyChannel.h index 5ab866cd..cc16226b 100644 --- a/ProxyChannel.h +++ b/ProxyChannel.h @@ -3,7 +3,7 @@ // ProxyChannel.h // // Copyright (c) Citron Network Inc. 2001-2003 -// Copyright (c) 2002-2020, Jan Willamowius +// Copyright (c) 2002-2021, Jan Willamowius // // This work is published under the GNU Public License version 2 (GPLv2) // see file COPYING for details. @@ -71,8 +71,8 @@ const WORD DEFAULT_PACKET_BUFFER_SIZE = 2048; void PrintQ931(int, const char *, const char *, const Q931 *, const H225_H323_UserInformation *); -ssize_t UDPSendWithSourceIP(int fd, void * data, size_t len, const IPAndPortAddress & toAddress); -ssize_t UDPSendWithSourceIP(int fd, void * data, size_t len, const PIPSocket::Address & ip, WORD port); +ssize_t UDPSendWithSourceIP(int fd, void * data, size_t len, const IPAndPortAddress & toAddress, PIPSocket::Address * gkIP); +ssize_t UDPSendWithSourceIP(int fd, void * data, size_t len, const PIPSocket::Address & ip, WORD port, PIPSocket::Address * gkIP); class ProxySocket : public USocket { @@ -705,7 +705,7 @@ class H46019Session static bool IsKeepAlive(unsigned len, bool isRTCP) { return isRTCP ? true : (len == 12); } void HandlePacket(DWORD receivedMultiplexID, const IPAndPortAddress & fromAddress, void * data, unsigned len, bool isRTCP); - static void Send(DWORD sendMultiplexID, const IPAndPortAddress & toAddress, int ossocket, void * data, unsigned len, bool bufferHasRoomForID = false); + static void Send(DWORD sendMultiplexID, const IPAndPortAddress & toAddress, int ossocket, void * data, unsigned len, bool bufferHasRoomForID, PIPSocket::Address * gkIP); public: bool m_deleted; // logically deleted, but still in list so other threads can leave methods diff --git a/RasSrv.cxx b/RasSrv.cxx index 9536de7b..c0ec946c 100644 --- a/RasSrv.cxx +++ b/RasSrv.cxx @@ -2,7 +2,7 @@ // // RAS Server for GNU Gatekeeper // -// Copyright (c) 2000-2020, Jan Willamowius +// Copyright (c) 2000-2021, Jan Willamowius // Copyright (c) Citron Network Inc. 2001-2003 // // This work is published under the GNU Public License version 2 (GPLv2) @@ -13,7 +13,9 @@ ////////////////////////////////////////////////////////////////// #include "config.h" -#include +#ifdef __GNU_LIBRARY__ +#include // for malloc_trim() +#endif #include #include #include @@ -2650,7 +2652,9 @@ bool RegistrationRequestPDU::Process() PTRACE(3, "RAS\tRRQ rejected by unknown reason from " << rx_addr); return BuildRRJ(H225_RegistrationRejectReason::e_undefinedReason); } - ep->SetRasServerIP(m_msg->m_localAddr); // remember which of our IPs the endpoint has sent the RRQ to (needed for H.460.18 SCI) + // remember which of our IPs the endpoint has sent the RRQ to (to keep all later signaling to this IP) + ep->SetRasServerIP(m_msg->m_localAddr); // JWX + PTRACE(0, "JW set rasserverip for " << m_msg->m_peerAddr << " to " << m_msg->m_localAddr); #ifdef HAS_H46017 if (usesH46017) { diff --git a/RasTbl.cxx b/RasTbl.cxx index f3ab9af6..ce3e143b 100644 --- a/RasTbl.cxx +++ b/RasTbl.cxx @@ -2,7 +2,7 @@ // // bookkeeping for RAS-Server in H.323 gatekeeper // -// Copyright (c) 2000-2020, Jan Willamowius +// Copyright (c) 2000-2021, Jan Willamowius // // This work is published under the GNU Public License version 2 (GPLv2) // see file COPYING for details. @@ -136,6 +136,8 @@ EndpointRec::EndpointRec( if (permanent) m_timeToLive = 0; + SetInvalid(m_rasServerIP); + LoadEndpointConfig(); } @@ -2621,7 +2623,7 @@ void H46019KeepAlive::SendKeepAlive(GkTimer * t) ka_ptr = (char*)&multiplexedRtpKeepAlive; ka_size = sizeof(multiplexedRtpKeepAlive); } - size_t sent = UDPSendWithSourceIP(ossocket, ka_ptr, ka_size, dest); + size_t sent = UDPSendWithSourceIP(ossocket, ka_ptr, ka_size, dest, gkIPptr); // JWX if (sent != ka_size) { PTRACE(1, "Error sending RTP keepAlive " << timer); SNMP_TRAP(10, SNMPError, Network, "Sending multiplexed RTP keepAlive failed"); @@ -2657,7 +2659,7 @@ void H46019KeepAlive::SendKeepAlive(GkTimer * t) ka_ptr = (char*)&multiplexedRtcpKeepAlive; ka_size = sizeof(multiplexedRtcpKeepAlive); } - size_t sent = UDPSendWithSourceIP(ossocket, ka_ptr, ka_size, dest); + size_t sent = UDPSendWithSourceIP(ossocket, ka_ptr, ka_size, dest, gkIPptr); // JWX if (sent != ka_size) { PTRACE(1, "Error sending RTCP keepAlive " << timer); SNMP_TRAP(10, SNMPError, Network, "Sending multiplexed RTCP keepAlive failed"); @@ -5080,7 +5082,7 @@ int CallRec::GetH46019Direction() const return dir; } -void CallRec::AddRTPKeepAlive(unsigned flcn, const IPAndPortAddress & keepAliveRTPAddr, unsigned keepAliveInterval, DWORD multiplexID) +void CallRec::AddRTPKeepAlive(unsigned flcn, const IPAndPortAddress & keepAliveRTPAddr, unsigned keepAliveInterval, DWORD multiplexID, PIPSocket::Address gkIP) { H46019KeepAlive ka; ka.type = RTP; @@ -5089,6 +5091,12 @@ void CallRec::AddRTPKeepAlive(unsigned flcn, const IPAndPortAddress & keepAliveR ka.interval = keepAliveInterval; ka.multiplexID = multiplexID; m_RTPkeepalives[flcn] = ka; + ka.gkIP = gkIP; + if (gkIP.IsValid()) { + ka.gkIPptr = &gkIP; + } else { + ka.gkIPptr = NULL; + } } void CallRec::SetRTPKeepAlivePayloadType(unsigned flcn, BYTE payloadType) @@ -5114,7 +5122,7 @@ void CallRec::StartRTPKeepAlive(unsigned flcn, int RTPOSSocket) } } -void CallRec::AddRTCPKeepAlive(unsigned flcn, const H245_UnicastAddress & keepAliveRTCPAddr, unsigned keepAliveInterval, DWORD multiplexID) +void CallRec::AddRTCPKeepAlive(unsigned flcn, const H245_UnicastAddress & keepAliveRTCPAddr, unsigned keepAliveInterval, DWORD multiplexID, PIPSocket::Address gkIP) { H46019KeepAlive ka; ka.type = RTCP; @@ -5123,6 +5131,12 @@ void CallRec::AddRTCPKeepAlive(unsigned flcn, const H245_UnicastAddress & keepAl ka.interval = keepAliveInterval; ka.multiplexID = multiplexID; m_RTCPkeepalives[flcn] = ka; + ka.gkIP = gkIP; + if (gkIP.IsValid()) { + ka.gkIPptr = &gkIP; + } else { + ka.gkIPptr = NULL; + } } void CallRec::StartRTCPKeepAlive(unsigned flcn, int RTCPOSSocket) @@ -5199,6 +5213,30 @@ void CallRec::AbortLogicalChannel(short session) } } +// which gatekeeper IP to use for sending to endpoint IP +void CallRec::SetEndpointIPMapping(PIPSocket::Address epIP, PIPSocket::Address gkIP) +{ + PWaitAndSignal lock(m_endpointIPMappingMutex); + + std::map::iterator it = m_endpointIPMapping.find(epIP); + if (it == m_endpointIPMapping.end()) { + PTRACE(0, "JW add mapping " << AsString(epIP) << " <=> " << AsString(gkIP)); + m_endpointIPMapping[epIP] = gkIP; + } +} + +bool CallRec::GetEndpointIPMapping(PIPSocket::Address epIP, PIPSocket::Address & gkIP) const +{ + PWaitAndSignal lock(m_endpointIPMappingMutex); + + std::map::const_iterator it = m_endpointIPMapping.find(epIP); + if (it != m_endpointIPMapping.end()) { + gkIP = it->second; + return true; + } + return false; +} + /* bool CallRec::IsTimeout( const time_t now, diff --git a/RasTbl.h b/RasTbl.h index 04ffccaa..1a73874f 100644 --- a/RasTbl.h +++ b/RasTbl.h @@ -2,7 +2,7 @@ // // bookkeeping for RAS-Server in H.323 gatekeeper // -// Copyright (c) 2000-2019, Jan Willamowius +// Copyright (c) 2000-2021, Jan Willamowius // // This work is published under the GNU Public License version 2 (GPLv2) // see file COPYING for details. @@ -117,7 +117,8 @@ class EndpointRec H225_TransportAddress GetCallSignalAddress() const; PIPSocket::Address GetIP() const; // for multi-homed servers: the IP that the endpoint sent his RAS messages to (needed for H.460.18 SCI) - PIPSocket::Address GetRasServerIP() const { return m_rasServerIP; } + PIPSocket::Address GetRasServerIP() const { return m_rasServerIP; } // caller need to check for IsValid() + //bool GetRasServerIP(PIPSocket::Address & ip) const { ip = m_rasServerIP; return m_rasServerIP.IsValid(); } // JWX H225_EndpointIdentifier GetEndpointIdentifier() const; H225_ArrayOf_AliasAddress GetAliases() const; H225_EndpointType GetEndpointType() const; @@ -692,6 +693,8 @@ class H46019KeepAlive int ossocket; DWORD multiplexID; BYTE payloadType; + PIPSocket::Address gkIP; // source IP + PIPSocket::Address * gkIPptr; // source IP pointer if valid GkTimerManager::GkTimerHandle timer; }; #endif @@ -1438,10 +1441,10 @@ class CallRec { PBYTEArray RetrieveSetup(); int GetH46019Direction() const; - void AddRTPKeepAlive(unsigned flcn, const IPAndPortAddress & keepAliveRTPAddr, unsigned keepAliveInterval, DWORD multiplexID); + void AddRTPKeepAlive(unsigned flcn, const IPAndPortAddress & keepAliveRTPAddr, unsigned keepAliveInterval, DWORD multiplexID, PIPSocket::Address gkIP); void SetRTPKeepAlivePayloadType(unsigned flcn, BYTE payloadType); void StartRTPKeepAlive(unsigned flcn, int RTPOSSocket); - void AddRTCPKeepAlive(unsigned flcn, const H245_UnicastAddress & keepAliveRTCPAddr, unsigned keepAliveInterval, DWORD multiplexID); + void AddRTCPKeepAlive(unsigned flcn, const H245_UnicastAddress & keepAliveRTCPAddr, unsigned keepAliveInterval, DWORD multiplexID, PIPSocket::Address gkIP); void StartRTCPKeepAlive(unsigned flcn, int RTCPOSSocket); void RemoveRTPKeepAlives(unsigned flcn); void RemoveAllRTPKeepAlives(); @@ -1469,6 +1472,10 @@ class CallRec { bool IsRTPInactive(short session) const; void AbortLogicalChannel(short session); + // which gatekeeper IP to use for sending to endpoint IP + void SetEndpointIPMapping(PIPSocket::Address epIP, PIPSocket::Address gkIP); + bool GetEndpointIPMapping(PIPSocket::Address epIP, PIPSocket::Address & gkIP) const; + private: void SendDRQ(); void InternalSetEP(endptr &, const endptr &); @@ -1734,6 +1741,9 @@ class CallRec { // Sorenson SInfo PString m_sinfoIP; vector m_channelFlcnList; // list of all channel Flcn every _tried_ to open so we can close them on Reroute + + std::map m_endpointIPMapping; + PMutex m_endpointIPMappingMutex; }; typedef CallRec::Ptr callptr; diff --git a/changes.txt b/changes.txt index 26a46a08..957ae945 100644 --- a/changes.txt +++ b/changes.txt @@ -1,5 +1,6 @@ Changes from 5.7 to 5.8 ======================= +- BUGFIX(ProxyChannel.cxx) fix H.460.18/.19 on multi-homed servers - BUGFIX(job.cxx) better endpointIDs on Windows when compiling without OpenSSL diff --git a/h323util.cxx b/h323util.cxx index f1cb6ccd..b94b9fc4 100644 --- a/h323util.cxx +++ b/h323util.cxx @@ -2,7 +2,7 @@ // // H.323 utility functions // -// Copyright (c) 2000-2019, Jan Willamowius +// Copyright (c) 2000-2021, Jan Willamowius // // This work is published under the GNU Public License version 2 (GPLv2) // see file COPYING for details. @@ -731,6 +731,11 @@ bool IsPrivate(const PIPSocket::Address & ip) return result; } +void SetInvalid(PIPSocket::Address & ip) +{ + ip = INADDR_ANY; // detected as !IsValid() +} + // is this IP part of this network bool IsInNetwork(const PIPSocket::Address & ip, const NetworkAddress & net) { diff --git a/h323util.h b/h323util.h index c19a7368..f904aaba 100644 --- a/h323util.h +++ b/h323util.h @@ -2,7 +2,7 @@ // // H.323 utility functions // -// Copyright (c) 2000-2019, Jan Willamowius +// Copyright (c) 2000-2021, Jan Willamowius // // This work is published under the GNU Public License version 2 (GPLv2) // see file COPYING for details. @@ -141,6 +141,9 @@ bool IsLoopback(const PIPSocket::Address & addr); bool IsPrivate(const PIPSocket::Address & ip); +// set IP to an invalid state, check with IsValid() +void SetInvalid(PIPSocket::Address & ip); + // is this IP part of this network bool IsInNetwork(const PIPSocket::Address & ip, const NetworkAddress & net); diff --git a/yasocket.cxx b/yasocket.cxx index ebbc1f41..214e336e 100644 --- a/yasocket.cxx +++ b/yasocket.cxx @@ -3,7 +3,7 @@ // yasocket.cxx // // Copyright (c) Citron Network Inc. 2002-2003 -// Copyright (c) 2004-2019, Jan Willamowius +// Copyright (c) 2004-2021, Jan Willamowius // // This work is published under the GNU Public License version 2 (GPLv2) // see file COPYING for details. @@ -189,26 +189,27 @@ PBoolean YaSocket::GetLocalAddress(Address & addr) const return GetLocalAddress(addr, pt); } +// now returns the IP used by the client for sockets bound to INADDR_ANY, unlike PTLib PBoolean YaSocket::GetLocalAddress(Address & addr, WORD & pt) const { #ifdef hasIPV6 - sockaddr_in6 inaddr; + sockaddr_in6 inaddr; #else - sockaddr_in inaddr; + sockaddr_in inaddr; #endif - socklen_t insize = sizeof(inaddr); - if (::getsockname(os_handle, (struct sockaddr*)&inaddr, &insize) == 0) { + socklen_t insize = sizeof(inaddr); + if (::getsockname(os_handle, (struct sockaddr*)&inaddr, &insize) == 0) { #ifdef hasIPV6 - if (((struct sockaddr*)&inaddr)->sa_family == AF_INET6) { - addr = ((struct sockaddr_in6*)&inaddr)->sin6_addr; - pt = ntohs(((struct sockaddr_in6*)&inaddr)->sin6_port); - } else + if (((struct sockaddr*)&inaddr)->sa_family == AF_INET6) { + addr = ((struct sockaddr_in6*)&inaddr)->sin6_addr; + pt = ntohs(((struct sockaddr_in6*)&inaddr)->sin6_port); + } else #endif - { - addr = ((struct sockaddr_in*)&inaddr)->sin_addr; - pt = ntohs(((struct sockaddr_in*)&inaddr)->sin_port); - } - } + { + addr = ((struct sockaddr_in*)&inaddr)->sin_addr; + pt = ntohs(((struct sockaddr_in*)&inaddr)->sin_port); + } + } return true; } @@ -519,6 +520,7 @@ YaUDPSocket::YaUDPSocket(WORD port, int iAddressFamily) memset(&recvaddr, 0, sizeof(recvaddr)); ((struct sockaddr*)&sendaddr)->sa_family = iAddressFamily; ((struct sockaddr_in*)&sendaddr)->sin_port = htons(port); + SetInvalid(lastDestAddress); } bool YaUDPSocket::Listen(unsigned, WORD pt, PSocket::Reusability reuse) @@ -608,7 +610,96 @@ bool YaUDPSocket::WriteTo(const void * buf, PINDEX len, const Address & addr, WO int YaUDPSocket::os_recv(void * buf, int sz) { socklen_t addrlen = sizeof(recvaddr); +#if !defined(IP_PKTINFO) && !defined(IP_RECVDSTADDR) return ::recvfrom(os_handle, (char *)buf, sz, 0, (struct sockaddr *)&recvaddr, &addrlen); +#endif + +#if defined(IP_PKTINFO) || defined(IP_RECVDSTADDR) + // TODO: move setsockopts right after socket creation and do it only once ? + int yes = 1; + int e = 0; +#ifdef IP_PKTINFO + // Linux + e = setsockopt(os_handle, IPPROTO_IP, IP_PKTINFO, &yes, sizeof(yes)); +#else +#ifdef IP_RECVDSTADDR + // FreeBSD (also *BSD, MacOS X ?) + e = setsockopt(os_handle, IPPROTO_IP, IP_RECVDSTADDR, &yes, sizeof(yes)); +#endif +#endif + if (e != 0) { + PTRACE(1, "Error: setsockopt IP_PKTINFO=" << errno); + } +#ifdef hasIPV6 + if (Toolkit::Instance()->IsIPv6Enabled()) { +#ifdef IPV6_RECVPKTINFO + // Linux + e = setsockopt(os_handle, IPPROTO_IPV6, IPV6_RECVPKTINFO, &yes, sizeof(yes)); +#else +#ifdef IPV6_PKTINFO + // Solaris (also BSD, Windows ?) + e = setsockopt(os_handle, IPPROTO_IPV6, IPV6_PKTINFO, &yes, sizeof(yes)); +#endif +#endif + if (e != 0) { + PTRACE(1, "Error: setsockopt IPV6_PKTINFO=" << errno); + } + } +#endif // hasIPV6 + struct iovec vec; + const size_t CONTROL_DATA_SIZE = 1024; + char cmsg[CONTROL_DATA_SIZE]; + struct msghdr hdr = {}; + + vec.iov_base = buf; + vec.iov_len = sz; + + hdr.msg_name = &recvaddr; + hdr.msg_namelen = addrlen; + hdr.msg_iov = &vec; + hdr.msg_iovlen = 1; + hdr.msg_control = cmsg; + hdr.msg_controllen = sizeof(cmsg); + + int result = ::recvmsg(os_handle, &hdr, 0); + PIPSocket::Address raddr; + WORD rpt; + GetLastReceiveAddress(raddr, rpt); + PTRACE(0, "JW recvmsg=" << result << " ep ip=" << AsString(raddr, rpt)); + + for ( // iterate through all control headers + struct cmsghdr *cmsg = CMSG_FIRSTHDR(&hdr); + cmsg != NULL; + cmsg = CMSG_NXTHDR(&hdr, cmsg)) + { +#ifdef IP_PKTINFO + PTRACE(0, "JW found CMSG type=" << cmsg->cmsg_type << " IP_PKTINFO=" << IP_PKTINFO << " IPV6_PKTINFO=" << IPV6_PKTINFO); + if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) { + struct in_pktinfo * pi = (struct in_pktinfo *)CMSG_DATA(cmsg); + // pi->ipi_addr is our IP that the endpoint sent to (in_addr) + lastDestAddress = pi->ipi_addr; + PTRACE(0, "JW IP_PKTINFO lastDestAddress=" << AsString(lastDestAddress)); + } +#endif +#ifdef IP_RECVDSTADDR + PTRACE(0, "JW found CMSG type=" << cmsg->cmsg_type << " IP_RECVDSTADDR=" << IP_RECVDSTADDR); + if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_RECVDSTADDR) { + struct in_addr * i = (struct in_addr *)CMSG_DATA(cmsg); + lastDestAddress = *i; + PTRACE(0, "JW IP_RECVDSTADDR lastDestAddress=" << AsString(lastDestAddress)); + } +#endif +#if defined(hasIPV6) && defined (IPV6_PKTINFO) + if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) { + struct in6_pktinfo * pi = (struct in6_pktinfo *)CMSG_DATA(cmsg); + // pi->ipi_addr is our IP that the endpoint sent to (in_addr) + lastDestAddress = pi->ipi6_addr; + PTRACE(0, "JW IPV6_PKTINFO lastDestAddress=" << AsString(lastDestAddress)); + } +#endif + } + return result; +#endif } int YaUDPSocket::os_send(const void * buf, int sz) diff --git a/yasocket.h b/yasocket.h index 93a0f949..0e406279 100644 --- a/yasocket.h +++ b/yasocket.h @@ -3,7 +3,7 @@ // yasocket.h // // Copyright (c) Citron Network Inc. 2002-2003 -// Copyright (c) 2004-2019, Jan Willamowius +// Copyright (c) 2004-2021, Jan Willamowius // // This work is published under the GNU Public License version 2 (GPLv2) // see file COPYING for details. @@ -140,6 +140,8 @@ class YaUDPSocket : public YaSocket, public PObject { virtual bool ReadFrom(void *, PINDEX, Address &, WORD); virtual bool WriteTo(const void *, PINDEX, const Address &, WORD); + virtual PBoolean GetLastDestAddress(Address & addr) const { addr = lastDestAddress; return true; } + protected: // override from class YaSocket virtual int os_recv(void *, int); @@ -155,6 +157,7 @@ class YaUDPSocket : public YaSocket, public PObject { #else sockaddr_in recvaddr, sendaddr; #endif + Address lastDestAddress; }; class YaSelectList {