Support the ident protocol

fix #3211
This commit is contained in:
louiz’
2016-11-15 00:23:19 +01:00
parent ed7e66471f
commit d872c2b492
14 changed files with 323 additions and 19 deletions
+1
View File
@@ -1,6 +1,7 @@
Version 5.0 Version 5.0
=========== ===========
- An identd server has been added
Version 4.0 - 2016-11-09 Version 4.0 - 2016-11-09
======================== ========================
+14
View File
@@ -154,6 +154,19 @@ if(USE_DATABASE)
target_link_libraries(irc database) target_link_libraries(irc database)
endif() 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 ## bridge
# #
@@ -172,6 +185,7 @@ target_link_libraries(${PROJECT_NAME}
bridge bridge
utils utils
src_utils src_utils
identd
config) config)
if(SYSTEMD_FOUND) if(SYSTEMD_FOUND)
target_link_libraries(xmpp ${SYSTEMD_LIBRARIES}) target_link_libraries(xmpp ${SYSTEMD_LIBRARIES})
+6
View File
@@ -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 interface with this address. Note that this is only used for connections
to IRC servers. to IRC servers.
identd_port
-----------
The TCP port on which to listen for identd queries. The default is the standard value: 113.
Usage Usage
===== =====
-2
View File
@@ -125,5 +125,3 @@ private:
}; };
std::string addr_to_string(const struct addrinfo* rp); std::string addr_to_string(const struct addrinfo* rp);
@@ -154,6 +154,25 @@ void TCPClientSocketHandler::connect(const std::string& address, const std::stri
#endif #endif
this->connection_date = std::chrono::system_clock::now(); 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(); this->on_connected();
return ; return ;
} }
@@ -231,3 +250,9 @@ std::string TCPClientSocketHandler::get_port() const
{ {
return this->port; 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;
}
@@ -31,6 +31,11 @@ class TCPClientSocketHandler: public TCPSocketHandler
void close() override final; void close() override final;
std::chrono::system_clock::time_point connection_date; 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: protected:
bool hostname_resolution_failed; bool hostname_resolution_failed;
/** /**
@@ -70,6 +75,8 @@ class TCPClientSocketHandler: public TCPSocketHandler
*/ */
std::string port; std::string port;
uint16_t local_port;
bool connected; bool connected;
bool connecting; bool connecting;
}; };
+86
View File
@@ -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;
}
};
+5
View File
@@ -1033,6 +1033,11 @@ std::unordered_map<std::string, std::shared_ptr<IrcClient>>& Bridge::get_irc_cli
return this->irc_clients; 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 std::set<char> Bridge::get_chantypes(const std::string& hostname) const
{ {
IrcClient* irc = this->find_irc_client(hostname); IrcClient* irc = this->find_irc_client(hostname);
+1
View File
@@ -231,6 +231,7 @@ public:
*/ */
void trigger_on_irc_message(const std::string& irc_hostname, const IrcMessage& message); 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(); 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; std::set<char> get_chantypes(const std::string& hostname) const;
#ifdef USE_DATABASE #ifdef USE_DATABASE
void set_record_history(const bool val); void set_record_history(const bool val);
+38
View File
@@ -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;
};
+69
View File
@@ -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();
}
+44
View File
@@ -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;
};
+14 -3
View File
@@ -14,6 +14,8 @@
#include <signal.h> #include <signal.h>
#include <litesql.hpp> #include <litesql.hpp>
#include <identd/identd_server.hpp>
// A flag set by the SIGINT signal handler. // A flag set by the SIGINT signal handler.
static std::atomic<bool> stop(false); static std::atomic<bool> stop(false);
// Flag set by the SIGUSR1/2 signal handler. // Flag set by the SIGUSR1/2 signal handler.
@@ -121,10 +123,13 @@ int main(int ac, char** av)
sigaction(SIGUSR2, &on_sigusr, nullptr); sigaction(SIGUSR2, &on_sigusr, nullptr);
auto p = std::make_shared<Poller>(); auto p = std::make_shared<Poller>();
auto xmpp_component = auto xmpp_component =
std::make_shared<BiboumiComponent>(p, hostname, password); std::make_shared<BiboumiComponent>(p, hostname, password);
xmpp_component->start(); xmpp_component->start();
IdentdServer identd(*xmpp_component, p, static_cast<uint16_t>(Config::get_int("identd_port", 113)));
#ifdef CARES_FOUND #ifdef CARES_FOUND
DNSHandler::instance.watch_dns_sockets(p); DNSHandler::instance.watch_dns_sockets(p);
#endif #endif
@@ -135,6 +140,7 @@ int main(int ac, char** av)
// Check for empty irc_clients (not connected, or with no joined // Check for empty irc_clients (not connected, or with no joined
// channel) and remove them // channel) and remove them
xmpp_component->clean(); xmpp_component->clean();
identd.clean();
if (stop) if (stop)
{ {
log_info("Signal received, exiting..."); log_info("Signal received, exiting...");
@@ -144,6 +150,7 @@ int main(int ac, char** av)
exiting = true; exiting = true;
stop.store(false); stop.store(false);
xmpp_component->shutdown(); xmpp_component->shutdown();
identd.shutdown();
// Cancel the timer for a potential reconnection // Cancel the timer for a potential reconnection
TimedEventsManager::instance().cancel("XMPP reconnection"); TimedEventsManager::instance().cancel("XMPP reconnection");
} }
@@ -157,9 +164,11 @@ int main(int ac, char** av)
// happened because we sent something invalid to it and it decided to // 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 // close the connection. This is a bug that should be fixed, but we
// still reconnect automatically instead of dropping everything // still reconnect automatically instead of dropping everything
if (!exiting && xmpp_component->ever_auth && if (!exiting &&
!xmpp_component->is_connected() && !xmpp_component->is_connected() &&
!xmpp_component->is_connecting()) !xmpp_component->is_connecting())
{
if (xmpp_component->ever_auth)
{ {
if (xmpp_component->first_connection_try == true) if (xmpp_component->first_connection_try == true)
{ // immediately re-try to connect { // immediately re-try to connect
@@ -173,11 +182,13 @@ int main(int ac, char** av)
xmpp_component->reset(); xmpp_component->reset();
xmpp_component->start(); xmpp_component->start();
}; };
TimedEvent event(std::chrono::steady_clock::now() + 2s, TimedEvent event(std::chrono::steady_clock::now() + 2s, reconnect_later, "XMPP reconnection");
reconnect_later, "XMPP reconnection");
TimedEventsManager::instance().add_event(std::move(event)); TimedEventsManager::instance().add_event(std::move(event));
} }
} }
else
identd.shutdown();
}
// If the only existing connection is the one to the XMPP component: // If the only existing connection is the one to the XMPP component:
// close the XMPP stream. // close the XMPP stream.
if (exiting && xmpp_component->is_connecting()) if (exiting && xmpp_component->is_connecting())
-1
View File
@@ -8,7 +8,6 @@
#include <xmpp/biboumi_adhoc_commands.hpp> #include <xmpp/biboumi_adhoc_commands.hpp>
#include <bridge/list_element.hpp> #include <bridge/list_element.hpp>
#include <config/config.hpp> #include <config/config.hpp>
#include <utils/sha1.hpp>
#include <utils/time.hpp> #include <utils/time.hpp>
#include <xmpp/jid.hpp> #include <xmpp/jid.hpp>