From 241768836ddfb9e3609f987224cd821058fcc948 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Tue, 1 Dec 2015 16:08:40 +0100 Subject: Add the outgoing_bind option Lets the admin choose a local address to bind each outgoing (IRC) socket. --- doc/biboumi.1.md | 9 +++++++++ louloulibs/network/tcp_socket_handler.cpp | 28 ++++++++++++++++++++++++++++ louloulibs/network/tcp_socket_handler.hpp | 6 ++++++ src/irc/irc_client.cpp | 3 +++ 4 files changed, 46 insertions(+) diff --git a/doc/biboumi.1.md b/doc/biboumi.1.md index 4aded1e..7a33f4f 100644 --- a/doc/biboumi.1.md +++ b/doc/biboumi.1.md @@ -120,6 +120,15 @@ The configuration file uses a simple format of the form negociating a TLS session. By default this value is unset and biboumi tries a list of well-known paths. +`outgoing_bind` + + An address (IPv4 or IPv6) to bind the outgoing sockets to. If no value is + specified, it will use the one assigned by the operating system. You can + 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, the connection to the XMPP server is always done locally + on 127.0.0.1. + The configuration can be re-read at runtime (you can for example change the log level without having to restart biboumi) by sending SIGUSR1 or SIGUSR2 (see kill(1)) to the process. diff --git a/louloulibs/network/tcp_socket_handler.cpp b/louloulibs/network/tcp_socket_handler.cpp index 78efdce..e6901c8 100644 --- a/louloulibs/network/tcp_socket_handler.cpp +++ b/louloulibs/network/tcp_socket_handler.cpp @@ -56,6 +56,34 @@ void TCPSocketHandler::init_socket(const struct addrinfo* rp) ::close(this->socket); if ((this->socket = ::socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol)) == -1) throw std::runtime_error("Could not create socket: "s + strerror(errno)); + // Bind the socket to a specific address, if specified + if (!this->bind_addr.empty()) + { + // Convert the address from string format to a sockaddr that can be + // used in bind() + struct addrinfo* result; + int err = ::getaddrinfo(this->bind_addr.data(), nullptr, nullptr, &result); + if (err != 0) + log_error("Failed to bind socket to " << this->bind_addr << ": " + << gai_strerror(err)); + else + { + struct addrinfo* rp; + int bind_error; + for (rp = result; rp; rp = rp->ai_next) + { + if ((bind_error = ::bind(this->socket, + reinterpret_cast(rp->ai_addr), + rp->ai_addrlen)) == 0) + break; + } + if (!rp) + log_error("Failed to bind socket to " << this->bind_addr << ": " + << strerror(bind_error)); + else + log_info("Socket successfully bound to " << this->bind_addr); + } + } int optval = 1; if (::setsockopt(this->socket, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval)) == -1) log_warning("Failed to enable TCP keepalive on socket: " << strerror(errno)); diff --git a/louloulibs/network/tcp_socket_handler.hpp b/louloulibs/network/tcp_socket_handler.hpp index d33b919..9f5caa3 100644 --- a/louloulibs/network/tcp_socket_handler.hpp +++ b/louloulibs/network/tcp_socket_handler.hpp @@ -224,6 +224,12 @@ protected: bool hostname_resolution_failed; + /** + * Address to bind the socket to, before calling connect(). + * If empty, it’s equivalent to binding to INADDR_ANY. + */ + std::string bind_addr; + private: TCPSocketHandler(const TCPSocketHandler&) = delete; TCPSocketHandler(TCPSocketHandler&&) = delete; diff --git a/src/irc/irc_client.cpp b/src/irc/irc_client.cpp index 93ea2ae..e53c540 100644 --- a/src/irc/irc_client.cpp +++ b/src/irc/irc_client.cpp @@ -86,6 +86,9 @@ void IrcClient::start() this->bridge->send_xmpp_message(this->hostname, "", "Connecting to "s + this->hostname + ":" + port + " (" + (tls ? "encrypted" : "not encrypted") + ")"); + + this->bind_addr = Config::get("outgoing_bind", ""); + this->connect(this->hostname, port, tls); } -- cgit v1.2.3