reSIProcate
SIP stack, Dialog Usage Manager and related libraries
Loading...
Searching...
No Matches
TurnAllocation.cxx
Go to the documentation of this file.
1#include "TurnAllocation.hxx"
3#include "TurnManager.hxx"
4#include "TurnPermission.hxx"
5#include "AsyncSocketBase.hxx"
6#include "UdpRelayServer.hxx"
7#include "RemotePeer.hxx"
9#include <rutil/Logger.hxx>
10#include "ReTurnSubsystem.hxx"
11
12#include <functional>
13
14#define RESIPROCATE_SUBSYSTEM ReTurnSubsystem::RETURN
15
16using namespace std;
17using namespace resip;
18
19#define TURN_PERMISSION_LIFETIME_SECONDS 300 // 5 minuntes
20//#define TURN_PERMISSION_LIFETIME_SECONDS 30 // TESTING only
21
22#ifdef BOOST_ASIO_HAS_STD_CHRONO
23using namespace std::chrono;
24#else
25#include <chrono>
26using namespace std::chrono;
27#endif
28
29namespace reTurn {
30
32 TurnAllocationManager& turnAllocationManager,
33 AsyncSocketBase* localTurnSocket,
34 const StunTuple& clientLocalTuple,
35 const StunTuple& clientRemoteTuple,
36 const StunAuth& clientAuth,
37 const StunTuple& requestedTuple,
38 unsigned int lifetime) :
39 mKey(clientLocalTuple, clientRemoteTuple),
40 mClientAuth(clientAuth),
41 mRequestedTuple(requestedTuple),
42 mTurnManager(turnManager),
43 mTurnAllocationManager(turnAllocationManager),
44 mAllocationTimer(turnManager.getIOService()),
45 mLocalTurnSocket(localTurnSocket),
46 mBadChannelErrorLogged(false),
47 mNoPermissionToPeerLogged(false),
48 mNoPermissionFromPeerLogged(false)
49{
50 InfoLog(<< "TurnAllocation created: clientLocal=" << clientLocalTuple << " clientRemote=" <<
51 clientRemoteTuple << " allocation=" << requestedTuple << " lifetime=" << lifetime);
52
53 refresh(lifetime);
54
55 // Register for Turn Transport onDestroyed notification
56 mLocalTurnSocket->registerAsyncSocketBaseHandler(this);
57
58 // Counted against the user's quota until destroyed
59 mTurnManager.addAllocation(mClientAuth.getClientUsername());
60}
61
63{
64 // Destructors are implicitly noexcept, so any exception escaping here would
65 // call std::terminate. Swallow anything thrown by logging or cleanup.
66 try
67 {
68 InfoLog(<< "TurnAllocation destroyed: clientLocal=" << mKey.getClientLocalTuple() << " clientRemote=" <<
69 mKey.getClientRemoteTuple() << " allocation=" << mRequestedTuple);
70
71 stopRelay();
72
73 // Deallocate Port
74 mTurnManager.deallocatePort(mRequestedTuple.getTransportType(), mRequestedTuple.getPort());
75 mTurnManager.removeAllocation(mClientAuth.getClientUsername());
76
77 // Cleanup Permission Memory
78 TurnPermissionMap::iterator it;
79 for(it = mTurnPermissionMap.begin(); it != mTurnPermissionMap.end(); it++)
80 {
81 delete it->second;
82 }
83
84 // Unregister for TurnTransport notifications
85 if(mLocalTurnSocket)
86 {
87 mLocalTurnSocket->registerAsyncSocketBaseHandler(0);
88 }
89 }
90 catch (...)
91 {
92 }
93}
94
95bool
97{
98 if(mRequestedTuple.getTransportType() == StunTuple::UDP)
99 {
100 mUdpRelayServer = std::make_shared<UdpRelayServer>(mTurnManager.getIOService(), *this);
101 if(!mUdpRelayServer->startReceiving())
102 {
103 stopRelay(); // Ensure allocation timer is stopped
104 return false;
105 }
106 return true;
107 }
108 else
109 {
110 ErrLog(<< "Only UDP relay's are currently implemented!");
111 resip_assert(false);
112 stopRelay(); // Ensure allocation timer is stopped
113 return false;
114 }
115}
116
117void
119{
120 // Stop and detach Relay Server
121 if (mUdpRelayServer)
122 {
123 mUdpRelayServer->stop();
124 mUdpRelayServer = nullptr;
125 }
126 mAllocationTimer.cancel();
127}
128
129void
130TurnAllocation::refresh(unsigned int lifetime) // update expiration time
131{
132 InfoLog(<< "TurnAllocation refreshed: clientLocal=" << mKey.getClientLocalTuple() << " clientRemote=" <<
133 mKey.getClientRemoteTuple() << " allocation=" << mRequestedTuple << " lifetime=" << lifetime);
134
135 mExpires = time(0) + lifetime;
136
137 // start timer
138 mAllocationTimer.expires_after(seconds(lifetime));
139 mAllocationTimer.async_wait(std::bind(&TurnAllocationManager::allocationExpired, &mTurnAllocationManager, std::placeholders::_1, mKey));
140}
141
142bool
143TurnAllocation::existsPermission(const asio::ip::address& address)
144{
145 TurnPermissionMap::iterator it = mTurnPermissionMap.find(address);
146 if(it != mTurnPermissionMap.end())
147 {
148 if(it->second->isExpired()) // check if expired
149 {
150 InfoLog(<< "TurnAllocation has expired permission: clientLocal=" << mKey.getClientLocalTuple() << " clientRemote=" <<
151 mKey.getClientRemoteTuple() << " allocation=" << mRequestedTuple << " exipred address=" << it->first.to_string());
152 delete it->second;
153 mTurnPermissionMap.erase(it);
154 return false;
155 }
156 return true;
157 }
158 return false;
159}
160
161void
162TurnAllocation::refreshPermission(const asio::ip::address& address)
163{
164 TurnPermissionMap::iterator it = mTurnPermissionMap.find(address);
165 TurnPermission* turnPermission = 0;
166 if(it != mTurnPermissionMap.end())
167 {
168 turnPermission = it->second;
169 }
170 if(!turnPermission) // create if doesn't exist
171 {
172 mTurnPermissionMap[address] = new TurnPermission(address, TURN_PERMISSION_LIFETIME_SECONDS);
173 InfoLog(<< "Permission for " << address.to_string() << " created: clientLocal=" << mKey.getClientLocalTuple() << " clientRemote=" <<
174 mKey.getClientRemoteTuple() << " allocation=" << mRequestedTuple);
175 }
176 else
177 {
178 turnPermission->refresh();
179 InfoLog(<< "Permission for " << address.to_string() << " refreshed: clientLocal=" << mKey.getClientLocalTuple() << " clientRemote=" <<
180 mKey.getClientRemoteTuple() << " allocation=" << mRequestedTuple);
181 }
182}
183
184void
186{
187 mTurnAllocationManager.removeTurnAllocation(mKey); // will delete this
188}
189
190void
191TurnAllocation::sendDataToPeer(unsigned short channelNumber, const std::shared_ptr<DataBuffer>& data, bool isFramed)
192{
193 RemotePeer* remotePeer = mChannelManager.findRemotePeerByChannel(channelNumber);
194 if(remotePeer)
195 {
196 // channel found - send Data
197 sendDataToPeer(remotePeer->getPeerTuple(), data, isFramed);
198 }
199 else
200 {
201 // Log at Warning level first time only
202 if(mBadChannelErrorLogged)
203 {
204 DebugLog(<< "sendDataToPeer bad channel number - discarding data: clientLocal=" << mKey.getClientLocalTuple() << " clientRemote=" <<
205 mKey.getClientRemoteTuple() << " allocation=" << mRequestedTuple << " channelNumber=" << channelNumber);
206 }
207 else
208 {
209 mBadChannelErrorLogged = true;
210 WarningLog(<< "sendDataToPeer bad channel number - discarding data: clientLocal=" << mKey.getClientLocalTuple() << " clientRemote=" <<
211 mKey.getClientRemoteTuple() << " allocation=" << mRequestedTuple << " channelNumber=" << channelNumber);
212 }
213 }
214}
215
216void
217TurnAllocation::sendDataToPeer(const StunTuple& peerAddress, const std::shared_ptr<DataBuffer>& data, bool isFramed)
218{
219 DebugLog(<< "TurnAllocation sendDataToPeer: clientLocal=" << mKey.getClientLocalTuple() << " clientRemote=" <<
220 mKey.getClientRemoteTuple() << " allocation=" << mRequestedTuple << " peerAddress=" << peerAddress);
221
222 // Ensure permission exists
223 if(!existsPermission(peerAddress.getAddress()))
224 {
225 // Log at Warning level first time only
226 if(mNoPermissionToPeerLogged)
227 {
228 DebugLog(<< "Turn send indication for destination=" << peerAddress.getAddress() << ", but no permission installed. Dropping.");
229 }
230 else
231 {
232 mNoPermissionToPeerLogged = true;
233 WarningLog(<< "Turn send indication for destination=" << peerAddress.getAddress() << ", but no permission installed. Dropping.");
234 }
235 return;
236 }
237 if(mRequestedTuple.getTransportType() == StunTuple::UDP)
238 {
239 resip_assert(mUdpRelayServer);
240 mUdpRelayServer->doSend(peerAddress, data, isFramed ? 4 /* bufferStartPos is 4 so that framing is skipped */ : 0);
241 }
242 else
243 {
244 if(data->size() <=4)
245 {
246 WarningLog(<< "Turn send indication with no data for non-UDP transport. Dropping.");
247 return;
248 }
249 // !SLG! TODO - implement TCP relays
250 resip_assert(false);
251 }
252}
253
254void
255TurnAllocation::sendDataToClient(const StunTuple& peerAddress, const std::shared_ptr<DataBuffer>& data)
256{
257 // See if a permission exists
258 if(!existsPermission(peerAddress.getAddress()))
259 {
260 // Log at Warning level first time only
261 if(mNoPermissionFromPeerLogged)
262 {
263 DebugLog(<< "Data received from peer=" << peerAddress << ", but no permission installed. Dropping.");
264 }
265 else
266 {
267 mNoPermissionFromPeerLogged = true;
268 WarningLog(<< "Data received from peer=" << peerAddress << ", but no permission installed. Dropping.");
269 }
270 return;
271 }
272 // See if a channel binding exists - if so, use it
273 RemotePeer* remotePeer = mChannelManager.findRemotePeerByPeerAddress(peerAddress);
274 if(remotePeer)
275 {
276 // send data to local client
277 mLocalTurnSocket->doSend(mKey.getClientRemoteTuple(), remotePeer->getChannel(), data);
278
279 DebugLog(<< "TurnAllocation sendDataToClient: clientLocal=" << mKey.getClientLocalTuple() << " clientRemote=" <<
280 mKey.getClientRemoteTuple() << " allocation=" << mRequestedTuple << " peer=" << peerAddress <<
281 " channelNumber=" << (int)remotePeer->getChannel());
282 }
283 else
284 {
285 // No Channel Binding - use DataInd
286 StunMessage dataInd;
288 dataInd.mCntTurnXorPeerAddress = 1;
290 dataInd.setTurnData(data->data(), (unsigned int)data->size());
291
292 // send DataInd to local client
293 unsigned int bufferSize = (unsigned int)data->size() + 8 /* Stun Header */ + 36 /* Remote Address (v6) */ + 8 /* TurnData Header + potential pad */;
294 const auto buffer = AsyncSocketBase::allocateBuffer(bufferSize);
295 unsigned int size = dataInd.stunEncodeMessage((char*)buffer->data(), bufferSize);
296 buffer->truncate(size); // Set size to proper size
297 mLocalTurnSocket->doSend(mKey.getClientRemoteTuple(), buffer);
298
299 DebugLog(<< "TurnAllocation sendDataToClient: clientLocal=" << mKey.getClientLocalTuple() << " clientRemote=" <<
300 mKey.getClientRemoteTuple() << " allocation=" << mRequestedTuple << " peer=" << peerAddress <<
301 " using DataInd.");
302 }
303}
304
305bool
306TurnAllocation::addChannelBinding(const StunTuple& peerAddress, unsigned short channelNumber)
307{
308 RemotePeer* remotePeer = mChannelManager.findRemotePeerByChannel(channelNumber);
309 if(remotePeer)
310 {
311 if(remotePeer->getPeerTuple() != peerAddress)
312 {
313 WarningLog(<< "addChannelBinding failed since channel is already in use: clientLocal=" << mKey.getClientLocalTuple() << " clientRemote=" <<
314 mKey.getClientRemoteTuple() << " allocation=" << mRequestedTuple << " channelNumber=" << channelNumber << " peerAddress=" << peerAddress);
315 return false;
316 }
317 // refresh channel binding lifetime
318 remotePeer->refresh();
319
320 InfoLog(<< "Channel " << channelNumber << " binding to " << peerAddress << " refreshed: clientLocal=" << mKey.getClientLocalTuple() << " clientRemote=" <<
321 mKey.getClientRemoteTuple() << " allocation=" << mRequestedTuple);
322 }
323 else
324 {
325 remotePeer = mChannelManager.findRemotePeerByPeerAddress(peerAddress);
326 if(remotePeer)
327 {
328 WarningLog(<< "addChannelBinding failed since peerAddress is alredy in use: clientLocal=" << mKey.getClientLocalTuple() << " clientRemote=" <<
329 mKey.getClientRemoteTuple() << " allocation=" << mRequestedTuple << " channelNumber=" << channelNumber << " peerAddress=" << peerAddress);
330 return false;
331 }
332 mChannelManager.createChannelBinding(peerAddress, channelNumber);
333
334 InfoLog(<< "Channel " << channelNumber << " binding to " << peerAddress << " created: clientLocal=" << mKey.getClientLocalTuple() << " clientRemote=" <<
335 mKey.getClientRemoteTuple() << " allocation=" << mRequestedTuple);
336 }
337
338 // Add or refresh permission
339 refreshPermission(peerAddress.getAddress());
340
341 return true;
342}
343
344
345} // namespace
346
347
348/* ====================================================================
349
350 Copyright (c) 2008-2026 SIP Spectrum, Inc. https://www.sipspectrum.com
351 Copyright (c) 2007-2008, Plantronics, Inc.
352 All rights reserved.
353
354 Redistribution and use in source and binary forms, with or without
355 modification, are permitted provided that the following conditions are
356 met:
357
358 1. Redistributions of source code must retain the above copyright
359 notice, this list of conditions and the following disclaimer.
360
361 2. Redistributions in binary form must reproduce the above copyright
362 notice, this list of conditions and the following disclaimer in the
363 documentation and/or other materials provided with the distribution.
364
365 3. Neither the name of Plantronics nor the names of its contributors
366 may be used to endorse or promote products derived from this
367 software without specific prior written permission.
368
369 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
370 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
371 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
372 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
373 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
374 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
375 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
376 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
377 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
378 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
379 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
380
381 ==================================================================== */
#define DebugLog(args_)
Definition Logger.hxx:128
#define InfoLog(args_)
Definition Logger.hxx:131
#define WarningLog(args_)
Definition Logger.hxx:134
#define ErrLog(args_)
Definition Logger.hxx:137
#define resip_assert(x)
This file needs to work for both C and C++ code.
#define TURN_PERMISSION_LIFETIME_SECONDS
static std::shared_ptr< DataBuffer > allocateBuffer(size_t size)
Utility API.
virtual void doSend(const StunTuple &destination, unsigned short channel, const std::shared_ptr< DataBuffer > &data, const std::size_t bufferStartPos=0)
Use these if you already operating within the ioService thread.
virtual void registerAsyncSocketBaseHandler(AsyncSocketBaseHandler *handler)
RemotePeer * findRemotePeerByChannel(unsigned short channelNumber)
RemotePeer * findRemotePeerByPeerAddress(const StunTuple &peerAddress)
RemotePeer * createChannelBinding(const StunTuple &peerTuple)
unsigned short getChannel() const
const StunTuple & getPeerTuple() const
const resip::Data & getClientUsername() const
Definition StunAuth.hxx:14
void setTurnData(const char *data, unsigned int len)
void createHeader(uint16_t stunclass, uint16_t method)
StunAtrAddress mTurnXorPeerAddress[TURN_MAX_XOR_PEER_ADDR]
static const uint16_t TurnDataMethod
static void setStunAtrAddressFromTuple(StunAtrAddress &address, const StunTuple &tuple)
unsigned int stunEncodeMessage(char *buf, unsigned int bufLen)
static const uint16_t StunClassIndication
TransportType getTransportType() const
Definition StunTuple.hxx:41
const asio::ip::address & getAddress() const
Definition StunTuple.hxx:44
unsigned int getPort() const
Definition StunTuple.hxx:47
const StunTuple & getClientRemoteTuple() const
const StunTuple & getClientLocalTuple() const
void allocationExpired(const asio::error_code &e, const TurnAllocationKey &turnAllocationKey)
void removeTurnAllocation(const TurnAllocationKey &turnAllocationKey)
bool addChannelBinding(const StunTuple &peerAddress, unsigned short channelNumber)
void sendDataToPeer(unsigned short channelNumber, const std::shared_ptr< DataBuffer > &data, bool isFramed)
void onSocketDestroyed() override
TurnAllocation(TurnManager &turnManager, TurnAllocationManager &turnAllocationManager, AsyncSocketBase *localTurnSocket, const StunTuple &clientLocalTuple, const StunTuple &clientRemoteTuple, const StunAuth &clientAuth, const StunTuple &requestedTuple, unsigned int lifetime)
void refresh(unsigned int lifetime)
void sendDataToClient(const StunTuple &peerAddress, const std::shared_ptr< DataBuffer > &data)
bool existsPermission(const asio::ip::address &address)
void refreshPermission(const asio::ip::address &address)
void removeAllocation(const resip::Data &username)
asio::io_context & getIOService()
void deallocatePort(StunTuple::TransportType transport, unsigned short port)
void addAllocation(const resip::Data &username)
dcm! – add UnusedChecking(_enum) below;