]> git.refcnt.org Git - distdns.git/commitdiff
Initial commit.
authorSteven Schubiger <stsc@refcnt.org>
Sat, 6 Apr 2013 17:20:25 +0000 (19:20 +0200)
committerSteven Schubiger <stsc@refcnt.org>
Sat, 6 Apr 2013 17:20:25 +0000 (19:20 +0200)
client.pl [new file with mode: 0755]
server.cgi [new file with mode: 0755]

diff --git a/client.pl b/client.pl
new file mode 100755 (executable)
index 0000000..917eec3
--- /dev/null
+++ b/client.pl
@@ -0,0 +1,151 @@
+#!/usr/bin/perl
+#
+# Copyright (c) 2013 Michel Ketterle, Steven Schubiger
+#
+# This file is part of distdns.
+#
+# distdns is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# distdns is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with distdns.  If not, see <http://www.gnu.org/licenses/>.
+
+use strict;
+use warnings;
+use constant false => 0;
+
+use Config::Tiny;
+use Digest::MD5 qw(md5_hex);
+use Fcntl ':flock';
+use File::Spec::Functions qw(rel2abs);
+use Getopt::Long qw(:config no_auto_abbrev no_ignore_case);
+use JSON qw(decode_json);
+use LWP::UserAgent;
+use Sys::Hostname qw(hostname);
+use Tie::File;
+
+my $VERSION = '0.04';
+
+#-----------------------
+# Start of configuration
+#-----------------------
+
+my $config_file  = 'dynuser.conf';
+my $hosts_file   = 'hosts';
+my $session_file = 'session.dat';
+my $server_url   = 'http://refcnt.org/~sts/cgi-bin/ketterle/server.cgi';
+
+#---------------------
+# End of configuration
+#---------------------
+
+sub usage
+{
+    print <<"USAGE";
+Usage: $0
+    -d, --debug    server debugging
+    -h, --help     this help screen
+    -i, --init     initialize session data
+USAGE
+    exit;
+}
+
+my %opts;
+GetOptions(\%opts, qw(d|debug h|help i|init)) or usage();
+usage() if $opts{h};
+
+$config_file  = rel2abs($config_file);
+$hosts_file   = rel2abs($hosts_file);
+$session_file = rel2abs($session_file);
+
+my $save_session = sub
+{
+    my ($session) = @_;
+
+    open(my $fh, '>', $session_file) or die "Cannot open client-side $session_file for writing: $!\n";
+    print {$fh} "$session\n";
+    close($fh);
+};
+
+my $get_session = sub
+{
+    open(my $fh, '<', $session_file) or die "Cannot open client-side $session_file for reading: $!\nPerhaps try running --init\n";
+    my $session = do { local $/; <$fh> };
+    chomp $session;
+    close($fh);
+
+    return $session;
+};
+
+my $session = $opts{i} ? substr(md5_hex(md5_hex(time() . {} . rand() . $$)), 0, 32) : $get_session->();
+
+my $config = Config::Tiny->new;
+   $config = Config::Tiny->read($config_file);
+
+my ($netz, $name) = @{$config->{data}}{qw(netz name)};
+
+die "$0: Network and/or name not set in $config_file\n" unless defined $netz && defined $name;
+
+my %params = (
+    netz    => $netz,
+    pc      => hostname(),
+    name    => $name,
+    debug   => $opts{d} || false,
+    init    => $opts{i} || false,
+    session => $session,
+);
+
+my $ua = LWP::UserAgent->new;
+
+my $response = $ua->post($server_url, \%params);
+
+if ($response->is_success) {
+    my $data;
+
+    eval {
+        $data = decode_json($response->decoded_content);
+    } or exit;
+
+    die "$0: $data->{error}" if defined $data->{error};
+
+    $save_session->($session) if $opts{i};
+
+    my %list;
+    foreach my $entry (@{$data->{entries}}) {
+        my $host = "$entry->{ip}\t" . join '.', @$entry{qw(name pc netz)};
+        push @{$list{$entry->{netz}}}, $host;
+    }
+
+    my $o = tie my @hosts, 'Tie::File', $hosts_file or die "$0: Cannot tie $hosts_file: $!\n";
+    $o->flock(LOCK_EX);
+
+    foreach my $network (keys %list) {
+        my %indexes;
+        for (my $i = 0; $i < @hosts; $i++) {
+            if ($hosts[$i] =~ /^\#$network\#$/i) {
+                $indexes{start} = $i;
+            }
+            elsif (exists $indexes{start} && $hosts[$i] =~ /^\#\/$network\#$/i) {
+                $indexes{end} = $i;
+                my $count = ($indexes{end} - $indexes{start} > 1)
+                  ? $indexes{end} - $indexes{start} - 1
+                  : 0;
+                splice @hosts, $indexes{start} + 1, $count, @{$list{$network}};
+                last;
+            }
+        }
+    }
+
+    undef $o;
+    untie @hosts;
+}
+else {
+    warn $response->status_line, "\n";
+}
diff --git a/server.cgi b/server.cgi
new file mode 100755 (executable)
index 0000000..f52f3d9
--- /dev/null
@@ -0,0 +1,128 @@
+#!/usr/bin/perl
+#
+# Copyright (c) 2013 Michel Ketterle, Steven Schubiger
+#
+# This file is part of distdns.
+#
+# distdns is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# distdns is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with distdns.  If not, see <http://www.gnu.org/licenses/>.
+
+use strict;
+use warnings;
+
+use CGI ();
+use Fcntl ':flock';
+use JSON qw(decode_json encode_json);
+
+my $VERSION = '0.04';
+
+#-----------------------
+# Start of configuration
+#-----------------------
+
+my $json_file    = 'data.json';
+my $session_file = 'session.dat';
+
+#---------------------
+# End of configuration
+#---------------------
+
+my $query = CGI->new;
+
+my @params = qw(netz pc name debug init session);
+my %params;
+
+foreach my $param (@params) {
+    $params{$param} = $query->param($param);
+}
+$params{ip} = $query->remote_addr;
+
+if ($params{debug}) {
+    $SIG{__DIE__} = sub
+    {
+        print $query->header('application/json');
+        print encode_json({ entries => [], error => $_[0] });
+        exit;
+    };
+}
+
+if ($params{init}) {
+    die "Delete server-side $session_file first\n" if -e $session_file;
+
+    open(my $fh, '>', $session_file) or die "Cannot open server-side $session_file for writing: $!\n";
+    print {$fh} "$params{session}\n";
+    close($fh);
+}
+else {
+    open(my $fh, '<', $session_file) or die "Cannot open server-side $session_file for reading: $!\nPerhaps try running --init\n";
+    my $session = do { local $/; <$fh> };
+    chomp $session;
+    close($fh);
+
+    die "Session ID mismatch\n" unless $params{session} eq $session;
+}
+
+my @missing_params = grep { not defined $params{$_} && length $params{$_} } @params;
+if (@missing_params) {
+    my $missing_params = join ', ', map "'$_'", @missing_params;
+    die "Incomplete query: param(s) $missing_params missing or not defined\n";
+}
+
+my %access;
+my $access_file = "$params{netz}.conf";
+
+if (-e $access_file) {
+    open(my $fh, '<', $access_file) or die "Cannot open $access_file for reading: $!\n";
+    while (my $line = <$fh>) {
+        chomp $line;
+        my ($name, $pc) = split /\s*,\s*/, $line;
+        push @{$access{$name}}, $pc;
+    }
+    close($fh);
+}
+else {
+    die "Access file $access_file does not exist\n";
+}
+
+if (exists $access{$params{name}} && grep /^$params{pc}$/i, @{$access{$params{name}}}) {
+    open(my $fh, '+<', $json_file) or die "Cannot open $json_file for read/write: $!\n";
+    flock($fh, LOCK_EX)            or die "Cannot lock $json_file: $!\n";
+
+    my $json = do { local $/; <$fh> };
+
+    my $data = defined $json && length $json ? decode_json($json) : [];
+
+    for (my $i = 0; $i < @$data; $i++) {
+        if ($params{netz} eq $data->[$i]->{netz}
+         && $params{pc}   eq $data->[$i]->{pc}
+         && $params{name} eq $data->[$i]->{name}) {
+            splice @$data, $i--, 1;
+        }
+    }
+    push @$data, { map { $_ => $params{$_} } qw(netz pc name ip) };
+
+    seek($fh, 0, 0)  or die "Cannot seek to start of $json_file: $!\n";
+    truncate($fh, 0) or die "Cannot truncate $json_file: $!\n";
+
+    print {$fh} encode_json($data);
+
+    close($fh);
+
+    my @data = grep $_->{netz} eq $params{netz}, @$data;
+
+    print $query->header('application/json');
+    print encode_json({ entries => \@data, error => undef });
+}
+else {
+    die "Access not permitted\n";
+}