Handle hostnames with upper-case letters
[webmin.git] / dhcp-dns / dhcp-dns-lib.pl
1 # Functions for configuring DNS and DHCP servers together
2
3 do '../web-lib.pl';
4 &init_config();
5 do '../ui-lib.pl';
6 &foreign_require("bind8", "bind8-lib.pl");
7 &foreign_require("dhcpd", "dhcpd-lib.pl");
8
9 # list_dhcp_hosts()
10 # Returns a list of DHCP host structures for managed hosts
11 sub list_dhcp_hosts
12 {
13 local $conf = &dhcpd::get_config();
14 local $parent = &dhcpd::get_parent_config();
15 local @rv;
16
17 # Top-level hosts
18 foreach my $h (&dhcpd::find("host", $conf)) {
19         $h->{'parent'} = $parent;
20         push(@rv, $h);
21         }
22
23 # Hosts in groups
24 foreach my $g (&dhcpd::find("group", $conf)) {
25         foreach my $h (&dhcpd::find("host", $g->{'members'})) {
26                 $h->{'parent'} = $g;
27                 push(@rv, $h);
28                 }
29         }
30
31 # Hosts in subjects, and hosts in groups in subnets
32 foreach my $s (&dhcpd::find("subnet", $conf)) {
33         foreach my $h (&dhcpd::find("host", $s->{'members'})) {
34                 $h->{'parent'} = $s;
35                 push(@rv, $h);
36                 }
37         foreach my $g (&dhcpd::find("group", $s->{'members'})) {
38                 foreach my $h (&dhcpd::find("host", $g->{'members'})) {
39                         $h->{'parent'} = $g;
40                         push(@rv, $h);
41                         }
42                 }
43         }
44
45 # All of those, in shared networks
46 foreach my $n (&dhcpd::find("shared-network", $conf)) {
47         # Hosts in shared network
48         foreach my $h (&dhcpd::find("host", $n->{'members'})) {
49                 $h->{'parent'} = $n;
50                 push(@rv, $h);
51                 }
52
53         # Hosts in groups
54         foreach my $g (&dhcpd::find("group", $n->{'members'})) {
55                 foreach my $h (&dhcpd::find("host", $g->{'members'})) {
56                         $h->{'parent'} = $g;
57                         push(@rv, $h);
58                         }
59                 }
60
61         # Hosts in subjects, and hosts in groups in subnets, in shared network
62         foreach my $s (&dhcpd::find("subnet", $n->{'members'})) {
63                 foreach my $h (&dhcpd::find("host", $s->{'members'})) {
64                         $h->{'parent'} = $s;
65                         push(@rv, $h);
66                         }
67                 foreach my $g (&dhcpd::find("group", $s->{'members'})) {
68                         foreach my $h (&dhcpd::find("host", $g->{'members'})) {
69                                 $h->{'parent'} = $g;
70                                 push(@rv, $h);
71                                 }
72                         }
73                 }
74         }
75
76 return @rv;
77 }
78
79 # list_dhcp_subnets()
80 # Returns a list of all DHCP subnet objects
81 sub list_dhcp_subnets
82 {
83 local @rv;
84 local $conf = &dhcpd::get_config();
85 local $parent = &dhcpd::get_parent_config();
86 foreach my $s (&dhcpd::find("subnet", $conf)) {
87         $s->{'parent'} = $parent;
88         push(@rv, $s);
89         }
90 foreach my $n (&dhcpd::find("shared-network", $conf)) {
91         foreach my $s (&dhcpd::find("subnet", $n->{'members'})) {
92                 $s->{'parent'} = $n;
93                 push(@rv, $s);
94                 }
95         }
96 return @rv;
97 }
98
99 # host_form([&host])
100 # Returns a form for editing or creating a host
101 sub host_form
102 {
103 local ($h) = @_;
104 local $new = !$h;
105 local $rv;
106 $rv .= &ui_form_start("save.cgi", "post");
107 if ($new) {
108         $rv .= &ui_hidden("new", 1);
109         }
110 else {
111         $rv .= &ui_hidden("old", $h->{'values'}->[0]);
112         }
113 $rv .= &ui_table_start($text{'form_header'}, "width=100%", 2);
114
115 # Description
116 $rv .= &ui_table_row($text{'form_comment'},
117         &ui_textbox("comment", $host->{'comment'}, 50));
118
119 # Hostname
120 local $short = &short_hostname($h->{'values'}->[0]);
121 local $indom = $new || $short ne $h->{'values'}->[0];
122 $rv .= &ui_table_row($text{'form_host'},
123         &ui_textbox("host", $short, 20).
124         ($indom ? "<tt>.$config{'domain'}</tt>" : ""));
125 $rv .= &ui_hidden("indom", $indom);
126
127 # Fixed IP address
128 local $fixed = &dhcpd::find("fixed-address", $h->{'members'});
129 $rv .= &ui_hidden("oldip", $fixed->{'values'}->[0]) if ($fixed);
130 local @subnets = &list_dhcp_subnets();
131 local $parsub;
132 if (!$new) {
133         ($parsub) = grep { $_ eq $h->{'parent'} } @subnets;
134         }
135 elsif (@subnets) {
136         $parsub = $subnets[0];
137         }
138 $rv .= &ui_table_row($text{'form_ip'},
139         &ui_textbox("ip", $fixed ? $fixed->{'values'}->[0] : undef, 20).
140         ($new ? " ".&ip_chooser_button("ip") : "").
141         " ".$text{'form_subnet'}." ".
142         &ui_select("subnet", $parsub ? $parsub->{'values'}->[0] : '',
143                    [ $parsub ? ( ) : ( [ '', $text{'form_nosubnet'} ] ),
144                      map { [ $_->{'values'}->[0] ] } @subnets ]));
145 $rv .= &ui_hidden("oldsubnet", $parsub ? $parsub->{'values'}->[0] : '');
146
147 # MAC address
148 local $hard = &dhcpd::find("hardware", $h->{'members'});
149 $rv .= &ui_hidden("oldmac", $hard->{'values'}->[1]) if ($hard);
150 $rv .= &ui_table_row($text{'form_mac'},
151 #       &ui_select("media", $hard ? $hard->{'values'}->[0] : "ethernet",
152 #                  [ [ "ethernet", $text{'form_ethernet'} ],
153 #                    [ "token-ring", $text{'form_tr'} ],
154 #                    [ "fddi", $text{'form_fddi'} ] ], 1, 0, 1).
155         &ui_hidden("media", $hard ? $hard->{'values'}->[0] : "ethernet").
156         &ui_textbox("mac", $hard ? $hard->{'values'}->[1] : undef, 20));
157
158 $rv .= &ui_table_end();
159 if ($new) {
160         $rv .= &ui_form_end([ [ undef, $text{'create'} ] ]);
161         }
162 else {
163         $rv .= &ui_form_end([ [ undef, $text{'save'} ],
164                               [ 'delete', $text{'delete'} ] ]);
165         }
166 return $rv;
167 }
168
169 sub short_hostname
170 {
171 local ($hn) = @_;
172 if ($hn =~ /^(\S+)\.\Q$config{'domain'}\E$/) {
173         return $1;
174         }
175 else {
176         return $hn;
177         }
178 }
179
180 # get_dns_zone()
181 # Returns the records file and list of records for the domain
182 sub get_dns_zone
183 {
184 local $conf = &bind8::get_config();
185 local @zones = &bind8::find("zone", $conf);
186 foreach my $v (&bind8::find("view", $conf)) {
187         push(@zones, &bind8::find("zone", $v->{'members'}));
188         }
189 local ($z) = grep { lc($_->{'value'}) eq lc($config{'domain'}) } @zones;
190 return ( ) if (!$z);
191 local $file = &bind8::find("file", $z->{'members'});
192 local $fn = $file->{'values'}->[0];
193 local @recs = &bind8::read_zone_file($fn, $config{'domain'});
194 return ( $fn, \@recs );
195 }
196
197 # get_reverse_dns_zone(ip)
198 # Returns the records file and list of records for the domain for some IP
199 sub get_reverse_dns_zone
200 {
201 local ($ip) = @_;
202 local $conf = &bind8::get_config();
203 local @zones = &bind8::find("zone", $conf);
204 foreach my $v (&bind8::find("view", $conf)) {
205         push(@zones, &bind8::find("zone", $v->{'members'}));
206         }
207 local $arpa = &bind8::ip_to_arpa($ip);
208 $arpa =~ s/\.$//;
209 local ($z) = grep { $arpa =~ /\.$_->{'value'}$/i } @zones;
210 return ( ) if (!$z);
211 local $file = &bind8::find("file", $z->{'members'});
212 local $fn = $file->{'values'}->[0];
213 local @recs = &bind8::read_zone_file($fn, $config{'domain'});
214 return ( $fn, \@recs, $arpa, $z->{'value'} );
215 }
216
217 sub apply_configuration
218 {
219 &lock_file("$module_config_directory/apply");
220 local $err;
221 $err = &dhcpd::restart_dhcpd();
222 if ($err) {
223         $err = "DHCPD failed : $err";
224         }
225 else {
226         $err = &bind8::restart_bind();
227         if ($err) {
228                 $err = "BIND failed : $err";
229                 }
230         }
231 &unlock_file("$module_config_directory/apply");
232 return $err;
233 }
234
235 # ip_chooser_button(field)
236 sub ip_chooser_button
237 {
238 return "<input type=button onClick='ifield = form.$_[0]; chooser = window.open(\"ip_chooser.cgi\", \"chooser\", \"toolbar=no,menubar=no,scrollbars=yes,resizable=yes,width=300,height=600\"); chooser.ifield = ifield; window.ifield = ifield' value=\"...\">\n";
239 }
240
241 1;
242