File Coverage

blib/lib/Net/DNS/DomainController/Discovery.pm
Criterion Covered Total %
statement 54 56 96.4
branch 14 16 87.5
condition 3 3 100.0
subroutine 12 12 100.0
pod 6 6 100.0
total 89 93 95.7


line stmt bran cond sub pod time code
1             package Net::DNS::DomainController::Discovery;
2              
3 10     10   2318748 use 5.006;
  10         217  
4 10     10   60 use strict;
  10         20  
  10         246  
5 10     10   63 use warnings;
  10         30  
  10         225  
6 10     10   56 use Carp;
  10         18  
  10         691  
7              
8             =head1 NAME
9              
10             Net::DNS::DomainController::Discovery - Discover Microsoft Active Directory domain controllers via DNS queries
11              
12             =head1 VERSION
13              
14             Version 0.02
15              
16             =cut
17              
18             our $VERSION = '0.02';
19              
20              
21             =head1 SYNOPSIS
22              
23             Issues DNS requests to provide a list of hostnames and IP addresses of the Microsoft
24             Active Directory domain controllers.
25              
26             use Net::DNS::DomainController::Discovery;
27              
28             my $foo = Net::DNS::DomainController::Discovery->domain_controllers('fabrikam.com');
29             ...
30              
31             =cut
32              
33 10     10   65 use Exporter qw(import);
  10         24  
  10         531  
34             our @EXPORT_OK = qw(domain_controllers srv_to_name srv_fqdn_list fqdn_to_ipaddr);
35              
36 10     10   913 use Net::DNS::Resolver;
  10         119809  
  10         5298  
37              
38             our $TestResolver;
39              
40             =head1 SUBROUTINES/METHODS
41              
42             =head2 srv_to_name
43              
44             Extract server name from the SRV response
45              
46             =cut
47              
48             sub srv_to_name {
49 10     10 1 8802 my $rr = shift;
50 10 100       33 if ( ! $rr ) {
51 1         254 confess "Need Net::DNS::RR record";
52             }
53 9 100       31 if ( $rr->type ne 'SRV' ) {
54 2         28 croak "Need Net::DNS::RR::SRV record (got \"". ${rr}->type . "\")";
55             }
56 7         75 return $rr->target;
57             }
58              
59             =head2 srv_fqdn_list
60              
61             =cut
62              
63             sub srv_fqdn_list {
64 6     6 1 10115 my ($resolver, $domain_name) = @_;
65 6         90 my $resp = $resolver->query( $domain_name, 'SRV' );
66 6         2455 my @dc_name_list;
67              
68 6 100       24 if ( ! $resp ) {
69 1         161 croak "No SRV records in \"$domain_name\"";
70             }
71 5         53 @dc_name_list = map { srv_to_name($_) } $resp->answer;
  7         117  
72             }
73              
74             =head2 fqdn_to_ipaddr
75              
76             =cut
77              
78             sub fqdn_to_ipaddr {
79 10     10 1 12277 my $rr = shift;
80 10 100       42 if ( ! $rr ) {
81 1         252 confess "Need Net::DNS::RR record";
82             }
83 9 100 100     32 if ( $rr->type ne 'A' && $rr->type ne 'AAAA' ) {
84 3         97 croak "Need Net::DNS::RR::A or AAAA record (got \"" . ${rr}->type . "\")";
85             }
86 6         89 return $rr->address;
87             }
88              
89             =head2 fqdn_ipaddr_list
90              
91             =cut
92              
93             sub fqdn_ipaddr_list {
94 5     5 1 11 my ($resolver, $fqdn) = @_;
95 5         16 my $resp = $resolver->query( $fqdn, 'A' );
96 5         1054 my @dc_ip_list;
97              
98 5 50       15 if ( ! $resp ) {
99 0         0 croak "No A records in \"$fqdn\"";
100             }
101 5         13 @dc_ip_list= map { fqdn_to_ipaddr($_) } $resp->answer;
  5         26  
102             }
103              
104             =head2 dc_to_srv
105              
106             =cut
107              
108             sub dc_to_srv {
109 4     4 1 18 return '_ldap._tcp.dc._msdcs.' . $_[0] . '.'
110             }
111              
112             =head2 domain_controllers
113              
114             =cut
115              
116             sub domain_controllers {
117              
118 5 100   5 1 43388 croak "Active Directory domain name not provided ($#_)" if $#_ < 1;
119              
120 4         9 shift;
121 4         7 my $domain_name = shift;
122              
123 4         9 my $resolver;
124 4 50       15 if (defined $TestResolver) {
125 4         8 $resolver = $TestResolver;
126             } else {
127 0         0 $resolver = Net::DNS::Resolver->new();
128             }
129              
130 4         9 my @dc;
131            
132 4         14 foreach my $fqdn (srv_fqdn_list( $resolver, dc_to_srv( $domain_name ))) {
133 5         143 foreach my $addr (fqdn_ipaddr_list( $resolver, $fqdn )) {
134 4         59 push @dc, [ $domain_name, $fqdn, $addr ];
135             }
136             }
137 2         21 return @dc;
138             }
139              
140             =head1 AUTHOR
141              
142             Marcin CIESLAK, C<< >>
143              
144             =head1 BUGS
145              
146             Please report any bugs or feature requests to C, or through
147             the web interface at L. I will be notified, and then you'll
148             automatically be notified of progress on your bug as I make changes.
149              
150             =head1 SUPPORT
151              
152             You can find documentation for this module with the perldoc command.
153              
154             perldoc Net::DNS::DomainController::Discovery
155              
156             You can also look for information at:
157              
158             =over 4
159              
160             =item * RT: CPAN's request tracker (report bugs here)
161              
162             L
163              
164             =item * CPAN search engine
165              
166             L
167              
168             =back
169              
170              
171             =head1 LICENSE AND COPYRIGHT
172              
173             This software is Copyright (c) 2020 by Marcin CIESLAK.
174              
175             This is free software, licensed under:
176              
177             The Artistic License 2.0 (GPL Compatible)
178              
179              
180             =cut
181              
182             1; # End of Net::DNS::DomainController::Discovery