#!/usr/sbin/perl use CGI qw(:standard); use Net::LDAP; use strict; use vars qw($LDAPHOST $BASEDN $BINDBASE $BINDUID $ANONBINDDN $ZONEEDIT $DEFAULT_MAIN @our_nameserver @zoneinfo @setinfo); my $LDAPHOST = "ldap0.server"; my $BASEDN = "ou=dns,o=tiscover"; my $BINDBASE = "ou=people,o=tiscover"; my $BINDUID = "uid"; my $ANONBINDDN = "ou=dns,o=tiscover"; my $ZONEEDIT = "zoneedit.pl"; my $DEFAULT_MAIN = "index.html"; my $LOGFILE = "/opt/httpd/logs/zoneedit.log"; my @our_nameserver = ( "ns1.tis.co.at", "ns2.tis.co.at" ); my @zoneinfo = qw( DNSzonename DNSserial DNSclass DNStype DNSexpire DNSretry DNSminimum DNSzonemaster DNSrefresh DNSadminmailbox DNSttl ); my @setinfo = qw( DNSdomainname DNStype DNSclass DNScname DNSipaddr DNSttl ); ################################################################################ eval { main(); }; if ($@) { errconfirm($@); } sub main { my $request = Apache->request(); my $query = new CGI; my $call = $query->param('call'); if (defined $call) { my $ldap = Net::LDAP->new($LDAPHOST) or die "can't make new LDAP object: $@"; my $user = $request->connection->user(); my $binddn = $BINDUID."=".$user.",$BINDBASE"; my ($ret, $password) = $request->get_basic_auth_pw(); my $mesg = $ldap->bind($binddn, password => $password); die "Unable to bind to LDAP server.
Reason: ".$mesg->error if ($mesg->code); my $selet = $query->param('selet') if $query->param('selet'); if ($call eq "dnslist") { dns_list($query, $ldap, $selet); } elsif ($call eq "newzone") { new_zone($query, $selet); } elsif ($call eq "addzone") { my $zonedn = add_zone($query, $ldap); log_action($user, "add_zone", $zonedn); $query->delete_all(); print $query->header, $query->start_html(-title=> 'Edit DNS Zone', -target=> 'main', -author=> 'jacob.rief@tiscover.com', -BGCOLOR=> 'WHITE'), "

"; edit_zone($query, $ldap, $zonedn, $selet); print $query->end_html; } elsif ($call eq "editzone") { my $zonedn = $query->param('zonedn'); if (defined $query->param('modifyzone')) { modify_zone($query, $ldap, $zonedn); log_action($user, "modify_zone_soa", $zonedn); } elsif (defined $query->param('addrrset')) { add_rrset($query, $ldap, $zonedn); log_action($user, "add_rrset", $zonedn); } elsif (defined $query->param('modifyrrset')) { my $setdn = $query->param('setdn'); modify_rrset($query, $ldap, $zonedn, $setdn); log_action($user, "modify_rrset", $setdn); } elsif (defined $query->param('deleterrset')) { my $setdn = $query->param('setdn'); delete_rrset($query, $ldap, $zonedn, $setdn); log_action($user, "delete_rrset", $setdn); } $query->delete_all(); print $query->header, $query->start_html(-title=> 'Edit DNS Zone', -target=> 'main', -author=> 'jacob.rief@tiscover.com', -BGCOLOR=> 'WHITE'), "

"; print_whois($ldap, $zonedn) if ($request->method eq "GET"); edit_zone($query, $ldap, $zonedn, $selet); print $query->end_html; } elsif ($call eq "deletezone") { my $zonedn = $query->param('zonedn'); delete_zone($query, $ldap, $zonedn); log_action($user, "delete_zone", $zonedn); } $ldap->unbind(); } else { # print frame print $query->header, "", " ", " ", ""; } } sub errconfirm { my $errmsg = shift; my $request = Apache->request(); $request->note_basic_auth_failure(); my $query = new CGI; print $query->header, $query->start_html(-title=> 'DNS Zone Admin', -target=> 'main', -author=> 'jacob.rief@tiscover.com', -BGCOLOR=> 'WHITE'), "

", $query->h2("An error occured"), "Message: $errmsg
\n", $query->end_html; $request->child_terminate(); } sub log_action { my ($user, $action, $dn) = @_; my ($sec,$min,$hour,$mday,$mon,$year) = localtime(); my @month = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec); my ($m, $y) = ($month[$mon], $year+1900); open(FILE, ">>$LOGFILE"); print FILE "[$mday/$m/$y:$hour:$min:$sec] $user $action $dn\n"; close(FILE); } sub list_attrs { my $attr = shift; my (@list, $key, $value); while (($key, $value) = each %$attr) { push(@list, $key => $value); } return \@list; } sub dns_list { my ($query, $ldap, $selet) = @_; my @letters = qw( 0 A B C D E F G H I J K L M N O P Q R S T U V W X Y Z ); print $query->header, $query->start_html(-title=> 'Zone-Selector', -target=> 'menu', -author=> 'jacob.rief@tiscover.com', -BGCOLOR=> 'WHITE'); my ($dnslookup, $resolver); if ($selet =~ /\~/) { $dnslookup = 1; use Net::DNS; $resolver = new Net::DNS::Resolver; } else { $dnslookup = 0; } print "\n", "\n"; foreach my $let (@letters) { if ($selet =~ /$let/) { my $newselet = $selet; $newselet =~ s/$let//; print "\n"; } else { my $newselet = $selet.$let; print "\n"; next; } my $mesg = $ldap->search(base => $BASEDN, filter => "(&(objectclass=DNSzone)(DNSzonename=$let*))"); my @entries = $mesg->entries; my ($zonename, %dn_entry, @unsorted); foreach my $entry (@entries) { $zonename = $entry->get_value('DNSzonename'); push @unsorted, $zonename; $dn_entry{$zonename} = $entry->dn(); } @entries = sort @unsorted; foreach $zonename (@entries) { my $zonedn = $dn_entry{$zonename}; if ($dnslookup) { my $query = $resolver->search($zonename, "NS"); my @ns; if ($query) { foreach my $rr ($query->answer) { next unless $rr->type eq "NS"; push @ns, $rr->nsdname; } } if (lc($ns[0]) eq lc($our_nameserver[0]) || lc($ns[1]) eq lc($our_nameserver[1]) || lc($ns[0]) eq lc($our_nameserver[1]) || lc($ns[1]) eq lc($our_nameserver[0]) ) { print "\n"; } } print "\n"; } else { print "&selet=$selet~\" TARGET=\"menu\">With DNS-lookup\n"; } print "
Add New Zone
- $let
+ $let
"; } elsif (defined $ns[0] || defined $ns[1]) { print "
"; } else { print "
"; } } else { print "
"; } print "$zonename
Without DNS-lookup
\n", $query->end_html; } sub print_zone_soa { my $zonedata = shift; print "Serial: ", textfield(-name=>'DNSserial', -size=>16, -maxlength=>24, -default=>$$zonedata{'DNSserial'}), "", "Refresh: ", textfield(-name=>'DNSrefresh', -size=>16, -maxlength=>24, -default=>$$zonedata{'DNSrefresh'}), "\n", "Retry: ", textfield(-name=>'DNSretry', -size=>16, -maxlength=>24, -default=>$$zonedata{'DNSretry'}), "", "Expire: ", textfield(-name=>'DNSexpire', -size=>16, -maxlength=>24, -default=>$$zonedata{'DNSexpire'}), "\n", "Minimum: ", textfield(-name=>'DNSminimum', -size=>16, -maxlength=>24, -default=>$$zonedata{'DNSminimum'}), "", "Adminmailbox: ", textfield(-name=>'DNSadminmailbox', -size=>16, -maxlength=>24, -default=>$$zonedata{'DNSadminmailbox'}), "\n", "Zonemaster: ", textfield(-name=>'DNSzonemaster', -size=>16, -maxlength=>24, -default=>$$zonedata{'DNSzonemaster'}), "", "Time to live: ", textfield(-name=>'DNSttl', -size=>16, -maxlength=>24, -default=>$$zonedata{'DNSttl'}), "\n"; } sub new_zone { my ($query, $selet) = @_; my %default_zonedata = ( "DNSzonename" => "", "DNSserial" => "", "DNSclass" => "IN", "DNStype" => "SOA", "DNSexpire" => "259200", "DNSretry" => "3600", "DNSminimum" => "86400", "DNSzonemaster" => "ns1.tis.co.at.", "DNSrefresh" => "10800", "DNSadminmailbox" => "domreg.tis.co.at.", "DNSttl" => "3600", ); my ($sec,$min,$hour,$mday,$mon,$year) = localtime(); $default_zonedata{"DNSserial"} = sprintf "%04d%02d%02d01", $year+1900, $mon+1, $mday; my $onsubmit = "{ parent.frames.menu.location='$ZONEEDIT?call=dnslist&selet=$selet'; }"; $query->param(call=>'addzone'); print $query->header, $query->start_html(-title=> 'Add DNS Zone', -target=> 'main', -author=> 'jacob.rief@tiscover.com', -BGCOLOR=> 'WHITE'), "

", $query->h2('Add DNS zone'), $query->start_multipart_form(-method=>'POST', -action=>"$ZONEEDIT", -target=>'main', -onSubmit=>$onsubmit), $query->hidden('call'), $query->hidden('selet'), "\n", "\n"; print_zone_soa(\%default_zonedata); print "
New Zonename: ", $query->textfield(-name=>'DNSzonename', -size=>40, -maxlength=>64), "
", $query->submit(-name=>" Submit "), "", $query->reset(), "
\n", $query->end_form(), $query->end_html; } sub add_zone { my ($query, $ldap) = @_; my %zonedata; foreach my $za (@zoneinfo) { $zonedata{$za} = $query->param($za) if defined $query->param($za); } my ($zonename, $zonedn) = ($zonedata{'DNSzonename'}, "cn=$zonedata{'DNSzonename'},$BASEDN"); my $attrs = list_attrs(\%zonedata); push(@$attrs, "objectclass", "DNSzone", "cn", "$zonename"); my $mesg = $ldap->add(dn=>$zonedn, attr=>$attrs); die "Failed to add zone: $zonename
Reason: ".$mesg->error if ($mesg->code); my @attr = ( "cn", "NS1:", "objectclass", "DNSrrset", "dnstype", "NS", "dnsclass", "IN", "dnsttl", "3600", "dnscname", $our_nameserver[0]."." ); my $dnch = "cn=NS1:,$zonedn"; die "Failed to add $dnch " if (($mesg = $ldap->add(dn=>$dnch, attr=>\@attr))->code); @attr = ( "cn", "NS2:", "objectclass", "DNSrrset", "dnstype", "NS", "dnsclass", "IN", "dnsttl", "3600", "dnscname", $our_nameserver[1]."." ); $dnch = "cn=NS2:,$zonedn"; die "Failed to add $dnch " if (($mesg = $ldap->add(dn=>$dnch, attr=>\@attr))->code); @attr = ( "cn", "A:www", "objectclass", "DNSrrset", "dnstype", "A", "dnsclass", "IN", "dnsdomainname", "www", "dnsttl", "3600", "dnsipaddr", "195.96.23.204" ); $dnch = "cn=A:www,$zonedn"; die "Failed to add $dnch
Reason: ".$mesg->error if (($mesg = $ldap->add(dn=>$dnch, attr=>\@attr))->code); return $zonedn; } sub modify_zone { my ($query, $ldap, $zonedn) = @_; my %zonedata; foreach my $za (@zoneinfo) { $zonedata{$za} = $query->param($za) if defined $query->param($za); } my @zonename; my $zn = ($ldap->search(base=>$zonedn, scope=>'base', filter=>"(objectclass=DNSzone)")->entry(0))->get_value('DNSzonename'); push @zonename, $zn; for (my $zc = 0; defined $query->param("DNSzonename$zc"); $zc++) { $zn = $query->param("DNSzonename$zc"); push @zonename, $zn if (length($zn)>3); } my $mesg = $ldap->modify($zonedn, delete => [ 'DNSzonename' ]); $mesg = $ldap->modify($zonedn, replace => \%zonedata) unless ($mesg->code); $mesg = $ldap->modify($zonedn, add => [ 'DNSzonename' => \@zonename ] ) unless ($mesg->code); die "Unable to modify zone: $zonedn
Reason: ".$mesg->error if ($mesg->code); } sub delete_zone { my ($query, $ldap, $zonedn) = @_; my $zonedn = $query->param('zonedn'); my $mesg = $ldap->search(base=>$zonedn, filter => "(objectclass=DNSrrset)"); my @entries = $mesg->entries; foreach my $entry (@entries) { $mesg = $ldap->delete($entry->dn()); last if ($mesg->code); } $mesg = $ldap->delete($zonedn) unless ($mesg->code); die "Unable to delete zone $zonedn.
Reason: ".$mesg->error if ($mesg->code); dnslist($query, $ldap); } sub edit_zone { my ($query, $ldap, $zonedn, $selet) = @_; my @zonename = ($ldap->search(base=>$zonedn, scope=>'base', filter=>"(objectclass=DNSzone)")->entry(0))->get_value('DNSzonename'); my $zonemaster = shift @zonename; $query->param(call=>'editzone'); $query->param(zonedn=>$zonedn); $query->param(selet=>$selet); # Table for SOA print $query->h2("Edit DNS zone $zonemaster"); print $query->start_multipart_form(-method=>'POST', -action=>"$ZONEEDIT", -target=>'main'), $query->hidden('call'), $query->hidden('zonedn'), $query->hidden('selet'), "\n"; my $zc = 0; my $entry = $ldap->search(base=>$zonedn, scope=>'base', filter=>"(objectclass=DNSzone)")->entry(0); my %zonedata; foreach my $za (@zoneinfo) { $zonedata{$za} = $entry->get_value($za); } print_zone_soa(\%zonedata); print "\n"; foreach my $zn (@zonename) { print "\n"; $zc++; } print "
Additional Zonename: ", $query->textfield(-name=>"DNSzonename$zc", -default=>$zn, -size=>40, -maxlength=>64), "
Add additional Zonename: ", $query->textfield(-name=>"DNSzonename$zc", -size=>40, -maxlength=>64), "
\n"; print "\n", $query->end_form(), "
", $query->submit(-name=>"modifyzone", -value=>" Modify Zone "), ""; my $onclick = "if(confirm('Do you really want to remove zone \"$zonemaster\" and all its resource records?'))" ."{ parent.frames.menu.location='$ZONEEDIT?call=deletezone&zonedn=$zonedn&selet=$selet'; parent.frames.main.location='$DEFAULT_MAIN'; }"; print $query->submit(-name=>"deletezone", -value=>" Delete Zone ", -onClick=>$onclick), "", $query->start_multipart_form(-method=>'POST', -action=>"$ZONEEDIT", -target=>'main'), $query->hidden('call'), $query->hidden('zonedn'), $query->hidden('selet'), $query->submit(-name=>"resetform", -value=>" Reset Form "), $query->end_form(), "
\n"; # Tables for RRsets my $mesg = $ldap->search(base=>$zonedn, filter => "(objectclass=DNSrrset)"); my @entries = $mesg->entries; print "\n\n", "\n"; foreach $entry (@entries) { my $setdn = $entry->dn(); my $domainname = $entry->get_value('DNSdomainname'); $domainname = "." if (!defined $domainname || length($domainname)<1); my $ipaddr = $entry->get_value('DNSipaddr'); my $cname = $entry->get_value('DNScname'); my $type = $entry->get_value('DNStype'); my $ttl = $entry->get_value('DNSttl'); $query->param(setdn => $setdn); print "", $query->start_multipart_form(-method=>'POST', -action=>"$ZONEEDIT", -target=>'main'), $query->hidden('call'), $query->hidden('selet'), $query->hidden('zonedn'), $query->hidden('setdn'), "", "", "", "", "", "", $query->end_form(), "\n"; } print "\n", $query->start_multipart_form(-method=>'POST', -action=>"$ZONEEDIT", -target=>'main'), $query->hidden('call'), $query->hidden('selet'), $query->hidden('zonedn'), "", "", "", "", "", "", $query->end_form(); print "
Name $#entriesTypeIPaddrCNAMETTL
$domainname$type", $query->textfield(-name=>'DNSipaddr', -default=>$ipaddr, -size=>16, -maxlength=>16), "", $query->textfield(-name=>'DNScname', -default=>$cname, -size=>16, -maxlength=>64), "", $query->textfield(-name=>'DNSttl', -default=>$ttl, -size=>6, -maxlength=>6), "", $query->submit(-name=>"modifyrrset", -value=>" Modify "), $query->submit(-name=>"deleterrset", -value=>" Delete "), "
", textfield(-name=>'DNSdomainname', -size=>8, -maxlength=>32), "", $query->popup_menu(-name=>'DNStype', -values=>['CNAME','A','MX','NS','PTR','TXT'], -default=>"A"), "", textfield(-name=>'DNSipaddr', -size=>16, -maxlength=>16), "", textfield(-name=>'DNScname', -size=>16, -maxlength=>64), "", textfield(-name=>'DNSttl', -default=>"3600", -size=>6, -maxlength=>6), "", $query->submit(-name=>"addrrset", -value=>" Add "), "
\n"; } sub add_rrset { my ($query, $ldap, $zonedn) = @_; my ($domainname, $type, @setattrs) = ($query->param('DNSdomainname'), $query->param('DNStype')); foreach my $za (@setinfo) { next unless (defined $query->param($za)); push (@setattrs, $za, $query->param($za)); } my $chdn = "$type:$domainname"; push (@setattrs, "objectclass", "DNSrrset", "cn", "$chdn"); $chdn = "cn=$chdn,$zonedn"; my $mesg = $ldap->add($chdn, attr => \@setattrs); die "Unable to add rrset: $chdn ".$mesg->error if ($mesg->code); my $newserial = $ldap->search(base=>$zonedn, scope=>'base', filter => "(objectclass=DNSzone)")->entry(0)->get_value('DNSserial')+1; $mesg = $ldap->modify($zonedn, replace => { 'DNSserial', $newserial }); die "Unable to modify serial number for: $zonedn ".$mesg->error if ($mesg->code); } sub modify_rrset { my ($query, $ldap, $zonedn, $setdn) = @_; my %setattrs; foreach my $za (@setinfo) { next unless (defined $query->param($za)); $setattrs{$za} = $query->param($za); } my $mesg = $ldap->modify($setdn, replace => \%setattrs); die "Unable to modify rrset: $setdn".$mesg->error if ($mesg->code); my $newserial = $ldap->search(base=>$zonedn, scope=>'base', filter => "(objectclass=DNSzone)")->entry(0)->get_value('DNSserial')+1; $mesg = $ldap->modify($zonedn, replace => { 'DNSserial', $newserial }); die "Unable to modify serial number for: $zonedn ".$mesg->error if ($mesg->code); } sub delete_rrset { my ($query, $ldap, $zonedn, $setdn) = @_; my $mesg = $ldap->delete($setdn); die "Unable to modify rrset: $setdn".$mesg->error if ($mesg->code); my $newserial = $ldap->search(base=>$zonedn, scope=>'base', filter => "(objectclass=DNSzone)")->entry(0)->get_value('DNSserial')+1; $mesg = $ldap->modify($zonedn, replace => { 'DNSserial', $newserial }); die "Unable to modify serial number for: $zonedn ".$mesg->error if ($mesg->code); } sub print_whois { my ($ldap, $zonedn) = @_; my ($zonename, $whois) = ($ldap->search(base=>$zonedn, scope=>'base', filter=>"(objectclass=DNSzone)")->entry(0))->get_value('DNSzonename'); use Net::Whois; unless ($whois = new Net::Whois::Domain $zonename) { print "

Unable to contact Whois-server

"; return; }; unless ($whois->ok) { print "

No Whois-record found for zone $zonename trying with "; # try with parent zone if ($zonename =~ /[^.]+\.(.*)/) { $zonename = $1; } print "$zonename

\n"; $whois = new Net::Whois::Domain($zonename); return unless ($whois->ok); } print "

Whois record for zone $zonename

\n"; print "Domain: ", $whois->domain, "
\n"; print "Name: ", $whois->name, "
\n"; print "Tag: ", $whois->tag, "
\n"; print "Address:\n", map { " $_
\n" } $whois->address; print "Country: ", $whois->country, "
\n"; print "Name Servers:
\n", map { " $$_[0] ($$_[1])
\n" } @{$whois->servers}; print "Record created:", $whois->record_created, "
\n"; print "Record updated:", $whois->record_updated, "
\n" ; }