< Previous by Date Date Index Next by Date >
< Previous in Thread Thread Index Next in Thread >

Re: [reSIProcate] Resiprocate/DUM assert hit when rearrenged/lost packets received


Hello all,

We still have this problem and I still can't solve it unfortunately :(
I have created a barebones testcase that reproduces the scenarios.
The attached sipp scenario XMLs should be run like:

./sipp -i 127.0.0.1 -p 12010 -sf refer100.xml
or
./sipp -i 127.0.0.1 -p 12010 -sf refer.xml

and run the  BasicCall from resip/dum/test patched with the attached
diff. This test app has only one leg, the client leg in my original
post, and I simulated the BYE from the "A" leg with end() in the
onNewSubscriptionFromRefer(). The BasicCall app will crash and produce
the stacktrace I described below.

Please take a look!

TIA

br

Szo

On 2016-11-11 11:41, Szokovacs Robert wrote:
> Hi all,
>
> We have found a situation where a malicious third party possibly can
> crash the resiprocate based application by carefully rearranging SIP
> packets.
>
> We have the following setup: the resiprocate-based B2BUA receives the
> incoming call, calls the application server on an other leg, which,
> after some processing, REFERs the call towards it's final destination.
>
> Relevant packet flow:
>
> 1, A leg -> INVITE -> B2BUA
>
> 2, A leg <- 100 <- B2BUA
>
> 3, A leg <- 200OK <- B2BUA
>
> 4, B2BUA -> INVITE -> AppServer
>
> 5, B2BUA <- 100 <- AppServer
>
> 6, B2BUA <- 200OK <- AppServer
>
> 7, B2BUA -> ACK -> AppServer
>
> 8, B2BUA <- REFER <- AppServer
>
> 9, B2BUA -> 202 -> AppServer
>
> Etc, etc.
>
>
> In our situation packet #6 got lost and for reasons not important here
> the AppServer doesn't wait for ACK (#7) before sending the #8 REFER, #9
> is 491 instead of 202. All this is fine and correct behaviour; the
> problem manifests when the A leg sends BYE, causing our B2BUA to end the
> B leg too (bye calling AppDialogSet::end(), which in turn calls
> DialogSet::end() ), before #6 was retransmitted and received. Upon
> receiving the BYE, our app crashed:
>
> #16 in resip::Dialog::cancel (this=<optimized out>) at Dialog.cxx:341
>
> line 338: void
> Dialog::cancel()
> {
>    resip_assert(mType == Invitation);
>    ClientInviteSession* uac =
> dynamic_cast<ClientInviteSession*>(mInviteSession);
>    resip_assert (uac);
>    uac->cancel();
> line 345: }
>
> #17 in resip::DialogSet::end (this=0x874a27) at DialogSet.cxx:987
>
> line 983: for (DialogMap::iterator it = mDialogs.begin(); it !=
> mDialogs.end(); it++)
>                {
>                   try
>                   {
>                      it->second->cancel();
>                   }
>                   catch (UsageUseException& e)
>                   {
>                      InfoLog(<< "Caught: " << e);
>                   }
> line 993:   }
>
>
> Further investigation uncovered an other situation, when both #5 and #6
> is missing and not retransmitted, we hit a different assert, when the
> timeout generates the internal 408 response:
>
> #3  in __GI___assert_fail (assertion=0x875e85 "mDialogs.empty()",
> file=0x85b510 "DialogSet.cxx", line=352, function=0x8768c0 "void
> resip::DialogSet::dispatch(const resip::SipMessage&)") at assert.c:103
> #4  in resip::DialogSet::dispatch (this=0x7efec001f8d0, msg=...) at
> DialogSet.cxx:352
>
> line 350:  if (mState == WaitingToEnd)
>    {
>       resip_assert(mDialogs.empty());
>       if (msg.isResponse())
> line 354:  {
>
> #5  in resip::DialogUsageManager::processResponse (this=<optimized out>,
> response=...) at DialogUsageManager.cxx:2173
> #6  in resip::DialogUsageManager::incomingProcess (this=0x7efefa6690c0,
> msg=...) at DialogUsageManager.cxx:1680
> #7  in resip::DialogUsageManager::internalProcess (this=0x7efefa6690c0,
> msg=...) at DialogUsageManager.cxx:1486
> #8  in resip::DialogUsageManager::process (this=0x7efefa6690c0,
> mutex=<optimized out>) at DialogUsageManager.cxx:1710
>
>
> What common in these scenarios is that the DialogSet has unexpected
> Dialogs hanging around (in the wrong state in the first case, existing
> at all in the second). We think the root of the problem is that after
> DUM sends the 491 response to the wayward REFER, it should get rid of
> the corresponding Dialog, but for some reason, doesn't. We don't see
> where would it fit naturally to do it, so we have no patch proposal yet,
> please comment, advise!
>
> Thanks in advance!
>
> br
>
> Szo
>

<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE scenario SYSTEM "sipp.dtd">

<scenario name="">
  <recv request="INVITE">
      <action>
          <ereg regexp="sip:.*[^>]" search_in="hdr" header="Contact:" check_it="true" assign_to="_contact" />
          <ereg regexp=".*" search_in="hdr" header="From: " check_it="true" assign_to="_from" />
          <ereg regexp=".*" search_in="hdr" header="To: " check_it="true" assign_to="_to" />
      </action>
  </recv>

  <send>
    <![CDATA[
      REFER [$_contact] SIP/2.0
      Via: SIP/2.0/UDP [local_ip]:[local_port];branch=[branch]
      From: [$_to]
      To: [$_from]
      [last_Call-ID:]
      [last_Expires:]
      Contact: sip:sipp@[local_ip]:[local_port]
      CSeq: 12 REFER
      Refer-To: sip:0123456789@internal
      Expires: 3600
      Max-Forwards: 70
      User-Agent: SEMS Call Control
      B2bua-Note: Call-Control;level=123
      Content-Length: 0
    ]]>
  </send>
  <recv response="202"> </recv> 


</scenario>
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE scenario SYSTEM "sipp.dtd">

<scenario name="">
  <recv request="INVITE">
      <action>
          <ereg regexp="sip:.*[^>]" search_in="hdr" header="Contact:" check_it="true" assign_to="_contact" />
          <ereg regexp=".*" search_in="hdr" header="From: " check_it="true" assign_to="_from" />
          <ereg regexp=".*" search_in="hdr" header="To: " check_it="true" assign_to="_to" />
      </action>
  </recv>
  <send>
    <![CDATA[
      SIP/2.0 100 Trying
      [last_Via:]
      [last_From:]
      [last_To:]
      [last_Call-ID:]
      [last_CSeq:]
      Contact: sip:sipp@[local_ip]:[local_port]
      Content-Length: 0
    ]]>
  </send>

  <send>
    <![CDATA[
      REFER [$_contact] SIP/2.0
      Via: SIP/2.0/UDP [local_ip]:[local_port];branch=[branch]
      From: [$_to]
      To: [$_from]
      [last_Call-ID:]
      [last_Expires:]
      Contact: sip:sipp@[local_ip]:[local_port]
      CSeq: 12 REFER
      Refer-To: sip:0123456789@internal
      Expires: 3600
      Max-Forwards: 70
      User-Agent: SEMS Call Control
      B2bua-Note: Call-Control;level=123
      Content-Length: 0
    ]]>
  </send>
  <recv response="202"> </recv> 


</scenario>
diff --git a/resip/dum/test/BasicCall.cxx b/resip/dum/test/BasicCall.cxx
index 304dc2e..16d0526 100644
--- a/resip/dum/test/BasicCall.cxx
+++ b/resip/dum/test/BasicCall.cxx
@@ -17,6 +17,7 @@
 #include "resip/dum/AppDialog.hxx"
 #include "resip/dum/AppDialogSet.hxx"
 #include "resip/dum/AppDialogSetFactory.hxx"
+#include "resip/dum/DefaultServerReferHandler.hxx"
 #include "rutil/Log.hxx"
 #include "rutil/Logger.hxx"
 #include "rutil/Random.hxx"
@@ -90,7 +91,7 @@ public:
 
 
 // Generic InviteSessionHandler
-class TestInviteSessionHandler : public InviteSessionHandler, public ClientRegistrationHandler, public OutOfDialogHandler
+class TestInviteSessionHandler : public InviteSessionHandler, public ClientRegistrationHandler, public OutOfDialogHandler, public DefaultServerReferHandler
 {
    public:
       Data name;
@@ -209,6 +210,12 @@ class TestInviteSessionHandler : public InviteSessionHandler, public ClientRegis
          cout << name << ": InviteSession-onRefer - " << msg.brief() << endl;
       }
 
+      virtual void onNewSubscriptionFromRefer(ServerSubscriptionHandle ssh, const SipMessage &msg)
+      {
+         cout << name << ": onNewSubscriptionFromRefer - " << msg.brief() << endl;
+         ssh->getAppDialogSet().get()->end();
+      }
+
       virtual void onReferAccepted(InviteSessionHandle, ClientSubscriptionHandle, const SipMessage& msg)
       {
          cout << name << ": InviteSession-onReferAccepted - " << msg.brief() << endl;
@@ -444,6 +451,9 @@ class TestUas : public TestInviteSessionHandler
          cout << name << ": Sending 180 response." << endl;
          mSis = sis;         
          sis->provisional(180);
+/*         NameAddr referTo;
+         CallId cid;
+         sis->refer(referTo, cid);*/
       }
 
       virtual void onTerminated(InviteSessionHandle, InviteSessionHandler::TerminatedReason reason, const SipMessage* msg)
@@ -608,6 +618,7 @@ main (int argc, char** argv)
 
    TestUac uac;
    dumUac->setInviteSessionHandler(&uac);
+   dumUac->addServerSubscriptionHandler("refer", &uac);
    dumUac->setClientRegistrationHandler(&uac);
    dumUac->addOutOfDialogHandler(OPTIONS, &uac);
 
@@ -627,91 +638,30 @@ main (int argc, char** argv)
    dumUac->getMasterProfile()->setDefaultFrom(uacAor);
    dumUac->getMasterProfile()->addSupportedMethod(INFO);
    dumUac->getMasterProfile()->addSupportedMethod(MESSAGE);
+   dumUac->getMasterProfile()->addSupportedMethod(REFER);
    dumUac->getMasterProfile()->addSupportedMimeType(INFO, PlainContents::getStaticType());
    dumUac->getMasterProfile()->addSupportedMimeType(MESSAGE, PlainContents::getStaticType());
    dumUac->getMasterProfile()->setDefaultRegistrationTime(70);
 
-   //set up UAS
-   SipStack stackUas;
-   DialogUsageManager* dumUas = new DialogUsageManager(stackUas);
-   stackUas.addTransport(UDP, 12010);
-   stackUas.addTransport(TCP, 12010);
+   uac.registered = true;
    
-   SharedPtr<MasterProfile> uasMasterProfile(new MasterProfile);
-   std::auto_ptr<ClientAuthManager> uasAuth(new ClientAuthManager);
-   dumUas->setMasterProfile(uasMasterProfile);
-   dumUas->setClientAuthManager(uasAuth);
-
-   if(doReg) 
-   {
-      dumUas->getMasterProfile()->setDigestCredential(uasAor.uri().host(), uasAor.uri().user(), uasPasswd);
-   }
-   if(useOutbound) 
-   {
-      dumUas->getMasterProfile()->setOutboundProxy(outboundUri);
-      dumUas->getMasterProfile()->addSupportedOptionTag(Token(Symbols::Outbound));
-   }
-
-   dumUas->getMasterProfile()->setDefaultFrom(uasAor);
-   dumUas->getMasterProfile()->addSupportedMethod(INFO);
-   dumUas->getMasterProfile()->addSupportedMethod(MESSAGE);
-   dumUas->getMasterProfile()->addSupportedMimeType(INFO, PlainContents::getStaticType());
-   dumUas->getMasterProfile()->addSupportedMimeType(MESSAGE, PlainContents::getStaticType());
-   dumUas->getMasterProfile()->setDefaultRegistrationTime(70);
-
-   time_t bHangupAt = 0;
-   TestUas uas(&bHangupAt);
-   dumUas->setClientRegistrationHandler(&uas);
-   dumUas->setInviteSessionHandler(&uas);
-   dumUas->addOutOfDialogHandler(OPTIONS, &uas);
-
-   auto_ptr<AppDialogSetFactory> uas_dsf(new testAppDialogSetFactory);
-   dumUas->setAppDialogSetFactory(uas_dsf);
-
-   if (doReg) 
-   {
-      SharedPtr<SipMessage> regMessage = dumUas->makeRegistration(uasAor, new testAppDialogSet(*dumUac, "UAS(Registration)"));
-      cout << "Sending register for Uas: " << endl << regMessage << endl;
-      dumUas->send(regMessage);
-   } 
-   else 
-   {
-      uas.registered = true;
-   }
-   if (doReg) 
-   {
-      SharedPtr<SipMessage> regMessage = dumUac->makeRegistration(uacAor, new testAppDialogSet(*dumUac, "UAC(Registration)"));
-      cout << "Sending register for Uac: " << endl << regMessage << endl;
-      dumUac->send(regMessage);
-   } 
-   else 
-   {
-      uac.registered = true;
-   }
-
    bool finishedTest = false;
    bool stoppedRegistering = false;
    bool startedCallFlow = false;
    bool hungup = false;   
-   TestShutdownHandler uasShutdownHandler("UAS");   
    TestShutdownHandler uacShutdownHandler("UAC");   
 
-   while (!(uasShutdownHandler.dumShutDown && uacShutdownHandler.dumShutDown))
+   while (!uacShutdownHandler.dumShutDown)
    {
      if (!uacShutdownHandler.dumShutDown)
      {
         stackUac.process(50);
         while(dumUac->process());
      }
-     if (!uasShutdownHandler.dumShutDown)
-     {
-        stackUas.process(50);
-        while(dumUas->process());
-     }
 
-     if (!(uas.done && uac.done))
+     if (!uac.done)
      {
-        if (uas.registered && uac.registered && !startedCallFlow)
+        if (uac.registered && !startedCallFlow)
         {
            if (!startedCallFlow)
            {
@@ -721,23 +671,14 @@ main (int argc, char** argv)
               }
 
               // Kick off call flow by sending an OPTIONS request then an INVITE request from the UAC to the UAS
-              cout << "UAC: Sending Options Request to UAS." << endl;
-              dumUac->send(dumUac->makeOutOfDialogRequest(uasAor, OPTIONS, new testAppDialogSet(*dumUac, "UAC(OPTIONS)")));  // Should probably add Allow, Accept, Accept-Encoding, Accept-Language and Supported headers - but this is fine for testing/demonstration
+              //cout << "UAC: Sending Options Request to UAS." << endl;
+              //dumUac->send(dumUac->makeOutOfDialogRequest(uasAor, OPTIONS, new testAppDialogSet(*dumUac, "UAC(OPTIONS)")));  // Should probably add Allow, Accept, Accept-Encoding, Accept-Language and Supported headers - but this is fine for testing/demonstration
 
               cout << "UAC: Sending Invite Request to UAS." << endl;
               dumUac->send(dumUac->makeInviteSession(uasAor, uac.mSdp, new testAppDialogSet(*dumUac, "UAC(INVITE)")));
            }
         }
 
-        // Check if we should hangup yet
-        if (bHangupAt!=0)
-        {
-           if (time(NULL)>bHangupAt && !hungup)
-           {
-              hungup = true;
-              uas.hangup();
-           }
-        }
      }
      else
      {
@@ -745,11 +686,9 @@ main (int argc, char** argv)
         {
            finishedTest = true;
            stoppedRegistering = true;
-           dumUas->shutdown(&uasShutdownHandler);
            dumUac->shutdown(&uacShutdownHandler);
             if ( doReg ) 
             {
-              uas.registerHandle->stopRegistering();
               uac.registerHandle->stopRegistering();
             }
         }
@@ -758,7 +697,6 @@ main (int argc, char** argv)
 
    // OK to delete DUM objects now
    delete dumUac; 
-   delete dumUas;
 
    cout << "!!!!!!!!!!!!!!!!!! Successful !!!!!!!!!! " << endl;
 #if defined(WIN32) && defined(_DEBUG) && defined(LEAK_CHECK)