Add a basic XMPP component implementation, doing the authentication
This commit is contained in:
@@ -8,7 +8,16 @@ set(${PROJECT_NAME}_VERSION_MINOR 1)
|
|||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -pedantic -Wall -Wextra")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -pedantic -Wall -Wextra")
|
||||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Og -fsanitize=address")
|
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Og -fsanitize=address")
|
||||||
|
|
||||||
|
#
|
||||||
|
## Look for external libraries
|
||||||
|
#
|
||||||
|
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/")
|
||||||
|
find_package(Cryptopp REQUIRED)
|
||||||
|
|
||||||
include_directories("src/")
|
include_directories("src/")
|
||||||
|
# the SYSTEM flag tells the compiler that we don't care about warnings
|
||||||
|
# coming from these headers.
|
||||||
|
include_directories(SYSTEM ${CRYPTO++_INCLUDE_DIR})
|
||||||
|
|
||||||
#
|
#
|
||||||
## network
|
## network
|
||||||
|
|||||||
@@ -0,0 +1,137 @@
|
|||||||
|
#include <xmpp/xmpp_component.hpp>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
// CryptoPP
|
||||||
|
#include <filters.h>
|
||||||
|
#include <hex.h>
|
||||||
|
#include <sha.h>
|
||||||
|
|
||||||
|
XmppComponent::XmppComponent(const std::string& hostname, const std::string& secret):
|
||||||
|
served_hostname(hostname),
|
||||||
|
secret(secret),
|
||||||
|
authenticated(false)
|
||||||
|
{
|
||||||
|
this->parser.add_stream_open_callback(std::bind(&XmppComponent::on_remote_stream_open, this,
|
||||||
|
std::placeholders::_1));
|
||||||
|
this->parser.add_stanza_callback(std::bind(&XmppComponent::on_stanza, this,
|
||||||
|
std::placeholders::_1));
|
||||||
|
this->parser.add_stream_close_callback(std::bind(&XmppComponent::on_remote_stream_close, this,
|
||||||
|
std::placeholders::_1));
|
||||||
|
this->stanza_handlers.emplace("handshake",
|
||||||
|
std::bind(&XmppComponent::handle_handshake, this,std::placeholders::_1));
|
||||||
|
}
|
||||||
|
|
||||||
|
XmppComponent::~XmppComponent()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void XmppComponent::start()
|
||||||
|
{
|
||||||
|
this->connect(this->served_hostname, "5347");
|
||||||
|
}
|
||||||
|
|
||||||
|
void XmppComponent::send_stanza(const Stanza& stanza)
|
||||||
|
{
|
||||||
|
std::cout << "====== Sending ========" << std::endl;
|
||||||
|
std::cout << stanza.to_string() << std::endl;
|
||||||
|
this->send_data(stanza.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
void XmppComponent::on_connected()
|
||||||
|
{
|
||||||
|
std::cout << "connected to XMPP server" << std::endl;
|
||||||
|
XmlNode node("stream:stream", nullptr);
|
||||||
|
node["xmlns"] = "jabber:component:accept";
|
||||||
|
node["xmlns:stream"] = "http://etherx.jabber.org/streams";
|
||||||
|
node["to"] = "irc.abricot";
|
||||||
|
this->send_stanza(node);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void XmppComponent::on_connection_close()
|
||||||
|
{
|
||||||
|
std::cout << "XMPP server closed connection" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void XmppComponent::parse_in_buffer()
|
||||||
|
{
|
||||||
|
this->parser.XML_Parse(this->in_buf.data(), this->in_buf.size(), false);
|
||||||
|
this->in_buf.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void XmppComponent::on_remote_stream_open(const XmlNode& node)
|
||||||
|
{
|
||||||
|
std::cout << "====== DOCUMENT_OPEN =======" << std::endl;
|
||||||
|
std::cout << node.to_string() << std::endl;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
this->stream_id = node["id"];
|
||||||
|
}
|
||||||
|
catch (const AttributeNotFound& e)
|
||||||
|
{
|
||||||
|
std::cout << "Error: no attribute 'id' found" << std::endl;
|
||||||
|
this->send_stream_error("bad-format", "missing 'id' attribute");
|
||||||
|
this->close_document();
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to authenticate
|
||||||
|
CryptoPP::SHA1 sha1;
|
||||||
|
std::string digest;
|
||||||
|
CryptoPP::StringSource foo(this->stream_id + this->secret, true,
|
||||||
|
new CryptoPP::HashFilter(sha1,
|
||||||
|
new CryptoPP::HexEncoder(
|
||||||
|
new CryptoPP::StringSink(digest), false)));
|
||||||
|
Stanza handshake("handshake", nullptr);
|
||||||
|
handshake.set_inner(digest);
|
||||||
|
handshake.close();
|
||||||
|
this->send_stanza(handshake);
|
||||||
|
}
|
||||||
|
|
||||||
|
void XmppComponent::on_remote_stream_close(const XmlNode& node)
|
||||||
|
{
|
||||||
|
std::cout << "====== DOCUMENT_CLOSE =======" << std::endl;
|
||||||
|
std::cout << node.to_string() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void XmppComponent::on_stanza(const Stanza& stanza)
|
||||||
|
{
|
||||||
|
std::cout << "=========== STANZA ============" << std::endl;
|
||||||
|
std::cout << stanza.to_string() << std::endl;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
const auto& handler = this->stanza_handlers.at(stanza.get_name());
|
||||||
|
handler(stanza);
|
||||||
|
}
|
||||||
|
catch (const std::out_of_range& exception)
|
||||||
|
{
|
||||||
|
std::cout << "No handler for stanza of type " << stanza.get_name() << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void XmppComponent::send_stream_error(const std::string& name, const std::string& explanation)
|
||||||
|
{
|
||||||
|
XmlNode node("stream:error", nullptr);
|
||||||
|
XmlNode error(name, nullptr);
|
||||||
|
error["xmlns"] = "urn:ietf:params:xml:ns:xmpp-streams";
|
||||||
|
if (!explanation.empty())
|
||||||
|
error.set_inner(explanation);
|
||||||
|
error.close();
|
||||||
|
node.add_child(std::move(error));
|
||||||
|
node.close();
|
||||||
|
this->send_stanza(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
void XmppComponent::close_document()
|
||||||
|
{
|
||||||
|
std::cout << "====== Sending ========" << std::endl;
|
||||||
|
std::cout << "</stream:stream>" << std::endl;
|
||||||
|
this->send_data("</stream:stream>");
|
||||||
|
}
|
||||||
|
|
||||||
|
void XmppComponent::handle_handshake(const Stanza& stanza)
|
||||||
|
{
|
||||||
|
this->authenticated = true;
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
#ifndef XMPP_COMPONENT_INCLUDED
|
||||||
|
# define XMPP_COMPONENT_INCLUDED
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <network/socket_handler.hpp>
|
||||||
|
|
||||||
|
#include <xmpp/xmpp_parser.hpp>
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An XMPP component, communicating with an XMPP server using the protocole
|
||||||
|
* described in XEP-0114: Jabber Component Protocol
|
||||||
|
*
|
||||||
|
* TODO: implement XEP-0225: Component Connections
|
||||||
|
*/
|
||||||
|
class XmppComponent: public SocketHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit XmppComponent(const std::string& hostname, const std::string& secret);
|
||||||
|
~XmppComponent();
|
||||||
|
void on_connected();
|
||||||
|
void on_connection_close();
|
||||||
|
void parse_in_buffer();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connect to the XMPP server
|
||||||
|
*/
|
||||||
|
void start();
|
||||||
|
/**
|
||||||
|
* Serialize the stanza and add it to the out_buf to be sent to the
|
||||||
|
* server.
|
||||||
|
*/
|
||||||
|
void send_stanza(const Stanza& stanza);
|
||||||
|
/**
|
||||||
|
* Handle the opening of the remote stream
|
||||||
|
*/
|
||||||
|
void on_remote_stream_open(const XmlNode& node);
|
||||||
|
/**
|
||||||
|
* Handle the closing of the remote stream
|
||||||
|
*/
|
||||||
|
void on_remote_stream_close(const XmlNode& node);
|
||||||
|
/**
|
||||||
|
* Handle received stanzas
|
||||||
|
*/
|
||||||
|
void on_stanza(const Stanza& stanza);
|
||||||
|
/**
|
||||||
|
* Send an error stanza. Message being the name of the element inside the
|
||||||
|
* stanza, and explanation being a short human-readable sentence
|
||||||
|
* describing the error.
|
||||||
|
*/
|
||||||
|
void send_stream_error(const std::string& message, const std::string& explanation);
|
||||||
|
/**
|
||||||
|
* Send the closing signal for our document (not closing the connection though).
|
||||||
|
*/
|
||||||
|
void close_document();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the various stanza types
|
||||||
|
*/
|
||||||
|
void handle_handshake(const Stanza& stanza);
|
||||||
|
|
||||||
|
private:
|
||||||
|
XmppParser parser;
|
||||||
|
std::string stream_id;
|
||||||
|
std::string served_hostname;
|
||||||
|
std::string secret;
|
||||||
|
bool authenticated;
|
||||||
|
|
||||||
|
std::unordered_map<std::string, std::function<void(const Stanza&)>> stanza_handlers;
|
||||||
|
|
||||||
|
XmppComponent(const XmppComponent&) = delete;
|
||||||
|
XmppComponent(XmppComponent&&) = delete;
|
||||||
|
XmppComponent& operator=(const XmppComponent&) = delete;
|
||||||
|
XmppComponent& operator=(XmppComponent&&) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // XMPP_COMPONENT_INCLUDED
|
||||||
|
|
||||||
Reference in New Issue
Block a user