|
reSIProcate/repro
9694
|
00001 #if defined(HAVE_CONFIG_H) 00002 #include "config.h" 00003 #endif 00004 00005 #include <iostream> 00006 00007 #include "resip/stack/ExtensionParameter.hxx" 00008 #include "resip/stack/InteropHelper.hxx" 00009 #include "resip/stack/SipMessage.hxx" 00010 #include "resip/stack/SipStack.hxx" 00011 #include "rutil/DnsUtil.hxx" 00012 #include "rutil/Inserter.hxx" 00013 #include "resip/stack/Helper.hxx" 00014 #include "rutil/Logger.hxx" 00015 #include "repro/Proxy.hxx" 00016 #include "repro/ResponseContext.hxx" 00017 #include "repro/RequestContext.hxx" 00018 #include "repro/RRDecorator.hxx" 00019 #include "repro/Ack200DoneMessage.hxx" 00020 #include "rutil/WinLeakCheck.hxx" 00021 00022 #define RESIPROCATE_SUBSYSTEM resip::Subsystem::REPRO 00023 00024 using namespace resip; 00025 using namespace repro; 00026 using namespace std; 00027 00028 ResponseContext::ResponseContext(RequestContext& context) : 00029 mRequestContext(context), 00030 mBestPriority(50), 00031 mSecure(false), //context.getOriginalRequest().header(h_RequestLine).uri().scheme() == Symbols::Sips) 00032 mIsClientBehindNAT(false) 00033 { 00034 } 00035 00036 00037 ResponseContext::~ResponseContext() 00038 { 00039 TransactionMap::iterator i; 00040 00041 for(i=mTerminatedTransactionMap.begin(); i!=mTerminatedTransactionMap.end();++i) 00042 { 00043 delete i->second; 00044 } 00045 mTerminatedTransactionMap.clear(); 00046 00047 for(i=mActiveTransactionMap.begin(); i!=mActiveTransactionMap.end();++i) 00048 { 00049 delete i->second; 00050 } 00051 mActiveTransactionMap.clear(); 00052 00053 for(i=mCandidateTransactionMap.begin(); i!=mCandidateTransactionMap.end();++i) 00054 { 00055 delete i->second; 00056 } 00057 mCandidateTransactionMap.clear(); 00058 } 00059 00060 resip::Data 00061 ResponseContext::addTarget(const NameAddr& addr, bool beginImmediately) 00062 { 00063 InfoLog (<< "Adding candidate " << addr); 00064 std::auto_ptr<Target> target(new Target(addr)); 00065 Data tid=target->tid(); 00066 addTarget(target, beginImmediately); 00067 return tid; 00068 } 00069 00070 bool 00071 ResponseContext::addTarget(std::auto_ptr<repro::Target> target, bool beginImmediately) 00072 { 00073 if(mRequestContext.mHaveSentFinalResponse || !target.get()) 00074 { 00075 return false; 00076 } 00077 00078 //Disallow sip: if secure 00079 if(mSecure && target->uri().scheme() != Symbols::Sips) 00080 { 00081 return false; 00082 } 00083 00084 //Make sure we don't have Targets with an invalid initial state. 00085 if(target->status() != Target::Candidate) 00086 { 00087 return false; 00088 } 00089 00090 if(beginImmediately) 00091 { 00092 if(isDuplicate(target.get())) 00093 { 00094 return false; 00095 } 00096 00097 mTargetList.push_back(target->rec()); 00098 00099 beginClientTransaction(target.get()); 00100 target->status()=Target::Started; 00101 Target* toAdd=target.release(); 00102 mActiveTransactionMap[toAdd->tid()]=toAdd; 00103 } 00104 else 00105 { 00106 if(target->mShouldAutoProcess) // note: for base repro - this is always true 00107 { 00108 std::list<resip::Data> queue; 00109 queue.push_back(target->tid()); 00110 mTransactionQueueCollection.push_back(queue); 00111 } 00112 00113 Target* toAdd=target.release(); 00114 mCandidateTransactionMap[toAdd->tid()]=toAdd; 00115 } 00116 00117 return true; 00118 } 00119 00120 bool 00121 ResponseContext::addTargetBatch(TargetPtrList& targets, 00122 bool highPriority) 00123 { 00124 std::list<resip::Data> queue; 00125 Target* target=0; 00126 TargetPtrList::iterator it; 00127 00128 if(mRequestContext.mHaveSentFinalResponse || targets.empty()) 00129 { 00130 for(it=targets.begin();it!=targets.end();it++) 00131 { 00132 delete *it; 00133 } 00134 00135 targets.clear(); 00136 return false; 00137 } 00138 00139 for(it=targets.begin();it!=targets.end();it++) 00140 { 00141 target=*it; 00142 00143 if((!mSecure || target->uri().scheme() == Symbols::Sips) && 00144 target->status() == Target::Candidate) 00145 { 00146 if(target->mShouldAutoProcess) 00147 { 00148 queue.push_back(target->tid()); 00149 } 00150 DebugLog(<<"Adding Target to Candidates: " << target->uri() << " tid=" << target->tid()); 00151 mCandidateTransactionMap[target->tid()]=target; 00152 } 00153 else 00154 { 00155 DebugLog(<<"Bad Target: " << target->uri()); 00156 delete target; 00157 } 00158 } 00159 00160 targets.clear(); 00161 00162 if(highPriority) // note: for base repro - this is always false 00163 { 00164 mTransactionQueueCollection.push_front(queue); 00165 } 00166 else 00167 { 00168 mTransactionQueueCollection.push_back(queue); 00169 } 00170 00171 return true; 00172 } 00173 00174 bool 00175 ResponseContext::beginClientTransactions() 00176 { 00177 bool result=false; 00178 00179 if(mCandidateTransactionMap.empty()) 00180 { 00181 return result; 00182 } 00183 00184 for (TransactionMap::iterator i=mCandidateTransactionMap.begin(); i != mCandidateTransactionMap.end(); ) 00185 { 00186 if(!isDuplicate(i->second) && !mRequestContext.mHaveSentFinalResponse) 00187 { 00188 mTargetList.push_back(i->second->rec()); // Add to Target list for future duplicate detection 00189 beginClientTransaction(i->second); 00190 result=true; 00191 // see rfc 3261 section 16.6 00192 //This code moves the Target from mCandidateTransactionMap to mActiveTransactionMap, 00193 //and begins the transaction. 00194 mActiveTransactionMap[i->second->tid()] = i->second; 00195 InfoLog (<< "Creating new client transaction " << i->second->tid() << " -> " << i->second->uri()); 00196 } 00197 else 00198 { 00199 i->second->status() = Target::Terminated; 00200 mTerminatedTransactionMap[i->second->tid()] = i->second; 00201 DebugLog(<<"Found a repeated target."); 00202 } 00203 00204 TransactionMap::iterator temp=i; 00205 i++; 00206 mCandidateTransactionMap.erase(temp); 00207 } 00208 00209 return result; 00210 } 00211 00212 bool 00213 ResponseContext::beginClientTransaction(const resip::Data& tid) 00214 { 00215 TransactionMap::iterator i = mCandidateTransactionMap.find(tid); 00216 if(i==mCandidateTransactionMap.end()) 00217 { 00218 return false; 00219 } 00220 00221 if(isDuplicate(i->second) || mRequestContext.mHaveSentFinalResponse) 00222 { 00223 i->second->status() = Target::Terminated; 00224 mTerminatedTransactionMap[i->second->tid()] = i->second; 00225 mCandidateTransactionMap.erase(i); 00226 return false; 00227 } 00228 00229 mTargetList.push_back(i->second->rec()); // Add to Target list for future duplicate detection 00230 00231 beginClientTransaction(i->second); 00232 mActiveTransactionMap[i->second->tid()] = i->second; 00233 InfoLog(<< "Creating new client transaction " << i->second->tid() << " -> " << i->second->uri()); 00234 mCandidateTransactionMap.erase(i); 00235 00236 return true; 00237 } 00238 00239 bool 00240 ResponseContext::cancelActiveClientTransactions() 00241 { 00242 if(mRequestContext.mHaveSentFinalResponse) 00243 { 00244 return false; 00245 } 00246 00247 InfoLog (<< "Cancel all proceeding client transactions: " << (mCandidateTransactionMap.size() + 00248 mActiveTransactionMap.size())); 00249 00250 if(mActiveTransactionMap.empty()) 00251 { 00252 return false; 00253 } 00254 00255 // CANCEL INVITE branches 00256 for (TransactionMap::iterator i = mActiveTransactionMap.begin(); 00257 i != mActiveTransactionMap.end(); ++i) 00258 { 00259 cancelClientTransaction(i->second); 00260 } 00261 00262 return true; 00263 00264 } 00265 00266 bool 00267 ResponseContext::cancelAllClientTransactions() 00268 { 00269 00270 InfoLog (<< "Cancel ALL client transactions: " << mCandidateTransactionMap.size() 00271 << " pending, " << mActiveTransactionMap.size() << " active."); 00272 00273 if(mActiveTransactionMap.empty() && mCandidateTransactionMap.empty()) 00274 { 00275 return false; 00276 } 00277 00278 // CANCEL INVITE branches 00279 if(mRequestContext.getOriginalRequest().method()==INVITE) 00280 { 00281 for (TransactionMap::iterator i = mActiveTransactionMap.begin(); 00282 i != mActiveTransactionMap.end(); ++i) 00283 { 00284 cancelClientTransaction(i->second); 00285 } 00286 } 00287 00288 clearCandidateTransactions(); 00289 00290 return true; 00291 00292 } 00293 00294 bool 00295 ResponseContext::clearCandidateTransactions() 00296 { 00297 bool result=false; 00298 for (TransactionMap::iterator j = mCandidateTransactionMap.begin(); 00299 j != mCandidateTransactionMap.end();) 00300 { 00301 result=true; 00302 cancelClientTransaction(j->second); 00303 mTerminatedTransactionMap[j->second->tid()] = j->second; 00304 TransactionMap::iterator temp = j; 00305 j++; 00306 mCandidateTransactionMap.erase(temp); 00307 } 00308 00309 return result; 00310 } 00311 00312 bool 00313 ResponseContext::cancelClientTransaction(const resip::Data& tid) 00314 { 00315 00316 TransactionMap::iterator i = mActiveTransactionMap.find(tid); 00317 if(mRequestContext.getOriginalRequest().method()==INVITE) 00318 { 00319 if(i!=mActiveTransactionMap.end()) 00320 { 00321 cancelClientTransaction(i->second); 00322 return true; 00323 } 00324 } 00325 00326 TransactionMap::iterator j = mCandidateTransactionMap.find(tid); 00327 if(j != mCandidateTransactionMap.end()) 00328 { 00329 cancelClientTransaction(j->second); 00330 mTerminatedTransactionMap[tid] = j->second; 00331 mCandidateTransactionMap.erase(j); 00332 return true; 00333 } 00334 00335 return false; 00336 } 00337 00338 Target* 00339 ResponseContext::getTarget(const resip::Data& tid) const 00340 { 00341 // .bwc. This tid is most likely to be found in either the Candidate targets, 00342 // or the Active targets. 00343 TransactionMap::const_iterator pend = mCandidateTransactionMap.find(tid); 00344 if(pend != mCandidateTransactionMap.end()) 00345 { 00346 assert(pend->second->status()==Target::Candidate); 00347 return pend->second; 00348 } 00349 00350 TransactionMap::const_iterator act = mActiveTransactionMap.find(tid); 00351 if(act != mActiveTransactionMap.end()) 00352 { 00353 assert(!(act->second->status()==Target::Candidate || act->second->status()==Target::Terminated)); 00354 return act->second; 00355 } 00356 00357 TransactionMap::const_iterator term = mTerminatedTransactionMap.find(tid); 00358 if(term != mTerminatedTransactionMap.end()) 00359 { 00360 assert(term->second->status()==Target::Terminated); 00361 return term->second; 00362 } 00363 00364 return 0; 00365 } 00366 00367 const ResponseContext::TransactionMap& 00368 ResponseContext::getCandidateTransactionMap() const 00369 { 00370 return mCandidateTransactionMap; 00371 } 00372 00373 bool 00374 ResponseContext::hasCandidateTransactions() const 00375 { 00376 return !mRequestContext.mHaveSentFinalResponse && !mCandidateTransactionMap.empty(); 00377 } 00378 00379 bool 00380 ResponseContext::hasActiveTransactions() const 00381 { 00382 return !mActiveTransactionMap.empty(); 00383 } 00384 00385 bool 00386 ResponseContext::hasTerminatedTransactions() const 00387 { 00388 return !mTerminatedTransactionMap.empty(); 00389 } 00390 00391 bool 00392 ResponseContext::hasTargets() const 00393 { 00394 return (hasCandidateTransactions() || 00395 hasActiveTransactions() || 00396 hasTerminatedTransactions()); 00397 } 00398 00399 bool 00400 ResponseContext::areAllTransactionsTerminated() const 00401 { 00402 return (mCandidateTransactionMap.empty() && mActiveTransactionMap.empty()); 00403 } 00404 00405 bool 00406 ResponseContext::isCandidate(const resip::Data& tid) const 00407 { 00408 TransactionMap::const_iterator i=mCandidateTransactionMap.find(tid); 00409 return i!=mCandidateTransactionMap.end(); 00410 } 00411 00412 bool 00413 ResponseContext::isActive(const resip::Data& tid) const 00414 { 00415 TransactionMap::const_iterator i=mActiveTransactionMap.find(tid); 00416 return i!=mActiveTransactionMap.end(); 00417 } 00418 00419 bool 00420 ResponseContext::isTerminated(const resip::Data& tid) const 00421 { 00422 TransactionMap::const_iterator i=mTerminatedTransactionMap.find(tid); 00423 return i!=mTerminatedTransactionMap.end(); 00424 } 00425 00426 void 00427 ResponseContext::removeClientTransaction(const resip::Data& transactionId) 00428 { 00429 // .bwc. This tid will most likely be found in the map of terminated 00430 // transactions, under normal circumstances. 00431 // NOTE: This does not remove the corresponding entry in mTargetList. 00432 // This is the intended behavior, because the same target should not 00433 // be added again later. 00434 00435 TransactionMap::iterator i = mTerminatedTransactionMap.find(transactionId); 00436 if(i!=mTerminatedTransactionMap.end()) 00437 { 00438 delete i->second; 00439 mTerminatedTransactionMap.erase(i); 00440 return; 00441 } 00442 00443 i=mCandidateTransactionMap.find(transactionId); 00444 if(i!=mCandidateTransactionMap.end()) 00445 { 00446 delete i->second; 00447 mCandidateTransactionMap.erase(i); 00448 return; 00449 } 00450 00451 i=mActiveTransactionMap.find(transactionId); 00452 if(i!=mActiveTransactionMap.end()) 00453 { 00454 delete i->second; 00455 mActiveTransactionMap.erase(i); 00456 WarningLog(<< "Something removed an active transaction, " << transactionId 00457 << ". It is very likely that something is broken here. "); 00458 return; 00459 } 00460 00461 } 00462 00463 bool 00464 ResponseContext::isDuplicate(const repro::Target* target) const 00465 { 00466 resip::ContactList::const_iterator i; 00467 // make sure each target is only inserted once 00468 00469 // !bwc! We can not optimize this by using stl, because operator 00470 // == does not conform to the partial-ordering established by operator 00471 // < (We can very easily have a < b and a==b simultaneously). 00472 // [TODO] Once we have a canonicalized form, we can improve this. 00473 00474 for(i=mTargetList.begin();i!=mTargetList.end();i++) 00475 { 00476 if(*i==target->rec()) 00477 { 00478 return true; 00479 } 00480 } 00481 00482 return false; 00483 } 00484 00485 void 00486 ResponseContext::beginClientTransaction(repro::Target* target) 00487 { 00488 // .bwc. This is a private function, and if anything calls this with a 00489 // target in an invalid state, it is a bug. 00490 assert(target->status() == Target::Candidate); 00491 00492 SipMessage& orig=mRequestContext.getOriginalRequest(); 00493 SipMessage request(orig); 00494 00495 // If the target has a ;lr parameter, then perform loose routing 00496 if(target->uri().exists(p_lr)) 00497 { 00498 request.header(h_Routes).push_front(NameAddr(target->uri())); 00499 } 00500 else 00501 { 00502 request.header(h_RequestLine).uri() = target->uri(); 00503 } 00504 00505 // .bwc. Proxy checks whether this is valid, and rejects if not. 00506 request.header(h_MaxForwards).value()--; 00507 00508 bool inDialog=false; 00509 00510 try 00511 { 00512 inDialog=request.header(h_To).exists(p_tag); 00513 } 00514 catch(resip::ParseException&) 00515 { 00516 // ?bwc? Do we ignore this and just say this is a dialog-creating 00517 // request? 00518 } 00519 00520 // Potential source Record-Route addition only for new dialogs 00521 // !bwc! It looks like we really ought to be record-routing in-dialog 00522 // stuff. 00523 00524 // only add record route if configured to do so 00525 if(!mRequestContext.mProxy.getRecordRoute(orig.getReceivedTransport()).uri().host().empty()) 00526 { 00527 if (!inDialog && // only for dialog-creating request 00528 (request.method() == INVITE || 00529 request.method() == SUBSCRIBE || 00530 request.method() == REFER)) 00531 { 00532 insertRecordRoute(request, 00533 orig.getReceivedTransport(), 00534 target); 00535 } 00536 else if(request.method()==REGISTER) 00537 { 00538 insertRecordRoute(request, 00539 orig.getReceivedTransport(), 00540 target, 00541 true /* do Path instead */); 00542 } 00543 } 00544 00545 if((resip::InteropHelper::getOutboundSupported() || 00546 resip::InteropHelper::getRRTokenHackEnabled() || 00547 mIsClientBehindNAT) && 00548 target->rec().mUseFlowRouting && 00549 target->rec().mReceivedFrom.mFlowKey) 00550 { 00551 // .bwc. We only override the destination if we are sending to an 00552 // outbound contact. If this is not an outbound contact, but the 00553 // endpoint has given us a Contact with the correct ip-address and 00554 // port, we might be able to find the connection they formed when they 00555 // registered earlier, but that will happen down in TransportSelector. 00556 request.setDestination(target->rec().mReceivedFrom); 00557 } 00558 00559 DebugLog(<<"Set tuple dest: " << request.getDestination()); 00560 00561 // .bwc. Path header addition. 00562 if(!target->rec().mSipPath.empty()) 00563 { 00564 request.header(h_Routes).append(target->rec().mSipPath); 00565 } 00566 00567 // a baboon might adorn the message, record call logs or CDRs, might 00568 // insert loose routes on the way to the next hop 00569 Helper::processStrictRoute(request); 00570 00571 //This is where the request acquires the tid of the Target. The tids 00572 //should be the same from here on out. 00573 request.header(h_Vias).push_front(target->via()); 00574 00575 if(!mRequestContext.mInitialTimerCSet && 00576 mRequestContext.getOriginalRequest().method()==INVITE) 00577 { 00578 mRequestContext.mInitialTimerCSet=true; 00579 mRequestContext.updateTimerC(); 00580 } 00581 00582 // the rest of 16.6 is implemented by the transaction layer of resip 00583 // - determining the next hop (tuple) 00584 // - adding a content-length if needed 00585 // - sending the request 00586 sendRequest(request); 00587 00588 target->status() = Target::Started; 00589 } 00590 00591 void 00592 ResponseContext::insertRecordRoute(SipMessage& outgoing, 00593 const Transport* receivedTransport, 00594 Target* target, 00595 bool doPathInstead) 00596 { 00597 resip::Data inboundFlowToken=getInboundFlowToken(doPathInstead); 00598 bool needsOutboundFlowToken=outboundFlowTokenNeeded(target); 00599 bool recordRouted=false; 00600 // .bwc. If we have a flow-token we need to insert, we need to record-route. 00601 // Also, we might record-route if we are configured to do so. 00602 if( !inboundFlowToken.empty() 00603 || needsOutboundFlowToken 00604 || mRequestContext.mProxy.getRecordRouteForced() ) 00605 { 00606 resip::NameAddr rt; 00607 if(inboundFlowToken.empty()) 00608 { 00609 rt=mRequestContext.mProxy.getRecordRoute(receivedTransport); 00610 } 00611 else 00612 { 00613 if(receivedTransport->getTuple().getType()==TLS || 00614 receivedTransport->getTuple().getType()==DTLS) 00615 { 00616 // .bwc. Debatable. Should we be willing to reuse a TLS connection 00617 // at the behest of a Route header with no hostname in it? 00618 rt=mRequestContext.mProxy.getRecordRoute(receivedTransport); 00619 rt.uri().scheme() = "sips"; 00620 } 00621 else 00622 { 00623 if(receivedTransport->getTuple().isAnyInterface()) 00624 { 00625 rt=mRequestContext.mProxy.getRecordRoute(receivedTransport); 00626 } 00627 else 00628 { 00629 rt.uri().host()=resip::Tuple::inet_ntop(receivedTransport->getTuple()); 00630 } 00631 rt.uri().port()=receivedTransport->getTuple().getPort(); 00632 rt.uri().param(resip::p_transport)=resip::Tuple::toDataLower(receivedTransport->getTuple().getType()); 00633 } 00634 rt.uri().user()=inboundFlowToken; 00635 } 00636 Helper::massageRoute(outgoing,rt); 00637 00638 #ifdef USE_SIGCOMP 00639 if(mRequestContext.getProxy().compressionEnabled() && 00640 target->uri().exists(p_comp) && 00641 target->uri().param(p_comp)=="sigcomp") 00642 { 00643 rt.uri().param(p_comp)="sigcomp"; 00644 } 00645 #endif 00646 00647 recordRouted=true; 00648 if(doPathInstead) 00649 { 00650 if(!inboundFlowToken.empty()) 00651 { 00652 // Only add ;ob parameter if client really supports outbound (ie. not for NAT detection mode or flow token hack) 00653 if(!mRequestContext.getOriginalRequest().empty(h_Supporteds) && 00654 mRequestContext.getOriginalRequest().header(h_Supporteds).find(Token(Symbols::Outbound))) 00655 { 00656 rt.uri().param(p_ob); 00657 } 00658 } 00659 outgoing.header(h_Paths).push_front(rt); 00660 if(!outgoing.header(h_Supporteds).find(Token("path"))) 00661 { 00662 outgoing.header(h_Supporteds).push_back(Token("path")); 00663 } 00664 InfoLog (<< "Added Path: " << rt); 00665 } 00666 else 00667 { 00668 outgoing.header(h_RecordRoutes).push_front(rt); 00669 InfoLog (<< "Added Record-Route: " << rt); 00670 } 00671 } 00672 00673 // .bwc. We always need to add this, since we never know if a transport 00674 // switch is going to happen. (Except for Path headers; we don't care about 00675 // transport switches on REGISTER requests. If we already put a Path header 00676 // in, we do care though.) 00677 if(!doPathInstead || recordRouted) 00678 { 00679 std::auto_ptr<resip::MessageDecorator> rrDecorator( 00680 new RRDecorator(mRequestContext.mProxy, 00681 receivedTransport, 00682 recordRouted, 00683 !inboundFlowToken.empty(), 00684 mRequestContext.mProxy.getRecordRouteForced(), 00685 doPathInstead, 00686 mIsClientBehindNAT)); 00687 outgoing.addOutboundDecorator(rrDecorator); 00688 } 00689 } 00690 00691 resip::Data 00692 ResponseContext::getInboundFlowToken(bool doPathInstead) 00693 { 00694 resip::Data flowToken=resip::Data::Empty; 00695 resip::SipMessage& orig=mRequestContext.getOriginalRequest(); 00696 if(orig.empty(h_Contacts) || !orig.header(h_Contacts).front().isWellFormed()) 00697 { 00698 return flowToken; 00699 } 00700 00701 const resip::NameAddr& contact(orig.header(h_Contacts).front()); 00702 00703 if(InteropHelper::getOutboundSupported() && 00704 (contact.uri().exists(p_ob) || contact.exists(p_regid))) 00705 { 00706 if(orig.header(h_Vias).size()==1) 00707 { 00708 // This arrived over an outbound flow (or so the endpoint claims) 00709 // (See outbound-09 Sec 4.3 para 3) 00710 resip::Data binaryFlowToken; 00711 resip::Tuple source(orig.getSource()); 00712 source.onlyUseExistingConnection=true; 00713 Tuple::writeBinaryToken(source, binaryFlowToken, Proxy::FlowTokenSalt); 00714 flowToken = binaryFlowToken.base64encode(); 00715 } 00716 else if(doPathInstead) 00717 { 00718 // Need to try to detect Path failures 00719 if(orig.empty(h_Paths) || !orig.header(h_Paths).back().uri().exists(p_ob)) 00720 { 00721 // Yikes! Client is trying to use outbound, but edge-proxy did not 00722 // support it. The registrar will either reject this (if it supports 00723 // outbound), or will not indicate outbound support (which should 00724 // let the client know that the flow setup failed) 00725 WarningLog(<<"Client asked for outbound processing, but the edge " 00726 "proxy did not support it. There's nothing we can do to " 00727 "salvage this. The registrar might end up rejecting the " 00728 "registration (if is supports outbound), or it might just " 00729 "fail to add a Supported: outbound. In either case, the " 00730 "client should know what's up, so we just let it all " 00731 "happen."); 00732 } 00733 } 00734 } 00735 00736 if(flowToken.empty() && orig.header(h_Vias).size()==1) 00737 { 00738 if(resip::InteropHelper::getRRTokenHackEnabled() || 00739 mIsClientBehindNAT || 00740 needsFlowTokenToWork(contact)) 00741 { 00742 // !bwc! TODO remove this when flow-token hack is no longer needed. 00743 // Poor-man's outbound. Shouldn't be our default behavior, because it 00744 // breaks target-refreshes (once a flow-token is in the Route-Set, the 00745 // flow-token cannot be changed, and will override any update to the 00746 // Contact) 00747 resip::Data binaryFlowToken; 00748 Tuple::writeBinaryToken(orig.getSource(), binaryFlowToken, Proxy::FlowTokenSalt); 00749 flowToken = binaryFlowToken.base64encode(); 00750 } 00751 } 00752 00753 return flowToken; 00754 } 00755 00756 bool 00757 ResponseContext::outboundFlowTokenNeeded(Target* target) 00758 { 00759 if(mRequestContext.mProxy.isMyUri(target->uri())) 00760 { 00761 // .bwc. We don't need to put flow-tokens pointed at ourselves. 00762 return false; 00763 } 00764 00765 if((target->rec().mReceivedFrom.mFlowKey && 00766 target->rec().mUseFlowRouting) 00767 || resip::InteropHelper::getRRTokenHackEnabled() 00768 || mIsClientBehindNAT) 00769 { 00770 target->rec().mReceivedFrom.onlyUseExistingConnection=true; 00771 return true; 00772 } 00773 00774 return false; 00775 } 00776 00777 bool 00778 ResponseContext::needsFlowTokenToWork(const resip::NameAddr& contact) const 00779 { 00780 if(DnsUtil::isIpAddress(contact.uri().host())) 00781 { 00782 // IP address in host-part. 00783 if(contact.uri().scheme()=="sips") 00784 { 00785 // TLS with no FQDN. Impossible without flow-token fixup, even if no 00786 // NAT is involved. 00787 return true; 00788 } 00789 00790 if(contact.uri().exists(p_transport)) 00791 { 00792 TransportType type = toTransportType(contact.uri().param(p_transport)); 00793 if(type==TLS || type == DTLS) 00794 { 00795 // TLS with no FQDN. Impossible without flow-token fixup, even if no 00796 // NAT is involved. 00797 return true; 00798 } 00799 } 00800 } 00801 00802 if(contact.uri().exists(p_sigcompId)) 00803 { 00804 if(contact.uri().exists(p_transport)) 00805 { 00806 TransportType type = toTransportType(contact.uri().param(p_transport)); 00807 if(type == TLS || type == TCP) 00808 { 00809 // Client is using sigcomp on the first hop using a connection- 00810 // oriented transport. For this to work, that connection has to be 00811 // reused for all traffic. 00812 return true; 00813 } 00814 } 00815 } 00816 return false; 00817 } 00818 00819 bool 00820 ResponseContext::sendingToSelf(Target* target) 00821 { 00822 if(mRequestContext.mProxy.isMyUri(target->uri())) 00823 { 00824 return true; 00825 } 00826 return false; 00827 } 00828 00829 void 00830 ResponseContext::sendRequest(resip::SipMessage& request) 00831 { 00832 assert (request.isRequest()); 00833 00834 if (request.method() != CANCEL && 00835 request.method() != ACK) 00836 { 00837 mRequestContext.getProxy().addClientTransaction(request.getTransactionId(), &mRequestContext); 00838 mRequestContext.mTransactionCount++; 00839 // if(!mRequestContext.getDigestIdentity().empty()) 00840 // { 00841 // requestPtr->header(h_Identity); 00842 // // !bwc! Need to fill in the Identity-Info header telling where our 00843 // // cert can be found. 00844 // } 00845 } 00846 00847 // TODO - P-Asserted-Identity Processing 00848 // RFC3325 - section 5 00849 // When a proxy forwards a message to another node, it must first 00850 // determine if it trusts that node or not. If it trusts the node, the 00851 // proxy does not remove any P-Asserted-Identity header fields that it 00852 // generated itself, or that it received from a trusted source. If it 00853 // does not trust the element, then the proxy MUST examine the Privacy 00854 // header field (if present) to determine if the user requested that 00855 // asserted identity information be kept private. 00856 00857 // Note: Since we have no better mechanism to determine if destination is trusted or 00858 // not we will assume that all destinations outside our domain are not-trusted 00859 // and will remove the P-Asserted-Identity header, if Privacy is set to "id" 00860 if(mRequestContext.getProxy().isPAssertedIdentityProcessingEnabled() && 00861 request.exists(h_Privacies) && 00862 request.header(h_Privacies).size() > 0 && 00863 request.exists(h_PAssertedIdentities) && 00864 !mRequestContext.getProxy().isMyUri(request.header(h_RequestLine).uri())) 00865 { 00866 // Look for "id" token 00867 bool found = false; 00868 PrivacyCategories::iterator it = request.header(h_Privacies).begin(); 00869 for(; it != request.header(h_Privacies).end() && !found; it++) 00870 { 00871 std::vector<Data>::iterator itToken = it->value().begin(); 00872 for(; itToken != it->value().end() && !found; itToken++) 00873 { 00874 if(*itToken == "id") 00875 { 00876 request.remove(h_PAssertedIdentities); 00877 found = true; 00878 } 00879 } 00880 } 00881 } 00882 00883 if (request.method() == ACK) 00884 { 00885 DebugLog(<<"Posting Ack200DoneMessage"); 00886 mRequestContext.getProxy().post(new Ack200DoneMessage(mRequestContext.getTransactionId())); 00887 } 00888 00889 mRequestContext.send(request); 00890 } 00891 00892 00893 void 00894 ResponseContext::processCancel(const SipMessage& request) 00895 { 00896 assert(request.isRequest()); 00897 assert(request.method() == CANCEL); 00898 00899 std::auto_ptr<SipMessage> ok(Helper::makeResponse(request, 200)); 00900 mRequestContext.sendResponse(*ok); 00901 00902 if (!mRequestContext.mHaveSentFinalResponse) 00903 { 00904 cancelAllClientTransactions(); 00905 if(!hasActiveTransactions()) 00906 { 00907 SipMessage reqterm; 00908 Helper::makeResponse(reqterm,mRequestContext.getOriginalRequest(),487); 00909 mRequestContext.sendResponse(reqterm); 00910 } 00911 } 00912 } 00913 00914 void 00915 ResponseContext::processTimerC() 00916 { 00917 if (!mRequestContext.mHaveSentFinalResponse) 00918 { 00919 InfoLog(<<"Canceling client transactions due to timer C."); 00920 cancelAllClientTransactions(); 00921 } 00922 } 00923 00924 void 00925 ResponseContext::processResponse(SipMessage& response) 00926 { 00927 InfoLog (<< "processResponse: " << endl << response); 00928 00929 // store this before we pop the via and lose the branch tag 00930 mCurrentResponseTid = response.getTransactionId(); 00931 00932 assert (response.isResponse()); 00933 assert (response.exists(h_Vias) && !response.header(h_Vias).empty()); 00934 response.header(h_Vias).pop_front(); 00935 00936 // Stop processing responses that have nowhere else to go 00937 if (response.header(h_Vias).empty()) 00938 { 00939 // CANCEL/200s only have one Via. Likewise 100s only have one Via 00940 // Silently stop processing the CANCEL responses. 00941 // We will handle the 100 responses later 00942 // Log other responses we can't forward 00943 00944 if(response.method()==CANCEL) 00945 { 00946 return; 00947 } 00948 else if (response.header(h_StatusLine).statusCode() > 199) 00949 { 00950 InfoLog( << "Received final response, but can't forward as there are " 00951 "no more Vias. Considering this branch failed. " 00952 << response.brief() ); 00953 // .bwc. Treat as server error. 00954 terminateClientTransaction(mCurrentResponseTid); 00955 return; 00956 } 00957 else if(response.header(h_StatusLine).statusCode() != 100) 00958 { 00959 InfoLog( << "Received provisional response, but can't forward as there" 00960 " are no more Vias. Ignoring. " << response.brief() ); 00961 return; 00962 } 00963 } 00964 else // We have a second Via 00965 { 00966 if(!mRequestContext.getOriginalRequest().getRFC2543TransactionId().empty()) 00967 { 00968 // .bwc. Original request had an RFC 2543 transaction-id. Set in 00969 // response. 00970 response.setRFC2543TransactionId(mRequestContext.getOriginalRequest().getRFC2543TransactionId()); 00971 } 00972 00973 const Via& via = response.header(h_Vias).front(); 00974 00975 if(!via.isWellFormed()) 00976 { 00977 // .bwc. Garbage via. Unrecoverable. Ignore if provisional, terminate 00978 // transaction if not. 00979 DebugLog(<<"Some endpoint has corrupted one of our Vias" 00980 " in their response. (Via is malformed) This is not fixable."); 00981 if(response.header(h_StatusLine).statusCode() > 199) 00982 { 00983 terminateClientTransaction(mCurrentResponseTid); 00984 } 00985 00986 return; 00987 } 00988 00989 const Via& origVia = mRequestContext.getOriginalRequest().header(h_Vias).front(); 00990 const Data& branch=(via.exists(p_branch) ? via.param(p_branch).getTransactionId() : Data::Empty); 00991 const Data& origBranch=(origVia.exists(p_branch) ? origVia.param(p_branch).getTransactionId() : Data::Empty); 00992 00993 if(!isEqualNoCase(branch,origBranch)) 00994 { 00995 // .bwc. Someone altered our branch. Ignore if provisional, terminate 00996 // transaction otherwise. 00997 DebugLog(<<"Some endpoint has altered one of our Vias" 00998 " in their response. (branch is different) This is not fixable."); 00999 if(response.header(h_StatusLine).statusCode() > 199) 01000 { 01001 terminateClientTransaction(mCurrentResponseTid); 01002 } 01003 01004 return; 01005 } 01006 } 01007 01008 DebugLog (<< "Search for " << mCurrentResponseTid << " in " << InserterP(mActiveTransactionMap)); 01009 01010 TransactionMap::iterator i = mActiveTransactionMap.find(mCurrentResponseTid); 01011 01012 int code = response.header(h_StatusLine).statusCode(); 01013 if (i == mActiveTransactionMap.end()) 01014 { 01015 // This is a response for a transaction that is no longer/was never active. 01016 // This is probably a useless response (at best) or a malicious response (at worst). 01017 // Log the response here: 01018 if ((code / 100) != 2) 01019 { 01020 InfoLog( << "Discarding stray response" ); 01021 } 01022 // Even though this is a tremendously bad idea, some developers may 01023 // decide they want to statelessly forward the response 01024 // Here is the gun. Don't say we didn't warn you! 01025 else 01026 { 01027 // !abr! Because we don't run timers on the transaction after 01028 // it has terminated and because the ACKs on INVITE 01029 // 200-class responses are end-to-end, we don't discard 01030 // 200 responses. To do this properly, we should run a 01031 // transaction timer for 64*T1 and remove transactions from 01032 // the ActiveTransactionMap *only* after that timer expires. 01033 // IN OTHER WORDS, REMOVE THIS CODE. 01034 mRequestContext.sendResponse(response); 01035 } 01036 return; 01037 } 01038 01039 switch (code / 100) 01040 { 01041 case 1: 01042 if(mRequestContext.getOriginalRequest().method()==INVITE) 01043 { 01044 mRequestContext.updateTimerC(); 01045 } 01046 01047 if (!mRequestContext.mHaveSentFinalResponse) 01048 { 01049 if (code == 100) 01050 { 01051 return; // stop processing 100 responses 01052 } 01053 01054 mRequestContext.sendResponse(response); 01055 return; 01056 } 01057 break; 01058 01059 case 2: 01060 terminateClientTransaction(mCurrentResponseTid); 01061 if (mRequestContext.getOriginalRequest().method() == INVITE) 01062 { 01063 cancelAllClientTransactions(); 01064 mRequestContext.mHaveSentFinalResponse = true; 01065 mBestResponse.header(h_StatusLine).statusCode()= 01066 response.header(h_StatusLine).statusCode(); 01067 mRequestContext.sendResponse(response); 01068 } 01069 else if (!mRequestContext.mHaveSentFinalResponse) 01070 { 01071 clearCandidateTransactions(); 01072 mRequestContext.mHaveSentFinalResponse = true; 01073 mBestResponse.header(h_StatusLine).statusCode()= 01074 response.header(h_StatusLine).statusCode(); 01075 01076 // If this is a registration response and we have flow timers enabled, and 01077 // we are doing outbound for this registration and there is no FlowTimer 01078 // header present already, then add a FlowTimer header 01079 if(response.method() == REGISTER && 01080 InteropHelper::getFlowTimerSeconds() > 0 && 01081 response.empty(h_FlowTimer) && 01082 ((!response.empty(h_Paths) && response.header(h_Paths).back().uri().exists(p_ob)) || 01083 (!response.empty(h_Requires) && response.header(h_Requires).find(Token(Symbols::Outbound))))) 01084 { 01085 response.header(h_FlowTimer).value() = InteropHelper::getFlowTimerSeconds(); 01086 mRequestContext.getProxy().getStack().enableFlowTimer(mRequestContext.getOriginalRequest().getSource()); 01087 } 01088 01089 mRequestContext.sendResponse(response); 01090 } 01091 break; 01092 01093 case 3: 01094 case 4: 01095 case 5: 01096 DebugLog (<< "forwardedFinal=" << mRequestContext.mHaveSentFinalResponse 01097 << " outstanding client transactions: " << InserterP(mActiveTransactionMap)); 01098 terminateClientTransaction(mCurrentResponseTid); 01099 if (!mRequestContext.mHaveSentFinalResponse) 01100 { 01101 int priority = getPriority(response); 01102 if (priority == mBestPriority) 01103 { 01104 if (code == 401 || code == 407) 01105 { 01106 if (response.exists(h_WWWAuthenticates)) 01107 { 01108 for ( Auths::iterator i=response.header(h_WWWAuthenticates).begin(); 01109 i != response.header(h_WWWAuthenticates).end() ; ++i) 01110 { 01111 mBestResponse.header(h_WWWAuthenticates).push_back(*i); 01112 } 01113 } 01114 01115 if (response.exists(h_ProxyAuthenticates)) 01116 { 01117 for ( Auths::iterator i=response.header(h_ProxyAuthenticates).begin(); 01118 i != response.header(h_ProxyAuthenticates).end() ; ++i) 01119 { 01120 mBestResponse.header(h_ProxyAuthenticates).push_back(*i); 01121 } 01122 mBestResponse.header(h_StatusLine).statusCode() = 407; 01123 } 01124 } 01125 else if (code / 100 == 3) // merge 3xx 01126 { 01127 01128 if(mBestResponse.header(h_StatusLine).statusCode()/100!=3) 01129 { 01130 // .bwc. Do not merge contacts in 3xx with contacts from a 01131 // previous 4xx or 5xx 01132 mBestResponse.header(h_Contacts).clear(); 01133 } 01134 01135 for (NameAddrs::iterator i=response.header(h_Contacts).begin(); 01136 i != response.header(h_Contacts).end(); ++i) 01137 { 01138 // TODO ?bwc? if we are going to be checking whether 01139 // this is "*", we should see if it is well-formed 01140 // first. If we shouldn't be doing any checks on 01141 // the contacts, we should simply remove all of this 01142 // checking code and just blindly copy the contacts over. 01143 01144 if(!i->isWellFormed() || i->isAllContacts()) 01145 { 01146 // .bwc. Garbage contact; ignore it. 01147 continue; 01148 } 01149 01150 mBestResponse.header(h_Contacts).push_back(*i); 01151 } 01152 01153 // ?bwc? it is possible for this code to end up with a 300 01154 // with no Contacts in it (because they were all malformed) 01155 // Is this acceptable? Also, is 300 the code we want here? 01156 mBestResponse.header(h_StatusLine).statusCode() = 300; 01157 } 01158 } 01159 else if (priority < mBestPriority) 01160 { 01161 mBestPriority = priority; 01162 mBestResponse = response; 01163 } 01164 01165 if (areAllTransactionsTerminated()) 01166 { 01167 forwardBestResponse(); 01168 } 01169 } 01170 break; 01171 01172 case 6: 01173 terminateClientTransaction(mCurrentResponseTid); 01174 if (!mRequestContext.mHaveSentFinalResponse) 01175 { 01176 if (mBestResponse.header(h_StatusLine).statusCode() / 100 != 6) 01177 { 01178 mBestResponse = response; 01179 mBestPriority=0; //6xx class responses take precedence over all 3xx,4xx, and 5xx 01180 if (mRequestContext.getOriginalRequest().method() == INVITE) 01181 { 01182 // CANCEL INVITE branches 01183 cancelAllClientTransactions(); 01184 } 01185 } 01186 01187 if (areAllTransactionsTerminated()) 01188 { 01189 forwardBestResponse(); 01190 } 01191 } 01192 break; 01193 01194 default: 01195 assert(0); 01196 break; 01197 } 01198 } 01199 01200 void 01201 ResponseContext::cancelClientTransaction(repro::Target* target) 01202 { 01203 if (target->status() == Target::Started) 01204 { 01205 InfoLog (<< "Cancel client transaction: " << target); 01206 mRequestContext.cancelClientTransaction(target->via().param(p_branch).getTransactionId()); 01207 01208 DebugLog(<< "Canceling a transaction with uri: " 01209 << resip::Data::from(target->uri()) << " , to host: " 01210 << target->via().sentHost()); 01211 target->status() = Target::Cancelled; 01212 } 01213 else if (target->status() == Target::Candidate) 01214 { 01215 target->status() = Target::Terminated; 01216 } 01217 } 01218 01219 void 01220 ResponseContext::terminateClientTransaction(const resip::Data& tid) 01221 { 01222 01223 InfoLog (<< "Terminating client transaction: " << tid << " all = " << areAllTransactionsTerminated()); 01224 01225 TransactionMap::iterator i = mActiveTransactionMap.find(tid); 01226 if(i != mActiveTransactionMap.end()) 01227 { 01228 InfoLog (<< "client transactions: " << InserterP(mActiveTransactionMap)); 01229 i->second->status() = Target::Terminated; 01230 mTerminatedTransactionMap[tid] = i->second; 01231 mActiveTransactionMap.erase(i); 01232 return; 01233 } 01234 01235 TransactionMap::iterator j = mCandidateTransactionMap.find(tid); 01236 if(j != mCandidateTransactionMap.end()) 01237 { 01238 InfoLog (<< "client transactions: " << InserterP(mCandidateTransactionMap)); 01239 j->second->status() = Target::Terminated; 01240 mTerminatedTransactionMap[tid] = j->second; 01241 mCandidateTransactionMap.erase(j); 01242 return; 01243 } 01244 01245 } 01246 01247 01248 int 01249 ResponseContext::getPriority(const resip::SipMessage& msg) 01250 { 01251 int responseCode = msg.header(h_StatusLine).statusCode(); 01252 int p = 0; // "p" is the relative priority of the response 01253 01254 assert(responseCode >= 300 && responseCode <= 599); 01255 if (responseCode <= 399) // 3xx response 01256 { 01257 return 5; // response priority is 5 01258 } 01259 if (responseCode >= 500) 01260 { 01261 switch(responseCode) 01262 { 01263 case 501: // these four have different priorities 01264 case 503: // which are addressed in the case statement 01265 case 580: // below (with the 4xx responses) 01266 case 513: 01267 break; 01268 default: 01269 return 42; // response priority of other 5xx is 42 01270 } 01271 } 01272 01273 switch(responseCode) 01274 { 01275 // Easy to Repair Responses: 412, 484, 422, 423, 407, 401, 300..399, 402 01276 case 412: // Publish ETag was stale 01277 return 1; 01278 case 484: // overlap dialing 01279 return 2; 01280 case 422: // Session-Timer duration too long 01281 case 423: // Expires too short 01282 return 3; 01283 case 407: // Proxy-Auth 01284 case 401: // UA Digest challenge 01285 return 4; 01286 01287 // 3xx responses have p = 5 01288 case 402: // Payment required 01289 return 6; 01290 01291 // Responses used for negotiation: 493, 429, 420, 406, 415, 488 01292 case 493: // Undecipherable, try again unencrypted 01293 return 10; 01294 01295 case 420: // Required Extension not supported, try again without 01296 return 12; 01297 01298 case 406: // Not Acceptable 01299 case 415: // Unsupported Media Type 01300 case 488: // Not Acceptable Here 01301 return 13; 01302 01303 // Possibly useful for negotiation, but less likely: 421, 416, 417, 494, 580, 485, 405, 501, 413, 414 01304 01305 case 416: // Unsupported scheme 01306 case 417: // Unknown Resource-Priority 01307 return 20; 01308 01309 case 405: // Method not allowed (both used for negotiating REFER, PUBLISH, etc.. 01310 case 501: // Usually used when the method is not OK 01311 return 21; 01312 01313 case 580: // Preconditions failure 01314 return 22; 01315 01316 case 485: // Ambiguous userpart. A bit better than 404? 01317 return 23; 01318 01319 case 428: // Use Identity header 01320 case 429: // Provide Referrer Identity 01321 case 494: // Use the sec-agree mechanism 01322 return 24; 01323 01324 case 413: // Request too big 01325 case 414: // URI too big 01326 return 25; 01327 01328 case 421: // An extension required by the server was not in the Supported header 01329 return 26; 01330 01331 // The request isn't repairable, but at least we can try to provide some 01332 // useful information: 486, 480, 410, 404, 403, 487 01333 01334 case 486: // Busy Here 01335 return 30; 01336 01337 case 480: // Temporarily unavailable 01338 return 31; 01339 01340 case 410: // Gone 01341 return 32; 01342 01343 case 436: // Bad Identity-Info 01344 case 437: // Unsupported Certificate 01345 case 513: // Message too large 01346 return 33; 01347 01348 case 403: // Forbidden 01349 return 34; 01350 01351 case 404: // Not Found 01352 return 35; 01353 01354 case 487: // Some branches were cancelled, if the UAC sent a CANCEL this is good news 01355 return 36; 01356 01357 // We are hosed: 503, 483, 482, 481, other 5xx, 400, 491, 408 // totally useless 01358 01359 case 503: // bad news, we should never forward this back anyway 01360 return 43; 01361 01362 case 483: // loops, encountered 01363 case 482: 01364 return 41; 01365 01366 // other 5xx p = 42 01367 01368 // UAS is seriously confused: p = 43 01369 // case 481: 01370 // case 400: 01371 // case 491: 01372 // default: 01373 01374 case 408: // very, very bad (even worse than the remaining 4xx responses) 01375 return 49; 01376 01377 default: 01378 return 43; 01379 } 01380 return p; 01381 } 01382 01383 bool 01384 ResponseContext::CompareStatus::operator()(const resip::SipMessage& lhs, const resip::SipMessage& rhs) const 01385 { 01386 assert(lhs.isResponse()); 01387 assert(rhs.isResponse()); 01388 01389 // !rwm! replace with correct thingy here 01390 return lhs.header(h_StatusLine).statusCode() < rhs.header(h_StatusLine).statusCode(); 01391 } 01392 01393 void 01394 ResponseContext::forwardBestResponse() 01395 { 01396 InfoLog (<< "Forwarding best response: " << mBestResponse.brief()); 01397 01398 clearCandidateTransactions(); 01399 01400 if(mRequestContext.getOriginalRequest().method()==INVITE) 01401 { 01402 cancelActiveClientTransactions(); 01403 } 01404 01405 if(mBestResponse.header(h_StatusLine).statusCode() == 503) 01406 { 01407 //See RFC 3261 sec 16.7, page 110, paragraph 2 01408 mBestResponse.header(h_StatusLine).statusCode() = 480; 01409 } 01410 01411 if(mBestResponse.header(h_StatusLine).statusCode() == 408 && 01412 mBestResponse.method()!=INVITE) 01413 { 01414 // We don't forward back NIT 408; we just silently abandon the transaction - RFC4321 - section 1.4 408 for non-INVITE is not useful 01415 DebugLog(<< "Got NIT 408, abandoning: "<<mRequestContext.getTransactionId()); 01416 mRequestContext.getProxy().getStack().abandonServerTransaction(mRequestContext.getTransactionId()); 01417 mRequestContext.mHaveSentFinalResponse = true; // To avoid Request context from sending a response 01418 } 01419 else 01420 { 01421 mRequestContext.sendResponse(mBestResponse); 01422 } 01423 } 01424 01425 EncodeStream& 01426 repro::operator<<(EncodeStream& strm, const ResponseContext& rc) 01427 { 01428 strm << "ResponseContext: " 01429 << " identity=" << rc.mRequestContext.getDigestIdentity() 01430 << " best=" << rc.mBestPriority << " " << rc.mBestResponse.brief() 01431 << " forwarded=" << rc.mRequestContext.mHaveSentFinalResponse 01432 << " pending=" << InserterP(rc.mCandidateTransactionMap) 01433 << " active=" << InserterP(rc.mActiveTransactionMap) 01434 << " terminated=" << InserterP(rc.mTerminatedTransactionMap); 01435 01436 return strm; 01437 } 01438 01439 01440 /* ==================================================================== 01441 * The Vovida Software License, Version 1.0 01442 * 01443 * Copyright (c) 2000 Vovida Networks, Inc. All rights reserved. 01444 * 01445 * Redistribution and use in source and binary forms, with or without 01446 * modification, are permitted provided that the following conditions 01447 * are met: 01448 * 01449 * 1. Redistributions of source code must retain the above copyright 01450 * notice, this list of conditions and the following disclaimer. 01451 * 01452 * 2. Redistributions in binary form must reproduce the above copyright 01453 * notice, this list of conditions and the following disclaimer in 01454 * the documentation and/or other materials provided with the 01455 * distribution. 01456 * 01457 * 3. The names "VOCAL", "Vovida Open Communication Application Library", 01458 * and "Vovida Open Communication Application Library (VOCAL)" must 01459 * not be used to endorse or promote products derived from this 01460 * software without prior written permission. For written 01461 * permission, please contact vocal@vovida.org. 01462 * 01463 * 4. Products derived from this software may not be called "VOCAL", nor 01464 * may "VOCAL" appear in their name, without prior written 01465 * permission of Vovida Networks, Inc. 01466 * 01467 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED 01468 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 01469 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND 01470 * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL VOVIDA 01471 * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES 01472 * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL, 01473 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 01474 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 01475 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 01476 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 01477 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 01478 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 01479 * DAMAGE. 01480 * 01481 * ==================================================================== 01482 * 01483 * This software consists of voluntary contributions made by Vovida 01484 * Networks, Inc. and many individuals on behalf of Vovida Networks, 01485 * Inc. For more information on Vovida Networks, Inc., please see 01486 * <http://www.vovida.org/>. 01487 * 01488 */
1.7.5.1