reSIProcate
SIP stack, Dialog Usage Manager and related libraries
Loading...
Searching...
No Matches
ResponseContext.cxx
Go to the documentation of this file.
1#if defined(HAVE_CONFIG_H)
2#include "config.h"
3#endif
4
5#include <iostream>
6#include <utility>
7
12#include "rutil/DnsUtil.hxx"
13#include "rutil/Inserter.hxx"
15#include "rutil/Logger.hxx"
16#include "repro/Proxy.hxx"
19#include "repro/RRDecorator.hxx"
23
24#define RESIPROCATE_SUBSYSTEM resip::Subsystem::REPRO
25
26using namespace resip;
27using namespace repro;
28using namespace std;
29
31 mRequestContext(context),
32 mBestPriority(50),
33 mSecure(false), //context.getOriginalRequest().header(h_RequestLine).uri().scheme() == Symbols::Sips)
34 mIsClientBehindNAT(false)
35{
36}
37
38
40{
41 TransactionMap::iterator i;
42
43 for(i=mTerminatedTransactionMap.begin(); i!=mTerminatedTransactionMap.end();++i)
44 {
45 delete i->second;
46 }
48
49 for(i=mActiveTransactionMap.begin(); i!=mActiveTransactionMap.end();++i)
50 {
51 delete i->second;
52 }
54
55 for(i=mCandidateTransactionMap.begin(); i!=mCandidateTransactionMap.end();++i)
56 {
57 delete i->second;
58 }
60}
61
63ResponseContext::addTarget(const NameAddr& addr, bool beginImmediately)
64{
65 InfoLog (<< "Adding candidate " << addr);
66 std::unique_ptr<Target> target(new Target(addr));
67 Data tid=target->tid();
68 addTarget(std::move(target), beginImmediately);
69 return tid;
70}
71
72bool
73ResponseContext::addTarget(std::unique_ptr<repro::Target> target, bool beginImmediately, bool checkDuplicates)
74{
75 if(mRequestContext.mHaveSentFinalResponse || !target.get())
76 {
77 return false;
78 }
79
80 //Disallow sip: if secure
81 if(mSecure && target->uri().scheme() != Symbols::Sips)
82 {
83 return false;
84 }
85
86 //Make sure we don't have Targets with an invalid initial state.
87 if(target->status() != Target::Candidate)
88 {
89 return false;
90 }
91
92 if(beginImmediately)
93 {
94 if (checkDuplicates && isDuplicate(target.get()))
95 {
96 return false;
97 }
98
99 mTargetList.push_back(target->rec());
100
101 beginClientTransaction(target.get());
102 target->status()=Target::Started;
103 Target* toAdd=target.release();
104 mActiveTransactionMap[toAdd->tid()]=toAdd;
105 }
106 else
107 {
108 if(target->mShouldAutoProcess) // note: for base repro - this is always true
109 {
110 std::list<resip::Data> queue;
111 queue.push_back(target->tid());
112 mTransactionQueueCollection.push_back(queue);
113 }
114
115 Target* toAdd=target.release();
116 mCandidateTransactionMap[toAdd->tid()]=toAdd;
117 }
118
119 return true;
120}
121
122bool
124 bool highPriority)
125{
126 std::list<resip::Data> queue;
127 Target* target=0;
128 TargetPtrList::iterator it;
129
130 if(mRequestContext.mHaveSentFinalResponse || targets.empty())
131 {
132 for(it=targets.begin();it!=targets.end();it++)
133 {
134 delete *it;
135 }
136
137 targets.clear();
138 return false;
139 }
140
141 for(it=targets.begin();it!=targets.end();it++)
142 {
143 target=*it;
144
145 if((!mSecure || target->uri().scheme() == Symbols::Sips) &&
146 target->status() == Target::Candidate)
147 {
148 if(target->mShouldAutoProcess)
149 {
150 queue.push_back(target->tid());
151 }
152 DebugLog(<<"Adding Target to Candidates: " << target->uri() << " tid=" << target->tid());
153 mCandidateTransactionMap[target->tid()]=target;
154 }
155 else
156 {
157 DebugLog(<<"Bad Target: " << target->uri());
158 delete target;
159 }
160 }
161
162 targets.clear();
163
164 if(highPriority) // note: for base repro - this is always false
165 {
166 mTransactionQueueCollection.push_front(queue);
167 }
168 else
169 {
170 mTransactionQueueCollection.push_back(queue);
171 }
172
173 return true;
174}
175
176bool
178{
179 bool result=false;
180
181 if(mCandidateTransactionMap.empty())
182 {
183 return result;
184 }
185
186 for (TransactionMap::iterator i=mCandidateTransactionMap.begin(); i != mCandidateTransactionMap.end(); )
187 {
189 {
190 mTargetList.push_back(i->second->rec()); // Add to Target list for future duplicate detection
191 beginClientTransaction(i->second);
192 result=true;
193 // see rfc 3261 section 16.6
194 //This code moves the Target from mCandidateTransactionMap to mActiveTransactionMap,
195 //and begins the transaction.
196 mActiveTransactionMap[i->second->tid()] = i->second;
197 InfoLog (<< "Creating new client transaction " << i->second->tid() << " -> " << i->second->uri());
198 }
199 else
200 {
201 i->second->status() = Target::Terminated;
202 mTerminatedTransactionMap[i->second->tid()] = i->second;
203 DebugLog(<<"Found a repeated target.");
204 }
205
206 TransactionMap::iterator temp=i;
207 i++;
208 mCandidateTransactionMap.erase(temp);
209 }
210
211 return result;
212}
213
214bool
216{
217 TransactionMap::iterator i = mCandidateTransactionMap.find(tid);
218 if(i==mCandidateTransactionMap.end())
219 {
220 return false;
221 }
222
224 {
225 i->second->status() = Target::Terminated;
226 mTerminatedTransactionMap[i->second->tid()] = i->second;
228 return false;
229 }
230
231 mTargetList.push_back(i->second->rec()); // Add to Target list for future duplicate detection
232
233 beginClientTransaction(i->second);
234 mActiveTransactionMap[i->second->tid()] = i->second;
235 InfoLog(<< "Creating new client transaction " << i->second->tid() << " -> " << i->second->uri());
237
238 return true;
239}
240
241bool
243{
245 {
246 return false;
247 }
248
249 InfoLog (<< "Cancel all proceeding client transactions: " << (mCandidateTransactionMap.size() +
250 mActiveTransactionMap.size()));
251
252 if(mActiveTransactionMap.empty())
253 {
254 return false;
255 }
256
257 // CANCEL INVITE branches
258 for (TransactionMap::iterator i = mActiveTransactionMap.begin();
259 i != mActiveTransactionMap.end(); ++i)
260 {
261 cancelClientTransaction(i->second, reasons);
262 }
263
264 return true;
265
266}
267
268bool
270{
271
272 InfoLog (<< "Cancel ALL client transactions: " << mCandidateTransactionMap.size()
273 << " pending, " << mActiveTransactionMap.size() << " active.");
274
276 {
277 return false;
278 }
279
280 // CANCEL INVITE branches
282 {
283 for (TransactionMap::iterator i = mActiveTransactionMap.begin();
284 i != mActiveTransactionMap.end(); ++i)
285 {
286 cancelClientTransaction(i->second, reasons);
287 }
288 }
289
291
292 return true;
293
294}
295
296bool
298{
299 bool result=false;
300 for (TransactionMap::iterator j = mCandidateTransactionMap.begin();
301 j != mCandidateTransactionMap.end();)
302 {
303 result=true;
304 cancelClientTransaction(j->second, reasons);
305 mTerminatedTransactionMap[j->second->tid()] = j->second;
306 TransactionMap::iterator temp = j;
307 j++;
308 mCandidateTransactionMap.erase(temp);
309 }
310
311 return result;
312}
313
314bool
316{
317
318 TransactionMap::iterator i = mActiveTransactionMap.find(tid);
320 {
321 if(i!=mActiveTransactionMap.end())
322 {
323 cancelClientTransaction(i->second, reasons);
324 return true;
325 }
326 }
327
328 TransactionMap::iterator j = mCandidateTransactionMap.find(tid);
329 if(j != mCandidateTransactionMap.end())
330 {
331 cancelClientTransaction(j->second, reasons);
332 mTerminatedTransactionMap[tid] = j->second;
334 return true;
335 }
336
337 return false;
338}
339
340Target*
342{
343 // .bwc. This tid is most likely to be found in either the Candidate targets,
344 // or the Active targets.
345 TransactionMap::const_iterator pend = mCandidateTransactionMap.find(tid);
346 if(pend != mCandidateTransactionMap.end())
347 {
348 resip_assert(pend->second->status()==Target::Candidate);
349 return pend->second;
350 }
351
352 TransactionMap::const_iterator act = mActiveTransactionMap.find(tid);
353 if(act != mActiveTransactionMap.end())
354 {
355 resip_assert(!(act->second->status()==Target::Candidate || act->second->status()==Target::Terminated));
356 return act->second;
357 }
358
359 TransactionMap::const_iterator term = mTerminatedTransactionMap.find(tid);
360 if(term != mTerminatedTransactionMap.end())
361 {
362 resip_assert(term->second->status()==Target::Terminated);
363 return term->second;
364 }
365
366 return 0;
367}
368
374
375bool
380
381bool
386
387bool
392
393bool
400
401bool
406
407bool
409{
410 TransactionMap::const_iterator i=mCandidateTransactionMap.find(tid);
411 return i!=mCandidateTransactionMap.end();
412}
413
414bool
416{
417 TransactionMap::const_iterator i=mActiveTransactionMap.find(tid);
418 return i!=mActiveTransactionMap.end();
419}
420
421bool
423{
424 TransactionMap::const_iterator i=mTerminatedTransactionMap.find(tid);
425 return i!=mTerminatedTransactionMap.end();
426}
427
428void
430{
431 // .bwc. This tid will most likely be found in the map of terminated
432 // transactions, under normal circumstances.
433 // NOTE: This does not remove the corresponding entry in mTargetList.
434 // This is the intended behavior, because the same target should not
435 // be added again later.
436
437 TransactionMap::iterator i = mTerminatedTransactionMap.find(transactionId);
438 if(i!=mTerminatedTransactionMap.end())
439 {
440 delete i->second;
442 return;
443 }
444
445 i=mCandidateTransactionMap.find(transactionId);
446 if(i!=mCandidateTransactionMap.end())
447 {
448 delete i->second;
450 return;
451 }
452
453 i=mActiveTransactionMap.find(transactionId);
454 if(i!=mActiveTransactionMap.end())
455 {
456 delete i->second;
457 mActiveTransactionMap.erase(i);
458 WarningLog(<< "Something removed an active transaction, " << transactionId
459 << ". It is very likely that something is broken here. ");
460 return;
461 }
462
463}
464
465bool
467{
468 resip::ContactList::const_iterator i;
469 // make sure each target is only inserted once
470
471 // !bwc! We can not optimize this by using stl, because operator
472 // == does not conform to the partial-ordering established by operator
473 // < (We can very easily have a < b and a==b simultaneously).
474 // [TODO] Once we have a canonicalized form, we can improve this.
475
476 for(i=mTargetList.begin();i!=mTargetList.end();i++)
477 {
478 if(*i==target->rec())
479 {
480 return true;
481 }
482 }
483
484 return false;
485}
486
487std::unique_ptr<resip::SipMessage>
489{
491 std::unique_ptr<SipMessage> request(new SipMessage(orig));
492
493 // If the target has a ;lr parameter, then perform loose routing
494 if (target->uri().exists(p_lr))
495 {
496 request->header(h_Routes).push_front(NameAddr(target->uri()));
497 }
498 else
499 {
500 request->header(h_RequestLine).uri() = target->uri();
501 }
502
503 // .bwc. Proxy checks whether this is valid, and rejects if not.
504 request->header(h_MaxForwards).value()--;
505
506 bool inDialog = false;
507
508 try
509 {
510 inDialog = request->header(h_To).exists(p_tag);
511 }
512 catch (resip::ParseException &)
513 {
514 // ?bwc? Do we ignore this and just say this is a dialog-creating
515 // request?
516 }
517
518 // Potential source Record-Route addition only for new dialogs
519 // !bwc! It looks like we really ought to be record-routing in-dialog
520 // stuff.
521
522 // only add record route if configured to do so
523 bool transportSpecificRecordRoute;
524 const NameAddr &receivedTransportRecordRoute = mRequestContext.mProxy.getRecordRoute(orig.getSource().mTransportKey, &transportSpecificRecordRoute);
525 if (!receivedTransportRecordRoute.uri().host().empty())
526 {
527 if (!inDialog && // only for dialog-creating request
528 (request->method() == INVITE ||
529 request->method() == SUBSCRIBE ||
530 request->method() == REFER))
531 {
532 insertRecordRoute(*request,
534 receivedTransportRecordRoute,
535 transportSpecificRecordRoute,
536 target);
537 }
538 else if (request->method() == REGISTER)
539 {
540 insertRecordRoute(*request,
542 receivedTransportRecordRoute,
543 transportSpecificRecordRoute,
544 target,
545 true /* do Path instead */);
546 }
547 }
548
552 target->rec().mUseFlowRouting &&
553 target->rec().mReceivedFrom.mFlowKey)
554 {
555 // .bwc. We only override the destination if we are sending to an
556 // outbound contact. If this is not an outbound contact, but the
557 // endpoint has given us a Contact with the correct ip-address and
558 // port, we might be able to find the connection they formed when they
559 // registered earlier, but that will happen down in TransportSelector.
560 request->setDestination(target->rec().mReceivedFrom);
561 }
562
563 DebugLog(<< "Set tuple dest: " << request->getDestination());
564
565 // .bwc. Path header addition.
566 if (!target->rec().mSipPath.empty())
567 {
568 request->header(h_Routes).append(target->rec().mSipPath);
569 }
570
571 // a baboon might adorn the message, record call logs or CDRs, might
572 // insert loose routes on the way to the next hop
574
575 //This is where the request acquires the tid of the Target. The tids
576 //should be the same from here on out.
577 request->header(h_Vias).push_front(target->via());
578
579 return request;
580}
581
582void
584{
585 // .bwc. This is a private function, and if anything calls this with a
586 // target in an invalid state, it is a bug.
588
589 auto targetRequest = buildTargetRequest(target);
590
591 if (mRequestContext.getOriginalRequest().method() == INVITE)
592 {
593 // Starting TimerC for the new client transaction
594 updateTimerC(targetRequest->getTransactionId());
595 }
596
597 // the rest of 16.6 is implemented by the transaction layer of resip
598 // - determining the next hop (tuple)
599 // - adding a content-length if needed
600 // - sending the request
601 sendRequest(*targetRequest);
602
603 target->status() = Target::Started;
604}
605
606void
608 const resip::Tuple& receivedTransportTuple,
609 const resip::NameAddr& receivedTransportRecordRoute,
610 bool transportSpecificRecordRoute,
611 Target* target,
612 bool doPathInstead)
613{
614 resip::Data inboundFlowToken=getInboundFlowToken(doPathInstead);
615 bool needsOutboundFlowToken=outboundFlowTokenNeeded(target);
616 bool recordRouted=false;
617 // .bwc. If we have a flow-token we need to insert, we need to record-route.
618 // Also, we might record-route if we are configured to do so.
619 if( !inboundFlowToken.empty()
620 || needsOutboundFlowToken
622 {
624 if(inboundFlowToken.empty())
625 {
626 rt = receivedTransportRecordRoute;
627 }
628 else
629 {
630 if(isSecure(receivedTransportTuple.getType()))
631 {
632 // .bwc. Debatable. Should we be willing to reuse a TLS connection
633 // at the behest of a Route header with no hostname in it?
634 rt = receivedTransportRecordRoute;
635 rt.uri().scheme() = "sips";
636 }
637 else
638 {
639 if(receivedTransportTuple.isAnyInterface() || transportSpecificRecordRoute)
640 {
641 rt = receivedTransportRecordRoute;
642 }
643 else
644 {
645 // If record-route is not transport specific (ie: global record route setting), then generate appropriate one from source
646 // as long as we are not bound to INADDR_ANY
647 rt.uri().host()=resip::Tuple::inet_ntop(receivedTransportTuple);
648 rt.uri().port() = receivedTransportTuple.getPort();
649 rt.uri().param(resip::p_transport) = resip::Tuple::toDataLower(receivedTransportTuple.getType());
650 }
651 }
652 rt.uri().user() = inboundFlowToken;
653 }
654 Helper::massageRoute(outgoing,rt);
655
656#ifdef USE_SIGCOMP
658 target->uri().exists(p_comp) &&
659 target->uri().param(p_comp)=="sigcomp")
660 {
661 rt.uri().param(p_comp)="sigcomp";
662 }
663#endif
664
665 recordRouted=true;
666 if(doPathInstead)
667 {
668 if(!inboundFlowToken.empty())
669 {
670 // Only add ;ob parameter if client really supports outbound (ie. not for NAT detection mode or flow token hack)
671 if(!mRequestContext.getOriginalRequest().empty(h_Supporteds) &&
673 {
674 rt.uri().param(p_ob);
675 }
676 }
677 outgoing.header(h_Paths).push_front(rt);
678 if(!outgoing.header(h_Supporteds).find(Token("path")))
679 {
680 outgoing.header(h_Supporteds).push_back(Token("path"));
681 }
682 InfoLog (<< "Added Path: " << rt);
683 }
684 else
685 {
686 outgoing.header(h_RecordRoutes).push_front(rt);
687 InfoLog (<< "Added Record-Route: " << rt);
688 }
689 }
690
691 // .bwc. We always need to add this, since we never know if a transport
692 // switch is going to happen. (Except for Path headers; we don't care about
693 // transport switches on REGISTER requests. If we already put a Path header
694 // in, we do care though.)
695 if(!doPathInstead || recordRouted)
696 {
697 std::unique_ptr<resip::MessageDecorator> rrDecorator(
699 receivedTransportTuple,
700 receivedTransportRecordRoute,
701 recordRouted,
702 !inboundFlowToken.empty(),
704 doPathInstead,
706 outgoing.addOutboundDecorator(std::move(rrDecorator));
707 }
708}
709
712{
715 if(orig.empty(h_Contacts) || !orig.header(h_Contacts).front().isWellFormed())
716 {
717 return flowToken;
718 }
719
720 const resip::NameAddr& contact(orig.header(h_Contacts).front());
721
723 (contact.uri().exists(p_ob) || contact.exists(p_regid)))
724 {
725 if(orig.header(h_Vias).size()==1)
726 {
727 // This arrived over an outbound flow (or so the endpoint claims)
728 // (See outbound-09 Sec 4.3 para 3)
729 resip::Data binaryFlowToken;
730 resip::Tuple source(orig.getSource());
731 source.onlyUseExistingConnection=true;
732 Tuple::writeBinaryToken(source, binaryFlowToken, Proxy::FlowTokenSalt);
733 flowToken = binaryFlowToken.base64encode();
734 }
735 else if(doPathInstead)
736 {
737 // Need to try to detect Path failures
738 if(orig.empty(h_Paths) || !orig.header(h_Paths).back().uri().exists(p_ob))
739 {
740 // Yikes! Client is trying to use outbound, but edge-proxy did not
741 // support it. The registrar will either reject this (if it supports
742 // outbound), or will not indicate outbound support (which should
743 // let the client know that the flow setup failed)
744 WarningLog(<<"Client asked for outbound processing, but the edge "
745 "proxy did not support it. There's nothing we can do to "
746 "salvage this. The registrar might end up rejecting the "
747 "registration (if is supports outbound), or it might just "
748 "fail to add a Supported: outbound. In either case, the "
749 "client should know what's up, so we just let it all "
750 "happen.");
751 }
752 }
753 }
754
755 if(flowToken.empty() && orig.header(h_Vias).size() == 1)
756 {
759 needsFlowTokenToWork(contact))
760 {
761 // !bwc! TODO remove this when flow-token hack is no longer needed.
762 // Poor-man's outbound. Shouldn't be our default behavior, because it
763 // breaks target-refreshes (once a flow-token is in the Route-Set, the
764 // flow-token cannot be changed, and will override any update to the
765 // Contact)
766 resip::Data binaryFlowToken;
768 flowToken = binaryFlowToken.base64encode();
769 }
770 }
771
772 if (flowToken.empty() &&
775 {
776 resip::Data binaryFlowToken;
778 flowToken = binaryFlowToken.base64encode();
779 }
780
781 return flowToken;
782}
783
784bool
786{
787 if(mRequestContext.mProxy.isMyUri(target->uri()))
788 {
789 // .bwc. We don't need to put flow-tokens pointed at ourselves.
790 return false;
791 }
792
793 if((target->rec().mReceivedFrom.mFlowKey &&
794 target->rec().mUseFlowRouting)
797 {
799 return true;
800 }
801
802 return false;
803}
804
805bool
807{
808 if(DnsUtil::isIpAddress(contact.uri().host()))
809 {
810 // IP address in host-part.
811 if(contact.uri().scheme()=="sips")
812 {
813 // TLS with no FQDN. Impossible without flow-token fixup, even if no
814 // NAT is involved.
815 return true;
816 }
817
818 if(contact.uri().exists(p_transport))
819 {
820 TransportType type = toTransportType(contact.uri().param(p_transport));
821 if(isSecure(type))
822 {
823 // TLS with no FQDN. Impossible without flow-token fixup, even if no
824 // NAT is involved.
825 return true;
826 }
827 }
828 }
829
830 if(contact.uri().exists(p_sigcompId))
831 {
832 if(contact.uri().exists(p_transport))
833 {
834 TransportType type = toTransportType(contact.uri().param(p_transport));
835 if(type == TLS || type == TCP)
836 {
837 // Client is using sigcomp on the first hop using a connection-
838 // oriented transport. For this to work, that connection has to be
839 // reused for all traffic.
840 return true;
841 }
842 }
843 }
844 return false;
845}
846
847bool
849{
850 if(mRequestContext.mProxy.isMyUri(target->uri()))
851 {
852 return true;
853 }
854 return false;
855}
856
857void
859{
860 resip_assert (request.isRequest());
861
862 // Do any required session accounting with this forward request - allows Session Routed event
863 mRequestContext.getProxy().doSessionAccounting(request, false /* received */, mRequestContext);
864
865 if (request.method() != CANCEL &&
866 request.method() != ACK)
867 {
870// if(!mRequestContext.getDigestIdentity().empty())
871// {
872// requestPtr->header(h_Identity);
873// // !bwc! Need to fill in the Identity-Info header telling where our
874// // cert can be found.
875// }
876 }
877
878 // If this request is destined outside our domain then remove certain headers
879 bool isMyUri;
880 if(request.exists(h_Routes) &&
881 !request.const_header(h_Routes).empty())
882 {
883 isMyUri = mRequestContext.getProxy().isMyUri(request.const_header(h_Routes).front().uri());
884 }
885 else
886 {
888 }
889 if(!isMyUri)
890 {
891 // TODO - P-Asserted-Identity Processing
892 // RFC3325 - section 5
893 // When a proxy forwards a message to another node, it must first
894 // determine if it trusts that node or not. If it trusts the node, the
895 // proxy does not remove any P-Asserted-Identity header fields that it
896 // generated itself, or that it received from a trusted source. If it
897 // does not trust the element, then the proxy MUST examine the Privacy
898 // header field (if present) to determine if the user requested that
899 // asserted identity information be kept private.
900
901 // Note: Since we have no better mechanism to determine if destination is trusted or
902 // not we will assume that all destinations outside our domain are not-trusted
903 // and will remove the P-Asserted-Identity header, if Privacy is set to "id"
905 request.exists(h_Privacies) &&
906 request.header(h_Privacies).size() > 0 &&
908 {
909 // Look for "id" token
910 bool found = false;
911 PrivacyCategories::iterator it = request.header(h_Privacies).begin();
912 for(; it != request.header(h_Privacies).end() && !found; it++)
913 {
914 std::vector<Data>::iterator itToken = it->value().begin();
915 for(; itToken != it->value().end() && !found; itToken++)
916 {
917 if(*itToken == "id")
918 {
920 found = true;
921 }
922 }
923 }
924 }
925
926 // Delete the Proxy-Auth header for this realm if forwarding outside our domain
927 // other Proxy-Auth headers might be needed by a downsteram node
928 if (request.exists(h_ProxyAuthorizations) && !mRequestContext.getProxy().isNeverStripProxyAuthorizationHeadersEnabled())
929 {
930 Auths &authHeaders = request.header(h_ProxyAuthorizations);
931 for (Auths::iterator i = authHeaders.begin(); i != authHeaders.end(); )
932 {
933 if(i->exists(p_realm) && mRequestContext.getProxy().isMyDomain(i->param(p_realm)))
934 {
935 i = authHeaders.erase(i);
936 }
937 else
938 {
939 ++i;
940 }
941 }
942 }
943 }
944
945 if (request.method() == ACK)
946 {
947 DebugLog(<<"Posting Ack200DoneMessage");
949 }
950
951 mRequestContext.send(request);
952}
953
954
955void
957{
958 resip_assert(request.isRequest());
959 resip_assert(request.method() == CANCEL);
960
961 std::unique_ptr<SipMessage> ok(Helper::makeResponse(request, 200));
963
965 {
966 if (request.exists(h_Reasons))
967 {
968 cancelAllClientTransactions(&request.header(h_Reasons));
969 }
970 else
971 {
973 }
975 {
976 SipMessage reqterm;
979 }
980 }
981}
982
983void
985{
986 InfoLog(<<"Updating timer C for client transaction " << tid);
987 int timerCSerial = TimerCSerialInit;
988 auto i = mTimerCSerial.find(tid);
989 if (i != mTimerCSerial.end())
990 {
991 timerCSerial = ++i->second;
992 }
993 else
994 {
995 mTimerCSerial[tid] = timerCSerial;
996 }
997 TimerCMessage *tc = new TimerCMessage(tid, timerCSerial);
998 mRequestContext.getProxy().postTimerC(std::unique_ptr<TimerCMessage>(tc));
999}
1000
1001void
1003{
1004 bool isLatestTimer = false;
1005 auto i = mTimerCSerial.find(tid);
1006 if (i != mTimerCSerial.end())
1007 {
1008 isLatestTimer = (serial == i->second);
1009 }
1010
1011 if (!mRequestContext.mHaveSentFinalResponse && isLatestTimer)
1012 {
1013 InfoLog(<<"Canceling client transaction " << tid << " due to timer C.");
1015
1016 if (serial == TimerCSerialInit)
1017 {
1018 // TimerC fired BEFORE receiving provisional response
1019 // From RFC3261, 16.8 -
1020 // the proxy MUST behave as if the transaction received a 408(Request Timeout) response.
1021 // Otherwise, relying on the client to correctly handle CANCEL request and generate 487, which will
1022 // also trigger next target processing
1023 auto i = mActiveTransactionMap.find(tid);
1024 if (i != mActiveTransactionMap.end())
1025 {
1026 // Reconstruct original target request to be able to generate the response
1027 auto targetRequest = buildTargetRequest(i->second);
1028 std::unique_ptr<resip::SipMessage> response(Helper::makeResponse(*targetRequest, 408));
1029 // Pretend like 408 response has been received to trigger next target processing
1030 mRequestContext.process(std::move(response));
1031 }
1032 }
1033 }
1034}
1035
1036void
1038{
1039 InfoLog (<< "processResponse: " << endl << response);
1040
1041 // store this before we pop the via and lose the branch tag
1043
1044 resip_assert (response.isResponse());
1045 resip_assert (response.exists(h_Vias) && !response.header(h_Vias).empty());
1046 response.header(h_Vias).pop_front();
1047
1048 // Stop processing responses that have nowhere else to go
1049 if (response.header(h_Vias).empty())
1050 {
1051 // CANCEL/200s only have one Via. Likewise 100s only have one Via
1052 // Silently stop processing the CANCEL responses.
1053 // We will handle the 100 responses later
1054 // Log other responses we can't forward
1055
1056 if(response.method()==CANCEL)
1057 {
1058 return;
1059 }
1060 else if (response.header(h_StatusLine).statusCode() != 100)
1061 {
1063 {
1064 // If handle MissingResponseVias returns false, then processing does not continue
1065 if (response.header(h_StatusLine).statusCode() > 199)
1066 {
1067 InfoLog(<< "Received final response, but can't forward as there are "
1068 "no more Vias. Considering this branch failed. "
1069 << response.brief());
1070 // .bwc. Treat as server error.
1072 }
1073 else
1074 {
1075 InfoLog(<< "Received provisional response, but can't forward as there"
1076 " are no more Vias. Ignoring. " << response.brief());
1077 }
1078 return;
1079 }
1080 }
1081 }
1082 else // We have a second Via
1083 {
1085 {
1086 // .bwc. Original request had an RFC 2543 transaction-id. Set in
1087 // response.
1089 }
1090
1091 const Via& via = response.header(h_Vias).front();
1092
1093 if(!via.isWellFormed())
1094 {
1095 // .bwc. Garbage via. Unrecoverable. Ignore if provisional, terminate
1096 // transaction if not.
1097 DebugLog(<<"Some endpoint has corrupted one of our Vias"
1098 " in their response. (Via is malformed) This is not fixable.");
1099 if(response.header(h_StatusLine).statusCode() > 199)
1100 {
1102 }
1103
1104 return;
1105 }
1106
1107 const Via& origVia = mRequestContext.getOriginalRequest().header(h_Vias).front();
1108 const Data& branch=(via.exists(p_branch) ? via.param(p_branch).getTransactionId() : Data::Empty);
1109 const Data& origBranch=(origVia.exists(p_branch) ? origVia.param(p_branch).getTransactionId() : Data::Empty);
1110
1111 if(!isEqualNoCase(branch,origBranch))
1112 {
1113 // .bwc. Someone altered our branch. Ignore if provisional, terminate
1114 // transaction otherwise.
1115 DebugLog(<<"Some endpoint has altered one of our Vias"
1116 " in their response. (branch is different) This is not fixable.");
1117 if(response.header(h_StatusLine).statusCode() > 199)
1118 {
1120 }
1121
1122 return;
1123 }
1124 }
1125
1126 DebugLog (<< "Search for " << mCurrentResponseTid << " in " << InserterP(mActiveTransactionMap));
1127
1128 TransactionMap::iterator i = mActiveTransactionMap.find(mCurrentResponseTid);
1129
1130 int code = response.header(h_StatusLine).statusCode();
1131 if (i == mActiveTransactionMap.end())
1132 {
1133 // This is a response for a transaction that is no longer/was never active.
1134 // This is probably a useless response (at best) or a malicious response (at worst).
1135 // Log the response here:
1136 if ((code / 100) != 2)
1137 {
1138 InfoLog( << "Discarding stray response" );
1139 }
1140 // Even though this is a tremendously bad idea, some developers may
1141 // decide they want to statelessly forward the response
1142 // Here is the gun. Don't say we didn't warn you!
1143 else
1144 {
1145 // !abr! Because we don't run timers on the transaction after
1146 // it has terminated and because the ACKs on INVITE
1147 // 200-class responses are end-to-end, we don't discard
1148 // 200 responses. To do this properly, we should run a
1149 // transaction timer for 64*T1 and remove transactions from
1150 // the ActiveTransactionMap *only* after that timer expires.
1151 // IN OTHER WORDS, REMOVE THIS CODE.
1152 mRequestContext.sendResponse(response);
1153 }
1154 return;
1155 }
1156
1157 switch (code / 100)
1158 {
1159 case 1:
1160 if(mRequestContext.getOriginalRequest().method() == INVITE)
1161 {
1163 }
1164
1166 {
1167 if (code == 100)
1168 {
1169 return; // stop processing 100 responses
1170 }
1171
1172 mRequestContext.sendResponse(response);
1173 return;
1174 }
1175 break;
1176
1177 case 2:
1179 if (mRequestContext.getOriginalRequest().method() == INVITE)
1180 {
1181 Tokens reasons;
1182 Token reason("SIP");
1183 reason.param(p_cause) = code;
1184 reason.param(p_text) = "Call completed elsewhere";
1185 // RFC 3326 reason-text is a quoted-string
1186 reason.getParameterByEnum(ParameterTypes::text)->setQuoted(true);
1187 reasons.push_back(reason);
1190 mBestResponse.header(h_StatusLine).statusCode() = code;
1191 mRequestContext.sendResponse(response);
1192 }
1194 {
1197 mBestResponse.header(h_StatusLine).statusCode() = code;
1198
1199 // If this is a registration response and we have flow timers enabled, and
1200 // we are doing outbound for this registration and there is no FlowTimer
1201 // header present already, then add a FlowTimer header
1202 if(response.method() == REGISTER &&
1204 response.empty(h_FlowTimer) &&
1205 ((!response.empty(h_Paths) && response.header(h_Paths).back().uri().exists(p_ob)) ||
1206 (!response.empty(h_Requires) && response.header(h_Requires).find(Token(Symbols::Outbound)))))
1207 {
1208 response.header(h_FlowTimer).value() = InteropHelper::getFlowTimerSeconds();
1210 }
1211
1212 mRequestContext.sendResponse(response);
1213 }
1214 break;
1215
1216 case 3:
1217 case 4:
1218 case 5:
1219 DebugLog (<< "forwardedFinal=" << mRequestContext.mHaveSentFinalResponse
1220 << " outstanding client transactions: " << InserterP(mActiveTransactionMap));
1223 {
1224 int priority = getPriority(response);
1225 if (priority == mBestPriority)
1226 {
1227 if (code == 401 || code == 407)
1228 {
1229 if (response.exists(h_WWWAuthenticates))
1230 {
1231 for ( Auths::iterator i=response.header(h_WWWAuthenticates).begin();
1232 i != response.header(h_WWWAuthenticates).end() ; ++i)
1233 {
1234 mBestResponse.header(h_WWWAuthenticates).push_back(*i);
1235 }
1236 }
1237
1238 if (response.exists(h_ProxyAuthenticates))
1239 {
1240 for ( Auths::iterator i=response.header(h_ProxyAuthenticates).begin();
1241 i != response.header(h_ProxyAuthenticates).end() ; ++i)
1242 {
1243 mBestResponse.header(h_ProxyAuthenticates).push_back(*i);
1244 }
1245 mBestResponse.header(h_StatusLine).statusCode() = 407;
1246 }
1247 }
1248 else if (code / 100 == 3) // merge 3xx
1249 {
1250 if(mBestResponse.header(h_StatusLine).statusCode() / 100 != 3)
1251 {
1252 // .bwc. Do not merge contacts in 3xx with contacts from a
1253 // previous 4xx or 5xx
1254 mBestResponse.header(h_Contacts).clear();
1255 }
1256
1257 for (NameAddrs::iterator i=response.header(h_Contacts).begin();
1258 i != response.header(h_Contacts).end(); ++i)
1259 {
1260 // TODO ?bwc? if we are going to be checking whether
1261 // this is "*", we should see if it is well-formed
1262 // first. If we shouldn't be doing any checks on
1263 // the contacts, we should simply remove all of this
1264 // checking code and just blindly copy the contacts over.
1265
1266 if(!i->isWellFormed() || i->isAllContacts())
1267 {
1268 // .bwc. Garbage contact; ignore it.
1269 continue;
1270 }
1271
1272 mBestResponse.header(h_Contacts).push_back(*i);
1273 }
1274
1275 // ?bwc? it is possible for this code to end up with a 300
1276 // with no Contacts in it (because they were all malformed)
1277 // Is this acceptable? Also, is 300 the code we want here?
1278 mBestResponse.header(h_StatusLine).statusCode() = 300;
1279 }
1280 }
1281 else if (priority < mBestPriority)
1282 {
1283 mBestPriority = priority;
1284 mBestResponse = response;
1285 }
1286
1288 {
1290 }
1291 }
1292 break;
1293
1294 case 6:
1297 {
1298 if (mBestResponse.header(h_StatusLine).statusCode() / 100 != 6)
1299 {
1300 mBestResponse = response;
1301 mBestPriority=0; //6xx class responses take precedence over all 3xx,4xx, and 5xx
1302 if (mRequestContext.getOriginalRequest().method() == INVITE)
1303 {
1304 // CANCEL INVITE branches
1305 Tokens reasons;
1306 Token reason("SIP");
1307 reason.param(p_cause) = code;
1308 reason.param(p_text) = response.header(h_StatusLine).reason();
1309 // RFC 3326 reason-text is a quoted-string
1310 reason.getParameterByEnum(ParameterTypes::text)->setQuoted(true);
1311 reasons.push_back(reason);
1313 }
1314 }
1315
1317 {
1319 }
1320 }
1321 break;
1322
1323 default:
1324 resip_assert(0);
1325 break;
1326 }
1327}
1328
1329void
1331{
1332 if (target->status() == Target::Started)
1333 {
1334 InfoLog (<< "Cancel client transaction: " << target);
1335 mRequestContext.cancelClientTransaction(target->via().param(p_branch).getTransactionId(), reasons);
1336
1337 DebugLog(<< "Canceling a transaction with uri: "
1338 << resip::Data::from(target->uri()) << " , to host: "
1339 << target->via().sentHost());
1340 target->status() = Target::Cancelled;
1341 }
1342 else if (target->status() == Target::Candidate)
1343 {
1344 target->status() = Target::Terminated;
1345 }
1346}
1347
1348void
1350{
1351
1352 InfoLog (<< "Terminating client transaction: " << tid << " all = " << areAllTransactionsTerminated());
1353
1354 TransactionMap::iterator i = mActiveTransactionMap.find(tid);
1355 if(i != mActiveTransactionMap.end())
1356 {
1357 InfoLog (<< "client transactions: " << InserterP(mActiveTransactionMap));
1358 i->second->status() = Target::Terminated;
1359 mTerminatedTransactionMap[tid] = i->second;
1360 mActiveTransactionMap.erase(i);
1361 return;
1362 }
1363
1364 TransactionMap::iterator j = mCandidateTransactionMap.find(tid);
1365 if(j != mCandidateTransactionMap.end())
1366 {
1367 InfoLog (<< "client transactions: " << InserterP(mCandidateTransactionMap));
1368 j->second->status() = Target::Terminated;
1369 mTerminatedTransactionMap[tid] = j->second;
1370 mCandidateTransactionMap.erase(j);
1371 return;
1372 }
1373}
1374
1375int
1377{
1378 int responseCode = msg.header(h_StatusLine).statusCode();
1379 int p = 0; // "p" is the relative priority of the response
1380
1381 resip_assert(responseCode >= 300 && responseCode <= 599);
1382 if (responseCode <= 399) // 3xx response
1383 {
1384 return 5; // response priority is 5
1385 }
1386 if (responseCode >= 500)
1387 {
1388 switch(responseCode)
1389 {
1390 case 501: // these four have different priorities
1391 case 503: // which are addressed in the case statement
1392 case 580: // below (with the 4xx responses)
1393 case 513:
1394 break;
1395 default:
1396 return 42; // response priority of other 5xx is 42
1397 }
1398 }
1399
1400 switch(responseCode)
1401 {
1402 // Easy to Repair Responses: 412, 484, 422, 423, 407, 401, 300..399, 402
1403 case 412: // Publish ETag was stale
1404 return 1;
1405 case 484: // overlap dialing
1406 return 2;
1407 case 422: // Session-Timer duration too long
1408 case 423: // Expires too short
1409 return 3;
1410 case 407: // Proxy-Auth
1411 case 401: // UA Digest challenge
1412 return 4;
1413
1414 // 3xx responses have p = 5
1415 case 402: // Payment required
1416 return 6;
1417
1418 // Responses used for negotiation: 493, 429, 420, 406, 415, 488
1419 case 493: // Undecipherable, try again unencrypted
1420 return 10;
1421
1422 case 420: // Required Extension not supported, try again without
1423 return 12;
1424
1425 case 406: // Not Acceptable
1426 case 415: // Unsupported Media Type
1427 case 488: // Not Acceptable Here
1428 return 13;
1429
1430 // Possibly useful for negotiation, but less likely: 421, 416, 417, 494, 580, 485, 405, 501, 413, 414
1431
1432 case 416: // Unsupported scheme
1433 case 417: // Unknown Resource-Priority
1434 return 20;
1435
1436 case 405: // Method not allowed (both used for negotiating REFER, PUBLISH, etc..
1437 case 501: // Usually used when the method is not OK
1438 return 21;
1439
1440 case 580: // Preconditions failure
1441 return 22;
1442
1443 case 485: // Ambiguous userpart. A bit better than 404?
1444 return 23;
1445
1446 case 428: // Use Identity header
1447 case 429: // Provide Referrer Identity
1448 case 494: // Use the sec-agree mechanism
1449 return 24;
1450
1451 case 413: // Request too big
1452 case 414: // URI too big
1453 return 25;
1454
1455 case 421: // An extension required by the server was not in the Supported header
1456 return 26;
1457
1458 // The request isn't repairable, but at least we can try to provide some
1459 // useful information: 486, 480, 410, 404, 403, 487
1460
1461 case 486: // Busy Here
1462 return 30;
1463
1464 case 480: // Temporarily unavailable
1465 return 31;
1466
1467 case 410: // Gone
1468 return 32;
1469
1470 case 436: // Bad Identity-Info
1471 case 437: // Unsupported Certificate
1472 case 513: // Message too large
1473 return 33;
1474
1475 case 403: // Forbidden
1476 return 34;
1477
1478 case 404: // Not Found
1479 return 35;
1480
1481 case 487: // Some branches were cancelled, if the UAC sent a CANCEL this is good news
1482 return 36;
1483
1484 // We are hosed: 503, 483, 482, 481, other 5xx, 400, 491, 408 // totally useless
1485
1486 case 503: // bad news, we should never forward this back anyway
1487 return 43;
1488
1489 case 483: // loops, encountered
1490 case 482:
1491 return 41;
1492
1493 // other 5xx p = 42
1494
1495 // UAS is seriously confused: p = 43
1496 // case 481:
1497 // case 400:
1498 // case 491:
1499 // default:
1500
1501 case 408: // very, very bad (even worse than the remaining 4xx responses)
1502 return 49;
1503
1504 default:
1505 return 43;
1506 }
1507 return p;
1508}
1509
1510bool
1512{
1513 resip_assert(lhs.isResponse());
1514 resip_assert(rhs.isResponse());
1515
1516 // !rwm! replace with correct thingy here
1517 return lhs.header(h_StatusLine).statusCode() < rhs.header(h_StatusLine).statusCode();
1518}
1519
1520void
1522{
1523 InfoLog (<< "Forwarding best response: " << mBestResponse.brief());
1524
1525 Tokens reasons;
1526 Token reason("SIP");
1527 reason.param(p_cause) = mBestResponse.header(h_StatusLine).statusCode();
1528 reason.param(p_text) = mBestResponse.header(h_StatusLine).reason();
1529 // RFC 3326 reason-text is a quoted-string
1530 reason.getParameterByEnum(ParameterTypes::text)->setQuoted(true);
1531 reasons.push_back(reason);
1532
1534
1536 {
1538 }
1539
1540 if(mBestResponse.header(h_StatusLine).statusCode() == 503)
1541 {
1542 // See RFC 3261 sec 16.7, page 111, paragraph 2
1543 mBestResponse.header(h_StatusLine).statusCode() = 500;
1544 }
1545
1546 if(mBestResponse.header(h_StatusLine).statusCode() == 408 &&
1547 mBestResponse.method()!=INVITE)
1548 {
1549 // We don't forward back NIT 408; we just silently abandon the transaction - RFC4321 - section 1.4 408 for non-INVITE is not useful
1550 DebugLog(<< "Got NIT 408, abandoning: "<<mRequestContext.getTransactionId());
1552 mRequestContext.mHaveSentFinalResponse = true; // To avoid Request context from sending a response
1553 }
1554 else
1555 {
1557 }
1558}
1559
1561repro::operator<<(EncodeStream& strm, const ResponseContext& rc)
1562{
1563 strm << "ResponseContext: "
1564 << " identity=" << rc.mRequestContext.getDigestIdentity()
1565 << " best=" << rc.mBestPriority << " " << rc.mBestResponse.brief()
1566 << " forwarded=" << rc.mRequestContext.mHaveSentFinalResponse
1567 << " pending=" << InserterP(rc.mCandidateTransactionMap)
1568 << " active=" << InserterP(rc.mActiveTransactionMap)
1569 << " terminated=" << InserterP(rc.mTerminatedTransactionMap);
1570
1571 return strm;
1572}
1573
1574
1575/* ====================================================================
1576 * The Vovida Software License, Version 1.0
1577 *
1578 * Copyright (c) 2000 Vovida Networks, Inc. All rights reserved.
1579 *
1580 * Redistribution and use in source and binary forms, with or without
1581 * modification, are permitted provided that the following conditions
1582 * are met:
1583 *
1584 * 1. Redistributions of source code must retain the above copyright
1585 * notice, this list of conditions and the following disclaimer.
1586 *
1587 * 2. Redistributions in binary form must reproduce the above copyright
1588 * notice, this list of conditions and the following disclaimer in
1589 * the documentation and/or other materials provided with the
1590 * distribution.
1591 *
1592 * 3. The names "VOCAL", "Vovida Open Communication Application Library",
1593 * and "Vovida Open Communication Application Library (VOCAL)" must
1594 * not be used to endorse or promote products derived from this
1595 * software without prior written permission. For written
1596 * permission, please contact vocal@vovida.org.
1597 *
1598 * 4. Products derived from this software may not be called "VOCAL", nor
1599 * may "VOCAL" appear in their name, without prior written
1600 * permission of Vovida Networks, Inc.
1601 *
1602 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
1603 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1604 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
1605 * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL VOVIDA
1606 * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
1607 * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
1608 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
1609 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
1610 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
1611 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1612 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
1613 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
1614 * DAMAGE.
1615 *
1616 * ====================================================================
1617 *
1618 * This software consists of voluntary contributions made by Vovida
1619 * Networks, Inc. and many individuals on behalf of Vovida Networks,
1620 * Inc. For more information on Vovida Networks, Inc., please see
1621 * <http://www.vovida.org/>.
1622 *
1623 */
#define DebugLog(args_)
Definition Logger.hxx:128
#define InfoLog(args_)
Definition Logger.hxx:131
#define WarningLog(args_)
Definition Logger.hxx:134
#define resip_assert(x)
This file needs to work for both C and C++ code.
virtual bool isMyUri(const resip::Uri &uri) const
Definition Proxy.cxx:600
const resip::NameAddr & getRecordRoute(unsigned int transportKey, bool *transportSpecific=0) const
Definition Proxy.cxx:664
void doSessionAccounting(const resip::SipMessage &sip, bool received, RequestContext &context)
Definition Proxy.cxx:703
bool isPAssertedIdentityProcessingEnabled()
Definition Proxy.hxx:84
static resip::Data FlowTokenSalt
Definition Proxy.hxx:61
bool compressionEnabled() const
Definition Proxy.cxx:685
resip::SipStack & getStack() noexcept
Definition Proxy.hxx:88
void postTimerC(std::unique_ptr< TimerCMessage > tc)
Definition Proxy.cxx:577
bool isNeverStripProxyAuthorizationHeadersEnabled()
Definition Proxy.hxx:85
bool getRecordRouteForced() const
Definition Proxy.hxx:81
void addClientTransaction(const resip::Data &transactionId, RequestContext *rc)
Definition Proxy.cxx:563
resip::SipMessage & getOriginalRequest()
Returns the SipMessage associated with the server transaction.
virtual void process(resip::TransactionTerminated &msg)
virtual bool handleMissingResponseVias(resip::SipMessage *response)
virtual void cancelClientTransaction(const resip::Data &tid, const resip::Tokens *reasons=0)
virtual void send(resip::SipMessage &msg)
const resip::Data & getDigestIdentity() const
void sendResponse(resip::SipMessage &response)
resip::Data getTransactionId() const
bool operator()(const resip::SipMessage &lhs, const resip::SipMessage &rhs) const
const TransactionMap & getCandidateTransactionMap() const
Self-explanatory.
virtual void forwardBestResponse()
bool isDuplicate(const repro::Target *target) const
void removeClientTransaction(const resip::Data &transactionId)
bool isTerminated(const resip::Data &tid) const
bool needsFlowTokenToWork(const resip::NameAddr &contact) const
std::unique_ptr< resip::SipMessage > buildTargetRequest(Target *target)
bool cancelAllClientTransactions(const resip::Tokens *reasons=0)
Cancels all active client transactions.
int getPriority(const resip::SipMessage &msg)
resip::Data addTarget(const resip::NameAddr &target, bool beginImmediately=false)
Adds this Target as a SimpleTarget to the collection of Targets.
RequestContext & mRequestContext
bwc! This should probably not be private, since these two classes are
resip::Data getInboundFlowToken(bool doPathInstead)
std::map< resip::Data, repro::Target * > TransactionMap
bool hasTerminatedTransactions() const
bool sendingToSelf(Target *target)
bool hasCandidateTransactions() const
TransactionMap mTerminatedTransactionMap
bool isActive(const resip::Data &tid) const
static const int TimerCSerialInit
void processTimerC(const resip::Data &tid, int serial)
resip::SipMessage mBestResponse
bool addTargetBatch(TargetPtrList &targets, bool highPriority=false)
Adds a batch of Targets.
void sendRequest(resip::SipMessage &request)
void terminateClientTransaction(const resip::Data &tid)
void updateTimerC(const resip::Data &tid)
bool outboundFlowTokenNeeded(Target *target)
ResponseContext(RequestContext &parent)
bool cancelClientTransaction(const resip::Data &serial, const resip::Tokens *reasons=0)
Cancels this client transaction if active, or Terminates it if Candidate.
void processCancel(const resip::SipMessage &request)
bool beginClientTransaction(const resip::Data &serial)
Begins a client transaction.
std::list< std::list< resip::Data > > mTransactionQueueCollection
Target * getTarget(const resip::Data &serial) const
Self-explanatory.
std::unordered_map< resip::Data, int > mTimerCSerial
void processResponse(resip::SipMessage &response)
bool clearCandidateTransactions(const resip::Tokens *reasons=0)
Removes all Candidate transactions.
void insertRecordRoute(resip::SipMessage &outgoing, const resip::Tuple &receivedTransportTuple, const resip::NameAddr &receivedTransportRecordRoute, bool transportSpecificRecordRoute, Target *target, bool doPathInstead=false)
TransactionMap mActiveTransactionMap
TransactionMap mCandidateTransactionMap
resip::ContactList mTargetList
bool beginClientTransactions()
Begins all Candidate client transactions.
bool isCandidate(const resip::Data &tid) const
bool cancelActiveClientTransactions(const resip::Tokens *reasons=0)
Cancels all active client transactions.
bool areAllTransactionsTerminated() const
virtual const resip::Uri & uri() const
Definition Target.hxx:45
virtual Status & status()
Definition Target.cxx:57
virtual const resip::Data & tid() const
Definition Target.cxx:51
virtual const resip::Via & via() const
Definition Target.cxx:75
virtual const resip::ContactInstanceRecord & rec() const
Definition Target.cxx:81
bool mShouldAutoProcess
Definition Target.hxx:69
An alternative to std::string, encapsulates an arbitrary buffer of bytes.
Definition Data.hxx:90
static const Data Empty
Constant that represents a zero-length data.
Definition Data.hxx:882
Data base64encode(bool useUrlSafe=false) const
Performs RFC 3548 Base 64 encoding of the contents of this data.
Definition Data.cxx:2682
bool empty() const noexcept
Checks whether the Data is empty.
Definition Data.hxx:591
static Data from(const T &x)
Converts from arbitrary other type to Data.
Definition Data.hxx:398
static bool isIpAddress(const Data &ipAddress)
Definition DnsUtil.cxx:453
static void processStrictRoute(SipMessage &request)
Definition Helper.cxx:1701
static void massageRoute(const SipMessage &request, NameAddr &route)
Definition Helper.cxx:1719
static void makeResponse(SipMessage &response, const SipMessage &request, int responseCode, const Data &reason=Data::Empty, const Data &hostname=Data::Empty, const Data &warning=Data::Empty)
Make a response to a provided request.
Definition Helper.cxx:412
static bool getAllowInboundFlowTokensForNonDirectClients()
static bool getRRTokenHackEnabled()
static unsigned int getFlowTimerSeconds()
static bool getOutboundSupported()
bool isWellFormed() const
Returns true iff this element was parsed successfully, according to the implementation of parse().
virtual Brief brief() const
return a facet for brief encoding of message
Definition Message.cxx:16
Represents the "name-addr" and the "addr-spec" elements in the RFC 3261 grammar.
Definition NameAddr.hxx:19
bool exists(const Param< NameAddr > &paramType) const
Definition NameAddr.cxx:477
virtual void setQuoted(bool)
Definition Parameter.hxx:27
BaseException used to indicate parse failures.
Parameter * getParameterByEnum(ParameterTypes::Type type) const
bool empty() const
return true or false indicating whether the mParsers vector is empty or not
An iterator class (bidirectional)
iterator end()
Returns an iterator pointing to the last header field value.
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.
const Uri & uri() const
Represents a SIP message.
void setRFC2543TransactionId(const Data &tid)
bool isRequest() const
Returns true if message is a request, false otherwise.
const Data & getRFC2543TransactionId() const
Calculates an MD5 hash over the Request-URI, To tag (for non-INVITE transactions),...
const Tuple & getReceivedTransportTuple() const
void addOutboundDecorator(std::unique_ptr< MessageDecorator > md)
Call a MessageDecorator to process the message before it is sent to the transport.
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.
virtual const Data & getTransactionId() const
Returns the transaction id from the branch or if 2543, the computed hash.
bool isResponse() const
Returns true if message is a response, false otherwise.
const RequestLine & const_header(const RequestLineType &l) const
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...
void remove(const HeaderBase &headerType)
Prevents a header field from being present when the message is prepared for sending to a transport.
resip::MethodTypes method() const
returns the method type of the message
void abandonServerTransaction(const Data &tid)
Tells the stack that the TU has abandoned a server transaction.
void enableFlowTimer(const resip::Tuple &flow)
static const char * Sips
Definition Symbols.hxx:63
static const char * Outbound
Definition Symbols.hxx:114
Represents the "token" element in the RFC 3261 grammar.
Definition Token.hxx:18
const Data & param(const ExtensionParameter &param) const
Const accessor for non-natively-supported parameter types.
bool isMyDomain(const Data &domain) const
Returns true iff domain matches one of the domains that this TransactionUser is responsible for.
void post(Message *msg)
Posts a Message to this TransactionUser's fifo.
Represents a network address.
Definition Tuple.hxx:59
bool isAnyInterface() const
TRUE if this address is equal to the "INADDR_ANY" value for this address family.
Definition Tuple.cxx:485
bool onlyUseExistingConnection
Definition Tuple.hxx:192
FlowKey mFlowKey
This is a (largely) opaque key that subclasses of Transport will use to help record/find flows.
Definition Tuple.hxx:189
static const Data & toDataLower(TransportType type)
Definition Tuple.cxx:933
static void writeBinaryToken(const Tuple &tuple, Data &container, const Data &salt=Data::Empty)
Definition Tuple.cxx:266
int getPort() const
Definition Tuple.cxx:466
TransportType getType() const
Definition Tuple.hxx:122
static Data inet_ntop(const Tuple &tuple)
Converts the binary socket address to presentation format, via the DnsUtil::inet_ntop() method.
Definition Tuple.cxx:939
TransportKey mTransportKey
Definition Tuple.hxx:190
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.
Represents the "via-parm" element in the RFC 3261 grammar.
Definition Via.hxx:18
bool exists(const Param< Via > &paramType) const
Definition Via.cxx:266
Data & sentHost()
Definition Via.cxx:135
const Data & param(const ExtensionParameter &param) const
Const accessor for non-natively-supported parameter types.
This Class is used to implement a primitive form of RPC using loose XML formatting.
EncodeStream & operator<<(EncodeStream &os, const repro::Processor &rp)
Definition Processor.cxx:69
std::list< Target * > TargetPtrList
Definition Target.hxx:81
dcm! – add UnusedChecking(_enum) below;
StatusLineType h_StatusLine
Definition Headers.cxx:348
bool isEqualNoCase(const Data &left, const Data &right)
Definition Data.hxx:1116
TransportType toTransportType(const resip::Data &transportName)
H_Privacys h_Privacies
Definition Headers.cxx:163
bool isSecure(TransportType type)
Returns true if passed in transport type is a secure transport protocol.
H_PAssertedIdentitys h_PAssertedIdentities
Definition Headers.cxx:215
InserterPClass< T > InserterP(const T &t)
Templatized function to construct an instance of InserterClass for a container to be inserted.
Definition Inserter.hxx:558
TransportType
An enumeration of transport protocols.
RequestLineType h_RequestLine
Definition Headers.cxx:347
#define EncodeStream