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 7defmodule RabbitMQ.CLI.Diagnostics.Commands.ResolveHostnameCommand do 8 @moduledoc """ 9 Resolves a hostname to one or more addresses of a given IP address family (IPv4 ot IPv6). 10 This command is not meant to compete with `dig` but rather provide a way 11 to perform basic resolution tests that take Erlang's inetrc file into account. 12 """ 13 14 import RabbitCommon.Records 15 alias RabbitMQ.CLI.Core.Networking 16 alias RabbitMQ.CLI.Core.ExitCodes 17 18 @behaviour RabbitMQ.CLI.CommandBehaviour 19 20 def scopes(), do: [:diagnostics] 21 22 def switches(), do: [address_family: :string, offline: :boolean] 23 def aliases(), do: [a: :address_family] 24 25 def merge_defaults(args, opts) do 26 {args, Map.merge(%{address_family: "IPv4", offline: false}, opts)} 27 end 28 29 def validate(args, _) when length(args) < 1, do: {:validation_failure, :not_enough_args} 30 def validate(args, _) when length(args) > 1, do: {:validation_failure, :too_many_args} 31 def validate([_], %{address_family: family}) do 32 case Networking.valid_address_family?(family) do 33 true -> :ok 34 false -> {:validation_failure, {:bad_argument, "unsupported IP address family #{family}. Valid values are: ipv4, ipv6"}} 35 end 36 end 37 def validate([_], _), do: :ok 38 39 def run([hostname], %{address_family: family, offline: true}) do 40 :inet.gethostbyname(to_charlist(hostname), Networking.address_family(family)) 41 end 42 def run([hostname], %{node: node_name, address_family: family, offline: false, timeout: timeout}) do 43 case :rabbit_misc.rpc_call(node_name, :inet, :gethostbyname, 44 [to_charlist(hostname), Networking.address_family(family)], timeout) do 45 {:error, _} = err -> err 46 {:error, _, _} = err -> err 47 {:ok, result} -> {:ok, result} 48 other -> other 49 end 50 end 51 52 def output({:error, :nxdomain}, %{node: node_name, formatter: "json"}) do 53 m = %{ 54 "result" => "error", 55 "node" => node_name, 56 "message" => "Hostname does not resolve (resolution failed with an nxdomain)" 57 } 58 {:error, ExitCodes.exit_dataerr(), m} 59 end 60 def output({:error, :nxdomain}, _opts) do 61 {:error, ExitCodes.exit_dataerr(), "Hostname does not resolve (resolution failed with an nxdomain)"} 62 end 63 def output({:ok, result}, %{node: node_name, address_family: family, formatter: "json"}) do 64 hostname = hostent(result, :h_name) 65 addresses = hostent(result, :h_addr_list) 66 {:ok, %{ 67 "result" => "ok", 68 "node" => node_name, 69 "hostname" => to_string(hostname), 70 "address_family" => family, 71 "addresses" => Networking.format_addresses(addresses) 72 }} 73 end 74 def output({:ok, result}, _opts) do 75 addresses = hostent(result, :h_addr_list) 76 {:ok, Enum.join(Networking.format_addresses(addresses), "\n")} 77 end 78 use RabbitMQ.CLI.DefaultOutput 79 80 def usage() do 81 "resolve_hostname <hostname> [--address-family <ipv4 | ipv6>]" 82 end 83 84 def help_section(), do: :configuration 85 86 def description(), do: "Resolves a hostname to a set of addresses. Takes Erlang's inetrc file into account." 87 88 def banner([hostname], %{offline: false, node: node_name, address_family: family}) do 89 "Asking node #{node_name} to resolve hostname #{hostname} to #{family} addresses..." 90 end 91 def banner([hostname], %{offline: true, address_family: family}) do 92 "Resolving hostname #{hostname} to #{family} addresses..." 93 end 94end 95