require 'database.pl';
if ("$ARGV[1]" eq '--cgi') {
+ open DEBUG,"/dev/null" or die $!;
require 'cgi-lib.pl';
&ReadParse;
} else {
- foreach $x (@ARGV) {
- $x =~ s/^(\w+)\=//;
- $in{$1}= $x;
+ open DEBUG,">&STDERR" or die $!;
+ foreach $x (@ARGV[1..$#ARGV]) {
+ $x =~ m/^(\w+)\=/ or die "$x ?";
+ $in{$1}= $';
+ print DEBUG "$1 -> \"$'\"\n";
}
}
-@areas= split(/ /,'10.0.0.0/8 172.16.0.0/12 192.168.0.0/16');
-$rec_area= '172.16.0.0/12';
+@area_networks= qw(0a000000 ac100000 c0a80000);
+@area_prefixes= qw(8 12 16);
+$rec_areai= 1;
-$current_area= $rec_area;
-$view_area= '';
-$viewoverlap= '';
+$current_areai= $rec_areai;
+$list_areai= -1;
+$listoverlap= '';
$intro= 0;
+$error= 0;
$registernew= 0;
$pick= 0;
$details= 0;
$fulldetails= 0;
-$created= 0;
-$updated= 0;
+$justcreated= 0;
+$justupdated= 0;
$picked= 0;
-$viewingall= 0;
-$view= 1;
+$listingall= 0;
+$passwordsent= 0;
+$list= 0;
$notfound= 0;
$id= '';
$name= '';
$email= '';
-$emailhidechecked= 0;
+$emailhidechecked= '';
$hiddenemail= 0;
-$viewingarea= length $view_area;
-$viewingoverlap= length $viewoverlap;
-
-if ($in{'dbid'}) {
- database();
- $id= $in{'dbid'};
- if (!exists $db{$id}) {
- $notfound= 1;
- $view= 0;
- } else {
- $details= 1;
- getinfo();
- }
+
+$listingarea= $list_areai>=0;
+$listingoverlap= length $listoverlap;
+
+open RAND,"/dev/urandom" or die $!;
+defined($now= time) or die $!;
+
+if (length $in{'register'}) {
+
+ lock_database();
+ read_database();
+
+ $id= randnybs(16);
+ $generation= 0;
+ $db{$id}= $ent= { };
+ $ent->{'created'}= $now;
+ $ent->{'changed'}= 0;
+ $email= $in{'email'};
+ set_entry();
+
+ $justcreated= 1;
+ show_entry();
+ finish();
+
+} elsif (length $in{'mailpasswd'}) {
+
+ read_database();
+ get_entry();
+ send_password();
+
+ $passwordsent= 1;
+ finish();
+
+} elsif (length $in{'view'}) {
+
+ read_database();
+ get_entry();
+ check_password();
+
+ $fulldetails= 1;
+ show_entry();
+ finish();
+
+} elsif (length $in{'update'}) {
+
+ lock_database();
+ get_entry();
+ check_password();
+ check_generation();
+
+ $ent->{'changed'}= $now;
+ $email= length($in{'email'}) ? $in{'email'} : $db{$id}->{'email'};
+ set_entry();
+
+ $justupdated= 1;
+ show_entry();
+ finish();
+
+} elsif (length $in{'delete'}) {
+
+ lock_database();
+ get_entry();
+ check_password();
+ check_generation();
+
+ delete $db{$id};
+ write_database();
+
+ $deleted= 1;
+ finish();
+
+} elsif (length $in{'pick'}) {
+
+ pick_net();
+ $picked= 1;
+ $pick= 1;
+ $list= 1;
+ finish();
+
+} elsif (length $in{'list'}) {
+
+ read_database();
+ list_database();
+ $list= 1;
+ finish();
+
+} elsif (length $in{'id'}) {
+
+ read_database();
+ get_entry();
+ $details= 1;
+ show_entry();
+ $list= 1;
+ $pick= 1;
+ finish();
+
} else {
+
$intro= 1;
- $registernew= 1;
+ $list= 1;
$pick= 1;
+ $registernew= 1;
+ finish();
+
+}
+
+sub set_entry () {
+ my $v, $b, @b, $val, $mask;
+ $name= $in{'name'};
+ foreach $v (qw(name email)) {
+ length $$v or finish_error("no$v");
+ finish_error("badchar") unless $$v =~ m/^[ -\176\240\376]+$/; $$v= $&;
+ }
+ $hiddenemail= !!length $in{'hiddenemail'};
+ $net= $in{'net'};
+print DEBUG "got $net\n";
+ $net =~ s,/(\d+)$,, or finish_error("badnet");
+print DEBUG "prefix $1\n";
+ $prefix= $1+0; ($prefix >= 0 && $prefix <= 32) or finish_error("badnet");
+print DEBUG "prefix $1 $net\n";
+ $network= ''; @b= split(/\./,$net);
+ @b<=4 or finish_error("badnet");
+print DEBUG "big enough\n";
+ @b*8 >= $prefix or finish_error("badnet");
+ while (@b<4) { push @b,0; }
+print DEBUG "@b\n";
+ foreach $b (@b) {
+ $b>=0 && $b<=255 or finish_error("badnet");
+ $network .= sprintf("%02x",$b);
+ }
+print DEBUG "$network\n";
+ ($val,$mask) = net_valuemask($network,$prefix);
+printf DEBUG "%08x %08x %08x\n", $val,$mask,~$mask;
+ !($val & ~$mask) or finish_error("badnet");
+print DEBUG "ok\n";
+ $current_areai= find_areai($network,$prefix);
+print DEBUG "$current_areai\n";
+ $current_areai>=0 or finish_error("wrongnet");
+print DEBUG "ok\n";
+
+ foreach $k (qw(generation network prefix name email hiddenemail)) {
+ $ent->{$k}= $$k;
+ }
+
+ write_database();
}
-open X, "template.html" or die $!;
-@x= <X>;
-close X or die $!;
+sub find_areai ($$) {
+ my ($network,$prefix) = @_;
+ my $i;
+ for ($i=0; $i<@area_networks; $i++) {
+ next unless net_subset($network,$prefix, $area_networks[$i],$area_prefixes[$i]);
+ return $i;
+ }
+ return -1;
+}
-$x[$#x] eq "\@\@\@eof:\@\@\@\n" or die $!;
-$#x--;
+sub net_subset ($$$$) {
+ my ($smln,$smlp, $bign,$bigp) = @_;
+ return 0 unless $smlp >= $bigp;
+ ($bigv,$bigm) = net_valuemask($bign,$bigp);
+ ($smlv,$smlm) = net_valuemask($bign,$bigp);
+ return 0 unless ($smlv & $bigm) == $bigv;
+ return 1;
+}
-$cl= 0;
-$level= -1;
-process(1);
+sub net_valuemask($$) {
+ my ($network,$prefix) = @_;
+ return (hex($network), (0xffffffff << (32-$prefix)));
+}
-close STDOUT or die $!;
+sub get_entry () {
+ length $in{'id'} or die;
+ $id= $in{'id'};
+ exists $db{$id} or finish_error('notfound');
+ $ent= $db{$id};
+}
+
+sub show_entry () {
+ my $k, $dk;
+ foreach $k (@db_fields) {
+ $$k= $ent->{$k};
+ }
+ foreach $k (qw(created changed)) {
+ $dk= "date$k";
+ $$dk= gmtime($$k)." GMT";
+ }
+ $net= display_net($network,$prefix);
+}
+
+sub list_database () {
+ my $t, $v, $k, $x;
+ $listing= '';
+ foreach $t (qw(all area overlap)) {
+ $listing= $t if length $in{"list$t"};
+ }
+ die unless $listing;
+ $v= "listing$listing"; $$v= 1;
+ &{"dblist_prep_$listing"}();
+ @kl= ();
+print DEBUG "ldb 1 @kl\n";
+ foreach $k (keys %db) {
+print DEBUG "ldb q $k\n";
+ next unless &{"dblist_cond_$listing"}($db{$k});
+ push @kl,$k;
+ }
+print DEBUG "ldb 2 @kl\n";
+ @kl= sort {
+ $x= $db{$a}->{'network'} cmp $db{$b}->{'network'}; return $x if $x;
+ $x= $db{$a}->{'prefix'} <=> $db{$b}->{'prefix'}; return $x if $x;
+ return -1 if $a eq 'picked' && $b ne 'picked';
+ return +1 if $b eq 'picked' && $a ne 'picked';;
+ $x= $db{$a}->{'name'} cmp $db{$b}->{'name'}; return $x if $x;
+ return $a cmp $b;
+ } @kl;
+print DEBUG "ldb 3 @kl\n";
+ $listingnonefound= @kl ? 0 : 1;
+print DEBUG "ldb end $listingnonefound\n";
+}
+
+sub check_generation () {
+ $ent->{'generation'} eq $ent->{'generation'} or finish_error('generation');
+}
+
+sub finish_error ($) {
+ my ($type) = @_;
+ my $t, $esel;
+ foreach $t (qw(noemail noname badnet wrongnet notfound generation badchar)) {
+ $esel= "error_$t";
+ $$esel= 0;
+ }
+ my $esel= "error_$type";
+ $$esel= 1;
+ $error= 1;
+ finish();
+}
+
+sub finish () {
+ open X, "template.html" or die $!;
+ @x= <X>;
+ close X or die $!;
+
+ $x[$#x] eq "\@\@\@eof:\@\@\@\n" or die $!;
+ $#x--;
+
+ $cl= 0;
+ $level= -1;
+ process(1);
+
+ close STDOUT or die $!;
+ exit 0;
+}
+
+sub display_net ($$) {
+ my ($network,$prefix) = @_;
+ return join('.', unpack("C4",pack("H8",$network)))."/".$prefix;
+}
+
+sub randnybs ($) {
+ my ($nybbles) = @_;
+ my $v;
+ my $bytes= $nybbles/2;
+ read(RAND,$v,$bytes) == $bytes or die $!;
+ return scalar unpack("H$nybbles",$v)
+}
sub out ($) {
print $_[0],"\n" or die $!;
return $$vn;
}
+sub dblist_prep_all { }
+sub dblist_cond_all { 1; }
+
sub foreach_start_area { $area_i=0; }
-sub foreach_cond_area { return $area_i < @areas; }
+sub foreach_cond_area { return $area_i < @area_networks; }
sub foreach_incr_area { $area_i++; }
sub foreach_setvars_area {
- $area= $areas[$area_i];
- $area_recommended= $area eq $rec_area;
- $area_pickchecked= $area eq $current_area ? 'checked' : '';
- $area_viewing= $area eq $viewing_area;
- out("<!-- setvars_area @areas $area_i $area -->");
+ $area_network= $area_networks[$area_i];
+ $area= display_net($area_network,$area_prefixes[$area_i]);
+ $area_recommended= $area_i==$rec_areai;
+ $area_pickchecked= $area_i==$current_areai ? 'checked' : '';
+ $area_listing= $area_i==$list_areai;
+# out("<!-- setvars_area @area_networks $area_i $area -->");
+}
+
+%saniarray= ('<','lt', '>','gt', '&','amp', '"','quot');
+sub html_sani {
+ local ($in) = @_;
+ local ($out);
+ while ($in =~ m/[<>&"]/) {
+ $out.= $`. '&'. $saniarray{$&}. ';';
+ $in=$';
+ }
+ $out.= $in;
+ $out;
}
-sub foreach_start_db { }
-sub foreach_cond_db { 0; }
-sub foreach_setvars_db { }
-sub foreach_incr_db { }
+sub foreach_start_db { $db_i=0; }
+sub foreach_cond_db { return $db_i < @kl; }
+sub foreach_incr_db { $db_i++; }
+sub foreach_setvars_db {
+ my $k, $ent;
+ $k= $kl[$db_i];
+ $ent= $db{$k};
+ if ($k eq 'picked') {
+ unset $db_id;
+ } else {
+ $db_id= $k;
+ }
+ $db_net= display_net($ent->{'network'}, $ent->{'prefix'});
+ $db_name= html_sani($ent->{'name'});
+ $db_email= $ent->{'hiddenemail'} ? "" : html_sani($ent->{'email'});
+}
@@@net@@@ picked
@@@endif:@@@
+@@@if:passwordsent@@@
+password sent for @@@name@@@
+@@@endif:@@@
+
@@@if:details@@@
details of @@@name@@@
@@@endif:@@@
-@@@if:created@@@
+@@@if:justcreated@@@
registered @@@name@@@
@@@endif:@@@
-@@@if:updated@@@
+@@@if:justupdated@@@
updated @@@name@@@
@@@endif:@@@
-@@@if:viewingall@@@
+@@@if:listingall@@@
entire database
@@@endif:@@@
-@@@if:viewingarea@@@
-subset for @@@viewarea@@@
+@@@if:listingarea@@@
+subset for @@@listarea@@@
@@@endif:@@@
-@@@if:viewingoverlap@@@
-overlapping with @@@viewoverlap@@@
+@@@if:listingoverlap@@@
+overlapping with @@@listoverlap@@@
@@@endif:@@@
-@@@if:notfound@@@
+@@@if:error@@@
+@@@if:error_notfound@@@
entry not found
@@@endif:@@@
+@@@ifnot:error_notfound@@@
+error
+@@@endif:@@@
+@@@endif:@@@
</title>
@@@endif:@@@
Database entry details: @@@name@@@
@@@endif:@@@
-@@@if:created@@@
+@@@if:justcreated@@@
@@@name@@@ registered in database
@@@endif:@@@
-@@@if:updated@@@
+@@@if:justupdated@@@
Database entry updated: @@@name@@@
@@@endif:@@@
-@@@if:viewingall@@@
+@@@if:listingall@@@
Entire database listing
@@@endif:@@@
-@@@if:viewingarea@@@
-Database subset listing for @@@viewarea@@@
+@@@if:listingarea@@@
+Database subset listing for @@@listarea@@@
@@@endif:@@@
-@@@if:viewingoverlap@@@
-Database search, entries overlapping with @@@viewoverlap@@@
+@@@if:listingoverlap@@@
+Database search, entries overlapping with @@@listoverlap@@@
@@@endif:@@@
-@@@if:notfound@@@
+@@@if:error@@@
+@@@if:error_notfound@@@
Database entry not found
@@@endif:@@@
+@@@ifnot:error_notfound@@@
+An error has occurred
+@@@endif:@@@
+@@@endif:@@@
</h1>
@@@endif:@@@
+@@@if:error@@@
+
+@@@if:error_badnet@@@
+You entered a syntactically invalid CIDR-format network number.
+<p>
+IPv4 network numbers should be expressed as a dotted quad followed by
+a prefix length. Each of the numbers in the dotted quad must be
+between 0 and 255. The prefix length is separated from the dotted
+quad by a slash, and must be between 0 and 32. None of the bits in
+the dotted quad beyond the prefix length must be set.
+@@@endif:@@@
+
+@@@if:error_wrongnet@@@
+The network number you specified does not lie wholly within the
+BCP5-allocated private-use address ranges.
+@@@endif:@@@
+
+@@@if:error_notfound@@@
+Sorry, the database entry cannot be found. Perhaps the entry has been
+deleted or expired.
+<p>
+If you believe that this has happened to you in error, contact the
+registry administrator, @@@adminemail@@@, quoting identifier @@@id@@@.
+@@@endif:@@@
+
+@@@if:error_generation@@@
+Another modification has been made to the record. Please use the
+`view' option to see the new values, before making any further
+changes.
+@@@endif:@@@
+
+@@@if:error_noemail@@@
+You must provide an email address.
+@@@endif:@@@
+
+@@@if:error_noname@@@
+You must provide a network name for the database.
+@@@endif:@@@
+
+@@@if:error_badchar@@@
+The name or email address you specified contains illegal character(s).
+@@@endif:@@@
+
+@@@endif:@@@
+
@@@if:registernew@@@
<h2>Register a network range you are using</h2>
You may register an existing network range you are using in our
<h2>Entry details</h2>
@@@endif:@@@
-@@@if:created@@@
+@@@if:justcreated@@@
<h2>Entry created</h2>
The database entry has been created, but will only persist for a short
time unless you confirm it. You have beeen sent an email requesting
email.
@@@endif:@@@
-@@@if:updated@@@
+@@@if:justupdated@@@
<h2>Entry updated/confirmed</h2>
The database entry below has been updated/confirmed/renewed.
@@@endif:@@@
-@@@if:registernew|details|fulldetails|created|updated|picked@@@
+@@@if:registernew|details|fulldetails|justcreated|justupdated|picked@@@
<p>
<form method="POST" action="@@@cgi@@@">
<input type=hidden name="net" value="@@@net@@@">@@@net@@@
@@@endif:@@@
@@@ifnot:picked@@@
-<input type=text name="net" size=20>
+<input type=text name="net" value="@@@net@@@" size=20>
@@@endif:@@@
<br>
<input type=text name="email" size="@@@emailboxlen@@@" value="@@@email@@@">
<br>
-<input type=checkbox name="hide" value="yes" @@@emailhidechecked@@@>
+<input type=checkbox name="hiddenemail" value="yes" @@@emailhidechecked@@@>
Hide email address from public database.
@@@if:registernew|picked@@@
<input type=submit name="register" value="Register">
@@@endif:@@@
-@@@if:details|fulldetails|created|updated@@@
+@@@if:details|fulldetails|justcreated|justupdated@@@
<p>
Entry created: @@@datecreated@@@<br>
-Last update: @@@datechanged@@@<br>
-Confirmed/renewed: @@@dateconfirmed@@@
+@@@if:changed@@@
+Last update/confirm: @@@datechanged@@@<br>
+@@@endif:@@@
+@@@ifnot:changed@@@
+Not yet confirmed.
+@@@endif:@@@
+<input type=hidden name="generation" value="@@@generation@@@">
@@@endif:@@@
@@@if:details@@@
-<p>
+<h3>Password request</h3>
Use this to obtain your password. Passwords expire after a while; if
you do not have a recent notification of your password, you should
request a current password: <br>
<input type=submit name="mailpasswd" value="Mail password">
-<p>
@@@endif:@@@
-@@@if:details|created@@@
+@@@if:details|fulldetails|justcreated|justupdated@@@
+<h3>Modify or manage existing entry</h3>
+@@@endif:@@@
+
+@@@if:details|justcreated@@@
Using your password, you can update the details above (if you have
changed them), confirm or renew your entry (to stop it expiring), and
view the full details, including any hidden email address: <br>
-Password: <input type=text name="password" size="30">
-@@@endif:@@@
-
-@@@if:fulldetails|updated@@@
-<input type=hidden name="password" value="@@@password@@@">
-Password: supplied.
@@@endif:@@@
-@@@if:details|created|updated@@@
-<input type=submit name="view" value="View full details."><br>
+@@@if:details|justcreated|justupdated@@@
+<input type=submit name="view" value="View full details.">
@@@endif:@@@
-@@@if:details|fulldetails|created|updated@@@
+@@@if:details|fulldetails|justcreated|justupdated@@@
<input type=submit name="update" value="Update/confirm/renew">
<input type=submit name="delete" value="Delete">
+<br>
+@@@endif:@@@
+
+@@@if:details|justcreated@@@
+Password: <input type=text name="password" size="30">
+@@@endif:@@@
+
+@@@if:fulldetails|justupdated@@@
+<input type=hidden name="password" value="@@@password@@@">
+Password: supplied.
@@@endif:@@@
</form>
@@@endif:@@@
-@@@if:notfound@@@
-Sorry, the database entry cannot be found. Perhaps the entry has been
-deleted or expired.
-<p>
-
-If you believe that this has happened to you in error, contact the
-registry administrator, @@@adminemail@@@, quoting identifier @@@id@@@.
-
-@@@endif:@@@
-
-
-@@@if:view@@@
+@@@if:list@@@
<h2>View database</h2>
<form method="GET" action="@@@cgi@@@">
+<input type=hidden name="list" value="yes">
-@@@ifnot:viewingall@@@
+@@@ifnot:listingall@@@
-<input type=submit name="viewall" value="Entire database">
+<input type=submit name="listall" value="Entire database">
@@@endif:@@@
<h3>Entries allocated from</h3>
+<ul>
@@@foreach:area@@@
-@@@ifnot:area_viewing@@@
-<input type=submit name="viewarea" value="@@@area@@@">
+<li>
+@@@ifnot:area_listing@@@
+<a href="@@@cgi@@@?listarea=@@@area@@@">@@@area@@@</A>
@@@endif:@@@
-@@@if:area_viewing@@@
+@@@if:area_listing@@@
@@@area@@@
@@@endif:@@@
-<br>
@@@endforeach:@@@
+</ul>
@@@endif:@@@
-@@@if:view@@@
+@@@if:list@@@
<h3>Entries overlapping with</h3>
-<input type=text name="with" value="@@@viewoverlap@@@" size="30">
-<input type=submit name="viewoverlap" value="Search">
+<input type=text name="with" value="@@@listoverlap@@@" size="30">
+<input type=submit name="listoverlap" value="Search">
</form>
@@@endif:@@@
-@@@if:viewingall|viewingoverlap|viewingarea@@@
+@@@if:listingall|listingarea|listingoverlap@@@
<h2>Database</h2>
<h3>
-@@@if:viewingall@@@
+@@@if:listingall@@@
Entire database
@@@endif:@@@
-@@@if:viewingoverlap@@@
-Entries which overlap with @@@viewoverlap@@@
+@@@if:listingoverlap@@@
+Entries which overlap with @@@listoverlap@@@
@@@endif:@@@
-@@@if:viewingarea@@@
-Database for entries allocated from @@@viewarea@@@
+@@@if:listingarea@@@
+Database for entries allocated from @@@listarea@@@
@@@endif:@@@
</h3>
@@@endif:@@@
<p>
+@@@ifnot:listingnonefound@@@
+
You can get details about an existing entry by clicking on its address
range; this can also be used to modify it.
<tr><td>Range <td>Name <td>Contact email
</tr><br>
@@@foreach:db@@@
-<tr><td><A href="@@@cgi@@@?dbid=@@@db_id@@@">@@@db_net@@@</A>
+<tr><td><A href="@@@cgi@@@?id=@@@db_id@@@">@@@db_net@@@</A>
<td>@@@db_name@@@
<td>@@@db_email@@@
</tr><br>
@@@endif:@@@
+@@@if:listingnonefound@@@
+There are no matching entries in the database.
+@@@endif:@@@
+
+@@@endif:@@@
+
</body>
</html>
@@@eof:@@@