reSIProcate
SIP stack, Dialog Usage Manager and related libraries
Loading...
Searching...
No Matches
TurnAsyncSocket.cxx
Go to the documentation of this file.
1#include "TurnAsyncSocket.hxx"
2#include "../AsyncSocketBase.hxx"
3#include "ErrorCode.hxx"
4#include <rutil/compat.hxx>
6#include <rutil/Logger.hxx>
7#include "../ReTurnSubsystem.hxx"
8
9#include <utility>
10#include <functional>
11
12#define RESIPROCATE_SUBSYSTEM ReTurnSubsystem::RETURN
13
14using namespace std;
15using namespace resip;
16
17//#define TURN_CHANNEL_BINDING_REFRESH_SECONDS 20 // TESTING only
18#define TURN_CHANNEL_BINDING_REFRESH_SECONDS 240 // 4 minuntes - this is one minute before the permission will expire, Note: ChannelBinding refreshes also refresh permissions
19#define SOFTWARE_STRING "reTURN Async Client 0.3 - RFC5389/turn-12 " // Note padding size to a multiple of 4, to help compatibility with older clients
20
21#ifdef BOOST_ASIO_HAS_STD_CHRONO
22using namespace std::chrono;
23#else
24#include <chrono>
25using namespace std::chrono;
26#endif
27
28namespace reTurn {
29
30// Initialize static members
31unsigned int TurnAsyncSocket::UnspecifiedLifetime = 0xFFFFFFFF;
32unsigned int TurnAsyncSocket::UnspecifiedBandwidth = 0xFFFFFFFF;
33unsigned short TurnAsyncSocket::UnspecifiedToken = 0;
35
36TurnAsyncSocket::TurnAsyncSocket(asio::io_context& ioService,
37 AsyncSocketBase& asyncSocketBase,
38 TurnAsyncSocketHandler* turnAsyncSocketHandler,
39 const asio::ip::address& address,
40 unsigned short port) :
41 mIOService(ioService),
42 mTurnAsyncSocketHandler(turnAsyncSocketHandler),
43 mLocalBinding(StunTuple::None /* Set properly by sub class */, address, port),
44 mSoftware(SOFTWARE_STRING),
45 mHaveAllocation(false),
46 mActiveDestination(0),
47 mAsyncSocketBase(asyncSocketBase),
48 mCloseAfterDestroyAllocationFinishes(false),
49 mAllocationTimer(ioService)
50{
51}
52
54{
55 // Destructors are implicitly noexcept; swallow anything thrown during cleanup
56 // so we don't call std::terminate.
57 try
58 {
59 clearActiveRequestMap();
60 cancelAllocationTimer();
61 cancelChannelBindingTimers();
62
63 DebugLog(<< "TurnAsyncSocket::~TurnAsyncSocket destroyed!");
64 }
65 catch (...)
66 {
67 }
68}
69
70void
76
77void
79{
80 asio::dispatch(mIOService, weak_bind<AsyncSocketBase, void()>(mAsyncSocketBase.shared_from_this(), [this] { doRequestSharedSecret(); }));
81}
82
83void
84TurnAsyncSocket::doRequestSharedSecret()
85{
86 // Should we check here if TLS and deny?
87
88 // Ensure Connected
89 if(!mAsyncSocketBase.isConnected())
90 {
93 }
94 else
95 {
96 // Form Shared Secret request
98
99 // Send the Request and start transaction timers
100 sendStunMessage(request);
101 }
102}
103
104void
105TurnAsyncSocket::setUsernameAndPassword(const char* username, const char* password, bool shortTermAuth)
106{
107 Data username_copy{username}, password_copy{password};
108 asio::dispatch(mIOService, weak_bind<AsyncSocketBase, void()>(mAsyncSocketBase.shared_from_this(), [RESIP_LAMBDA_CAPTURE_ALL_AND_THIS]() mutable { doSetUsernameAndPassword(std::move(username_copy), std::move(password_copy), shortTermAuth); }));
109}
110
111void
112TurnAsyncSocket::doSetUsernameAndPassword(Data&& username, Data&& password, bool shortTermAuth)
113{
114 mUsername = std::move(username);
115 if(shortTermAuth)
116 {
117 // If we are using short term auth, then use short term password as HMAC key
118 mHmacKey = password;
119 }
120 mPassword = std::move(password);
121}
122
123void
125{
126 Data password_copy{password};
127 asio::dispatch(mIOService, weak_bind<AsyncSocketBase, void()>(mAsyncSocketBase.shared_from_this(), [RESIP_LAMBDA_CAPTURE_ALL_AND_THIS]() mutable { doSetLocalPassword(std::move(password_copy)); }));
128}
129
130void
131TurnAsyncSocket::doSetLocalPassword(Data&& password)
132{
133 mLocalHmacKey = std::move(password);
134}
135
136void
138{
139 asio::dispatch(mIOService, weak_bind<AsyncSocketBase, void()>(mAsyncSocketBase.shared_from_this(), [this] { doBindRequest(); }));
140}
141
142void
143TurnAsyncSocket::doBindRequest()
144{
145 // Ensure Connected
146 if(!mAsyncSocketBase.isConnected())
147 {
150 }
151 else
152 {
153 // Form Stun Bind request
154 StunMessage* request = createNewStunMessage(StunMessage::StunClassRequest, StunMessage::BindMethod);
155 sendStunMessage(request);
156 }
157}
158
159void
160TurnAsyncSocket::connectivityCheck(const StunTuple& targetAddr, uint32_t peerRflxPriority, bool setIceControlling, bool setIceControlled, unsigned int numRetransmits, unsigned int retrans_iterval_ms)
161{
162 resip_assert(setIceControlling || setIceControlled);
163 asio::dispatch(mIOService, weak_bind<AsyncSocketBase, void()>(mAsyncSocketBase.shared_from_this(), [RESIP_LAMBDA_CAPTURE_ALL_AND_THIS] { doConnectivityCheck(new StunTuple(targetAddr.getTransportType(), targetAddr.getAddress(), targetAddr.getPort()), peerRflxPriority, setIceControlling, setIceControlled, numRetransmits, retrans_iterval_ms); }));
164}
165
166void
167TurnAsyncSocket::doConnectivityCheck(StunTuple* targetAddr, uint32_t peerRflxPriority, bool setIceControlling, bool setIceControlled, unsigned int numRetransmits, unsigned int retrans_iterval_ms)
168{
169 // Form Stun Bind request
170 StunMessage* request = createNewStunMessage(StunMessage::StunClassRequest, StunMessage::BindMethod);
171 request->setIcePriority(peerRflxPriority);
172 if (setIceControlling)
173 {
174 request->setIceControlling();
175 request->setIceUseCandidate();
176 }
177 else if (setIceControlled)
178 {
179 request->setIceControlled();
180 }
181 request->mHasFingerprint = true;
182
183 sendStunMessage(request, false, numRetransmits, retrans_iterval_ms, targetAddr);
184 delete targetAddr;
185}
186
187void
188TurnAsyncSocket::createAllocation(unsigned int lifetimeSecs,
189 unsigned int bandwidth, // Kilobits per second
190 unsigned char requestedProps,
191 uint64_t reservationToken,
192 StunTuple::TransportType requestedTransportType)
193{
194 asio::dispatch(mIOService, weak_bind<AsyncSocketBase, void()>(mAsyncSocketBase.shared_from_this(), [RESIP_LAMBDA_CAPTURE_ALL_AND_THIS] { doCreateAllocation(lifetimeSecs, bandwidth, requestedProps, reservationToken, requestedTransportType); }));
195}
196
197void
198TurnAsyncSocket::doCreateAllocation(unsigned int lifetimeSecs,
199 unsigned int bandwidth,
200 unsigned char requestedProps,
201 uint64_t reservationToken,
202 StunTuple::TransportType requestedTransportType)
203{
204 // Store Allocation Properties
205 mRequestedTransportType = requestedTransportType;
206
207 // Relay Transport Type is requested type or socket type
209 {
211 }
212 else
213 {
215 }
216
217 // Ensure Connected
218 if(!mAsyncSocketBase.isConnected())
219 {
222 return;
223 }
224
226 {
229 return;
230 }
231
232 // Form Turn Allocate request
234 if(lifetimeSecs != UnspecifiedLifetime)
235 {
236 request->mHasTurnLifetime = true;
237 request->mTurnLifetime = lifetimeSecs;
238 }
239
240 if(bandwidth != UnspecifiedBandwidth)
241 {
242 request->mHasTurnBandwidth = true;
243 request->mTurnBandwidth = bandwidth;
244 }
245
246 if(requestedTransportType == StunTuple::None)
247 {
248 requestedTransportType = mLocalBinding.getTransportType();
249 }
250 request->mHasTurnRequestedTransport = true;
251 if(requestedTransportType == StunTuple::UDP)
252 {
253 request->mTurnRequestedTransport = StunMessage::RequestedTransportUdp;
254 }
255 else if(requestedTransportType == StunTuple::TCP &&
256 mLocalBinding.getTransportType() != StunTuple::UDP) // Ensure client is not requesting TCP over a UDP transport
257 {
258 request->mTurnRequestedTransport = StunMessage::RequestedTransportTcp;
259 }
260 else
261 {
264 delete request;
265 return;
266 }
267
268 if(requestedProps != StunMessage::PropsNone)
269 {
270 request->mHasTurnEvenPort = true;
271 request->mTurnEvenPort.propType = requestedProps;
272 }
273 else if(reservationToken != 0)
274 {
275 request->mHasTurnReservationToken = true;
276 request->mTurnReservationToken = reservationToken;
277 }
278
279 sendStunMessage(request);
280}
281
282void
283TurnAsyncSocket::refreshAllocation(unsigned int lifetimeSecs)
284{
285 asio::dispatch(mIOService, weak_bind<AsyncSocketBase, void()>(mAsyncSocketBase.shared_from_this(), [RESIP_LAMBDA_CAPTURE_ALL_AND_THIS] { doRefreshAllocation(lifetimeSecs); }));
286}
287
288void
289TurnAsyncSocket::doRefreshAllocation(unsigned int lifetimeSecs)
290{
291 if(!mHaveAllocation)
292 {
293 {
295 if (mTurnAsyncSocketHandler) mTurnAsyncSocketHandler->onRefreshFailure(getSocketDescriptor(), asio::error_code(NoAllocation, asio::error::misc_category));
296 }
297 if(mCloseAfterDestroyAllocationFinishes)
298 {
299 actualClose();
300 }
301 return;
302 }
303
304 // Form Turn Refresh request
306 if(lifetimeSecs != UnspecifiedLifetime)
307 {
308 request->mHasTurnLifetime = true;
309 request->mTurnLifetime = lifetimeSecs;
310 }
311 //if(mRequestedBandwidth != UnspecifiedBandwidth)
312 //{
313 // request.mHasTurnBandwidth = true;
314 // request.mTurnBandwidth = mRequestedBandwidth;
315 //}
316
317 sendStunMessage(request);
318}
319
320void
322{
323 asio::dispatch(mIOService, weak_bind<AsyncSocketBase, void()>(mAsyncSocketBase.shared_from_this(), [this] { doDestroyAllocation(); }));
324}
325
326void
327TurnAsyncSocket::doDestroyAllocation()
328{
329 doRefreshAllocation(0);
330}
331
332void
333TurnAsyncSocket::setActiveDestination(const asio::ip::address& address, unsigned short port)
334{
335 asio::dispatch(mIOService, weak_bind<AsyncSocketBase, void()>(mAsyncSocketBase.shared_from_this(), [RESIP_LAMBDA_CAPTURE_ALL_AND_THIS] { doSetActiveDestination(address, port); }));
336}
337
338void
339TurnAsyncSocket::doSetActiveDestination(const asio::ip::address& address, unsigned short port)
340{
341 // Setup Remote Peer
342 StunTuple remoteTuple(mRelayTransportType, address, port);
343 RemotePeer* remotePeer = mChannelManager.findRemotePeerByPeerAddress(remoteTuple);
344 if(remotePeer)
345 {
346 mActiveDestination = remotePeer;
347 }
348 else
349 {
350 // No channel binding yet (ie. no data sent or received from remote peer) - so create one
353 doChannelBinding(*mActiveDestination);
354 }
355 DebugLog(<< "TurnAsyncSocket::doSetActiveDestination: Active Destination set to: " << remoteTuple);
358}
359
360void TurnAsyncSocket::doChannelBinding(RemotePeer& remotePeer)
361{
362 // Form Channel Bind request
364
365 // Set headers
366 request->mHasTurnChannelNumber = true;
367 request->mTurnChannelNumber = remotePeer.getChannel();
368 request->mCntTurnXorPeerAddress = 1;
369 StunMessage::setStunAtrAddressFromTuple(request->mTurnXorPeerAddress[0], remotePeer.getPeerTuple());
370
371 // Send the Request and start transaction timers
372 sendStunMessage(request);
373
374 // If not using UDP - then mark channel as confirmed - otherwise wait for ChannelBind response
376 {
377 remotePeer.setChannelConfirmed();
378 }
379
382}
383
384void
386{
387 asio::dispatch(mIOService, weak_bind<AsyncSocketBase, void()>(mAsyncSocketBase.shared_from_this(), [this] { doClearActiveDestination(); }));
388}
389
390void
391TurnAsyncSocket::doClearActiveDestination()
392{
393 // ensure there is an allocation
394 if(!mHaveAllocation)
395 {
398 return;
399 }
400
404}
405
407TurnAsyncSocket::createNewStunMessage(uint16_t stunclass, uint16_t method, bool addAuthInfo)
408{
409 StunMessage* msg = new StunMessage();
410 msg->createHeader(stunclass, method);
411
412 // Add Software Attribute
413 if(!mSoftware.empty())
414 {
415 msg->setSoftware(mSoftware.c_str());
416 }
417
418 if(addAuthInfo && !mUsername.empty() && !mHmacKey.empty())
419 {
420 msg->mHasMessageIntegrity = true;
421 msg->setUsername(mUsername.c_str());
422 msg->mHmacKey = mHmacKey;
423 if(!mRealm.empty())
424 {
425 msg->setRealm(mRealm.c_str());
426 }
427 if(!mNonce.empty())
428 {
429 msg->setNonce(mNonce.c_str());
430 }
431 }
432 return msg;
433}
434
435void
436TurnAsyncSocket::sendStunMessage(StunMessage* message, bool reTransmission, unsigned int numRetransmits, unsigned int retrans_iterval_ms, const StunTuple* targetAddress)
437{
438 constexpr size_t REQUEST_BUFFER_SIZE = 4096;
439 const auto buffer = AsyncSocketBase::allocateBuffer(REQUEST_BUFFER_SIZE);
440 unsigned int bufferSize;
441 bufferSize = message->stunEncodeMessage((char*)buffer->data(), REQUEST_BUFFER_SIZE);
442 buffer->truncate(bufferSize); // set size to real size
443
444 if(!reTransmission)
445 {
446 // If message is a request, then start appropriate transaction and retranmission timers
447 if(message->mClass == StunMessage::StunClassRequest)
448 {
449 const auto requestEntry = std::make_shared<RequestEntry>(mIOService, this, message, numRetransmits, retrans_iterval_ms, targetAddress);
450 mActiveRequestMap[message->mHeader.magicCookieAndTid] = requestEntry;
451 requestEntry->startTimer();
452 }
453 else
454 {
455 delete message;
456 }
457 }
458
459 if (targetAddress)
460 {
461 sendToUnframed(targetAddress->getAddress(), targetAddress->getPort(), buffer);
462 }
463 else
464 {
465 sendUnframed(buffer);
466 }
467}
468
469void
470TurnAsyncSocket::handleReceivedData(const asio::ip::address& address, unsigned short port, const std::shared_ptr<DataBuffer>& data)
471{
473 if(data->size() <= 4)
474 {
475 WarningLog(<< "TurnAsyncSocket::handleReceivedData: not enough data received (" << data->size() << " bytes) for stun or channel data message - discarding!");
477 return;
478 }
479
480 // What a packet is, by its first byte (RFC 7983): 0-3 STUN, 64-127 TURN ChannelData (channel
481 // numbers 0x4000-0x7FFF), and the rest the application's data - eg. DTLS (20-63), RTP (128-191)
482 const unsigned char firstByte = (*data)[0];
483 if(firstByte <= 3)
484 {
486 StunTuple(mLocalBinding.getTransportType(), address, port),
487 &(*data)[0], (unsigned int)data->size());
488 if(stunMsg.isValid())
489 {
490 handleStunMessage(stunMsg);
491 return;
492 }
493 }
494 else if(mHaveAllocation && (firstByte & 0xC0) == 0x40)
495 {
496 handleChannelData(data);
497 return;
498 }
499
501 {
502 // Everything the TURN server sends is STUN or ChannelData: peers' data comes through it
503 DebugLog(<< "TurnAsyncSocket::handleReceivedData: neither STUN nor channel data received from " << address.to_string() << ":" << port << " while allocated - discarding");
504 return;
505 }
506
508}
509
510void
511TurnAsyncSocket::handleChannelData(const std::shared_ptr<DataBuffer>& data)
512{
513 // Get Channel number
514 unsigned short channelNumber;
515 memcpy(&channelNumber, &(*data)[0], 2);
516 channelNumber = ntohs(channelNumber);
517
519 {
520 // Check if the UDP datagram size is too short to contain the claimed length of the ChannelData message, then discard
521 unsigned short dataLen;
522 memcpy(&dataLen, &(*data)[2], 2);
523 dataLen = ntohs(dataLen);
524
525 if(data->size() < (unsigned int)dataLen+4)
526 {
527 WarningLog(<< "ChannelData message size=" << dataLen+4 << " too large for UDP packet size=" << data->size() <<". Dropping.");
528 return;
529 }
530 }
531
532 RemotePeer* remotePeer = mChannelManager.findRemotePeerByChannel(channelNumber);
533 if(remotePeer)
534 {
535 data->offset(4); // move buffer start past framing for callback
537 remotePeer->getPeerTuple().getAddress(),
538 remotePeer->getPeerTuple().getPort(),
539 data);
540 }
541 else
542 {
543 WarningLog(<< "TurnAsyncSocket::handleReceivedData: receive channel data for non-existing channel - discarding!");
544 }
545}
546
547asio::error_code
548TurnAsyncSocket::handleStunMessage(StunMessage& stunMessage)
549{
550 asio::error_code errorCode;
551 if(stunMessage.isValid())
552 {
555 {
556 if(!stunMessage.checkMessageIntegrity(mHmacKey))
557 {
558 WarningLog(<< "TurnAsyncSocket::handleStunMessage: Stun message integrity is bad!");
559 return asio::error_code(reTurn::BadMessageIntegrity, asio::error::misc_category);
560 }
561 }
562 else
563 {
564 if(!stunMessage.checkMessageIntegrity(mLocalHmacKey))
565 {
566 WarningLog(<< "TurnAsyncSocket::handleStunMessage: Stun message integrity is bad!");
567 return asio::error_code(reTurn::BadMessageIntegrity, asio::error::misc_category);
568 }
569 }
570
571 // Request is authenticated, process it
572 switch(stunMessage.mClass)
573 {
575 switch (stunMessage.mMethod)
576 {
578 if(stunMessage.mUnknownRequiredAttributes.numAttributes > 0)
579 {
580 // There were unknown comprehension-required attributes in the request
581 StunMessage* response = new StunMessage();
582 response->mClass = StunMessage::StunClassErrorResponse;
583 response->mMethod = stunMessage.mMethod;
584 response->setErrorCode(420, "Unknown Attribute");
585 // Copy over TransactionId
586 response->mHeader.magicCookieAndTid = stunMessage.mHeader.magicCookieAndTid;
587 // Add Unknown Attributes
588 response->mHasUnknownAttributes = true;
589 response->mUnknownAttributes = stunMessage.mUnknownRequiredAttributes;
590 // Add Software Attribute
591 if(!mSoftware.empty())
592 {
593 response->setSoftware(mSoftware.c_str());
594 }
595 sendStunMessage(response);
596 }
597 else
598 {
599 errorCode = handleBindRequest(stunMessage);
600 }
601 break;
605 default:
606 // These requests are not handled by a client
607 StunMessage* response = new StunMessage();
608 response->mClass = StunMessage::StunClassErrorResponse;
609 response->mMethod = stunMessage.mMethod;
610 response->setErrorCode(400, "Invalid Request Method");
611 // Copy over TransactionId
612 response->mHeader.magicCookieAndTid = stunMessage.mHeader.magicCookieAndTid;
613 // Add Software Attribute
614 if(!mSoftware.empty())
615 {
616 response->setSoftware(mSoftware.c_str());
617 }
618 sendStunMessage(response);
619 break;
620 }
621 break;
622
624 switch (stunMessage.mMethod)
625 {
627 if(stunMessage.mUnknownRequiredAttributes.numAttributes > 0)
628 {
629 // Unknown Comprehension-Required Attributes found
630 WarningLog(<< "Ignoring DataInd with unknown comprehension required attributes.");
631 errorCode = asio::error_code(reTurn::UnknownRequiredAttributes, asio::error::misc_category);
632 }
633 else
634 {
635 errorCode = handleDataInd(stunMessage);
636 }
637 break;
639 // A Bind indication is simply a keepalive with no response required
640 break;
641 case StunMessage::TurnSendMethod: // Don't need to handle these - only sent by client, never received
642 default:
643 // Unknown indication - just ignore
644 break;
645 }
646 break;
647
650 {
651 if(stunMessage.mUnknownRequiredAttributes.numAttributes > 0)
652 {
653 // Unknown Comprehension-Required Attributes found
654 WarningLog(<< "Ignoring Response with unknown comprehension required attributes.");
655 return asio::error_code(reTurn::UnknownRequiredAttributes, asio::error::misc_category);
656 }
657
658 // First check if this response is for an active request
659 RequestMap::iterator it = mActiveRequestMap.find(stunMessage.mHeader.magicCookieAndTid);
660 if(it == mActiveRequestMap.end())
661 {
662 // Stray response - dropping
663 return asio::error_code(reTurn::StrayResponse, asio::error::misc_category);
664 }
665
666 {
667 const auto requestEntry = it->second;
668 mActiveRequestMap.erase(it);
669 requestEntry->stopTimer();
670
671 // If a realm and nonce attributes are present and the response is a 401 or 438 (Nonce Expired),
672 // then re-issue request with new auth attributes
673 if(stunMessage.mHasRealm &&
674 stunMessage.mHasNonce &&
675 stunMessage.mHasErrorCode &&
676 stunMessage.mErrorCode.errorClass == 4 &&
677 ((stunMessage.mErrorCode.number == 1 && mHmacKey.empty()) || // Note if 401 error then ensure we haven't already tried once - if we've tried then mHmacKey will be populated
678 stunMessage.mErrorCode.number == 38))
679 {
680 mNonce = *stunMessage.mNonce;
681 mRealm = *stunMessage.mRealm;
683
684 // Create a new transaction - by starting with old request
685 StunMessage* newRequest = requestEntry->mRequestMessage;
686 requestEntry->mRequestMessage = 0; // clear out pointer in mActiveRequestMap so that it will not be deleted
687
688 newRequest->createHeader(newRequest->mClass, newRequest->mMethod); // updates TID
689 newRequest->mHasMessageIntegrity = true;
690 newRequest->setUsername(mUsername.c_str());
691 newRequest->mHmacKey = mHmacKey;
692 newRequest->setRealm(mRealm.c_str());
693 newRequest->setNonce(mNonce.c_str());
694 sendStunMessage(newRequest);
695 return errorCode;
696 }
697
698 switch (stunMessage.mMethod)
699 {
701 errorCode = handleBindResponse(*requestEntry->mRequestMessage, stunMessage);
702 break;
704 errorCode = handleSharedSecretResponse(*requestEntry->mRequestMessage, stunMessage);
705 break;
707 errorCode = handleAllocateResponse(*requestEntry->mRequestMessage, stunMessage);
708 break;
710 errorCode = handleRefreshResponse(*requestEntry->mRequestMessage, stunMessage);
711 break;
713 errorCode = handleChannelBindResponse(*requestEntry->mRequestMessage, stunMessage);
714 break;
715 default:
716 // Unknown method - just ignore
717 break;
718 }
719 }
720 }
721 break;
722
723 default:
724 // Illegal message class - ignore
725 break;
726 }
727 }
728 else
729 {
730 WarningLog(<< "TurnAsyncSocket::handleStunMessage: Read Invalid StunMsg.");
731 return asio::error_code(reTurn::ErrorParsingMessage, asio::error::misc_category);
732 }
733 return errorCode;
734}
735
736asio::error_code
737TurnAsyncSocket::handleDataInd(StunMessage& stunMessage)
738{
739 if(stunMessage.mCntTurnXorPeerAddress == 0 || !stunMessage.mHasTurnData)
740 {
741 // Missing RemoteAddress or TurnData attribute
742 WarningLog(<< "TurnAsyncSocket::handleDataInd: DataInd missing attributes.");
743 return asio::error_code(reTurn::MissingAttributes, asio::error::misc_category);
744 }
745
746 StunTuple remoteTuple;
747 remoteTuple.setTransportType(mRelayTransportType);
748 StunMessage::setTupleFromStunAtrAddress(remoteTuple, stunMessage.mTurnXorPeerAddress[0]);
749
750 // Should we record all the remoteTuples we have sent to before, and reject this message if
751 // not from one of the those endpoints?
752
753 const auto data = std::make_shared<DataBuffer>(stunMessage.mTurnData->data(), stunMessage.mTurnData->size());
756 remoteTuple.getAddress(),
757 remoteTuple.getPort(),
758 data);
759
760 return asio::error_code();
761}
762
763asio::error_code
764TurnAsyncSocket::handleChannelBindResponse(StunMessage &request, StunMessage &response)
765{
766 if(response.mClass == StunMessage::StunClassSuccessResponse)
767 {
768 resip_assert(request.mHasTurnChannelNumber);
769
770 RemotePeer* remotePeer = mChannelManager.findRemotePeerByChannel(request.mTurnChannelNumber);
771 if(!remotePeer)
772 {
773 // Remote Peer not found - discard
774 WarningLog(<< "TurnAsyncSocket::handleChannelBindResponse: Received ChannelBindResponse for unknown channel (" << response.mTurnChannelNumber << ") - discarding");
775 asio::error_code ec(reTurn::InvalidChannelNumberReceived, asio::error::misc_category);
778 return ec;
779 }
780
781 DebugLog(<< "TurnAsyncSocket::handleChannelBindResponse: Channel " << remotePeer->getChannel() << " is now bound to " << remotePeer->getPeerTuple());
782 remotePeer->refresh();
783 remotePeer->setChannelConfirmed();
784 startChannelBindingTimer(remotePeer->getChannel());
785
788 }
789 else
790 {
791 // Check error code
792 if(response.mHasErrorCode)
793 {
794 ErrLog(<< "TurnAsyncSocket::handleChannelBindResponse: Received ChannelBindResponse error: " << response.mErrorCode.errorClass * 100 + response.mErrorCode.number);
795 asio::error_code ec(response.mErrorCode.errorClass * 100 + response.mErrorCode.number, asio::error::misc_category);
798 return ec;
799 }
800 else
801 {
802 ErrLog(<< "TurnAsyncSocket::handleChannelBindResponse: Received ChannelBindResponse error but no error code attribute found.");
803 asio::error_code ec(MissingAttributes, asio::error::misc_category);
806 return ec;
807 }
808 }
809 return asio::error_code();
810}
811
812asio::error_code
813TurnAsyncSocket::handleSharedSecretResponse(StunMessage &request, StunMessage &response)
814{
815 if(response.mClass == StunMessage::StunClassSuccessResponse)
816 {
817 // Copy username and password to callers buffer - checking sizes first
818 if(!response.mHasUsername || !response.mHasPassword)
819 {
820 WarningLog(<< "TurnAsyncSocket::handleSharedSecretResponse: Stun response message for SharedSecretRequest is missing username and/or password!");
823 return asio::error_code(MissingAttributes, asio::error::misc_category);
824 }
825
827 if(mTurnAsyncSocketHandler) mTurnAsyncSocketHandler->onSharedSecretSuccess(getSocketDescriptor(), response.mUsername->c_str(), (unsigned int)response.mUsername->size(),
828 response.mPassword->c_str(), (unsigned int)response.mPassword->size());
829 }
830 else
831 {
832 // Check error code
833 if(response.mHasErrorCode)
834 {
836 if(mTurnAsyncSocketHandler) mTurnAsyncSocketHandler->onSharedSecretFailure(getSocketDescriptor(), asio::error_code(response.mErrorCode.errorClass * 100 + response.mErrorCode.number, asio::error::misc_category));
837 }
838 else
839 {
842 return asio::error_code(MissingAttributes, asio::error::misc_category);
843 }
844 }
845 return asio::error_code();
846}
847
848asio::error_code
849TurnAsyncSocket::handleBindRequest(StunMessage& stunMessage)
850{
851 // Note: handling of BindRequest is not fully backwards compatible with RFC3489 - it is inline with RFC5389
852 StunMessage* response = new StunMessage();
853
854 // form the outgoing message
855 response->mClass = StunMessage::StunClassSuccessResponse;
856 response->mMethod = StunMessage::BindMethod;
857
858 // Copy over TransactionId
859 response->mHeader.magicCookieAndTid = stunMessage.mHeader.magicCookieAndTid;
860
861 // Add XOrMappedAddress to response
862 response->mHasXorMappedAddress = true;
863 StunMessage::setStunAtrAddressFromTuple(response->mXorMappedAddress, stunMessage.mRemoteTuple);
864
865 // Add Software Attribute
866 if(!mSoftware.empty())
867 {
868 response->setSoftware(mSoftware.c_str());
869 }
870
871 // If the request contained MESSAGE-INTEGRITY, then the response needs to as well
872 if (stunMessage.mHasMessageIntegrity)
873 {
874 response->mHasMessageIntegrity = true;
875 response->mHmacKey = mLocalHmacKey;
876 }
877
878 if (stunMessage.mHasIceControlled || stunMessage.mHasIceControlling || stunMessage.mHasIcePriority)
879 {
880 response->mHasFingerprint = true;
881 }
882
883 // send bindResponse to local client
884 DebugLog(<< "Sending response to BIND to " << stunMessage.mRemoteTuple);
885 sendStunMessage(response, false, UDP_MAX_RETRANSMITS, DEFAULT_RETRANS_INTERVAL_MS, &(stunMessage.mRemoteTuple));
886
889
890 return asio::error_code();
891}
892
893asio::error_code
894TurnAsyncSocket::handleBindResponse(StunMessage &request, StunMessage &response)
895{
896 if(response.mClass == StunMessage::StunClassSuccessResponse)
897 {
898 StunTuple reflexiveTuple;
899 reflexiveTuple.setTransportType(mLocalBinding.getTransportType());
900 if(response.mHasXorMappedAddress)
901 {
902 StunMessage::setTupleFromStunAtrAddress(reflexiveTuple, response.mXorMappedAddress);
903 }
904 else if(response.mHasMappedAddress) // Only look at MappedAddress if XorMappedAddress is not found - for backwards compatibility
905 {
906 StunMessage::setTupleFromStunAtrAddress(reflexiveTuple, response.mMappedAddress);
907 }
908 else
909 {
911 if(mTurnAsyncSocketHandler) mTurnAsyncSocketHandler->onBindFailure(getSocketDescriptor(), asio::error_code(MissingAttributes, asio::error::misc_category), response.mRemoteTuple);
912 return asio::error_code(MissingAttributes, asio::error::misc_category);
913 }
914 if(mTurnAsyncSocketHandler) mTurnAsyncSocketHandler->onBindSuccess(getSocketDescriptor(), reflexiveTuple, response.mRemoteTuple);
915 }
916 else
917 {
918 // Check if success or not
919 if(response.mHasErrorCode)
920 {
922 if(mTurnAsyncSocketHandler) mTurnAsyncSocketHandler->onBindFailure(getSocketDescriptor(), asio::error_code(response.mErrorCode.errorClass * 100 + response.mErrorCode.number, asio::error::misc_category), response.mRemoteTuple);
923 }
924 else
925 {
927 if(mTurnAsyncSocketHandler) mTurnAsyncSocketHandler->onBindFailure(getSocketDescriptor(), asio::error_code(MissingAttributes, asio::error::misc_category), response.mRemoteTuple);
928 return asio::error_code(MissingAttributes, asio::error::misc_category);
929 }
930 }
931 return asio::error_code();
932}
933
934asio::error_code
935TurnAsyncSocket::handleAllocateResponse(StunMessage &request, StunMessage &response)
936{
937 if(response.mClass == StunMessage::StunClassSuccessResponse)
938 {
939 StunTuple reflexiveTuple;
940 StunTuple relayTuple;
941 if(response.mHasXorMappedAddress)
942 {
943 reflexiveTuple.setTransportType(mLocalBinding.getTransportType());
944 StunMessage::setTupleFromStunAtrAddress(reflexiveTuple, response.mXorMappedAddress);
945 }
946 if(response.mHasTurnXorRelayedAddress)
947 {
948 relayTuple.setTransportType(mRelayTransportType);
949 StunMessage::setTupleFromStunAtrAddress(relayTuple, response.mTurnXorRelayedAddress);
950 }
951 if(response.mHasTurnLifetime)
952 {
953 mLifetime = response.mTurnLifetime;
954 }
955 else
956 {
957 mLifetime = 0;
958 }
959
960 // All was well - return 0 errorCode
961 if(mLifetime != 0)
962 {
963 mHaveAllocation = true;
964 startAllocationTimer();
967 reflexiveTuple,
968 relayTuple,
969 mLifetime,
970 response.mHasTurnBandwidth ? response.mTurnBandwidth : 0,
971 response.mHasTurnReservationToken ? response.mTurnReservationToken : 0);
972 }
973 else
974 {
977 }
978 }
979 else
980 {
981 // Check if success or not
982 if(response.mHasErrorCode)
983 {
985 if(mTurnAsyncSocketHandler) mTurnAsyncSocketHandler->onAllocationFailure(getSocketDescriptor(), asio::error_code(response.mErrorCode.errorClass * 100 + response.mErrorCode.number, asio::error::misc_category));
986 }
987 else
988 {
991 return asio::error_code(MissingAttributes, asio::error::misc_category);
992 }
993 }
994 return asio::error_code();
995}
996
997asio::error_code
998TurnAsyncSocket::handleRefreshResponse(StunMessage &request, StunMessage &response)
999{
1000 if(response.mClass == StunMessage::StunClassSuccessResponse)
1001 {
1002 if(response.mHasTurnLifetime)
1003 {
1004 mLifetime = response.mTurnLifetime;
1005 }
1006 else
1007 {
1008 mLifetime = 0;
1009 }
1010 if(mLifetime != 0)
1011 {
1012 mHaveAllocation = true;
1013 startAllocationTimer();
1014 {
1017 }
1018 if(mCloseAfterDestroyAllocationFinishes)
1019 {
1020 mHaveAllocation = false;
1021 actualClose();
1022 }
1023 }
1024 else
1025 {
1026 cancelAllocationTimer();
1027 mHaveAllocation = false;
1029 {
1032 }
1033 if(mCloseAfterDestroyAllocationFinishes)
1034 {
1035 actualClose();
1036 }
1037 }
1038 }
1039 else
1040 {
1041 // Check if success or not
1042 if(response.mHasErrorCode)
1043 {
1044 {
1046 if (mTurnAsyncSocketHandler) mTurnAsyncSocketHandler->onRefreshFailure(getSocketDescriptor(), asio::error_code(response.mErrorCode.errorClass * 100 + response.mErrorCode.number, asio::error::misc_category));
1047 }
1048 if(mCloseAfterDestroyAllocationFinishes)
1049 {
1050 cancelAllocationTimer();
1051 mHaveAllocation = false;
1052 actualClose();
1053 }
1054 else if(response.mErrorCode.errorClass == 4 && response.mErrorCode.number == 37) // response is 437, then remove allocation
1055 {
1056 cancelAllocationTimer();
1057 mHaveAllocation = false;
1059 }
1060 }
1061 else
1062 {
1063 {
1065 if (mTurnAsyncSocketHandler) mTurnAsyncSocketHandler->onRefreshFailure(getSocketDescriptor(), asio::error_code(MissingAttributes, asio::error::misc_category));
1066 }
1067 if(mCloseAfterDestroyAllocationFinishes)
1068 {
1069 cancelAllocationTimer();
1070 mHaveAllocation = false;
1071 actualClose();
1072 }
1073 return asio::error_code(MissingAttributes, asio::error::misc_category);
1074 }
1075 }
1076 return asio::error_code();
1077}
1078
1079void
1080TurnAsyncSocket::send(const char* const buffer, const size_t size)
1081{
1082 sendFramed(std::make_shared<DataBuffer>(buffer, size));
1083}
1084
1085void
1086TurnAsyncSocket::sendTo(const asio::ip::address& address, unsigned short port, const char* const buffer, const size_t size)
1087{
1088 sendToFramed(address, port, std::make_shared<DataBuffer>(buffer, size));
1089}
1090
1091void
1092TurnAsyncSocket::sendFramed(const std::shared_ptr<DataBuffer>& data)
1093{
1094 asio::dispatch(mIOService, weak_bind<AsyncSocketBase, void()>(mAsyncSocketBase.shared_from_this(), [RESIP_LAMBDA_CAPTURE_ALL_AND_THIS] { doSendFramed(data); }));
1095}
1096
1097void
1098TurnAsyncSocket::sendToFramed(const asio::ip::address& address, unsigned short port, const std::shared_ptr<DataBuffer>& data)
1099{
1100 asio::dispatch(mIOService, weak_bind<AsyncSocketBase, void()>(mAsyncSocketBase.shared_from_this(), [RESIP_LAMBDA_CAPTURE_ALL_AND_THIS] { doSendToFramed(address, port, data); }));
1101}
1102
1103void
1104TurnAsyncSocket::sendUnframed(const std::shared_ptr<DataBuffer>& data)
1105{
1106 StunTuple destination(mLocalBinding.getTransportType(), mAsyncSocketBase.getConnectedAddress(), mAsyncSocketBase.getConnectedPort());
1107 mAsyncSocketBase.send(destination, data);
1108}
1109
1110void
1111TurnAsyncSocket::sendToUnframed(const asio::ip::address& address, unsigned short port, const std::shared_ptr<DataBuffer>& data)
1112{
1113 StunTuple destination(mLocalBinding.getTransportType(), address, port);
1114 mAsyncSocketBase.send(destination, data);
1115}
1116
1117void
1118TurnAsyncSocket::doSendFramed(const std::shared_ptr<DataBuffer>& data)
1119{
1121 {
1122 sendToRemotePeer(*mActiveDestination, data);
1123 }
1124 else if(mAsyncSocketBase.isConnected())
1125 {
1126 //DebugLog(<<"Sending to connected peer");
1127 sendToUnframed(mAsyncSocketBase.getConnectedAddress(), mAsyncSocketBase.getConnectedPort(), data);
1128 }
1129 else
1130 {
1131 DebugLog(<<"no allocation, can't send!");
1132 }
1133}
1134
1135void
1136TurnAsyncSocket::doSendToFramed(const asio::ip::address& address, unsigned short port, const std::shared_ptr<DataBuffer>& data)
1137{
1138 // Without an allocation there's no relay to frame the data for: send it as it is (eg. DTLS,
1139 // and media, on a flow that isn't using TURN)
1140 if(!mHaveAllocation)
1141 {
1142 sendToUnframed(address, port, data);
1143 return;
1144 }
1145
1146 // Setup Remote Peer
1147 StunTuple remoteTuple(mRelayTransportType, address, port);
1148 RemotePeer* remotePeer = mChannelManager.findRemotePeerByPeerAddress(remoteTuple);
1149 if(!remotePeer)
1150 {
1151 // No remote peer yet (ie. no data sent or received from remote peer) - so create one
1152 remotePeer = mChannelManager.createChannelBinding(remoteTuple);
1153 resip_assert(remotePeer);
1154 doChannelBinding(*remotePeer);
1155 }
1156 return sendToRemotePeer(*remotePeer, data);
1157}
1158
1159void
1160TurnAsyncSocket::sendToRemotePeer(RemotePeer& remotePeer, const std::shared_ptr<DataBuffer>& data)
1161{
1162 if(remotePeer.isChannelConfirmed())
1163 {
1164 // send framed data to active destination
1165 sendOverChannel(remotePeer.getChannel(), data);
1166 //InfoLog( << "TurnAsyncSocket::sendTo: using channel " << remotePeer.getChannel() << " to send " << data->size() << " bytes.");
1167 }
1168 else
1169 {
1170 // Data must be wrapped in a Send Indication
1171 // Wrap data in a SendInd
1173 ind->mCntTurnXorPeerAddress = 1;
1174 StunMessage::setStunAtrAddressFromTuple(ind->mTurnXorPeerAddress[0], remotePeer.getPeerTuple());
1175 if(data->size() > 0)
1176 {
1177 ind->setTurnData(data->data(), (unsigned int)data->size());
1178 }
1179
1180 // Send indication to Turn Server
1181 sendStunMessage(ind);
1182 }
1183}
1184
1185void
1186TurnAsyncSocket::setSoftware(const char* software)
1187{
1188 Data software_copy{software};
1189 asio::dispatch(mIOService, weak_bind<AsyncSocketBase, void()>(mAsyncSocketBase.shared_from_this(), [RESIP_LAMBDA_CAPTURE_ALL_AND_THIS]() mutable { doSetSoftware(std::move(software_copy)); }));
1190}
1191
1192void
1193TurnAsyncSocket::doSetSoftware(Data&& software)
1194{
1195 mSoftware = std::move(software);
1196
1197 const uint32_t unpaddedSize = (unsigned int)mSoftware.size();
1198 if(unpaddedSize > 0)
1199 {
1200 // Pad size to a multiple of 4, to help compatibility with older clients
1201 const uint32_t remainder = unpaddedSize % 4,
1202 paddedSize = remainder ? unpaddedSize + 4 - remainder : unpaddedSize;
1203
1204 while(mSoftware.size() < paddedSize)
1205 mSoftware.append(" ", 1);
1206 }
1207}
1208
1209void
1210TurnAsyncSocket::connect(const std::string& address, unsigned short port)
1211{
1212 mAsyncSocketBase.connect(address, port);
1213}
1214
1215void
1217{
1218 asio::post(mIOService, weak_bind<AsyncSocketBase, void()>(mAsyncSocketBase.shared_from_this(), [this] { doClose(); }));
1219}
1220
1221void
1222TurnAsyncSocket::doClose()
1223{
1224 // If we have an allocation over UDP then we should send a refresh with lifetime 0 to destroy the allocation
1225 // Note: For TCP and TLS, the socket disconnection will destroy the allocation automatically
1227 {
1228 mCloseAfterDestroyAllocationFinishes = true;
1230 }
1231 else
1232 {
1233 actualClose();
1234 }
1235}
1236
1237void
1238TurnAsyncSocket::actualClose()
1239{
1240 clearActiveRequestMap();
1241 cancelAllocationTimer();
1242 cancelChannelBindingTimers();
1243 mAsyncSocketBase.close();
1244}
1245
1246void
1248{
1250 {
1251 //mAsyncSocketBase.receive();
1252 mAsyncSocketBase.doReceive();
1253 }
1254 else
1255 {
1256 //mAsyncSocketBase.framedReceive();
1257 mAsyncSocketBase.doFramedReceive();
1258 }
1259}
1260
1261void
1262TurnAsyncSocket::sendOverChannel(unsigned short channel, const std::shared_ptr<DataBuffer>& data)
1263{
1264 StunTuple destination(mLocalBinding.getTransportType(), mAsyncSocketBase.getConnectedAddress(), mAsyncSocketBase.getConnectedPort());
1265 mAsyncSocketBase.send(destination, channel, data);
1266}
1267
1268TurnAsyncSocket::RequestEntry::RequestEntry(asio::io_context& ioService,
1269 TurnAsyncSocket* turnAsyncSocket,
1270 StunMessage* requestMessage,
1271 unsigned int rc,
1272 unsigned int retrans_interval_ms,
1273 const StunTuple* dest)
1274 :
1275 mIOService(ioService),
1276 mTurnAsyncSocket(turnAsyncSocket),
1277 mRequestMessage(requestMessage),
1278 mRequestTimer(ioService),
1279 mRequestsSent(1),
1280 mDest(dest ? new StunTuple(dest->getTransportType(), dest->getAddress(), dest->getPort()) : 0),
1281 mRc(rc),
1282 mRetransIntervalMs(retrans_interval_ms)
1283{
1284 //std::cout << "RequestEntry::RequestEntry() " << mRequestMessage->mHeader.magicCookieAndTid << " dest: " << (mDest ? dest->getPort() : 0) << std::endl;
1285
1286 mTimeout = mTurnAsyncSocket->mLocalBinding.getTransportType() == StunTuple::UDP ? UDP_RT0 : TCP_RESPONSE_TIME;
1287}
1288
1289void
1290TurnAsyncSocket::RequestEntry::startTimer()
1291{
1292 //std::cout << "RequestEntry::startTimer() " << mTimeout << " " << mRequestMessage->mHeader.magicCookieAndTid << std::endl;
1293 // start the request timer
1294 mRequestTimer.expires_after(milliseconds(mTimeout));
1295 mRequestTimer.async_wait(weak_bind<RequestEntry, void(const asio::error_code&)>(shared_from_this(), std::bind(&TurnAsyncSocket::RequestEntry::requestTimerExpired, this, std::placeholders::_1)));
1296}
1297
1298void
1299TurnAsyncSocket::RequestEntry::stopTimer()
1300{
1301 //std::cout << "RequestEntry::stopTimer() " << mRequestMessage->mHeader.magicCookieAndTid << std::endl;
1302 // stop the request timer
1303 mRequestTimer.cancel();
1304}
1305
1306TurnAsyncSocket::RequestEntry::~RequestEntry()
1307{
1308 // Destructors are implicitly noexcept; swallow anything thrown during cleanup
1309 // so we don't call std::terminate.
1310 try
1311 {
1312 //std::cout << "RequestEntry::~RequestEntry() " << mRequestMessage->mHeader.magicCookieAndTid << std::endl;
1313 delete mRequestMessage;
1314 delete mDest;
1315 stopTimer();
1316 }
1317 catch (...)
1318 {
1319 }
1320}
1321
1322void
1323TurnAsyncSocket::RequestEntry::requestTimerExpired(const asio::error_code& e)
1324{
1325 if(!e && mRequestMessage) // Note: There is a race condition with clearing out of mRequestMessage when 401 is received - check that mRequestMessage is not 0 avoids any resulting badness
1326 {
1327 //std::cout << "RequestEntry::requestTimerExpired() " << mRequestMessage->mHeader.magicCookieAndTid << std::endl;
1328
1329 if(mTurnAsyncSocket->mLocalBinding.getTransportType() != StunTuple::UDP || mRequestsSent == mRc)
1330 {
1331 mTurnAsyncSocket->requestTimeout(mRequestMessage->mHeader.magicCookieAndTid);
1332 return;
1333 }
1334 // timed out and should retransmit - calculate next timeout
1335 if(mRetransIntervalMs == DEFAULT_RETRANS_INTERVAL_MS)
1336 {
1337 if(mRequestsSent == mRc - 1)
1338 {
1339 mTimeout = UDP_FINAL_REQUEST_TIME;
1340 }
1341 else
1342 {
1343 mTimeout = (mTimeout*2);
1344 }
1345 }
1346 else
1347 {
1348 mTimeout = mRetransIntervalMs;
1349 }
1350
1351 // retransmit
1352 DebugLog(<< "RequestEntry::requestTimerExpired: retransmitting...");
1353 mRequestsSent++;
1354 mTurnAsyncSocket->sendStunMessage(mRequestMessage, true, UDP_MAX_RETRANSMITS, DEFAULT_RETRANS_INTERVAL_MS, mDest);
1355
1356 startTimer();
1357 }
1358}
1359
1360void
1361TurnAsyncSocket::requestTimeout(UInt128 tid)
1362{
1363 RequestMap::iterator it = mActiveRequestMap.find(tid);
1364 if (it != mActiveRequestMap.end())
1365 {
1366 const auto requestEntry = it->second;
1367 mActiveRequestMap.erase(tid);
1368
1369 switch (requestEntry->mRequestMessage->mMethod)
1370 {
1372 {
1374 if (mTurnAsyncSocketHandler) mTurnAsyncSocketHandler->onBindFailure(getSocketDescriptor(), asio::error_code(reTurn::ResponseTimeout, asio::error::misc_category), (requestEntry->mDest ? *(requestEntry->mDest) : StunTuple()));
1375 }
1376 break;
1378 {
1381 }
1382 break;
1384 {
1387 }
1388 break;
1390 {
1393 }
1394 if (mCloseAfterDestroyAllocationFinishes)
1395 {
1396 mHaveAllocation = false;
1397 actualClose();
1398 }
1399 break;
1401 {
1402 // Note: ChannelBind can happen after SetActiveDestination, after a sendTo, or during a channel bind refresh
1405 }
1406 break;
1407 default:
1408 resip_assert(false);
1409 }
1410 }
1411}
1412
1413void
1414TurnAsyncSocket::clearActiveRequestMap()
1415{
1416 // Clear out active request map - !slg! TODO this really should happen from the io service thread
1417 RequestMap::iterator it = mActiveRequestMap.begin();
1418 for(;it != mActiveRequestMap.end(); it++)
1419 {
1420 it->second->stopTimer();
1421 }
1422 mActiveRequestMap.clear();
1423}
1424
1425void
1426TurnAsyncSocket::startAllocationTimer()
1427{
1428 mAllocationTimer.expires_after(seconds((mLifetime*5)/8)); // Allocation refresh should sent before 3/4 lifetime - use 5/8 lifetime
1429 mAllocationTimer.async_wait(weak_bind<AsyncSocketBase, void(const asio::error_code&)>(mAsyncSocketBase.shared_from_this(), std::bind(&TurnAsyncSocket::allocationTimerExpired, this, std::placeholders::_1)));
1430}
1431
1432void
1433TurnAsyncSocket::cancelAllocationTimer()
1434{
1435 mAllocationTimer.cancel();
1436}
1437
1438void
1439TurnAsyncSocket::allocationTimerExpired(const asio::error_code& e)
1440{
1441 if(!e)
1442 doRefreshAllocation(mLifetime);
1443 }
1444
1445void
1446TurnAsyncSocket::startChannelBindingTimer(unsigned short channel)
1447{
1448 ChannelBindingTimerMap::iterator it = mChannelBindingTimers.find(channel);
1449 if(it==mChannelBindingTimers.end())
1450 {
1451 std::pair<ChannelBindingTimerMap::iterator,bool> ret =
1452 mChannelBindingTimers.insert(std::pair<unsigned short, asio::steady_timer*>(channel, new asio::steady_timer(mIOService)));
1453 resip_assert(ret.second);
1454 it = ret.first;
1455 }
1456 it->second->expires_after(seconds(TURN_CHANNEL_BINDING_REFRESH_SECONDS));
1457 it->second->async_wait(weak_bind<AsyncSocketBase, void(const asio::error_code&)>( mAsyncSocketBase.shared_from_this(), std::bind(&TurnAsyncSocket::channelBindingTimerExpired, this, std::placeholders::_1, channel)));
1458}
1459
1460void
1461TurnAsyncSocket::cancelChannelBindingTimers()
1462{
1463 // Cleanup ChannelBinding Timers
1464 ChannelBindingTimerMap::iterator it = mChannelBindingTimers.begin();
1465 for(;it!=mChannelBindingTimers.end();it++)
1466 {
1467 it->second->cancel();
1468 delete it->second;
1469 }
1470 mChannelBindingTimers.clear();
1471}
1472
1473void
1474TurnAsyncSocket::channelBindingTimerExpired(const asio::error_code& e, unsigned short channel)
1475{
1476 if(!e)
1477 {
1478 RemotePeer* remotePeer = mChannelManager.findRemotePeerByChannel(channel);
1479 if(remotePeer)
1480 {
1481 doChannelBinding(*remotePeer);
1482 }
1483 }
1484}
1485
1486void
1491
1492} // namespace
1493
1494
1495/* ====================================================================
1496
1497 Copyright (c) 2023-2026, SIP Specturm, Inc. http://sipspectrum.com
1498 Copyright (c) 2007-2008, Plantronics, Inc.
1499 All rights reserved.
1500
1501 Redistribution and use in source and binary forms, with or without
1502 modification, are permitted provided that the following conditions are
1503 met:
1504
1505 1. Redistributions of source code must retain the above copyright
1506 notice, this list of conditions and the following disclaimer.
1507
1508 2. Redistributions in binary form must reproduce the above copyright
1509 notice, this list of conditions and the following disclaimer in the
1510 documentation and/or other materials provided with the distribution.
1511
1512 3. Neither the name of Plantronics nor the names of its contributors
1513 may be used to endorse or promote products derived from this
1514 software without specific prior written permission.
1515
1516 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1517 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1518 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
1519 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
1520 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
1521 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
1522 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
1523 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
1524 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1525 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
1526 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1527
1528 ==================================================================== */
#define DebugLog(args_)
Definition Logger.hxx:128
#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 SOFTWARE_STRING
#define TURN_CHANNEL_BINDING_REFRESH_SECONDS
#define UDP_FINAL_REQUEST_TIME
#define TCP_RESPONSE_TIME
#define UDP_MAX_RETRANSMITS
#define UDP_RT0
#define DEFAULT_RETRANS_INTERVAL_MS
asio::ip::address & getConnectedAddress() noexcept
static std::shared_ptr< DataBuffer > allocateBuffer(size_t size)
Utility API.
unsigned short getConnectedPort() const noexcept
std::function< void(unsigned int)> BeforeClosedHandler
virtual void setOnBeforeSocketClosedFp(BeforeClosedHandler fp)
bool isConnected() const noexcept
virtual void send(const StunTuple &destination, const std::shared_ptr< DataBuffer > &data)
Note: destination is ignored for TCP and TLS connections.
virtual void connect(const std::string &address, unsigned short port)=0
RemotePeer * findRemotePeerByChannel(unsigned short channelNumber)
RemotePeer * findRemotePeerByPeerAddress(const StunTuple &peerAddress)
RemotePeer * createChannelBinding(const StunTuple &peerTuple)
const StunTuple & getPeerTuple() const
static const uint16_t StunClassSuccessResponse
static const uint16_t SharedSecretMethod
static const uint16_t BindMethod
static const uint8_t PropsNone
StunAtrError mErrorCode
static const uint16_t TurnSendMethod
StunAtrUnknown mUnknownRequiredAttributes
static const uint16_t StunClassErrorResponse
static const uint16_t StunClassRequest
void calculateHmacKey(resip::Data &hmacKey, const resip::Data &longtermAuthenticationPassword)
static const uint16_t TurnAllocateMethod
bool checkMessageIntegrity(const resip::Data &hmacKey)
static const uint32_t RequestedTransportTcp
static const uint16_t TurnDataMethod
resip::Data * mRealm
static void setStunAtrAddressFromTuple(StunAtrAddress &address, const StunTuple &tuple)
static const uint16_t TurnRefreshMethod
resip::Data * mNonce
static const uint32_t RequestedTransportUdp
void setIcePriority(uint32_t priority)
static const uint16_t TurnChannelBindMethod
static const uint16_t StunClassIndication
static void setTupleFromStunAtrAddress(StunTuple &tuple, const StunAtrAddress &address)
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
virtual void onRefreshSuccess(unsigned int socketDesc, unsigned int lifetimeSecs)=0
virtual void onReceiveFailure(unsigned int socketDesc, const asio::error_code &e)=0
virtual void onBindSuccess(unsigned int socketDesc, const StunTuple &reflexiveTuple, const StunTuple &stunServerTuple)=0
virtual void onSharedSecretFailure(unsigned int socketDesc, const asio::error_code &e)=0
virtual void onClearActiveDestinationFailure(unsigned int socketDesc, const asio::error_code &e)=0
virtual void onBindFailure(unsigned int socketDesc, const asio::error_code &e, const StunTuple &stunServerTuple)=0
virtual void onAllocationSuccess(unsigned int socketDesc, const StunTuple &reflexiveTuple, const StunTuple &relayTuple, unsigned int lifetimeSecs, unsigned int bandwidth, uint64_t reservationToken)=0
virtual void onIncomingBindRequestProcessed(unsigned int socketDesc, const StunTuple &sourceTuple)=0
virtual void onChannelBindRequestSent(unsigned int socketDesc, unsigned short channelNumber)=0
virtual void onChannelBindSuccess(unsigned int socketDesc, unsigned short channelNumber)=0
virtual void onSharedSecretSuccess(unsigned int socketDesc, const char *username, unsigned int usernameSize, const char *password, unsigned int passwordSize)=0
virtual void onChannelBindFailure(unsigned int socketDesc, const asio::error_code &e)=0
virtual void onRefreshFailure(unsigned int socketDesc, const asio::error_code &e)=0
virtual void onSetActiveDestinationSuccess(unsigned int socketDesc)=0
virtual void onAllocationFailure(unsigned int socketDesc, const asio::error_code &e)=0
virtual void onClearActiveDestinationSuccess(unsigned int socketDesc)=0
virtual void onReceiveSuccess(unsigned int socketDesc, const asio::ip::address &address, unsigned short port, const std::shared_ptr< DataBuffer > &data)=0
void refreshAllocation(unsigned int lifetimeSecs)
virtual unsigned int getSocketDescriptor()=0
resip::RecursiveMutex mHandlerMutex
virtual void sendUnframed(const std::shared_ptr< DataBuffer > &data)
void connectivityCheck(const StunTuple &targetAddr, uint32_t peerRflxPriority, bool setIceControlling, bool setIceControlled, unsigned int numRetransmits, unsigned int retrans_iterval_ms)
virtual void sendToFramed(const asio::ip::address &address, unsigned short port, const std::shared_ptr< DataBuffer > &data)
static unsigned int UnspecifiedBandwidth
void setUsernameAndPassword(const char *username, const char *password, bool shortTermAuth=false)
void handleReceivedData(const asio::ip::address &address, unsigned short port, const std::shared_ptr< DataBuffer > &data)
void connect(const std::string &address, unsigned short port)
StunTuple::TransportType mRelayTransportType
void createAllocation(unsigned int lifetimeSecs=UnspecifiedLifetime, unsigned int bandwidth=UnspecifiedBandwidth, unsigned char requestedPortProps=StunMessage::PropsNone, uint64_t reservationToken=UnspecifiedToken, StunTuple::TransportType requestedTransportType=StunTuple::None)
virtual void sendToUnframed(const asio::ip::address &address, unsigned short port, const std::shared_ptr< DataBuffer > &data)
TurnAsyncSocketHandler * mTurnAsyncSocketHandler
asio::io_context & mIOService
void setSoftware(const char *software)
StunTuple::TransportType mRequestedTransportType
static asio::ip::address UnspecifiedIpAddress
virtual void sendTo(const asio::ip::address &address, unsigned short port, const char *buffer, size_t size)
virtual void sendFramed(const std::shared_ptr< DataBuffer > &data)
static unsigned short UnspecifiedToken
void setActiveDestination(const asio::ip::address &address, unsigned short port)
TurnAsyncSocket(asio::io_context &ioService, AsyncSocketBase &asyncSocketBase, TurnAsyncSocketHandler *turnAsyncSocketHandler, const asio::ip::address &address=UnspecifiedIpAddress, unsigned short port=0)
static unsigned int UnspecifiedLifetime
void handleChannelData(const std::shared_ptr< DataBuffer > &data)
virtual void setOnBeforeSocketClosedFp(AsyncSocketBase::BeforeClosedHandler fp)
virtual void send(const char *buffer, size_t size)
void setLocalPassword(const char *password)
virtual void disableTurnAsyncHandler()
An alternative to std::string, encapsulates an arbitrary buffer of bytes.
Definition Data.hxx:90
const char * c_str() const
Returns a null-terminated string representing.
Definition Data.cxx:1097
Data & append(const char *str, size_type len)
Appends the specified number of bytes to the end of this Data.
Definition Data.cxx:1058
bool empty() const noexcept
Checks whether the Data is empty.
Definition Data.hxx:591
size_type size() const noexcept
Returns the number of bytes in this Data.
Definition Data.hxx:599
This file is used to handle compatibility fixes/tweaks so reSIProcate can function on multiple platfo...
#define RESIP_LAMBDA_CAPTURE_ALL_AND_THIS
Lambda capture default that also captures 'this'.
Definition compat.hxx:37
static const ErrorType ErrorParsingMessage
Definition ErrorCode.hxx:18
static const ErrorType NotConnected
Definition ErrorCode.hxx:28
static const ErrorType InvalidChannelNumberReceived
Definition ErrorCode.hxx:24
static const ErrorType BadMessageIntegrity
Definition ErrorCode.hxx:17
static const ErrorType MissingAttributes
Definition ErrorCode.hxx:25
static const ErrorType ResponseTimeout
Definition ErrorCode.hxx:22
static const ErrorType NoAllocation
Definition ErrorCode.hxx:19
static const ErrorType AlreadyAllocated
Definition ErrorCode.hxx:29
static const ErrorType UnknownRequiredAttributes
Definition ErrorCode.hxx:31
static const ErrorType StrayResponse
Definition ErrorCode.hxx:30
static const ErrorType FrameError
Definition ErrorCode.hxx:23
static const ErrorType InvalidRequestedTransport
Definition ErrorCode.hxx:27
dcm! – add UnusedChecking(_enum) below;
std::unique_lock< RecursiveMutex > RecursiveLock
Definition Lock.hxx:29
unsigned short uint16_t
Definition stdint.h:84
unsigned int uint32_t
Definition stdint.h:85
unsigned __int64 uint64_t
Definition stdint.h:95