?? b9ddns-lib.pl
字號:
#! /usr/bin/perl## B9DDNS - BIND 9 dynamic DNS webmin module.# Copyright (C) 2003 John Horne. <john.horne@plymouth.ac.uk># Copyright (C) 2004 John Horne. <john.horne@plymouth.ac.uk>## This program is free software; you can redistribute it and/or modify# it under the terms of the GNU General Public License as published by# the Free Software Foundation; either version 2 of the License, or# (at your option) any later version.## This program is distributed in the hope that it will be useful,# but WITHOUT ANY WARRANTY; without even the implied warranty of# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the# GNU General Public License for more details.## You should have received a copy of the GNU General Public License# along with this program; if not, write to the Free Software# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.### Common functions for the dynamic DNS module#use strict;no strict 'vars';no strict 'refs';do '../web-lib.pl';&init_config();do 'records-lib.pl';## This returns an array of references to assocations,# each containing the details of one named directive.## get_config()#sub get_config { unless (@get_config_cache) { @get_config_cache = &read_config_file($config{'named_conf'}); } return \@get_config_cache;}## This returns a structure containing the top-level config as members.## get_config_parent()#sub get_config_parent { local $conf = &get_config(); return { 'file' => $config{'named_conf'}, 'type' => 1, 'line' => -1, 'eline' => $lines_count{$config{'named_conf'}}, 'members' => $conf };}## Reads a named config file and returns an array of values.## read_config_file(file, [do not expand includes])#sub read_config_file { my $i = my $j = 0; my $lnum = my $line = 0; my $dir = my $str = ''; my @inc = my @ltok = my @lnum = my @tok = (); my @rv = (); if (open(FILE, $config{'chroot'} . $_[0])) { $dir = $1 if ($_[0] =~ /^(.*)\/[^\/]+$/o); } else { return @rv; } while (defined($line = <FILE>)) { chomp($line); $line =~ s/#.*$//o; # Strip out comments. $line =~ s://.*$::o if ($line !~ m:".*//.*":o); $line =~ s:/\*.*?\*/::go; $line =~ s/\s+$//o; # Split the line into tokens. while ($line =~ /^\s*"([^"]*)"(.*)$/o || $line =~ /^\s*([{};])(.*)$/o || $line =~ /^\s*((?:!)?[^{};\s]+)(.*)$/o) { push(@tok, $1); push(@lnum, $lnum); $line = $2; } $lnum++; } close(FILE); $lines_count{$_[0]} = $lnum; # # Now parse the tokens into data structures. # while ($i < @tok) { $str = &parse_struct(\@tok, \@lnum, \$i, $j++, $_[0]); push(@rv, $str) if (defined($str)); } unless ($_[1]) { # # Expand any INCLUDE directives. # &recursive_includes(\@rv, $dir); } return @rv;}## recursive_includes(&dirs, base)#sub recursive_includes { my $i = my $j = 0; my $ifile = my $dir = ''; my @inc = (); $dir = $_[1]; for ($i = 0; $i < @{ $_[0] }; $i++) { if (lc($_[0]->[$i]->{'name'}) eq 'include') { # # Found one...replace the INCLUDE directive with it. # $ifile = $_[0]->[$i]->{'value'}; if ($ifile !~ /^\//o) { $ifile = $dir . '/' . $ifile; } @inc = &read_config_file($ifile, 1); # # Update the index of included structures. # for ($j = 0; $j < @inc; $j++) { $inc[$j]->{'index'} += $_[0]->[$i]->{'index'}; } # # Update the index of structures after the include. # for ($j = $i + 1; $j < @{ $_[0] }; $j++) { $_[0]->[$j]->{'index'} += $#inc; } splice(@{ $_[0] }, $i--, 1, @inc); } elsif ($_[0]->[$i]->{'type'} == 1) { # # Check sub-structures too. # &recursive_includes($_[0]->[$i]->{'members'}, $dir); } } return;}## A structure can either have one value or a list of values.## parse_struct(&tokens, &lines, &line_num, index, file)#sub parse_struct { my $i = my $j = 0; my $struct = my $token = ''; local @vals = (); local %str = (); $i = ${$_[2]}; $str{'name'} = lc($_[0]->[$i]); $str{'line'} = $_[1]->[$i]; while (1) { $token = $_[0]->[++$i]; if ($token eq '{' || $token eq ';' || $token eq '}') { last; } elsif (! defined($token)) { ${$_[2]} = $i; return undef; } else { push(@vals, $token); } } $str{'values'} = \@vals; $str{'value'} = $vals[0]; $str{'index'} = $_[3]; $str{'file'} = $_[4]; if ($token eq '{') { # It contains sub-structures...parse them local @members = (); $i++; # Skip the { $j = 0; $str{'type'} = 1; while ($_[0]->[$i] ne '}') { unless (defined($_[0]->[$i])) { ${$_[2]} = $i; return undef; } $struct = &parse_struct($_[0], $_[1], \$i, $j++, $_[4]); push(@members, $struct) if (defined($struct)); } $str{'members'} = \@members; $i++; # Skip the trailing } $i++ if ($_[0]->[$i] eq ';'); # Skip any trailing ';' } else { # only a single value... $str{'type'} = 0; $i++ if ($token eq ';'); # Skip the trailing ; } # 'ending line' is the line number the trailing ; is on. $str{'eline'} = $_[1]->[$i - 1]; ${$_[2]} = $i; return \%str;}## Find a named entry.## find(name, &array)#sub find { my $c = ''; my @rv = (); my $name = lc($_[0]); foreach $c (@{ $_[1] }) { push(@rv, $c) if (lc($c->{'name'}) eq $name); } return (@rv) ? (wantarray ? @rv : $rv[0]) : (wantarray ? () : undef);}## Find the value of an entry.## find_value(name, &array)#sub find_value { my $value = ''; my @values = (); my $name = lc($_[0]); @values = &find($name, $_[1]); unless (@values) { return undef; } elsif (wantarray) { return map { $_->{'value'} } @values; } else { $value = $values[0]->{'value'}; # # We must leave filenames as they are. # $value = lc($value) if ($name !~ /file$/o && $name ne 'directory'); return $value; }}## Returns the base directory for named files.## base_directory([&config])#sub base_directory { my $opts = my $dir = my $conf = ''; $conf = $_[0] ? $_[0] : &get_config(); $opts = &find('options', $conf); if (defined($opts)) { $dir = &find('directory', $opts->{'members'}); $dir = (defined($dir)) ? $dir->{'values'}->[0] : ''; } return $dir;}## Given a structure containing a directive name, type, values and members,# add, update or remove that directive in the config structure and data files.# The updating of files assumes that there is no overlap between directives -# each line in the config file must contain part or all of only one directive.## The position argument takes the values:## 1: directive is to be placed at the top of the file# 0: directive is to be placed at the bottom of the file (default)# -1: directive is to be appended to end of file (regardless of whether# any directives of the same name exist).## save_directive(&parent, name|&old, &values, indent, [structonly], [position])#sub save_directive { my $pm = my $lref = my $ol = my $pos = my $i = my $n = 0; my @oldv = my @newv = my @nl = (); $pm = $_[0]->{'members'}; $pos = $_[5] if (defined($_[5])); @oldv = ref($_[1]) ? @{ $_[1] } : &find($_[1], $pm) unless ($pos == -1); @newv = @{ $_[2] }; for($i = 0; $i < @oldv || $i < @newv; $i++) { if ($i >= @oldv && $pos <= 0) { # # A new directive is being added. # Put it at the end of the parent. # unless ($_[4]) { $lref = &read_file_lines($config{'chroot'} . $_[0]->{'file'}); @nl = &directive_lines($newv[$i], $_[3]); splice(@$lref, $_[0]->{'eline'}, 0, @nl); $newv[$i]->{'file'} = $_[0]->{'file'}; $newv[$i]->{'line'} = $_[0]->{'eline'}; $newv[$i]->{'eline'} = $_[0]->{'eline'} + $#nl; &renumber(&get_config(), $_[0]->{'eline'}, $_[0]->{'file'}, scalar(@nl)); } push(@$pm, $newv[$i]); } elsif ($i >= @oldv && $pos == 1) { # # A new directive is being added. # Put it at the start of the parent. # unless ($_[4]) { $lref = &read_file_lines($config{'chroot'} . $_[0]->{'file'}); @nl = &directive_lines($newv[$i], $_[3]); splice(@$lref, $_[0]->{'line'} + 1, 0, @nl); $newv[$i]->{'file'} = $_[0]->{'file'}; $newv[$i]->{'line'} = $_[0]->{'line'} + 1; $newv[$i]->{'eline'} = $_[0]->{'line'} + scalar(@nl); &renumber(&get_config(), $_[0]->{'line'}, $_[0]->{'file'}, scalar(@nl)); } splice(@$pm, 0, 0, $newv[$i]); } elsif ($i >= @newv) { # # A directive was deleted. # unless ($_[4]) { $lref = &read_file_lines($config{'chroot'} . $oldv[$i]->{'file'}); $ol = $oldv[$i]->{'eline'} - $oldv[$i]->{'line'} + 1; splice(@$lref, $oldv[$i]->{'line'}, $ol); &renumber(&get_config(), $oldv[$i]->{'eline'}, $oldv[$i]->{'file'}, -$ol); } splice(@$pm, &indexof($oldv[$i], @$pm), 1); } else { # # Updating some directive. # unless ($_[4]) { $lref = &read_file_lines($config{'chroot'} . $oldv[$i]->{'file'}); @nl = &directive_lines($newv[$i], $_[3]); $ol = $oldv[$i]->{'eline'} - $oldv[$i]->{'line'} + 1; splice(@$lref, $oldv[$i]->{'line'}, $ol, @nl); $newv[$i]->{'file'} = $_[0]->{'file'}; $newv[$i]->{'line'} = $oldv[$i]->{'line'}; $newv[$i]->{'eline'} = $oldv[$i]->{'line'} + $#nl; &renumber(&get_config(), $oldv[$i]->{'eline'}, $oldv[$i]->{'file'}, scalar(@nl) - $ol); } $pm->[&indexof($oldv[$i], @$pm)] = $newv[$i]; } } return;}## Renders some directive into a number of lines of text.## directive_lines(&directive, tabs)#sub directive_lines { my $first = my $quoteit = 0; my $dir_name = my $v = ''; my @rv = (); my %need_quote = ( 'file' => 1, 'zone' => 1, 'pid-file' => 1, 'statistics-file' => 1, 'dump-file' => 1, 'directory' => 1, 'secret' => 1, 'view' => 1, 'version' => 1, 'random-device' => 1); if ($_[0]->{'name'}) { $dir_name = lc($_[0]->{'name'}); $quoteit = exists($need_quote{$dir_name}); $rv[0] = "\t" x $_[1]; $rv[0] .= $dir_name; } foreach $v (@{ $_[0]->{'values'} }) { if ($quoteit && ! $first) { $rv[0] .= ' "' . $v . '"'; } else { $rv[0] .= ' ' . $v; } $first++; } if ($_[0]->{'type'}) { # Multiple values should be included as well. $rv[0] .= ' in' if ($dir_name eq 'zone'); $rv[0] .= ' {'; foreach $v (@{ $_[0]->{'members'} }) { push(@rv, &directive_lines($v, $_[1] + 1)); } push(@rv, ("\t" x ($_[1] + 1)) . '}'); } $rv[$#rv] .= ';' if ($dir_name && $dir_name ne 'inet'); return @rv;}## Runs through a given array of directives and increases the line numbers# of all those greater than some given line by the given count.## renumber(&directives, line, file, count)#sub renumber { my $d = ''; foreach $d (@{ $_[0] }) { if ($d->{'file'} eq $_[2]) { if ($d->{'line'} > $_[1]) { $d->{'line'} += $_[3]; } if ($d->{'eline'} > $_[1]) { $d->{'eline'} += $_[3]; } } if ($d->{'type'}) { &renumber($d->{'members'}, $_[1], $_[2], $_[3]); } } return;}## Output HTML for multiple input choices.## choice_input(text, name, &config, disabled, [display, option]+)#sub choice_input { my $i = 0; my $str = my $disabled = my $v = ''; $disabled = 'disabled' if ($_[3]); $v = &find_value($_[1], $_[2]); $str = '<td valign=top><b>' . $_[0] . '</b></td><td valign=top>'; for($i = 4; $i < @_; $i += 2) { $str .= sprintf "<input type=radio $disabled name=%s value='%s' %s> %s\n", $_[1], $_[$i + 1], &checked($v eq $_[$i + 1]), $_[$i]; } return $str . "</td>\n";}## save_choice(name, &parent, indent)#sub save_choice { my $nd = 0; if ($in{$_[0]}) { $nd = { 'name' => $_[0], 'values' => [ $in{$_[0]} ] }; } &save_directive($_[1], $_[0], $nd ? [ $nd ] : [ ], $_[2]); return;}## Output a field for editing a list of addresses, partial addresses and ACL's.## addr_match_input(text, name, &config, ip_only, disabled)#sub addr_match_input { my $i = 0; my $name = my $disabled = my $rv = my $v = my $n = ''; my @av = (); $disabled = 'disabled' if ($_[4]); $v = &find($_[1], $_[2]); $rv = '<td valign=top><b>' . $_[0] . '</b></td><td valign=top>'; unless ($_[3]) { $rv .= sprintf "<input type=radio $disabled name=%s_def value=1 %s> %s ", $_[1], &checked(! $v), $text{'default'}; $rv .= sprintf "<input type=radio $disabled name=%s_def value=0 %s> %s<br>", $_[1], &checked($v), $text{'listed'}; } if (defined($v)) { foreach $name (@{ $v->{'values'} }) { $n .= $name . ' '; } push (@av, $n) if ($n); foreach $name (@{ $v->{'members'} }) { $n = $name->{'name'}; for ($i = 0; $i < @{ $name->{'values'} }; $i++) { $n .= ' ' . $name->{'values'}->[$i]; } push(@av, $n); } } $rv .= "<textarea $disabled name=" . $_[1] . ' rows=3 cols=25>' . join("\n", @av) . "</textarea></td>\n"; return $rv;}## save_addr_match(name, &config, indent, ip_only, port_allowed)#sub save_addr_match { my $master_port = my $port_allowed = my $ip_only = my $i = 0; my $ip_seen = 0; my $dir = my $ip = my $not = ''; my @vals = my @addr = (); my $has_port = sub { # Anonymous subroutine. if ($port_allowed && $i + 1 < @addr && $addr[$i+1] eq 'port') { if ($i + 2 >= @addr || $addr[$i + 2] =~ /\D/o) { &terror('controls_eport'); } else { $i += 2;
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -