示例#1
0
UtlBoolean SipDialogMgr::isNewRemoteTransaction(const SipMessage& message)
{
    UtlBoolean matchesTransaction = FALSE;
    UtlString handle;
    SipDialog::getDialogHandle(message, handle);

    UtlString callId;
    UtlString fromTag;
    UtlString toTag;
    Url fromField;
    Url toField;
    message.getFromUrl(fromField);
    message.getToUrl(toField);
    message.getCallIdField(callId);
    fromField.getFieldParameter("tag", fromTag);
    toField.getFieldParameter("tag", toTag);

    lock();
    // Looking for any dialog that matches this handle
    SipDialog* dialog = findDialog(handle,
                                   TRUE, // if established, match early dialog
                                   TRUE); // if early, match established dialog

    if(dialog && 
       dialog->isTransactionRemotelyInitiated(callId, fromTag, toTag) &&
       dialog->isNextRemoteCseq(message))
    {
        matchesTransaction = TRUE;
    }

    unlock();
    
    return(matchesTransaction);
}
void SipImpliedSubscriptions::buildSubscribeRequest( const SipMessage& registerMessage
                           ,int duration
                           ,SipMessage& subscribeRequest
                           ,UtlString&  callId
                           ,UtlString&  fromTag
                           ,UtlString&  fromUri
                           )
{
   UtlString registrationValue;
   UtlString tagNameValuePair;
   UtlString contactUri;
   int      sequenceNumber = 0;

   // Get the From URL, and change the tag
   Url fromUrl;
   registerMessage.getFromUrl( fromUrl );
   fromUrl.removeFieldParameter("tag"); // discard from tag from REGISTER
   registerMessage.getFromUri( &fromUri );
   (void) registerMessage.getContactUri(0, &contactUri);
   (void) registerMessage.getCSeqField(&sequenceNumber, &registrationValue);

   Url toUrl;
   registerMessage.getToUrl( toUrl );
   toUrl.removeFieldParameter("tag");
   
   UtlString toUri;
   registerMessage.getToUri( &toUri );

   registerMessage.getCallIdField( &callId );
   callId.prepend("implied-mwi-");

   // Build a from tag for the SUBSCRIBE
   //   - hash the call id so that it will be the same on each refresh
   UtlString callIdHash;
   NetMd5Codec::encode( callId.data(), callIdHash );
   fromUrl.setFieldParameter("tag", callIdHash.data() );
   fromTag = callIdHash; // for constructing the nonce

   subscribeRequest.setVoicemailData( fromUrl.toString() // From:
                                     ,toUrl.toString()   // To:
                                     ,toUri.data()       // request URI
                                     ,contactUri.data()  // taken from registration
                                     ,callId.data()
                                     ,++sequenceNumber
                                     ,duration
                                     );

   /*
    * Rewrite the event field to add our extension parameter to
    * ensure that the registration and subscription are synchronized.
    */
   const char* standardEventHeader = subscribeRequest.getHeaderValue(0, SIP_EVENT_FIELD);
   UtlString extendedEventHeader(standardEventHeader);
   extendedEventHeader.append(";" SIPX_IMPLIED_SUB "=");
   char durationString[12];
   sprintf(durationString, "%d", duration);
   extendedEventHeader.append(durationString);
   subscribeRequest.setHeaderValue(SIP_EVENT_FIELD, extendedEventHeader.data(), 0);
}
/// Decode the identity from a message.
SipXauthIdentity::SipXauthIdentity(const SipMessage& message,
                                   const HeaderName headerName,
                                   DialogRule bindRule
                                   )
  : mIsValidIdentity(FALSE)
{
   UtlString callId;
   UtlString fromTag;
   Url fromUrl;
   message.getCallIdField(&callId);
   message.getFromUrl(fromUrl);
   fromUrl.getFieldParameter("tag", fromTag);

   decode(headerName, message, callId, fromTag, bindRule);
}
/// Add identity info to a message.
bool SipXauthIdentity::insert(SipMessage & message,
                              HeaderName headerName,
                              const OsDateTime * timestamp)
{
   // Don't proceed if the encapsulated identity is invalid
   if (!mIsValidIdentity)
   {
      Os::Logger::instance().log(FAC_SIP, PRI_CRIT,
                    "SipXauthIdentity::insert: "
                    "encapsulated SipXauthIdentity is invalid");
   }
   else
   {
      // make sure no existing identity in the message
      remove(message, headerName);

      // set Call-Id and from-tag for the signature calculation
      UtlString callId;
      UtlString fromTag;
      Url fromUrl;
      message.getCallIdField(&callId);
      message.getFromUrl(fromUrl);
      fromUrl.getFieldParameter("tag", fromTag);

      OsDateTime now;
      OsDateTime::getCurTime(now);
      if (NULL==timestamp)
      {
         timestamp = &now;
      }

      UtlString value;
      encode(value, callId, fromTag, *timestamp);

      // Insert displayName if it is an P-Asserted-Identity header.
      if (headerName == SipXauthIdentity::PAssertedIdentityHeaderName)
      {
          UtlString displayName;
          fromUrl.getDisplayName(displayName);
          value.prepend(displayName.data());
      }

      message.addHeaderField(headerName, value.data());
   }

   return mIsValidIdentity;
}
示例#5
0
int SubscribeServerThread::removeErrorSubscription (const SipMessage& sipMessage )
{
    int returnStatus = STATUS_SUCCESS;
    UtlString callId;
    UtlString to;
    UtlString from;
    sipMessage.getToField(&to);
    sipMessage.getFromField(&from);
    sipMessage.getCallIdField(&callId);

    OsSysLog::add(FAC_SIP, PRI_WARNING,
                  "SubscribeServerThread::removeErrorSubscription %s",
                  callId.data());

    removeErrorRow(from, to, callId);
    return returnStatus;
}
示例#6
0
AuthPlugin::AuthResult
SubscriptionAuth::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 wrt modifying
                                    bool bSpiralingRequest, ///< request spiraling indication
                                    UtlString&  reason      ///< rejection reason
                                    )
{
   AuthResult result = CONTINUE;
   UtlString eventField;
   UtlString targetUser;
   requestUri.getUserId(targetUser);

   if (CONTINUE == priorResult &&
       id.isNull() &&
       method.compareTo(SIP_SUBSCRIBE_METHOD) == 0 &&
       request.getEventField(eventField) &&
       mEventPackagesRequiringAuthentication.contains( &eventField ) &&
       !isTargetExemptedFromAuthentication(targetUser))
   {
      // we do not have an authenticated ID for the request - challenge it.
      // get the call-id to use in logging
      UtlString callId;
      request.getCallIdField(&callId);

      OsSysLog::add(FAC_AUTH, PRI_INFO, "SubscriptionAuth[%s]::authorizeAndModify "
                    "challenging subscription for dialog event package '%s' (call id = '%s')",
                    mInstanceName.data(), eventField.data(), callId.data()
                    );
      result = DENY;
      reason = "Authentication Required to Subscribe to " + eventField;
   }
   return result;
}
示例#7
0
RedirectPlugin::LookUpStatus
SipRedirectorJoin::lookUp(
   const SipMessage& message,
   const UtlString& requestString,
   const Url& requestUri,
   const UtlString& method,
   ContactList& contactList,
   RedirectPlugin::RequestSeqNo requestSeqNo,
   int redirectorNo,
   SipRedirectorPrivateStorage*& privateStorage,
   ErrorDescriptor& errorDescriptor)
{
   UtlString userId;
   UtlString incomingCallId;

   requestUri.getUserId(userId);
   message.getCallIdField(&incomingCallId);

   if (!mCallJoinCode.isNull() &&
       userId.length() > mCallJoinCode.length() &&
       userId.index(mCallJoinCode.data()) == 0 &&
       userId.compareTo(mExcludedUser1) != 0 &&
       userId.compareTo(mExcludedUser2) != 0)
   {
      return lookUpDialog(requestString,
                          incomingCallId,
                          contactList,
                          requestSeqNo,
                          redirectorNo,
                          privateStorage,
                          // The suffix of the request URI after the
                          // directed call pick-up code.
                          userId.data() + mCallJoinCode.length(),
                          // Only examine confirmed dialogs.
                          stateConfirmed);
   }
   else
   {
      // We do not recognize the user, so we do nothing.
      return RedirectPlugin::SUCCESS;
   }
}
/// Encode identity info into a URL
bool SipXauthIdentity::encodeUri(Url              & uri,
                                 const SipMessage & request,
                                 const OsDateTime * timestamp)
{
   // Don't proceed if the encapsulated identity is invalid
   if (!mIsValidIdentity)
   {
      Os::Logger::instance().log(FAC_SIP, PRI_CRIT,
                    "SipXauthIdentity::encodeUri[bound]: encapsulated SipXauthIdentity is invalid");
   }
   else
   {
      // make sure no existing identity in the URI
      uri.removeHeaderParameter(SipXauthIdentity::AuthIdentityHeaderName);
      // set Call-Id and from-tag for the signature calculation
      UtlString callId;
      UtlString fromTag;
      Url fromUrl;
      request.getCallIdField(&callId);
      request.getFromUrl(fromUrl);
      fromUrl.getFieldParameter("tag", fromTag);

      OsDateTime now;
      OsDateTime::getCurTime(now);
      if (NULL==timestamp)
      {
         timestamp = &now;
      }

      UtlString value;
      encode(value, callId, fromTag, *timestamp);
      uri.setHeaderParameter(SipXauthIdentity::AuthIdentityHeaderName, value.data());

      Os::Logger::instance().log(FAC_SIP, PRI_DEBUG,
                    "SipXauthIdentity::encodeUri[bound] encoded URI '%s'",
                    uri.toString().data()
                    );
   }

   return mIsValidIdentity;
}
示例#9
0
UtlBoolean SipDialogMgr::isLastLocalTransaction(const SipMessage& message, 
                                                const char* dialogHandle)
{
    UtlBoolean matchesTransaction = FALSE;
    UtlString handle(dialogHandle ? dialogHandle : "");
    // If the dialog handle was not set, get it from the message
    if(handle.isNull())
    {
       SipDialog::getDialogHandle(message, handle);
    }

    UtlString callId;
    UtlString fromTag;
    UtlString toTag;
    Url fromField;
    Url toField;
    message.getFromUrl(fromField);
    message.getToUrl(toField);
    message.getCallIdField(callId);
    fromField.getFieldParameter("tag", fromTag);
    toField.getFieldParameter("tag", toTag);

    lock();
    // Looking for any dialog that matches this handle
    SipDialog* dialog = findDialog(handle,
                                   TRUE, // if established, match early dialog
                                   TRUE); // if early, match established dialog

    if(dialog && 
       dialog->isTransactionLocallyInitiated(callId, fromTag, toTag) &&
       dialog->isSameLocalCseq(message))
    {
        matchesTransaction = TRUE;
    }

    unlock();
    
    return(matchesTransaction);
}
// Update the IMDB with the NOTIFY CSeq now in notifyRequest and the
// specified 'version' for the given eventTypeKey.
void SipPersistentSubscriptionMgr::updateVersion(SipMessage& notifyRequest,
                                                 int version,
                                                 const UtlString& eventTypeKey)
{
   // Call the superclass's updateVersion.
   SipSubscriptionMgr::updateVersion(notifyRequest, version, eventTypeKey);

   // Extract from the NOTIFY the information we need to find the right
   // IMDB row.
   int cseq;
   UtlString method;
   notifyRequest.getCSeqField(&cseq, &method);

   UtlString to;
   UtlString from;
   UtlString callId;
   UtlString eventType, eventId;
   int now;

   // Note that the "to" and "from" fields of the subscription table
   // are as those URIs appear in the SUBSCRIBE message, which is
   // reversed in the NOTIFY message.
   notifyRequest.getToField(&from);
   notifyRequest.getFromField(&to);
   notifyRequest.getCallIdField(&callId);
   notifyRequest.getEventField(&eventType, &eventId);
   now = (int) OsDateTime::getSecsSinceEpoch();

   OsSysLog::add(FAC_SIP, PRI_DEBUG,
                 "SipPersistentSubscriptionMgr::updateVersion "
                 "callId = '%s', to = '%s', from = '%s', eventType = '%s', eventTypeKey = '%s', eventId = '%s', cseq = %d, version = %d",
                 callId.data(), to.data(), from.data(), eventType.data(), eventTypeKey.data(), eventId.data(), cseq, version);
   mSubscriptionDBInstance->updateNotifyUnexpiredSubscription(
      mComponent, to, from, callId, eventTypeKey, eventId, now, cseq, version);

   // Start the save timer.
   mPersistenceTimer.oneshotAfter(sPersistInterval);
}
示例#11
0
UtlBoolean SipDialog::isSameDialog(const SipMessage& message) const
{
    UtlString messageCallId;
    message.getCallIdField(&messageCallId);
    UtlBoolean isSameDialog = FALSE;
    if(messageCallId.compareTo(*this, UtlString::ignoreCase) == 0)
    {
        Url messageFromUrl;
        message.getFromUrl(messageFromUrl);
        UtlString messageFromTag;
        messageFromUrl.getFieldParameter("tag", messageFromTag);
        if(messageFromTag.compareTo(mLocalTag, UtlString::ignoreCase) == 0)
        {
            Url messageToUrl;
            message.getToUrl(messageToUrl);
            UtlString messageToTag;
            messageToUrl.getFieldParameter("tag", messageToTag);
            if(messageToTag.compareTo(mRemoteTag, UtlString::ignoreCase) == 0)
            {
                isSameDialog = TRUE;
            }
        }
        else if(messageFromTag.compareTo(mRemoteTag, UtlString::ignoreCase) == 0)
        {
            Url messageToUrl;
            message.getToUrl(messageToUrl);
            UtlString messageToTag;
            messageToUrl.getFieldParameter("tag", messageToTag);
            if(messageToTag.compareTo(mLocalTag, UtlString::ignoreCase) == 0)
            {
                isSameDialog = TRUE;
            }
        }

    }
    return(isSameDialog);

}
示例#12
0
/// Decode the identity from a message by searching for SipXauthIdentity then P-Asserted-Identity
SipXauthIdentity::SipXauthIdentity( const SipMessage& message,
                                    UtlString& matchedHeaderName,
                                    bool bSipXauthIdentityTakesPrecedence,
                                    DialogRule bindRule )
  : mIsValidIdentity(FALSE)
{
   UtlString callId;
   UtlString fromTag;
   Url fromUrl;
   message.getCallIdField(&callId);
   message.getFromUrl(fromUrl);
   fromUrl.getFieldParameter("tag", fromTag);
   matchedHeaderName.remove(0);
   HeaderName firstHeaderToTest;
   HeaderName secondHeaderToTest;

   if( bSipXauthIdentityTakesPrecedence == true )
   {
      firstHeaderToTest  = AuthIdentityHeaderName;
      secondHeaderToTest = PAssertedIdentityHeaderName;
   }
   else
   {
      firstHeaderToTest  = PAssertedIdentityHeaderName;
      secondHeaderToTest = AuthIdentityHeaderName;
   }

   if( decode(firstHeaderToTest, message, callId, fromTag, bindRule) )
   {
      matchedHeaderName = firstHeaderToTest;
   }
   else if( decode(secondHeaderToTest, message, callId, fromTag, bindRule) )
   {
      matchedHeaderName = secondHeaderToTest;
   }
}
UtlBoolean SipPersistentSubscriptionMgr::updateDialogInfo(
   const SipMessage& subscribeRequest,
   UtlString& resourceId,
   UtlString& eventTypeKey,
   UtlString& eventType,
   UtlString& subscribeDialogHandle,
   UtlBoolean& isNew,
   UtlBoolean& isSubscriptionExpired,
   SipMessage& subscribeResponse,
   SipSubscribeServerEventHandler& handler)
{
   OsSysLog::add(FAC_SIP, PRI_DEBUG,
                 "SipPersistentSubscriptionMgr::updateDialogInfo "
                 "resourceId = '%s', eventTypeKey = '%s'",
                 resourceId.data(), eventTypeKey.data());

   UtlBoolean ret;

   // Call SipSubscriptionMgr to update the in-memory data.
   ret = SipSubscriptionMgr::updateDialogInfo(subscribeRequest,
                                              resourceId,
                                              eventTypeKey,
                                              eventType,
                                              subscribeDialogHandle,
                                              isNew,
                                              isSubscriptionExpired,
                                              subscribeResponse,
                                              handler);

   // If that succeeded, update the IMDB.
   if (ret)
   {
      UtlString requestUri;
      UtlString callId;
      UtlString contactEntry;
      UtlString to;
      UtlString from;
      UtlString route;
      UtlString accept;

      subscribeRequest.getRequestUri(&requestUri);
      subscribeRequest.getCallIdField(&callId);
      subscribeRequest.getContactEntry(0, &contactEntry);
      subscribeRequest.getToField(&to);
      subscribeRequest.getFromField(&from);
      subscribeRequest.buildRouteField(&route);
      accept.append(subscribeRequest.getHeaderValue(0, SIP_ACCEPT_FIELD));

      int expires = 0;
      subscribeResponse.getExpiresField(&expires);
      expires += OsDateTime::getSecsSinceEpoch();

      int subscribeCseq;
      UtlString subscribeCseqMethod;
      subscribeRequest.getCSeqField(&subscribeCseq, &subscribeCseqMethod);

      OsSysLog::add(FAC_SIP, PRI_DEBUG,
                    "SipPersistentSubscriptionMgr::updateDialogInfo "
                    "mComponent = '%s', requestUri = '%s', callId = '%s', contactEntry = '%s', expires = %d, to = '%s', from = '%s', key = '%s', route = '%s', accept = '%s'",
                    mComponent.data(), requestUri.data(),
                    callId.data(), contactEntry.data(), expires,
                    to.data(), from.data(), resourceId.data(), route.data(),
                    accept.data());

      // Attempt to update an existing row.
      int now = (int)OsDateTime::getSecsSinceEpoch();
      ret = mSubscriptionDBInstance->updateSubscribeUnexpiredSubscription(
         mComponent, to, from, callId, eventTypeKey, "",
         now, expires, subscribeCseq);
      
      if (!ret)
      {
         // Add a new row.

         // This call assumes that eventTypeKey is OK for use as the <eventtype>,
         // and that the NOTIFY CSeq's will start at 1.  0 is used as
         // the initial XML version.
         ret = mSubscriptionDBInstance->insertRow(
            mComponent, requestUri, callId, contactEntry,
            expires, subscribeCseq, eventTypeKey, "",
            to, from, resourceId, route, 1, accept, 0);

         if (!ret)
         {
            OsSysLog::add(FAC_SIP, PRI_ERR,
                          "SipPersistantSubscriptionMgr::addSubscription "
                          "Could not update or insert record in database");
         }
      }

      // Start the save timer.
      mPersistenceTimer.oneshotAfter(sPersistInterval);
   }

   return ret;
}
UtlBoolean AppAgentSubscribePolicy::isAuthenticated(const SipMessage & subscribeRequest,
                                                    SipMessage & subscribeResponse)
{
   UtlBoolean isAuthorized = FALSE;

   UtlString callId;
   subscribeRequest.getCallIdField(&callId);

   Url fromNameAddr;
   UtlString fromTag;
   subscribeRequest.getFromUrl(fromNameAddr);
   fromNameAddr.getFieldParameter("tag", fromTag);

   UtlString authNonce;
   UtlString authRealm;
   UtlString authUser;
   UtlString authUserBase;
   UtlString uriParam;
   UtlString authCnonce;
   UtlString authNonceCount;
   UtlString authQop;

   // Iterate through Authorization and Proxy-Authorization credentials,
   // looking for one that shows this request is authenticated.
   for (int authIndex = 0;
        ! isAuthorized
        && subscribeRequest.getDigestAuthorizationData(&authUser,
                                                       &authRealm,
                                                       &authNonce,
                                                       NULL, NULL, 
                                                       &uriParam,
                                                       &authCnonce,
                                                       &authNonceCount,
                                                       &authQop,
                                                       HttpMessage::SERVER, 
                                                       authIndex,
                                                       &authUserBase);
        authIndex++
      )
   {
      Os::Logger::instance().log(FAC_AUTH, PRI_DEBUG, 
                    "AppAgentSubscribePolicy::isAuthenticated "
                    "Message Authorization received: "
                    "reqRealm='%s', reqUser='******', reqUserBase='%s'",
                    authRealm.data(), authUser.data(), authUserBase.data());

      UtlString qopType;

      if (mRealm.compareTo(authRealm) ) // case sensitive check that realm is correct
      {
         Os::Logger::instance().log(FAC_AUTH, PRI_DEBUG,
                       "AppAgentSubscribePolicy::isAuthenticated "
                       "Realm does not match");
      }

      // validate the nonce
      else if (!mNonceDb.isNonceValid(authNonce, callId, fromTag, mRealm, mNonceExpiration))
      {
          Os::Logger::instance().log(FAC_AUTH, PRI_INFO,
                        "AppAgentSubscribePolicy::isAuthenticated -"
                        "Invalid nonce: nonce='%s', callId='%s'",
                        authNonce.data(), callId.data());
      }

      // verify that qop,cnonce, nonceCount are compatible
      else if (subscribeRequest.verifyQopConsistency(authCnonce.data(),
                                                     authNonceCount.data(),
                                                     &authQop,
                                                     qopType)
               >= HttpMessage::AUTH_QOP_NOT_SUPPORTED)
      {
          Os::Logger::instance().log(FAC_AUTH, PRI_INFO,
                        "AppAgentSubscribePolicy::isAuthenticated -"
                        "Invalid combination of QOP('%s'), cnonce('%s') and nonceCount('%s')",
                        authQop.data(), authCnonce.data(), authNonceCount.data());
      }

      else // realm, nonce and qop are all ok
      {    
         // build the "authorization identity" from the auth credentials
         Url authIdentity;
         UtlString authTypeDB;
         UtlString passTokenDB;

         // then get the credentials for this user & realm
         if (mEntityDb.getCredential(authUserBase, authRealm, authIdentity, passTokenDB, authTypeDB))
         {
            // only DIGEST is used, so the authTypeDB above is ignored
            if ((isAuthorized =
                 subscribeRequest.verifyMd5Authorization(authUser.data(),
                                                         passTokenDB.data(),
                                                         authNonce.data(),
                                                         authRealm.data(),
                                                         authCnonce.data(),
                                                         authNonceCount.data(),
                                                         authQop.data(),
                                                         uriParam.data())
                   ))
            {
               Os::Logger::instance().log(FAC_AUTH, PRI_DEBUG,
                             "AppAgentSubscribePolicy::isAuthenticated "
                             "response auth hash matches");
            }
            else
            {
               UtlString identity;
               authIdentity.getIdentity(identity);
               Os::Logger::instance().log(FAC_AUTH, PRI_ERR,
                             "AppAgentSubscribePolicy::isAuthenticated "
                             "Response auth hash does not match (bad password?)"
                             " authIdentity='%s' authUser='******' authUserBase='%s'",
                             identity.data(), authUser.data(), authUserBase.data());
            }
         }
         else // failed to get credentials
         {
            UtlString identity;
            authIdentity.getIdentity(identity);
            Os::Logger::instance().log(FAC_AUTH, PRI_ERR,
                          "AppAgentSubscribePolicy::isAuthenticated "
                          "Unable to get credentials for realm='%s', user='******', userBase = '%s'",
                          mRealm.data(), authUser.data(), authUserBase.data());
         }
      }     // end DB check
   } //end for

   if ( !isAuthorized )
   {
      // Generate a new challenge
      UtlString newNonce;
      UtlString opaque;

      mNonceDb.createNewNonce(callId,
                              fromTag,
                              mRealm,
                              newNonce);

      subscribeResponse.setRequestUnauthorized(&subscribeRequest,
                                               HTTP_DIGEST_AUTHENTICATION,
                                               mRealm,
                                               newNonce,
                                               NULL // opaque
         );
   }

   return isAuthorized;
}
示例#15
0
   /// wrapper function for all reg-info event tests
   bool ContactSetTest(UtlString regContactxml, UtlString requestUri, UtlString route)
   {
      bool ret = FALSE;
      instantiateAllTestFixtures( "resource-lists2.xml", 
                                  "subscription1", 
                                  "credential1", 
                                  "sip:127.0.0.1:45141",
                                  FALSE);

      // receive the reg-info subscribe 
      SipMessage request;
      while(getNextMessageFromRlsClientUnderTest( request, 5 ) )
      {
         UtlString method;
         request.getRequestMethod(&method);
         if(!request.isResponse() &&
            0 == method.compareTo(SIP_SUBSCRIBE_METHOD) )
         {
            // Accept the Subscription, regardless of whether it for a 'dialog' or 'reg' event
            // in order to stop retransmissions
            SipMessage regResponse;
            regResponse.setResponseData(&request, 202, "Accepted", "sip:127.0.0.1:45141");
            SipMessage * dispatchedMessage = new SipMessage(regResponse);            
            pResourceServerUnderTest->mClientUserAgent.dispatch(dispatchedMessage);

            // Deal with the two events separately
            UtlString eventField;
            request.getEventField(eventField);
            if(0 == eventField.compareTo("reg"))
            {
               UtlString contactInfo;
               request.getContactUri(0, &contactInfo);
               UtlString callid;
               request.getCallIdField(&callid);
               int cseq;
               request.getCSeqField(&cseq, NULL);

               Url toField;
               regResponse.getToUrl(toField);

               SipMessage regNotify;
               regNotify.setNotifyData(&request, 1, "", "", "reg");
               UtlString regInfo ("<?xml version=\"1.0\"?>\r\n"
                                 "<reginfo xmlns=\"urn:ietf:params:xml:ns:reginfo\" "
                                 "xmlns:gr=\"urn:ietf:params:xml:ns:gruuinfo\" version=\"911\" state=\"full\">\r\n"
                                 "   <registration aor=\"sip:[email protected]\" id=\"sip:[email protected]\" state=\"active\">\r\n "
                                 "      <contact id=\"sip:[email protected]@@&lt;");
               regInfo.append(contactInfo);
               regInfo.append("&gt;\" state=\"active\" event=\"registered\" q=\"1\" callid=\"");
               regInfo.append(callid);
               regInfo.append("\" cseq=\"");
               regInfo.appendNumber(cseq);
               regInfo.append("\">\r\n");
               regInfo.append(regContactxml);
               regInfo.append("      </contact>\r\n"
                              "   </registration>\r\n"
                              "</reginfo>");
               HttpBody * newBody = new HttpBody (regInfo, strlen(regInfo), "application/reginfo+xml");
               regNotify.setContentType("application/reginfo+xml");
               regNotify.setBody(newBody);

               // Set the From field the same as the to field from the 202 response, as it
               // contains the dialog identifying to tags
               regNotify.setRawFromField(toField.toString().data());
               sendToRlsServerUnderTest( regNotify );
            }
            else if(0 == eventField.compareTo("dialog"))
            {
               // If we find a dialog event subscription with the request uri and route
               // that we are looking for, mark the test as passed

               UtlString uri;
               UtlString myRoute;
               request.getRequestUri(&uri);
               request.getRouteField(&myRoute); 
               if(0 == uri.compareTo(requestUri) &&
                  0 == route.compareTo(myRoute))
               {
                  ret = true;
               }
            }
         }
      }
      return ret;
   }
示例#16
0
UtlBoolean TestRegistrar::handleRegisterRequest(SipMessage message)
{
    UtlBoolean messageProcessed = false;
    SipMessage finalResponse;
    UtlString responseToAddress;
    UtlString protocol;
    int responseToPort;
    int seqNum;
    UtlString method;
    UtlString contactField;
    int expires;
    static int retrySeqNum = 0;
    UtlString callId;

    message.getCallIdField(&callId);

    message.getContactField(0, contactField);
    message.getExpiresField(&expires);

    message.getCSeqField(&seqNum, &method);
    message.getFromAddress(&responseToAddress, &responseToPort, &protocol);

    finalResponse.setContactField(contactField);
    finalResponse.setExpiresField(expires);
    finalResponse.setFromField("sip:127.0.0.1:5070", 5070);
    finalResponse.setSendAddress(contactField, responseToPort);
    finalResponse.setToField(contactField, responseToPort, protocol);
    finalResponse.setUserAgentField("TestRegistrar");
//    finalResponse.setRegisterData(responseToAddress, contactField, "127.0.0.1", contactField, callId, seqNum);

    if (contactField.contains("anon"))      // anonymous user doesnt need authentication, just say ok
    {
        finalResponse.setResponseData(&message, 200, "OK");
    }
    else if (contactField.contains("mike"))  // mike requires registration, say 401
    {
        UtlString realm;

        UtlString requestUser;
        UtlString requestRealm;
        UtlString requestNonce;
        UtlString uriParam;

        // TBD - 25-jan-2010 work might be needed if these tests are re-enabled
        message.getDigestAuthorizationData(
                   &requestUser, &requestRealm, &requestNonce,
                   NULL, NULL, &uriParam,
                   NULL,  // TBD cnonce
                   NULL,  // TBD nonceCount
                   NULL,  // TBD qop
                   HttpMessage::SERVER, 0);

        if (seqNum == retrySeqNum) // if this is a retry response
        {
            // if they've sent any auth field, just accept it.
            // TODO:  figure out if a username and password has been encrypted and sent.
            finalResponse.setCSeqField(++seqNum, SIP_REGISTER_METHOD);
            finalResponse.setResponseData(&message, 200, "OK");
        }
        else
        {
            message.getCSeqField(&seqNum, &method);
            finalResponse.setCSeqField(++seqNum, method);
            retrySeqNum = seqNum;
#ifdef _WIN32
            finalResponse.setAuthenticateData("md5", "TestRegistrar", NULL, NULL, NULL, HttpMessage::HttpEndpointEnum::SERVER );
#else
            finalResponse.setAuthenticateData("md5", "TestRegistrar", NULL, NULL, NULL, HttpMessage::SERVER );
#endif
            finalResponse.setResponseData(&message, 401, "Not authorized");
        }
    }
    else if (contactField.contains("xyzzy"))
    {
        // this is our special username that will cause a response
        // to be echoed back with the 3 digit value after xyzzy.
        // for instance, the contact xyzzy401 will cause a 401 response
        int pos = contactField.first("xyzzy");
        char szCode[4];
        szCode[0] = contactField[pos + 5];
        szCode[1] = contactField[pos + 6];
        szCode[2] = contactField[pos + 7];
        szCode[3] = '\0';

        finalResponse.setResponseData(&message, atoi(szCode), "OK");
    }


    mpUserAgent->send(finalResponse);

    return messageProcessed;
}
示例#17
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;
}
示例#18
0
void SipDialog::updateDialogData(const SipMessage& message)
{
    UtlString messageCallId;
    message.getCallIdField(&messageCallId);
    Url messageFromUrl;
    message.getFromUrl(messageFromUrl);
    UtlString messageFromTag;
    messageFromUrl.getFieldParameter("tag", messageFromTag);
    Url messageToUrl;
    message.getToUrl(messageToUrl);
    UtlString messageToTag;
    messageToUrl.getFieldParameter("tag", messageToTag);

    int cSeq;
    UtlString method;
    message.getCSeqField(&cSeq, &method);
    int responseCode = message.getResponseStatusCode();

    // Figure out if the request is from the local or
    // the remote side
    if(isTransactionLocallyInitiated(messageCallId,
                                     messageFromTag,
                                     messageToTag))
    {
        // This message is part of a transaction initiated by
        // the local side of the dialog

        if(cSeq > mLastLocalCseq)
        {
            mLastLocalCseq = cSeq;
        }

        if(cSeq >= mLastLocalCseq)
        {
            // Always update the contact if it is set
            UtlString messageContact;
            // Get the Contact value, but as an addr-spec.
            if(message.getContactUri(0, &messageContact) &&
                !messageContact.isNull())
            {
                if(message.isResponse())
                {
                   mRemoteContact.fromString(messageContact, TRUE);
                }
                else
                {
                   mLocalContact.fromString(messageContact, TRUE);
                }
            }
        }

        // Cannot assume that we only establish a dialog with the
        // initial cseq.  For example if there is an authentication
        // challenge, the dialog will not be established until the
        // second transaction.
        if(cSeq == mLastLocalCseq)
        {
            // A successful response to an INVITE or SUBSCRIBE
            // make this early dialog a set up dialog
            if(mLocalInitiatedDialog &&
               message.isResponse() &&
               responseCode >= SIP_2XX_CLASS_CODE && // successful dialog setup
               responseCode < SIP_3XX_CLASS_CODE &&
               mRemoteTag.isNull() && // tag not set
               mRouteSet.isNull()) // have not yet set the route set
            {
                // Change this early dialog to a set up dialog.
                // The tag gets set in the 2xx response
                // so we need to update the URL
                message.getToUrl(mRemoteField);
                mRemoteField.getFieldParameter("tag", mRemoteTag);

                // Need to get the route set as well
                // Make sure the Request Method is allowed to set Record-Routes
                if(message.isRecordRouteAccepted())
                {
                    message.buildRouteField(&mRouteSet);
                }
            }
        }
    }
    else if(isTransactionRemotelyInitiated(messageCallId,
                                           messageFromTag,
                                           messageToTag))
    {
        int prevRemoteCseq = mLastRemoteCseq;

        // This message is part of a transaction initiated by
        // the callee/destination of the session
        if(cSeq > mLastRemoteCseq)
        {
            mLastRemoteCseq = cSeq;
        }

        if(cSeq >= mLastRemoteCseq)
        {
            // Always update the contact if it is set
            UtlString messageContact;
            // Get the Contact value, but as an addr-spec.
            if(message.getContactUri(0, &messageContact) &&
                !messageContact.isNull())
            {
                if(message.isResponse())
                {
                   mLocalContact.fromString(messageContact, TRUE);
                }
                else
                {
                   mRemoteContact.fromString(messageContact, TRUE);
                }
            }
        }

        // First transaction from the otherside
        if(cSeq == mLastRemoteCseq && prevRemoteCseq == -1)
        {
            // A response (e.g. NOTIFY) can come before we get the
            // successful response to the initial transaction
            if(!mLocalInitiatedDialog &&
               !message.isResponse() &&
               mRemoteTag.isNull()) // tag not set
            {
                // Change this early dialog to a set up dialog.
                // The tag gets set in the 2xx response
                // so we need to update the URL
                message.getFromUrl(mRemoteField);
                mRemoteField.getFieldParameter("tag", mRemoteTag);
            }
        }

        // First successful response from the local side
        if(cSeq == mLastRemoteCseq)
        {
            if(!mLocalInitiatedDialog &&
               message.isResponse() &&
               responseCode >= SIP_2XX_CLASS_CODE && // successful dialog setup
               responseCode < SIP_3XX_CLASS_CODE &&
               mLocalTag.isNull())
            {
                // Update the local tag
                message.getToUrl(mLocalField);
                mLocalField.getFieldParameter("tag", mLocalTag);
            }
        }
    }
}
示例#19
0
AuthPlugin::AuthResult
TransferControl::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 wrt modifying
                                    bool bSpiralingRequest, ///< request spiraling indication 
                                    UtlString&  reason      ///< rejection reason
                                    )
{
   AuthResult result = CONTINUE;
   
   // get the call-id to use in logging
   UtlString callId;
   request.getCallIdField(&callId);

   UtlString hostAddress;
   int hostPort;
   UtlString hostProtocols;
   //requestUri.getHostAddress(hostAddress);
   //request.getContactUri(0, &hostAddress);;
   request.getContactAddress(0, &hostAddress,&hostPort,&hostProtocols);
   if (DENY != priorResult)
   {
      if (method.compareTo(SIP_REFER_METHOD) == 0)
      {
         UtlString targetStr;
         if (request.getReferToField(targetStr))
         {
            Url target(targetStr, Url::NameAddr);  // parse the target URL

            UtlString targetMethod; 
            if (   Url::SipUrlScheme == target.getScheme() 
                /* REFER can create requests other than INVITE: we don't care about those       *
                 * so check that the method is INVITE or is unspecified (INVITE is the default) */
                && (   ! target.getUrlParameter(SIP_METHOD_URI_PARAMETER, targetMethod)
                    || (0==targetMethod.compareTo(SIP_INVITE_METHOD, UtlString::ignoreCase))
                    ))
            {
               if (id.isNull())
               {
                  // UnAuthenticated REFER. Do challenge the REFER to confirm the 
                  // identity of the transferor.  Note:  prior to XECS-2487, we used to challenge
                  // only the unauthenticated REFERs that didn't carry a Replaces header.
                  // The fix for XECS-2487 now requires that all unauthenticated REFERs
                  // be challenged so that consultative transfers get routed properly
                  // when user-based gateway section is used.  See tracker for the details
                  if (mpSipRouter->isLocalDomain(target))
                  {
			//White list of two servets to let Exchange REFER to sipXecs endpoints
			if (hostAddress.compareTo(server1, UtlString::ignoreCase) == 0 || hostAddress.compareTo(server2, UtlString::ignoreCase) == 0)
			{
			     Os::Logger::instance().log(FAC_AUTH, PRI_INFO, "TransferControl[%s]::authorizeAndModify "
					   "Whitelist host '%s' in call '%s'",
					   mInstanceName.data(),hostAddress.data(),callId.data()
					   );
			     result = ALLOW; //Whitelist matched so allow the transfer
			}else{
			     Os::Logger::instance().log(FAC_AUTH, PRI_INFO, "TransferControl[%s]::authorizeAndModify "
					   "challenging transfer in call '%s' from host '%s'",
					   mInstanceName.data(), callId.data(),hostAddress.data()
					   );
			     result = DENY; // we need an identity to attach to the Refer-To URI
			}
                  }
                  else
                  {
                     /*
                      * This is a transfer to a target outside our domain, so let it go
                      * unchallenged.  See XECS-806
                      */
                     Os::Logger::instance().log(FAC_AUTH, PRI_DEBUG, "TransferControl[%s]::authorizeAndModify "
                                   "allowing foriegn transfer in call '%s'",
                                   mInstanceName.data(), callId.data()
                                   );
                     // Add the References to the refer-to. Adding the callId field as a reference
                     // header (will be used in resulting INVITE) in the Refer-To provides us
                     // with enough information to be able to logically tie the calls together.
                     // Useful for CDR records.  
                     UtlString refcallId(callId);
                     refcallId.append(";rel=refer");
                     target.setHeaderParameter(SIP_REFERENCES_FIELD, refcallId.data());
                  
                     Os::Logger::instance().log(FAC_AUTH, PRI_DEBUG, "TransferControl[%s]::authorizeAndModify "
                                   "adding Reference field [%s] to refer-to",
                                   mInstanceName.data(), callId.data()
                                  );
                     request.setReferToField(target.toString().data());

                     result = ALLOW;
                  }
               }
               else
               {
                   UtlString  contactString;
                   request.getContactEntry(0, &contactString);
                   Url contactUri( contactString );
                   UtlString userId;
                   contactUri.getUserId(contactString);

            	   Os::Logger::instance().log(FAC_AUTH, PRI_DEBUG, "TransferControl::authorizeAndModify - Contact field is: %s ", contactString.data());

                   if (contactString != "callcontroller") {
                	   // Authenticated REFER
                	   // annotate the refer-to with the authenticated controller identity
                	   SipXauthIdentity controllerIdentity;
                	   controllerIdentity.setIdentity(id);
                	   controllerIdentity.encodeUri(target);

                	   // add the References to the refer-to.
                	   UtlString refcallId(callId);
                	   refcallId.append(";rel=refer");
                	   target.setHeaderParameter(SIP_REFERENCES_FIELD, refcallId.data());
                  
                	   Os::Logger::instance().log(FAC_AUTH, PRI_DEBUG, "TransferControl[%s]::authorizeAndModify "
                                "adding Reference field [%s] to refer-to",
                                mInstanceName.data(), callId.data()
                               );
                	   request.setReferToField(target.toString().data());
                   }
               }
            }
            else
            {
               Os::Logger::instance().log(FAC_AUTH, PRI_WARNING, "TransferControl[%s]::authorizeAndModify "
                             "unrecognized refer target '%s' for call '%s'",
                             mInstanceName.data(), targetStr.data(), callId.data()
                             );
            }
         }
         else
         {
            // REFER without a Refer-To header... incorrect, but just ignore it.
            Os::Logger::instance().log(FAC_AUTH, PRI_WARNING,
                          "TransferControl[%s]::authorizeAndModify "
                          "REFER method without Refer-To in call '%s'",
                          mInstanceName.data(), callId.data()
                          );
         }
      }
      else if (method.compareTo(SIP_INVITE_METHOD) == 0)
      {
         UtlString targetCallId;
         UtlString targetFromTag;
         UtlString targetToTag;

         if (request.getReplacesData(targetCallId, targetToTag, targetFromTag))
         {
            /*
             * This is an INVITE with Replaces: probably either the completion
             * of a call pickup or a consultative transfer.
             * In any case, it will not create a new call - just connect something
             * to an existing call - so we don't need to make any new authorization
             * decisions.
             */
            result = ALLOW;
         }
         else
         {
            // INVITE without Replaces: is not a transfer - ignore it.
         }
      }
      else
      {
         // neither REFER nor INVITE, so is not a transfer - ignore it.
      }
   }
   else
   {
      // Some earlier plugin already denied this - don't waste time figuring it out.
      Os::Logger::instance().log(FAC_AUTH, PRI_DEBUG, "TransferControl[%s]::authorizeAndModify "
                    "prior authorization result %s for call %s",
                    mInstanceName.data(), AuthResultStr(priorResult), callId.data()
                    );
   }
   
   return result;
}
示例#20
0
   void AppearanceTest()
   {
      instantiateAllTestFixtures( "appearance-groups1.xml",
                                  "subscription1",
                                  "credential1",
                                  "177.0.0.1:54140");

      UtlString sharedUri = "sip:[email protected]:54140";
      UtlString app1uri = "sip:127.0.0.1:45141";
      UtlString dialogHandle;

      // receive the reg-info subscribe
      SipMessage request;
      UtlString b;
      ssize_t l;
      while(getNextMessageFromAppearanceAgentUnderTest( request, 5 ))
      {
         request.getBytes(&b, &l);
         OsSysLog::add(FAC_RLS, PRI_DEBUG, "got message %s", b.data());
         UtlString method;
         request.getRequestMethod(&method);
         if(!request.isResponse() &&
            0 == method.compareTo(SIP_SUBSCRIBE_METHOD) )
         {
            // Accept the Subscription, regardless of whether it for a 'dialog' or 'reg' event
            // in order to stop retransmissions
            SipMessage regResponse;
            regResponse.setResponseData(&request, 202, "Accepted", app1uri);
            SipMessage * dispatchedMessage = new SipMessage(regResponse);
            dispatchedMessage->getBytes(&b, &l);
            OsSysLog::add(FAC_RLS, PRI_DEBUG, "sent message %s", b.data());
            pAppearanceAgentUnderTest->mServerUserAgent.dispatch(dispatchedMessage);

            // Deal with the two events separately
            UtlString eventField;
            request.getEventField(eventField);
            if(0 == eventField.compareTo("reg"))
            {
               UtlString contactInfo;
               request.getContactUri(0, &contactInfo);
               UtlString callid;
               request.getCallIdField(&callid);
               int cseq;
               request.getCSeqField(&cseq, NULL);

               Url toField;
               regResponse.getToUrl(toField);

               SipMessage regNotify;
               regNotify.setNotifyData(&request, 1, "", "", "reg");
               UtlString regInfo ("<?xml version=\"1.0\"?>\r\n"
                                 "<reginfo xmlns=\"urn:ietf:params:xml:ns:reginfo\" "
                                 "xmlns:gr=\"urn:ietf:params:xml:ns:gruuinfo\" version=\"911\" state=\"full\">\r\n"
                                 "   <registration aor=\"sip:[email protected]:54140\" id=\"sip:[email protected]:54140\" state=\"active\">\r\n "
                                 "      <contact id=\"sip:[email protected]:54140@@&lt;");
               regInfo.append(contactInfo);
               regInfo.append("&gt;\" state=\"active\" event=\"registered\" q=\"1\" callid=\"");
               regInfo.append(callid);
               regInfo.append("\" cseq=\"");
               regInfo.appendNumber(cseq);
               regInfo.append("\">\r\n");
               regInfo.append("<uri>");
               regInfo.append(app1uri);
               regInfo.append("</uri>");
               regInfo.append("      </contact>\r\n"
                              "   </registration>\r\n"
                              "</reginfo>");
               HttpBody * newBody = new HttpBody (regInfo, strlen(regInfo), "application/reginfo+xml");
               regNotify.setContentType("application/reginfo+xml");
               regNotify.setBody(newBody);

               // Set the From field the same as the to field from the 202 response, as it
               // contains the dialog identifying to tags
               regNotify.setRawFromField(toField.toString().data());
               sendToAppearanceAgentUnderTest( regNotify );
               regNotify.getBytes(&b, &l);
               OsSysLog::add(FAC_RLS, PRI_DEBUG, "sent reg NOTIFY to AppAgent");
               OsSysLog::add(FAC_RLS, PRI_DEBUG, "sent message %s", b.data());
            }
            else if (0 == eventField.compareTo(SLA_EVENT_TYPE))
             {
                // should send empty NOTIFY, but no one will care
                // save dialogHandle for this subscription/Appearance (ignore retransmissions)
                if (dialogHandle.isNull())
                {
                   SipMessage fake(b);
                   fake.getDialogHandle(dialogHandle);
                   OsSysLog::add(FAC_RLS, PRI_DEBUG, "got SUBSCRIBE(sla) request: dialogHandle %s", dialogHandle.data());
                }
             }
         }
      }

      CPPUNIT_ASSERT( !dialogHandle.isNull() );
      OsSysLog::add(FAC_RLS, PRI_DEBUG, "we now have an Appearance - test it");
      AppearanceGroup* pAppGroup = pAppearanceAgentUnderTest->getAppearanceGroupSet().
         findAppearanceGroup(sharedUri);
      CPPUNIT_ASSERT( pAppGroup );

      Appearance* pApp = pAppGroup->findAppearance(dialogHandle);
      CPPUNIT_ASSERT( pApp );
      ASSERT_STR_EQUAL( app1uri.data(), pApp->getUri()->data() );

      // test adding a new dialog
      const char* dialogEventString =
            "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
            "<dialog-info xmlns=\"urn:ietf:params:xml:ns:dialog-info\" version=\"0\" state=\"partial\" entity=\"sip:[email protected]:54140\">\n"
            "<dialog id=\"1\" call-id=\"[email protected]\" local-tag=\"264460498\" remote-tag=\"1c10982\" direction=\"recipient\">\n"
            "<state>confirmed</state>\n"
            "<local>\n"
            "<identity>[email protected]:5120</identity>\n"
            "<target uri=\"sip:[email protected]:5120\">\n"
            "<param pname=\"x-line-id\" pval=\"0\"/>\n"
            "<param pname=\"+sip.rendering\" pval=\"yes\"/>\n"
            "</target>\n"
            "</local>\n"
            "<remote>\n"
            "<identity>[email protected]</identity>\n"
            "</remote>\n"
            "</dialog>\n"
            "</dialog-info>\n"
            ;
      SipDialogEvent dialogEvent(dialogEventString);
      bool bFullContentChanged = false;
      bool bPartialContentChanged = pApp->updateState(&dialogEvent, bFullContentChanged);
      CPPUNIT_ASSERT(bPartialContentChanged);
      CPPUNIT_ASSERT(bFullContentChanged);
      pApp->dumpState();
      CPPUNIT_ASSERT(pApp->appearanceIsBusy());
      CPPUNIT_ASSERT(pApp->appearanceIdIsSeized("0"));
      CPPUNIT_ASSERT(!pApp->appearanceIdIsSeized("1"));

      // simulate user putting the call on hold
      const char* dialogEventString2 =
            "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
            "<dialog-info xmlns=\"urn:ietf:params:xml:ns:dialog-info\" version=\"0\" state=\"partial\" entity=\"sip:[email protected]:54140\">\n"
            "<dialog id=\"1\" call-id=\"[email protected]\" local-tag=\"264460498\" remote-tag=\"1c10982\" direction=\"recipient\">\n"
            "<state>confirmed</state>\n"
            "<local>\n"
            "<identity>[email protected]:5120</identity>\n"
            "<target uri=\"sip:[email protected]:5120\">\n"
            "<param pname=\"x-line-id\" pval=\"0\"/>\n"
            "<param pname=\"+sip.rendering\" pval=\"no\"/>\n"
            "</target>\n"
            "</local>\n"
            "<remote>\n"
            "<identity>[email protected]</identity>\n"
            "</remote>\n"
            "</dialog>\n"
            "</dialog-info>\n"
            ;
      SipDialogEvent dialogEvent2(dialogEventString2);
      bPartialContentChanged = pApp->updateState(&dialogEvent2, bFullContentChanged);
      CPPUNIT_ASSERT(bPartialContentChanged);
      CPPUNIT_ASSERT(bFullContentChanged);
      pApp->dumpState();
      CPPUNIT_ASSERT(!pApp->appearanceIsBusy());
      CPPUNIT_ASSERT(pApp->appearanceIdIsSeized("0"));
      CPPUNIT_ASSERT(!pApp->appearanceIdIsSeized("1"));

      // test MESSAGE debug handling
      const char* message =
         "MESSAGE sip:[email protected]:54140 SIP/2.0\r\n"
         "From: <sip:[email protected]>;tag=17211757-9E4FBD78\r\n"
         "To: <sip:[email protected]:54140>\r\n"
         "CSeq: 1 MESSAGE\r\n"
         "Call-ID: 51405734-b9be4835-dcd9d196\r\n"
         "Contact: <sip:[email protected]>\r\n"
         "Allow: INVITE, ACK, BYE, CANCEL, OPTIONS, INFO, MESSAGE, SUBSCRIBE, NOTIFY, PRACK, UPDATE, REFER\r\n"
         "Event: dialog\r\n"
         "User-Agent: UnitTest\r\n"
         "Accept-Language: en\r\n"
         "Accept: application/dialog-info+xml\r\n"
         "Max-Forwards: 70\r\n"
         "Expires: 3600\r\n"
         "Content-Length: 0\r\n"
         "\r\n";

      // send the MESSAGE
      SipMessage messageRequest( message, strlen( message ) );
      CPPUNIT_ASSERT( sendToAppearanceAgentUnderTest( messageRequest ) );

      // receive the 200 OK response
      SipMessage response;
      CPPUNIT_ASSERT( getNextMessageFromAppearanceAgentUnderTest( response, 5 ) );
      CPPUNIT_ASSERT( response.isResponse() );
      CPPUNIT_ASSERT( response.getResponseStatusCode() == SIP_OK_CODE );
   }
示例#21
0
UtlBoolean SipXProxyCseObserver::handleMessage(OsMsg& eventMessage)
{
   int msgType = eventMessage.getMsgType();
   switch (msgType)
   {
   case OsMsg::OS_EVENT:
      switch (eventMessage.getMsgSubType())
      {
      case OsEventMsg::NOTIFY:
         if (mpWriter)
         {
            mpWriter->flush();
         }
         break;
      }
      break ;
      
   case OsMsg::PHONE_APP:
   {
      SipMessage* sipMsg;

      if(SipMessageEvent::TRANSPORT_ERROR == ((SipMessageEvent&)eventMessage).getMessageStatus())
      {
         OsSysLog::add(FAC_SIP, PRI_ERR,
                       "SipXProxyCseObserver::handleMessage transport error");
      }
      else if((sipMsg = (SipMessage*)((SipMessageEvent&)eventMessage).getMessage()))
      {
         UtlString method;
         int       rspStatus = 0;
         UtlString rspText;
         UtlString contact;
         UtlString toTag;
         
         enum 
            {
               UnInteresting,
               aCallRequest,
               aCallSetup,
               aCallFailure,
               aCallEnd,
               aCallTransfer
            } thisMsgIs = UnInteresting;
         
         Url toUrl;
         sipMsg->getToUrl(toUrl);
         // explicitly, an INVITE Request
         toUrl.getFieldParameter("tag", toTag);

         if (!sipMsg->isResponse())
         {

            // sipMsg is a Request
            sipMsg->getRequestMethod(&method);

            if (0==method.compareTo(SIP_INVITE_METHOD, UtlString::ignoreCase))
            {
               if (toTag.isNull())
               {
                  thisMsgIs = aCallRequest;
               }
            }
            else if (0==method.compareTo(SIP_REFER_METHOD, UtlString::ignoreCase))
            {
               thisMsgIs = aCallTransfer;
               sipMsg->getContactEntry(0, &contact);               
            }
            else if (0==method.compareTo(SIP_BYE_METHOD, UtlString::ignoreCase))
            {
               thisMsgIs = aCallEnd; // no additional information needed
            }
            else
            {
               // other request methods are not interesting
            }
         }
         else // this is a response
         {
            int seq;
            if (sipMsg->getCSeqField(&seq, &method)) // get the method out of cseq field
            {
               if (0==method.compareTo(SIP_INVITE_METHOD, UtlString::ignoreCase))
               {
                  // Responses to INVITES are handled differently based on whether
                  // or not the INVITE is dialog-forming.  If dialog-forming,
                  // any final response above 400 is considered a failure for CDR
                  // purposes.  If not dialog-forming, then any final response above 400
                  // except 401 Unauthorized, 407 Proxy Authentication Required and 
                  // 408 Request Timeout will terminate.  If we're in a dialog then
            	  // only 408 (Request Timeout) and 481 (Call/Transaction does not exist)
            	  // will terminate the dialog.

                  rspStatus = sipMsg->getResponseStatusCode();
                  if (rspStatus >= SIP_4XX_CLASS_CODE) // any failure
                  {
                     // a failure code - this is a potential CallFailure - Call Resolver will determine.
                     thisMsgIs = aCallFailure;
                     sipMsg->getResponseStatusText(&rspText);
                  }
                  else if (   ( rspStatus >= SIP_2XX_CLASS_CODE )
                           && ( rspStatus <  SIP_3XX_CLASS_CODE )
                           )
                  {
                     thisMsgIs = aCallSetup;
                     sipMsg->getContactEntry(0, &contact);
                  }
               }
               else
               {
                  // responses to non-INVITES are not interesting
               }
            }
            else
            {
               OsSysLog::add(FAC_SIP, PRI_ERR, "SipXProxyCseObserver - no Cseq in response");
            }
         }

#        ifdef LOG_DEBUG
         OsSysLog::add(FAC_SIP, PRI_DEBUG, "SipXProxyCseObserver message is %s",
                       (  thisMsgIs == UnInteresting ? "UnInteresting"
                        : thisMsgIs == aCallEnd      ? "a Call End"
                        : thisMsgIs == aCallFailure  ? "a Call Failure"
                        : thisMsgIs == aCallRequest  ? "a call Request"      
                        : thisMsgIs == aCallSetup    ? "a Call Setup"
                        : thisMsgIs == aCallTransfer ? "a Call Transfer"
                        : "BROKEN"
                        )); 
#        endif

         if (thisMsgIs != UnInteresting)
         {
            // collect the sequence data
            mSequenceNumber++;
            
            OsTime timeNow;
            OsDateTime::getCurTime(timeNow); 

            // collect the dialog information
            UtlString callId;
            sipMsg->getCallIdField(&callId);
         
            Url toUrl;
            sipMsg->getToUrl(toUrl);
            UtlString toTag;
            toUrl.getFieldParameter("tag", toTag);

            Url fromUrl;
            sipMsg->getFromUrl(fromUrl);
            UtlString fromTag;
            fromUrl.getFieldParameter("tag", fromTag);

            // collect the To and From
            UtlString toField;
            sipMsg->getToField(&toField);
            
            UtlString fromField;
            sipMsg->getFromField(&fromField);

            UtlString referTo;
            UtlString referredBy;
            UtlString requestUri;
            sipMsg->getReferToField(referTo);
            sipMsg->getReferredByField(referredBy);   
            sipMsg->getRequestUri(&requestUri);
            
            UtlString responseMethod;
            int cseqNumber;
            sipMsg->getCSeqField(&cseqNumber, &responseMethod);            

            // generate the call state event record
            if (mpBuilder)
            {
               switch (thisMsgIs)
               {
               case aCallRequest:
                  mpBuilder->callRequestEvent(mSequenceNumber, timeNow, contact);
                  break;
                  
               case aCallSetup:
                  mpBuilder->callSetupEvent(mSequenceNumber, timeNow, contact);
                  break;
   
               case aCallFailure:
                  mpBuilder->callFailureEvent(mSequenceNumber, timeNow, rspStatus, rspText);
                  break;
                  
               case aCallEnd:
                  mpBuilder->callEndEvent(mSequenceNumber, timeNow);
                  break;
                  
               case aCallTransfer:
                  mpBuilder->callTransferEvent(mSequenceNumber, timeNow, 
                                               contact, referTo, referredBy, requestUri);
                  break;   
   
               default:
                  // shouldn't be possible to get here
                  OsSysLog::add(FAC_SIP, PRI_ERR, "SipXProxyCseObserver invalid thisMsgIs");
                  break;
               }
   
               mpBuilder->addCallData(cseqNumber, callId, fromTag, toTag, fromField, toField);
               UtlString via;
               for (int i=0; sipMsg->getViaField(&via, i); i++)
               {
                  mpBuilder->addEventVia(via);
               }
   
               mpBuilder->completeCallEvent();
                 
               // get the completed record
               UtlString event;
               mpBuilder->finishElement(event);
               
               if (mpWriter)
               {
                 mpWriter->writeLog(event.data());
               }
            }
            else
            {
               OsSysLog::add(FAC_SIP, PRI_ERR, "SipXProxyCseObserver - no CallStateEventBuilder!");               
            }
         }
      }
      else
      {
         OsSysLog::add(FAC_SIP, PRI_ERR, "SipXProxyCseObserver getMessage returned NULL");
      }
   }
   break;
   
   default:
   {
      OsSysLog::add(FAC_SIP, PRI_ERR, "SipXProxyCseObserver invalid message type %d", msgType );
   }
   } // end switch (msgType)
   
   return(TRUE);
}
示例#22
0
// Send a NOTIFY to all subscribers to the given resourceId and eventTypeKey.
UtlBoolean SipSubscribeServer::notifySubscribers(const char* resourceId,
                                                 const char* eventTypeKey,
                                                 const char* eventType,
                                                 const char* reason)
{
   OsSysLog::add(FAC_SIP, PRI_DEBUG,
                 "SipSubscribeServer::notifySubscribers resourceId '%s', eventTypeKey '%s', eventType '%s', reason '%s'",
                 resourceId, eventTypeKey, eventType,
                 reason ? reason : "[null]");
    UtlBoolean notifiedSubscribers = FALSE;
    UtlString eventName(eventType);

    lockForRead();
    SubscribeServerEventData* eventData =
       dynamic_cast <SubscribeServerEventData*>
           (mEventDefinitions.find(&eventName));

    // Get the event-specific info to find subscriptions interested in
    // this content.
    if (eventData)
    {
        int numSubscriptions = 0;
        SipMessage** notifyArray = NULL;
        UtlString** acceptHeaderValuesArray = NULL;

        // Select the subscriptionChange value to describe what we're doing
        // with the subscriptions.
        enum SipSubscriptionMgr::subscriptionChange change;

        {
           // Select the correct format for generating the Subscription-State value.
           UtlString* formatp = NULL; // We may need a temp UtlString.
           const char* format;

           if (reason == NULL)
           {
              format = "active;expires=%ld";
              change = SipSubscriptionMgr::subscriptionContinues;
           }
           else if (strcmp(reason, terminationReasonSilent) == 0)
           {
              // Do not admit that the subscription is ending.
              format = "active;expires=%ld";
              change = SipSubscriptionMgr::subscriptionTerminatedSilently;
           }
           else if (strcmp(reason, terminationReasonNone) == 0)
           {
              format = "terminated";
              change = SipSubscriptionMgr::subscriptionTerminated;
           }
           else
           {
              // Allocate a UtlString and assemble the Subscription-State format in it.
              formatp = new UtlString();
              formatp->append("terminated;reason=");
              formatp->append(reason);
              format = formatp->data();
              change = SipSubscriptionMgr::subscriptionTerminated;
           }

           // Construct a NOTIFY (without body) for each subscription, containing
           // the dialog-related information about each subscription.
           eventData->mpEventSpecificSubscriptionMgr->
              createNotifiesDialogInfo(resourceId,
                                       eventTypeKey,
                                       format,
                                       numSubscriptions,
                                       acceptHeaderValuesArray,
                                       notifyArray);

           OsSysLog::add(FAC_SIP, PRI_DEBUG,
                         "SipSubscribeServer::notifySubscribers numSubscriptions for '%s' = %d",
                         resourceId, numSubscriptions);

           // Free the temporary UtlString, if necessary.
           if (formatp)
           {
              delete formatp;
           }
        }

        // For each NOTIFY, add the subscription-related information and then
        // send it.
        for (int notifyIndex = 0;
             notifyIndex < numSubscriptions;
             notifyIndex++)
        {
            SipMessage* notify = notifyArray[notifyIndex];

            // Check to see if the dialog information could be added.
            // (The subscription might have been destroyed between when
            // it was decided to respond to it, and when the dialog information
            // was retrieved.)
            UtlString callId;
            notify->getCallIdField(&callId);
            if (!callId.isNull())
            {
               if (change != SipSubscriptionMgr::subscriptionTerminatedSilently)
               {
                  // Fill in the NOTIFY request body/content
                  eventData->mpEventSpecificHandler->
                     getNotifyContent(resourceId,
                                      eventTypeKey,
                                      eventType,
                                      *(eventData->mpEventSpecificContentMgr),
                                      *(acceptHeaderValuesArray[notifyIndex]),
                                      *notify,
                                      eventData->mEventSpecificFullState,
                                      NULL);

                  // Call the application callback to edit the NOTIFY
                  // content if that is required for this event type.
                  // Also gets 'version' (if relevant) and 'savedEventTypeKey'.
                  int version;
                  UtlString savedEventTypeKey;
                  eventData->mpEventSpecificSubscriptionMgr->
                     updateNotifyVersion(eventData->mpEventSpecificContentVersionCallback,
                                         *notify,
                                         version,
                                         savedEventTypeKey);

                  // Update the saved record of the NOTIFY CSeq and the
                  // XML version number for the specified savedEventTypeKey,
                  // as needed by the subscription manager.
                  // In practice, this is only used by SipPersistentSubscriptionMgr
                  // to write the NOTIFY Cseq and XML version into the IMDB.
                  eventData->mpEventSpecificSubscriptionMgr->
                     updateVersion(*notify, version, savedEventTypeKey);

                  // Set the Contact header.
                  setContact(notify);

                  // Send the NOTIFY request.
                  eventData->mpEventSpecificUserAgent->send(*notify);
               }

               if (change != SipSubscriptionMgr::subscriptionContinues)
               {
                  // Remove the record of the subscription.
                  UtlString dialogHandle;
                  notify->getDialogHandle(dialogHandle);
                  eventData->mpEventSpecificSubscriptionMgr->
                     endSubscription(dialogHandle, change);
               }
            }
        }

        // Free the NOTIFY requests and accept header field values.
        eventData->mpEventSpecificSubscriptionMgr->
           freeNotifies(numSubscriptions,
                        acceptHeaderValuesArray,
                        notifyArray);
    }
    // event type not enabled
    else
    {
        OsSysLog::add(FAC_SIP, PRI_ERR,
                      "SipSubscribeServer::notifySubscribers "
                      "event type: %s not enabled - "
                      "Why are we seeing a callback for this?",
            eventName.data());
    }

    unlockForRead();

    return notifiedSubscribers;
}
示例#23
0
/// Terminate all subscriptions and accept no new ones.
void SipSubscribeServer::shutdown(const char* reason)
{
   // If reason is NULL, use the default reason established by the constructor.
   if (!reason)
   {
      reason = mDefaultTermination;
   }

   OsSysLog::add(FAC_SIP, PRI_DEBUG,
                 "SipSubscribeServer::shutdown reason '%s'",
                 reason ? reason : "[null]");                 

   lockForRead();

   // Select the subscriptionChange value to describe what we're doing
   // with the subscriptions.
   enum SipSubscriptionMgr::subscriptionChange change;

   // Select the correct format for generating the Subscription-State value.
   UtlString* formatp = NULL; // We may need a temp UtlString.
   const char* format;

   if (reason == NULL)
   {
      format = "active;expires=%ld";
      change = SipSubscriptionMgr::subscriptionContinues;
   }
   else if (strcmp(reason, terminationReasonSilent) == 0)
   {
      // Do not admit that the subscription is ending.
      format = "active;expires=%ld";
      change = SipSubscriptionMgr::subscriptionTerminatedSilently;
   }
   else if (strcmp(reason, terminationReasonNone) == 0)
   {
      format = "terminated";
      change = SipSubscriptionMgr::subscriptionTerminated;
   }
   else
   {
      // Allocate a UtlString and assemble the Subscription-State format in it.
      formatp = new UtlString();
      formatp->append("terminated;reason=");
      formatp->append(reason);
      format = formatp->data();
      change = SipSubscriptionMgr::subscriptionTerminated;
   }

   // For each event type that is registered, delete all the subscriptions.

   UtlHashBagIterator event_itor(mEventDefinitions);
   SubscribeServerEventData* eventData;
   while ((eventData =
              dynamic_cast <SubscribeServerEventData*> (event_itor())))
   {
      // Unregister interest in SUBSCRIBE requests and NOTIFY
      // responses for this event type, so we do not service new subscriptions.

      eventData->mpEventSpecificUserAgent->removeMessageObserver(*(getMessageQueue()));
      
      int numSubscriptions = 0;
      SipMessage** notifyArray = NULL;
      UtlString** acceptHeaderValuesArray = NULL;
      UtlString** resourceIdArray = NULL;
      UtlString** eventTypeKeyArray = NULL;

      // :TODO: The four situations where NOTIFYs are generated should
      // be factored into a series of methods in
      // mpEventSpecificSubscriptionMgr that generate NOTIFYs
      // sequentially, and for each NOTIFY, call a common service
      // method that does the remaining operations and sends the
      // NOTIFY.

      // Construct a NOTIFY (without body) for every subscription, containing
      // the dialog-related information about each subscription.
      eventData->mpEventSpecificSubscriptionMgr->
         createNotifiesDialogInfoEvent(static_cast <const UtlString&> (*eventData),
                                       format,
                                       numSubscriptions,
                                       acceptHeaderValuesArray,
                                       notifyArray,
                                       resourceIdArray,
                                       eventTypeKeyArray);

      OsSysLog::add(FAC_SIP, PRI_DEBUG,
                    "SipSubscribeServer::shutdown eventType = '%s', numSubscriptions = %d",
                    eventData->data(), numSubscriptions);

      // For each NOTIFY, add the subscription-related information and then
      // send it.
      for (int notifyIndex = 0;
           notifyIndex < numSubscriptions;
           notifyIndex++)
      {
         SipMessage* notify = notifyArray[notifyIndex];

         // Check to see if the dialog information could be added.
         // (The subscription might have been destroyed between when
         // it was decided to respond to it, and when the dialog information
         // was retrieved.)
         UtlString callId;
         notify->getCallIdField(&callId);
         if (!callId.isNull())
         {
            if (change != SipSubscriptionMgr::subscriptionTerminatedSilently)
            {
               // Fill in the NOTIFY request body/content
               eventData->mpEventSpecificHandler->
                  getNotifyContent(*(resourceIdArray[notifyIndex]),
                                   *(eventTypeKeyArray[notifyIndex]),
                                   *eventData,
                                   *(eventData->mpEventSpecificContentMgr),
                                   *(acceptHeaderValuesArray[notifyIndex]),
                                   *notify,
                                   eventData->mEventSpecificFullState,
                                   NULL);

               // Call the application callback to edit the NOTIFY
               // content if that is required for this event type.
               // Also gets 'version' (if relevant) and 'savedEventTypeKey'.
               int version;
               UtlString savedEventTypeKey;
               eventData->mpEventSpecificSubscriptionMgr->
                  updateNotifyVersion(eventData->mpEventSpecificContentVersionCallback,
                                      *notify,
                                      version,
                                      savedEventTypeKey);

               // Set the Contact header.
               setContact(notify);

               // Send the NOTIFY request.
               eventData->mpEventSpecificUserAgent->send(*notify);
            }

            // Remove the record of the subscription.
            UtlString dialogHandle;
            notify->getDialogHandle(dialogHandle);
            eventData->mpEventSpecificSubscriptionMgr->
               endSubscription(dialogHandle, change);
         }
      }

      // Free the NOTIFY requests and accept header field values.
      SipSubscriptionMgr::freeNotifies(numSubscriptions,
                                       acceptHeaderValuesArray,
                                       notifyArray);

      // Free the resource and event type arrays.
      for (int index = 0;
           index < numSubscriptions;
           index++)
      {
         delete resourceIdArray[index];
         delete eventTypeKeyArray[index];
      }
      delete[] resourceIdArray;
      delete[] eventTypeKeyArray;

      // Remove eventData from mEventDefinitions.
      mEventDefinitions.removeReference(eventData);
      delete eventData;
   }

   unlockForRead();

   // Free the temporary UtlString, if necessary.
   if (formatp)
   {
      delete formatp;
   }

   lockForWrite();
   mEventDefinitions.destroyAll();
   unlockForWrite();
}
UtlBoolean SipXProxyCseObserver::handleMessage(OsMsg& eventMessage)
{
   int msgType = eventMessage.getMsgType();
   switch (msgType)
   {
   case OsMsg::OS_EVENT:
      switch (eventMessage.getMsgSubType())
      {
      case OsEventMsg::NOTIFY:
         if (mpWriter)
         {
            mpWriter->flush();
         }
         break;
      }
      break ;
      
   case OsMsg::PHONE_APP:
   {
      SipMessage* sipMsg;

      if(SipMessageEvent::TRANSPORT_ERROR == ((SipMessageEvent&)eventMessage).getMessageStatus())
      {
         Os::Logger::instance().log(FAC_SIP, PRI_ERR,
                       "SipXProxyCseObserver::handleMessage transport error");
      }
      else if((sipMsg = (SipMessage*)((SipMessageEvent&)eventMessage).getMessage()))
      {
         UtlString method;
         int       rspStatus = 0;
         UtlString rspText;
         UtlString contact;
         UtlString toTag;
         
         enum 
            {
               UnInteresting,
               aCallRequest,
               aCallSetup,
               aCallFailure,
               aCallEnd,
               aCallTransfer
            } thisMsgIs = UnInteresting;
         
         Url toUrl;

         sipMsg->getToUrl(toUrl);

         // explicitly, an INVITE Request
         toUrl.getFieldParameter("tag", toTag);

         if (!sipMsg->isResponse())
         {

            // sipMsg is a Request
            sipMsg->getRequestMethod(&method);

            if (0==method.compareTo(SIP_INVITE_METHOD, UtlString::ignoreCase))
            {
               if (toTag.isNull())
               {
                  sipMsg->getContactEntry(0, &contact);               
                  thisMsgIs = aCallRequest;
               }
            }
            else if (0==method.compareTo(SIP_REFER_METHOD, UtlString::ignoreCase))
            {
               thisMsgIs = aCallTransfer;
               sipMsg->getContactEntry(0, &contact);               
            }
            else if (0==method.compareTo(SIP_BYE_METHOD, UtlString::ignoreCase))
            {
               thisMsgIs = aCallEnd; // no additional information needed
            }
            else
            {
               // other request methods are not interesting
            }
         }
         else // this is a response
         {
            int seq;
            if (sipMsg->getCSeqField(&seq, &method)) // get the method out of cseq field
            {
               if (0==method.compareTo(SIP_INVITE_METHOD, UtlString::ignoreCase))
               {
                  // Responses to INVITES are handled differently based on whether
                  // or not the INVITE is dialog-forming.  If dialog-forming,
                  // any final response above 400 is considered a failure for CDR
                  // purposes.  If not dialog-forming, then any final response above 400
                  // except 401 Unauthorized, 407 Proxy Authentication Required and 
                  // 408 Request Timeout will terminate.  If we're in a dialog then
            	  // only 408 (Request Timeout) and 481 (Call/Transaction does not exist)
            	  // will terminate the dialog.

                  rspStatus = sipMsg->getResponseStatusCode();
                  if (rspStatus >= SIP_4XX_CLASS_CODE) // any failure
                  {
                     // a failure code - this is a potential CallFailure - Call Resolver will determine.
                     thisMsgIs = aCallFailure;
                     sipMsg->getResponseStatusText(&rspText);
                  }
                  else if (   ( rspStatus >= SIP_2XX_CLASS_CODE )
                           && ( rspStatus <  SIP_3XX_CLASS_CODE )
                           )
                  {
                     thisMsgIs = aCallSetup;
                     sipMsg->getContactEntry(0, &contact);
                  }
               }
               else
               {
                  // responses to non-INVITES are not interesting
               }
            }
            else
            {
               Os::Logger::instance().log(FAC_SIP, PRI_ERR, "SipXProxyCseObserver - no Cseq in response");
            }
         }

#        ifdef LOG_DEBUG
         Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "SipXProxyCseObserver message is %s",
                       (  thisMsgIs == UnInteresting ? "UnInteresting"
                        : thisMsgIs == aCallEnd      ? "a Call End"
                        : thisMsgIs == aCallFailure  ? "a Call Failure"
                        : thisMsgIs == aCallRequest  ? "a call Request"      
                        : thisMsgIs == aCallSetup    ? "a Call Setup"
                        : thisMsgIs == aCallTransfer ? "a Call Transfer"
                        : "BROKEN"
                        )); 
#        endif

         if (thisMsgIs != UnInteresting)
         {
            // collect the sequence data
            mSequenceNumber++;
            
            OsTime timeNow;
            OsDateTime::getCurTime(timeNow); 

            // collect the dialog information
            UtlString callId;
            sipMsg->getCallIdField(&callId);
         
            Url toUrl;
            sipMsg->getToUrl(toUrl);
            UtlString toTag;
            toUrl.getFieldParameter("tag", toTag);

            Url fromUrl;
            sipMsg->getFromUrl(fromUrl);
            UtlString fromTag;
            fromUrl.getFieldParameter("tag", fromTag);

            // collect the To and From
            UtlString toField;
            sipMsg->getToField(&toField);
            
            UtlString fromField;
            sipMsg->getFromField(&fromField);


            // collect the branch Id (i.e. transaction id) and via count.
            UtlString viaValue;
            int viaCount;
            UtlString branchId;
            viaCount = sipMsg->getCountHeaderFields(SIP_VIA_FIELD);
            viaCount = viaCount + sipMsg->getCountHeaderFields(SIP_SHORT_VIA_FIELD);
            if ( sipMsg->getViaFieldSubField( &viaValue, 0 ) ) {
               sipMsg->getViaTag( viaValue, "branch", branchId );
            }
            UtlString referTo;
            UtlString referredBy;
            UtlString requestUri;
            UtlString references;
            UtlString replaces_callId;
            UtlString replaces_toTag;
            UtlString replaces_fromTag;
            UtlString matchingIdentityHeader;
            SipXauthIdentity sipxIdentity(*sipMsg, matchingIdentityHeader, true,SipXauthIdentity::allowUnbound);

            sipMsg->getReferToField(referTo);
            sipMsg->getReferredByField(referredBy);   
            sipMsg->getRequestUri(&requestUri);
            sipMsg->getReferencesField(&references);
            if (sipMsg->getReplacesData(replaces_callId, replaces_toTag, replaces_fromTag)) {
               if (references.length() != 0) {
                  references.append(",");
               }
               references.append(replaces_callId);
               references.append(";rel=xfer");
            }
            
            UtlString responseMethod;
            UtlString calleeRoute;

            int cseqNumber;
            sipMsg->getCSeqField(&cseqNumber, &responseMethod);            

            BranchTimePair* callIdBranchIdTime;
            // generate the call state event record
            if (mpBuilder)
            {
               UtlString identity;
               UtlString recordRoute;
               bool routeFound = false;
               bool paiPresent = false;

               switch (thisMsgIs)
               {
               case aCallRequest:
                 
                  if (sipxIdentity.getIdentity(identity)) {
                     paiPresent = true;
                  }

                  if ( branchId && branchId.data() ) {
                     mCallTransMutex.acquire();
                     unsigned long currentTime = OsDateTime::getSecsSinceEpoch();

                     UtlString* keyCallId = new UtlString(callId);
                     BranchTimePair* valBranchTimePair = new BranchTimePair(branchId.data(), &currentTime, &paiPresent);
                     if (NULL == mCallTransMap.insertKeyAndValue(keyCallId, valBranchTimePair) ) {
                        // Unable to add callId to map so it must already be present.  
                        delete keyCallId;
                        delete valBranchTimePair;

                        // Check if the paiPresent value is set to true or not.
                        // If not set and we now have a PAI for this call, set it and generate another call request state event 
                        // with this info. Otherwise skip over.
                        if ( paiPresent ) {
                           callIdBranchIdTime = (BranchTimePair*) mCallTransMap.findValue(&callId);
                           if ( callIdBranchIdTime && (*callIdBranchIdTime->getPaiPresent() == false) ) {
                              // need to generate another call request event in order to state originator is internal.
                              callIdBranchIdTime->setPaiPresent(&paiPresent);
                           }
                           else {
                              mCallTransMutex.release();
                              return(TRUE);
                           }
                        }
                        else {
                           mCallTransMutex.release();
                           return(TRUE);
                        }
                     }
                     mCallTransMutex.release();
                  }
                  mpBuilder->callRequestEvent(mSequenceNumber, timeNow, contact, references, branchId, viaCount, paiPresent);
                  break;
                  
               case aCallSetup:
                  // Clear out from the map only if rspStatus is higher than 200 as its possible to receive multiple 200 messages.
                  // If the response is 200, the call in the map will be cleared out when the call ends.
                  mCallTransMutex.acquire();
                  callIdBranchIdTime = (BranchTimePair*) mCallTransMap.findValue(&callId);
                  if ( callIdBranchIdTime && (0 == branchId.compareTo(callIdBranchIdTime)) ) {
                     if ( rspStatus > SIP_2XX_CLASS_CODE ) {
                           mCallTransMap.destroy(&callId);
                        }
                     mCallTransMutex.release();
                  }
                  else
                  {
                     // CallId/BranchId are either not found or doesn't match.  Not a final response.
                     mCallTransMutex.release();
                     return(TRUE);
                  }
                  for (int rrNum = 0;
                       (!routeFound && sipMsg->getRecordRouteUri(rrNum, &recordRoute));
                       rrNum++
                  )
                  {
                     Url recordRouteUrl(recordRoute);
                     if (mpSipUserAgent->isMyHostAlias(recordRouteUrl)) {
                        // This is a record route for our proxy, extract Call tags if they exist.
                        recordRouteUrl.getUrlParameter(SIP_SIPX_CALL_DEST_FIELD, calleeRoute, 0);
                        routeFound = true;
                     }
                  }
                  mpBuilder->callSetupEvent(mSequenceNumber, timeNow, contact, calleeRoute, branchId, viaCount);
                  break;
   
               case aCallFailure:
                  // Failure case means that the response code is > 400.  If the call is found
                  // in the map, then this is a final response.  Delete from the map and build an event.
                  mCallTransMutex.acquire();
                  callIdBranchIdTime = (BranchTimePair*) mCallTransMap.findValue(&callId);
                  if ( callIdBranchIdTime && (0 == branchId.compareTo(callIdBranchIdTime)) ) {
                     mCallTransMap.destroy(&callId);
                     mCallTransMutex.release();
                     if ( rspStatus != SIP_PROXY_AUTH_REQ_CODE ) {
                        mpBuilder->callFailureEvent(mSequenceNumber, timeNow, branchId, viaCount, rspStatus, rspText);
                     }
                     else {
                        // response was an authentication required.  Don't build a CSE for these as a new Invite will
                        // occur.
                        return(TRUE);
                     }
                  }
                  else
                  {
                     // Call was not found in the map so this is not a final response.  Ignore it.
                     mCallTransMutex.release();
                     return(TRUE);
                  }
                  break;
                  
               case aCallEnd:
                  mCallTransMutex.acquire();
                  mCallTransMap.destroy(&callId);
                  mCallTransMutex.release();
                  mpBuilder->callEndEvent(mSequenceNumber, timeNow);
                  break;
                  
               case aCallTransfer:
                  mpBuilder->callTransferEvent(mSequenceNumber, timeNow, 
                                               contact, referTo, referredBy, requestUri);
                  break;   
   
               default:
                  // shouldn't be possible to get here
                  Os::Logger::instance().log(FAC_SIP, PRI_ERR, "SipXProxyCseObserver invalid thisMsgIs");
                  break;
               }
   
               mpBuilder->addCallData(cseqNumber, callId, fromTag, toTag, fromField, toField);
               UtlString via;
               for (int i=0; sipMsg->getViaField(&via, i); i++)
               {
                  mpBuilder->addEventVia(via);
               }
   
               mpBuilder->completeCallEvent();
                 
               // get the completed record
               UtlString event;
               mpBuilder->finishElement(event);
               
               if (mpWriter)
               {
                 mpWriter->writeLog(event.data());
               }
            }
            else
            {
               Os::Logger::instance().log(FAC_SIP, PRI_ERR, "SipXProxyCseObserver - no CallStateEventBuilder!");               
            }
         }
      }
      else
      {
         Os::Logger::instance().log(FAC_SIP, PRI_ERR, "SipXProxyCseObserver getMessage returned NULL");
      }
   }
   break;
   
   default:
   {
      Os::Logger::instance().log(FAC_SIP, PRI_ERR, "SipXProxyCseObserver invalid message type %d", msgType );
   }
   } // end switch (msgType)
   
   return(TRUE);
}
AuthPlugin::AuthResult
MSFT_ExchangeTransferHack::authorizeAndModify(const UtlString& id,
                                              const Url&  requestUri,
                                              RouteState& routeState,
                                              const UtlString& method,
                                              AuthResult  priorResult,
                                              SipMessage& request,
                                              bool bSpiralingRequest,  
                                              UtlString&  reason
                                              )
{
   AuthResult result = CONTINUE; // we modify, but never make an authorization decision

   if (mUserAgentRegEx) // if not configured, do nothing
   {
      // get the call-id to use in logging
      UtlString callId;
      request.getCallIdField(&callId);

      /*
       * Note: a REFER from Exchange, with the bug we are hunting here, will always look like
       * it is tranferring to some foreign domain.  This will cause the TransferControl AuthPlugin
       * to ALLOW it.  If TransferControl starts challenging REFER from Exchange, that may mean
       * that MSFT has fixed the bug this plugin is meant to compensate for.
       */
      if (DENY != priorResult) // ignore anything some other plugin has already nixed
      {
         if (method.compareTo(SIP_REFER_METHOD) == 0) // we only care about REFER
         {
            UtlString userAgent;
            request.getUserAgentField( &userAgent );

            if (mUserAgentRegEx->Search(userAgent)) // does this look like Exchange?
            {
               UtlString targetStr;
               if (request.getReferToField(targetStr)) // get the address of the transfer target
               {
                  Url target(targetStr);
                  if (Url::SipUrlScheme == target.getScheme()) // target address parsed ok?
                  {
                     // check whether or not this is REFER with Replaces
                     UtlString targetDialog;
                     if (!target.getHeaderParameter(SIP_REPLACES_FIELD, targetDialog))
                     {
                        /*
                         * This is a REFER without Replaces from Exchange
                         * so check the domain parts of the two URIs to see if they match.
                         */

                        // Get the domain part of the transfer-target URI
                        UtlString targetDomain;
                        target.getHostWithPort(targetDomain);

                        // Get the domain part of the request URI
                        UtlString requestDomain;
                        requestUri.getHostWithPort(requestDomain);

                        if (targetDomain.compareTo(requestDomain, UtlString::ignoreCase) == 0)
                        {
                           // The domains are the same; this is the bug we're looking for...

                           UtlString correctDomain;
                           mpSipRouter->getDomain(correctDomain);
                           target.setHostAddress(correctDomain);
                           target.setHostPort(PORT_NONE);

                           UtlString modifiedTarget;
                           target.toString(modifiedTarget);
                           request.setReferToField(modifiedTarget.data());

                           Os::Logger::instance().log(FAC_AUTH, PRI_INFO,
                                         "MSFT_ExchangeTransferHack[%s]::authorizeAndModify "
                                         "corrected transfer target domain in call '%s'\n"
                                         "changed '@%s' -> '@%s'",
                                         mInstanceName.data(), callId.data(),
                                         targetDomain.data(), correctDomain.data()
                                         );
                        }
                        else
                        {
                           // oh my god... did MSFT fix Exchange?
                           Os::Logger::instance().log(FAC_AUTH, PRI_DEBUG,
                                         "MSFT_ExchangeTransferHack[%s]::authorizeAndModify "
                                         "request and target domain differ in '%s'; not modified",
                                         mInstanceName.data(), callId.data()
                                         );
                        }
                     }
                     else
                     {
                        // This is a REFER with Replaces from Exchange
                        // I don't expect this to happen, but if it does then don't mess with it.
                        Os::Logger::instance().log(FAC_AUTH, PRI_INFO,
                                      "MSFT_ExchangeTransferHack[%s]::authorizeAndModify "
                                      "allowing REFER with Replaces in call '%s' to '%s'; no action",
                                      mInstanceName.data(), callId.data(), targetDialog.data()
                                      );
                     }
                  }
                  else
                  {
                     Os::Logger::instance().log(FAC_AUTH, PRI_WARNING,
                                   "MSFT_ExchangeTransferHack[%s]::authorizeAndModify "
                                   "unrecognized refer target '%s' for call '%s'",
                                   mInstanceName.data(), targetStr.data(), callId.data()
                                   );
                  }
               }
               else
               {
                  // REFER without a Refer-To header... incorrect, but just ignore it.
                  Os::Logger::instance().log(FAC_AUTH, PRI_WARNING,
                                "MSFT_ExchangeTransferHack[%s]::authorizeAndModify "
                                "REFER method without Refer-To in call '%s'",
                                mInstanceName.data(), callId.data()
                                );
               }
            }
            else
            {
               Os::Logger::instance().log(FAC_AUTH, PRI_DEBUG,
                             "MSFT_ExchangeTransferHack[%s]::authorizeAndModify "
                             "User-Agent '%s' does not match recognizer in %s",
                             mInstanceName.data(), userAgent.data(), callId.data()
                             );
            }
         }
         else
         {
            // not a REFER - ignore it.
         }
      }
      else
      {
         // Some earlier plugin already decided on this - don't waste time figuring it out.
         Os::Logger::instance().log(FAC_AUTH, PRI_DEBUG, "MSFT_ExchangeTransferHack[%s]::authorizeAndModify "
                       "prior authorization result %s for call %s",
                       mInstanceName.data(), AuthResultStr(priorResult), callId.data()
                       );
      }
   }
   return result;
}