-
Notifications
You must be signed in to change notification settings - Fork 13
/
fqdn
executable file
·134 lines (113 loc) · 3.3 KB
/
fqdn
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
#!/usr/bin/env perl
# fqdn -- Retrieve a hostname's fully qualified name
#
# Uses forward lookups via AI_CANONNAME (like most tools do). Does not use
# reverse DNS, although that should be added as an option someday.
use warnings;
use strict;
use Getopt::Long qw(:config bundling no_ignore_case);
use Socket qw(AI_CANONNAME getaddrinfo);
use Sys::Hostname;
$::arg0 = (split m!/!, $0)[-1];
sub usage {
print for
"Usage: $::arg0 [-s [-d <domains>]] [-v] <name>...\n",
"\n",
"Options:\n",
" -s perform a suffix search\n",
" -d domains specify a custom list of domain suffixes\n",
" -v be verbose about suffix expansion\n",
"\n",
#12345678........12345678........12345678........12345678........12345678........
"In default mode, the given name is looked up using system DNS search suffixes\n",
"and the alias chain is followed to find the canonnical name (like getaddrinfo\n",
"with AI_CANONNAME).\n",
"\n",
"In suffix search mode, the given name is looked up using only the specified\n",
"suffixes (aliases are NOT followed; each name is merely tested to see if it\n",
"resolves at all).\n";
}
sub joinhost {
my ($domain, $suffix) = @_;
return $domain if $domain =~ /\.$/;
return $domain if $suffix eq "";
return $domain if $suffix eq ".";
return $domain.".".$suffix;
}
sub get_resolv_conf_suffixes {
if (open(my $fh, "</etc/resolv.conf")) {
while (my $line = <$fh>) {
if ($line =~ /^(domain|search) (.+)$/) {
close $fh;
return $2;
}
}
close $fh;
return "";
}
}
sub get_fqdn_from_suffix {
my ($host, $suffixes, $v) = @_;
my @suffixes = @$suffixes;
# Make sure we can find names which already are qualified.
# (But don't do so for non-qualified names, to avoid the delay
# caused by mDNS/LLMNR lookups.)
push @suffixes, "." if $host =~ /\./;
for my $suffix (@suffixes) {
my $fqdn = joinhost($host, $suffix);
warn "fqdn: trying to resolve '$fqdn'\n" if $v;
my ($err, @res) = getaddrinfo($fqdn, undef, {});
if (@res && !$err) {
# Return $fqdn instead of canonname, because in
# this case it is not our goal to chase CNAMEs,
# only to discover the correct domain suffix.
if ($fqdn =~ /\./) {
return $fqdn;
} else {
$err = "empty suffix was specified";
}
}
warn "fqdn: lookup of '$fqdn' failed: $err\n" if $v;
}
warn "fqdn: could not find '$host' with specified suffixes\n";
return undef;
}
sub get_fqdn_from_cnames {
my ($host, $v) = @_;
my ($err, @res) = getaddrinfo($host, undef, {flags => AI_CANONNAME});
if (@res && !$err) {
return $res[0]->{canonname};
}
warn "fqdn: could not resolve '$host': $err\n";
return undef;
}
my $search = 0;
my $suffixes = $ENV{LOCALDOMAIN};
my $verbose = 0;
GetOptions(
"s|search" => \$search,
"d|domains=s" => \$suffixes,
"v|verbose" => \$verbose,
"help" => sub { usage(); exit(0); },
) || exit(2);
if ($search && !length($suffixes)) {
$suffixes = get_resolv_conf_suffixes();
warn "fqdn: using system suffixes '$suffixes'\n" if $verbose;
}
my @suffixes = split(/[:, ]/, $suffixes // "");
my @args = @ARGV ? @ARGV : hostname();
my $errs = 0;
for my $arg (@args) {
my $res;
if ($search) {
$res = get_fqdn_from_suffix($arg, \@suffixes, $verbose);
} else {
$res = get_fqdn_from_cnames($arg, $verbose);
}
if ($res) {
print "$res\n";
} else {
print "$arg\n"; ++$errs;
}
}
exit(!!$errs);