#!/usr/bin/perl # ########################################################################## # # Filename: djb_update.pl # Description: Dynamic DNS-DHCP update scrypt # Author: Michael Stella # Created: November 10, 1998 # Last Updated: December 05, 2002 # Email: kazin@thismetalsky.org # HomePage: http://www.thismetalsky.org/magic/projects/dhcp_dns.html # ########################################################################### # # This code is Copyright (c) 1998-2002 by Michael Stella # # NO WARRANTY is given for this program. If it doesn't # work on your system, sorry. If it eats your hard drive, # again, sorry. It works fine on mine. Good luck! # # Note the lack of the GPL statement. I'm done with that, this is free # software. Just let me know if you're using it, I'm curious. # ########################################################################### # # This script reads a dhcpd.leases file and dynamically updates tinydns with # hostname and ip information. # # It assumes that your DHCP server recieves hostnames from the # clients, and that your clients offer their hostnames to the server. # Some versions of Linux DHCP clients don't do that. I use ISC's # DHCPD, found at http://www.isc.org - though others may work just # fine. # # As of version 1.0 you MUST use djbdns. # ########################################################################### # # 11/23/1998 - fixed the rarp stuff # 02/23/1999 - added support for multiple subnets # 02/24/1999 - improved lease file processing # 04/15/1999 - switched to dynamic DNS update - BIND 8 dynamic capabilities # 04/18/1999 - improved handling of client hostnames, to catch dumb user # problems improved error handling Thanks to Wayne Roberts # # 04/20/1999 - bugfix, error messages now show up correctly. # 08/24/1999 - switched to using logger for logging to system log files # Thanks to Michael Matsumura # 08/27/1999 - Actually figured out how to update the reverse-lookup zones, # Thanks again to Michael Matsumura. # 02/13/2001 - Ditched BIND, moved to djbdns # 05/25/2001 - Thanks to Nate Keegan - # apparently I didn't think to allow configuring the dnscache # path in the makefile. That's been fixed. # 12/05/2002 - Thanks to Ryan VanderBijl # for noticing that we should make sure that an entry doesn't # exist in the static file! # And thanks for writing his own changelog entry to MY code, # praising himself. :) # ########################################################################### use strict; $|=1; ########################################################################### ### Globals - you can change these as needed # Domain name my $domain_name = `hostname -f`; chomp($domain_name); # DHCPD lease file my $lease_file = "/var/state/dhcp/dhcpd.leases"; ## where does tinydns stuff live? my $tinydnspath = "/home/system/tinydns"; # tinydns text database files my $dhcp_dnsfile = "/home/system/tinydns/dhcp.conf"; my $static_dnsfile = "/home/system/tinydns/static.conf"; # number of seconds to check the lease file for updates my $update_freq = 5; my $debug = 0; ########################################################################### ### Don't mess with anything below unless you REALLY need to modify the ### code. And if you do, please let me know, I'm always interested in ### in improving this program. # Make a pid file `echo $$ > /var/run/djb_update.pid`; my $logstr; # last modified time my $modtime = 0; use vars qw (%db %static); my $version = "1.1.0"; ########################################################################### # Main Loop while (1) { # check the file's last updated time, if it's been changed, update # the DNS and save the modified time. This will ALWAYS run once - on # startup, since $modtime starts at zero. my @stats = stat ($lease_file); if ($stats[9] > $modtime) { # clear the old hash undef %db; printf STDERR "dhcpd.leases changed - updating DNS\n"; $modtime = $stats[9]; ## if error reading static dns file, dont do any update next unless (&read_static_dns); &read_lease_file; &update_dns; } # wait till next check time sleep $update_freq; } # end main ########################################################################### ## read in the static file, return 1 if we couldn't sub read_static_dns { %static = (); ## clear the list unless (open(DNSFILE, $static_dnsfile)) { print STDERR "Can't open static DNS file: won't generate dns from dhcp\n"; return 0; } while(defined($_ = )) { chop; next if /^\s*#/ || /^\s*$/; # skip comments/blanks s/^.(([^:]+):([^:]+)):?.*$/$1/; $static{lc $2} = $3; } close(DNSFILE); return 1; } ### write out the tinydns import file sub update_dns { my ($ip, $hostname); unless (open(DNSFILE, ">$dhcp_dnsfile")) { print STDERR "Can't open dhcp DNS file\n"; return; } while (($hostname,$ip) = each (%db)) { # in the future, set this to the end of the lease time, # i.e. do some math on that. print DNSFILE "=$hostname.$domain_name:$ip:5\n"; } close DNSFILE; `cd $tinydnspath && make > /dev/null`; } ### reads the lease file & makes a hash of what's in there. sub read_lease_file { unless (open(LEASEFILE,$lease_file)) { #`logger -t dns_update.pl error opening dhcpd lease file`; print STDERR "Can't open lease file\n"; return; } my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime(time); my $curdate = sprintf "%04d%02d%02d%02d%02d%02d", ($year+1900),($mon+1),$mday,$hour,$min,$sec; ## Loop here, reading from LEASEFILE while () { my ($ip, $hostname, $mac, $enddate,$endtime); if (/^\s*lease/i) { # find ip address $_ =~ /^\s*lease\s+(\S+)/; $ip = $1; # do the rest of the block - we're interested in hostname, # mac address, and the lease time while ($_ !~ /^}/) { $_ = ; # find hostname if ($_ =~ /^\s*client/i) { #chomp $_; #chop $_; $_ =~ /\"(.*)\"/; $hostname = $1; # change spaces to dash, remove dots - microsoft # really needs to not do this crap $hostname =~ s/\s+/-/g; $hostname =~ s/\.//g; } # get the lease end date elsif ($_ =~ /^\s*ends/i) { $_ =~ m/^\s*ends\s+\d\s+([^;]+);/; $enddate = $1; $enddate =~ s|[/: ]||g; } } # lowercase it - stupid dhcp clients $hostname = lc $hostname; ($debug < 1 ) || print STDERR "$hostname $ip $enddate $curdate\n"; ## if the hostname is defined in the static list, skip it if (exists $static{"$hostname.$domain_name"}) { print STDERR "Skipping $hostname.$domain_name dhcp dns " . "entry: static entry with same name exists\n"; next; } # Store hostname/ip in hash - this way we can do easy dupe checking if (($hostname ne "") and ($enddate > $curdate)) { $db{$hostname} = $ip; } } } close LEASEFILE; }