diff options
-rw-r--r-- | CHANGELOG.rst | 1 | ||||
-rw-r--r-- | CMakeLists.txt | 14 | ||||
-rw-r--r-- | doc/biboumi.1.rst | 6 | ||||
-rw-r--r-- | louloulibs/network/resolver.hpp | 2 | ||||
-rw-r--r-- | louloulibs/network/tcp_client_socket_handler.cpp | 25 | ||||
-rw-r--r-- | louloulibs/network/tcp_client_socket_handler.hpp | 7 | ||||
-rw-r--r-- | louloulibs/network/tcp_server_socket.hpp | 86 | ||||
-rw-r--r-- | src/bridge/bridge.cpp | 5 | ||||
-rw-r--r-- | src/bridge/bridge.hpp | 1 | ||||
-rw-r--r-- | src/identd/identd_server.hpp | 38 | ||||
-rw-r--r-- | src/identd/identd_socket.cpp | 69 | ||||
-rw-r--r-- | src/identd/identd_socket.hpp | 44 | ||||
-rw-r--r-- | src/main.cpp | 43 | ||||
-rw-r--r-- | src/xmpp/biboumi_component.cpp | 1 |
14 files changed, 323 insertions, 19 deletions
diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 5f61106..dfc435b 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,7 @@ Version 5.0 =========== + - An identd server has been added Version 4.0 - 2016-11-09 ======================== diff --git a/CMakeLists.txt b/CMakeLists.txt index 4b51b87..10815b7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -155,6 +155,19 @@ if(USE_DATABASE) endif() # +## identd +# +file(GLOB source_identd + src/identd/*.[hc]pp) +add_library(identd STATIC ${source_identd}) +target_link_libraries(identd bridge network utils src_utils logger) + +if(USE_DATABASE) + target_link_libraries(xmpp database) + target_link_libraries(irc database) +endif() + +# ## bridge # file(GLOB source_bridge @@ -172,6 +185,7 @@ target_link_libraries(${PROJECT_NAME} bridge utils src_utils + identd config) if(SYSTEMD_FOUND) target_link_libraries(xmpp ${SYSTEMD_LIBRARIES}) diff --git a/doc/biboumi.1.rst b/doc/biboumi.1.rst index 1df125d..06f32ff 100644 --- a/doc/biboumi.1.rst +++ b/doc/biboumi.1.rst @@ -152,6 +152,12 @@ for example use outgoing_bind=192.168.1.11 to force biboumi to use the interface with this address. Note that this is only used for connections to IRC servers. +identd_port +----------- + +The TCP port on which to listen for identd queries. The default is the standard value: 113. + + Usage ===== diff --git a/louloulibs/network/resolver.hpp b/louloulibs/network/resolver.hpp index 29e6f3a..7365f93 100644 --- a/louloulibs/network/resolver.hpp +++ b/louloulibs/network/resolver.hpp @@ -125,5 +125,3 @@ private: }; std::string addr_to_string(const struct addrinfo* rp); - - diff --git a/louloulibs/network/tcp_client_socket_handler.cpp b/louloulibs/network/tcp_client_socket_handler.cpp index f22d756..dc9221d 100644 --- a/louloulibs/network/tcp_client_socket_handler.cpp +++ b/louloulibs/network/tcp_client_socket_handler.cpp @@ -154,6 +154,25 @@ void TCPClientSocketHandler::connect(const std::string& address, const std::stri #endif this->connection_date = std::chrono::system_clock::now(); + // Get our local TCP port and store it + this->local_port = static_cast<uint16_t>(-1); + if (rp->ai_family == AF_INET6) + { + struct sockaddr_in6 a; + socklen_t l = sizeof(a); + if (::getsockname(this->socket, (struct sockaddr*)&a, &l) != -1) + this->local_port = ::ntohs(a.sin6_port); + } + else if (rp->ai_family == AF_INET) + { + struct sockaddr_in a; + socklen_t l = sizeof(a); + if (::getsockname(this->socket, (struct sockaddr*)&a, &l) != -1) + this->local_port = ::ntohs(a.sin_port); + } + + log_debug("Local port: ", this->local_port, ", and remote port: ", this->port); + this->on_connected(); return ; } @@ -231,3 +250,9 @@ std::string TCPClientSocketHandler::get_port() const { return this->port; } + +bool TCPClientSocketHandler::match_port_pairt(const uint16_t local, const uint16_t remote) const +{ + const uint16_t remote_port = static_cast<uint16_t>(std::stoi(this->port)); + return this->is_connected() && local == this->local_port && remote == remote_port; +} diff --git a/louloulibs/network/tcp_client_socket_handler.hpp b/louloulibs/network/tcp_client_socket_handler.hpp index 7dd476c..3ad8ec3 100644 --- a/louloulibs/network/tcp_client_socket_handler.hpp +++ b/louloulibs/network/tcp_client_socket_handler.hpp @@ -31,6 +31,11 @@ class TCPClientSocketHandler: public TCPSocketHandler void close() override final; std::chrono::system_clock::time_point connection_date; + /** + * 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; + protected: bool hostname_resolution_failed; /** @@ -70,6 +75,8 @@ class TCPClientSocketHandler: public TCPSocketHandler */ std::string port; + uint16_t local_port; + bool connected; bool connecting; }; diff --git a/louloulibs/network/tcp_server_socket.hpp b/louloulibs/network/tcp_server_socket.hpp new file mode 100644 index 0000000..a54d3bf --- /dev/null +++ b/louloulibs/network/tcp_server_socket.hpp @@ -0,0 +1,86 @@ +#pragma once + +#include <network/socket_handler.hpp> +#include <network/resolver.hpp> +#include <network/poller.hpp> + +#include <string> + +#include <arpa/inet.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/ip.h> + +#include <logger/logger.hpp> + +#include <cstring> +#include <cassert> + +using namespace std::string_literals; + +template <typename RemoteSocketType> +class TcpSocketServer: public SocketHandler +{ + public: + TcpSocketServer(std::shared_ptr<Poller> poller, const uint16_t port): + SocketHandler(poller, -1) + { + if ((this->socket = ::socket(AF_INET6, SOCK_STREAM, 0)) == -1) + throw std::runtime_error("Could not create socket: "s + std::strerror(errno)); + + int opt = 1; + if (::setsockopt(this->socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1) + throw std::runtime_error("Failed to set socket option: "s + std::strerror(errno)); + + struct sockaddr_in6 addr{}; + addr.sin6_family = AF_INET6; + addr.sin6_port = htons(port); + addr.sin6_addr = IN6ADDR_ANY_INIT; + if ((::bind(this->socket, (const struct sockaddr*)&addr, sizeof(addr))) == -1) + { // If we can’t listen on this port, we just give up, but this is not fatal. + log_warning("Failed to bind on port ", std::to_string(port), ": ", std::strerror(errno)); + return; + } + + if ((::listen(this->socket, 10)) == -1) + throw std::runtime_error("listen() failed"); + + this->accept(); + } + ~TcpSocketServer() = default; + + void on_recv() override + { + // Accept a RemoteSocketType + int socket = ::accept(this->socket, nullptr, nullptr); + + auto client = std::make_unique<RemoteSocketType>(poller, socket, *this); + this->poller->add_socket_handler(client.get()); + this->sockets.push_back(std::move(client)); + } + + protected: + std::vector<std::unique_ptr<RemoteSocketType>> sockets; + + private: + void accept() + { + this->poller->add_socket_handler(this); + } + void on_send() override + { + assert(false); + } + void connect() override + { + assert(false); + } + bool is_connected() const override + { + return true; + } + bool is_connecting() const override + { + return false; + } +}; diff --git a/src/bridge/bridge.cpp b/src/bridge/bridge.cpp index a0ecc6e..6fb03bd 100644 --- a/src/bridge/bridge.cpp +++ b/src/bridge/bridge.cpp @@ -1033,6 +1033,11 @@ std::unordered_map<std::string, std::shared_ptr<IrcClient>>& Bridge::get_irc_cli return this->irc_clients; } +const std::unordered_map<std::string, std::shared_ptr<IrcClient>>& Bridge::get_irc_clients() const +{ + return this->irc_clients; +} + std::set<char> Bridge::get_chantypes(const std::string& hostname) const { IrcClient* irc = this->find_irc_client(hostname); diff --git a/src/bridge/bridge.hpp b/src/bridge/bridge.hpp index 18ebfeb..8f2dcef 100644 --- a/src/bridge/bridge.hpp +++ b/src/bridge/bridge.hpp @@ -231,6 +231,7 @@ public: */ void trigger_on_irc_message(const std::string& irc_hostname, const IrcMessage& message); std::unordered_map<std::string, std::shared_ptr<IrcClient>>& get_irc_clients(); + const std::unordered_map<std::string, std::shared_ptr<IrcClient>>& get_irc_clients() const; std::set<char> get_chantypes(const std::string& hostname) const; #ifdef USE_DATABASE void set_record_history(const bool val); diff --git a/src/identd/identd_server.hpp b/src/identd/identd_server.hpp new file mode 100644 index 0000000..4270749 --- /dev/null +++ b/src/identd/identd_server.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include <network/tcp_server_socket.hpp> +#include <identd/identd_socket.hpp> +#include <unistd.h> + +class BiboumiComponent; + +class IdentdServer: public TcpSocketServer<IdentdSocket> +{ + public: + IdentdServer(const BiboumiComponent& biboumi_component, std::shared_ptr<Poller> poller, const uint16_t port): + TcpSocketServer<IdentdSocket>(poller, port), + biboumi_component(biboumi_component) + {} + + const BiboumiComponent& get_biboumi_component() const + { + return this->biboumi_component; + } + void shutdown() + { + if (this->poller->is_managing_socket(this->socket)) + this->poller->remove_socket_handler(this->socket); + ::close(this->socket); + } + void clean() + { + this->sockets.erase(std::remove_if(this->sockets.begin(), this->sockets.end(), + [](const std::unique_ptr<IdentdSocket>& socket) + { + return socket->get_socket() == -1; + }), + this->sockets.end()); + } + private: + const BiboumiComponent& biboumi_component; +}; diff --git a/src/identd/identd_socket.cpp b/src/identd/identd_socket.cpp new file mode 100644 index 0000000..c1ad55e --- /dev/null +++ b/src/identd/identd_socket.cpp @@ -0,0 +1,69 @@ +#include <identd/identd_socket.hpp> +#include <identd/identd_server.hpp> +#include <xmpp/biboumi_component.hpp> +#include <iomanip> + +#include <utils/sha1.hpp> + +#include <logger/logger.hpp> + +IdentdSocket::IdentdSocket(std::shared_ptr<Poller> poller, const socket_t socket, TcpSocketServer<IdentdSocket>& server): + TCPSocketHandler(poller), + server(dynamic_cast<IdentdServer&>(server)) +{ + this->socket = socket; +} + +void IdentdSocket::parse_in_buffer(const std::size_t) +{ + while (true) + { + const auto line_end = this->in_buf.find('\n'); + if (line_end == std::string::npos) + break; + 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; + char sep; + line >> local_port >> sep >> remote_port; + const auto& xmpp = this->server.get_biboumi_component(); + auto response = this->generate_answer(xmpp, local_port, remote_port); + + this->send_data(std::move(response)); + } +} + +static std::string hash_jid(const std::string& jid) +{ + sha1nfo sha1; + sha1_init(&sha1); + sha1_write(&sha1, jid.data(), jid.size()); + const uint8_t* res = sha1_result(&sha1); + std::ostringstream result; + for (int i = 0; i < HASH_LENGTH; i++) + result << std::hex << std::setfill('0') << std::setw(2) << static_cast<int>(res[i]); + return result.str(); +} + +std::string IdentdSocket::generate_answer(const BiboumiComponent& biboumi, uint16_t local, uint16_t remote) +{ + for (const Bridge* bridge: biboumi.get_bridges()) + { + for (const auto& pair: bridge->get_irc_clients()) + { + if (pair.second->match_port_pairt(local, remote)) + { + std::ostringstream os; + os << local << " , " << remote << " : USERID : OTHER : " << hash_jid(bridge->get_bare_jid()); + log_debug("Identd, sending: ", os.str()); + return os.str(); + } + } + } + std::ostringstream os; + os << local << " , " << remote << " ERROR : NO-USER"; + log_debug("Identd, sending: ", os.str()); + return os.str(); +} diff --git a/src/identd/identd_socket.hpp b/src/identd/identd_socket.hpp new file mode 100644 index 0000000..6d6cc1d --- /dev/null +++ b/src/identd/identd_socket.hpp @@ -0,0 +1,44 @@ +#pragma once + +#include <network/socket_handler.hpp> + +#include <cassert> +#include <network/tcp_socket_handler.hpp> + +#include <logger/logger.hpp> +#include <xmpp/biboumi_component.hpp> + +class XmppComponent; +class IdentdSocket; +class IdentdServer; +template <typename T> +class TcpSocketServer; + +class IdentdSocket: public TCPSocketHandler +{ + public: + IdentdSocket(std::shared_ptr<Poller> poller, const socket_t socket, TcpSocketServer<IdentdSocket>& server); + ~IdentdSocket() = default; + std::string generate_answer(const BiboumiComponent& biboumi, uint16_t local, uint16_t remote); + + void parse_in_buffer(const std::size_t size) override final; + void on_connection_close(const std::string& message) override final + {} + + bool is_connected() const override final + { + return true; + } + bool is_connecting() const override final + { + return false; + } + + private: + void connect() override + { assert(false); } + void on_connection_failed(const std::string&) override final + { assert(false); } + + IdentdServer& server; +}; diff --git a/src/main.cpp b/src/main.cpp index 019dff0..072345f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -14,6 +14,8 @@ #include <signal.h> #include <litesql.hpp> +#include <identd/identd_server.hpp> + // A flag set by the SIGINT signal handler. static std::atomic<bool> stop(false); // Flag set by the SIGUSR1/2 signal handler. @@ -121,10 +123,13 @@ int main(int ac, char** av) sigaction(SIGUSR2, &on_sigusr, nullptr); auto p = std::make_shared<Poller>(); + auto xmpp_component = std::make_shared<BiboumiComponent>(p, hostname, password); xmpp_component->start(); + IdentdServer identd(*xmpp_component, p, static_cast<uint16_t>(Config::get_int("identd_port", 113))); + #ifdef CARES_FOUND DNSHandler::instance.watch_dns_sockets(p); #endif @@ -135,6 +140,7 @@ int main(int ac, char** av) // Check for empty irc_clients (not connected, or with no joined // channel) and remove them xmpp_component->clean(); + identd.clean(); if (stop) { log_info("Signal received, exiting..."); @@ -144,6 +150,7 @@ int main(int ac, char** av) exiting = true; stop.store(false); xmpp_component->shutdown(); + identd.shutdown(); // Cancel the timer for a potential reconnection TimedEventsManager::instance().cancel("XMPP reconnection"); } @@ -157,26 +164,30 @@ int main(int ac, char** av) // happened because we sent something invalid to it and it decided to // close the connection. This is a bug that should be fixed, but we // still reconnect automatically instead of dropping everything - if (!exiting && xmpp_component->ever_auth && + if (!exiting && !xmpp_component->is_connected() && !xmpp_component->is_connecting()) { - if (xmpp_component->first_connection_try == true) - { // immediately re-try to connect - xmpp_component->reset(); - xmpp_component->start(); - } - else - { // Re-connecting failed, we now try only each few seconds - auto reconnect_later = [xmpp_component]() + if (xmpp_component->ever_auth) { - xmpp_component->reset(); - xmpp_component->start(); - }; - TimedEvent event(std::chrono::steady_clock::now() + 2s, - reconnect_later, "XMPP reconnection"); - TimedEventsManager::instance().add_event(std::move(event)); - } + if (xmpp_component->first_connection_try == true) + { // immediately re-try to connect + xmpp_component->reset(); + xmpp_component->start(); + } + else + { // Re-connecting failed, we now try only each few seconds + auto reconnect_later = [xmpp_component]() + { + xmpp_component->reset(); + xmpp_component->start(); + }; + TimedEvent event(std::chrono::steady_clock::now() + 2s, reconnect_later, "XMPP reconnection"); + TimedEventsManager::instance().add_event(std::move(event)); + } + } + else + identd.shutdown(); } // If the only existing connection is the one to the XMPP component: // close the XMPP stream. diff --git a/src/xmpp/biboumi_component.cpp b/src/xmpp/biboumi_component.cpp index d6782e2..ca24f3a 100644 --- a/src/xmpp/biboumi_component.cpp +++ b/src/xmpp/biboumi_component.cpp @@ -8,7 +8,6 @@ #include <xmpp/biboumi_adhoc_commands.hpp> #include <bridge/list_element.hpp> #include <config/config.hpp> -#include <utils/sha1.hpp> #include <utils/time.hpp> #include <xmpp/jid.hpp> |