reSIProcate/repro  9694
RRDecorator.cxx
Go to the documentation of this file.
00001 #ifdef HAVE_CONFIG_H
00002 #include "config.h"
00003 #endif
00004 
00005 #include "repro/RRDecorator.hxx"
00006 
00007 #include "repro/Proxy.hxx"
00008 
00009 #include "resip/stack/ExtensionParameter.hxx"
00010 #include "resip/stack/Helper.hxx"
00011 #include "resip/stack/InteropHelper.hxx"
00012 #include "resip/stack/SipMessage.hxx"
00013 #include "resip/stack/Tuple.hxx"
00014 #include "resip/stack/Transport.hxx"
00015 
00016 #include "rutil/Logger.hxx"
00017 
00018 #define RESIPROCATE_SUBSYSTEM resip::Subsystem::REPRO
00019 
00020 using resip::ExtensionParameter;
00021 using resip::NameAddr;
00022 using resip::UDP;
00023 using resip::h_RecordRoutes;
00024 using resip::h_Paths;
00025 using resip::h_RequestLine;
00026 using resip::h_Routes;
00027 using resip::p_comp;
00028 
00029 namespace repro
00030 {
00031 RRDecorator::RRDecorator(const Proxy& proxy,
00032                          const resip::Transport* receivedTransport,
00033                          bool alreadySingleRecordRouted,
00034                          bool hasInboundFlowToken,
00035                          bool forceRecordRouteEnabled,
00036                          bool doPath,
00037                          bool isOriginalSenderBehindNAT) :
00038    mProxy(proxy),
00039    mAddedRecordRoute(0),
00040    mAlreadySingleRecordRouted(alreadySingleRecordRouted),
00041    mHasInboundFlowToken(hasInboundFlowToken),
00042    mForceRecordRouteEnabled(forceRecordRouteEnabled),
00043    mDoPath(doPath),
00044    mIsOriginalSenderBehindNAT(isOriginalSenderBehindNAT),
00045    mReceivedTransport(receivedTransport)
00046 {}
00047 
00048 RRDecorator::~RRDecorator()
00049 {}
00050 
00051 void 
00052 RRDecorator::decorateMessage(resip::SipMessage& request, 
00053                                const resip::Tuple &source,
00054                                const resip::Tuple &destination,
00055                                const resip::Data& sigcompId) 
00056 {
00057    DebugLog(<<"Proxy::decorateMessage called.");
00058    resip::NameAddr rt;
00059 
00060    if(isTransportSwitch(source))
00061    {
00062       if(mAlreadySingleRecordRouted)
00063       {
00064          singleRecordRoute(request, source, destination, sigcompId);
00065       }
00066       else
00067       {
00068          doubleRecordRoute(request, source, destination, sigcompId);
00069       }
00070    }
00071    else
00072    {
00073       // We might still want to record-route in this case; if we need an 
00074       // outbound flow token or we've already added an inbound flow token
00075       if(outboundFlowTokenNeeded(request, source, destination, sigcompId) ||
00076          mHasInboundFlowToken)  // or we have an inbound flow
00077       {
00078          assert(mAlreadySingleRecordRouted);
00079          singleRecordRoute(request, source, destination, sigcompId);
00080       }
00081    }
00082 
00083    static ExtensionParameter p_drr("drr");
00084    resip::NameAddrs* routes=0;
00085    if(mDoPath)
00086    {
00087       routes=&(request.header(resip::h_Paths));
00088    }
00089    else
00090    {
00091       routes=&(request.header(resip::h_RecordRoutes));
00092    }
00093 
00094    if(routes->size() > 1 && 
00095       mAddedRecordRoute && 
00096       routes->front().uri().exists(p_drr))
00097    {
00098       // .bwc. It is possible that we have duplicate Record-Routes at this 
00099       // point, if we have done a transport switch but both transports use the 
00100       // same FQDN.
00101       resip::NameAddrs::iterator second = ++(routes->begin());
00102       if(*second == routes->front())
00103       {
00104          // Duplicated record-routes; pare down to a single one.
00105          routes->pop_front();
00106          --mAddedRecordRoute;
00107          routes->front().uri().remove(p_drr);
00108       }
00109    }
00110 }
00111 
00112 void 
00113 RRDecorator::singleRecordRoute(resip::SipMessage& request, 
00114                                const resip::Tuple &source,
00115                                const resip::Tuple &destination,
00116                                const resip::Data& sigcompId)
00117 {
00118    resip::NameAddr rt;
00119    // .bwc. outboundFlowTokenNeeded means that we are assuming that whoever is
00120    // just downstream will remain in the call-path throughout the dialog.
00121    if(outboundFlowTokenNeeded(request, source, destination, sigcompId))
00122    {
00123       if(destination.getType()==resip::TLS || 
00124          destination.getType()==resip::DTLS)
00125       {
00126          rt = mProxy.getRecordRoute(destination.transport);
00127          rt.uri().scheme()="sips";
00128       }
00129       else
00130       {
00131          // .bwc. It is safe to put ip+port+proto here, since we have an 
00132          // existing flow to the next hop.
00133          rt.uri().host()=resip::Tuple::inet_ntop(source);
00134          rt.uri().port()=source.getPort();
00135          rt.uri().param(resip::p_transport)=resip::Tuple::toDataLower(source.getType());
00136       }
00137       // .bwc. If our target has an outbound flow to us, we need to put a flow
00138       // token in a Record-Route.
00139       resip::Helper::massageRoute(request,rt);
00140       resip::Data binaryFlowToken;
00141       resip::Tuple::writeBinaryToken(destination,binaryFlowToken, Proxy::FlowTokenSalt);
00142       
00143       rt.uri().user()=binaryFlowToken.base64encode();
00144    }
00145    else
00146    {
00147       // No need for a flow-token; just use an ordinary record-route.
00148       rt = mProxy.getRecordRoute(destination.transport);
00149       resip::Helper::massageRoute(request,rt);
00150    }
00151 
00152 #ifdef USE_SIGCOMP
00153    if(mProxy.compressionEnabled() && !sigcompId.empty())
00154    {
00155       rt.uri().param(p_comp)="sigcomp";
00156    }
00157 #endif
00158 
00159    static ExtensionParameter p_drr("drr");
00160    rt.uri().param(p_drr);
00161 
00162    resip::NameAddrs* routes=0;
00163    if(mDoPath)
00164    {
00165       routes=&(request.header(resip::h_Paths));
00166       InfoLog(<< "Adding outbound Path: " << rt);
00167    }
00168    else
00169    {
00170       routes=&(request.header(resip::h_RecordRoutes));
00171       InfoLog(<< "Adding outbound Record-Route: " << rt);
00172    }
00173 
00174    routes->front().uri().param(p_drr);
00175    routes->push_front(rt);
00176    ++mAddedRecordRoute;
00177 }
00178 
00179 void 
00180 RRDecorator::doubleRecordRoute(resip::SipMessage& request, 
00181                                const resip::Tuple &source,
00182                                const resip::Tuple &destination,
00183                                const resip::Data& sigcompId)
00184 {
00185    // We only use this on transport switch when we have not yet Record-Routed.
00186    // If we needed a flow-token in the inbound Record-Route, it would have been 
00187    // added already.
00188    resip::NameAddr rt(mProxy.getRecordRoute(mReceivedTransport));
00189    resip::Helper::massageRoute(request,rt);
00190    if(mDoPath)
00191    {
00192       request.header(h_Paths).push_front(rt);
00193    }
00194    else
00195    {
00196       request.header(h_RecordRoutes).push_front(rt);
00197    }
00198    ++mAddedRecordRoute;
00199    singleRecordRoute(request, source, destination, sigcompId);
00200 }
00201 
00202 bool 
00203 RRDecorator::isTransportSwitch(const resip::Tuple& sendingFrom)
00204 {
00205    if(mForceRecordRouteEnabled)
00206    {
00207       // If we are forcing record routes to be added, then DRR on any transport switch
00208       return mReceivedTransport != sendingFrom.transport;
00209    }
00210    else
00211    {
00212       // If record routing is not forced then only DRR if we are switching transport types or
00213       // protocol versions, since the interfaces themselves may all be equally reachable
00214       // !slg! - could make this behavior more configurable
00215       return sendingFrom.getType() != mReceivedTransport->getTuple().getType() ||
00216              sendingFrom.ipVersion() != mReceivedTransport->getTuple().ipVersion();
00217    }
00218 }
00219 
00220 bool 
00221 RRDecorator::outboundFlowTokenNeeded(resip::SipMessage &msg, 
00222                                      const resip::Tuple &source,
00223                                      const resip::Tuple &destination,
00224                                      const resip::Data& sigcompId)
00225 {
00226    return (destination.onlyUseExistingConnection            // destination is an outbound target
00227            || resip::InteropHelper::getRRTokenHackEnabled() // or the token is enabled
00228            || mIsOriginalSenderBehindNAT                    // or the nat detection hack is enabled
00229            || !sigcompId.empty());                          // or we are routing to a SigComp transport 
00230                                                             // ?slg? For Sigcomp are we guaranteed to always have 
00231                                                             // single RR at this point?  If not, then strangeness 
00232                                                             // will happen when singleRecordRoute adds a ;drr param
00233 }
00234 
00235 void 
00236 RRDecorator::rollbackMessage(resip::SipMessage& request) 
00237 {
00238    resip::NameAddrs* routes=0;
00239    if(mDoPath)
00240    {
00241       routes=&(request.header(resip::h_Paths));
00242    }
00243    else
00244    {
00245       routes=&(request.header(resip::h_RecordRoutes));
00246    }
00247 
00248    while(mAddedRecordRoute--)
00249    {
00250       assert(!routes->empty());
00251       routes->pop_front();
00252    }
00253 
00254    if(mAlreadySingleRecordRouted)
00255    {
00256       // Make sure we remove the drr param if it is there.
00257       static ExtensionParameter p_drr("drr");
00258       routes->front().uri().remove(p_drr);
00259    }
00260 }
00261 
00262 resip::MessageDecorator* 
00263 RRDecorator::clone() const 
00264 {
00265    return new RRDecorator(*this);
00266 }
00267 
00268 } // of namespace repro