summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/biboumi.1.md9
-rw-r--r--louloulibs/network/tcp_socket_handler.cpp28
-rw-r--r--louloulibs/network/tcp_socket_handler.hpp6
-rw-r--r--src/irc/irc_client.cpp3
4 files changed, 46 insertions, 0 deletions
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<const struct sockaddr*>(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);
}