Пример #1
0
AuthPlugin::AuthResult
CallerAlias::authorizeAndModify(const UtlString& id,    /**< The authenticated identity of the
                                                         *   request originator, if any (the null
                                                         *   string if not).
                                                         *   This is in the form of a SIP uri
                                                         *   identity value as used in the
                                                         *   credentials database (user@domain)
                                                         *   without the scheme or any parameters.
                                                         */
                                const Url&  requestUri, ///< parsed target Uri
                                RouteState& routeState, ///< the state for this request.  
                                const UtlString& method,///< the request method
                                AuthResult  priorResult,///< results from earlier plugins.
                                SipMessage& request,    ///< see AuthPlugin regarding modifying
                                bool bSpiralingRequest, ///< spiraling indication 
                                UtlString&  reason      ///< rejection reason
                                )
{
   // get the call-id to use in logging
   UtlString callId;
   request.getCallIdField(&callId);

   if (   (priorResult != DENY) // no point in modifying a request that won't be sent
       )   
   {
      UtlString callerFrom;
      UtlString callerFromTagOffsetStr;
      UtlString aliasFrom;
      UtlString aliasFromTagOffsetStr;
      UtlString originalFromTag;

      if (   !routeState.getParameter(mInstanceName.data(), CALLER_FROM_PARAM, callerFrom)
          || !routeState.getParameter(mInstanceName.data(), CALLER_TAG_OFFSET_PARAM, callerFromTagOffsetStr)
          || !routeState.getParameter(mInstanceName.data(), ALIAS_FROM_PARAM, aliasFrom)
          || !routeState.getParameter(mInstanceName.data(), ALIAS_TAG_OFFSET_PARAM, aliasFromTagOffsetStr)
          || !routeState.originalCallerFromTagValue(mInstanceName.data(), originalFromTag)
          )
      {
         if (   routeState.isMutable()
             && routeState.directionIsCallerToCalled(mInstanceName.data())
             ) // a new dialog?
         {
            /*
             * Get the callers identity by getting the caller URI and:
             *    remove all parameters
             *    remove the scheme name
             */
            UtlString callerIdentity;

            UtlString originalFromField;
            request.getFromField(&originalFromField);
            Url originalFromUrl(originalFromField);
            
            /*
             * Extract the from identity as a key for the caller alias table
             * Start with the From header field (someday we should use the Identity if present)
             */
            Url fromUrl(originalFromUrl);
            fromUrl.removeParameters(); // parameters are not relevant for this 
         
            Url::Scheme fromUrlScheme = fromUrl.getScheme();
            switch (fromUrlScheme)
            {
            case Url::SipsUrlScheme:
               // sips and sip are equivalent for identity purposes,
               //   so just set to sip 
               fromUrl.setScheme(Url::SipUrlScheme);
               //   and fall through to extract the identity...

            case Url::SipUrlScheme:
               // case Url::TelUrlScheme: will go here, since 'tel' and 'sip' are the same length
               fromUrl.getUri(callerIdentity);
               callerIdentity.remove(0,4 /* strlen("sip:") */); // strip off the scheme name
               break;

            default:
               // for all other schemes, treat identity as null
               Os::Logger::instance().log(FAC_SIP, PRI_WARNING,
                             "CallerAlias[%s]::check4andApplyAlias From uses unsupported scheme '%s'"
                             " - using null identity",
                             mInstanceName.data(),
                             Url::schemeName(fromUrlScheme)
                             );
               break;
            }

            /*
             * Determine whether the identity is one for which this proxy
             * is authoritative; if not, we will not use wildcard matches.
             */
            bool identityIsLocal = mpSipRouter->isLocalDomain(fromUrl);
            
            // now we have callerIdentity set; use for looking up each contact.
            Os::Logger::instance().log(FAC_SIP, PRI_DEBUG,
                          "CallerAlias[%s]::check4andApplyAlias "
                          "\n  caller '%s' %s",
                          mInstanceName.data(),
                          callerIdentity.data(),
                          identityIsLocal ? "is local" : "is not local"
                          );

            /*
             * Examine the request URI,
             * checking for a caller alias set for its domain(including asssociated gateway sipxecsLineid)  with callerIdentity
             */

            UtlString sipxecsLineIdField;
            requestUri.getUrlParameter(SIPX_SIPXECS_LINEID_URI_PARAM, sipxecsLineIdField);

            Os::Logger::instance().log(FAC_SIP, PRI_DEBUG,
                             "getUrlParameter: sipxecsLineid[%s]"
                             " in CallerAlias",
                             sipxecsLineIdField.data()
                             );

            UtlString targetDomain;
            requestUri.getHostWithPort(targetDomain);

            if (!(sipxecsLineIdField.isNull()))
            {
                targetDomain.append(";").append(SIPX_SIPXECS_LINEID_URI_PARAM).append("=").append(sipxecsLineIdField.data());
            }

            Os::Logger::instance().log(FAC_SIP, PRI_DEBUG,
                          "CallerAlias::targetDomain [%s]",
                          targetDomain.data()
                          );

            // look up any caller alias for this identity and contact domain
            UtlString callerAlias;
            if (identityIsLocal && getCallerAlias(callerIdentity, targetDomain, callerAlias) )
            {
               // found a caller alias, so rewrite the From information
               /*
                * The From header requires special handling
                * - we need to preserve the tag, if any, from the original header
                */
               originalFromUrl.getFieldParameter("tag", originalFromTag);

               Url newFromUrl(callerAlias.data());
               newFromUrl.removeFieldParameter("tag"); // specifying a tag is a no-no
               if ( !originalFromTag.isNull() )
               {
                  newFromUrl.setFieldParameter("tag", originalFromTag.data());
               }
               UtlString newFromFieldValue;
               newFromUrl.toString(newFromFieldValue);
                   
               // log the change we are making before stripping the tag from the field values
               Os::Logger::instance().log( FAC_SIP, PRI_INFO,
                             "CallerAlias[%s]::check4andApplyAlias call %s set caller alias\n"
                             "  Original-From: %s\n"
                             "  Aliased-From:  %s",
                             mInstanceName.data(), callId.data(),
                             originalFromField.data(),
                             newFromFieldValue.data()
                             );

               // rewrite the caller identity with the aliased value
               request.setRawFromField(newFromFieldValue.data());

               // Factor the tag values out of the field values stored in the RouteState
               //  We do this because otherwise we'll end up encoding and sending two copies
               //  of the tag; since some phones send really long tag values (no one knows why),
               //  this can cause such large Record-Route headers that they cause interop problems.
               if ( ! originalFromTag.isNull() )
               {
                  // find the offset of the tag value in the callers from field
                  ssize_t callerFromTagOffset;
                  callerFromTagOffset = originalFromField.index(originalFromTag);
                  callerFromTagOffsetStr.appendNumber(callerFromTagOffset);
                  // strip the tag value from the original From value to be stored in the RouteState
                  originalFromField.replace(callerFromTagOffset, originalFromTag.length(), "");
                  
                  // find the offset of the tag value in the aliased from field
                  ssize_t aliasFromTagOffset;
                  aliasFromTagOffset = newFromFieldValue.index(originalFromTag);
                  aliasFromTagOffsetStr.appendNumber(aliasFromTagOffset);
                  // strip the tag value from the aliased From value to be stored in the RouteState
                  newFromFieldValue.replace(aliasFromTagOffset, originalFromTag.length(), "");
               }

               // save the original and new values so that we can fix them later
               routeState.setParameter(mInstanceName.data(),
                                       CALLER_FROM_PARAM,originalFromField);
               routeState.setParameter(mInstanceName.data(),
                                       CALLER_TAG_OFFSET_PARAM,callerFromTagOffsetStr);
               routeState.setParameter(mInstanceName.data(),
                                       ALIAS_FROM_PARAM,newFromFieldValue);
               routeState.setParameter(mInstanceName.data(),
                                       ALIAS_TAG_OFFSET_PARAM,aliasFromTagOffsetStr);
            }
            else
            {
               Os::Logger::instance().log( FAC_SIP, PRI_DEBUG,
                             "CallerAlias[%s]::check4andApplyAlias call %s found no alias",
                             mInstanceName.data(), callId.data()
                             );
            }
         }
         else
         {
            Os::Logger::instance().log(FAC_SIP, PRI_DEBUG,
                          "CallerAlias[%s]::authorizeAndModify "
                          "not mutable - no rewrite",
                          mInstanceName.data()                          
                          );
         }
      }
      else // the callerFrom and aliasFrom parameters were found
      {
         /*
          * This request has had its From rewritten, so fix either the From
          * or the To depending on which direction this request is going.
          */
         if (!request.isResponse()) // can't modify responses, so don't bother
         {
            size_t tagOffset;
            
            if (routeState.directionIsCallerToCalled(mInstanceName.data()))
            {
               // replace the from tag value in the stored aliased header
               tagOffset = strtol(aliasFromTagOffsetStr.data(), NULL, 10);
               aliasFrom.insert(tagOffset, originalFromTag);

               // put the aliased header into the message
               request.setRawFromField(aliasFrom);
               Os::Logger::instance().log(FAC_AUTH, PRI_DEBUG, "CallerAlias[%s]::authorizeAndModify "
                             "call %s reset From",
                             mInstanceName.data(), callId.data()
                             );
            }
            else // direction is Called to Caller
            {
               // replace the from tag value in the stored original header
               tagOffset = strtol(callerFromTagOffsetStr.data(), NULL, 10);
               callerFrom.insert(tagOffset, originalFromTag);

               request.setRawToField(callerFrom.data());
               Os::Logger::instance().log(FAC_AUTH, PRI_DEBUG, "CallerAlias[%s]::authorizeAndModify "
                             "call %s reset To",
                             mInstanceName.data(), callId.data()
                             );
            }
         }
      }
   }
   return AuthPlugin::CONTINUE;
}
Пример #2
0
bool CallTracker::notifyIncomingDialogFormingInvite( SipMessage& request, RouteState& routeState, const EndpointDescriptor*& prCaller, const EndpointDescriptor*& prCallee )
{
   bool                bResult = false;
   UtlString           sessionContextHandle;
   SessionContext*     pSessionContext;

   // We need to process and incoming dialog-forming request.  In some scenarios, it is possible
   // for such a request to visit us twice.  If there is no session-id encoded in the RouteState,
   // it indicates that this is the first time we see it.
   if( !getSessionContextHandle( routeState, sessionContextHandle ) )
   {
      // we are not tracking this session yet.  Allocate a new session context to track
      // this particular fork and save a copy of the message's SDP body if not already done - this
      // may be useful if the request spirals back to us and we need to restore a patched
      // SDP to its original form.
      pSessionContext = createSessionContextAndSetHandle( request, routeState, true, sessionContextHandle );
      if( pSessionContext )
      {
         prCaller = &pSessionContext->getEndpointDescriptor( CALLER );
         prCallee = &pSessionContext->getEndpointDescriptor( CALLEE );
         if( !mpSavedOriginalSdpOfferCopy )
         {
            mpSavedOriginalSdpOfferCopy = const_cast<SdpBody*>( request.getSdpBody() );
         }
         bResult = true;
      }
      else
      {
         Os::Logger::instance().log( FAC_NAT, PRI_ERR, "CallTracker[%zd]::notifyIncomingDialogFormingInvite[1] failed to create session context ",
                        mHandle );
      }
   }
   else
   {
      // the RouteState already has a session-id which means that we are already tracking this
      // session.  There are several scenarios that can lead up to that situation.  Consider this:
      //
      // User A--------sipXecs1-----------sipXecs2-------User C(call forwarded to User B)
      // User B---+
      // User B---+
      //
      // Users A & B are registered against sipXecs1 and User C is registered againt sipXecs2.
      // User B is registered from two endpoints.
      // User C has its sipXecs-based call forward all calls set to SetB@sipXecs1.
      // When User A calls User C, the INVITE takes the following path:
      //           UserA-->sipXecs1-->sipXecs2-->sipXecs1-->UserB
      //                                                 +->UserB
      // A SessionContext is created when sipXecs1 is visited the first time and its session-id
      // is encoded in the RouteState.  When sipXecs1 is revisited by the INVITE it sees that
      // the RouteState already carries a session-id which tells it that this is a revisiting
      // INVITE.

      // If no special handling is performed here, several problems can appear.
      //
      // Problem #1- callee's real location wasn't known when SessionContext was created on first visit
      // ==============================================================================================
      //     When the dialog-forming INVITE was first seen by sipXecs1, the target was UserC@sipxecs2.
      //     Given that the call was routed via a SIP trunk, sipXecs1 didn't know the real location of the
      //     callee and therefore pegged the callee a being at an UNKNOWN location and imposed the use
      //     of a media relay using the media relay's public IP adddress as the media connection address
      //     in the SDP presented to the callee.
      //     When the dialog-forming INVITE comes back to sipxecs1,  the request target of the INVITE, namely
      //     UserB@sipxecs1, is known to sipXecs1 and can therefore promote its location information
      //     from UNKNOWN to its precise location based on the information it collected from the set
      //     at registration time.  The SessionContext that was initially created by the CallTracker to
      //     handle the NAT traversal was created at the time when the location of the callee was unknown.
      //     Now that the location is known, the old SessionContext can be abandonned and a new one created
      //     that will choose whether or not to involve a media relay based on an accurate representation
      //     of the caller and callee's locations.
      //
      // Solution to Problem #1
      // ======================
      // - Restore the SDP to its original state (i.e. before any transformation) if it got changed by us.
      // - Allocate a new SessionContext that will be responsible handling all the dialogs forked off
      //   of this INVITE using the updated callee location information.
      // - Remove any 'id' param containing the handle of the original SessionContext in the Vias
      //   of the request.  This procedure will prevent the original SessionContext from handling
      //   responses pertaining to the newly created SessionContext.
      pSessionContext = getSessionContextFromHandle( sessionContextHandle );
      if( pSessionContext )
      {
         // First, allocate a new SessionContext that will take care of this new fork.
         UtlString handleOfnewSessionHandle;
         pSessionContext = createSessionContextAndSetHandle( request, routeState, true, handleOfnewSessionHandle );
         if( pSessionContext )
         {
            prCaller = &pSessionContext->getEndpointDescriptor( CALLER );
            prCallee = &pSessionContext->getEndpointDescriptor( CALLEE );
            bResult = true;

            // Second, restore the SDP to its original form as saved by the original SessionContext
            if( mpSavedOriginalSdpOfferCopy )
            {
               request.setBody( mpSavedOriginalSdpOfferCopy->copy() );
            }

            // third, remove the handle of the SessionContext being replaced in the Vias
            removeSessionHandleFromVias( request, sessionContextHandle );
         }
         else
         {
            Os::Logger::instance().log( FAC_NAT, PRI_ERR, "CallTracker[%zd]::notifyIncomingDialogFormingInvite[2] failed to create session context ",
                           mHandle );
         }
      }

      // Problem #2- sipXecs2 may not know how to reach UserB directly
      // =============================================================
      //     As we can see in the example above, the sipXecs1 is visited twice but the SipRouter
      //     logic is such that a Record-Route is added the first time it sees the request and
      //     not the others.  This means that when the request arrives at UserB's sets it will
      //     have Record-route: <sip:sipXecs2>,<sip:sipXecs1>. That particular arrangement
      //     means that although sipXecs1 was the proxy that routed the dialog-forming
      //     request to UserB's sets, sipXecs2 will be responsible for routing all subsequent in-dialog
      //     requests.  Since UserB's sets are registered against sipXecs1 only, it is the only
      //     proxy that truly knows the public and private IP address information of UserB's sets
      //     and therefore the only one that can successfully deliver requests to them accross NATs.
      //     Even if sipXecs2 could somehow "learn" the sets public IP addresses, if UserB's set happened to
      //     be behind a non-full cone NAT  sipXecs2 will not have the ability to send requests to
      //     that set as they will be rejected by the NAT because pinholes exist between the set and sipXecs2
      //
      // Solution to Problem #2
      // ======================
      // To solve this problem, the following piece of logic will ask the RouteState to add a copy of the
      // Record-Route header when it is updated in cases where sipXproxy is not already at the top of the
      // route set.
      UtlString topRecordRoute;
      if( routeState.isFound() )
      {
         if( request.getRecordRouteUri( 0, &topRecordRoute ) )
         {
            Url topRecordRouteUrl( topRecordRoute );
            UtlString topRecordRouteHost;
            topRecordRouteUrl.getHostAddress( topRecordRouteHost );
            int topRecordRoutePort = topRecordRouteUrl.getHostPort();

            if( ( topRecordRouteHost != mpNatTraversalRules->getProxyTransportInfo().getAddress() ||
                  topRecordRoutePort != mpNatTraversalRules->getProxyTransportInfo().getPort() ) &&
                ( topRecordRouteHost != mpNatTraversalRules->getPublicTransportInfo().getAddress() ||
                  topRecordRoutePort != mpNatTraversalRules->getPublicTransportInfo().getPort() ) )
            {
               routeState.addCopy();
            }
         }
      }
   }

   // If the method successfully completed and if the caller is a remote worker then add it to the
   // list of endpoints whose NAT pinholes need to be kept alive for the duration of the call.
   // NOTE: the called party is already handled the NatMaintainer's RegDB lookups.
   if( bResult == true )
   {
      if( prCaller->getLocationCode() == REMOTE_NATED &&
          prCaller->getPublicTransportAddress().getTransportProtocol().compareTo( "udp", UtlString::ignoreCase ) == 0 &&
          !mpCallerPinholeInformation &&
          mpNatMaintainer )
      {
         mpCallerPinholeInformation = new TransportData( prCaller->getPublicTransportAddress().getAddress(),
                                                         prCaller->getPublicTransportAddress().getPort() );

         mpNatMaintainer->addEndpointToKeepAlive( *mpCallerPinholeInformation );
      }
   }
   return bResult;
}
Пример #3
0
bool CallTracker::setSessionContextHandle( RouteState& routeState, const UtlString& handle ) const
{
   routeState.setParameter( mInstanceNameForRouteState.data(), SESSION_CONTEXT_ID_PARAM, handle );
   return true;
}
Пример #4
0
bool CallTracker::unsetSessionContextHandle( RouteState& routeState ) const
{
   routeState.unsetParameter( mInstanceNameForRouteState.data(), SESSION_CONTEXT_ID_PARAM );
   return true;
}
Пример #5
0
bool CallTracker::getSessionContextHandle( const RouteState& routeState, UtlString& handle ) const
{
   return routeState.getParameter( mInstanceNameForRouteState.data(), SESSION_CONTEXT_ID_PARAM, handle );
}