From 7f5c91142bfc46c9b890e8e82e1f3214407c987b Mon Sep 17 00:00:00 2001 From: Steven Schubiger Date: Sat, 6 Apr 2013 19:20:25 +0200 Subject: Initial commit. --- client.pl | 151 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ server.cgi | 128 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 279 insertions(+) create mode 100755 client.pl create mode 100755 server.cgi 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 . + +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 . + +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"; +} -- cgit v1.2.3