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

Annotation of botnow/DNS.pm, Revision 1.1.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