Software /
code /
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 |
parent | 3621:c2c851722a8a |
child | 3640: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); |