reSIProcate
SIP stack, Dialog Usage Manager and related libraries
Loading...
Searching...
No Matches
TurnSocket.cxx
Go to the documentation of this file.
1#include "TurnSocket.hxx"
2#include "../AsyncSocketBase.hxx"
3#include "ErrorCode.hxx"
4#include <rutil/Lock.hxx>
6#include <rutil/Logger.hxx>
7#include "../ReTurnSubsystem.hxx"
8
9#define RESIPROCATE_SUBSYSTEM ReTurnSubsystem::RETURN
10
11using namespace std;
12using namespace resip;
13
14#define UDP_RT0 100 // RTO - Estimate of Roundtrip time - 100ms is recommened for fixed line transport - the initial value should be configurable
15 // Should also be calculation this on the fly
16#define UDP_MAX_RETRANSMITS 7 // Defined by RFC5389 (Rc) - should be configurable
17#define TCP_RESPONSE_TIME 39500 // Defined by RFC5389 (Ti) - should be configurable
18#define UDP_Rm 16 // Defined by RFC5389 - should be configurable
19#define UDP_FINAL_REQUEST_TIME (UDP_RT0 * UDP_Rm) // Defined by RFC5389
20
21//#define TURN_CHANNEL_BINDING_REFRESH_SECONDS 20 // TESTING only
22#define TURN_CHANNEL_BINDING_REFRESH_SECONDS 240 // 4 minuntes - this is one minute before the permission will expire, Note: ChannelBinding refreshes also refresh permissions
23
24#define SOFTWARE_STRING "reTURN Sync Client 0.3 - RFC5389/turn-12"
25
26#ifdef BOOST_ASIO_HAS_STD_CHRONO
27using namespace std::chrono;
28#else
29#include <chrono>
30using namespace std::chrono;
31#endif
32
33namespace reTurn {
34
35// Initialize static members
36unsigned int TurnSocket::UnspecifiedLifetime = 0xFFFFFFFF;
37unsigned int TurnSocket::UnspecifiedBandwidth = 0xFFFFFFFF;
38unsigned short TurnSocket::UnspecifiedToken = 0;
39asio::ip::address TurnSocket::UnspecifiedIpAddress = asio::ip::make_address("0.0.0.0");
40
41TurnSocket::TurnSocket(const asio::ip::address& address, unsigned short port) :
42 mLocalBinding(StunTuple::None /* Set properly by sub class */, address, port),
43 mSoftware(SOFTWARE_STRING),
44 mHaveAllocation(false),
45 mActiveDestination(0),
46 mReadTimer(mIOService),
47 mConnected(false)
48{
49}
50
54
55asio::error_code
56TurnSocket::requestSharedSecret(char* username, unsigned int usernameSize,
57 char* password, unsigned int passwordSize)
58{
59 asio::error_code errorCode;
60 resip::RecursiveLock lock(mMutex);
61
62 // Should we check here if TLS and deny?
63
64 // Ensure Connected
65 if(!mConnected)
66 {
67 return asio::error_code(reTurn::NotConnected, asio::error::misc_category);
68 }
69
70 // Form Shared Secret request
71 StunMessage request;
73
74 // Get Response
75 StunMessage* response = sendRequestAndGetResponse(request, errorCode, false);
76 if(response == 0)
77 {
78 return errorCode;
79 }
80
81 // Check if success or not
82 if(response->mHasErrorCode)
83 {
84 errorCode = asio::error_code(response->mErrorCode.errorClass * 100 + response->mErrorCode.number, asio::error::misc_category);
85 delete response;
86 return errorCode;
87 }
88
89 // Copy username and password to callers buffer - checking sizes first
90 if(!response->mHasUsername || !response->mHasPassword)
91 {
92 WarningLog(<< "Stun response message for SharedSecretRequest is missing username and/or password!");
93 errorCode = asio::error_code(reTurn::MissingAuthenticationAttributes, asio::error::misc_category);
94 delete response;
95 return errorCode;
96 }
97
98 if(response->mUsername->size() > usernameSize || response->mPassword->size() > passwordSize)
99 {
100 WarningLog( << "Stun response message for SharedSecretRequest contains data that is too large to return!");
101 errorCode = asio::error_code(reTurn::BufferTooSmall, asio::error::misc_category);
102 delete response;
103 return errorCode;
104 }
105
106 // Copy username and password to passed in buffers
107 memcpy(username, response->mUsername->c_str(), response->mUsername->size()+1);
108 memcpy(password, response->mPassword->c_str(), response->mPassword->size()+1);
109
110 // All was well - return 0 errorCode
111 delete response;
112 return errorCode;
113}
114
115void
116TurnSocket::setUsernameAndPassword(const char* username, const char* password, bool shortTermAuth)
117{
118 mUsername = username;
119 mPassword = password;
120 if(shortTermAuth)
121 {
122 // If we are using short term auth, then use short term password as HMAC key
123 mHmacKey = password;
124 }
125}
126
127void
128TurnSocket::setSoftware(const char* software)
129{
130 mSoftware = software;
131
132 const uint32_t unpaddedSize = (unsigned int)mSoftware.size();
133 if(unpaddedSize > 0)
134 {
135 // Pad size to a multiple of 4, to help compatibility with older clients
136 const uint32_t remainder = unpaddedSize % 4,
137 paddedSize = remainder ? unpaddedSize + 4 - remainder : unpaddedSize;
138
139 while(mSoftware.size() < paddedSize)
140 mSoftware.append(" ", 1);
141 }
142}
143
144asio::error_code
146{
147 asio::error_code errorCode;
148 resip::RecursiveLock lock(mMutex);
149
150 // Ensure Connected
151 if(!mConnected)
152 {
153 return asio::error_code(reTurn::NotConnected, asio::error::misc_category);
154 }
155
156 // Form Stun Bind request
157 StunMessage request;
159
160 StunMessage* response = sendRequestAndGetResponse(request, errorCode);
161 if(response == 0)
162 {
163 return errorCode;
164 }
165
167 if(response->mHasXorMappedAddress)
168 {
170 }
171 else if(response->mHasMappedAddress) // Only look at MappedAddress if XorMappedAddress is not found - for backwards compatibility
172 {
174 }
175
176 // Check if success or not
177 if(response->mHasErrorCode)
178 {
179 errorCode = asio::error_code(response->mErrorCode.errorClass * 100 + response->mErrorCode.number, asio::error::misc_category);
180 }
181
182 delete response;
183 return errorCode;
184}
185
186asio::error_code
187TurnSocket::createAllocation(unsigned int lifetime,
188 unsigned int bandwidth,
189 unsigned char requestedProps,
190 uint64_t reservationToken,
191 StunTuple::TransportType requestedTransportType)
192{
193 asio::error_code errorCode;
194 resip::RecursiveLock lock(mMutex);
195
196 // Store Allocation Properties
197 mRequestedLifetime = lifetime;
198 mRequestedBandwidth = bandwidth;
199 mRequestedProps = requestedProps;
200 mReservationToken = reservationToken;
201 mRequestedTransportType = requestedTransportType;
202
203 // Ensure Connected
204 if(!mConnected)
205 {
206 return asio::error_code(reTurn::NotConnected, asio::error::misc_category);
207 }
208
210 {
211 return asio::error_code(reTurn::AlreadyAllocated, asio::error::misc_category);
212 }
213
214 // Form Turn Allocate request
215 StunMessage request;
218 {
219 request.mHasTurnLifetime = true;
221 }
222
224 {
225 request.mHasTurnBandwidth = true;
227 }
228
230 {
232 }
233 request.mHasTurnRequestedTransport = true;
235 {
237 }
239 mLocalBinding.getTransportType() != StunTuple::UDP) // Ensure client is not requesting TCP over a UDP transport
240 {
242 }
243 else
244 {
245 return asio::error_code(reTurn::InvalidRequestedTransport, asio::error::misc_category);
246 }
247
249 {
250 request.mHasTurnEvenPort = true;
252 }
253 else if(mReservationToken != 0)
254 {
255 request.mHasTurnReservationToken = true;
257 }
258
259 StunMessage* response = sendRequestAndGetResponse(request, errorCode);
260 if(response == 0)
261 {
262 return errorCode;
263 }
264
265 if(response->mHasXorMappedAddress)
266 {
269 }
270 if(response->mHasTurnXorRelayedAddress)
271 {
272 // Transport Type is requested type or socket type
274 {
276 }
277 else
278 {
280 }
282 }
283 if(response->mHasTurnLifetime)
284 {
285 mLifetime = response->mTurnLifetime;
286 }
287 if(response->mHasTurnBandwidth)
288 {
289 mBandwidth = response->mTurnBandwidth;
290 }
291
292 // Check if success or not
293 if(response->mHasErrorCode)
294 {
295 errorCode = asio::error_code(response->mErrorCode.errorClass * 100 + response->mErrorCode.number, asio::error::misc_category);
296 delete response;
297 return errorCode;
298 }
299
300 // All was well - return 0 errorCode
301 if(mLifetime != 0)
302 {
303 mHaveAllocation = true;
304 mAllocationRefreshTime = time(0) + ((mLifetime*5)/8); // Allocation refresh should sent before 3/4 lifetime - use 5/8 lifetime
305 }
306 delete response;
307 return errorCode;
308}
309
310asio::error_code
312{
313 asio::error_code errorCode;
314 resip::RecursiveLock lock(mMutex);
315
316 // Form Turn Allocate request
317 StunMessage request;
320 {
321 request.mHasTurnLifetime = true;
323 }
325 {
326 request.mHasTurnBandwidth = true;
328 }
329
330 StunMessage* response = sendRequestAndGetResponse(request, errorCode);
331 if(response == 0)
332 {
333 return errorCode;
334 }
335
336 // Check if success or not
337 if(response->mHasErrorCode)
338 {
339 if(mRequestedLifetime != 0 ||
340 (response->mErrorCode.errorClass == 4 && response->mErrorCode.number == 37)) // if we receive a 437 response to a refresh, we should ensure the allocation is destroyed
341 {
342 mHaveAllocation = false;
343 }
344
345 errorCode = asio::error_code(response->mErrorCode.errorClass * 100 + response->mErrorCode.number, asio::error::misc_category);
346 delete response;
347 return errorCode;
348 }
349
350 // All was well - return 0 errorCode
351 if(mLifetime != 0)
352 {
353 mHaveAllocation = true;
354 mAllocationRefreshTime = time(0) + ((mLifetime*5)/8); // Allocation refresh should sent before 3/4 lifetime - use 5/8 lifetime
355 }
356 else
357 {
358 mHaveAllocation = false;
359 }
360 delete response;
361 return errorCode;
362}
363
364asio::error_code
366{
367 resip::RecursiveLock lock(mMutex);
369 {
375
376 return refreshAllocation();
377 }
378 else
379 {
380 return asio::error_code(reTurn::NoAllocation, asio::error::misc_category);
381 }
382}
383
384StunTuple&
389
390StunTuple&
395
396unsigned int
398{
399 return mLifetime;
400}
401
402unsigned int
404{
405 return mBandwidth;
406}
407
408asio::error_code
409TurnSocket::setActiveDestination(const asio::ip::address& address, unsigned short port)
410{
411 asio::error_code errorCode;
412 resip::RecursiveLock lock(mMutex);
413
414 // ensure there is an allocation
415 if(!mHaveAllocation)
416 {
417 if(mConnected)
418 {
419 // TODO - Disconnect
420 }
421 return connect(address.to_string(), port);
422 //return asio::error_code(reTurn::NoAllocation, asio::error::misc_category);
423 }
424
425 // Ensure Connected
426 if(!mConnected)
427 {
428 return asio::error_code(reTurn::NotConnected, asio::error::misc_category);
429 }
430
431 // Setup Remote Peer
432 StunTuple remoteTuple(mRelayTuple.getTransportType(), address, port);
433 RemotePeer* remotePeer = mChannelManager.findRemotePeerByPeerAddress(remoteTuple);
434 if(remotePeer)
435 {
436 mActiveDestination = remotePeer;
437 }
438 else
439 {
440 // No remote peer yet (ie. not data sent or received from remote peer) - so create one
443 errorCode = channelBind(*mActiveDestination);
444 }
445
446 return errorCode;
447}
448
449asio::error_code
450TurnSocket::channelBind(RemotePeer& remotePeer)
451{
452 asio::error_code ret;
453
454 // Form Channel Bind request
455 StunMessage request;
457
458 // Set headers
459 request.mHasTurnChannelNumber = true;
460 request.mTurnChannelNumber = remotePeer.getChannel();
461 request.mCntTurnXorPeerAddress = 1;
463
464 StunMessage* response = sendRequestAndGetResponse(request, ret);
465 if(response == 0)
466 {
467 return ret;
468 }
469
470 // Check if success or not
471 if(response->mHasErrorCode)
472 {
473 ret = asio::error_code(response->mErrorCode.errorClass * 100 + response->mErrorCode.number, asio::error::misc_category);
474 delete response;
475 return ret;
476 }
477
478 remotePeer.refresh();
479 remotePeer.setChannelConfirmed();
481
482 return ret;
483}
484
485asio::error_code
487{
488 asio::error_code errorCode;
489 resip::RecursiveLock lock(mMutex);
490
491 // ensure there is an allocation
492 if(!mHaveAllocation)
493 {
494 return asio::error_code(reTurn::NoAllocation, asio::error::misc_category);
495 }
496
498
499 return errorCode;
500}
501
502asio::error_code
503TurnSocket::send(const char* buffer, unsigned int size)
504{
505 // Allow raw data to be sent if there is no allocation
507 {
508 return rawWrite(buffer, size);
509 }
510
512 {
513 return asio::error_code(reTurn::NoActiveDestination, asio::error::misc_category);
514 }
515
516 return sendTo(*mActiveDestination, buffer, size);
517}
518
519asio::error_code
520TurnSocket::sendTo(const asio::ip::address& address, unsigned short port, const char* buffer, unsigned int size)
521{
522 resip::RecursiveLock lock(mMutex);
523 // ensure there is an allocation
524 if(!mHaveAllocation)
525 {
526 return asio::error_code(reTurn::NoAllocation, asio::error::misc_category);
527 }
528
529 // Setup Remote Peer
530 StunTuple remoteTuple(mRelayTuple.getTransportType(), address, port);
531 RemotePeer* remotePeer = mChannelManager.findRemotePeerByPeerAddress(remoteTuple);
532 if(!remotePeer)
533 {
534 // No remote peer - then just create a temp one for sending
535 // For blocking TurnSocket we do not send ChannelBind unless setActiveDestination is used
536 // to avoid blocking on the sendTo call
537 RemotePeer remotePeer(remoteTuple, 0 /* channel - not important */, 0 /* lifetime - not important */);
538 return sendTo(remotePeer, buffer, size);
539 }
540 else
541 {
542 return sendTo(*remotePeer, buffer, size);
543 }
544}
545
546asio::error_code
547TurnSocket::sendTo(RemotePeer& remotePeer, const char* buffer, unsigned int size)
548{
549 resip::RecursiveLock lock(mMutex);
550
551 // Check to see if an allocation refresh is required - if so send it and make sure it was sucessful
552 asio::error_code ret = checkIfAllocationRefreshRequired();
553 if(ret)
554 {
555 return ret;
556 }
557
558 // Check to see if a channel binding refresh is required - if so send it and make sure it was successful
559 ret = checkIfChannelBindingRefreshRequired();
560 if(ret)
561 {
562 return ret;
563 }
564
565 if(remotePeer.isChannelConfirmed())
566 {
567 // send framed data to active destination
568 char framing[4];
569 unsigned short channelNumber = remotePeer.getChannel();
570 channelNumber = htons(channelNumber);
571 memcpy(&framing[0], &channelNumber, 2);
572
573 uint16_t turnDataSize = size;
574 turnDataSize = htons(turnDataSize);
575 memcpy((void*)&framing[2], &turnDataSize, 2);
576
577 std::vector<asio::const_buffer> bufs;
578 bufs.push_back(asio::buffer(framing, sizeof(framing)));
579 bufs.push_back(asio::buffer(buffer, size));
581 {
582 // Over TCP and TLS, padded to a multiple of 4 bytes (RFC 5766 11.5, RFC 8656 12.5)
583 static const char zeros[3] = { 0, 0, 0 };
584 bufs.push_back(asio::buffer(zeros, AsyncSocketBase::channelDataPadding(size)));
585 }
586
587 return rawWrite(bufs);
588 }
589 else
590 {
591 // Data must be wrapped in a Send Indication
592 // Wrap data in a SendInd
593 StunMessage ind;
595 ind.mCntTurnXorPeerAddress = 1;
596 ind.mTurnXorPeerAddress[0].port = remotePeer.getPeerTuple().getPort();
597 if(remotePeer.getPeerTuple().getAddress().is_v6())
598 {
599 ind.mTurnXorPeerAddress[0].family = StunMessage::IPv6Family;
600 memcpy(&ind.mTurnXorPeerAddress[0].addr.ipv6, remotePeer.getPeerTuple().getAddress().to_v6().to_bytes().data(), sizeof(ind.mTurnXorPeerAddress[0].addr.ipv6));
601 }
602 else
603 {
604 ind.mTurnXorPeerAddress[0].family = StunMessage::IPv4Family;
605 ind.mTurnXorPeerAddress[0].addr.ipv4 = remotePeer.getPeerTuple().getAddress().to_v4().to_uint();
606 }
607 if(size > 0)
608 {
609 ind.setTurnData(buffer, size);
610 }
611
612 // Send indication to Turn Server
613 unsigned int msgsize = ind.stunEncodeMessage(mWriteBuffer, sizeof(mWriteBuffer));
614 return rawWrite(mWriteBuffer, msgsize);
615 }
616}
617
618void
620{
622 {
623 WarningLog(<< "TurnSocket: " << mReceivedWhileWaiting.size() << " packets received while waiting for a response, not yet received - dropping the oldest");
624 mReceivedWhileWaiting.pop_front();
625 }
626 mReceivedWhileWaiting.emplace_back(mReadBuffer, mReadBuffer + size);
627}
628
629asio::error_code
630TurnSocket::receive(char* buffer, unsigned int& size, unsigned int timeout, asio::ip::address* sourceAddress, unsigned short* sourcePort)
631{
632 asio::error_code errorCode;
633 resip::RecursiveLock lock(mMutex);
634 bool done = false;
635
636 // TODO - rethink this scheme so that we don't need to copy recieved data
637 // TODO - if we loop around more than once - timeout needs to be adjusted
638
639 while(!done)
640 {
641 done = true;
642
643 // Wait for response - unless peers' data arrived while waiting for a response to a request
644 unsigned int readSize;
645 if(!mHaveAllocation)
646 {
647 mReceivedWhileWaiting.clear();
648 }
649 if(!mReceivedWhileWaiting.empty())
650 {
651 readSize = (unsigned int)mReceivedWhileWaiting.front().size();
652 memcpy(mReadBuffer, mReceivedWhileWaiting.front().data(), readSize);
653 mReceivedWhileWaiting.pop_front();
654 }
655 else
656 {
657 errorCode = rawRead(timeout, &readSize, sourceAddress, sourcePort); // Note: SourceAddress and sourcePort may be overwritten below if from Turn Relay
658 if(errorCode)
659 {
660 return errorCode;
661 }
662 }
663
664 // Note if this is a UDP RFC3489 back compat socket, then allocations are not allowed and handleRawData will always be used
665 if(!mHaveAllocation)
666 {
667 return handleRawData(mReadBuffer, readSize, readSize, buffer, size);
668 }
669
670 if(readSize > 4)
671 {
672 // What it is, by its first byte (RFC 7983): 0-3 STUN, 64-127 TURN ChannelData (channel
673 // numbers 0x4000-0x7FFF). Everything the TURN server sends is one or the other - peers'
674 // data comes through it - so anything else (eg. sent straight to us) is skipped.
675 const unsigned char firstByte = (unsigned char)mReadBuffer[0];
676 if(firstByte <= 3)
677 {
678 // StunMessage
679 StunMessage* stunMsg = new StunMessage(mLocalBinding, mConnectedTuple, &mReadBuffer[0], readSize);
680 if(!stunMsg->isValid())
681 {
682 DebugLog(<< "TurnSocket::receive: discarding a packet that isn't valid STUN");
683 done = false;
684 }
685 else
686 {
687 unsigned int tempsize = size;
688 errorCode = handleStunMessage(*stunMsg, buffer, tempsize, sourceAddress, sourcePort);
689 if(!errorCode && tempsize == 0) // Signifies that a Stun/Turn request was received and there is nothing to return to receive caller
690 {
691 done = false;
692 }
693 else
694 {
695 size = tempsize;
696 }
697 }
698 delete stunMsg;
699 }
700 else if((firstByte & 0xC0) != 0x40)
701 {
702 DebugLog(<< "TurnSocket::receive: discarding a packet that is neither STUN nor channel data");
703 done = false;
704 }
705 else // Channel Data Message
706 {
707 unsigned short channelNumber;
708 memcpy(&channelNumber, &mReadBuffer[0], 2);
709 channelNumber = ntohs(channelNumber);
710
711 RemotePeer* remotePeer = mChannelManager.findRemotePeerByChannel(channelNumber);
712 if(remotePeer)
713 {
714 uint16_t dataLen;
715 memcpy(&dataLen, &mReadBuffer[2], 2);
716 dataLen = ntohs(dataLen);
717
718 if(sourceAddress)
719 {
720 *sourceAddress = remotePeer->getPeerTuple().getAddress();
721 }
722 if(sourcePort)
723 {
724 *sourcePort = remotePeer->getPeerTuple().getPort();
725 }
726 errorCode = handleRawData(&mReadBuffer[4], readSize-4, dataLen, buffer, size);
727 }
728 else
729 {
730 // Invalid Channel - teardown?
731 errorCode = asio::error_code(reTurn::InvalidChannelNumberReceived, asio::error::misc_category);
732 done = true;
733 }
734 }
735 }
736 else // size <= 4
737 {
738 // Less data than frame size received
739 errorCode = asio::error_code(reTurn::FrameError, asio::error::misc_category);
740 done = true;
741 }
742 }
743 return errorCode;
744}
745
746asio::error_code
747TurnSocket::receiveFrom(const asio::ip::address& address, unsigned short port, char* buffer, unsigned int& size, unsigned int timeout)
748{
749 asio::ip::address sourceAddress;
750 unsigned short sourcePort;
751 bool done = false;
752 asio::error_code errorCode;
753 resip::RecursiveLock lock(mMutex);
754
755 while(!done)
756 {
757 done = true;
758 errorCode = receive(buffer, size, timeout, &sourceAddress, &sourcePort);
759 if(!errorCode)
760 {
761 if(sourceAddress != address || sourcePort != port)
762 {
763 WarningLog(<< "Recevied message but not from requested address/port - Discarding.");
764 done = false;
765 }
766 }
767 }
768 return errorCode;
769}
770
771asio::error_code
772TurnSocket::handleRawData(char* data, unsigned int dataSize, unsigned int expectedSize, char* buffer, unsigned int& bufferSize)
773{
774 asio::error_code errorCode;
775
776 if(dataSize != expectedSize)
777 {
778 // TODO - fix read logic so that we can read in chuncks
779 WarningLog(<< "Did not read entire message: read=" << dataSize << " wanted=" << expectedSize);
780 return asio::error_code(reTurn::ReadError, asio::error::misc_category);
781 }
782
783 if(dataSize > bufferSize)
784 {
785 // Passed in buffer is not large enough
786 WarningLog(<< "Passed in buffer not large enough.");
787 return asio::error_code(reTurn::BufferTooSmall, asio::error::misc_category);
788 }
789
790 // Copy data to return buffer
791 memcpy(buffer, data, dataSize);
792 bufferSize = dataSize;
793
794 return errorCode;
795}
796
797asio::error_code
798TurnSocket::handleStunMessage(StunMessage& stunMessage, char* buffer, unsigned int& size, asio::ip::address* sourceAddress, unsigned short* sourcePort)
799{
800 asio::error_code errorCode;
801 if(stunMessage.isValid())
802 {
803 if(stunMessage.mClass == StunMessage::StunClassIndication && stunMessage.mMethod == StunMessage::TurnDataMethod)
804 {
805 if(stunMessage.mUnknownRequiredAttributes.numAttributes > 0)
806 {
807 // Unknown Comprehension-Required Attributes found
808 WarningLog(<< "DataInd with unknown comprehension required attributes.");
809 return asio::error_code(reTurn::UnknownRequiredAttributes, asio::error::misc_category);
810 }
811
812 if(stunMessage.mCntTurnXorPeerAddress == 0 || !stunMessage.mHasTurnData)
813 {
814 // Missing RemoteAddress or TurnData attribute
815 WarningLog(<< "DataInd missing attributes.");
816 return asio::error_code(reTurn::MissingAttributes, asio::error::misc_category);
817 }
818
819 StunTuple remoteTuple;
820 remoteTuple.setTransportType(mRelayTuple.getTransportType());
821 StunMessage::setTupleFromStunAtrAddress(remoteTuple, stunMessage.mTurnXorPeerAddress[0]);
822
823 // Should we record all the remoteTuples we have sent to before, and reject this message if
824 // not from one of the those endpoints?
825
826 if(stunMessage.mTurnData->size() > size)
827 {
828 // Passed in buffer is not large enough
829 WarningLog(<< "Passed in buffer not large enough.");
830 return asio::error_code(reTurn::BufferTooSmall, asio::error::misc_category);
831 }
832
833 memcpy(buffer, stunMessage.mTurnData->data(), stunMessage.mTurnData->size());
834 size = (unsigned int)stunMessage.mTurnData->size();
835
836 if(sourceAddress != 0)
837 {
838 *sourceAddress = remoteTuple.getAddress();
839 }
840 if(sourcePort != 0)
841 {
842 *sourcePort = remoteTuple.getPort();
843 }
844 }
845 else if(stunMessage.mClass == StunMessage::StunClassRequest && stunMessage.mMethod == StunMessage::BindMethod)
846 {
847 // Note: handling of BindRequest is not fully backwards compatible with RFC3489 - it is inline with RFC5389
848 StunMessage response;
849
850 // form the outgoing message
851 response.mMethod = StunMessage::BindMethod;
852
853 // Copy over TransactionId
854 response.mHeader.magicCookieAndTid = stunMessage.mHeader.magicCookieAndTid;
855
856 if(stunMessage.mUnknownRequiredAttributes.numAttributes > 0)
857 {
858 // Unknown Comprehension-Required Attributes found
859 WarningLog(<< "BindRequest with unknown comprehension required attributes.");
860
861 response.mClass = StunMessage::StunClassErrorResponse;
862
863 // Add unknown attributes
864 response.mHasUnknownAttributes = true;
865 response.mUnknownAttributes = stunMessage.mUnknownRequiredAttributes;
866 }
867 else
868 {
870
871 // Add XOrMappedAddress to response
872 response.mHasXorMappedAddress = true;
873 StunMessage::setStunAtrAddressFromTuple(response.mXorMappedAddress, stunMessage.mRemoteTuple);
874 }
875
876 // Add Software Attribute
877 if(!mSoftware.empty())
878 {
879 response.setSoftware(mSoftware.c_str());
880 }
881
882 // send bind response to local client
883 unsigned int bufferSize = 512; // enough room for Stun Header + XorMapped Address (v6) or Unknown Attributes + Software Attribute;
884 resip::Data buffer(bufferSize, resip::Data::Preallocate);
885 unsigned int writeSize = response.stunEncodeMessage((char*)buffer.data(), bufferSize);
886
887 errorCode = rawWrite(buffer.data(), writeSize);
888 size = 0; // go back to receiving
889 }
890 else if(stunMessage.mClass == StunMessage::StunClassIndication && stunMessage.mMethod == StunMessage::BindMethod)
891 {
892 // Nothing to do
893 size = 0;
894 }
895 else if(stunMessage.mClass == StunMessage::StunClassSuccessResponse || stunMessage.mClass == StunMessage::StunClassErrorResponse)
896 {
897 // Received a stray or response retranmission - ignore
898 size = 0;
899 }
900 else
901 {
902 // TODO - handle others ????
903 }
904 }
905 else
906 {
907 WarningLog(<< "Read Invalid StunMsg.");
908 return asio::error_code(reTurn::ErrorParsingMessage, asio::error::misc_category);
909 }
910 return errorCode;
911}
912
913void
914TurnSocket::startReadTimer(unsigned int timeout)
915{
916 if(timeout != 0)
917 {
918 mReadTimer.expires_after(milliseconds(timeout));
919 mReadTimer.async_wait(std::bind(&TurnSocket::handleRawReadTimeout, this, std::placeholders::_1));
920 }
921}
922
923void
924TurnSocket::handleRawRead(const asio::error_code& errorCode, size_t bytesRead)
925{
926 mBytesRead = bytesRead;
927 mReadErrorCode = errorCode;
928 mReadTimer.cancel();
929}
930
931void
932TurnSocket::handleRawReadTimeout(const asio::error_code& errorCode)
933{
934 if(!errorCode)
935 {
936 cancelSocket();
937 }
938}
939
940asio::error_code
941TurnSocket::checkIfAllocationRefreshRequired()
942{
943 if(mHaveAllocation && (time(0) >= mAllocationRefreshTime))
944 {
945 // Do allocation refresh
946 return refreshAllocation();
947 }
948 return asio::error_code(); // 0
949}
950
951asio::error_code
952TurnSocket::checkIfChannelBindingRefreshRequired()
953{
954 asio::error_code ret; // 0
956 {
957 time_t now = time(0);
958 ChannelBindingRefreshTimeMap::iterator it = mChannelBindingRefreshTimes.begin();
959 for(;it!=mChannelBindingRefreshTimes.end();it++)
960 {
961 if(it->second != 0 && now >= it->second)
962 {
963 it->second = 0; // will be reset by channelBind if success
964
965 // Do channel binding refresh
966 RemotePeer* remotePeer = mChannelManager.findRemotePeerByChannel(it->first);
967 if(remotePeer)
968 {
969 ret = channelBind(*remotePeer);
970 }
971 }
972 }
973 }
974 return ret;
975}
976
978TurnSocket::sendRequestAndGetResponse(StunMessage& request, asio::error_code& errorCode, bool addAuthInfo)
979{
980 bool sendRequest = true;
981 bool reliableTransport = mLocalBinding.getTransportType() != StunTuple::UDP;
982 unsigned int timeout = reliableTransport ? TCP_RESPONSE_TIME : UDP_RT0;
983 unsigned int totalTime = 0;
984 unsigned int requestsSent = 0;
985 unsigned int readsize = 0;
986
987 // Add Software Attribute
988 if(!mSoftware.empty())
989 {
990 request.setSoftware(mSoftware.c_str());
991 }
992
993 if(addAuthInfo && !mUsername.empty() && !mHmacKey.empty())
994 {
995 request.mHasMessageIntegrity = true;
996 request.setUsername(mUsername.c_str());
997 request.mHmacKey = mHmacKey;
998 if(!mRealm.empty())
999 {
1000 request.setRealm(mRealm.c_str());
1001 }
1002 if(!mNonce.empty())
1003 {
1004 request.setNonce(mNonce.c_str());
1005 }
1006 }
1007
1008 unsigned int writesize = request.stunEncodeMessage(mWriteBuffer, sizeof(mWriteBuffer));
1009
1010 while(true)
1011 {
1012 if(sendRequest)
1013 {
1014 // Send request to Turn Server
1015 if(requestsSent > 0)
1016 {
1017 DebugLog(<< "TurnSocket: retranmitting request...");
1018 }
1019 requestsSent++;
1020 errorCode = rawWrite(mWriteBuffer, writesize);
1021 if(errorCode)
1022 {
1023 return 0;
1024 }
1025 sendRequest = false;
1026 }
1027
1028 // Wait for response
1029 errorCode = rawRead(timeout, &readsize);
1030 if(errorCode)
1031 {
1032 if(errorCode == asio::error::operation_aborted)
1033 {
1034 totalTime += timeout;
1035 if(reliableTransport || requestsSent == UDP_MAX_RETRANSMITS)
1036 {
1037 InfoLog(<< "Timed out waiting for Stun response!");
1038 errorCode = asio::error_code(reTurn::ResponseTimeout, asio::error::misc_category);
1039 return 0;
1040 }
1041
1042 // timed out and should retransmit - calculate next timeout
1043 if(requestsSent == UDP_MAX_RETRANSMITS - 1)
1044 {
1045 timeout = UDP_FINAL_REQUEST_TIME;
1046 }
1047 else
1048 {
1049 timeout = (timeout*2);
1050 }
1051 sendRequest = true;
1052 continue;
1053 }
1054 return 0;
1055 }
1056
1057 if(readsize > 4)
1058 {
1059 // What it is, by its first byte (RFC 7983): 0-3 STUN, 64-127 TURN ChannelData (with an
1060 // allocation). Anything else isn't for us here (eg. sent straight to us): skip it.
1061 const unsigned char firstByte = (unsigned char)mReadBuffer[0];
1062 if(firstByte > 3 && !(mHaveAllocation && (firstByte & 0xC0) == 0x40))
1063 {
1064 DebugLog(<< "TurnSocket: discarding a packet that is neither STUN nor channel data, while waiting for a response");
1065 continue; // read next message
1066 }
1067 if(firstByte <= 3)
1068 {
1069 StunMessage* response = new StunMessage(mLocalBinding, mConnectedTuple, &mReadBuffer[0], readsize);
1070
1071 // If response is valid and has no unknown comprehension-required attributes then we can process it
1072 if(response->isValid() && response->mUnknownRequiredAttributes.numAttributes == 0)
1073 {
1074 // Not a response: a Data indication (a peer's data, kept for receive()), or
1075 // something not for us here
1076 if(response->mClass != StunMessage::StunClassSuccessResponse && response->mClass != StunMessage::StunClassErrorResponse)
1077 {
1078 if(mHaveAllocation && response->mClass == StunMessage::StunClassIndication && response->mMethod == StunMessage::TurnDataMethod)
1079 {
1080 queueReceived(readsize);
1081 }
1082 delete response;
1083 continue; // read next message
1084 }
1085
1086 if(!response->checkMessageIntegrity(request.mHmacKey))
1087 {
1088 WarningLog(<< "Stun response message integrity is bad!");
1089 delete response;
1090 errorCode = asio::error_code(reTurn::BadMessageIntegrity, asio::error::misc_category);
1091 return 0;
1092 }
1093
1094 // Check that TID matches request
1095 if(!(response->mHeader.magicCookieAndTid == request.mHeader.magicCookieAndTid))
1096 {
1097 InfoLog(<< "Stun response TID does not match request - discarding!");
1098 delete response;
1099 continue; // read next message
1100 }
1101
1102 // If response is a 401, then store Realm, nonce, and hmac key
1103 // If a realm and nonce attributes are present and the response is a 401 or 438 (Nonce Expired),
1104 // then re-issue request with new auth attributes
1105 if(response->mHasRealm &&
1106 response->mHasNonce &&
1107 response->mHasErrorCode &&
1108 response->mErrorCode.errorClass == 4 &&
1109 ((response->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
1110 response->mErrorCode.number == 38))
1111 {
1112 mNonce = *response->mNonce;
1113 mRealm = *response->mRealm;
1114 response->calculateHmacKey(mHmacKey, mUsername, mRealm, mPassword);
1115
1116 // Re-Issue reques (with new TID)
1117 request.createHeader(request.mClass, request.mMethod); // updates TID
1118 delete response;
1119 return sendRequestAndGetResponse(request, errorCode, true);
1120 }
1121
1122 errorCode = asio::error_code(reTurn::Success, asio::error::misc_category);
1123 return response;
1124 }
1125 else
1126 {
1127 WarningLog(<< "Stun response message is invalid!");
1128 delete response;
1129 errorCode = asio::error_code(reTurn::ErrorParsingMessage, asio::error::misc_category);
1130 return 0;
1131 }
1132 }
1133 else // Channel Data Message: a peer's data, kept for receive()
1134 {
1135 queueReceived(readsize);
1136 continue; // read next message
1137 }
1138 }
1139 else
1140 {
1141 // Less data than frame size received
1142 errorCode = asio::error_code(reTurn::FrameError, asio::error::misc_category);
1143 return 0;
1144 }
1145 }
1146}
1147
1148} // namespace
1149
1150
1151/* ====================================================================
1152
1153 Copyright (c) 2026 SIP Spectrum, Inc. https://www.sipspectrum.com
1154 Copyright (c) 2007-2008, Plantronics, Inc.
1155 All rights reserved.
1156
1157 Redistribution and use in source and binary forms, with or without
1158 modification, are permitted provided that the following conditions are
1159 met:
1160
1161 1. Redistributions of source code must retain the above copyright
1162 notice, this list of conditions and the following disclaimer.
1163
1164 2. Redistributions in binary form must reproduce the above copyright
1165 notice, this list of conditions and the following disclaimer in the
1166 documentation and/or other materials provided with the distribution.
1167
1168 3. Neither the name of Plantronics nor the names of its contributors
1169 may be used to endorse or promote products derived from this
1170 software without specific prior written permission.
1171
1172 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1173 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1174 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
1175 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
1176 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
1177 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
1178 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
1179 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
1180 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1181 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
1182 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1183
1184 ==================================================================== */
#define DebugLog(args_)
Definition Logger.hxx:128
#define InfoLog(args_)
Definition Logger.hxx:131
#define WarningLog(args_)
Definition Logger.hxx:134
#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
static size_t channelDataPadding(size_t length)
Over TCP and TLS, a TURN ChannelData message is padded to a multiple of 4 bytes, the padding not coun...
RemotePeer * findRemotePeerByChannel(unsigned short channelNumber)
RemotePeer * findRemotePeerByPeerAddress(const StunTuple &peerAddress)
RemotePeer * createChannelBinding(const StunTuple &peerTuple)
unsigned short getChannel() const
const StunTuple & getPeerTuple() const
bool isChannelConfirmed() const
static const uint8_t IPv4Family
define stun address families
static const uint16_t StunClassSuccessResponse
static const uint16_t SharedSecretMethod
static const uint16_t BindMethod
StunAtrAddress mMappedAddress
static const uint8_t PropsNone
StunAtrError mErrorCode
void createHeader(uint16_t stunclass, uint16_t method)
static const uint16_t TurnSendMethod
uint64_t mTurnReservationToken
resip::Data * mUsername
uint8_t mTurnRequestedTransport
static const uint16_t StunClassErrorResponse
resip::Data * mPassword
static const uint16_t StunClassRequest
StunAtrAddress mTurnXorPeerAddress[TURN_MAX_XOR_PEER_ADDR]
static const uint16_t TurnAllocateMethod
StunAtrAddress mXorMappedAddress
static const uint32_t RequestedTransportTcp
static const uint16_t TurnDataMethod
static void setStunAtrAddressFromTuple(StunAtrAddress &address, const StunTuple &tuple)
static const uint8_t IPv6Family
static const uint16_t TurnRefreshMethod
static const uint32_t RequestedTransportUdp
StunAtrAddress mTurnXorRelayedAddress
static const uint16_t TurnChannelBindMethod
TurnAtrEvenPort mTurnEvenPort
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
void setTransportType(TransportType transport)
Definition StunTuple.hxx:42
resip::Data mNonce
void setSoftware(const char *software)
char mWriteBuffer[8192]
asio::error_code destroyAllocation()
virtual void cancelSocket()=0
void handleRawRead(const asio::error_code &errorCode, size_t bytesRead)
resip::Data mUsername
StunTuple & getReflexiveTuple()
StunTuple mReflexiveTuple
asio::steady_timer mReadTimer
unsigned int mBandwidth
asio::error_code receive(char *buffer, unsigned int &size, unsigned int timeout, asio::ip::address *sourceAddress=0, unsigned short *sourcePort=0)
resip::Data mHmacKey
static unsigned int UnspecifiedBandwidth
ChannelManager mChannelManager
static asio::ip::address UnspecifiedIpAddress
asio::error_code sendTo(const asio::ip::address &address, unsigned short port, const char *buffer, unsigned int size)
resip::Data mPassword
StunTuple mLocalBinding
virtual ~TurnSocket()
TurnSocket(const asio::ip::address &address=UnspecifiedIpAddress, unsigned short port=0)
virtual asio::error_code rawWrite(const char *buffer, unsigned int size)=0
unsigned int mLifetime
virtual asio::error_code connect(const std::string &address, unsigned short port)=0
asio::error_code receiveFrom(const asio::ip::address &address, unsigned short port, char *buffer, unsigned int &size, unsigned int timeout)
resip::Data mRealm
asio::error_code mReadErrorCode
asio::error_code clearActiveDestination()
time_t mAllocationRefreshTime
ChannelBindingRefreshTimeMap mChannelBindingRefreshTimes
asio::error_code bindRequest()
unsigned int getLifetime()
asio::error_code requestSharedSecret(char *username, unsigned int usernameSize, char *password, unsigned int passwordSize)
unsigned char mRequestedProps
char mReadBuffer[8192]
RemotePeer * mActiveDestination
void startReadTimer(unsigned int timeout)
void setUsernameAndPassword(const char *username, const char *password, bool shortTermAuth=false)
asio::error_code refreshAllocation()
virtual asio::error_code rawRead(unsigned int timeout, unsigned int *bytesRead, asio::ip::address *sourceAddress=0, unsigned short *sourcePort=0)=0
uint64_t mReservationToken
asio::error_code setActiveDestination(const asio::ip::address &address, unsigned short port)
static unsigned short UnspecifiedToken
unsigned int mRequestedLifetime
static const size_t MaxReceivedWhileWaiting
static unsigned int UnspecifiedLifetime
asio::error_code createAllocation(unsigned int lifetime=UnspecifiedLifetime, unsigned int bandwidth=UnspecifiedBandwidth, unsigned char requestedProps=StunMessage::PropsNone, uint64_t reservationToken=UnspecifiedToken, StunTuple::TransportType requestedTransportType=StunTuple::None)
StunTuple & getRelayTuple()
StunTuple mConnectedTuple
asio::error_code send(const char *buffer, unsigned int size)
unsigned int mRequestedBandwidth
unsigned int getBandwidth()
resip::Data mSoftware
void handleRawReadTimeout(const asio::error_code &errorCode)
std::deque< std::vector< char > > mReceivedWhileWaiting
StunTuple::TransportType mRequestedTransportType
void queueReceived(unsigned int size)
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
static const PreallocateType Preallocate
used only to disambiguate constructors
Definition Data.hxx:116
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
static const ErrorType MissingAuthenticationAttributes
Definition ErrorCode.hxx:15
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 Success
Definition ErrorCode.hxx:10
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 BufferTooSmall
Definition ErrorCode.hxx:16
static const ErrorType NoActiveDestination
Definition ErrorCode.hxx:20
static const ErrorType ReadError
Definition ErrorCode.hxx:21
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 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