Handle hostnames with upper-case letters
[webmin.git] / dnsadmin / dns-lib.pl
1 # dnsadmin common functions
2
3 BEGIN { push(@INC, ".."); };
4 use WebminCore;
5 &init_config();
6 &foreign_require("bind8", "bind8-lib.pl");
7 do "$bind8::module_root_directory/records-lib.pl";
8
9 # mapping between record types and names
10 %code_map = ("A", "Address", "NS", "Name Server", "CNAME", "Name Alias",
11              "MX", "Mail Server", "HINFO", "Host Information", "TXT", "Text",
12              "WKS", "Well Known Service", "RP", "Responsible Person",
13              "PTR", "Reverse Address");
14
15 # get_config()
16 # Returns the current bind4 configuration
17 sub get_config
18 {
19 if (!@get_config_cache) {
20         @get_config_cache = &read_config_file($config{'named_boot_file'});
21         }
22 return \@get_config_cache;
23 }
24
25 # read_config_file(filename, [dont expand includes])
26 # Read and parse a BIND4 format config file
27 sub read_config_file
28 {
29 # read primary config file
30 local $lnum = 0;
31 local $n = 0;
32 local($i, $j, @rv);
33 open(CONF, $_[0]);
34 while(<CONF>) {
35         s/\r|\n//g;     # strip newline
36         s/;.*$//g;      # strip comments
37         s/\s+$//g;      # strip trailing spaces
38         if (/^(\S+)\s*(.*)$/) {
39                 local(%dir);
40                 $dir{'name'} = $1;
41                 $dir{'value'} = $2;
42                 $dir{'values'} = [ split(/\s+/, $2) ];
43                 $dir{'line'} = $lnum;
44                 $dir{'file'} = $_[0];
45                 $dir{'index'} = $n++;
46                 push(@rv, \%dir);
47                 }
48         $lnum++;
49         }
50 close(CONF);
51
52 # expand include directives
53 for($i=0; $i<@rv; $i++) {
54         if ($rv[$i]->{'name'} eq "include" && !$_[1]) {
55                 # replace this include directive
56                 $inc = $rv[$i]->{'value'};
57                 if ($inc !~ /^\//) {
58                         $inc = &base_directory(\@rv)."/".$inc;
59                         }
60                 @inc = &read_config_file($inc, 1);
61
62                 # update index of included structures
63                 for($j=0; $j<@inc; $j++) {
64                         $inc[$j]->{'index'} += $rv[$i]->{'index'};
65                         }
66
67                 # update index of directives after include
68                 for($j=$i+1; $j<@rv; $j++) {
69                         $rv[$j]->{'index'} += scalar(@inc) - 1;
70                         }
71
72                 splice(@rv, $i--, 1, @inc);
73                 }
74         }
75 return @rv;
76 }
77
78 # find_config(name, &array)
79 sub find_config
80 {
81 local($c, @rv);
82 foreach $c (@{$_[1]}) {
83         if ($c->{'name'} eq $_[0]) {
84                 push(@rv, $c);
85                 }
86         }
87 return @rv ? wantarray ? @rv : $rv[0]
88            : wantarray ? () : undef;
89 }
90
91 # base_directory([&config])
92 # Returns the base directory for include and domain files
93 sub base_directory
94 {
95 $conf = @_ ? $_[0] : &get_config();
96 $dir = &find_config("directory", $conf);
97 if ($dir) { return $dir->{'values'}->[0]; }
98 $config{'named_boot_file'} =~ /^(.*)\/[^\/]+$/;
99 return $1;
100 }
101
102 # create_zone(&details)
103 sub create_zone
104 {
105 local(@v) = @{$_[0]->{'values'}};
106 open(ZONE, ">> $config{'named_boot_file'}");
107 print ZONE $_[0]->{'name'}.(@v ? " ".join(" ", @v) : "")."\n";
108 close(ZONE);
109 }
110
111 # modify_zone(&old, &details)
112 sub modify_zone
113 {
114 local(@v) = @{$_[1]->{'values'}};
115 &replace_file_line($_[0]->{'file'}, $_[0]->{'line'},
116                    $_[1]->{'name'}.(@v ? " ".join(" ", @v) : "")."\n");
117 }
118
119 # delete_zone(&old)
120 sub delete_zone
121 {
122 &replace_file_line($_[0]->{'file'}, $_[0]->{'line'});
123 }
124
125 # find_reverse(address)
126 # Returns the zone and record structures for the PTR record for some address
127 sub find_reverse
128 {
129 local($conf, @zl, $rev, $z, $revconf, $revfile, $revrec, @revrecs, @octs, $rr);
130
131 # find reverse domain
132 $conf = &get_config();
133 @zl = &find_config("primary", $conf);
134 @octs = split(/\./, $_[0]);
135 for($i=2; $i>=0; $i--) {
136         $rev = &ip_to_arpa(join('.', @octs[0..$i]));
137         $rev =~ s/\.$//g;
138         foreach $z (@zl) {
139                 if (lc($z->{'values'}->[0]) eq $rev) {
140                         # found the reverse master domain
141                         $revconf = $z;
142                         last;
143                         }
144                 }
145         }
146
147 # find reverse record
148 if ($revconf) {
149         $revfile = $revconf->{'values'}->[1];
150         @revrecs = &read_zone_file($revfile, $revconf->{'values'}->[0]);
151         local $addr = &ip_to_arpa($_[0]);
152         foreach $rr (@revrecs) {
153                 if ($rr->{'type'} eq "PTR" &&
154                     lc($rr->{'name'}) eq lc($addr)) {
155                         # found the reverse record
156                         $revrec = $rr;
157                         last;
158                         }
159                 }
160         }
161 return ($revconf, $revfile, $revrec);
162 }
163
164 # find_forward(address)
165 # Returns the zone and record structures for the A record for some address
166 sub find_forward
167 {
168 local($conf, @zl, $fwd, $z, $fwdconf, $fwdfile, $fwdrec, @fwdrecs, @octs, $rr);
169
170 # find reverse domain
171 local $host = $_[0]; $host =~ s/\.$//;
172 $conf = &get_config();
173 @zl = &find_config("primary", $conf);
174 local @parts = split(/\./, $host);
175 DOMAIN: for($i=1; $i<@parts; $i++) {
176         local $fwd = join(".", @parts[$i .. @parts-1]);
177         foreach $z (@zl) {
178                 local $typed;
179                 if (lc($z->{'values'}->[0]) eq $fwd) {
180                         # Found the forward master!
181                         $fwdconf = $z;
182                         last DOMAIN;
183                         }
184                 }
185         }
186
187 # find forward record
188 if ($fwdconf) {
189         $fwdfile = $fwdconf->{'values'}->[1];
190         local @fwdrecs = &read_zone_file($fwdfile, $fwdconf->{'values'}->[0]);
191         foreach $fr (@fwdrecs) {
192                 if ($fr->{'type'} eq 'A' &&
193                     $fr->{'name'} eq $_[0]) {
194                         # found the forward record
195                         $fwdrec = $fr;
196                         last;
197                         }
198                 }
199         }
200 return ($fwdconf, $fwdfile, $fwdrec);
201 }
202
203 # can_edit_zone(&access, zone)
204 sub can_edit_zone
205 {
206 local %zcan;
207 return 1 if ($access{'zones'} eq '*');
208 foreach (split(/\s+/, $access{'zones'})) {
209         return 1 if ($_ eq $_[1]);
210         }
211 return 0;
212 }
213
214 sub make_chroot
215 {
216 return $_[0];
217 }
218
219 1;
220