Mercurial > prosody-modules
comparison mod_smacks/mod_smacks.lua @ 3634:915e32d5a147
mod_smacks: fix bug for missbehaving clients sending multiple acks in a row
Missbehaving clients, sending multiple acks in a row (I'm looking at you Monal!)
triggered the ack-loop-prevention code added in 2017, leaving unacked stanzas
in the queue. This fixes the bug while still preventing ack-loops.
| author | tmolitor <thilo@eightysoft.de> |
|---|---|
| date | Tue, 30 Jul 2019 02:07:13 +0200 |
| parents | c2c851722a8a |
| children | b2f32b3c6ec1 |
comparison
equal
deleted
inserted
replaced
| 3633:6b0db0f2d57a | 3634:915e32d5a147 |
|---|---|
| 158 end); | 158 end); |
| 159 | 159 |
| 160 local function request_ack_if_needed(session, force, reason) | 160 local function request_ack_if_needed(session, force, reason) |
| 161 local queue = session.outgoing_stanza_queue; | 161 local queue = session.outgoing_stanza_queue; |
| 162 if session.awaiting_ack == nil and not session.hibernating then | 162 if session.awaiting_ack == nil and not session.hibernating then |
| 163 -- this check of last_queue_count prevents ack-loops if missbehaving clients report wrong | |
| 164 -- stanza counts. it is set when an <r> is really sent (e.g. inside timer), preventing any | |
| 165 -- further requests until the queue count changes (either by incoming acks or by adding | |
| 166 -- more stanzas) | |
| 163 if (#queue > max_unacked_stanzas and session.last_queue_count ~= #queue) or force then | 167 if (#queue > max_unacked_stanzas and session.last_queue_count ~= #queue) or force then |
| 164 session.log("debug", "Queuing <r> (in a moment) from %s - #queue=%d", reason, #queue); | 168 session.log("debug", "Queuing <r> (in a moment) from %s - #queue=%d", reason, #queue); |
| 165 session.awaiting_ack = false; | 169 session.awaiting_ack = false; |
| 166 session.awaiting_ack_timer = stoppable_timer(1e-06, function () | 170 session.awaiting_ack_timer = stoppable_timer(1e-06, function () |
| 167 if not session.awaiting_ack and not session.hibernating then | 171 if not session.awaiting_ack and not session.hibernating then |
| 168 session.log("debug", "Sending <r> (inside timer, before send)"); | 172 session.log("debug", "Sending <r> (inside timer, before send)"); |
| 169 (session.sends2s or session.send)(st.stanza("r", { xmlns = session.smacks })) | 173 (session.sends2s or session.send)(st.stanza("r", { xmlns = session.smacks })) |
| 174 session.awaiting_ack = true; | |
| 175 session.last_queue_count = #queue; | |
| 170 session.log("debug", "Sending <r> (inside timer, after send)"); | 176 session.log("debug", "Sending <r> (inside timer, after send)"); |
| 171 session.awaiting_ack = true; | |
| 172 if not session.delayed_ack_timer then | 177 if not session.delayed_ack_timer then |
| 173 session.delayed_ack_timer = stoppable_timer(delayed_ack_timeout, function() | 178 session.delayed_ack_timer = stoppable_timer(delayed_ack_timeout, function() |
| 174 delayed_ack_function(session); | 179 delayed_ack_function(session); |
| 175 end); | 180 end); |
| 176 end | 181 end |
| 185 -- would not trigger this event (again). | 190 -- would not trigger this event (again). |
| 186 if #queue > max_unacked_stanzas and session.awaiting_ack and session.delayed_ack_timer == nil then | 191 if #queue > max_unacked_stanzas and session.awaiting_ack and session.delayed_ack_timer == nil then |
| 187 session.log("debug", "Calling delayed_ack_function directly (still waiting for ack)"); | 192 session.log("debug", "Calling delayed_ack_function directly (still waiting for ack)"); |
| 188 delayed_ack_function(session); | 193 delayed_ack_function(session); |
| 189 end | 194 end |
| 190 | |
| 191 session.last_queue_count = #queue; | |
| 192 end | 195 end |
| 193 | 196 |
| 194 local function outgoing_stanza_filter(stanza, session) | 197 local function outgoing_stanza_filter(stanza, session) |
| 195 local is_stanza = stanza.attr and not stanza.attr.xmlns and not stanza.name:find":"; | 198 local is_stanza = stanza.attr and not stanza.attr.xmlns and not stanza.name:find":"; |
| 196 if is_stanza and not stanza._cached then -- Stanza in default stream namespace | 199 if is_stanza and not stanza._cached then -- Stanza in default stream namespace |
| 585 session.delayed_ack_timer = nil; | 588 session.delayed_ack_timer = nil; |
| 586 end | 589 end |
| 587 return false; -- Kick the session | 590 return false; -- Kick the session |
| 588 end | 591 end |
| 589 session.log("debug", "Sending <r> (read timeout)"); | 592 session.log("debug", "Sending <r> (read timeout)"); |
| 590 session.awaiting_ack = false; | |
| 591 (session.sends2s or session.send)(st.stanza("r", { xmlns = session.smacks })); | 593 (session.sends2s or session.send)(st.stanza("r", { xmlns = session.smacks })); |
| 592 session.awaiting_ack = true; | 594 session.awaiting_ack = true; |
| 593 if not session.delayed_ack_timer then | 595 if not session.delayed_ack_timer then |
| 594 session.delayed_ack_timer = stoppable_timer(delayed_ack_timeout, function() | 596 session.delayed_ack_timer = stoppable_timer(delayed_ack_timeout, function() |
| 595 delayed_ack_function(session); | 597 delayed_ack_function(session); |
