#!/usr/bin/perl -w

use Net::DNS;
use Fuse;
use Data::Dumper;
use POSIX qw(ENOENT EISDIR EINVAL);

my $res = Net::DNS::Resolver->new(
		recurse => 1,
		debug => 0);
my %files = ();

sub dns_lookup {
	my ($type, $name) = @_;
	my @data;

	if ($name eq "so") {
		return ();
	}

	my $query = $res->send($name, $type);
	foreach my $rr ($query->answer) {
		if ($rr->type eq $type) {
			push(@data, $rr);
		}
	}
	print $res->errorstring . "\n";
	return @data;
}

sub get_dirname {
	my $name = shift;
	my @RR = get_NS($name);
	if (@RR) {
		return 1;
	} else {
		return 0;
	}
}

sub get_filename {
	my $name = shift;
	my @AAAA = dns_lookup("AAAA", $name);
	if (@AAAA) {
		@RR = dns_lookup("A", $name);
		if (@RR) {
			return (@RR, @AAAA);
		}
		return @AAAA;
	}
	return 0;
}

sub get_SOA {
	my $zone = shift;
	my @SOA = dns_lookup("SOA", $zone);
	return @SOA;
}

sub get_NS {
	my $zone = shift;
	my @NS  = dns_lookup("NS", $zone);
	return @NS;
}

# get a zone
# try a few nameservers before giving up
sub axfr_zone {
	my $zone = shift;
	my $rr;
	my @nameservers_raw = dns_lookup("NS", $zone);
	my @zonedata;
	my $res_axfr;

	foreach $rr (@nameservers_raw) { 
		$res_axfr = Net::DNS::Resolver->new(
				recurse => 0,  # important
				nameservers => [($rr->rdatastr)],
				debug => 0);

		@zonedata = $res_axfr->axfr($zone);
		if (@zonedata) {
			return @zonedata;
		}

	}
	return;
}

sub generate_dir {
	my $zone = shift;
	my (@SOA, @NS);
	my $rr;

	@files = qw(. .. SOA NS);
	my @axfr = axfr_zone($zone);
	foreach $rr (@axfr) {
		if ($rr->type eq "NS") {
			my $name = lc($rr->name);
			if ($seen{$name} != 1) {
				$seen{$name} = 1;
				push(@files, $name);
			}
		}
	}
	return @files;
}

print "READY\n";

sub filename_fixup {
	my ($file) = shift;
	$file =~ s/\//./g;
	$file =~ s/^\.//;
	if ($file eq "") {
		$file = ".";
	}
	return $file;
}

sub filename_reverse {
	my ($name) = shift;
	@parts = reverse(split(/\./, $name));
	$dns   = join(".", @parts);
	return $dns;
}

sub e_getattr {
	$file = shift;
	print "in e_getattr: " . $file . "\n";
	$file = filename_fixup($file);
	print "out e_getattr: " . $file . "\n";

	my @RR = ();
	my ($dev, $ino, $rdev, $blocks, $gid, $uid, $nlink, $blksize) = (0,0,0,1,0,0,1,1024);
	# our special names
	# SOA and NS
	if ($file =~ /(.*)SOA$/) {
		$tld = filename_reverse($1);
		print "SOA $tld\n";
		foreach $rr (get_SOA($tld)) {
			push(@RR, $rr->string . "\n");
		}
	}
	# SOA and NS
	if ($file =~ /(.*)NS$/) {
		$tld = filename_reverse($1);
		print "NS $tld\n";
		foreach $rr (get_NS($tld)) {
			push(@RR, $rr->string. "\n");
		}
	}
	if (@RR) {
		# is was a file, return the appropiate stuff
		$modes = (0100<<9) + 0644;
		$size = length(join("\n", @RR));
		$atime = $ctime = $mtime = time();
		return ($dev,$ino,$modes,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks);
	} else {
		$tld = filename_reverse($file);
		if (get_dirname($tld)) {
			# its a dir 
			$modes = (0040<<9) + 0755;
			$size = 4096;
			$atime = $ctime = $mtime = time();
			return ($dev,$ino,$modes,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks);
		}
		@a = get_filename($tld);
		if ($a[0] != 0) {
#		if (get_filename($tld)) {
		print "filess\n";
			# its a file still 
			$modes = (0100<<9) + 0644;
			$size = 0;
			$atime = $ctime = $mtime = time();
			return ($dev,$ino,$modes,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks);
		}
		return -ENOENT()
	}
	return -ENOENT()
}

sub e_getdir {
	$file = shift;
	print "in e_getdir: " . $file . "\n";
	$file = filename_fixup($file);
	print "out e_getdir: " . $file . "\n";

	@files = generate_dir($file);
	# return as many text filenames as you like, followed by the retval.
	print Dumper(@files);
	return @files,0;
}

sub e_open {
	$file  = shift;
	my ($buf, $off) = @_;
	if (!$buf) {
		$buf = 4096;
	}
	if (!$off) {
		$off = 0;
	}
	print "in e_open: " . $file . "\n";
	$file = filename_fixup($file);
	print "out e_open: " . $file . "\n";

	if ($file =~ /(.*)SOA$/) {
		return 0;
	}
	if ($file =~ /(.*)NS$/) {
		return 0;
	}
	$tld = filename_reverse($file);
	print "T:" .$tld . "\n";
	if (get_dirname($tld)) {
		return -EISDIR();
	}
	if (get_filename($tld)) {
		print "file\n";
		return 0;
	}
	return -ENOENT();
}

sub e_read {
	$file  = shift;
	my ($buf, $off) = @_;
	if (!$buf) {
		$buf = 4096;
	}
	if (!$off) {
		$off = 0;
	}
	print "in e_read: " . $file . "\n";
	$file = filename_fixup($file);
	print "out e_read: " . $file . "\n";

	@RR = ();
	if ($file =~ /(.*)SOA$/) {
		$tld = filename_reverse($1);
		print "SOA $tld\n";
		foreach $rr (get_SOA($tld)) {
			push(@RR, $rr->string . "\n");
		}
		return substr( join("", @RR), $off, $buf);
	}
	if ($file =~ /(.*)NS$/) {
		$tld = filename_reverse($1);
		print "NS $tld\n";
		foreach $rr (get_NS($tld)) {
			push(@RR, $rr->string . "\n");
		}
		return substr( join("", @RR), $off, $buf);
	}
	$tld = filename_reverse($file);
	print $tld . "\n";
	@a = get_filename($tld);
	if ($a[0] != 0) {
		foreach $rr (@a) {
			push(@RR, $rr->string . "\n");
		}
		return substr( join("", @RR), $off, $buf);
	}
	return -ENOENT();
}

sub e_statfs { return 255, 1, 1, 1, 1, 2 }

# If you run the script directly, it will run fusermount, which will in turn
# re-run this script.  Hence the funky semantics.
my ($mountpoint) = "";
$mountpoint = shift(@ARGV) if @ARGV;
Fuse::main(
	debug => 0,
	mountpoint=>$mountpoint,
	getattr=>"main::e_getattr",
	getdir =>"main::e_getdir",
	open   =>"main::e_open",
	statfs =>"main::e_statfs",
	read   =>"main::e_read",
	threaded=>0
	);

e_getattr("/");
e_getattr("/nl");
e_getattr("/nl/SOA");
e_getattr("SOA");
e_getattr("/nl/nlnetlabs/SOA");
print e_read("/SOA");
print e_read("/nl/nlnetlabs/SOA");
print e_read("/nl/nlnetlabs/www");
print e_read("/nl/nlnetlabs/jelte");
