Hi Michael,
On Thu, 2025-03-13 at 12:05 +0100, Michael Trapp wrote:
> Use MHD_OPTION_SOCK_ADDR to bind the http listen socket to a single address.
> The address can be any IPv4 or IPv6 address configured on the system:
> --http-addr=127.0.0.1
> --http-addr=::1
> --http-addr='ANY_ACTIVE_LOCAL_IP_ADDRESS'
> As debuginfod does not include any security features, a listen on the
> localhost address is sufficient for a HTTP/HTTPS reverse-proxy setup.
I like the idea behind this patch. But is the option name clear enough?
Should it maybe be --bind-address= or --listen-address= simply --addr=
like it is --port=?
Or even combine it as --listen=addr:port ?
Does it make sense to allow multiple local addresses to bind to? Like
both 127.0.01 and ::1 ? I guess maybe not because if you are using this
you probably are behind a proxy anyway. So maybe it could simply be --
listen-local and debuginfod figures out whether that is ipv4
(127.0.0.1) and/or ipv6 (::1) ?
I don't know what other programs do, can we follow some semi-standard?
The code itself does look ok, although I think it could be simplified a
little if we go for something like --listen-local only (assuming that
makes sense).
Cheers,
Mark
> ---
> debuginfod/debuginfod.cxx | 115 ++++++++++++++++++++++++++------------
> doc/debuginfod.8 | 5 ++
> 2 files changed, 84 insertions(+), 36 deletions(-)
>
> diff --git a/debuginfod/debuginfod.cxx b/debuginfod/debuginfod.cxx
> index 0edd57cb..30916093 100644
> --- a/debuginfod/debuginfod.cxx
> +++ b/debuginfod/debuginfod.cxx
> @@ -81,6 +81,7 @@ extern "C" {
> #include <math.h>
> #include <float.h>
> #include <fnmatch.h>
> +#include <arpa/inet.h>
>
>
> /* If fts.h is included before config.h, its indirect inclusions may not
> @@ -481,6 +482,8 @@ static const struct argp_option options[] =
> #define ARGP_KEY_METADATA_MAXTIME 0x100C
> { "metadata-maxtime", ARGP_KEY_METADATA_MAXTIME, "SECONDS", 0,
> "Number of seconds to limit metadata query run time, 0=unlimited.", 0 },
> +#define ARGP_KEY_HTTP_ADDR 0x100D
> + { "http-addr", ARGP_KEY_HTTP_ADDR, "ADDR", 0, "HTTP address to listen
> on.", 0 },
> { NULL, 0, NULL, 0, NULL, 0 },
> };
>
> @@ -512,6 +515,8 @@ static volatile sig_atomic_t sigusr1 = 0;
> static volatile sig_atomic_t forced_groom_count = 0;
> static volatile sig_atomic_t sigusr2 = 0;
> static unsigned http_port = 8002;
> +static struct sockaddr_in6 http_sockaddr;
> +static string addr_info = "";
> static bool webapi_cors = false;
> static unsigned rescan_s = 300;
> static unsigned groom_s = 86400;
> @@ -753,6 +758,16 @@ parse_opt (int key, char *arg,
> requires_koji_sigcache_mapping = true;
> break;
> #endif
> + case ARGP_KEY_HTTP_ADDR:
> + if (inet_pton(AF_INET, arg,
> &(((sockaddr_in*)&http_sockaddr)->sin_addr)) == 1)
> + http_sockaddr.sin6_family = AF_INET;
> + else
> + if (inet_pton(AF_INET6, arg, &http_sockaddr.sin6_addr) == 1)
> + http_sockaddr.sin6_family = AF_INET6;
> + else
> + argp_failure(state, 1, EINVAL, "HTTP address");
> + addr_info = arg;
> + break;
> // case 'h': argp_state_help (state, stderr,
> ARGP_HELP_LONG|ARGP_HELP_EXIT_OK);
> default: return ARGP_ERR_UNKNOWN;
> }
> @@ -5596,6 +5611,8 @@ main (int argc, char *argv[])
> fdcache_prefetch = 64; // guesstimate storage is this much less costly
> than re-decompression
>
> /* Parse and process arguments. */
> + memset(&http_sockaddr, 0, sizeof(http_sockaddr));
> + http_sockaddr.sin6_family = AF_UNSPEC;
> int remaining;
> (void) argp_parse (&argp, argc, argv, ARGP_IN_ORDER, &remaining, NULL);
> if (remaining != argc)
> @@ -5702,50 +5719,75 @@ main (int argc, char *argv[])
> #endif
> | MHD_USE_DEBUG); /* report errors to stderr */
>
> - // Start httpd server threads. Use a single dual-homed pool.
> - MHD_Daemon *d46 = MHD_start_daemon (mhd_flags, http_port,
> - NULL, NULL, /* default accept policy */
> - handler_cb, NULL, /* handler callback */
> - MHD_OPTION_EXTERNAL_LOGGER,
> - error_cb, NULL,
> - MHD_OPTION_THREAD_POOL_SIZE,
> - (int)connection_pool,
> - MHD_OPTION_END);
> -
> - MHD_Daemon *d4 = NULL;
> - if (d46 == NULL)
> + MHD_Daemon *dsa = NULL,
> + *d4 = NULL,
> + *d46 = NULL;
> +
> + if (http_sockaddr.sin6_family != AF_UNSPEC)
> {
> - // Cannot use dual_stack, use ipv4 only
> - mhd_flags &= ~(MHD_USE_DUAL_STACK);
> - d4 = MHD_start_daemon (mhd_flags, http_port,
> - NULL, NULL, /* default accept policy */
> + if (http_sockaddr.sin6_family == AF_INET)
> + ((sockaddr_in*)&http_sockaddr)->sin_port = htons(http_port);
> + if (http_sockaddr.sin6_family == AF_INET6)
> + http_sockaddr.sin6_port = htons(http_port);
> + // Start httpd server threads on socket addr:port.
> + dsa = MHD_start_daemon (mhd_flags & ~MHD_USE_DUAL_STACK, http_port,
> + NULL, NULL, /* default accept policy */
> handler_cb, NULL, /* handler callback */
> MHD_OPTION_EXTERNAL_LOGGER,
> error_cb, NULL,
> - (connection_pool
> - ? MHD_OPTION_THREAD_POOL_SIZE
> - : MHD_OPTION_END),
> - (connection_pool
> - ? (int)connection_pool
> - : MHD_OPTION_END),
> + MHD_OPTION_SOCK_ADDR,
> + (struct sockaddr *) &http_sockaddr,
> + MHD_OPTION_THREAD_POOL_SIZE,
> + (int)connection_pool,
> MHD_OPTION_END);
> - if (d4 == NULL)
> + }
> + else
> + {
> + // Start httpd server threads. Use a single dual-homed pool.
> + d46 = MHD_start_daemon (mhd_flags, http_port,
> + NULL, NULL, /* default accept policy */
> + handler_cb, NULL, /* handler callback */
> + MHD_OPTION_EXTERNAL_LOGGER,
> + error_cb, NULL,
> + MHD_OPTION_THREAD_POOL_SIZE,
> + (int)connection_pool,
> + MHD_OPTION_END);
> + addr_info = "IPv4 IPv6";
> + if (d46 == NULL)
> {
> - sqlite3 *database = db;
> - sqlite3 *databaseq = dbq;
> - db = dbq = 0; // for signal_handler not to freak
> - sqlite3_close (databaseq);
> - sqlite3_close (database);
> - error (EXIT_FAILURE, 0, "cannot start http server at port %d",
> - http_port);
> + // Cannot use dual_stack, use ipv4 only
> + mhd_flags &= ~(MHD_USE_DUAL_STACK);
> + d4 = MHD_start_daemon (mhd_flags, http_port,
> + NULL, NULL, /* default accept policy */
> + handler_cb, NULL, /* handler callback */
> + MHD_OPTION_EXTERNAL_LOGGER,
> + error_cb, NULL,
> + (connection_pool
> + ? MHD_OPTION_THREAD_POOL_SIZE
> + : MHD_OPTION_END),
> + (connection_pool
> + ? (int)connection_pool
> + : MHD_OPTION_END),
> + MHD_OPTION_END);
> + addr_info = "IPv4";
> }
> -
> }
> - obatched(clog) << "started http server on"
> - << (d4 != NULL ? " IPv4 " : " IPv4 IPv6 ")
> - << "port=" << http_port
> - << (webapi_cors ? " with cors" : "")
> - << endl;
> + if (d4 == NULL && d46 == NULL && dsa == NULL)
> + {
> + sqlite3 *database = db;
> + sqlite3 *databaseq = dbq;
> + db = dbq = 0; // for signal_handler not to freak
> + sqlite3_close (databaseq);
> + sqlite3_close (database);
> + error (EXIT_FAILURE, 0, "cannot start http server on %s port %d",
> + addr_info.c_str(), http_port);
> + }
> +
> + obatched(clog) << "started http server on "
> + << addr_info
> + << " port=" << http_port
> + << (webapi_cors ? " with cors" : "")
> + << endl;
>
> // add maxigroom sql if -G given
> if (maxigroom)
> @@ -5869,6 +5911,7 @@ main (int argc, char *argv[])
> pthread_join (it, NULL);
>
> /* Stop all the web service threads. */
> + if (dsa) MHD_stop_daemon (dsa);
> if (d46) MHD_stop_daemon (d46);
> if (d4) MHD_stop_daemon (d4);
>
> diff --git a/doc/debuginfod.8 b/doc/debuginfod.8
> index 1cf9a18f..6774f8c8 100644
> --- a/doc/debuginfod.8
> +++ b/doc/debuginfod.8
> @@ -154,6 +154,11 @@ listen, to service HTTP requests. Both IPv4 and IPV6
> sockets are
> opened, if possible. The webapi is documented below. The default
> port number is 8002.
>
> +.TP
> +.B "\-\-http\-addr=ADDR"
> +Set the IP address (any local IPv4/IPv6 address of the system) on
> +which debuginfod should listen, to service HTTP requests.
> +
> .TP
> .B "\-\-cors"
> Add CORS-related response headers and OPTIONS method processing.