1%% This Source Code Form is subject to the terms of the Mozilla Public
2%% License, v. 2.0. If a copy of the MPL was not distributed with this
3%% file, You can obtain one at https://mozilla.org/MPL/2.0/.
4%%
5%% Copyright (c) 2007-2021 VMware, Inc. or its affiliates.  All rights reserved.
6%%
7
8-module(rabbit_peer_discovery_dns).
9-behaviour(rabbit_peer_discovery_backend).
10
11-include_lib("rabbit_common/include/rabbit.hrl").
12
13-export([list_nodes/0, supports_registration/0, register/0, unregister/0,
14         post_registration/0, lock/1, unlock/1]).
15%% for tests
16-export([discover_nodes/2, discover_hostnames/2]).
17
18%%
19%% API
20%%
21
22-spec list_nodes() ->
23    {ok, {Nodes :: [node()], rabbit_types:node_type()}}.
24
25list_nodes() ->
26    case application:get_env(rabbit, cluster_formation) of
27      undefined         ->
28            {ok, {[], disc}};
29      {ok, ClusterFormation} ->
30        case proplists:get_value(peer_discovery_dns, ClusterFormation) of
31            undefined ->
32              rabbit_log:warning("Peer discovery backend is set to ~s "
33                                 "but final config does not contain rabbit.cluster_formation.peer_discovery_dns. "
34                                 "Cannot discover any nodes because seed hostname is not configured!",
35                                 [?MODULE]),
36              {ok, {[], disc}};
37            Proplist  ->
38              Hostname = rabbit_data_coercion:to_list(proplists:get_value(hostname, Proplist)),
39
40              {ok, {discover_nodes(Hostname, net_kernel:longnames()), rabbit_peer_discovery:node_type()}}
41        end
42    end.
43
44
45-spec supports_registration() -> boolean().
46
47supports_registration() ->
48    false.
49
50
51-spec register() -> ok.
52
53register() ->
54    ok.
55
56-spec unregister() -> ok.
57
58unregister() ->
59    ok.
60
61-spec post_registration() -> ok.
62
63post_registration() ->
64    ok.
65
66-spec lock(Node :: atom()) -> not_supported.
67
68lock(_Node) ->
69    not_supported.
70
71-spec unlock(Data :: term()) -> ok.
72
73unlock(_Data) ->
74    ok.
75
76%%
77%% Implementation
78%%
79
80discover_nodes(SeedHostname, LongNamesUsed) ->
81    [list_to_atom(rabbit_peer_discovery:append_node_prefix(H)) ||
82        H <- discover_hostnames(SeedHostname, LongNamesUsed)].
83
84discover_hostnames(SeedHostname, LongNamesUsed) ->
85    lookup(SeedHostname, LongNamesUsed, ipv4) ++
86    lookup(SeedHostname, LongNamesUsed, ipv6).
87
88decode_record(ipv4) ->
89    a;
90decode_record(ipv6) ->
91    aaaa.
92
93lookup(SeedHostname, LongNamesUsed, IPv) ->
94    IPs   = inet_res:lookup(SeedHostname, in, decode_record(IPv)),
95    rabbit_log:info("Addresses discovered via ~s records of ~s: ~s",
96		    [string:to_upper(atom_to_list(decode_record(IPv))),
97		     SeedHostname,
98		     string:join([inet_parse:ntoa(IP) || IP <- IPs], ", ")]),
99    Hosts = [extract_host(inet:gethostbyaddr(A), LongNamesUsed, A) ||
100		A <- IPs],
101    lists:filter(fun(E) -> E =/= error end, Hosts).
102
103
104%% long node names are used
105extract_host({ok, {hostent, FQDN, _, _, _, _}}, true, _Address) ->
106  FQDN;
107%% short node names are used
108extract_host({ok, {hostent, FQDN, _, _, _, _}}, false, _Address) ->
109  lists:nth(1, string:tokens(FQDN, "."));
110extract_host({error, Error}, _, Address) ->
111  rabbit_log:error("Reverse DNS lookup for address ~s failed: ~p",
112                   [inet_parse:ntoa(Address), Error]),
113  error.
114