Annotation of botnow/DNS.pm, Revision 1.1
1.1 ! bountyht 1: #!/usr/bin/perl
! 2:
! 3: package DNS;
! 4:
! 5: use strict;
! 6: use warnings;
! 7: use OpenBSD::Pledge;
! 8: use OpenBSD::Unveil;
! 9: use Data::Dumper;
! 10: use File::Copy qw(copy);
! 11:
! 12: my %conf = %main::conf;
! 13: my $chans = $conf{chans};
! 14: my $staff = $conf{staff};
! 15: my $key = $conf{key};
! 16: my $hash = $conf{hash};
! 17: my $hostname = $conf{hostname};
! 18: my $verbose = $conf{verbose};
! 19: my $ipv4 = $conf{ipv4};
! 20: my $zonedir = $conf{zonedir};
! 21: my $ipv6path = $conf{ipv6path};
! 22: my $hostnameif = $conf{hostnameif};
! 23: # Validate ipv6s if it exists, otherwise load addresses from /etc/hostname.if
! 24: my @ipv6s;
! 25: if (!(-s "$ipv6path")) {
! 26: print "No IPv6 addresses in $ipv6path, loading from $hostnameif...\n";
! 27: @ipv6s = readipv6s($hostnameif);
! 28: } else {
! 29: @ipv6s = readipv6s($ipv6path);
! 30: }
! 31: if (!@ipv6s) { die "No IPv6 addresses in $ipv6path or $hostnameif!"; }
! 32: if (host($hostname) =~ /(\d+\.){3,}\d+/) {
! 33: $ipv4 = $&;
! 34: }
! 35: main::cbind("msg", "-", "setrdns", \&msetrdns);
! 36: main::cbind("msg", "-", "delrdns", \&mdelrdns);
! 37: main::cbind("msg", "-", "setdns", \&msetdns);
! 38: main::cbind("msg", "-", "deldns", \&mdeldns);
! 39: main::cbind("msg", "-", "host", \&mhost);
! 40: main::cbind("msg", "-", "nextdns", \&mnextdns);
! 41:
! 42: sub init {
! 43: unveil("$ipv6path", "rwc") or die "Unable to unveil $!";
! 44: unveil("$zonedir", "rwc") or die "Unable to unveil $!";
! 45: #dependencies for doas
! 46: unveil("/usr/bin/doas", "rx") or die "Unable to unveil $!";
! 47: #dependencies for host
! 48: unveil("/usr/bin/host", "rx") or die "Unable to unveil $!";
! 49: }
! 50:
! 51: sub msetrdns {
! 52: my ($bot, $nick, $host, $hand, $text) = @_;
! 53: if (! (main::isstaff($bot, $nick))) { return; }
! 54: if ($text =~ /^([0-9A-Fa-f:\.]{3,})\s+([-0-9A-Za-z\.]+)/) {
! 55: my ($ip, $hostname) = ($1, $2);
! 56: if (setrdns($ip, $hostname)) {
! 57: main::putserv($bot, "PRIVMSG $nick :$hostname set to $ip");
! 58: } else {
! 59: main::putserv($bot, "PRIVMSG $nick :ERROR: failed to set rDNS");
! 60: }
! 61: }
! 62: }
! 63: sub mdelrdns {
! 64: my ($bot, $nick, $host, $hand, $text) = @_;
! 65: if (! (main::isstaff($bot, $nick))) { return; }
! 66: if ($text =~ /^([0-9A-Fa-f:\.]{3,})$/) {
! 67: my $ip = $1;
! 68: my $hostname = "notset";
! 69: if (setrdns($ip, $hostname)) {
! 70: main::putserv($bot, "PRIVMSG $nick :$ip rDNS deleted");
! 71: } else {
! 72: main::putserv($bot, "PRIVMSG $nick :ERROR: failed to set rDNS");
! 73: }
! 74: }
! 75: }
! 76: sub msetdns {
! 77: my ($bot, $nick, $host, $hand, $text) = @_;
! 78: if (! (main::isstaff($bot, $nick))) { return; }
! 79: if ($text =~ /^([-0-9A-Za-z\.]+)\s+([0-9A-Fa-f:\.]+)/) {
! 80: my ($hostname, $ip) = ($1, $2);
! 81: if (setdns($hostname, $ip)) {
! 82: main::putserv($bot, "PRIVMSG $nick :$hostname set to $ip");
! 83: } else {
! 84: main::putserv($bot, "PRIVMSG $nick :ERROR: failed to set DNS");
! 85: }
! 86: }
! 87: }
! 88: sub mdeldns {
! 89: my ($bot, $nick, $host, $hand, $text) = @_;
! 90: if (! (main::isstaff($bot, $nick))) { return; }
! 91: if ($text =~ /^([-0-9A-Za-z\.]+)/) {
! 92: if (setdns($text)) {
! 93: main::putserv($bot, "PRIVMSG $nick :$text deleted");
! 94: } else {
! 95: main::putserv($bot, "PRIVMSG $nick :ERROR: failed to delete DNS records");
! 96: }
! 97: }
! 98: }
! 99: sub mhost {
! 100: my ($bot, $nick, $host, $hand, $text) = @_;
! 101: if (! (main::isstaff($bot, $nick))) { return; }
! 102: if ($text =~ /^([-0-9A-Za-z:\.]{3,})/) {
! 103: my ($hostname, $version) = ($1, $2);
! 104: main::putserv($bot, "PRIVMSG $nick :".host($hostname));
! 105: }
! 106: }
! 107:
! 108: sub mnextdns {
! 109: my ($bot, $nick, $host, $hand, $text) = @_;
! 110: if (! (main::isstaff($bot, $nick))) { return; }
! 111: if ($text =~ /^([-0-9a-zA-Z]+)/) {
! 112: main::putserv($bot, "PRIVMSG $nick :$text set to ".nextdns($text));
! 113: }
! 114: }
! 115:
! 116: # Given filename, return a list of ipv6 addresses
! 117: sub readipv6s {
! 118: my ($filename) = @_;
! 119: my @lines = main::readarray($filename);
! 120: my @ipv6s;
! 121: foreach my $line (@lines) {
! 122: if ($line =~ /^\s*inet6 (alias )?([0-9a-f:]{4,}) [0-9]+\s*$/i) {
! 123: push(@ipv6s, $2);
! 124: } elsif ($line =~ /^\s*([0-9a-f:]{4,})\s*$/i) {
! 125: push(@ipv6s, $1);
! 126: }
! 127: }
! 128: return @ipv6s;
! 129: }
! 130:
! 131: # TODO: fix rdns request with buyvm's api, the ips must not skip 0s
! 132: # returns true upon success, false upon failure
! 133: sub setrdns {
! 134: my ($ip, $hostname) = @_;
! 135: my $stdout = `curl -d \"key=$key&hash=$hash&action=rdns&ip=$ip&rdns=$hostname\" https://manage.buyvm.net/api/client/command.php`;
! 136: if ($stdout !~ /success/) {
! 137: return 0;
! 138: }
! 139: return 1;
! 140: }
! 141: # set $domain to $ip if provided; otherwise, delete $domain
! 142: # returns true upon success, false upon failure
! 143: sub setdns {
! 144: my ($domain, $ip) = @_;
! 145: my $filename = "$zonedir/$hostname";
! 146: my $subdomain;
! 147: if ($domain =~ /^([a-zA-Z][-\.a-zA-Z0-9]+)\.$hostname$/) {
! 148: $subdomain = $1;
! 149: } else {
! 150: return 0;
! 151: }
! 152: my @lines = main::readarray($filename);
! 153: foreach my $line (@lines) {
! 154: # increment the zone's serial number
! 155: if ($line =~ /(\d{8})(\d{2})((\s+\d+){4}\s*\))/) {
! 156: my $date = main::date();
! 157: my $serial = 0;
! 158: if ($date <= $1) { $serial = $2+1; }
! 159: $line = $`.$date.sprintf("%02d",$serial).$3.$';
! 160: }
! 161: }
! 162: if ($ip =~ /^([0-9\.]+)$/) { # if IPv4
! 163: push(@lines, "$subdomain 3600 IN A $ip");
! 164: } elsif ($ip =~ /:/) { # if IPv6
! 165: push(@lines, "$subdomain 3600 IN AAAA $ip");
! 166: } elsif (!defined($ip)) { # delete records
! 167: @lines = grep !/\b$subdomain\s*3600\s*IN/, @lines;
! 168: }
! 169: # trailing newline necessary
! 170: main::writefile("$filename.bak", join("\n", @lines)."\n");
! 171: copy "$filename.bak", $filename;
! 172: if (system("doas -u _nsd nsd-control reload")) {
! 173: return 0;
! 174: } else {
! 175: return 1;
! 176: }
! 177: }
! 178:
! 179: # given hostname, return IP addresses; or given IP address, return hostname
! 180: sub host {
! 181: my ($name) = @_;
! 182: my @matches;
! 183: my @lines = split /\n/m, `host $name`;
! 184: if ($name =~ /^[0-9\.]+$/ or $name =~ /:/) { # IP address
! 185: foreach my $line (@lines) {
! 186: if ($line =~ /([\d\.]+).(in-addr|ip6).arpa domain name pointer (.*)/) {
! 187: push(@matches, $3);
! 188: }
! 189: }
! 190: } else { # hostname
! 191: foreach my $line (@lines) {
! 192: if ($line =~ /$name has (IPv6 )?address ([0-9a-fA-F\.:]+)/) {
! 193: push(@matches, $2);
! 194: }
! 195: }
! 196: }
! 197: return join(' ', @matches);
! 198: }
! 199:
! 200: # create A and AAAA records for subdomain, set the rDNS,
! 201: # and return the new ipv6 address
! 202: sub nextdns {
! 203: my ($subdomain) = @_;
! 204: my $ipv6 = shift(@ipv6s);
! 205: my $fqdn = "$subdomain.$hostname";
! 206: main::writefile($ipv6path, join("\n", @ipv6s));
! 207: if (setdns($fqdn, $ipv4) && setdns($fqdn, $ipv6) && setrdns($ipv6, $fqdn)) {
! 208: return "$ipv6";
! 209: }
! 210: return "false";
! 211: }
! 212:
! 213: 1; # MUST BE LAST STATEMENT IN FILE
CVSweb