~singpolyma/biboumi

241768836ddfb9e3609f987224cd821058fcc948 — Florent Le Coz 8 years ago 6dcb21a
Add the outgoing_bind option

Lets the admin choose a local address to bind each outgoing (IRC) socket.
M doc/biboumi.1.md => doc/biboumi.1.md +9 -0
@@ 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.

M louloulibs/network/tcp_socket_handler.cpp => louloulibs/network/tcp_socket_handler.cpp +28 -0
@@ 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));

M louloulibs/network/tcp_socket_handler.hpp => louloulibs/network/tcp_socket_handler.hpp +6 -0
@@ 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;

M src/irc/irc_client.cpp => src/irc/irc_client.cpp +3 -0
@@ 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);
}