/[resiprocate]/main/resip/dum/ClientRegistration.cxx
ViewVC logotype

Contents of /main/resip/dum/ClientRegistration.cxx

Parent Directory Parent Directory | Revision Log Revision Log


Revision 10975 - (show annotations) (download)
Sun Feb 16 20:04:10 2014 UTC (5 years, 8 months ago) by sgodin
File MIME type: text/plain
File size: 34703 byte(s)
-removed use of raw transport pointers in Tuple and SipMessage (finally!!)
 -required repro record route logic to be modified - no longer store transport specific
  record routes in the Transport class - the Proxy class now tracks these
 -removed SipMessage::getReceivedTransport and added isFromWire method that can be used in
  it's place in various locations

1 #include <algorithm>
2 #include <iterator>
3
4 #include "resip/stack/Helper.hxx"
5 #include "resip/dum/BaseCreator.hxx"
6 #include "resip/dum/ClientAuthManager.hxx"
7 #include "resip/dum/ClientRegistration.hxx"
8 #include "resip/dum/RegistrationHandler.hxx"
9 #include "resip/dum/DialogUsageManager.hxx"
10 #include "resip/dum/Dialog.hxx"
11 #include "resip/dum/MasterProfile.hxx"
12 #include "resip/dum/UsageUseException.hxx"
13 #include "rutil/Logger.hxx"
14 #include "rutil/Inserter.hxx"
15 #include "rutil/Random.hxx"
16 #include "rutil/ParseBuffer.hxx"
17 #include "rutil/TransportType.hxx"
18 #include "rutil/WinLeakCheck.hxx"
19
20 #define RESIPROCATE_SUBSYSTEM Subsystem::DUM
21
22 using namespace resip;
23
24 static const int UnreasonablyLowExpirationThreshold = 7; // The threshold before which we consider a contacts expiry to be unreasonably low
25
26 ClientRegistrationHandle
27 ClientRegistration::getHandle()
28 {
29 return ClientRegistrationHandle(mDum, getBaseHandle().getId());
30 }
31
32 ClientRegistration::ClientRegistration(DialogUsageManager& dum,
33 DialogSet& dialogSet,
34 SharedPtr<SipMessage> request)
35 : NonDialogUsage(dum, dialogSet),
36 mLastRequest(request),
37 mTimerSeq(0),
38 mState(mLastRequest->exists(h_Contacts) ? Adding : Querying),
39 mEnding(false),
40 mEndWhenDone(false),
41 mUserRefresh(false),
42 mRegistrationTime(mDialogSet.mUserProfile->getDefaultRegistrationTime()),
43 mExpires(0),
44 mRefreshTime(0),
45 mQueuedState(None),
46 mQueuedRequest(new SipMessage)
47 {
48 // If no Contacts header, this is a query
49 if (mLastRequest->exists(h_Contacts))
50 {
51 NameAddr all;
52 all.setAllContacts();
53 if(!(mLastRequest->header(h_Contacts).front() == all))
54 {
55 // store if not special all contacts header
56 mMyContacts = mLastRequest->header(h_Contacts);
57 }
58 }
59
60 if(mLastRequest->exists(h_Expires) &&
61 mLastRequest->header(h_Expires).isWellFormed())
62 {
63 mRegistrationTime = mLastRequest->header(h_Expires).value();
64 }
65
66 mNetworkAssociation.setDum(&dum);
67 }
68
69 ClientRegistration::~ClientRegistration()
70 {
71 DebugLog ( << "ClientRegistration::~ClientRegistration" );
72 mDialogSet.mClientRegistration = 0;
73
74 // !dcm! Will not interact well with multiple registrations from the same AOR
75 mDialogSet.mUserProfile->setServiceRoute(NameAddrs());
76 }
77
78 void
79 ClientRegistration::addBinding(const NameAddr& contact)
80 {
81 addBinding(contact, mDialogSet.mUserProfile->getDefaultRegistrationTime());
82 }
83
84 SharedPtr<SipMessage>
85 ClientRegistration::tryModification(ClientRegistration::State state)
86 {
87 if (mState != Registered)
88 {
89 if(mState != RetryAdding && mState != RetryRefreshing)
90 {
91 if (mQueuedState != None)
92 {
93 WarningLog (<< "Trying to modify bindings when another request is already queued");
94 throw UsageUseException("Queuing multiple requests for Registration Bindings", __FILE__,__LINE__);
95 }
96
97 *mQueuedRequest = *mLastRequest;
98 mQueuedState = state;
99
100 return mQueuedRequest;
101 }
102 else
103 {
104 ++mTimerSeq; // disable retry timer
105 }
106 }
107
108 assert(mQueuedState == None);
109 mState = state;
110
111 return mLastRequest;
112 }
113
114 void
115 ClientRegistration::addBinding(const NameAddr& contact, UInt32 registrationTime)
116 {
117 SharedPtr<SipMessage> next = tryModification(Adding);
118 mMyContacts.push_back(contact);
119 tagContact(mMyContacts.back());
120
121 next->header(h_Contacts) = mMyContacts;
122 mRegistrationTime = registrationTime;
123 next->header(h_Expires).value() = mRegistrationTime;
124 next->header(h_CSeq).sequence()++;
125 // caller prefs
126
127 if (mQueuedState == None)
128 {
129 send(next);
130 }
131 }
132
133 void
134 ClientRegistration::removeBinding(const NameAddr& contact)
135 {
136 if (mState == Removing)
137 {
138 WarningLog (<< "Already removing a binding");
139 throw UsageUseException("Can't remove binding when already removing registration bindings", __FILE__,__LINE__);
140 }
141
142 SharedPtr<SipMessage> next = tryModification(Removing);
143 for (NameAddrs::iterator i=mMyContacts.begin(); i != mMyContacts.end(); i++)
144 {
145 if (i->uri() == contact.uri())
146 {
147 next->header(h_Contacts).clear();
148 next->header(h_Contacts).push_back(*i);
149 next->header(h_Expires).value() = 0;
150 next->header(h_CSeq).sequence()++;
151
152 if (mQueuedState == None)
153 {
154 send(next);
155 }
156
157 mMyContacts.erase(i);
158 return;
159 }
160 }
161
162 // !jf! What state are we left in now?
163 throw Exception("No such binding", __FILE__, __LINE__);
164 }
165
166 void
167 ClientRegistration::removeAll(bool stopRegisteringWhenDone)
168 {
169 if (mState == Removing)
170 {
171 WarningLog (<< "Already removing a binding");
172 throw UsageUseException("Can't remove binding when already removing registration bindings", __FILE__,__LINE__);
173 }
174
175 SharedPtr<SipMessage> next = tryModification(Removing);
176
177 mAllContacts.clear();
178 mMyContacts.clear();
179
180 NameAddr all;
181 all.setAllContacts();
182 next->header(h_Contacts).clear();
183 next->header(h_Contacts).push_back(all);
184 next->header(h_Expires).value() = 0;
185 next->header(h_CSeq).sequence()++;
186 mEndWhenDone = stopRegisteringWhenDone;
187
188 if (mQueuedState == None)
189 {
190 send(next);
191 }
192 }
193
194 void
195 ClientRegistration::removeMyBindings(bool stopRegisteringWhenDone)
196 {
197 InfoLog (<< "Removing binding");
198
199 if (mState == Removing)
200 {
201 WarningLog (<< "Already removing a binding");
202 throw UsageUseException("Can't remove binding when already removing registration bindings", __FILE__,__LINE__);
203 }
204
205 if (mMyContacts.empty())
206 {
207 WarningLog (<< "No bindings to remove");
208 throw UsageUseException("No bindings to remove", __FILE__,__LINE__);
209 }
210
211 SharedPtr<SipMessage> next = tryModification(Removing);
212
213 next->header(h_Contacts) = mMyContacts;
214 mMyContacts.clear();
215
216 NameAddrs& myContacts = next->header(h_Contacts);
217
218 for (NameAddrs::iterator i=myContacts.begin(); i != myContacts.end(); i++)
219 {
220 i->param(p_expires) = 0;
221 }
222
223 next->remove(h_Expires);
224 next->header(h_CSeq).sequence()++;
225
226 // !jf! is this ok if queued
227 mEndWhenDone = stopRegisteringWhenDone;
228
229 if (mQueuedState == None)
230 {
231 if(mEnding && whenExpires() == 0)
232 {
233 assert(mEndWhenDone); // will always be true when mEnding is true
234 // We are not actually registered, and we are ending - no need to send un-register - just terminate now
235 stopRegistering();
236 return;
237 }
238 send(next);
239 }
240 }
241
242 class ClientRegistrationRemoveMyBindings : public DumCommandAdapter
243 {
244 public:
245 ClientRegistrationRemoveMyBindings(const ClientRegistrationHandle& clientRegistrationHandle, bool stopRegisteringWhenDone)
246 : mClientRegistrationHandle(clientRegistrationHandle),
247 mStopRegisteringWhenDone(stopRegisteringWhenDone)
248 {
249 }
250
251 virtual void executeCommand()
252 {
253 if(mClientRegistrationHandle.isValid())
254 {
255 mClientRegistrationHandle->removeMyBindings(mStopRegisteringWhenDone);
256 }
257 }
258
259 virtual EncodeStream& encodeBrief(EncodeStream& strm) const
260 {
261 return strm << "ClientRegistrationRemoveMyBindings";
262 }
263 private:
264 ClientRegistrationHandle mClientRegistrationHandle;
265 bool mStopRegisteringWhenDone;
266 };
267
268 void
269 ClientRegistration::removeMyBindingsCommand(bool stopRegisteringWhenDone)
270 {
271 mDum.post(new ClientRegistrationRemoveMyBindings(getHandle(), stopRegisteringWhenDone));
272 }
273
274 void
275 ClientRegistration::stopRegistering()
276 {
277 //timers aren't a concern, as DUM checks for Handle validity before firing.
278 delete this;
279 }
280
281 void
282 ClientRegistration::requestRefresh(UInt32 expires)
283 {
284 // Set flag so that handlers get called for success/failure
285 mUserRefresh = true;
286 internalRequestRefresh(expires);
287 }
288
289 void
290 ClientRegistration::internalRequestRefresh(UInt32 expires)
291 {
292 if(mState == RetryAdding && mState == RetryRefreshing)
293 {
294 // disable retry time and try refresh immediately
295 ++mTimerSeq;
296 }
297 else if (mState != Registered)
298 {
299 InfoLog (<< "a request is already in progress, no need to refresh " << *this);
300 return;
301 }
302
303 InfoLog (<< "requesting refresh of " << *this);
304
305 mState = Refreshing;
306 mLastRequest->header(h_CSeq).sequence()++;
307 mLastRequest->header(h_Contacts)=mMyContacts;
308 if(expires > 0)
309 {
310 mRegistrationTime = expires;
311 }
312 mLastRequest->header(h_Expires).value()=mRegistrationTime;
313
314 send(mLastRequest);
315 }
316
317 const NameAddrs&
318 ClientRegistration::myContacts()
319 {
320 return mMyContacts;
321 }
322
323 const NameAddrs&
324 ClientRegistration::allContacts()
325 {
326 return mAllContacts;
327 }
328
329 UInt32
330 ClientRegistration::whenExpires() const
331 {
332 UInt64 now = Timer::getTimeSecs();
333 if(mExpires > now)
334 {
335 return (UInt32)(mExpires - now);
336 }
337 else
338 {
339 return 0;
340 }
341 }
342
343 void
344 ClientRegistration::end()
345 {
346 if(!mEnding)
347 {
348 mEnding = true;
349 removeMyBindings(true);
350 }
351 }
352
353 class ClientRegistrationEndCommand : public DumCommandAdapter
354 {
355 public:
356 ClientRegistrationEndCommand(const ClientRegistrationHandle& clientRegistrationHandle)
357 : mClientRegistrationHandle(clientRegistrationHandle)
358 {
359 }
360
361 virtual void executeCommand()
362 {
363 if(mClientRegistrationHandle.isValid())
364 {
365 mClientRegistrationHandle->end();
366 }
367 }
368
369 virtual EncodeStream& encodeBrief(EncodeStream& strm) const
370 {
371 return strm << "ClientRegistrationEndCommand";
372 }
373 private:
374 ClientRegistrationHandle mClientRegistrationHandle;
375 };
376
377 void
378 ClientRegistration::endCommand()
379 {
380 mDum.post(new ClientRegistrationEndCommand(getHandle()));
381 }
382
383 EncodeStream&
384 ClientRegistration::dump(EncodeStream& strm) const
385 {
386 strm << "ClientRegistration " << mLastRequest->header(h_From).uri();
387 return strm;
388 }
389
390 void
391 ClientRegistration::dispatch(const SipMessage& msg)
392 {
393 try
394 {
395 // !jf! there may be repairable errors that we can handle here
396 assert(msg.isResponse());
397 const int& code = msg.header(h_StatusLine).statusCode();
398 bool nextHopSupportsOutbound = false;
399 int keepAliveTime = 0;
400
401 // If registration was successful - look for next hop indicating support for outbound
402 if(mDialogSet.mUserProfile->clientOutboundEnabled() && msg.isExternal() && code >= 200 && code < 300)
403 {
404 // If ClientOutbound support is enabled and the registration response contains
405 // Requires: outbound or a Path with outbound then outbound is supported by the
406 // server, so store the flow key in the UserProfile
407 // and use it for sending all messages (DialogUsageManager::sendUsingOutboundIfAppropriate)
408 try
409 {
410 if((!msg.empty(h_Paths) && msg.header(h_Paths).back().uri().exists(p_ob)) ||
411 (!msg.empty(h_Requires) && msg.header(h_Requires).find(Token(Symbols::Outbound))))
412 {
413 mDialogSet.mUserProfile->mClientOutboundFlowTuple = msg.getSource();
414 mDialogSet.mUserProfile->mClientOutboundFlowTuple.onlyUseExistingConnection = true;
415 nextHopSupportsOutbound = true;
416 if(!msg.empty(h_FlowTimer))
417 {
418 keepAliveTime = msg.header(h_FlowTimer).value();
419 }
420 }
421 }
422 catch(BaseException&e)
423 {
424 ErrLog(<<"Error parsing Path or Requires:" << e);
425 }
426 }
427
428 if(msg.isFromWire())
429 {
430 resip::TransportType receivedTransport = toTransportType(
431 msg.header(h_Vias).front().transport());
432 if(keepAliveTime == 0)
433 {
434 if(isReliable(receivedTransport))
435 {
436 keepAliveTime = mDialogSet.mUserProfile->getKeepAliveTimeForStream();
437 }
438 else
439 {
440 keepAliveTime = mDialogSet.mUserProfile->getKeepAliveTimeForDatagram();
441 }
442 }
443
444 if(keepAliveTime > 0)
445 {
446 mNetworkAssociation.update(msg, keepAliveTime, nextHopSupportsOutbound);
447 }
448 }
449
450 if (code < 200)
451 {
452 // throw it away
453 return;
454 }
455 else if (code < 300) // success
456 {
457 try
458 {
459 if (msg.exists(h_ServiceRoutes))
460 {
461 InfoLog(<< "Updating service route: " << Inserter(msg.header(h_ServiceRoutes)));
462 mDialogSet.mUserProfile->setServiceRoute(msg.header(h_ServiceRoutes));
463 }
464 else
465 {
466 DebugLog(<< "Clearing service route (" << Inserter(getUserProfile()->getServiceRoute()) << ")");
467 mDialogSet.mUserProfile->setServiceRoute(NameAddrs());
468 }
469 }
470 catch(BaseException &e)
471 {
472 ErrLog(<< "Error Parsing Service Route:" << e);
473 }
474
475 //gruu update, should be optimized
476 try
477 {
478 if(mDialogSet.mUserProfile->gruuEnabled() && msg.exists(h_Contacts))
479 {
480 for (NameAddrs::const_iterator it = msg.header(h_Contacts).begin();
481 it != msg.header(h_Contacts).end(); it++)
482 {
483 if (it->exists(p_Instance) && it->param(p_Instance) == mDialogSet.mUserProfile->getInstanceId())
484 {
485 if(it->exists(p_pubGruu))
486 {
487 mDialogSet.mUserProfile->setPublicGruu(Uri(it->param(p_pubGruu)));
488 }
489 if(it->exists(p_tempGruu))
490 {
491 mDialogSet.mUserProfile->setTempGruu(Uri(it->param(p_tempGruu)));
492 }
493 break;
494 }
495 }
496 }
497 }
498 catch(BaseException&e)
499 {
500 ErrLog(<<"Error parsing GRUU:" << e);
501 }
502
503 // !jf! consider what to do if no contacts
504 // !ah! take list of ctcs and push into mMy or mOther as required.
505
506 // make timers to re-register
507 UInt64 nowSecs = Timer::getTimeSecs();
508 UInt32 expiry = calculateExpiry(msg);
509 mExpires = nowSecs + expiry;
510 if(msg.exists(h_Contacts))
511 {
512 mAllContacts = msg.header(h_Contacts);
513 }
514 else
515 {
516 mAllContacts.clear();
517 }
518
519 if (expiry != 0 && expiry != UINT_MAX)
520 {
521 if(expiry >= UnreasonablyLowExpirationThreshold)
522 {
523 int exp = Helper::aBitSmallerThan(expiry);
524 mRefreshTime = exp + nowSecs;
525 mDum.addTimer(DumTimeout::Registration,
526 exp,
527 getBaseHandle(),
528 ++mTimerSeq);
529 }
530 else
531 {
532 WarningLog(<< "Server is using an unreasonably low expiry: "
533 << expiry
534 << " We're just going to end this registration.");
535 end();
536 return;
537 }
538 }
539
540 switch (mState)
541 {
542 case Querying:
543 case Adding:
544 if(expiry != 0)
545 {
546 mState = Registered;
547 mDum.mClientRegistrationHandler->onSuccess(getHandle(), msg);
548 }
549 else
550 {
551 mDum.mClientRegistrationHandler->onRemoved(getHandle(), msg);
552 checkProfileRetry(msg);
553 }
554 break;
555
556 case Removing:
557 //mDum.mClientRegistrationHandler->onSuccess(getHandle(), msg);
558 mDum.mClientRegistrationHandler->onRemoved(getHandle(), msg);
559 InfoLog (<< "Finished removing registration " << *this << " mEndWhenDone=" << mEndWhenDone);
560 if (mEndWhenDone)
561 {
562 // !kh!
563 // stopRegistering() deletes 'this'
564 // furthur processing makes no sense
565 //assert(mQueuedState == None);
566 stopRegistering();
567 return;
568 }
569 break;
570
571 case Registered:
572 case Refreshing:
573 mState = Registered;
574 if(expiry != 0)
575 {
576 if(mUserRefresh)
577 {
578 mUserRefresh = false;
579 mDum.mClientRegistrationHandler->onSuccess(getHandle(), msg);
580 }
581 }
582 else
583 {
584 mDum.mClientRegistrationHandler->onRemoved(getHandle(), msg);
585 checkProfileRetry(msg);
586 }
587 break;
588
589 default:
590 break;
591 }
592
593 if (mQueuedState != None)
594 {
595 if(mQueuedState == Removing && mEnding && whenExpires() == 0)
596 {
597 assert(mEndWhenDone); // will always be true when mEnding is true
598 // We are not actually registered, and we are ending - no need to send un-register - just terminate now
599 stopRegistering();
600 return;
601 }
602 InfoLog (<< "Sending queued request: " << *mQueuedRequest);
603 mState = mQueuedState;
604 mQueuedState = None;
605 *mLastRequest = *mQueuedRequest;
606 send(mLastRequest);
607 }
608 }
609 else
610 {
611 if((mState == Adding || mState == Refreshing) && !mEndWhenDone)
612 {
613 if (code == 423) // interval too short
614 {
615 UInt32 maxRegistrationTime = mDialogSet.mUserProfile->getDefaultMaxRegistrationTime();
616 if (msg.exists(h_MinExpires) &&
617 (maxRegistrationTime == 0 || msg.header(h_MinExpires).value() < maxRegistrationTime)) // If maxRegistrationTime is enabled, then check it
618 {
619 mRegistrationTime = msg.header(h_MinExpires).value();
620 mLastRequest->header(h_Expires).value() = mRegistrationTime;
621 mLastRequest->header(h_CSeq).sequence()++;
622 send(mLastRequest);
623 return;
624 }
625 }
626 else if (code == 408 || (code == 503 && !msg.isFromWire()))
627 {
628 int retry = mDum.mClientRegistrationHandler->onRequestRetry(getHandle(), 0, msg);
629
630 if (retry < 0)
631 {
632 DebugLog(<< "Application requested failure on Retry-After");
633 }
634 else if (retry == 0)
635 {
636 DebugLog(<< "Application requested immediate retry on 408 or internal 503");
637
638 mLastRequest->header(h_CSeq).sequence()++;
639 send(mLastRequest);
640 mUserRefresh = true; // Reset this flag, so that the onSuccess callback will be called if we are successful when re-trying
641 return;
642 }
643 else
644 {
645 DebugLog(<< "Application requested delayed retry on 408 or internal 503: " << retry);
646 mRefreshTime = 0;
647 switch(mState)
648 {
649 case Adding:
650 mState = RetryAdding;
651 break;
652 case Refreshing:
653 mState = RetryRefreshing;
654 break;
655 default:
656 assert(false);
657 break;
658 }
659 if(mDum.mClientAuthManager.get()) mDum.mClientAuthManager.get()->clearAuthenticationState(DialogSetId(*mLastRequest));
660 mDum.addTimer(DumTimeout::RegistrationRetry,
661 retry,
662 getBaseHandle(),
663 ++mTimerSeq);
664 mUserRefresh = true; // Reset this flag, so that the onSuccess callback will be called if we are successful when re-trying
665 return;
666 }
667 }
668 }
669
670 mDum.mClientRegistrationHandler->onFailure(getHandle(), msg);
671 mUserRefresh = true; // Reset this flag, so that the onSuccess callback will be called if we are successful when re-trying
672
673 // Retry if Profile setting is set
674 unsigned int retryInterval = checkProfileRetry(msg);
675 if(retryInterval > 0)
676 {
677 InfoLog( << "Registration error " << code << " for " << msg.header(h_To) << ", retrying in " << retryInterval << " seconds.");
678 return;
679 }
680
681 // assume that if a failure occurred, the bindings are gone
682 if (mEndWhenDone)
683 {
684 mDum.mClientRegistrationHandler->onRemoved(getHandle(), msg);
685 }
686 delete this;
687 }
688 }
689 catch(BaseException& e)
690 {
691 InfoLog( << "Exception in ClientRegistration::dispatch: " << e.getMessage());
692 mDum.mClientRegistrationHandler->onFailure(getHandle(), msg);
693 delete this;
694 }
695 }
696
697 void
698 ClientRegistration::tagContact(NameAddr& contact) const
699 {
700 tagContact(contact, mDum, mDialogSet.mUserProfile);
701 }
702
703 void
704 ClientRegistration::tagContact(NameAddr& contact, DialogUsageManager& dum, SharedPtr<UserProfile>& userProfile)
705 {
706 if(contact.uri().host().empty() ||
707 dum.getSipStack().isMyDomain(contact.uri().host(), contact.uri().port()))
708 {
709 // Contact points at us; it is appropriate to add a +sip.instance to
710 // this Contact. We don't need to have full gruu support enabled to add
711 // a +sip.instance either...
712 if(userProfile->hasInstanceId())
713 {
714 contact.param(p_Instance) = userProfile->getInstanceId();
715 if(userProfile->getRegId() != 0)
716 {
717 contact.param(p_regid) = userProfile->getRegId();
718 }
719 }
720 else if(userProfile->getRinstanceEnabled())
721 {
722 // !slg! poor mans instance id so that we can tell which contacts
723 // are ours - to be replaced by gruu someday.
724 InfoLog(<< "You really should consider setting an instance id in"
725 " the UserProfile (see UserProfile::setInstanceId())."
726 " This is really easy, and makes this class much less "
727 "likely to clash with another endpoint registering at "
728 "the same AOR.");
729 contact.uri().param(p_rinstance) = Random::getCryptoRandomHex(8);
730 }
731 else if(!contact.uri().user().empty())
732 {
733 WarningLog(<< "Ok, not only have you not specified an instance id, "
734 "you have disabled the rinstance hack (ie; resip's \"poor"
735 " man's +sip.instance\"). We will try to match Contacts"
736 " based on what you've put in the user-part of your "
737 "Contact, but this can be dicey, especially if you've put"
738 " something there that another endpoint is likely to "
739 "use.");
740 }
741 else
742 {
743 ErrLog(<< "Ok, not only have you not specified an instance id, "
744 "you have disabled the rinstance hack (ie; resip's \"poor"
745 " man's +sip.instance\"), _and_ you haven't put anything"
746 " in the user-part of your Contact. This is asking for "
747 "confusion later. We'll do our best to try to match things"
748 " up later when the response comes in...");
749 }
750 }
751 else
752 {
753 // Looks like a third-party registration. +sip.instance is out of the
754 // question, but we can still use rinstance.
755 if(userProfile->getRinstanceEnabled())
756 {
757 // !slg! poor mans instance id so that we can tell which contacts
758 // are ours - to be replaced by gruu someday.
759 contact.uri().param(p_rinstance) = Random::getCryptoRandomHex(8);
760 }
761 else if(!contact.uri().user().empty())
762 {
763 WarningLog(<< "You're trying to do a third-party registration, but "
764 "you have disabled the rinstance hack (ie; resip's \"poor"
765 " man's +sip.instance\"). We will try to match Contacts"
766 " based on what you've put in the user-part of your "
767 "Contact, but this can be dicey, especially if you've put"
768 " something there that another endpoint is likely to "
769 "use.");
770 }
771 else
772 {
773 ErrLog(<< "You're trying to do a third-party registration, and "
774 "not only have you disabled the rinstance hack (ie; "
775 "resip's \"poor man's +sip.instance\"), you haven't"
776 " put anything in the user-part of your Contact. This is "
777 "asking for confusion later. We'll do our best to try to "
778 "match things up later when the response comes in...");
779 }
780 }
781
782 if (userProfile->getMethodsParamEnabled())
783 {
784 contact.param(p_methods) = dum.getMasterProfile()->getAllowedMethodsData();
785 }
786
787 // ?bwc? Host and port override?
788 }
789
790 unsigned long
791 ClientRegistration::calculateExpiry(const SipMessage& reg200) const
792 {
793 unsigned long expiry=mRegistrationTime;
794 if(reg200.exists(h_Expires) &&
795 reg200.header(h_Expires).isWellFormed() &&
796 reg200.header(h_Expires).value() < expiry)
797 {
798 expiry=reg200.header(h_Expires).value();
799 }
800
801 if(!reg200.exists(h_Contacts))
802 {
803 return expiry;
804 }
805
806 const NameAddrs& contacts(reg200.header(h_Contacts));
807
808 // We are going to track two things here:
809 // 1. expiry - the lowest expiration value of all of our contacts
810 // 2. reasonableExpiry - the lowest expiration value of all of our contacts
811 // that is above the UnreasonablyLowExpirationThreshold (7 seconds)
812 // Before we return, if expiry is less than UnreasonablyLowExpirationThreshold
813 // but we had another contact that had a reasonable expiry value, then return
814 // that value instead. This logic covers a very interesting scenario:
815 //
816 // Consider the case where we are registered over TCP due to DNS SRV record
817 // configuration. Let's say an administrator reconfigures the DNS records to
818 // now make UDP the preferred transport. When we re-register we will now
819 // send the re-registration message over UDP. This will cause our contact
820 // address to be changed (ie: ;tranport=tcp will no longer exist). So for a
821 // short period of time the registrar will return two contacts to us, both
822 // belonging to us, one for TCP and one for UDP. The TCP one will expire in
823 // a short amount of time, and if we return this expiry to the dispatch()
824 // method then it will cause the ClientRegistration to end (see logic in
825 // dispatch() that prints out the error "Server is using an unreasonably low
826 // expiry: "...
827 unsigned long reasonableExpiry = 0xFFFFFFFF;
828
829 for(NameAddrs::const_iterator c=contacts.begin();c!=contacts.end();++c)
830 {
831 // Our expiry is never going to increase if we find one of our contacts,
832 // so if the expiry is not lower, we just ignore it. For registrars that
833 // leave our requested expiry alone, this code ends up being pretty quick,
834 // especially if there aren't contacts from other endpoints laying around.
835 if(c->isWellFormed() && c->exists(p_expires))
836 {
837 unsigned long contactExpires = c->param(p_expires);
838 if((contactExpires < expiry ||
839 contactExpires < reasonableExpiry) &&
840 contactIsMine(*c))
841 {
842 expiry = contactExpires;
843 if(contactExpires >= UnreasonablyLowExpirationThreshold)
844 {
845 reasonableExpiry = contactExpires;
846 }
847 }
848 }
849 }
850 // If expiry is less than UnreasonablyLowExpirationThreshold and we have another
851 // contact that has a reasonable expiry value, then return that value instead.
852 // See large comment above for more details.
853 if(expiry < UnreasonablyLowExpirationThreshold && reasonableExpiry != 0xFFFFFFFF)
854 {
855 expiry = reasonableExpiry;
856 }
857 return expiry;
858 }
859
860 bool
861 ClientRegistration::contactIsMine(const NameAddr& contact) const
862 {
863 // Try to find this contact in mMyContacts
864 if(mDialogSet.mUserProfile->hasInstanceId() &&
865 contact.exists(p_Instance))
866 {
867 return contact.param(p_Instance)==mDialogSet.mUserProfile->getInstanceId();
868 }
869 else if(mDialogSet.mUserProfile->getRinstanceEnabled() &&
870 contact.uri().exists(p_rinstance))
871 {
872 return rinstanceIsMine(contact.uri().param(p_rinstance));
873 }
874 else
875 {
876 return searchByUri(contact.uri());
877 }
878 }
879
880 bool
881 ClientRegistration::rinstanceIsMine(const Data& rinstance) const
882 {
883 // !bwc! This could be made faster if we used a single rinstance...
884 for(NameAddrs::const_iterator m=mMyContacts.begin(); m!=mMyContacts.end(); ++m)
885 {
886 if(m->uri().exists(p_rinstance) && m->uri().param(p_rinstance)==rinstance)
887 {
888 return true;
889 }
890 }
891 return false;
892 }
893
894 bool
895 ClientRegistration::searchByUri(const Uri& cUri) const
896 {
897 for(NameAddrs::const_iterator m=mMyContacts.begin(); m!=mMyContacts.end(); ++m)
898 {
899 if(m->uri()==cUri)
900 {
901 return true;
902 }
903 else if(m->uri().host().empty() &&
904 m->uri().user()==cUri.user() &&
905 m->uri().scheme()==cUri.scheme() &&
906 mDum.getSipStack().isMyDomain(cUri.host(), cUri.port()))
907 {
908 // Empty host-part in our contact; this means we're relying on the
909 // stack to fill out this Contact header. Also, the user-part matches.
910 return true;
911 }
912 }
913 return false;
914 }
915
916 unsigned int
917 ClientRegistration::checkProfileRetry(const SipMessage& msg)
918 {
919 unsigned int retryInterval = mDialogSet.mUserProfile->getDefaultRegistrationRetryTime();
920 if (retryInterval > 0 &&
921 (mState == Adding || mState == Refreshing) &&
922 !mEndWhenDone)
923 {
924 if (msg.exists(h_RetryAfter) && msg.header(h_RetryAfter).value() > 0)
925 {
926 // Use retry interval from error response
927 retryInterval = msg.header(h_RetryAfter).value();
928 }
929 mRefreshTime = 0;
930 switch(mState)
931 {
932 case Adding:
933 mState = RetryAdding;
934 break;
935 case Refreshing:
936 mState = RetryRefreshing;
937 break;
938 default:
939 assert(false);
940 break;
941 }
942
943 if(mDum.mClientAuthManager.get()) mDum.mClientAuthManager.get()->clearAuthenticationState(DialogSetId(*mLastRequest));
944 mDum.addTimer(DumTimeout::RegistrationRetry,
945 retryInterval,
946 getBaseHandle(),
947 ++mTimerSeq);
948 return retryInterval;
949 }
950 return 0;
951 }
952
953 void
954 ClientRegistration::dispatch(const DumTimeout& timer)
955 {
956 switch(timer.type())
957 {
958 case DumTimeout::Registration:
959 // If you happen to be Adding/Updating when the timer goes off, you should just ignore
960 // it since a new timer will get added when the 2xx is received.
961 if (timer.seq() == mTimerSeq && mState == Registered)
962 {
963 if (!mMyContacts.empty())
964 {
965 internalRequestRefresh();
966 }
967 }
968 break;
969
970 case DumTimeout::RegistrationRetry:
971 if (timer.seq() == mTimerSeq)
972 {
973 switch(mState)
974 {
975 case RetryAdding:
976 mState = Adding;
977 break;
978 case RetryRefreshing:
979 mState = Refreshing;
980 break;
981 default:
982 assert(false);
983 break;
984 }
985
986 // Resend last request
987 mLastRequest->header(h_CSeq).sequence()++;
988 mLastRequest->remove(h_ProxyAuthorizations);
989 mLastRequest->remove(h_Authorizations);
990 send(mLastRequest);
991 }
992 break;
993 default:
994 break;
995 }
996 }
997
998 void
999 ClientRegistration::flowTerminated()
1000 {
1001 // Clear the network association
1002 mNetworkAssociation.clear();
1003
1004 // Notify application - not default handler implementation is to immediately attempt
1005 // a re-registration in order to form a new flow
1006 mDum.mClientRegistrationHandler->onFlowTerminated(getHandle());
1007 }
1008
1009
1010 /* ====================================================================
1011 * The Vovida Software License, Version 1.0
1012 *
1013 * Copyright (c) 2000 Vovida Networks, Inc. All rights reserved.
1014 *
1015 * Redistribution and use in source and binary forms, with or without
1016 * modification, are permitted provided that the following conditions
1017 * are met:
1018 *
1019 * 1. Redistributions of source code must retain the above copyright
1020 * notice, this list of conditions and the following disclaimer.
1021 *
1022 * 2. Redistributions in binary form must reproduce the above copyright
1023 * notice, this list of conditions and the following disclaimer in
1024 * the documentation and/or other materials provided with the
1025
1026 * distribution.
1027 *
1028 * 3. The names "VOCAL", "Vovida Open Communication Application Library",
1029 * and "Vovida Open Communication Application Library (VOCAL)" must
1030 * not be used to endorse or promote products derived from this
1031 * software without prior written permission. For written
1032 * permission, please contact vocal@vovida.org.
1033 *
1034 * 4. Products derived from this software may not be called "VOCAL", nor
1035 * may "VOCAL" appear in their name, without prior written
1036 * permission of Vovida Networks, Inc.
1037 *
1038 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
1039 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1040 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
1041 * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL VOVIDA
1042 * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
1043 * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
1044 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
1045 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
1046 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
1047 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1048 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
1049 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
1050 * DAMAGE.
1051 *
1052 * ====================================================================
1053 *
1054 * This software consists of voluntary contributions made by Vovida
1055 * Networks, Inc. and many individuals on behalf of Vovida Networks,
1056 * Inc. For more information on Vovida Networks, Inc., please see
1057 * <http://www.vovida.org/>.
1058 *
1059 */

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