#!/usr/bin/env perl # # Copyright (c) 2023 Mischa Peters # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # use 5.024; use strict; use warnings; use autodie; use Fcntl qw(:flock); use File::Basename; use File::Copy; use POSIX qw(strftime); use Net::IP; my $ipv4_range = new Net::IP("46.23.80.0/20"); my $ipv6_range = new Net::IP("2a03:6000::/29"); my $nsd = "/var/nsd/zones/master"; my $zonefile = "high5.nl"; my $workdir = dirname($0); my $serial; my $serial_prev; opendir my $dh, "${workdir}/records"; while (my $file = readdir $dh) { chomp $file; next if $file =~ /^\./; open my $fh_ptr, '<', "${workdir}/records/$file"; my $replace = <$fh_ptr>; chomp $replace; my ($_hostname, $_in, $_a, $match) = split(' ', $replace, 4); close $fh_ptr; if (qx(rlog ${nsd}/${zonefile} | grep 'locked by') =~ m/locked by/) { _log("$file zone file locked, trying again later..."); next; } else { open my $fh_in, '<', "${nsd}/$zonefile"; open my $fh_out, '>', "${workdir}/zonefiles/$zonefile"; while (my $row = <$fh_in>) { chomp $row; if ($row =~ m/^\s*(\d+)\s*; serial$/) { $serial = $serial_prev = $1; my $timestamp = strftime ("%Y%m%d", localtime()) . "01"; if ($serial < $timestamp) { $serial = $timestamp; } else { $serial++; } $row =~ s/${serial_prev}/${serial}/; } if ($row =~ m/^[0-9a-z-]+\s+IN\s+A{1,4}\s+${match}( ;.*)?$/) { if ($1) { my $comment = $1; $row =~ s/^.*\s+${match}.*$/${replace}${comment}/; } else { $row =~ s/^.*\s+${match}.*$/${replace}/; } } print $fh_out "$row\n"; } close $fh_in; close $fh_out; (my $diff = qx(diff ${nsd}/${zonefile} ${workdir}/zonefiles/${zonefile} | wc -l)) =~ s/^\s*(.*?)\s*$/$1/; if ($diff == 8) { _log("$file diff within limits ($diff), $serial_prev -> $serial"); copy("${nsd}/${zonefile}", "${workdir}/zonefiles-archive/${zonefile}-${serial}"); qx(co -q -l ${nsd}/${zonefile}); copy("${workdir}/zonefiles/${zonefile}", "${nsd}/${zonefile}"); qx(ci -q -u -m"updated for ${file}" ${nsd}/${zonefile}); move("${workdir}/records/${file}", "${workdir}/records-archive/${file}-${serial}"); qx(${workdir}/../bin/auto-sign.sh ${zonefile}); qx(rcctl reload nsd); qx(rdist -f /etc/Distfile) if (-r '/etc/Distfile'); open my $fh_email, '|-', '/usr/sbin/sendmail -t'; print $fh_email "To: ard\@high5.nl\n"; print $fh_email "From: ard\@high5.nl\n"; print $fh_email "Subject: High5! A(AAA) $zonefile\n"; print $fh_email "Content-Type: text/plain; charset=utf-8\n\n"; print $fh_email "$serial_prev -> $serial\n$file\n$replace\n"; close $fh_email; } else { _log("$file diff is outside limits ($diff), cleaning up"); unlink("${workdir}/records/${file}", "${workdir}/zonefiles/${zonefile}"); } } } sub _log { my ($msg) = @_; open my $fh, '>>', '/var/log/ard.log'; flock $fh, LOCK_EX; print $fh sprintf("%s %s: %s \n", strftime("%b %d %H:%M:%S", localtime), basename($0), $msg); close $fh; }