diff options
l--------- | .#.clang-format | 1 | ||||
-rw-r--r-- | CHANGELOG.rst | 6 | ||||
-rw-r--r-- | packaging/biboumi.spec.cmake | 5 | ||||
-rw-r--r-- | src/bridge/bridge.cpp | 22 | ||||
-rw-r--r-- | src/bridge/bridge.hpp | 2 | ||||
-rw-r--r-- | src/identd/identd_server.hpp | 1 | ||||
-rw-r--r-- | src/identd/identd_socket.cpp | 8 | ||||
-rw-r--r-- | src/network/tcp_client_socket_handler.cpp | 6 | ||||
-rw-r--r-- | src/network/tcp_client_socket_handler.hpp | 2 | ||||
-rw-r--r-- | src/xmpp/biboumi_component.cpp | 23 | ||||
-rw-r--r-- | src/xmpp/xmpp_component.cpp | 4 | ||||
-rw-r--r-- | src/xmpp/xmpp_component.hpp | 4 | ||||
-rw-r--r-- | tests/end_to_end/__main__.py | 2 | ||||
-rw-r--r-- | tests/end_to_end/functions.py | 6 | ||||
-rw-r--r-- | tests/end_to_end/scenarios/channel_messages.py | 6 | ||||
-rw-r--r-- | tests/end_to_end/scenarios/muc_disco_info.py | 1 | ||||
-rw-r--r-- | tests/end_to_end/scenarios/multisessionnick.py | 10 | ||||
-rw-r--r-- | tests/end_to_end/scenarios/stable_id.py | 32 | ||||
-rw-r--r-- | unit/biboumi.service.cmake | 1 |
19 files changed, 109 insertions, 33 deletions
diff --git a/.#.clang-format b/.#.clang-format new file mode 120000 index 0000000..d3f6657 --- /dev/null +++ b/.#.clang-format @@ -0,0 +1 @@ +louiz@abricot.3560:1477183697
\ No newline at end of file diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 172bb7c..a5d3f58 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -31,6 +31,12 @@ For developers A tutorial and a documentation have been written. It should now be easy to write a test that demonstrates a bug or a missing feature. +Version 8.4 - 2020-02-25 +======================== + +- Fix a possible crash that could be caused by a very well timed identd + query + Version 8.3 - 2018-06-01 ======================== diff --git a/packaging/biboumi.spec.cmake b/packaging/biboumi.spec.cmake index 4114b7e..4a87bb0 100644 --- a/packaging/biboumi.spec.cmake +++ b/packaging/biboumi.spec.cmake @@ -69,7 +69,10 @@ make check %{?_smp_mflags} - Build against botan2 - Build with sphinx instead of pandoc -* Fri Jun 1 2018 Le Coz Florent <louiz@louiz.org> - 8.3-1 +* Tue Feb 25 2020 Le Coz Florent <louiz@louiz.org> - 8.4-1 + Update to version 8.4 + +* Wed Jun 1 2018 Le Coz Florent <louiz@louiz.org> - 8.3-1 Update to version 8.3 * Fri May 25 2018 Le Coz Florent <louiz@louiz.org> - 8.2-1 diff --git a/src/bridge/bridge.cpp b/src/bridge/bridge.cpp index a2e2c9c..424c72a 100644 --- a/src/bridge/bridge.cpp +++ b/src/bridge/bridge.cpp @@ -184,7 +184,7 @@ bool Bridge::join_irc_channel(const Iid& iid, std::string nickname, auto res_in_chan = this->is_resource_in_chan(ChannelKey{iid.get_local(), hostname}, resource); if (!res_in_chan) this->add_resource_to_chan(ChannelKey{iid.get_local(), hostname}, resource); - if (irc->is_channel_joined(iid.get_local()) == false) + if (!irc->is_channel_joined(iid.get_local())) { irc->send_join_command(iid.get_local(), password); return true; @@ -195,7 +195,7 @@ bool Bridge::join_irc_channel(const Iid& iid, std::string nickname, return false; } -void Bridge::send_channel_message(const Iid& iid, const std::string& body, std::string id) +void Bridge::send_channel_message(const Iid& iid, const std::string& body, std::string id, std::vector<XmlNode> nodes_to_reflect) { if (iid.get_server().empty()) { @@ -233,15 +233,21 @@ void Bridge::send_channel_message(const Iid& iid, const std::string& body, std:: if (!first || id.empty()) id = utils::gen_uuid(); - MessageCallback mirror_to_all_resources = [this, iid, uuid, id](const IrcClient* irc, const IrcMessage& message) { + MessageCallback mirror_to_all_resources = [this, iid, uuid, id, nodes_to_reflect](const IrcClient* irc, const IrcMessage& message) { std::string line = message.arguments[1]; // “temporary” workaround for \01ACTION…\01 -> /me messages if ((line.size() > strlen("\01ACTION\01")) && (line.substr(0, 7) == "\01ACTION") && line[line.size() - 1] == '\01') line = "/me " + line.substr(8, line.size() - 9); for (const auto& resource: this->resources_in_chan[iid.to_tuple()]) - this->xmpp.send_muc_message(std::to_string(iid), irc->get_own_nick(), this->make_xmpp_body(line), - this->user_jid + "/" + resource, uuid, id); + { + auto stanza = this->xmpp.make_muc_message(std::to_string(iid), irc->get_own_nick(), this->make_xmpp_body(line), + this->user_jid + "/" + + resource, uuid, id); + for (const auto& node: nodes_to_reflect) + stanza.add_child(node); + this->xmpp.send_stanza(stanza); + } }; if (line.substr(0, 5) == "/mode") @@ -858,8 +864,10 @@ void Bridge::send_message(const Iid& iid, const std::string& nick, const std::st #endif for (const auto& resource: this->resources_in_chan[iid.to_tuple()]) { - this->xmpp.send_muc_message(std::to_string(iid), nick, this->make_xmpp_body(body, encoding), - this->user_jid + "/" + resource, uuid, utils::gen_uuid()); + auto stanza = this->xmpp.make_muc_message(std::to_string(iid), nick, this->make_xmpp_body(body, encoding), + this->user_jid + "/" + + resource, uuid, utils::gen_uuid()); + this->xmpp.send_stanza(stanza); } } else diff --git a/src/bridge/bridge.hpp b/src/bridge/bridge.hpp index 8f474e5..6b15478 100644 --- a/src/bridge/bridge.hpp +++ b/src/bridge/bridge.hpp @@ -79,7 +79,7 @@ public: const std::string& resource, HistoryLimit history_limit); - void send_channel_message(const Iid& iid, const std::string& body, std::string id); + void send_channel_message(const Iid& iid, const std::string& body, std::string id, std::vector<XmlNode> nodes_to_reflect); void send_private_message(const Iid& iid, const std::string& body, const std::string& type="PRIVMSG"); void send_raw_message(const std::string& hostname, const std::string& body); void leave_irc_channel(Iid&& iid, const std::string& status_message, const std::string& resource); diff --git a/src/identd/identd_server.hpp b/src/identd/identd_server.hpp index b1c8ec8..55fe225 100644 --- a/src/identd/identd_server.hpp +++ b/src/identd/identd_server.hpp @@ -24,6 +24,7 @@ class IdentdServer: public TcpSocketServer<IdentdSocket> if (this->poller->is_managing_socket(this->socket)) this->poller->remove_socket_handler(this->socket); ::close(this->socket); + this->sockets.clear(); } void clean() { diff --git a/src/identd/identd_socket.cpp b/src/identd/identd_socket.cpp index 92cd80b..7688bbe 100644 --- a/src/identd/identd_socket.cpp +++ b/src/identd/identd_socket.cpp @@ -25,10 +25,12 @@ void IdentdSocket::parse_in_buffer(const std::size_t) std::istringstream line(this->in_buf.substr(0, line_end)); this->consume_in_buffer(line_end + 1); - uint16_t local_port; - uint16_t remote_port; + uint16_t local_port{}; + uint16_t remote_port{}; char sep; line >> local_port >> sep >> remote_port; + if (line.fail()) // Data did not match the expected format, ignore the line entirely + continue; const auto& xmpp = this->server.get_biboumi_component(); auto response = this->generate_answer(xmpp, local_port, remote_port); @@ -47,7 +49,7 @@ std::string IdentdSocket::generate_answer(const BiboumiComponent& biboumi, uint1 { for (const auto& pair: bridge->get_irc_clients()) { - if (pair.second->match_port_pairt(local, remote)) + if (pair.second->match_port_pair(local, remote)) { std::ostringstream os; os << local << " , " << remote << " : USERID : OTHER : " << hash_jid(bridge->get_bare_jid()) << "\r\n"; diff --git a/src/network/tcp_client_socket_handler.cpp b/src/network/tcp_client_socket_handler.cpp index 6f67f02..7d1029f 100644 --- a/src/network/tcp_client_socket_handler.cpp +++ b/src/network/tcp_client_socket_handler.cpp @@ -260,8 +260,10 @@ std::string TCPClientSocketHandler::get_port() const return this->port; } -bool TCPClientSocketHandler::match_port_pairt(const uint16_t local, const uint16_t remote) const +bool TCPClientSocketHandler::match_port_pair(const uint16_t local, const uint16_t remote) const { + if (!this->is_connected()) + return false; const auto remote_port = static_cast<uint16_t>(std::stoi(this->port)); - return this->is_connected() && local == this->local_port && remote == remote_port; + return local == this->local_port && remote == remote_port; } diff --git a/src/network/tcp_client_socket_handler.hpp b/src/network/tcp_client_socket_handler.hpp index 74caca9..38d8b84 100644 --- a/src/network/tcp_client_socket_handler.hpp +++ b/src/network/tcp_client_socket_handler.hpp @@ -34,7 +34,7 @@ class TCPClientSocketHandler: public TCPSocketHandler /** * Whether or not this connection is using the two given TCP ports. */ - bool match_port_pairt(const uint16_t local, const uint16_t remote) const; + bool match_port_pair(const uint16_t local, const uint16_t remote) const; protected: bool hostname_resolution_failed; diff --git a/src/xmpp/biboumi_component.cpp b/src/xmpp/biboumi_component.cpp index 91be091..f49b3b6 100644 --- a/src/xmpp/biboumi_component.cpp +++ b/src/xmpp/biboumi_component.cpp @@ -302,7 +302,26 @@ void BiboumiComponent::handle_message(const Stanza& stanza) if (body && !body->get_inner().empty()) { if (bridge->is_resource_in_chan(iid.to_tuple(), from.resource)) - bridge->send_channel_message(iid, body->get_inner(), id); + { + // Extract some XML nodes that we must include in the + // reflection (if any), because XMPP says so + std::vector<XmlNode> nodes_to_reflect; + const XmlNode* origin_id = stanza.get_child("origin-id", STABLE_ID_NS); + if (origin_id) + nodes_to_reflect.push_back(*origin_id); + const auto own_address = std::to_string(iid) + '@' + this->served_hostname; + for (const XmlNode* stanza_id: stanza.get_children("stanza-id", STABLE_ID_NS)) + { + // Stanza ID generating entities, which encounter a + // <stanza-id/> element where the 'by' attribute matches + // the 'by' attribute they would otherwise set, MUST + // delete that element even if they are not adding their + // own stanza ID. + if (stanza_id->get_tag("by") != own_address) + nodes_to_reflect.push_back(*stanza_id); + } + bridge->send_channel_message(iid, body->get_inner(), id, std::move(nodes_to_reflect)); + } else { error_type = "modify"; @@ -1008,7 +1027,7 @@ void BiboumiComponent::send_irc_channel_disco_info(const std::string& id, const identity["category"] = "conference"; identity["type"] = "irc"; 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, SELF_PING_FLAG, "muc_nonanonymous"}) + for (const char *ns: {DISCO_INFO_NS, MUC_NS, ADHOC_NS, PING_NS, MAM_NS, VERSION_NS, STABLE_MUC_ID_NS, SELF_PING_FLAG, "muc_nonanonymous", STABLE_ID_NS}) { XmlSubNode feature(query, "feature"); feature["var"] = ns; diff --git a/src/xmpp/xmpp_component.cpp b/src/xmpp/xmpp_component.cpp index f82f9ce..767fb2e 100644 --- a/src/xmpp/xmpp_component.cpp +++ b/src/xmpp/xmpp_component.cpp @@ -367,7 +367,7 @@ void XmppComponent::send_topic(const std::string& from, Xmpp::body&& topic, cons this->send_stanza(message); } -void XmppComponent::send_muc_message(const std::string& muc_name, const std::string& nick, Xmpp::body&& xmpp_body, const std::string& jid_to, std::string uuid, std::string id) +Stanza XmppComponent::make_muc_message(const std::string& muc_name, const std::string& nick, Xmpp::body&& xmpp_body, const std::string& jid_to, std::string uuid, std::string id) { Stanza message("message"); message["to"] = jid_to; @@ -399,7 +399,7 @@ void XmppComponent::send_muc_message(const std::string& muc_name, const std::str stanza_id["id"] = std::move(uuid); } - this->send_stanza(message); + return message; } #ifdef USE_DATABASE diff --git a/src/xmpp/xmpp_component.hpp b/src/xmpp/xmpp_component.hpp index 156e286..ee6b776 100644 --- a/src/xmpp/xmpp_component.hpp +++ b/src/xmpp/xmpp_component.hpp @@ -135,8 +135,8 @@ public: /** * Send a (non-private) message to the MUC */ - void send_muc_message(const std::string& muc_name, const std::string& nick, Xmpp::body&& body, const std::string& jid_to, - std::string uuid, std::string id); + Stanza make_muc_message(const std::string& muc_name, const std::string& nick, Xmpp::body&& xmpp_body, const std::string& jid_to, + std::string uuid, std::string id); #ifdef USE_DATABASE /** * Send a message, with a <delay/> element, part of a MUC history diff --git a/tests/end_to_end/__main__.py b/tests/end_to_end/__main__.py index cef554e..dcf154d 100644 --- a/tests/end_to_end/__main__.py +++ b/tests/end_to_end/__main__.py @@ -100,7 +100,7 @@ class XMPPComponent(slixmpp.BaseXMPP): def on_timeout(self, xpaths): error_msg = "Timeout while waiting for a stanza that would match the expected xpath(s):" for xpath in xpaths: - error_msg += "\n" + xpath + error_msg += "\n" + str(xpath) self.error(error_msg) self.run_scenario() diff --git a/tests/end_to_end/functions.py b/tests/end_to_end/functions.py index 97cdfb0..3a21fcf 100644 --- a/tests/end_to_end/functions.py +++ b/tests/end_to_end/functions.py @@ -18,10 +18,10 @@ common_replacements = { 'jid_one': 'first@example.com', 'jid_two': 'second@example.com', 'jid_admin': 'admin@example.com', - 'nick_two': 'Bobby', - 'nick_three': 'Bernard', + 'nick_two': 'Nick2', + 'nick_three': 'Nick3', 'lower_nick_one': 'nick', - 'lower_nick_two': 'bobby', + 'lower_nick_two': 'nick2', } class SkipStepError(Exception): diff --git a/tests/end_to_end/scenarios/channel_messages.py b/tests/end_to_end/scenarios/channel_messages.py index cc358e1..e321114 100644 --- a/tests/end_to_end/scenarios/channel_messages.py +++ b/tests/end_to_end/scenarios/channel_messages.py @@ -21,15 +21,15 @@ scenario = ( ), # Send a channel message - send_stanza("<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='groupchat'><body>coucou</body></message>"), + send_stanza("<message id='first_id' from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='groupchat'><body>coucou</body></message>"), # Receive the message, forwarded to the two users expect_unordered( [ - "/message[@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='coucou']", + "/message[@id='first_id'][@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_one}/{resource_one}'][@type='groupchat']/body[text()='coucou']", "/message/stable_id:stanza-id[@by='#foo%{irc_server_one}'][@id]" ], [ - "/message[@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_two}/{resource_one}'][@type='groupchat']/body[text()='coucou']", + "/message[@id][@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_two}/{resource_one}'][@type='groupchat']/body[text()='coucou']", "/message/stable_id:stanza-id[@by='#foo%{irc_server_one}'][@id]" ] ), diff --git a/tests/end_to_end/scenarios/muc_disco_info.py b/tests/end_to_end/scenarios/muc_disco_info.py index 0c1aea5..36fdbc6 100644 --- a/tests/end_to_end/scenarios/muc_disco_info.py +++ b/tests/end_to_end/scenarios/muc_disco_info.py @@ -10,6 +10,7 @@ scenario = ( "/iq/disco_info:query/disco_info:feature[@var='urn:xmpp:mam:2']", "/iq/disco_info:query/disco_info:feature[@var='jabber:iq:version']", "/iq/disco_info:query/disco_info:feature[@var='muc_nonanonymous']", + "/iq/disco_info:query/disco_info:feature[@var='urn:xmpp:sid:0']", "!/iq/disco_info:query/dataform:x/dataform:field[@var='muc#roominfo_occupants']"), # Join the channel, and re-do the same query diff --git a/tests/end_to_end/scenarios/multisessionnick.py b/tests/end_to_end/scenarios/multisessionnick.py index f47b18e..761e66e 100644 --- a/tests/end_to_end/scenarios/multisessionnick.py +++ b/tests/end_to_end/scenarios/multisessionnick.py @@ -70,8 +70,8 @@ scenario = ( # First occupant (with the two resources) changes her/his nick to a conflicting one send_stanza("<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_two}' />"), expect_unordered( - ["/message[@to='{jid_one}/{resource_one}'][@type='chat']/body[text()='irc.localhost: Bobby: Nickname is already in use.']"], - ["/message[@to='{jid_one}/{resource_two}'][@type='chat']/body[text()='irc.localhost: Bobby: Nickname is already in use.']"], + ["/message[@to='{jid_one}/{resource_one}'][@type='chat']/body[text()='irc.localhost: Nick2: Nickname is already in use.']"], + ["/message[@to='{jid_one}/{resource_two}'][@type='chat']/body[text()='irc.localhost: Nick2: Nickname is already in use.']"], ["/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}'][@type='error']"], ["/presence[@to='{jid_one}/{resource_two}'][@from='#foo%{irc_server_one}/{nick_two}'][@type='error']"] ), @@ -80,14 +80,14 @@ scenario = ( send_stanza("<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_three}' />"), expect_unordered( [ - "/presence[@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_two}/{resource_one}'][@type='unavailable']/muc_user:x/muc_user:item[@nick='Bernard']", + "/presence[@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_two}/{resource_one}'][@type='unavailable']/muc_user:x/muc_user:item[@nick='Nick3']", "/presence/muc_user:x/muc_user:status[@code='303']" ], [ "/presence[@from='#foo%{irc_server_one}/{nick_three}'][@to='{jid_two}/{resource_one}']" ], [ - "/presence[@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_one}/{resource_one}'][@type='unavailable']/muc_user:x/muc_user:item[@nick='Bernard']", + "/presence[@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_one}/{resource_one}'][@type='unavailable']/muc_user:x/muc_user:item[@nick='Nick3']", "/presence/muc_user:x/muc_user:status[@code='303']", "/presence/muc_user:x/muc_user:status[@code='110']" ], @@ -96,7 +96,7 @@ scenario = ( "/presence/muc_user:x/muc_user:status[@code='110']" ], [ - "/presence[@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_one}/{resource_two}'][@type='unavailable']/muc_user:x/muc_user:item[@nick='Bernard']", + "/presence[@from='#foo%{irc_server_one}/{nick_one}'][@to='{jid_one}/{resource_two}'][@type='unavailable']/muc_user:x/muc_user:item[@nick='Nick3']", "/presence/muc_user:x/muc_user:status[@code='303']", "/presence/muc_user:x/muc_user:status[@code='110']" ], diff --git a/tests/end_to_end/scenarios/stable_id.py b/tests/end_to_end/scenarios/stable_id.py new file mode 100644 index 0000000..9f3181b --- /dev/null +++ b/tests/end_to_end/scenarios/stable_id.py @@ -0,0 +1,32 @@ +from scenarios import * + +import scenarios.simple_channel_join + +# see https://xmpp.org/extensions/xep-0359.html + +scenario = ( + scenarios.simple_channel_join.scenario, + + send_stanza("""<message id='first_id' from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='groupchat'> + <origin-id xmlns='urn:xmpp:sid:0' id='client-origin-id-should-be-kept'/> + <stanza-id xmlns='urn:xmpp:sid:0' id='client-stanza-id-should-be-removed' by='#foo%{irc_server_one}'/> + <stanza-id xmlns='urn:xmpp:sid:0' id='client-stanza-id-should-be-kept' by='someother@jid'/> + <body>coucou</body></message>"""), + + # Entities, which are routing stanzas, SHOULD NOT strip any elements + # qualified by the 'urn:xmpp:sid:0' namespace from message stanzas + # unless the preceding rule applied to those elements. + expect_stanza("/message/stable_id:origin-id[@id='client-origin-id-should-be-kept']", + # Stanza ID generating entities, which encounter a <stanza-id/> + # element where the 'by' attribute matches the 'by' attribute they + # would otherwise set, MUST delete that element even if they are not + # adding their own stanza ID. + "/message/stable_id:stanza-id[@id][@by='#foo%{irc_server_one}']", + "!/message/stable_id:stanza-id[@id='client-stanza-id-should-be-removed']", + # Entities, which are routing stanzas, SHOULD NOT strip + # any elements qualified by the 'urn:xmpp:sid:0' + # namespace from message stanzas unless the preceding + # rule applied to those elements. + "/message/stable_id:stanza-id[@id='client-stanza-id-should-be-kept'][@by='someother@jid']", + ), +) diff --git a/unit/biboumi.service.cmake b/unit/biboumi.service.cmake index 150045b..bcbda1f 100644 --- a/unit/biboumi.service.cmake +++ b/unit/biboumi.service.cmake @@ -11,6 +11,7 @@ WatchdogSec=${WATCHDOG_SEC} Restart=always User=${SERVICE_USER} Group=${SERVICE_GROUP} +AmbientCapabilities=CAP_NET_BIND_SERVICE [Install] WantedBy=multi-user.target |