Commit b68f3605 authored by Jonas Schäfer's avatar Jonas Schäfer

XEP-0410: implement server-side optimisation for self-pings

This prevents the ping from round-tripping through IRC and
possibly a random other client of the user. Please see XEP-0410
for the rationale.

Fixes #3385.
parent a8cd2414
...@@ -11,6 +11,9 @@ For users ...@@ -11,6 +11,9 @@ For users
- All commands sent to IRC servers are now throttled to avoid being - All commands sent to IRC servers are now throttled to avoid being
disconnected for excess flood. The limit value can be customized using the disconnected for excess flood. The limit value can be customized using the
ad-hoc configuration form on a server JID. ad-hoc configuration form on a server JID.
- Support for XEP-0410 Self-Ping Optimization. This will prevent clients
which use self-ping from dropping out of the MUC if another client with
bad connectivity is also joined from the same account.
For admins For admins
---------- ----------
......
...@@ -747,6 +747,23 @@ void Bridge::send_irc_participant_ping_request(const Iid& iid, const std::string ...@@ -747,6 +747,23 @@ void Bridge::send_irc_participant_ping_request(const Iid& iid, const std::string
"", true); "", true);
return; return;
} }
if (chan->get_self()->nick == nick)
{
// XEP-0410 self-ping optimisation: always reply without going the full
// round-trip through IRC and possibly another XMPP client. See the XEP
// for details.
Jid iq_from(from_jid);
iq_from.local = std::to_string(iid);
iq_from.resource = nick;
Stanza iq("iq");
iq["from"] = iq_from.full();
iq["to"] = to_jid;
iq["id"] = iq_id;
iq["type"] = "result";
this->xmpp.send_stanza(iq);
return;
}
if (chan->get_self()->nick != nick && !chan->find_user(nick)) if (chan->get_self()->nick != nick && !chan->find_user(nick))
{ {
this->xmpp.send_stanza_error("iq", to_jid, from_jid, iq_id, "cancel", "item-not-found", this->xmpp.send_stanza_error("iq", to_jid, from_jid, iq_id, "cancel", "item-not-found",
......
...@@ -997,7 +997,7 @@ void BiboumiComponent::send_irc_channel_disco_info(const std::string& id, const ...@@ -997,7 +997,7 @@ void BiboumiComponent::send_irc_channel_disco_info(const std::string& id, const
identity["category"] = "conference"; identity["category"] = "conference";
identity["type"] = "irc"; identity["type"] = "irc";
identity["name"] = ""s + iid.get_local() + " on " + iid.get_server(); identity["name"] = ""s + iid.get_local() + " on " + iid.get_server();
for (const char *ns: {DISCO_INFO_NS, MUC_NS, ADHOC_NS, PING_NS, MAM_NS, VERSION_NS, STABLE_MUC_ID_NS}) for (const char *ns: {DISCO_INFO_NS, MUC_NS, ADHOC_NS, PING_NS, MAM_NS, VERSION_NS, STABLE_MUC_ID_NS, SELF_PING_FLAG})
{ {
XmlSubNode feature(query, "feature"); XmlSubNode feature(query, "feature");
feature["var"] = ns; feature["var"] = ns;
......
...@@ -38,6 +38,7 @@ ...@@ -38,6 +38,7 @@
#define MUC_TRAFFIC_NS "http://jabber.org/protocol/muc#traffic" #define MUC_TRAFFIC_NS "http://jabber.org/protocol/muc#traffic"
#define STABLE_ID_NS "urn:xmpp:sid:0" #define STABLE_ID_NS "urn:xmpp:sid:0"
#define STABLE_MUC_ID_NS "http://jabber.org/protocol/muc#stable_id" #define STABLE_MUC_ID_NS "http://jabber.org/protocol/muc#stable_id"
#define SELF_PING_FLAG MUC_NS"#self-ping-optimization"
/** /**
* An XMPP component, communicating with an XMPP server using the protocole * An XMPP component, communicating with an XMPP server using the protocole
......
...@@ -1515,24 +1515,12 @@ if __name__ == '__main__': ...@@ -1515,24 +1515,12 @@ if __name__ == '__main__':
# Send a ping to ourself # Send a ping to ourself
partial(send_stanza, partial(send_stanza,
"<iq type='get' from='{jid_one}/{resource_one}' id='first_ping' to='#foo%{irc_server_one}/{nick_one}'><ping xmlns='urn:xmpp:ping' /></iq>"), "<iq type='get' from='{jid_one}/{resource_one}' id='first_ping' to='#foo%{irc_server_one}/{nick_one}'><ping xmlns='urn:xmpp:ping' /></iq>"),
# We receive our own ping request,
partial(expect_stanza,
"/iq[@from='{lower_nick_one}%{irc_server_one}'][@type='get'][@to='{jid_one}/{resource_one}'][@id='gnip_tsrif']"),
# Respond to the request with an error
partial(send_stanza,
"<iq from='{jid_one}/{resource_one}' id='gnip_tsrif' to='{lower_nick_one}%{irc_server_one}' type='error'><error type='cancel'><feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/></error></iq>"),
partial(expect_stanza, partial(expect_stanza,
"/iq[@from='#foo%{irc_server_one}/{nick_one}'][@type='result'][@to='{jid_one}/{resource_one}'][@id='first_ping']"), "/iq[@from='#foo%{irc_server_one}/{nick_one}'][@type='result'][@to='{jid_one}/{resource_one}'][@id='first_ping']"),
# Send a ping to ourself # Send a ping to ourself
partial(send_stanza, partial(send_stanza,
"<iq type='get' from='{jid_one}/{resource_one}' id='first_ping' to='#foo%{irc_server_one}/{nick_one}'><ping xmlns='urn:xmpp:ping' /></iq>"), "<iq type='get' from='{jid_one}/{resource_one}' id='first_ping' to='#foo%{irc_server_one}/{nick_one}'><ping xmlns='urn:xmpp:ping' /></iq>"),
# We receive our own ping request,
partial(expect_stanza,
"/iq[@from='{lower_nick_one}%{irc_server_one}'][@type='get'][@to='{jid_one}/{resource_one}'][@id='gnip_tsrif']"),
# Respond to the request with an error
partial(send_stanza,
"<iq from='{jid_one}/{resource_one}' id='gnip_tsrif' to='{lower_nick_one}%{irc_server_one}' type='error'><error type='cancel'><service-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/></error></iq>"),
partial(expect_stanza, partial(expect_stanza,
"/iq[@from='#foo%{irc_server_one}/{nick_one}'][@type='result'][@to='{jid_one}/{resource_one}'][@id='first_ping']"), "/iq[@from='#foo%{irc_server_one}/{nick_one}'][@type='result'][@to='{jid_one}/{resource_one}'][@id='first_ping']"),
]), ]),
...@@ -1581,12 +1569,6 @@ if __name__ == '__main__': ...@@ -1581,12 +1569,6 @@ if __name__ == '__main__':
# Send a ping to ourself # Send a ping to ourself
partial(send_stanza, partial(send_stanza,
"<iq type='get' from='{jid_one}/{resource_one}' id='first_ping' to='#foo%{irc_server_one}/{nick_one}'><ping xmlns='urn:xmpp:ping' /></iq>"), "<iq type='get' from='{jid_one}/{resource_one}' id='first_ping' to='#foo%{irc_server_one}/{nick_one}'><ping xmlns='urn:xmpp:ping' /></iq>"),
# We receive our own ping request,
partial(expect_stanza,
"/iq[@from='{lower_nick_one}%{irc_server_one}'][@type='get'][@to='{jid_one}/{resource_one}'][@id='gnip_tsrif']"),
# Respond to the request
partial(send_stanza,
"<iq type='result' to='{lower_nick_one}%{irc_server_one}' id='gnip_tsrif' from='{jid_one}/{resource_one}'/>"),
partial(expect_stanza, partial(expect_stanza,
"/iq[@from='#foo%{irc_server_one}/{nick_one}'][@type='result'][@to='{jid_one}/{resource_one}'][@id='first_ping']"), "/iq[@from='#foo%{irc_server_one}/{nick_one}'][@type='result'][@to='{jid_one}/{resource_one}'][@id='first_ping']"),
...@@ -1602,23 +1584,11 @@ if __name__ == '__main__': ...@@ -1602,23 +1584,11 @@ if __name__ == '__main__':
# And re-send a self ping # And re-send a self ping
partial(send_stanza, partial(send_stanza,
"<iq type='get' from='{jid_one}/{resource_one}' id='second_ping' to='#foo%{irc_server_one}/{nick_one}'><ping xmlns='urn:xmpp:ping' /></iq>"), "<iq type='get' from='{jid_one}/{resource_one}' id='second_ping' to='#foo%{irc_server_one}/{nick_one}'><ping xmlns='urn:xmpp:ping' /></iq>"),
# We receive our own ping request. Note that we don't know the to value, it could be one of our two resources.
partial(expect_stanza,
"/iq[@from='{lower_nick_one}%{irc_server_one}'][@type='get'][@to][@id='gnip_dnoces']",
after = partial(save_value, "to", partial(extract_attribute, "/iq", "to"))),
# Respond to the request, using the extracted 'to' value as our 'from'
partial(send_stanza,
"<iq type='result' to='{lower_nick_one}%{irc_server_one}' id='gnip_dnoces' from='{to}'/>"),
partial(expect_stanza, partial(expect_stanza,
"/iq[@from='#foo%{irc_server_one}/{nick_one}'][@type='result'][@to='{jid_one}/{resource_one}'][@id='second_ping']"), "/iq[@from='#foo%{irc_server_one}/{nick_one}'][@type='result'][@to='{jid_one}/{resource_one}'][@id='second_ping']"),
## And re-do exactly the same thing, just change the resource initiating the self ping ## And re-do exactly the same thing, just change the resource initiating the self ping
partial(send_stanza, partial(send_stanza,
"<iq type='get' from='{jid_one}/{resource_two}' id='third_ping' to='#foo%{irc_server_one}/{nick_one}'><ping xmlns='urn:xmpp:ping' /></iq>"), "<iq type='get' from='{jid_one}/{resource_two}' id='third_ping' to='#foo%{irc_server_one}/{nick_one}'><ping xmlns='urn:xmpp:ping' /></iq>"),
partial(expect_stanza,
"/iq[@from='{lower_nick_one}%{irc_server_one}'][@type='get'][@to][@id='gnip_driht']",
after = partial(save_value, "to", partial(extract_attribute, "/iq", "to"))),
partial(send_stanza,
"<iq type='result' to='{lower_nick_one}%{irc_server_one}' id='gnip_driht' from='{to}'/>"),
partial(expect_stanza, partial(expect_stanza,
"/iq[@from='#foo%{irc_server_one}/{nick_one}'][@type='result'][@to='{jid_one}/{resource_two}'][@id='third_ping']"), "/iq[@from='#foo%{irc_server_one}/{nick_one}'][@type='result'][@to='{jid_one}/{resource_two}'][@id='third_ping']"),
...@@ -1639,12 +1609,6 @@ if __name__ == '__main__': ...@@ -1639,12 +1609,6 @@ if __name__ == '__main__':
# Send a ping to ourself # Send a ping to ourself
partial(send_stanza, partial(send_stanza,
"<iq type='get' from='{jid_one}/{resource_one}' id='first_ping' to='#foo@{biboumi_host}/{nick_one}'><ping xmlns='urn:xmpp:ping' /></iq>"), "<iq type='get' from='{jid_one}/{resource_one}' id='first_ping' to='#foo@{biboumi_host}/{nick_one}'><ping xmlns='urn:xmpp:ping' /></iq>"),
# We receive our own ping request,
partial(expect_stanza,
"/iq[@from='{lower_nick_one}@{biboumi_host}'][@type='get'][@to='{jid_one}/{resource_one}'][@id='gnip_tsrif']"),
# Respond to the request
partial(send_stanza,
"<iq type='result' to='{lower_nick_one}@{biboumi_host}' id='gnip_tsrif' from='{jid_one}/{resource_one}'/>"),
partial(expect_stanza, partial(expect_stanza,
"/iq[@from='#foo@{biboumi_host}/{nick_one}'][@type='result'][@to='{jid_one}/{resource_one}'][@id='first_ping']"), "/iq[@from='#foo@{biboumi_host}/{nick_one}'][@type='result'][@to='{jid_one}/{resource_one}'][@id='first_ping']"),
], conf="fixed_server"), ], conf="fixed_server"),
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment