diff options
Diffstat (limited to 'deps/rabbitmq_cli/lib/rabbitmq/cli/core/node_name.ex')
-rw-r--r-- | deps/rabbitmq_cli/lib/rabbitmq/cli/core/node_name.ex | 198 |
1 files changed, 198 insertions, 0 deletions
diff --git a/deps/rabbitmq_cli/lib/rabbitmq/cli/core/node_name.ex b/deps/rabbitmq_cli/lib/rabbitmq/cli/core/node_name.ex new file mode 100644 index 0000000000..c39b215ca7 --- /dev/null +++ b/deps/rabbitmq_cli/lib/rabbitmq/cli/core/node_name.ex @@ -0,0 +1,198 @@ +## This Source Code Form is subject to the terms of the Mozilla Public +## License, v. 2.0. If a copy of the MPL was not distributed with this +## file, You can obtain one at https://mozilla.org/MPL/2.0/. +## +## Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved. + +defmodule RabbitMQ.CLI.Core.NodeName do + alias RabbitMQ.CLI.Core.Config + + @moduledoc """ + Provides functions for correctly constructing node names given a node type and optional base name. + """ + + @doc """ + Constructs complete node name based on :longnames or :shortnames and + a base name, in the same manner as Erlang/OTP lib/kernel/src/net_kernel.erl + """ + def create(base, type) do + create_name(base, type, 1) + end + + @doc """ + Get local hostname + """ + def hostname, do: :inet_db.gethostname() |> List.to_string() + + @doc """ + Get hostname part of current node name + """ + def hostname_from_node do + [_, hostname] = split_node(Node.self()) + hostname + end + + @doc """ + Get hostname part of given node name + """ + def hostname_from_node(name) do + [_, hostname] = split_node(name) + hostname + end + + def split_node(name) when is_atom(name) do + split_node(to_string(name)) + end + + def split_node(name) do + case String.split(name, "@", parts: 2) do + ["", host] -> + default_name = to_string(Config.default(:node)) + [default_name, host] + + [_head, _host] = rslt -> + rslt + + [head] -> + [head, ""] + end + end + + @doc """ + Get local domain. If unavailable, makes a good guess. We're using + :inet_db here because that's what Erlang/OTP uses when it creates a node + name: + https://github.com/erlang/otp/blob/8ca061c3006ad69c2a8d1c835d0d678438966dfc/lib/kernel/src/net_kernel.erl#L1363-L1445 + """ + def domain do + domain(1) + end + + # + # Implementation + # + + defp domain(attempt) do + case {attempt, :inet_db.res_option(:domain), :os.type()} do + {1, [], _} -> + do_load_resolv() + domain(0) + + {0, [], {:unix, :darwin}} -> + "local" + + {0, [], _} -> + "localdomain" + + {_, domain, _} -> + List.to_string(domain) + end + end + + defp create_name(name, long_or_short_names, attempt) do + {head, host1} = create_hostpart(name, long_or_short_names) + + case host1 do + {:ok, host_part} -> + case valid_name_head(head) do + true -> + {:ok, String.to_atom(head <> "@" <> host_part)} + + false -> + {:error, {:node_name, :invalid_node_name_head}} + end + + {:error, :long} when attempt == 1 -> + do_load_resolv() + create_name(name, long_or_short_names, 0) + + {:error, :long} when attempt == 0 -> + case valid_name_head(head) do + true -> + {:ok, String.to_atom(head <> "@" <> hostname() <> "." <> domain())} + + false -> + {:error, {:node_name, :invalid_node_name_head}} + end + + {:error, :hostname_not_allowed} -> + {:error, {:node_name, :hostname_not_allowed}} + + {:error, err_type} -> + {:error, {:node_name, err_type}} + end + end + + defp create_hostpart(name, long_or_short_names) do + [head, host] = split_node(name) + + host1 = + case {host, long_or_short_names} do + {"", :shortnames} -> + case :inet_db.gethostname() do + inet_db_host when inet_db_host != [] -> + {:ok, to_string(inet_db_host)} + + _ -> + {:error, :short} + end + + {"", :longnames} -> + case {:inet_db.gethostname(), :inet_db.res_option(:domain)} do + {inet_db_host, inet_db_domain} + when inet_db_host != [] and inet_db_domain != [] -> + {:ok, to_string(inet_db_host) <> "." <> to_string(inet_db_domain)} + + _ -> + {:error, :long} + end + + {_, type} -> + validate_hostname(host, type) + end + + {head, host1} + end + + defp validate_hostname(host, :longnames) do + case String.contains?(host, ".") do + true -> + validate_hostname_rx(host) + + _ -> + validate_hostname(host <> "." <> domain(), :longnames) + end + end + + defp validate_hostname(host, :shortnames) do + case String.contains?(host, ".") do + true -> + {:error, :short} + + _ -> + validate_hostname_rx(host) + end + end + + defp validate_hostname_rx(host) do + rx = Regex.compile!("^[!-ΓΏ]*$", [:unicode]) + + case Regex.match?(rx, host) do + true -> + {:ok, host} + + false -> + {:error, :hostname_not_allowed} + end + end + + defp valid_name_head(head) do + rx = Regex.compile!("^[0-9A-Za-z_\\-]+$", [:unicode]) + Regex.match?(rx, head) + end + + defp do_load_resolv do + # It could be we haven't read domain name from resolv file yet + :ok = :inet_config.do_load_resolv(:os.type(), :longnames) + end +end |