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

File: [local] / botnow / DNS.pm (download)

Revision 1.1.1.1 (vendor branch), Sat May 15 15:12:32 2021 UTC (2 years, 11 months ago) by bountyht
Branch: ircnow
CVS Tags: start
Changes since 1.1: +0 -0 lines

Second import

#!/usr/bin/perl

package DNS;

use strict;
use warnings;
use OpenBSD::Pledge;
use OpenBSD::Unveil;
use Data::Dumper;
use File::Copy qw(copy);

my %conf = %main::conf;
my $chans = $conf{chans};
my $staff = $conf{staff};
my $key = $conf{key};
my $hash = $conf{hash};
my $hostname = $conf{hostname};
my $verbose = $conf{verbose};
my $ipv4 = $conf{ipv4};
my $zonedir = $conf{zonedir};
my $ipv6path = $conf{ipv6path};
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+/) {
	$ipv4 = $&;
}
main::cbind("msg", "-", "setrdns", \&msetrdns);
main::cbind("msg", "-", "delrdns", \&mdelrdns);
main::cbind("msg", "-", "setdns", \&msetdns);
main::cbind("msg", "-", "deldns", \&mdeldns);
main::cbind("msg", "-", "host", \&mhost);
main::cbind("msg", "-", "nextdns", \&mnextdns);

sub init {
	unveil("$ipv6path", "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 $!";
	#dependencies for host
	unveil("/usr/bin/host", "rx") or die "Unable to unveil $!";
}

sub msetrdns {
	my ($bot, $nick, $host, $hand, $text) = @_;
	if (! (main::isstaff($bot, $nick))) { return; }
	if ($text =~ /^([0-9A-Fa-f:\.]{3,})\s+([-0-9A-Za-z\.]+)/) {
		my ($ip, $hostname) = ($1, $2);
		if (setrdns($ip, $hostname)) {
			main::putserv($bot, "PRIVMSG $nick :$hostname set to $ip");
		} else {
			main::putserv($bot, "PRIVMSG $nick :ERROR: failed to set rDNS");
		}
	}
}
sub mdelrdns {
	my ($bot, $nick, $host, $hand, $text) = @_;
	if (! (main::isstaff($bot, $nick))) { return; }
	if ($text =~ /^([0-9A-Fa-f:\.]{3,})$/) {
		my $ip = $1;
		my $hostname = "notset";
		if (setrdns($ip, $hostname)) {
			main::putserv($bot, "PRIVMSG $nick :$ip rDNS deleted");
		} else {
			main::putserv($bot, "PRIVMSG $nick :ERROR: failed to set rDNS");
		}
	}
}
sub msetdns {
	my ($bot, $nick, $host, $hand, $text) = @_;
	if (! (main::isstaff($bot, $nick))) { return; }
	if ($text =~ /^([-0-9A-Za-z\.]+)\s+([0-9A-Fa-f:\.]+)/) {
		my ($hostname, $ip) = ($1, $2);
		if (setdns($hostname, $ip)) {
			main::putserv($bot, "PRIVMSG $nick :$hostname set to $ip");
		} else {
			main::putserv($bot, "PRIVMSG $nick :ERROR: failed to set DNS");
		}
	}
}
sub mdeldns {
	my ($bot, $nick, $host, $hand, $text) = @_;
	if (! (main::isstaff($bot, $nick))) { return; }
	if ($text =~ /^([-0-9A-Za-z\.]+)/) {
		if (setdns($text)) {
			main::putserv($bot, "PRIVMSG $nick :$text deleted");
		} else {
			main::putserv($bot, "PRIVMSG $nick :ERROR: failed to delete DNS records");
		}
	}
}
sub mhost {
	my ($bot, $nick, $host, $hand, $text) = @_;
	if (! (main::isstaff($bot, $nick))) { return; }
	if ($text =~ /^([-0-9A-Za-z:\.]{3,})/) {
		my ($hostname, $version) = ($1, $2);
		main::putserv($bot, "PRIVMSG $nick :".host($hostname));
	}
}

sub mnextdns {
	my ($bot, $nick, $host, $hand, $text) = @_;
	if (! (main::isstaff($bot, $nick))) { return; }
	if ($text =~ /^([-0-9a-zA-Z]+)/) {
		main::putserv($bot, "PRIVMSG $nick :$text set to ".nextdns($text));
	}
}

# Given filename, return a list of ipv6 addresses
sub readipv6s {
	my ($filename) = @_;
	my @lines = main::readarray($filename);
	my @ipv6s;
	foreach my $line (@lines) {
		if ($line =~ /^\s*inet6 (alias )?([0-9a-f:]{4,}) [0-9]+\s*$/i) {
			push(@ipv6s, $2);
		} elsif ($line =~ /^\s*([0-9a-f:]{4,})\s*$/i) {
			push(@ipv6s, $1);
		}
	}
	return @ipv6s;
}

# TODO: fix rdns request with buyvm's api, the ips must not skip 0s
# returns true upon success, false upon failure
sub setrdns {
	my ($ip, $hostname) = @_;
	my $stdout = `curl -d \"key=$key&hash=$hash&action=rdns&ip=$ip&rdns=$hostname\" https://manage.buyvm.net/api/client/command.php`;
	if ($stdout !~ /success/) {
		return 0;
	}
        return 1;
}
# set $domain to $ip if provided; otherwise, delete $domain
# returns true upon success, false upon failure
sub setdns {
	my ($domain, $ip) = @_;
	my $filename = "$zonedir/$hostname";
	my $subdomain;
	if ($domain =~ /^([a-zA-Z][-\.a-zA-Z0-9]+)\.$hostname$/) {
		$subdomain = $1;
	} else {
		return 0;
	}
	my @lines = main::readarray($filename);
	foreach my $line (@lines) {
		# increment the zone's serial number
		if ($line =~ /(\d{8})(\d{2})((\s+\d+){4}\s*\))/) {
			my $date = main::date();
			my $serial = 0;
			if ($date <= $1) { $serial = $2+1; }
			$line = $`.$date.sprintf("%02d",$serial).$3.$';
		}
	}
	if ($ip =~ /^([0-9\.]+)$/) { # if IPv4
        	push(@lines, "$subdomain	3600	IN	A	$ip");
	} elsif ($ip =~ /:/) { # if IPv6
        	push(@lines, "$subdomain	3600	IN	AAAA	$ip");
	} elsif (!defined($ip)) { # delete records
		@lines = grep !/\b$subdomain\s*3600\s*IN/, @lines;
	}
	# trailing newline necessary
	main::writefile("$filename.bak", join("\n", @lines)."\n");
	copy "$filename.bak", $filename;
	if (system("doas -u _nsd nsd-control reload")) {
		return 0;
	} else {
        	return 1;
	}
}

# given hostname, return IP addresses; or given IP address, return hostname
sub host {
	my ($name) = @_;
	my @matches;
	my @lines = split /\n/m, `host $name`;
	if ($name =~ /^[0-9\.]+$/ or $name =~ /:/) { # IP address
		foreach my $line (@lines) {
			if ($line =~ /([\d\.]+).(in-addr|ip6).arpa domain name pointer (.*)/) {
				push(@matches, $3);
			}
		}
	} else { # hostname
		foreach my $line (@lines) {
			if ($line =~ /$name has (IPv6 )?address ([0-9a-fA-F\.:]+)/) {
				push(@matches, $2);
			}
		}
	}
	return join(' ', @matches);
}

# create A and AAAA records for subdomain, set the rDNS,
# and return the new ipv6 address
sub nextdns {
	my ($subdomain) = @_;
	my $ipv6 = shift(@ipv6s);
	my $fqdn = "$subdomain.$hostname";
	main::writefile($ipv6path, join("\n", @ipv6s));
	if (setdns($fqdn, $ipv4) && setdns($fqdn, $ipv6) && setrdns($ipv6, $fqdn)) {
		return "$ipv6";
	}
	return "false";
}

1; # MUST BE LAST STATEMENT IN FILE