summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xclient.pl151
-rwxr-xr-xserver.cgi128
2 files changed, 279 insertions, 0 deletions
diff --git a/client.pl b/client.pl
new file mode 100755
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
index 0000000..f52f3d9
--- /dev/null
+++ b/server.cgi
@@ -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";
+}