Handle hostnames with upper-case letters
[webmin.git] / bind8 / save_record.cgi
1 #!/usr/local/bin/perl
2 # save_record.cgi
3 # Adds or updates a record of some type
4
5 require './bind8-lib.pl';
6 &ReadParse();
7 &error_setup($text{'edit_err'});
8 $zone = &get_zone_name($in{'index'}, $in{'view'});
9 $dom = $zone->{'name'};
10 @zl = &list_zone_names();
11 $reverse = ($in{'origin'} =~ /\.in-addr\.arpa/i ||
12             $in{'origin'} =~ /\.$ipv6revzone/i);
13 &can_edit_zone($zone) || &error($text{'recs_ecannot'});
14 &can_edit_type($in{'type'}) ||
15         &error($text{'recs_ecannottype'});
16 $access{'ro'} && &error($text{'master_ero'});
17 &lock_file(&make_chroot(&absolute_path($zone->{'file'})));
18
19 # Read the existing records
20 if ($config{'largezones'} && !defined($in{'num'})) {
21         # Adding to a large zone, so only read the SOA
22         @recs = &read_zone_file($in{'file'}, $in{'origin'}, undef, 1);
23         }
24 else {
25         # Read all records
26         @recs = &read_zone_file($in{'file'}, $in{'origin'});
27         }
28
29 # get the old record if needed
30 if (defined($in{'num'})) {
31         $r = &find_record_by_id(\@recs, $in{'id'}, $in{'num'});
32         $r || &error($text{'edit_egone'});
33         }
34
35 # check for deletion
36 if ($in{'delete'}) {
37         # Check if confirmation is needed
38         if (!$in{'confirm'} && $config{'confirm_rec'}) {
39                 &ui_print_header(undef, $text{'edit_dtitle'}, "");
40
41                 print &ui_confirmation_form("save_record.cgi",
42                         &text('edit_rusure', "<tt>$r->{'name'}</tt>",
43                                              "<tt>$in{'origin'}</tt>"),
44                         [ map { [ $_, $in{$_} ] } (keys %in) ],
45                         [ [ 'confirm', $text{'edit_dok'} ] ],
46                         );
47
48                 &ui_print_footer("edit_recs.cgi?index=$in{'index'}&view=$in{'view'}&type=$in{'redirtype'}&sort=$in{'sort'}", $text{'recs_return'});
49                 }
50         else {
51                 # Delete the record
52                 &lock_file(&make_chroot($r->{'file'}));
53                 &delete_record($r->{'file'}, $r);
54                 &bump_soa_record($in{'file'}, \@recs);
55                 &sign_dnssec_zone_if_key($zone, \@recs);
56
57                 # Update reverse
58                 $fulloldvalue0 = &convert_to_absolute(
59                                         $in{'oldvalue0'}, $in{'origin'});
60                 $fulloldname = &convert_to_absolute(
61                                         $in{'oldname'}, $in{'origin'});
62                 ($orevconf, $orevfile, $orevrec) = &find_reverse($in{'oldvalue0'},
63                                                                  $in{'view'});
64                 if ($in{'rev'} && $orevrec && &can_edit_reverse($orevconf) &&
65                     $fulloldname eq $orevrec->{'values'}->[0] &&
66                     ($in{'type'} eq "A" &&
67                      $in{'oldvalue0'} eq &arpa_to_ip($orevrec->{'name'}) ||
68                      $in{'type'} eq "AAAA" &&
69                      &expandall_ip6($in{'oldvalue0'}) eq &expandall_ip6(&ip6int_to_net($orevrec->{'name'})))) {
70                         &lock_file(&make_chroot($orevrec->{'file'}));
71                         &delete_record($orevrec->{'file'} , $orevrec);
72                         &lock_file(&make_chroot($orevfile));
73                         @orrecs = &read_zone_file($orevfile, $orevconf->{'name'});
74                         &bump_soa_record($orevfile, \@orrecs);
75                         &sign_dnssec_zone_if_key($orevconf, \@orrecs);
76                         }
77
78                 # Update forward
79                 local($ipv6 = ($fulloldvalue0 =~ /\.$ipv6revzone/i));
80                 ($ofwdconf, $ofwdfile, $ofwdrec) = &find_forward($fulloldvalue0, $ipv6);
81                 if ($in{'fwd'} && $ofwdrec && &can_edit_zone($ofwdconf) &&
82                     (!$ipv6 && &arpa_to_ip($in{'oldname'}) eq $ofwdrec->{'values'}->[0] ||
83                      $ipv6 && &expandall_ip6(&ip6int_to_net($in{'oldname'})) eq &expandall_ip6($ofwdrec->{'values'}->[0])) &&
84                     $fulloldvalue0 eq $ofwdrec->{'name'}) {
85                         &lock_file(&make_chroot($ofwdrec->{'file'}));
86                         &delete_record($ofwdrec->{'file'}, $ofwdrec);
87                         &lock_file(&make_chroot($ofwdfile));
88                         @ofrecs = &read_zone_file($ofwdfile, $ofwdconf->{'name'});
89                         &bump_soa_record($ofwdfile, \@ofrecs);
90                         &sign_dnssec_zone_if_key($ofwdconf, \@ofrecs);
91                         }
92
93                 &redirect("edit_recs.cgi?index=$in{'index'}&view=$in{'view'}&type=$in{'redirtype'}&sort=$in{'sort'}");
94                 &unlock_all_files();
95                 &webmin_log('delete', 'record', $in{'origin'}, $r);
96                 }
97         exit;
98         }
99
100 # Create values string based on inputs
101 if (!$in{'ttl_def'}) {
102         $in{'ttl'} =~ /^\d+$/ ||
103                 &error(&text('edit_ettl', $in{'ttl'}));
104         $ttl = $in{'ttl'}.$in{'ttlunit'};
105         }
106 $vals = $in{'value0'};
107 for($i=1; defined($in{"value$i"}); $i++) {
108         $vals .= " ".$in{"value$i"};
109         }
110 $vals =~ s/^\s+//;
111 $vals =~ s/\s+$//;
112
113 if ($in{'type'} eq "PTR" && $reverse) {
114         # a reverse address
115         local($ipv4);
116         ($ipv4 = $in{'origin'} =~ /in-addr\.arpa/i) ||
117             $in{'origin'} =~ /\.$ipv6revzone/i ||
118                 &error(&text('edit_eip', $in{'name'}));
119         if ($ipv4) {
120                 if ($in{'name'} =~ /^\d+$/) {
121                         $in{'name'} = &arpa_to_ip($in{'origin'}).".".$in{'name'};
122                         }
123                 &check_ipaddress($in{'name'}) ||
124                     ($in{'name'} =~ /^(.*)\.(\d+)$/ && &check_ipaddress("$1")) ||
125                     ($in{'name'} =~ /^(.*)\.(\d+)$/ && $1 eq &arpa_to_ip($in{'origin'})) ||
126                         &error(&text('edit_eip', $in{'name'}));
127                 $name = &ip_to_arpa($in{'name'});
128                 }
129         else {
130                 &check_ip6address($in{'name'}) ||
131                         &error(&text('edit_eip6', $in{'name'}));
132                 $name = &net_to_ip6int($in{'name'});
133                 }
134         &valname($in{'value0'}) ||
135                 &error(&text('edit_ehost', $vals[0]));
136         if ($in{'value0'} !~ /\.$/) { $vals .= "."; }
137         }
138 else {
139         # some other kind of record
140         $in{'name'} eq "" || $in{'name'} eq "@" || &valnamewild($in{'name'}) ||
141                 &error(&text('edit_ename', $in{'name'}));
142         if ($in{'type'} eq "A") {
143                 &check_ipaddress($vals) ||
144                         &error(&text('edit_eip', $vals));
145                 if (!$access{'multiple'}) {
146                         # Is this address already in use? Search all domains
147                         # to find out..
148                         foreach $z (@zl) {
149                                 next if ($z->{'type'} ne "master");
150                                 next if ($z->{'name'} =~ /in-addr\.arpa/i);
151                                 $file = $z->{'file'};
152                                 @frecs = &read_zone_file($file, $z->{'name'});
153                                 foreach $fr (@frecs) {
154                                         if ($fr->{'type'} eq "A" &&
155                                             $fr->{'values'}->[0] eq $vals &&
156                                             $fr->{'name'} ne $r->{'name'}) {
157                                                 &error(&text('edit_edupip',
158                                                              $vals));
159                                                 }
160                                         }
161                                 }
162                         }
163                 }
164         elsif ($in{'type'} eq "AAAA") {
165                 &check_ip6address($vals) ||
166                         &error(&text('edit_eip6', $vals));
167                 if (!$access{'multiple'}) {
168                         # Is this address already in use? Search all domains
169                         # to find out..
170                         foreach $z (@zl) {
171                                 next if ($z->{'type'} ne "master");
172                                 next if ($z->{'name'} =~ /\.$ipv6revzone/i);
173                                 $file = $z->{'file'};
174                                 @frecs = &read_zone_file($file, $z->{'name'});
175                                 foreach $fr (@frecs) {
176                                         if ($fr->{'type'} eq "AAAA" &&
177                                             &expandall_ip6($fr->{'values'}->[0]) eq &expandall_ip6($vals) &&
178                                             $fr->{'name'} ne $r->{'name'}) {
179                                                 &error(&text('edit_edupip',
180                                                              $vals));
181                                                 }
182                                         }
183                                 }
184                         }
185                 }
186         elsif ($in{'type'} eq "NS") {
187                 &valname($vals) ||
188                         &error(&text('edit_ens', $vals));
189                 if ($vals =~ /\.\Q$in{'origin'}\E$/) {
190                         # Make absolute
191                         $vals .= ".";
192                         }
193                 }
194         elsif ($in{'type'} eq "CNAME") {
195                 &valname($vals) || $vals eq '@' ||
196                         &error(&text('edit_ecname', $vals));
197                 if ($vals =~ /\.\Q$in{'origin'}\E$/) {
198                         $vals .= ".";
199                         }
200                 }
201         elsif ($in{'type'} eq "MX") {
202                 &valname($in{'value1'}) ||
203                         &error(&text('edit_emx', $in{'value1'}));
204                 $in{'value0'} =~ /^\d+$/ ||
205                         &error(&text('edit_epri', $in{'value0'}));
206                 if ($vals =~ /\.\Q$in{'origin'}\E$/) {
207                         $vals .= ".";
208                         }
209                 }
210         elsif ($in{'type'} eq "HINFO") {
211                 $in{'value0'} =~ /\S/ ||
212                         &error($text{'edit_ehard'});
213                 $in{'value1'} =~ /\S/ ||
214                         &error($text{'edit_eos'});
215                 $in{'value0'} = "\"$in{'value0'}\"" if ($in{'value0'} =~ /\s/);
216                 $in{'value1'} = "\"$in{'value1'}\"" if ($in{'value1'} =~ /\s/);
217                 $vals = $in{'value0'}." ".$in{'value1'};
218                 }
219         elsif ($in{'type'} eq "TXT") {
220                 $vals = $in{'value0'};
221                 $vals =~ s/((?:^|[^\\])(?:\\\\)*)[\"]/$1\\\"/g;
222                 $vals = "\"$vals\"";
223                 }
224         elsif ($in{'type'} eq "WKS") {
225                 &check_ipaddress($in{'value0'}) ||
226                         &error(&text('edit_eip', $in{'value0'}));
227                 if (!$in{'value2'}) {
228                         &error($text{'edit_eserv'});
229                         }
230                 @ws = split(/[\r\n]+|\s+/, $in{'value2'});
231                 $vals = "$in{'value0'} $in{'value1'} (";
232                 foreach $ws (@ws) {
233                         $ws =~ /^[a-z]([\w\-]*\w)?$/i ||
234                                 &error(&text('edit_ebadserv', $ws));
235                         $vals .= "\n\t\t\t\t\t$ws";
236                         }
237                 $vals .= " )";
238                 }
239         elsif ($in{'type'} eq "RP") {
240                 if (!$in{'value0'}) {
241                         $in{'value0'} = ".";
242                         }
243                 elsif (!&valemail($in{'value0'})) {
244                         &error(&text('edit_eemail', $in{'value0'}));
245                         }
246                 &valname($in{'value1'}) ||
247                         &error(&text('edit_etxt', $in{'value1'}));
248                 $in{'value0'} = &email_to_dotted($in{'value0'});
249                 $vals = "$in{'value0'} $in{'value1'}";
250                 }
251         elsif ($in{'type'} eq "LOC") {
252                 $in{'value0'} =~ /\S/ || &error($text{'edit_eloc'});
253                 }
254         elsif ($in{'type'} eq 'SRV') {
255                 $in{'serv'} =~ /^[A-Za-z0-9\-\_]+$/ ||
256                         &error(&text('edit_eserv2', $in{'serv'}));
257                 $in{'name'} = join(".", "_".$in{'serv'}, "_".$in{'proto'},
258                                    $in{'name'} ? ( $in{'name'} ) : ( ));
259                 $in{'value0'} =~ /^\d+$/ ||
260                         &error(text('edit_epri', $in{'value0'}));
261                 $in{'value1'} =~ /^\d+$/ ||
262                         &error(text('edit_eweight', $in{'value1'}));
263                 $in{'value2'} =~ /^\d+$/ ||
264                         &error(text('edit_eport', $in{'value2'}));
265                 &valname($in{'value3'}) ||
266                         &error(&text('edit_etarget', $in{'value3'}));
267                 }
268         elsif ($in{'type'} eq 'KEY') {
269                 $in{'value0'} =~ /^(\d+|0x[0-9a-f]+)$/i ||
270                         &error(text('edit_eflags', $in{'value0'}));
271                 $in{'value1'} =~ /^\d+$/ ||
272                         &error(text('edit_eproto', $in{'value1'}));
273                 $in{'value2'} =~ /^\d+$/ ||
274                         &error(text('edit_ealg', $in{'value2'}));
275                 $in{'value3'} =~ s/[ \r\n]//g;
276                 $in{'value3'} =~ /^[a-zA-Z0-9\/\+]+$/ ||
277                         &error(text('edit_ekey'));
278                 $vals = join(" ", $in{'value0'}, $in{'value1'},
279                                   $in{'value2'}, $in{'value3'});
280                 }
281         elsif ($in{'type'} eq 'PTR') {
282                 $vals = $in{'value0'};
283                 &valname($vals) ||
284                         &error(&text('edit_eptr', $vals));
285                 }
286         elsif ($in{'type'} eq 'SPF') {
287                 # For SPF records, build the SPF string from the inputs
288                 $spf = $r ? &parse_spf(@{$r->{'values'}}) : { };
289                 $spf->{'a'} = $in{'spfa'};
290                 $spf->{'mx'} = $in{'spfmx'};
291                 $spf->{'ptr'} = $in{'spfptr'};
292                 $spf->{'a:'} = [ split(/\s+/, $in{'spfas'}) ];
293                 foreach my $a (@{$spf->{'a:'}}) {
294                         &to_ipaddress($a) || &error(&text('edit_espfa', $a));
295                         }
296                 $spf->{'mx:'} = [ split(/\s+/, $in{'spfmxs'}) ];
297                 foreach my $mx (@{$spf->{'mx:'}}) {
298                         &valname($mx) || &error(&text('edit_espfmx', $mx));
299                         }
300                 @{$spf->{'mx:'}} <= 10 ||
301                         &error(&text('edit_espfmxmax', 10));
302                 $spf->{'ip4:'} = [ split(/\s+/, $in{'spfip4s'}) ];
303                 foreach my $ip (@{$spf->{'ip4:'}}) {
304                         &check_ipaddress($ip) ||
305                           ($ip =~ /^(\S+)\/\d+$/ && &check_ipaddress($1)) ||
306                             &error(&text('edit_espfip', $ip));
307                         }
308                 if (&supports_ipv6()) {
309                         $spf->{'ip6:'} = [ split(/\s+/, $in{'spfip6s'}) ];
310                         foreach my $ip (@{$spf->{'ip6:'}}) {
311                                 &check_ip6address($ip) ||
312                                   ($ip =~ /^(\S+)\/\d+$/ &&
313                                    &check_ip6address($1)) ||
314                                     &error(&text('edit_espfip6', $ip));
315                                 }
316                         }
317                 $spf->{'include:'} = [ split(/\s+/, $in{'spfincludes'}) ];
318                 foreach my $i (@{$spf->{'include:'}}) {
319                         &valname($i) || &error(&text('edit_espfinclude', $i));
320                         }
321                 $spf->{'all'} = $in{'spfall'};
322                 foreach my $m ('redirect', 'exp') {
323                         if ($in{'spf'.$m.'_def'}) {
324                                 delete($spf->{$m});
325                                 }
326                         else {
327                                 &valname($in{'spf'.$m}) || 
328                                         &error(&text('edit_espf'.$m, 
329                                                      $in{'spf'.$m}));
330                                 $spf->{$m} = $in{'spf'.$m};
331                                 }
332                         }
333                 $vals = "\"".&join_spf($spf)."\"";
334                 }
335         else {
336                 # For other record types, just save the lines
337                 $in{'values'} =~ s/\r//g;
338                 local @vlines = split(/\n/, $in{'values'});
339                 $vals = join(" ",map { $_ =~ /^\S+$/ ? $_ : "\"$_\"" } @vlines);
340                 }
341         $fullname = &convert_to_absolute($in{'name'}, $in{'origin'});
342         if ($config{'short_names'}) {
343                 $name = $in{'name'};
344                 }
345         else {
346                 $name = $fullname;
347                 }
348         }
349
350 # check for CNAME collision
351 if (!defined($in{'num'}) || $name ne $r->{'name'}) {
352         foreach $cr (@recs) {
353                 if ($cr->{'name'} eq $name) {
354                         if ($in{'type'} eq CNAME) {
355                                 &error($text{'edit_ecname1'});
356                                 }
357                         elsif ($cr->{'type'} eq 'CNAME') {
358                                 &error($text{'edit_ecname2'});
359                                 }
360                         }
361                 }
362         }
363
364 if ($in{'new'}) {
365         # adding a new record
366         ($revconf, $revfile, $revrec) = &find_reverse($in{'value0'},
367                                                       $in{'view'});
368         if ($in{'rev'} && $config{'rev_must'} && !$revconf) {
369                 # Reverse zone must exist, but doesn't
370                 &error($text{'edit_erevmust'});
371                 }
372         &create_record($in{'file'}, $name, $ttl, "IN", $in{'type'}, $vals,
373                        $in{'comment'});
374         $r = { 'name' => $name, 'ttl' => $ttl, 'class' => 'IN',
375                'type' => $in{'type'}, 'values' => [ split(/\s+/, $vals) ],
376                'comment' => $in{'comment'} };
377         if ($in{'rev'} && $revconf && &can_edit_reverse($revconf) &&
378             $in{'value0'} !~ /\*/) {
379                 local $rname = $in{'type'} eq "A" ? &ip_to_arpa($in{'value0'})
380                                                 : &net_to_ip6int($in{'value0'});
381                 if ($revrec && $in{'rev'} == 2) {
382                         # Upate the existing reverse for the domain
383                         &lock_file(&make_chroot($revrec->{'file'}));
384                         &modify_record($revrec->{'file'}, $revrec,
385                                        $rname, $revrec->{'ttl'}, "IN", "PTR",
386                                        $fullname);
387                         @rrecs = &read_zone_file($revfile, $revconf->{'name'});
388                         &bump_soa_record($revfile, \@rrecs);
389                         &sign_dnssec_zone_if_key($revconf, \@rrecs);
390                         }
391                 elsif (!$revrec) {
392                         # Add a reverse record if we are the master for the
393                         # reverse domain, and if there is not already a
394                         # reverse record for the address.
395                         &lock_file(&make_chroot($revfile));
396                         &create_record($revfile, $rname,
397                                 $ttl, "IN", "PTR", $fullname);
398                         @rrecs = &read_zone_file($revfile, $revconf->{'name'});
399                         &bump_soa_record($revfile, \@rrecs);
400                         &sign_dnssec_zone_if_key($revconf, \@rrecs);
401                         }
402                 }
403
404         ($fwdconf, $fwdfile, $fwdrec) = &find_forward($vals, $vals =~ /\.$ipv6revzone/i);
405         if ($in{'fwd'} && $fwdconf && !$fwdrec &&
406             &can_edit_zone($fwdconf)) {
407                 # Add a forward record if we are the master for the forward
408                 # domain, and if there is not already an A record
409                 # for the address
410                 local($rtype);
411                 if (&check_ipaddress($in{'name'})) {
412                         $rtype = "A";
413                         }
414                 elsif ($config{'support_aaaa'} &&
415                        &check_ip6address($in{'name'})) {
416                         $rtype = "AAAA";
417                         }
418                 if ($rtype) {
419                         &lock_file(&make_chroot($fwdfile));
420                         &create_record($fwdfile, $vals,
421                                        $ttl, "IN", $rtype, $in{'name'});
422                         @frecs = &read_zone_file($fwdfile, $fwdconf->{'name'});
423                         &bump_soa_record($fwdfile, \@frecs);
424                         &sign_dnssec_zone_if_key($fwdconf, \@frecs);
425                         }
426                 }
427         }
428 else {
429         # update an existing record
430         $fulloldvalue0 = &convert_to_absolute($in{'oldvalue0'}, $in{'origin'});
431         $fulloldname = &convert_to_absolute($in{'oldname'}, $in{'origin'});
432         ($orevconf, $orevfile, $orevrec) = &find_reverse($in{'oldvalue0'},
433                                                          $in{'view'});
434         ($revconf, $revfile, $revrec) = &find_reverse($in{'value0'},
435                                                       $in{'view'});
436         if ($in{'rev'} && $config{'rev_must'} && !$revconf) {
437                 # Reverse zone must exist, but doesn't
438                 &error($text{'edit_erevmust'});
439                 }
440         &lock_file(&make_chroot($r->{'file'}));
441         &modify_record($r->{'file'}, $r, $name, $ttl,
442                        "IN", $in{'type'}, $vals, $in{'comment'});
443
444         if ($in{'rev'} && $orevrec && &can_edit_reverse($orevconf) &&
445             $fulloldname eq $orevrec->{'values'}->[0] &&
446             ($in{'type'} eq "A" &&
447              $in{'oldvalue0'} eq &arpa_to_ip($orevrec->{'name'}) ||
448              $in{'type'} eq "AAAA" &&
449              &expandall_ip6($in{'oldvalue0'}) eq &expandall_ip6(&ip6int_to_net($orevrec->{'name'})))) {
450                 # Updating the reverse record. Either the name, address
451                 # or both may have changed. Furthermore, the reverse record
452                 # may now be in a different file!
453                 &lock_file(&make_chroot($orevfile));
454                 &lock_file(&make_chroot($revfile));
455                 @orrecs = &read_zone_file($orevfile, $orevconf->{'name'});
456                 @rrecs = &read_zone_file($revfile, $revconf->{'name'});
457                 if ($revconf eq $orevconf && &can_edit_reverse($revconf)) {
458                         # old and new in the same file
459                         &modify_record($orevrec->{'file'} , $orevrec, 
460                                        &net_to_ip6int(&ip_to_arpa($in{'value0'})),
461                                        $orevrec->{'ttl'}, "IN", "PTR", $fullname,
462                                        $in{'comment'});
463                         &bump_soa_record($orevfile, \@orrecs);
464                         &sign_dnssec_zone_if_key($orevconf, \@orrecs);
465                         }
466                 elsif ($revconf && &can_edit_reverse($revconf)) {
467                         # old and new in different files
468                         &delete_record($orevrec->{'file'} , $orevrec);
469                         &create_record($revfile, &net_to_ip6int(&ip_to_arpa($in{'value0'})),
470                                        $orevrec->{'ttl'}, "IN", "PTR", $fullname,
471                                        $in{'comment'});
472                         &bump_soa_record($orevfile, \@orrecs);
473                         &bump_soa_record($revfile, \@rrecs);
474                         &sign_dnssec_zone_if_key($orevconf, \@orrecs);
475                         &sign_dnssec_zone_if_key($revconf, \@rrecs);
476                         }
477                 else {
478                         # we don't handle the new reverse domain.. lose the
479                         # reverse record
480                         &delete_record($orevrec->{'file'}, $orevrec);
481                         &bump_soa_record($orevfile, \@orrecs);
482                         &sign_dnssec_zone_if_key($orevconf, \@orrecs);
483                         }
484                 }
485         elsif ($in{'rev'} && !$orevrec && $revconf && !$revrec && 
486                &can_edit_reverse($revconf)) {
487                 # we don't handle the old reverse domain but handle the new 
488                 # one.. create a new reverse record
489                 &lock_file(&make_chroot($revfile));
490                 @rrecs = &read_zone_file($revfile, $revconf->{'name'});
491                 &create_record($revfile, &net_to_ip6int(&ip_to_arpa($in{'value0'})),
492                                $ttl, "IN", "PTR", $fullname, $in{'comment'});
493                 &bump_soa_record($revfile, \@rrecs);
494                 &sign_dnssec_zone_if_key($revconf, \@rrecs);
495                 }
496
497         local($ipv6 = ($in{'value0'} =~ /\.$ipv6revzone/i));
498         ($ofwdconf, $ofwdfile, $ofwdrec) = &find_forward($fulloldvalue0, $ipv6);
499         ($fwdconf, $fwdfile, $fwdrec) = &find_forward($in{'value0'}, $ipv6);
500         if ($in{'fwd'} && $ofwdrec && &can_edit_zone($ofwdconf) &&
501             &expandall_ip6(&ip6int_to_net(&arpa_to_ip($in{'oldname'}))) eq
502             &expandall_ip6($ofwdrec->{'values'}->[0]) &&
503             $fulloldvalue0 eq $ofwdrec->{'name'}) {
504                 # Updating the forward record
505                 &lock_file(&make_chroot($ofwdfile));
506                 &lock_file(&make_chroot($fwdfile));
507                 @ofrecs = &read_zone_file($ofwdfile, $ofwdconf->{'name'});
508                 @frecs = &read_zone_file($fwdfile, $fwdconf->{'name'});
509                 if ($fwdconf eq $ofwdconf &&
510                     &can_edit_zone($fwdconf)) {
511                         # old and new are in the same file
512                         &modify_record($ofwdrec->{'file'} , $ofwdrec, $vals,
513                                        $ofwdrec->{'ttl'}, "IN",
514                                        $ipv6 ? "AAAA" : "A",
515                                        $in{'name'}, $in{'comment'});
516                         &bump_soa_record($ofwdfile, \@ofrecs);
517                         &sign_dnssec_zone_if_key($ofwdconf, \@ofrecs);
518                         }
519                 elsif ($fwdconf && &can_edit_zone($fwdconf)) {
520                         # old and new in different files
521                         &delete_record($ofwdrec->{'file'} , $ofwdrec);
522                         if (!ipv6 || $config{'support_aaaa'}) {
523                                 &create_record($fwdfile, $vals, $ofwdrec->{'ttl'},
524                                                "IN", $ipv6 ? "AAAA" : "A",
525                                                $in{'name'}, $in{'comment'});
526                                 &bump_soa_record($fwdfile, \@frecs);
527                                 &sign_dnssec_zone_if_key($fwdconf, \@frecs);
528                                 }
529                         &bump_soa_record($ofwdfile, \@ofrecs);
530                         &sign_dnssec_zone_if_key($ofwdconf, \@ofrecs);
531                         }
532                 else {
533                         # lose the forward because it has been moved to
534                         # a zone not handled by this server
535                         &delete_record($ofwdrec->{'file'} , $ofwdrec);
536                         &bump_soa_record($ofwdfile, \@ofrecs);
537                         &sign_dnssec_zone_if_key($ofwdconf, \@ofrecs);
538                         }
539                 }
540         }
541 &bump_soa_record($in{'file'}, \@recs);
542 &sign_dnssec_zone_if_key($zone, \@recs);
543 &unlock_all_files();
544 $r->{'newvalues'} = $vals;
545 &webmin_log($in{'new'} ? 'create' : 'modify', 'record', $in{'origin'}, $r);
546 &redirect("edit_recs.cgi?index=$in{'index'}&view=$in{'view'}&".
547           "type=$in{'redirtype'}&sort=$in{'sort'}");
548
549 # valname(name)
550 sub valname
551 {
552 return valdnsname($_[0], 0, $in{'origin'});
553 }
554
555 # valnamewild(name)
556 sub valnamewild
557 {
558 return valdnsname($_[0], 1, $in{'origin'});
559 }
560