reSIProcate
SIP stack, Dialog Usage Manager and related libraries
Loading...
Searching...
No Matches
RequestHandler.cxx
Go to the documentation of this file.
1#include "RequestHandler.hxx"
2#include <ctype.h>
3#include <fstream>
4#include <sstream>
5#include <string>
6
7#include "TurnAllocation.hxx"
8#include "AsyncSocketBase.hxx"
9#include "StunAuth.hxx"
10#include <rutil/Random.hxx>
11#include <rutil/Timer.hxx>
12#include <rutil/ParseBuffer.hxx>
14#include <rutil/Logger.hxx>
15#include "ReTurnSubsystem.hxx"
16
17#define RESIPROCATE_SUBSYSTEM ReTurnSubsystem::RETURN
18
19using namespace std;
20using namespace resip;
21
22namespace reTurn {
23
24// !slg! TODO these need to be made into settings
25#define DEFAULT_BANDWIDTH 100 // 100 kbit/s - enough for G711 RTP ?slg? what do we want this to be?
26
28 const asio::ip::address* prim3489Address, unsigned short* prim3489Port,
29 const asio::ip::address* alt3489Address, unsigned short* alt3489Port)
30 : mTurnManager(turnManager)
31{
32 if(prim3489Address && prim3489Port && alt3489Address && alt3489Port)
33 {
34 mRFC3489SupportEnabled = true;
35 mPrim3489Address = *prim3489Address;
36 mPrim3489Port = *prim3489Port;
37 mAlt3489Address = *alt3489Address;
38 mAlt3489Port = *alt3489Port;
39 }
40 else
41 {
42 mRFC3489SupportEnabled = false;
43 }
44 mPrivateNonceKey = Random::getCryptoRandomHex(24);
45}
46
48RequestHandler::processStunMessage(AsyncSocketBase* turnSocket, TurnAllocationManager& turnAllocationManager, StunMessage& request, StunMessage& response, bool isRFC3489BackwardsCompatServer)
49{
51
52 response.mRemoteTuple = request.mRemoteTuple; // Default to send response back to sender
53
54 if(handleAuthentication(request, response))
55 {
56 // Check if there were unknown required attributes
58 {
59 InfoLog(<< "Received Request with unknown comprehension-required attributes. Sending 420. Sender=" << request.mRemoteTuple);
60 buildErrorResponse(response, 420, "Unknown attribute");
61 response.mHasUnknownAttributes = true;
63 }
64 else
65 {
66 // Request is authenticated, process it
67 switch(request.mClass)
68 {
70 switch (request.mMethod)
71 {
73 result = processStunBindingRequest(request, response, isRFC3489BackwardsCompatServer);
74 break;
75
77 result = processStunSharedSecretRequest(request, response);
78 break;
79
81 result = processTurnAllocateRequest(turnSocket, turnAllocationManager, request, response);
82 if(result != NoResponseToSend)
83 {
84 // Add an XOrMappedAddress to all TurnAllocateResponses
85 response.mHasXorMappedAddress = true;
87 }
88 break;
89
91 result = processTurnRefreshRequest(turnAllocationManager, request, response);
92 break;
93
95 result = processTurnCreatePermissionRequest(turnAllocationManager, request, response);
96 break;
97
99 result = processTurnChannelBindRequest(turnAllocationManager, request, response);
100 break;
101
102 default:
103 buildErrorResponse(response, 400, "Invalid Request Method");
104 break;
105 }
106 break;
107
109 result = NoResponseToSend; // Indications don't have responses
110 switch (request.mMethod)
111 {
113 processTurnSendIndication(turnAllocationManager, request);
114 break;
115
117 // A Bind indication is simply a keepalive with no response required
118 break;
119
120 case StunMessage::TurnDataMethod: // Don't need to handle these - only sent by server, never received
121 default:
122 // Unknown indication - just ignore
123 break;
124 }
125 break;
126
129 default:
130 // A server should never receive a response
131 result = NoResponseToSend;
132 break;
133 }
134 }
135 }
136
137 if(result != NoResponseToSend)
138 {
139 // Fill in common response properties
140 response.mMethod = request.mMethod;
141
142 // Copy over TransactionId
144
145 if (!getConfig().mSoftwareName.empty())
146 {
147 response.setSoftware(getConfig().mSoftwareName.c_str());
148 }
149
150 // If fingerprint is used in request, then use fingerprint in response
151 if(request.mHasFingerprint)
152 {
153 response.mHasFingerprint = true;
154 }
155 }
156
157 return result;
158}
159
160void
161RequestHandler::buildErrorResponse(StunMessage& response, unsigned short errorCode, const char* msg)
162{
164 response.setErrorCode(errorCode, msg);
165}
166
167void
168RequestHandler::buildErrorResponseWithRealmAndNonce(StunMessage& response, unsigned short errorCode, const char* msg, const Data& realm, const StunMessage& request)
169{
170 response.mClass = StunMessage::StunClassErrorResponse;
171 response.setErrorCode(errorCode, msg);
172 response.setRealm(realm.c_str());
173 // Add a random nonce value that is expirable
174 Data nonce(100, Data::Preallocate);
175 Data timestamp(Timer::getTimeMs() / 1000);
176 generateNonce(timestamp, nonce, request);
177 response.setNonce(nonce.c_str());
178}
179
180void
181RequestHandler::generateNonce(const Data& timestamp, Data& nonce, const StunMessage& request)
182{
183 nonce += timestamp;
184 nonce += ":";
185 Data noncePrivate(100, Data::Preallocate);
186 {
187 DataStream ds(noncePrivate);
188 ds << timestamp
189 << ":" << request.getRemoteTuple()
190 << ":" << mPrivateNonceKey;
191 }
192 nonce += noncePrivate.md5();
193}
194
195RequestHandler::CheckNonceResult
196RequestHandler::checkNonce(const Data& nonce, const StunMessage& request)
197{
198 ParseBuffer pb(nonce.data(), nonce.size());
199 if (!pb.eof() && !isdigit(static_cast< unsigned char >(*pb.position())))
200 {
201 DebugLog(<< "Invalid nonce. Expected timestamp.");
202 return NotValid;
203 }
204 const char* anchor = pb.position();
205 pb.skipToChar(':');
206 if (pb.eof())
207 {
208 DebugLog(<< "Invalid nonce. Expected timestamp terminator.");
209 return NotValid;
210 }
211 uint64_t now = Timer::getTimeMs()/1000;
212 Data creationTimeData;
213 uint64_t creationTime;
214 pb.data(creationTimeData, anchor);
215 creationTime = creationTimeData.convertUInt64();
216 if((now-creationTime) <= getConfig().mNonceLifetime)
217 {
218 // If nonce hasn't expired yet - ensure this is a nonce we generated
219 Data nonceToMatch(100, Data::Preallocate);
220 generateNonce(creationTimeData, nonceToMatch, request);
221 if(nonceToMatch == nonce)
222 {
223 return Valid;
224 }
225 else
226 {
227 DebugLog(<< "Invalid nonce. Not generated by this server.");
228 return NotValid;
229 }
230 }
231 else
232 {
233 DebugLog(<< "Invalid nonce. Expired.");
234 return Expired;
235 }
236}
237
238bool
239RequestHandler::handleAuthentication(StunMessage& request, StunMessage& response)
240{
241 // Don't authenticate indications/responses, or, shared secret and binding requests
242 if((request.mClass != StunMessage::StunClassRequest) ||
243 (request.mClass == StunMessage::StunClassRequest && request.mMethod == StunMessage::SharedSecretMethod) ||
244 (request.mClass == StunMessage::StunClassRequest && request.mMethod == StunMessage::BindMethod))
245 {
246 return true;
247 }
248
249 if (!request.mHasMessageIntegrity)
250 {
251 InfoLog(<< "Received Request with no Message Integrity. Sending 401. Sender=" << request.mRemoteTuple);
252 buildErrorResponseWithRealmAndNonce(response, 401, "Unauthorized (no MessageIntegrity)", getConfig().mAuthenticationRealm, request);
253 return false;
254 }
255 else
256 {
257 if (!request.mHasUsername)
258 {
259 WarningLog(<< "No Username and contains MessageIntegrity. Sending 400. Sender=" << request.mRemoteTuple);
260 buildErrorResponse(response, 400, "Bad Request (no Username and contains MessageIntegrity)");
261 return false;
262 }
263
264 if(!request.mHasRealm)
265 {
266 WarningLog(<< "No Realm. Sending 400. Sender=" << request.mRemoteTuple);
267 buildErrorResponse(response, 400, "Bad Request (No Realm)");
268 return false;
269 }
270 if(!request.mHasNonce)
271 {
272 WarningLog(<< "No Nonce and contains realm. Sending 400. Sender=" << request.mRemoteTuple);
273 buildErrorResponse(response, 400, "Bad Request (No Nonce and contains Realm)");
274 return false;
275 }
276 switch(checkNonce(*request.mNonce, request))
277 {
278 case Valid:
279 // Do nothing
280 break;
281 case Expired:
282 WarningLog(<< "Nonce expired. Sending 438. Sender=" << request.mRemoteTuple);
283 buildErrorResponseWithRealmAndNonce(response, 438, "Stale Nonce", getConfig().mAuthenticationRealm, request);
284 return false;
285 break;
286 case NotValid:
287 default:
288 WarningLog(<< "Invalid Nonce. Sending 400. Sender=" << request.mRemoteTuple);
289 buildErrorResponse(response, 400, "BadRequest (Invalid Nonce)");
290 return false;
291 break;
292 }
293
294 StackLog(<< "Validating username: " << *request.mUsername); // Note: we ensure username is present above
295
296 // !slg! need to determine whether the USERNAME contains a known entity, and is known
297 // within the realm of the REALM attribute of the request
298 if (!getConfig().isUserNameValid(*request.mUsername, *request.mRealm))
299 {
300 WarningLog(<< "Invalid username '" << *request.mUsername << "' or realm '" << *request.mRealm << "' (username unknown or potential AuthorizationRealm mismatch). Sending 401. Sender=" << request.mRemoteTuple);
301 buildErrorResponseWithRealmAndNonce(response, 401, "Unauthorized", getConfig().mAuthenticationRealm, request);
302 return false;
303 }
304
305 StackLog(<< "Validating MessageIntegrity");
306
307 // Need to calculate HMAC across entire message - for LongTermAuthentication we use
308 // username:realm:password string as the key
309 Data hmacKey;
310 resip_assert(request.mHasUsername); // Note: This is checked above
311
312 request.calculateHmacKeyForHa1(hmacKey, getConfig().getHa1ForUsername(*request.mUsername, *request.mRealm));
313
314 if(!request.checkMessageIntegrity(hmacKey))
315 {
316 WarningLog(<< "MessageIntegrity is bad. Sending 401. Sender=" << request.mRemoteTuple);
317 buildErrorResponseWithRealmAndNonce(response, 401, "Unauthorized", getConfig().mAuthenticationRealm, request);
318 return false;
319 }
320
321 // need to compute this later after message is filled in
322 response.mHasMessageIntegrity = true;
323 response.mHmacKey = hmacKey; // Used to later calculate Message Integrity during encoding
324 }
325
326 return true;
327}
328
330RequestHandler::processStunBindingRequest(StunMessage& request, StunMessage& response, bool isRFC3489BackwardsCompatServer)
331{
333
334 // form the outgoing message
336
337 // Add XOrMappedAddress to response if RFC5389 sender
338 if(request.hasMagicCookie())
339 {
340 response.mHasXorMappedAddress = true;
341 StunMessage::setStunAtrAddressFromTuple(response.mXorMappedAddress, request.mRemoteTuple);
342 }
343 else
344 {
345 // Add Mapped address to response
346 response.mHasMappedAddress = true;
347 StunMessage::setStunAtrAddressFromTuple(response.mMappedAddress, request.mRemoteTuple);
348
349 if(0) // TODO - setting to add XOR address even if older client
350 {
351 response.mHasXorMappedAddress = true;
352 StunMessage::setStunAtrAddressFromTuple(response.mXorMappedAddress, request.mRemoteTuple);
353 }
354 }
355
356 // the following code is for RFC3489 backward compatibility
357 if(mRFC3489SupportEnabled && isRFC3489BackwardsCompatServer)
358 {
359 StunTuple sendFromTuple;
360 StunTuple changeTuple;
361
362 sendFromTuple.setTransportType(request.mLocalTuple.getTransportType());
363 changeTuple.setTransportType(request.mLocalTuple.getTransportType());
364 changeTuple.setAddress(request.mLocalTuple.getAddress() == mPrim3489Address ? mAlt3489Address : mPrim3489Address);
365 changeTuple.setPort(request.mLocalTuple.getPort() == mPrim3489Port ? mAlt3489Port : mPrim3489Port);
366 uint32_t changeRequest = request.mHasChangeRequest ? request.mChangeRequest : 0;
367
368 if(changeRequest & StunMessage::ChangeIpFlag && changeRequest & StunMessage::ChangePortFlag)
369 {
371 sendFromTuple.setAddress(changeTuple.getAddress());
372 sendFromTuple.setPort(changeTuple.getPort());
373 }
374 else if(changeRequest & StunMessage::ChangePortFlag)
375 {
377 sendFromTuple.setAddress(request.mLocalTuple.getAddress());
378 sendFromTuple.setPort(changeTuple.getPort());
379 }
380 else if(changeRequest & StunMessage::ChangeIpFlag)
381 {
382 result = RespondFromAlternateIp;
383 sendFromTuple.setAddress(changeTuple.getAddress());
384 sendFromTuple.setPort(request.mLocalTuple.getPort());
385 }
386 else
387 {
388 // default to send from receiving transport
389 sendFromTuple.setAddress(request.mLocalTuple.getAddress());
390 sendFromTuple.setPort(request.mLocalTuple.getPort());
391 }
392
393 // Add source address - for RFC3489 backwards compatibility
394 response.mHasSourceAddress = true;
395 StunMessage::setStunAtrAddressFromTuple(response.mSourceAddress, sendFromTuple);
396
397 // Add changed address - for RFC3489 backward compatibility
398 response.mHasChangedAddress = true;
399 StunMessage::setStunAtrAddressFromTuple(response.mChangedAddress, changeTuple);
400
401 // If Response-Address is present, then response should be sent here instead of source address to be
402 // compliant with RFC3489. In this case we need to add a REFLECTED-FROM attribute
403 if(request.mHasResponseAddress)
404 {
405 StunMessage::setTupleFromStunAtrAddress(response.mRemoteTuple, request.mResponseAddress);
406
407 // If the username is present and is greater than or equal to 92 bytes long, then we assume this username
408 // was obtained from a shared secret request
409 if (request.mHasUsername && (request.mUsername->size() >= 92 ) )
410 {
411 // Extract source address that sent the shared secret request from the encoded username and use this in response address
412 StunTuple responseTuple;
413 request.getTupleFromUsername(responseTuple);
414
415 response.mHasReflectedFrom = true;
416 StunMessage::setStunAtrAddressFromTuple(response.mReflectedFrom, responseTuple);
417 }
418 else
419 {
420 response.mHasReflectedFrom = true;
421 StunMessage::setStunAtrAddressFromTuple(response.mReflectedFrom, request.mRemoteTuple);
422 }
423 }
424 }
425
426 return result;
427}
428
430RequestHandler::processStunSharedSecretRequest(StunMessage& request, StunMessage& response)
431{
432 // Only allow shared secret requests on TLS transport
433 if(request.mLocalTuple.getTransportType() != StunTuple::TLS)
434 {
435 WarningLog(<< "Invalid transport for shared secret request. TLS required. Sending 433. Sender=" << request.mRemoteTuple);
436 buildErrorResponse(response, 433, "Invalid transport. TLS required.");
438 }
439
440 // form the outgoing success response
442
443 // Set the username and password
444 response.createUsernameAndPassword();
445
447}
448
450RequestHandler::processTurnAllocateRequest(AsyncSocketBase* turnSocket, TurnAllocationManager& turnAllocationManager, StunMessage& request, StunMessage& response)
451{
452 // Turn Allocate requests must be authenticated (note: if this attribute is present
453 // then handleAuthentication would have validated authentication info)
454 if(!request.mHasMessageIntegrity)
455 {
456 WarningLog(<< "Turn allocate request without authentication. Sending 401. Sender=" << request.mRemoteTuple);
457 buildErrorResponseWithRealmAndNonce(response, 401, "Missing Message Integrity", getConfig().mAuthenticationRealm, request);
459 }
460
461 DebugLog(<< "Allocation request received: localTuple=" << request.mLocalTuple << ", remoteTuple=" << request.mRemoteTuple);
462
463 TurnAllocation* allocation = turnAllocationManager.findTurnAllocation(TurnAllocationKey(request.mLocalTuple, request.mRemoteTuple));
464
465 // If this is a subsequent allocation request, return error
466 if(allocation)
467 {
468 WarningLog(<< "Allocation requested but already exists. Sending 437. Sender=" << request.mRemoteTuple);
469 buildErrorResponse(response, 437, "Allocation Mismatch");
471 }
472
473 // The user may only have so many allocations at once (MaxAllocationsPerUser)
474 if(mTurnManager.allocationQuotaReached(*request.mUsername))
475 {
476 WarningLog(<< "User " << *request.mUsername << " already has " << getConfig().mMaxAllocationsPerUser << " allocations. Sending 486. Sender=" << request.mRemoteTuple);
477 buildErrorResponse(response, 486, "Allocation Quota Reached");
479 }
480
481 // Build the Allocation Tuple
482 StunTuple allocationTuple(request.mLocalTuple.getTransportType(), // Default to receiving transport
483 request.mLocalTuple.getAddress(), // default ip address is at the discretion of the server
484 0); // port to be populated later
485
486 // Check for requested properties
487 if(request.mHasTurnRequestedTransport)
488 {
489 if(request.mTurnRequestedTransport != StunMessage::RequestedTransportUdp) // We only support UDP allocations right now
490 {
491 WarningLog(<< "Invalid transport requested. Sending 442. Sender=" << request.mRemoteTuple);
492 buildErrorResponse(response, 442, "Unsupported Transport Protocol");
494 }
495 allocationTuple.setTransportType(StunTuple::UDP);
496 }
497 else
498 {
499 WarningLog(<< "Missing requested transport header. Sending 400. Sender=" << request.mRemoteTuple);
500 buildErrorResponse(response, 400, "Bad Request - Missing requested transport");
502 }
503
504 // Check if Don't Fragment attribute is present - if so return an error - TODO implement DF bit, then remove this check
505 if(request.mHasTurnDontFragment)
506 {
507 WarningLog(<< "Turn allocate request with Don't Fragment requested, not yet implemented. Sending 420. Sender=" << request.mRemoteTuple);
508 buildErrorResponse(response, 420, "Don't Fragment not yet implemented");
510 }
511
512 // Check if bandwidth is available
513 if(request.mHasTurnBandwidth)
514 {
515 // TODO - bandwidth check - if insufficient send 507 (Insufficient Bandwidth Capacity)
516 }
517
518 if(request.mHasTurnEvenPort && request.mHasTurnReservationToken)
519 {
520 WarningLog(<< "Both Even Port and Reservation Token attributes are present. Sending 400. Sender=" << request.mRemoteTuple);
521 buildErrorResponse(response, 400, "Bad request - both Even Port and Reservation Token present");
523 }
524
526 if(request.mHasTurnLifetime)
527 {
528 // Check if the requested value is greater than the server max
529 if(request.mTurnLifetime > getConfig().mMaxAllocationLifetime)
530 {
532 }
533 // The server should ignore requests for a lifetime less than it's default
534 else if(request.mTurnLifetime > getConfig().mDefaultAllocationLifetime)
535 {
536 lifetime = request.mTurnLifetime;
537 }
538 }
539
540 // If the relay can't be bound to the port (eg. another program uses it, or the OS has excluded
541 // it), the port is freed and the next one tried, until each in the range has been - except a
542 // reserved port, which the reservation token names
543 const unsigned int portsInRange = getConfig().mAllocationPortRangeMax - getConfig().mAllocationPortRangeMin + 1;
544 for(unsigned int attempt = 1; ; ++attempt)
545 {
546 unsigned short port = 0; // Default to Any free port
547 if(request.mHasTurnEvenPort)
548 {
549 if(request.mTurnEvenPort.propType == StunMessage::PropsPortEven)
550 {
551 // Attempt to allocate an even port
552 port = mTurnManager.allocateEvenPort(allocationTuple.getTransportType());
553 }
554 else if(request.mTurnEvenPort.propType == StunMessage::PropsPortPair)
555 {
556 // Attempt to allocate an even port, with a free adjacent odd port
557 port = mTurnManager.allocateEvenPortPair(allocationTuple.getTransportType());
558
559 // Add Reservation Token to response - for now just use reserved port number as token
560 response.mHasTurnReservationToken = port != 0;
561 response.mTurnReservationToken = port + 1;
562 }
563 if(port == 0)
564 {
565 WarningLog(<< "Unable to allocate requested port. Sending 508. Sender=" << request.mRemoteTuple);
566 buildErrorResponse(response, 508, "Insufficient Port Capacity");
568 }
569 }
570
571 if(request.mHasTurnReservationToken)
572 {
573 // Try to allocate reserved port - for now reservation token is reserved port number
574 port = (unsigned short)request.mTurnReservationToken;
575 if(!mTurnManager.allocatePort(allocationTuple.getTransportType(), port, true /* allocate reserved */))
576 {
577 WarningLog(<< "Unable to allocate requested port - bad reservation token. Sending 508. Sender=" << request.mRemoteTuple);
578 buildErrorResponse(response, 508, "Insufficient Port Capacity");
580 }
581 }
582
583 if(port == 0)
584 {
585 // Allocate any available port
586 port = mTurnManager.allocateAnyPort(allocationTuple.getTransportType());
587 if(port == 0)
588 {
589 WarningLog(<< "Unable to allocate port. Sending 508. Sender=" << request.mRemoteTuple);
590 buildErrorResponse(response, 508, "Insufficient Port Capacity");
592 }
593 }
594
595 allocationTuple.setPort(port);
596
597 // We now have an internal 5-Tuple and an allocation tuple - create the allocation
598 try
599 {
600 allocation = new TurnAllocation(mTurnManager,
601 turnAllocationManager,
602 turnSocket,
603 request.mLocalTuple,
604 request.mRemoteTuple,
605 StunAuth(*request.mUsername, response.mHmacKey), // The HMAC is already calculated and added to the response in handleAuthentication
606 allocationTuple,
607 lifetime);
608 }
609 catch (const asio::system_error&)
610 {
611 mTurnManager.deallocatePort(allocationTuple.getTransportType(), port);
612 ErrLog(<< "Error allocating socket for allocation. Sending 500. Sender=" << request.mRemoteTuple);
613 response.mHasTurnReservationToken = false;
614 buildErrorResponse(response, 500, "Server Error");
616 }
617
618 if(allocation->startRelay())
619 {
620 break;
621 }
622 delete allocation; // frees its port (and a port reserved with it), and its place in the user's quota
623 allocation = 0;
624 if(request.mHasTurnReservationToken || attempt >= portsInRange)
625 {
626 ErrLog(<< "Unable to start an allocation relay. Sending 508. Sender=" << request.mRemoteTuple);
627 response.mHasTurnReservationToken = false;
628 buildErrorResponse(response, 508, "Insufficient Port Capacity");
630 }
631 WarningLog(<< "Unable to start an allocation relay on port " << port << ", trying another. Sender=" << request.mRemoteTuple);
632 }
633
634 // Add the new allocation to be managed
635 turnAllocationManager.addTurnAllocation(allocation);
636
637 // form the outgoing success response
639
640 response.mHasTurnLifetime = true;
641 response.mTurnLifetime = lifetime;
642
643 response.mHasTurnXorRelayedAddress = true;
644 StunMessage::setStunAtrAddressFromTuple(response.mTurnXorRelayedAddress, allocation->getRequestedTuple());
645
646 // Note: XorMappedAddress is added to all TurnAllocate responses in processStunMessage
647
648 //Reserved for future draft
649 //response.mHasTurnBandwidth = true;
650 //response.mTurnBandwidth = request.mHasTurnBandwidth ? request.mTurnBandwidth : DEFAULT_BANDWIDTH;
651
652 // Note: Message Integrity added by handleAuthentication
653
655}
656
658RequestHandler::processTurnRefreshRequest(TurnAllocationManager& turnAllocationManager, StunMessage& request, StunMessage& response)
659{
660 // Turn Allocate requests must be authenticated (note: if this attribute is present
661 // then handleAuthentication would have validation authentication info)
662 if(!request.mHasMessageIntegrity)
663 {
664 WarningLog(<< "Turn refresh request without authentication. Sending 401. Sender=" << request.mRemoteTuple);
665 buildErrorResponseWithRealmAndNonce(response, 401, "Missing Message Integrity", getConfig().mAuthenticationRealm, request);
667 }
668
669 TurnAllocation* allocation = turnAllocationManager.findTurnAllocation(TurnAllocationKey(request.mLocalTuple, request.mRemoteTuple));
670
671 if(!allocation)
672 {
673 WarningLog(<< "Refresh requested with non-matching allocation. Sending 437. Sender=" << request.mRemoteTuple);
674 buildErrorResponse(response, 437, "Allocation Mismatch");
676 }
677
678 // If allocation was found, then ensure that the same username and shared secret was used
679 if(allocation->getClientAuth().getClientUsername() != *request.mUsername) // Note: Its OK to assume that mUsername is set here, since handleAuthentication validates it
680 {
681 WarningLog(<< "Refresh requested with username not matching allocation. Sending 441. Sender=" << request.mRemoteTuple);
682 buildErrorResponse(response, 441, "Wrong Credentials");
684 }
685 if(allocation->getClientAuth().getClientSharedSecret() != response.mHmacKey) // The HMAC is already calculated and added to the response in handleAuthentication
686 {
687 WarningLog(<< "Refresh requested with shared secret not matching allocation. Sending 441. Sender=" << request.mRemoteTuple);
688 buildErrorResponse(response, 441, "Wrong Credentials");
690 }
691
692 // check if Lifetime is 0, if so then just send success response
693 if(request.mHasTurnLifetime && request.mTurnLifetime == 0)
694 {
695 // form the outgoing success response
697
698 response.mHasTurnLifetime = true;
699 response.mTurnLifetime = 0;
700
701 // If allocation exists then delete it
702 if(allocation)
703 {
704 turnAllocationManager.removeTurnAllocation(allocation->getKey()); // will delete allocation
705 }
706
708 }
709
711 if(request.mHasTurnLifetime)
712 {
713 // Check if the requested value is greater than the server max
714 if(request.mTurnLifetime > getConfig().mMaxAllocationLifetime)
715 {
717 }
718 // The server should ignore requests for a lifetime less than it's default
719 else if(request.mTurnLifetime > getConfig().mDefaultAllocationLifetime)
720 {
721 lifetime = request.mTurnLifetime;
722 }
723 }
724
725 // Check if this is a subsequent allocate request
726 allocation->refresh(lifetime);
727
728 // form the outgoing success response
730
731 response.mHasTurnLifetime = true;
732 response.mTurnLifetime = lifetime;
733
734 //Reserved for future draft
735 //response.mHasTurnBandwidth = true;
736 //response.mTurnBandwidth = request.mHasTurnBandwidth ? request.mTurnBandwidth : DEFAULT_BANDWIDTH;
737
738 // Note: Message Integrity added by handleAuthentication
739
741}
742
744RequestHandler::processTurnCreatePermissionRequest(TurnAllocationManager& turnAllocationManager, StunMessage& request, StunMessage& response)
745{
746 // TurnCreatePermission requests must be authenticated
747 if(!request.mHasMessageIntegrity)
748 {
749 WarningLog(<< "Turn create permission request without authentication. Send 401. Sender=" << request.mRemoteTuple);
750 buildErrorResponseWithRealmAndNonce(response, 401, "Missing Message Integrity", getConfig().mAuthenticationRealm, request);
752 }
753
754 TurnAllocation* allocation = turnAllocationManager.findTurnAllocation(TurnAllocationKey(request.mLocalTuple, request.mRemoteTuple));
755
756 if(!allocation)
757 {
758 WarningLog(<< "Turn create permission request for non-existing allocation. Send 437. Sender=" << request.mRemoteTuple);
759 buildErrorResponse(response, 437, "No Allocation");
761 }
762
763 // If allocation was found, then ensure that the same username and shared secret was used
764 if(allocation->getClientAuth().getClientUsername() != *request.mUsername) // Note: Its OK to assume that mUsername is set here, since handleAuthentication validates it
765 {
766 WarningLog(<< "Create permission requested with username not matching allocation. Sending 441. Sender=" << request.mRemoteTuple);
767 buildErrorResponse(response, 441, "Wrong Credentials");
769 }
770 if(allocation->getClientAuth().getClientSharedSecret() != response.mHmacKey) // The HMAC is already calculated and added to the response in handleAuthentication
771 {
772 WarningLog(<< "Create permission requested with shared secret not matching allocation. Sending 441. Sender=" << request.mRemoteTuple);
773 buildErrorResponse(response, 441, "Wrong Credentials");
775 }
776
777 if(request.mCntTurnXorPeerAddress > 0)
778 {
779 StunTuple remoteAddress;
780 remoteAddress.setTransportType(allocation->getRequestedTuple().getTransportType());
781 for (int i = 0; i < request.mCntTurnXorPeerAddress; i++)
782 {
783 StunMessage::setTupleFromStunAtrAddress(remoteAddress, request.mTurnXorPeerAddress[i]);
784 allocation->refreshPermission(remoteAddress.getAddress());
785 }
786 }
787 else
788 {
789 WarningLog(<< "Create permission request missing peer address. Sending 400. Sender=" << request.mRemoteTuple);
790 buildErrorResponse(response, 400, "Bad Request - missing attribute");
792 }
793
794 // form the outgoing success response
796
797 // Note: Message Integrity added by handleAuthentication
798
800}
801
803RequestHandler::processTurnChannelBindRequest(TurnAllocationManager& turnAllocationManager, StunMessage& request, StunMessage& response)
804{
805 // TurnChannelBind requests must be authenticated
806 if(!request.mHasMessageIntegrity)
807 {
808 WarningLog(<< "Turn channel bind request without authentication. Send 401. Sender=" << request.mRemoteTuple);
809 buildErrorResponseWithRealmAndNonce(response, 401, "Missing Message Integrity", getConfig().mAuthenticationRealm, request);
811 }
812
813 TurnAllocation* allocation = turnAllocationManager.findTurnAllocation(TurnAllocationKey(request.mLocalTuple, request.mRemoteTuple));
814
815 if(!allocation)
816 {
817 WarningLog(<< "Turn channel bind request for non-existing allocation. Send 437. Sender=" << request.mRemoteTuple);
818 buildErrorResponse(response, 437, "No Allocation");
820 }
821
822 // If allocation was found, then ensure that the same username and shared secret was used
823 if(allocation->getClientAuth().getClientUsername() != *request.mUsername) // Note: Its OK to assume that mUsername is set here, since handleAuthentication validates it
824 {
825 WarningLog(<< "Channel bind requested with username not matching allocation. Sending 441. Sender=" << request.mRemoteTuple);
826 buildErrorResponse(response, 441, "Wrong Credentials");
828 }
829 if(allocation->getClientAuth().getClientSharedSecret() != response.mHmacKey) // The HMAC is already calculated and added to the response in handleAuthentication
830 {
831 WarningLog(<< "Channel bind requested with shared secret not matching allocation. Sending 441. Sender=" << request.mRemoteTuple);
832 buildErrorResponse(response, 441, "Wrong Credentials");
834 }
835
836 if(request.mCntTurnXorPeerAddress > 0 && request.mHasTurnChannelNumber)
837 {
838 // Ensure channel number is in valid range
839 if(request.mTurnChannelNumber < MIN_CHANNEL_NUM || request.mTurnChannelNumber > MAX_CHANNEL_NUM)
840 {
841 WarningLog(<< "Channel bind requested with an out of range channel number=" << request.mTurnChannelNumber << ". Sending 400. Sender=" << request.mRemoteTuple);
842 buildErrorResponse(response, 400, "Bad Request - channel number out of range");
844 }
845
846 StunTuple remoteAddress;
847 remoteAddress.setTransportType(allocation->getRequestedTuple().getTransportType());
848 // Shouldn't have more than one xor-peer-address attribute in this request
849 StunMessage::setTupleFromStunAtrAddress(remoteAddress, request.mTurnXorPeerAddress[0]);
850
851 if(!allocation->addChannelBinding(remoteAddress, request.mTurnChannelNumber))
852 {
853 WarningLog(<< "Channel bind request invalid. Sending 400. Sender=" << request.mRemoteTuple);
854 buildErrorResponse(response, 400, "Bad Request");
856 }
857 }
858 else
859 {
860 WarningLog(<< "Channel bind request missing peer address and/or channel number. Sending 400. Sender=" << request.mRemoteTuple);
861 buildErrorResponse(response, 400, "Bad Request - missing attribute");
863 }
864
865 // form the outgoing success response
867
868 // Add the channel number to make the clients job easier
869 //response.mHasTurnChannelNumber = true;
870 //response.mTurnChannelNumber = request.mTurnChannelNumber;
871
872 // Note: Message Integrity added by handleAuthentication
873
875}
876
877void
878RequestHandler::processTurnSendIndication(TurnAllocationManager& turnAllocationManager, StunMessage& request)
879{
880 TurnAllocation* allocation = turnAllocationManager.findTurnAllocation(TurnAllocationKey(request.mLocalTuple, request.mRemoteTuple));
881
882 if(!allocation)
883 {
884 DebugLog(<< "Turn SendIndication for non existing allocation (Local=" << request.mLocalTuple << ", remote=" << request.mRemoteTuple << "). Dropping.");
885 return;
886 }
887
888 if(request.mCntTurnXorPeerAddress == 0 || !request.mHasTurnData)
889 {
890 DebugLog(<< "Turn send indication with no peer address or data (Local=" << request.mLocalTuple << ", remote=" << request.mRemoteTuple << "). Dropping.");
891 return;
892 }
893
894 // Check if Don't Fragment attribute is present - if so drop - TODO implement DF bit, then remove this check
895 if(request.mHasTurnDontFragment)
896 {
897 DebugLog(<< "Turn send indication with Don't Fragment requested, not yet implemented. Dropping. Sender=" << request.mRemoteTuple);
898 return;
899 }
900
901 StunTuple remoteAddress;
902 remoteAddress.setTransportType(allocation->getRequestedTuple().getTransportType());
903 // Shouldn't have more than one xor-peer-address attribute in this request
904 StunMessage::setTupleFromStunAtrAddress(remoteAddress, request.mTurnXorPeerAddress[0]);
905
906 const auto data = std::make_shared<DataBuffer>(request.mTurnData->data(), request.mTurnData->size());
907 allocation->sendDataToPeer(remoteAddress, data, false /* isFramed? */);
908}
909
910void
911RequestHandler::processTurnData(TurnAllocationManager& turnAllocationManager, unsigned short channelNumber, const StunTuple& localTuple, const StunTuple& remoteTuple, const std::shared_ptr<DataBuffer>& data)
912{
913 TurnAllocation* allocation = turnAllocationManager.findTurnAllocation(TurnAllocationKey(localTuple, remoteTuple));
914
915 if(!allocation)
916 {
917 DebugLog(<< "Turn channel data for non existing allocation. Dropping. Sender=" << remoteTuple);
918 return;
919 }
920
921 allocation->sendDataToPeer(channelNumber, data, true /* isFramed? */);
922}
923
924} // namespace
925
926
927/* ====================================================================
928
929 Copyright (c) 2026 SIP Spectrum, Inc. https://www.sipspectrum.com
930 Copyright (c) 2007-2008, Plantronics, Inc.
931 All rights reserved.
932
933 Redistribution and use in source and binary forms, with or without
934 modification, are permitted provided that the following conditions are
935 met:
936
937 1. Redistributions of source code must retain the above copyright
938 notice, this list of conditions and the following disclaimer.
939
940 2. Redistributions in binary form must reproduce the above copyright
941 notice, this list of conditions and the following disclaimer in the
942 documentation and/or other materials provided with the distribution.
943
944 3. Neither the name of Plantronics nor the names of its contributors
945 may be used to endorse or promote products derived from this
946 software without specific prior written permission.
947
948 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
949 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
950 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
951 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
952 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
953 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
954 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
955 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
956 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
957 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
958 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
959
960 ==================================================================== */
#define MAX_CHANNEL_NUM
#define MIN_CHANNEL_NUM
#define DebugLog(args_)
Definition Logger.hxx:128
#define StackLog(args_)
Definition Logger.hxx:125
#define InfoLog(args_)
Definition Logger.hxx:131
#define WarningLog(args_)
Definition Logger.hxx:134
#define ErrLog(args_)
Definition Logger.hxx:137
#define resip_assert(x)
This file needs to work for both C and C++ code.
unsigned short mAllocationPortRangeMax
unsigned short mAllocationPortRangeMin
unsigned long mDefaultAllocationLifetime
unsigned long mMaxAllocationLifetime
void processTurnData(TurnAllocationManager &turnAllocationManager, unsigned short channelNumber, const StunTuple &localTuple, const StunTuple &remoteTuple, const std::shared_ptr< DataBuffer > &data)
const ReTurnConfig & getConfig()
RequestHandler(TurnManager &turnManager, const asio::ip::address *prim3489Address=0, unsigned short *prim3489Port=0, const asio::ip::address *alt3489Address=0, unsigned short *alt3489Port=0)
ProcessResult processStunMessage(AsyncSocketBase *turnSocket, TurnAllocationManager &turnAllocationManager, StunMessage &request, StunMessage &response, bool isRFC3489BackwardsCompatServer=false)
Process a received StunMessage, and produce a reply Returns true if the response message is to be sen...
static const uint16_t StunClassSuccessResponse
static const uint16_t SharedSecretMethod
static const uint16_t BindMethod
static const uint16_t TurnSendMethod
StunAtrUnknown mUnknownAttributes
static const uint32_t ChangeIpFlag
StunAtrUnknown mUnknownRequiredAttributes
static const uint16_t StunClassErrorResponse
static const uint16_t TurnCreatePermissionMethod
static const uint16_t StunClassRequest
static const uint16_t TurnAllocateMethod
StunAtrAddress mXorMappedAddress
void setSoftware(const char *software)
static const uint32_t ChangePortFlag
void setErrorCode(unsigned short errorCode, const char *reason)
static const uint16_t TurnDataMethod
static void setStunAtrAddressFromTuple(StunAtrAddress &address, const StunTuple &tuple)
static const uint8_t PropsPortEven
static const uint16_t TurnRefreshMethod
static const uint8_t PropsPortPair
static const uint32_t RequestedTransportUdp
static const uint16_t TurnChannelBindMethod
static const uint16_t StunClassIndication
static void setTupleFromStunAtrAddress(StunTuple &tuple, const StunAtrAddress &address)
TurnAllocation * findTurnAllocation(const TurnAllocationKey &turnAllocationKey)
void sendDataToPeer(unsigned short channelNumber, const std::shared_ptr< DataBuffer > &data, bool isFramed)
unsigned short allocateEvenPort(StunTuple::TransportType transport)
bool allocatePort(StunTuple::TransportType transport, unsigned short port, bool reserved=false)
unsigned short allocateEvenPortPair(StunTuple::TransportType transport)
unsigned short allocateAnyPort(StunTuple::TransportType transport)
void deallocatePort(StunTuple::TransportType transport, unsigned short port)
bool allocationQuotaReached(const resip::Data &username) const
Allocations per user, over all transports: TurnAllocation adds and removes its own (MaxAllocationsPer...
An iostream that operates on an existing Data.
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
uint64_t convertUInt64() const
Takes the contents of the Data and converts them to an unsigned 64-bit integer.
Definition Data.cxx:1758
static const PreallocateType Preallocate
used only to disambiguate constructors
Definition Data.hxx:116
size_type size() const noexcept
Returns the number of bytes in this Data.
Definition Data.hxx:599
Data md5(EncodingType type=HEX) const
Computes the MD5 hash of the current data.
Definition Data.cxx:1174
const char * data() const noexcept
Returns a pointer to the contents of this Data.
Definition Data.hxx:608
Provides string-parsing functionality with protection from buffer overflow.
static Data getCryptoRandomHex(unsigned int numBytes)
Definition Random.cxx:394
static uint64_t getTimeMs()
Returns the current clock time in milliseconds.
Definition Timer.hxx:72
dcm! – add UnusedChecking(_enum) below;
unsigned int uint32_t
Definition stdint.h:85
unsigned __int64 uint64_t
Definition stdint.h:95