Commit e86a630 for jssip.net

commit e86a6302b015c5b5a8d6ee71e1b48a9ddcf29f0c
Author: José Luis Millán <jmillan@aliax.net>
Date:   Thu Jan 8 13:27:44 2026 +0100

    Dialog: Fix wrong ACK Cseq for re-INVITE

    Fixes #943

diff --git a/lib/Dialog.js b/lib/Dialog.js
index 9454142..8ec47a6 100644
--- a/lib/Dialog.js
+++ b/lib/Dialog.js
@@ -63,7 +63,8 @@ module.exports = class Dialog
       this._remote_uri = message.parseHeader('from').uri;
       this._remote_target = contact.uri;
       this._route_set = message.getHeaders('record-route');
-      this._ack_seqnum = message.cseq;
+      this.incoming_ack_seqnum = message.cseq;
+      this.outgoing_ack_seqnum = null;
     }
     // RFC 3261 12.1.2.
     else if (type === 'UAC')
@@ -83,7 +84,8 @@ module.exports = class Dialog
       this._remote_uri = message.parseHeader('to').uri;
       this._remote_target = contact.uri;
       this._route_set = message.getHeaders('record-route').reverse();
-      this._ack_seqnum = this._local_seqnum;
+      this.incoming_ack_seqnum = null;
+      this.outgoing_ack_seqnum = this._local_seqnum;

     }

@@ -163,6 +165,12 @@ module.exports = class Dialog
     eventHandlers.onAuthenticated = () =>
     {
       this._local_seqnum += 1;
+
+      // In case of re-INVITE store outgoing ack_seqnum for its CANCEL or ACK.
+      if (request.method === JsSIP_C.INVITE)
+      {
+        this._outgoing_ack_seqnum = this._local_seqnum;
+      }
     };

     const request_sender = new Dialog_RequestSender(this, request, eventHandlers);
@@ -182,14 +190,14 @@ module.exports = class Dialog
     }

     // ACK received. Cleanup this._ack_seqnum.
-    if (request.method === JsSIP_C.ACK && this._ack_seqnum !== null)
+    if (request.method === JsSIP_C.ACK && this.incoming_ack_seqnum !== null)
     {
-      this._ack_seqnum = null;
+      this.incoming_ack_seqnum = null;
     }
     // INVITE received. Set this._ack_seqnum.
     else if (request.method === JsSIP_C.INVITE)
     {
-      this._ack_seqnum = request.cseq;
+      this.incoming_ack_seqnum = request.cseq;
     }

     this._owner.receiveRequest(request);
@@ -204,9 +212,15 @@ module.exports = class Dialog

     // CANCEL and ACK must use the same sequence number as the INVITE.
     const cseq = (method === JsSIP_C.CANCEL || method === JsSIP_C.ACK) ?
-      this._ack_seqnum :
+      this.outgoing_ack_seqnum :
       this._local_seqnum += 1;

+    // In case of re-INVITE store ack_seqnum for future CANCEL or ACK.
+    if (method === JsSIP_C.INVITE)
+    {
+      this.outgoing_ack_seqnum = cseq;
+    }
+
     const request = new SIPMessage.OutgoingRequest(
       method,
       this._remote_target,
@@ -237,7 +251,8 @@ module.exports = class Dialog
       {
         // We are not expecting any ACK with lower seqnum than the current one.
         // Or this is not the ACK we are waiting for.
-        if (this._ack_seqnum === null || request.cseq !== this._ack_seqnum)
+        if (this.incoming_ack_seqnum === null ||
+            request.cseq !== this.incoming_ack_seqnum)
         {
           return false;
         }