[BACK]Return to DNS.pm CVS log [TXT][DIR] Up to [local] / botnow

Diff for /botnow/DNS.pm between version 1.1 and 1.2

version 1.1, 2021/05/15 15:12:32 version 1.2, 2021/07/20 17:40:08
Line 16  my $key = $conf{key};
Line 16  my $key = $conf{key};
 my $hash = $conf{hash};  my $hash = $conf{hash};
 my $hostname = $conf{hostname};  my $hostname = $conf{hostname};
 my $verbose = $conf{verbose};  my $verbose = $conf{verbose};
 my $ipv4 = $conf{ipv4};  my $ip4 = $conf{ip4};
   my $ip6 = $conf{ip6};
   my $ip6subnet = $conf{ip6subnet};
 my $zonedir = $conf{zonedir};  my $zonedir = $conf{zonedir};
 my $ipv6path = $conf{ipv6path};  
 my $hostnameif = $conf{hostnameif};  my $hostnameif = $conf{hostnameif};
 # Validate ipv6s if it exists, otherwise load addresses from /etc/hostname.if  
 my @ipv6s;  
 if (!(-s "$ipv6path")) {  
         print "No IPv6 addresses in $ipv6path, loading from $hostnameif...\n";  
         @ipv6s = readipv6s($hostnameif);  
 } else {  
         @ipv6s = readipv6s($ipv6path);  
 }  
 if (!@ipv6s) { die "No IPv6 addresses in $ipv6path or $hostnameif!"; }  
 if (host($hostname) =~ /(\d+\.){3,}\d+/) {  if (host($hostname) =~ /(\d+\.){3,}\d+/) {
         $ipv4 = $&;          $ip4 = $&;
 }  }
 main::cbind("msg", "-", "setrdns", \&msetrdns);  main::cbind("msg", "-", "setrdns", \&msetrdns);
 main::cbind("msg", "-", "delrdns", \&mdelrdns);  main::cbind("msg", "-", "delrdns", \&mdelrdns);
Line 38  main::cbind("msg", "-", "setdns", \&msetdns);
Line 30  main::cbind("msg", "-", "setdns", \&msetdns);
 main::cbind("msg", "-", "deldns", \&mdeldns);  main::cbind("msg", "-", "deldns", \&mdeldns);
 main::cbind("msg", "-", "host", \&mhost);  main::cbind("msg", "-", "host", \&mhost);
 main::cbind("msg", "-", "nextdns", \&mnextdns);  main::cbind("msg", "-", "nextdns", \&mnextdns);
   main::cbind("msg", "-", "readip6s", \&mreadip6s);
   
 sub init {  sub init {
         unveil("$ipv6path", "rwc") or die "Unable to unveil $!";  
         unveil("$zonedir", "rwc") or die "Unable to unveil $!";          unveil("$zonedir", "rwc") or die "Unable to unveil $!";
         #dependencies for doas  
         unveil("/usr/bin/doas", "rx") or die "Unable to unveil $!";          unveil("/usr/bin/doas", "rx") or die "Unable to unveil $!";
         #dependencies for host  
         unveil("/usr/bin/host", "rx") or die "Unable to unveil $!";          unveil("/usr/bin/host", "rx") or die "Unable to unveil $!";
           unveil("$hostnameif", "rwc") or die "Unable to unveil $!";
 }  }
   
   # !setrdns 2001:bd8:: username.example.com
 sub msetrdns {  sub msetrdns {
         my ($bot, $nick, $host, $hand, $text) = @_;          my ($bot, $nick, $host, $hand, $text) = @_;
         if (! (main::isstaff($bot, $nick))) { return; }          if (! (main::isstaff($bot, $nick))) { return; }
         if ($text =~ /^([0-9A-Fa-f:\.]{3,})\s+([-0-9A-Za-z\.]+)/) {          if ($text =~ /^([0-9A-Fa-f:\.]{3,})\s+([-0-9A-Za-z\.]+)$/) {
                 my ($ip, $hostname) = ($1, $2);                  my ($ip, $hostname) = ($1, $2);
                 if (setrdns($ip, $hostname)) {                  if (setrdns($ip, $ip6subnet, $hostname)) {
                         main::putserv($bot, "PRIVMSG $nick :$hostname set to $ip");                          main::putserv($bot, "PRIVMSG $nick :$hostname set to $ip");
                 } else {                  } else {
                         main::putserv($bot, "PRIVMSG $nick :ERROR: failed to set rDNS");                          main::putserv($bot, "PRIVMSG $nick :ERROR: failed to set rDNS");
                 }                  }
         }          }
 }  }
   
   # !delrdns 2001:bd8::
 sub mdelrdns {  sub mdelrdns {
         my ($bot, $nick, $host, $hand, $text) = @_;          my ($bot, $nick, $host, $hand, $text) = @_;
         if (! (main::isstaff($bot, $nick))) { return; }          if (! (main::isstaff($bot, $nick))) { return; }
         if ($text =~ /^([0-9A-Fa-f:\.]{3,})$/) {          if ($text =~ /^([0-9A-Fa-f:\.]{3,})$/) {
                 my $ip = $1;                  my ($ip) = ($1);
                 my $hostname = "notset";                  if (delrdns($ip, $ip6subnet)) {
                 if (setrdns($ip, $hostname)) {  
                         main::putserv($bot, "PRIVMSG $nick :$ip rDNS deleted");                          main::putserv($bot, "PRIVMSG $nick :$ip rDNS deleted");
                 } else {                  } else {
                         main::putserv($bot, "PRIVMSG $nick :ERROR: failed to set rDNS");                          main::putserv($bot, "PRIVMSG $nick :ERROR: failed to set rDNS");
                 }                  }
         }          }
 }  }
   # !setdns username 1.2.3.4
 sub msetdns {  sub msetdns {
         my ($bot, $nick, $host, $hand, $text) = @_;          my ($bot, $nick, $host, $hand, $text) = @_;
         if (! (main::isstaff($bot, $nick))) { return; }          if (! (main::isstaff($bot, $nick))) { return; }
         if ($text =~ /^([-0-9A-Za-z\.]+)\s+([0-9A-Fa-f:\.]+)/) {          if ($text =~ /^([-0-9A-Za-z\.]+)\s+([0-9A-Fa-f:\.]+)/) {
                 my ($hostname, $ip) = ($1, $2);                  my ($name, $value) = ($1, $2);
                 if (setdns($hostname, $ip)) {                  if ($value =~ /:/ and setdns($name, $hostname, "AAAA", $value)) {
                         main::putserv($bot, "PRIVMSG $nick :$hostname set to $ip");                          main::putserv($bot, "PRIVMSG $nick :$name.$hostname AAAA set to $value");
                   } elsif (setdns($name, $hostname, "A", $value)) {
                           main::putserv($bot, "PRIVMSG $nick :$name.$hostname A set to $value");
                 } else {                  } else {
                         main::putserv($bot, "PRIVMSG $nick :ERROR: failed to set DNS");                          main::putserv($bot, "PRIVMSG $nick :ERROR: failed to set DNS");
                 }                  }
         }          }
 }  }
   
   # !deldns username
 sub mdeldns {  sub mdeldns {
         my ($bot, $nick, $host, $hand, $text) = @_;          my ($bot, $nick, $host, $hand, $text) = @_;
         if (! (main::isstaff($bot, $nick))) { return; }          if (! (main::isstaff($bot, $nick))) { return; }
         if ($text =~ /^([-0-9A-Za-z\.]+)/) {          if ($text =~ /^([-0-9A-Za-z\.]+)$/) {
                 if (setdns($text)) {                  my ($name) = ($1);
                   if (setdns($name, $hostname)) {
                         main::putserv($bot, "PRIVMSG $nick :$text deleted");                          main::putserv($bot, "PRIVMSG $nick :$text deleted");
                 } else {                  } else {
                         main::putserv($bot, "PRIVMSG $nick :ERROR: failed to delete DNS records");                          main::putserv($bot, "PRIVMSG $nick :ERROR: failed to delete DNS records");
                 }                  }
         }          }
 }  }
   
   # !host username
 sub mhost {  sub mhost {
         my ($bot, $nick, $host, $hand, $text) = @_;          my ($bot, $nick, $host, $hand, $text) = @_;
         if (! (main::isstaff($bot, $nick))) { return; }          if (! (main::isstaff($bot, $nick))) { return; }
         if ($text =~ /^([-0-9A-Za-z:\.]{3,})/) {          if ($text =~ /^([-0-9A-Za-z:\.]{3,})/) {
                 my ($hostname, $version) = ($1, $2);                  my ($hostname) = ($1);
                 main::putserv($bot, "PRIVMSG $nick :".host($hostname));                  main::putserv($bot, "PRIVMSG $nick :".host($hostname));
         }          }
 }  }
   
   # !nextdns username
 sub mnextdns {  sub mnextdns {
         my ($bot, $nick, $host, $hand, $text) = @_;          my ($bot, $nick, $host, $hand, $text) = @_;
         if (! (main::isstaff($bot, $nick))) { return; }          if (! (main::isstaff($bot, $nick))) { return; }
Line 113  sub mnextdns {
Line 115  sub mnextdns {
         }          }
 }  }
   
 # Given filename, return a list of ipv6 addresses  # !readip6s
 sub readipv6s {  sub mreadip6s {
           my ($bot, $nick, $host, $hand, $text) = @_;
           if (! (main::isstaff($bot, $nick))) { return; }
           foreach my $line (readip6s($hostnameif)) {
                   print "$line\n"
           }
   }
   
   # Return list of ipv6 addresses from filename
   sub readip6s {
         my ($filename) = @_;          my ($filename) = @_;
         my @lines = main::readarray($filename);          my @lines = main::readarray($filename);
         my @ipv6s;          my @ipv6s;
         foreach my $line (@lines) {          foreach my $line (@lines) {
                 if ($line =~ /^\s*inet6 (alias )?([0-9a-f:]{4,}) [0-9]+\s*$/i) {                  if ($line =~ /^\s*inet6\s+(alias\s+)?([0-9a-f:]{4,})\s+[0-9]+\s*$/i) {
                         push(@ipv6s, $2);                          push(@ipv6s, $2);
                 } elsif ($line =~ /^\s*([0-9a-f:]{4,})\s*$/i) {                  } elsif ($line =~ /^\s*([0-9a-f:]{4,})\s*$/i) {
                         push(@ipv6s, $1);                          push(@ipv6s, $1);
Line 128  sub readipv6s {
Line 139  sub readipv6s {
         return @ipv6s;          return @ipv6s;
 }  }
   
 # TODO: fix rdns request with buyvm's api, the ips must not skip 0s  # set rdns of $ip6 to $hostname given $subnet
 # returns true upon success, false upon failure  # return true on success; false on failure
 sub setrdns {  sub setrdns {
         my ($ip, $hostname) = @_;          my ($ip6, $subnet, $hostname) = @_;
         my $stdout = `curl -d \"key=$key&hash=$hash&action=rdns&ip=$ip&rdns=$hostname\" https://manage.buyvm.net/api/client/command.php`;          my $digits = ip6full($ip6);
         if ($stdout !~ /success/) {          $digits =~ tr/://d;
                 return 0;          my $reversed = reverse($digits);
         }          my $origin = substr($reversed, 32-$subnet/4);
         return 1;          $origin = join('.', split(//, $origin)).".ip6.arpa";
           my $name = substr($reversed, 0, 32-$subnet/4);
           $name = join('.', split(//, $name));
           # delete old PTR records, then set new one
           return setdns($name, $origin) && setdns($name, $origin, "PTR", $hostname);
 }  }
 # set $domain to $ip if provided; otherwise, delete $domain  # delete rdns of $ip6 given $subnet
   # return true on success; false on failure
   sub delrdns {
           my ($ip6, $subnet) = @_;
           return setrdns($ip6, $subnet);
   }
   
   # given $origin. create $name RR of $type and set to $value if provided;
   # if $value is missing, delete $domain
 # returns true upon success, false upon failure  # returns true upon success, false upon failure
 sub setdns {  sub setdns {
         my ($domain, $ip) = @_;          my ($name, $origin, $type, $value) = @_;
         my $filename = "$zonedir/$hostname";          my $filename = "$zonedir/$origin";
         my $subdomain;  
         if ($domain =~ /^([a-zA-Z][-\.a-zA-Z0-9]+)\.$hostname$/) {  
                 $subdomain = $1;  
         } else {  
                 return 0;  
         }  
         my @lines = main::readarray($filename);          my @lines = main::readarray($filename);
         foreach my $line (@lines) {          foreach my $line (@lines) {
                 # increment the zone's serial number                  # increment the zone's serial number
Line 159  sub setdns {
Line 176  sub setdns {
                         $line = $`.$date.sprintf("%02d",$serial).$3.$';                          $line = $`.$date.sprintf("%02d",$serial).$3.$';
                 }                  }
         }          }
         if ($ip =~ /^([0-9\.]+)$/) { # if IPv4          if (!defined($value)) { # delete records
                 push(@lines, "$subdomain        3600    IN      A       $ip");                  @lines = grep !/\b$name\s*3600\s*IN/, @lines;
         } elsif ($ip =~ /:/) { # if IPv6          } else {
                 push(@lines, "$subdomain        3600    IN      AAAA    $ip");                  push(@lines, "$name     3600    IN      $type   $value");
         } elsif (!defined($ip)) { # delete records  
                 @lines = grep !/\b$subdomain\s*3600\s*IN/, @lines;  
         }          }
         # trailing newline necessary          # trailing newline necessary
         main::writefile("$filename.bak", join("\n", @lines)."\n");          main::writefile("$filename.bak", join("\n", @lines)."\n");
Line 197  sub host {
Line 212  sub host {
         return join(' ', @matches);          return join(' ', @matches);
 }  }
   
   # Return an ipv6 address with all zeroes filled in
   sub ip6full {
           my ($ip6) = @_;
           my $left = substr($ip6, 0, index($ip6, "::"));
           my $leftcolons = ($left =~ tr/://);
           $ip6 =~ s{::}{:};
           my @quartets = split(':', $ip6);
           my $length = scalar(@quartets);
           for (my $n = 1; $n <= 8 - $length; $n++) {
                   splice(@quartets, $leftcolons+1, 0, "0000");
           }
           my @newquartets = map(sprintf('%04s', $_), @quartets);
           my $full = join(':',@newquartets);
           return $full;
   }
   # Returns the network part of the first IPv6 address (indicated by subnet)
   # with the host part of the second IPv6 address
   sub ip6mask {
           my ($ip6net, $subnet, $ip6host) = @_;
           my $netdigits = ip6full($ip6net);
           $netdigits =~ tr/://d;
           my $hostdigits = ip6full($ip6host);
           $hostdigits =~ tr/://d;
           my $digits = substr($netdigits,0,($subnet/4)).substr($hostdigits,($subnet/4));
           my $ip6;
           for (my $n = 0; $n < 32; $n++) {
                   if ($n > 0 && $n % 4 == 0) {
                           $ip6 .= ":";
                   }
                   $ip6 .= substr($digits,$n,1);
           }
           return $ip6;
   }
   sub randip6 {
           return join ':', map { sprintf '%04x', rand 0x10000 } (1 .. 8);
   }
   
 # create A and AAAA records for subdomain, set the rDNS,  # create A and AAAA records for subdomain, set the rDNS,
 # and return the new ipv6 address  # and return the new ipv6 address
 sub nextdns {  sub nextdns {
         my ($subdomain) = @_;          my ($subdomain) = @_;
         my $ipv6 = shift(@ipv6s);          my $newip6 = $ip6;
         my $fqdn = "$subdomain.$hostname";          my @allip6s = readip6s($hostnameif);
         main::writefile($ipv6path, join("\n", @ipv6s));          while (grep(/$newip6/, @allip6s)) {
         if (setdns($fqdn, $ipv4) && setdns($fqdn, $ipv6) && setrdns($ipv6, $fqdn)) {                  $newip6 = ip6mask($ip6, $ip6subnet,randip6());
                 return "$ipv6";          }
           main::appendfile($hostnameif, "inet6 alias $newip6 48\n");
           `doas ifconfig vio0 inet6 $newip6/48`;
           if (setdns($subdomain, $hostname, "A", $ip4) && setdns($subdomain, $hostname, "AAAA", $newip6) && setrdns($newip6, $ip6subnet, "$subdomain.$hostname")) {
                   return "$newip6";
         }          }
         return "false";          return "false";
 }  }

Legend:
Removed from v.1.1  
changed lines
  Added in v.1.2

CVSweb