diff options
author | Florent Le Coz <louiz@louiz.org> | 2013-11-03 17:51:32 +0100 |
---|---|---|
committer | Florent Le Coz <louiz@louiz.org> | 2013-11-03 17:51:32 +0100 |
commit | f2f94618fcf87b4fc1ad86902c63a7a48be745b8 (patch) | |
tree | be3e64ef9119f91b043b5c2cea96f7508158d1f9 | |
parent | 5bbd34a3a909fa904ee4402f01dac6bac59211b1 (diff) | |
download | biboumi-f2f94618fcf87b4fc1ad86902c63a7a48be745b8.tar.gz biboumi-f2f94618fcf87b4fc1ad86902c63a7a48be745b8.tar.bz2 biboumi-f2f94618fcf87b4fc1ad86902c63a7a48be745b8.tar.xz biboumi-f2f94618fcf87b4fc1ad86902c63a7a48be745b8.zip |
Add a basic XMPP component implementation, doing the authentication
-rw-r--r-- | CMakeLists.txt | 9 | ||||
-rw-r--r-- | src/xmpp/xmpp_component.cpp | 137 | ||||
-rw-r--r-- | src/xmpp/xmpp_component.hpp | 80 |
3 files changed, 226 insertions, 0 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 1c21b84..363eb79 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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_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/") +# the SYSTEM flag tells the compiler that we don't care about warnings +# coming from these headers. +include_directories(SYSTEM ${CRYPTO++_INCLUDE_DIR}) # ## network diff --git a/src/xmpp/xmpp_component.cpp b/src/xmpp/xmpp_component.cpp new file mode 100644 index 0000000..0563aa7 --- /dev/null +++ b/src/xmpp/xmpp_component.cpp @@ -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; +} diff --git a/src/xmpp/xmpp_component.hpp b/src/xmpp/xmpp_component.hpp new file mode 100644 index 0000000..464ecaa --- /dev/null +++ b/src/xmpp/xmpp_component.hpp @@ -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 + |