/[resiprocate]/main/reTurn/RequestHandler.cxx
ViewVC logotype

Contents of /main/reTurn/RequestHandler.cxx

Parent Directory Parent Directory | Revision Log Revision Log


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

Properties

Name Value
svn:eol-style native
svn:mime-type text/plain

webmaster AT resiprocate DOT org
ViewVC Help
Powered by ViewVC 1.1.27