Skip to main content

src/barrel_p2p_discovery_dns.erl

%%% -*- erlang -*-
%%% Copyright (c) 2026 Benoit Chesneau
%%% SPDX-License-Identifier: Apache-2.0
%%%
-module(barrel_p2p_discovery_dns).
-behaviour(quic_discovery).

%% DNS-fallback discovery backend. Resolves the host part of a
%% `name@host' node atom via `inet:getaddr/2', then pairs it with
%% the QUIC-dist port from `application:get_env(quic, dist, [{port, _}])'
%% (default 4433).

-define(DEFAULT_PORT, 4433).

-export([lookup/2]).

lookup(Node, Host) ->
    Port = application:get_env(quic, dist_port, ?DEFAULT_PORT),
    case resolve_host(Host) of
        {ok, IP} ->
            {ok, {IP, Port}};
        {error, _} ->
            case extract_host(Node) of
                {ok, NodeHost} ->
                    case resolve_host(NodeHost) of
                        {ok, IP} -> {ok, {IP, Port}};
                        Err -> Err
                    end;
                Err ->
                    Err
            end
    end.

%%====================================================================
%% Internal
%%====================================================================

%% Accept either a node atom (`name@host') or a bare name string
%% (`"name"') as upstream `quic_dist' may pass either depending on
%% the path. Bare names have no host part to extract; return error
%% so the caller falls back to the host argument.
extract_host(Node) when is_atom(Node) ->
    extract_host(atom_to_list(Node));
extract_host(Node) when is_binary(Node) ->
    extract_host(binary_to_list(Node));
extract_host(Node) when is_list(Node) ->
    case string:split(Node, "@") of
        [_, Host] -> {ok, Host};
        _ -> {error, invalid_node_name}
    end;
extract_host(_) ->
    {error, invalid_node_name}.

resolve_host(Host) when is_list(Host) ->
    case inet:parse_address(Host) of
        {ok, IP} ->
            {ok, IP};
        {error, _} ->
            case inet:getaddr(Host, inet) of
                {ok, IP} -> {ok, IP};
                {error, _} -> inet:getaddr(Host, inet6)
            end
    end;
resolve_host(Host) when is_binary(Host) ->
    resolve_host(binary_to_list(Host));
resolve_host(_) ->
    {error, invalid_host}.