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