Send a stanza error when receiving bad stanzas

Whether this is a bad-request (missing XML attributes or elements) or an
internal server error.
This commit is contained in:
Florent Le Coz
2014-03-28 01:16:05 +01:00
parent 2ffbfb71dd
commit 3c5f823621
2 changed files with 116 additions and 10 deletions
+110 -10
View File
@@ -1,4 +1,5 @@
#include <utils/make_unique.hpp> #include <utils/make_unique.hpp>
#include <utils/scopeguard.hpp>
#include <logger/logger.hpp> #include <logger/logger.hpp>
#include <xmpp/xmpp_component.hpp> #include <xmpp/xmpp_component.hpp>
@@ -204,6 +205,38 @@ void XmppComponent::send_stream_error(const std::string& name, const std::string
this->send_stanza(node); this->send_stanza(node);
} }
void XmppComponent::send_stanza_error(const std::string& kind, const std::string& to, const std::string& from,
const std::string& id, const std::string& error_type,
const std::string& defined_condition, const std::string& text)
{
Stanza node(kind);
if (!to.empty())
node["to"] = to;
if (!from.empty())
node["from"] = from;
if (!id.empty())
node["id"] = id;
node["type"] = "error";
XmlNode error("error");
error["type"] = error_type;
XmlNode inner_error(defined_condition);
inner_error["xmlns"] = STANZA_NS;
inner_error.close();
error.add_child(std::move(inner_error));
if (!text.empty())
{
XmlNode text_node("text");
text_node["xmlns"] = STANZA_NS;
text_node.set_inner(text);
text_node.close();
error.add_child(std::move(text_node));
}
error.close();
node.add_child(std::move(error));
node.close();
this->send_stanza(node);
}
void XmppComponent::close_document() void XmppComponent::close_document()
{ {
log_debug("XMPP SENDING: </stream:stream>"); log_debug("XMPP SENDING: </stream:stream>");
@@ -222,6 +255,14 @@ void XmppComponent::handle_handshake(const Stanza& stanza)
void XmppComponent::handle_presence(const Stanza& stanza) void XmppComponent::handle_presence(const Stanza& stanza)
{ {
std::string id;
try {
id = stanza["id"];
} catch (const AttributeNotFound&) {}
utils::ScopeGuard malformed_stanza_error([this, &id](){
this->send_stanza_error("presence", "", this->served_hostname, id,
"modify", "bad-request", "");
});
Bridge* bridge = this->get_user_bridge(stanza["from"]); Bridge* bridge = this->get_user_bridge(stanza["from"]);
Jid to(stanza["to"]); Jid to(stanza["to"]);
Iid iid(to.local); Iid iid(to.local);
@@ -230,6 +271,20 @@ void XmppComponent::handle_presence(const Stanza& stanza)
type = stanza["type"]; type = stanza["type"];
} }
catch (const AttributeNotFound&) {} catch (const AttributeNotFound&) {}
malformed_stanza_error.disable();
// An error stanza is sent whenever we exit this function without
// disabling this scopeguard. If error_type and error_name are not
// changed, the error signaled is internal-server-error. Change their
// value to signal an other kind of error. For example
// feature-not-implemented, etc. Any non-error process should reach the
// stanza_error.disable() call at the end of the function.
std::string error_type("cancel");
std::string error_name("internal-server-error");
utils::ScopeGuard stanza_error([this, &stanza, &error_type, &error_name, &id](){
this->send_stanza_error("presence", stanza["from"], stanza["to"], id,
error_type, error_name, "");
});
if (!iid.chan.empty() && !iid.server.empty()) if (!iid.chan.empty() && !iid.server.empty())
{ // presence toward a MUC that corresponds to an irc channel { // presence toward a MUC that corresponds to an irc channel
@@ -252,15 +307,37 @@ void XmppComponent::handle_presence(const Stanza& stanza)
if (type.empty()) if (type.empty())
this->send_invalid_room_error(to.local, to.resource, stanza["from"]); this->send_invalid_room_error(to.local, to.resource, stanza["from"]);
} }
stanza_error.disable();
} }
void XmppComponent::handle_message(const Stanza& stanza) void XmppComponent::handle_message(const Stanza& stanza)
{ {
std::string id;
try {
id = stanza["id"];
} catch (const AttributeNotFound&) {}
utils::ScopeGuard malformed_stanza_error([this, &id](){
this->send_stanza_error("message", "", this->served_hostname, id,
"modify", "bad-request", "");
});
Bridge* bridge = this->get_user_bridge(stanza["from"]); Bridge* bridge = this->get_user_bridge(stanza["from"]);
Jid to(stanza["to"]); Jid to(stanza["to"]);
Iid iid(to.local); Iid iid(to.local);
std::string type;
try {
type = stanza["type"];
}
catch (const AttributeNotFound&) {}
malformed_stanza_error.disable();
std::string error_type("cancel");
std::string error_name("internal-server-error");
utils::ScopeGuard stanza_error([this, &stanza, &error_type, &error_name, &id](){
this->send_stanza_error("message", stanza["from"], stanza["to"], id,
error_type, error_name, "");
});
XmlNode* body = stanza.get_child(COMPONENT_NS":body"); XmlNode* body = stanza.get_child(COMPONENT_NS":body");
if (stanza["type"] == "groupchat") if (type == "groupchat")
{ {
if (to.resource.empty()) if (to.resource.empty())
if (body && !body->get_inner().empty()) if (body && !body->get_inner().empty())
@@ -274,25 +351,37 @@ void XmppComponent::handle_message(const Stanza& stanza)
if (body && !body->get_inner().empty()) if (body && !body->get_inner().empty())
bridge->send_private_message(iid, body->get_inner()); bridge->send_private_message(iid, body->get_inner());
} }
stanza_error.disable();
} }
void XmppComponent::handle_iq(const Stanza& stanza) void XmppComponent::handle_iq(const Stanza& stanza)
{ {
std::string id;
try {
id = stanza["id"];
} catch (const AttributeNotFound&) {}
utils::ScopeGuard malformed_stanza_error([this, &id](){
this->send_stanza_error("iq", "", this->served_hostname, id,
"modify", "bad-request", "");
});
Bridge* bridge = this->get_user_bridge(stanza["from"]); Bridge* bridge = this->get_user_bridge(stanza["from"]);
Jid to(stanza["to"]); Jid to(stanza["to"]);
std::string type; std::string type = stanza["type"];
try { malformed_stanza_error.disable();
type = stanza["type"];
} std::string error_type("cancel");
catch (const AttributeNotFound&) std::string error_name("internal-server-error");
{ return; } utils::ScopeGuard stanza_error([this, &stanza, &error_type, &error_name, &id](){
this->send_stanza_error("iq", stanza["from"], stanza["to"], id,
error_type, error_name, "");
});
if (type == "set") if (type == "set")
{ {
XmlNode* query; XmlNode* query;
if ((query = stanza.get_child(MUC_ADMIN_NS":query"))) if ((query = stanza.get_child(MUC_ADMIN_NS":query")))
{ {
XmlNode* child; const XmlNode* child = query->get_child(MUC_ADMIN_NS":item");
if ((child = query->get_child(MUC_ADMIN_NS":item"))) if (child)
{ {
std::string nick; std::string nick;
std::string role; std::string role;
@@ -301,7 +390,11 @@ void XmppComponent::handle_iq(const Stanza& stanza)
role = (*child)["role"]; role = (*child)["role"];
} }
catch (const AttributeNotFound&) catch (const AttributeNotFound&)
{ return; } {
error_type = "modify";
error_name = "bad-request";
return;
}
if (!nick.empty() && role == "none") if (!nick.empty() && role == "none")
{ {
std::string reason; std::string reason;
@@ -314,6 +407,13 @@ void XmppComponent::handle_iq(const Stanza& stanza)
} }
} }
} }
else
{
error_type = "cancel";
error_name = "feature-not-implemented";
return;
}
stanza_error.disable();
} }
void XmppComponent::handle_error(const Stanza& stanza) void XmppComponent::handle_error(const Stanza& stanza)
+6
View File
@@ -68,6 +68,12 @@ public:
* describing the error. * describing the error.
*/ */
void send_stream_error(const std::string& message, const std::string& explanation); void send_stream_error(const std::string& message, const std::string& explanation);
/**
* Send error stanza, described in http://xmpp.org/rfcs/rfc6120.html#stanzas-error
*/
void send_stanza_error(const std::string& kind, const std::string& to, const std::string& from,
const std::string& id, const std::string& error_type,
const std::string& defined_condition, const std::string& text);
/** /**
* Send the closing signal for our document (not closing the connection though). * Send the closing signal for our document (not closing the connection though).
*/ */