X-Git-Url: http://dxcluster.net/gitweb/gitweb.cgi?a=blobdiff_plain;f=perl%2FDXProt.pm;h=0d137e529bd3710f7dd391eadf9ee126f4fe0083;hb=f0ac8322367c66080b6dbb74da4de72dae126dc3;hp=fbc63bc09ed2df2a5c2304ecf627ca86a0851be7;hpb=4ed8f51684c6f2183ffd5e46f1fc69dc4f52ebd9;p=spider.git diff --git a/perl/DXProt.pm b/perl/DXProt.pm index fbc63bc0..0d137e52 100644 --- a/perl/DXProt.pm +++ b/perl/DXProt.pm @@ -30,14 +30,17 @@ use Carp; use strict; use vars qw($me $pc11_max_age $pc11_dup_age $pc23_dup_age %spotdup %wwvdup $last_hour %pings %rcmds - %nodehops @baddx $baddxfn); + %nodehops @baddx $baddxfn $pc12_dup_age + %anndup $allowzero); $me = undef; # the channel id for this cluster $pc11_max_age = 1*3600; # the maximum age for an incoming 'real-time' pc11 $pc11_dup_age = 24*3600; # the maximum time to keep the spot dup list for $pc23_dup_age = 24*3600; # the maximum time to keep the wwv dup list for +$pc12_dup_age = 24*3600; # the maximum time to keep the ann dup list for %spotdup = (); # the pc11 and 26 dup hash -%wwvdup = (); # the pc23 and 27 dup hash +%wwvdup = (); # the pc23 and 27 dup hash +%anndup = (); # the PC12 dup hash $last_hour = time; # last time I did an hourly periodic update %pings = (); # outstanding ping requests outbound %rcmds = (); # outstanding rcmd requests outbound @@ -113,8 +116,9 @@ sub start $self->{wwvfilter} = Filter::read_in('wwv', $call); $self->{annfilter} = Filter::read_in('ann', $call); - # set unbuffered + # set unbuffered and no echo $self->send_now('B',"0"); + $self->send_now('E',"0"); # send initialisation string if (!$self->{outbound}) { @@ -174,6 +178,14 @@ sub normal } if ($pcno == 11 || $pcno == 26) { # dx spot + + # route 'foreign' pc26s + if ($pcno == 26) { + if ($field[7] ne $main::mycall) { + route($field[7], $line); + return; + } + } # if this is a 'nodx' node then ignore it last SWITCH if grep $field[7] =~ /^$_/, @DXProt::nodx_node; @@ -230,39 +242,26 @@ sub normal return if $pcno == 26; # send out the filtered spots - my @dxchan = get_all_ak1a(); - my $dxchan; - - # send it if it isn't the except list and isn't isolated and still has a hop count - # taking into account filtering and so on - foreach $dxchan (@dxchan) { - next if $dxchan == $self; - my $routeit; - my ($filter, $hops) = Filter::it($dxchan->{spotfilter}, @spot, $self->{call} ) if $dxchan->{spotfilter}; - if ($hops) { - $routeit = $line; - $routeit =~ s/\^H\d+\^\~$/\^H$hops\^\~/; - } else { - $routeit = adjust_hops($dxchan, $line); # adjust its hop count by node name - next unless $routeit; - } - if ($filter) { - $dxchan->send($routeit) if $routeit; - } else { - $dxchan->send($routeit) unless $dxchan->{isolate} || $self->{isolate}; - } - } - - # send orf to the users - if (@spot) { - my $buf = Spot::formatb($field[1], $field[2], $d, $text, $spotter); - broadcast_users("$buf\a\a", 'dx', $spot[0]); - } - + send_dx_spot($self, $line, @spot) if @spot; return; } if ($pcno == 12) { # announces + # announce duplicate checking + my $text = uc unpad($field[3]); + my $dupkey = $field[1].$field[2].$text.$field[4].$field[6]; + if ($anndup{$dupkey}) { + dbg('chan', "Duplicate Announce ignored\n"); + return; + } + $anndup{$dupkey} = $main::systime; + + # global ann filtering + my ($filter, $hops) = Filter::it($self->{annfilter}, @field[1..6], $self->{call} ) if $self->{annfilter}; + if ($self->{annfilter} && !$filter) { + dbg('chan', "Rejected by filter"); + return; + } if ($field[2] eq '*' || $field[2] eq $main::mycall) { @@ -314,8 +313,17 @@ sub normal if ($pcno == 16) { # add a user my $node = DXCluster->get_exact($field[1]); - last SWITCH if !$node; # ignore if havn't seen a PC19 for this one yet - last SWITCH unless $node->isa('DXNode'); + return unless $node; # ignore if havn't seen a PC19 for this one yet + return unless $node->isa('DXNode'); + if ($node->dxchan != $self) { + dbg('chan', "LOOP: $field[1] came in on wrong channel"); + return; + } + my $dxchan; + if (($dxchan = DXChannel->get($field[1])) && $dxchan != $self) { + dbg('chan', "LOOP: $field[1] connected locally"); + return; + } my $i; @@ -335,7 +343,7 @@ sub normal $user = DXUser->new($call) if !$user; $user->homenode($node->call) if !$user->homenode; $user->node($node->call); - $user->lastin($main::systime); + $user->lastin($main::systime) unless DXChannel->get($call); $user->put; } @@ -345,33 +353,70 @@ sub normal } if ($pcno == 17) { # remove a user - + my $node = DXCluster->get_exact($field[2]); + return unless $node; + return unless $node->isa('DXNode'); + if ($node->dxchan != $self) { + dbg('chan', "LOOP: $field[2] came in on wrong channel"); + return; + } + my $dxchan; + if (($dxchan = DXChannel->get($field[2])) && $dxchan != $self) { + dbg('chan', "LOOP: $field[2] connected locally"); + return; + } my $ref = DXCluster->get_exact($field[1]); $ref->del() if $ref; last SWITCH; } if ($pcno == 18) { # link request + $self->state('init'); + + # first clear out any nodes on this dxchannel + my @gonenodes = map { $_->dxchan == $self ? $_ : () } DXNode::get_all(); + foreach my $node (@gonenodes) { + next if $node->dxchan == $DXProt::me; + broadcast_ak1a(pc21($node->call, 'Gone, re-init') , $self) unless $self->{isolate}; + $node->del(); + } $self->send_local_config(); $self->send(pc20()); - $self->state('init'); return; # we don't pass these on } if ($pcno == 19) { # incoming cluster list my $i; + my $newline = "PC19^"; for ($i = 1; $i < $#field-1; $i += 4) { my $here = $field[$i]; my $call = uc $field[$i+1]; - my $confmode = $field[$i+2] eq '*'; + my $confmode = $field[$i+2]; my $ver = $field[$i+3]; + + $ver = 5400 if !$ver && $allowzero; # now check the call over - next if DXCluster->get_exact($call); # we already have this + my $node = DXCluster->get_exact($call); + if ($node) { + my $dxchan; + if (($dxchan = DXChannel->get($call)) && $dxchan != $self) { + dbg('chan', "LOOP: $call connected locally"); + } + if ($node->dxchan != $self) { + dbg('chan', "LOOP: $call come in on wrong channel"); + next; + } + dbg('chan', "already have $call"); + next; + } # check for sane parameters next if $ver < 5000; # only works with version 5 software next if length $call < 3; # min 3 letter callsigns + + # add it to the nodes table and outgoing line + $newline .= "$here^$call^$confmode^$ver^"; DXNode->new($self, $call, $confmode, $here, $ver); # unbusy and stop and outgoing mail (ie if somehow we receive another PC19 without a disconnect) @@ -388,12 +433,17 @@ sub normal $user->homenode($call); $user->node($call); } - $user->lastin($main::systime); + $user->lastin($main::systime) unless DXChannel->get($call); $user->put; } # queue up any messages DXMsg::queue_msg(0) if $self->state eq 'normal'; + return if $newline eq "PC19^"; + + # add hop count + $newline .= get_hops(19) . "^"; + $line = $newline; last SWITCH; } @@ -411,8 +461,22 @@ sub normal if ($pcno == 21) { # delete a cluster from the list my $call = uc $field[1]; if ($call ne $main::mycall) { # don't allow malicious buggers to disconnect me! - my $ref = DXCluster->get_exact($call); - $ref->del() if $ref; + my $node = DXCluster->get_exact($call); + if ($node) { + if ($node->dxchan != $self) { + dbg('chan', "LOOP: $call come in on wrong channel"); + return; + } + my $dxchan; + if (($dxchan = DXChannel->get($call)) && $dxchan != $self) { + dbg('chan', "LOOP: $call connected locally"); + return; + } + $node->del(); + } else { + dbg('chan', "$call not in table, dropped"); + return; + } } last SWITCH; } @@ -426,6 +490,15 @@ sub normal } if ($pcno == 23 || $pcno == 27) { # WWV info + + # route 'foreign' pc27s + if ($pcno == 27) { + if ($field[8] ne $main::mycall) { + route($field[8], $line); + return; + } + } + # do some de-duping my $d = cltounix($field[1], sprintf("%02d18Z", $field[2])); my $sfi = unpad($field[3]); @@ -443,11 +516,11 @@ sub normal $wwvdup{$dupkey} = $d; $field[6] =~ s/-\d+$//o; # remove spotter's ssid - my $wwv = Geomag::update($d, $field[2], $sfi, $k, $i, @field[6..$#field]); + my $wwv = Geomag::update($d, $field[2], $sfi, $k, $i, @field[6..8]); my $r; eval { - $r = Local::wwv($self, $field[1], $field[2], $sfi, $k, $i, @field[6..$#field]); + $r = Local::wwv($self, $field[1], $field[2], $sfi, $k, $i, @field[6..8]); }; # dbg('local', "Local::wwv2 error $@") if $@; return if $r; @@ -455,9 +528,9 @@ sub normal # DON'T be silly and send on PC27s! return if $pcno == 27; - # broadcast to the eager users - broadcast_users("WWV de $field[7] <$field[2]>: SFI=$sfi, A=$k, K=$i, $field[6]", 'wwv', $wwv ); - last SWITCH; + # broadcast to the eager world + send_wwv_spot($self, $line, $d, $field[2], $sfi, $k, $i, @field[6..8]); + return; } if ($pcno == 24) { # set here status @@ -468,8 +541,12 @@ sub normal } if ($pcno == 25) { # merge request - unless ($field[1] eq $main::mycall) { - dbg('chan', "merge request to $field[1] from $field[2] ignored"); + if ($field[1] ne $main::mycall) { + route($field[1], $line); + return; + } + if ($field[2] eq $main::mycall) { + dbg('chan', "Trying to merge to myself, ignored"); return; } @@ -480,7 +557,7 @@ sub normal my @in = reverse Spot::search(1, undef, undef, 0, $field[3]-1); my $in; foreach $in (@in) { - $self->send(pc26(@{$in}[0..4], $in->[7])); + $self->send(pc26(@{$in}[0..4], $field[2])); } } @@ -489,34 +566,44 @@ sub normal my @in = reverse Geomag::search(0, $field[4], time, 1); my $in; foreach $in (@in) { - $self->send(pc27(@{$in})); + $self->send(pc27(@{$in}[0..5], $field[2])); } } return; } - + if (($pcno >= 28 && $pcno <= 33) || $pcno == 40 || $pcno == 42 || $pcno == 49) { # mail/file handling - DXMsg::process($self, $line); + if ($field[1] eq $main::mycall) { + DXMsg::process($self, $line); + } else { + route($field[1], $line); + } return; } if ($pcno == 34 || $pcno == 36) { # remote commands (incoming) if ($field[1] eq $main::mycall) { my $ref = DXUser->get_current($field[2]); + my $cref = DXCluster->get($field[2]); Log('rcmd', 'in', $ref->{priv}, $field[2], $field[3]); - unless ($field[3] =~ /rcmd/i) { # not allowed to relay RCMDS! + unless ($field[3] =~ /rcmd/i || !$cref || !$ref || $cref->mynode->call ne $ref->homenode) { # not allowed to relay RCMDS! if ($ref->{priv}) { # you have to have SOME privilege, the commands have further filtering $self->{remotecmd} = 1; # for the benefit of any command that needs to know + my $oldpriv = $self->{priv}; + $self->{priv} = $ref->{priv}; # assume the user's privilege level my @in = (DXCommandmode::run_cmd($self, $field[3])); + $self->{priv} = $oldpriv; for (@in) { s/\s*$//og; $self->send(pc35($main::mycall, $field[2], "$main::mycall:$_")); Log('rcmd', 'out', $field[2], $_); } delete $self->{remotecmd}; + } else { + $self->send(pc35($main::mycall, $field[2], "$main::mycall:sorry...!")); } } else { - $self->send(pc35($main::mycall, $field[2], "$main::mycall:Tut tut tut...!")); + $self->send(pc35($main::mycall, $field[2], "$main::mycall:your attempt is logged, Tut tut tut...!")); } } else { route($field[1], $line); @@ -538,10 +625,8 @@ sub normal return; } - if ($pcno == 37) { - last SWITCH; - } - + # for pc 37 see 44 onwards + if ($pcno == 38) { # node connected list from neighbour return; } @@ -578,25 +663,22 @@ sub normal if ($pcno == 43) { last SWITCH; } - if ($pcno == 44) { - last SWITCH; - } - if ($pcno == 45) { - last SWITCH; - } - if ($pcno == 46) { - last SWITCH; - } - if ($pcno == 47) { - last SWITCH; - } - if ($pcno == 48) { - last SWITCH; + if ($pcno == 37 || $pcno == 44 || $pcno == 45 || $pcno == 46 || $pcno == 47 || $pcno == 49) { + if ($field[1] eq $main::mycall) { + ; + } else { + route($field[1], $line); + } + return; } if ($pcno == 50) { # keep alive/user list - my $ref = DXCluster->get_exact($field[1]); - $ref->update_users($field[2]) if $ref; + my $node = DXCluster->get_exact($field[1]); + if ($node) { + return unless $node->isa('DXNode'); + return unless $node->dxchan == $self; + $node->update_users($field[2]); + } last SWITCH; } @@ -671,6 +753,10 @@ sub process while (($key, $val) = each %wwvdup) { delete $wwvdup{$key} if $val < $cutoff; } + $cutoff = $main::systime - $pc12_dup_age; + while (($key, $val) = each %anndup) { + delete $anndup{$key} if $val < $cutoff; + } $last_hour = $main::systime; } } @@ -702,7 +788,7 @@ sub finish delete $pings{$call}; # now broadcast to all other ak1a nodes that I have gone - broadcast_ak1a(pc21($call, 'Gone.'), $self); + broadcast_ak1a(pc21($call, 'Gone.'), $self) unless $self->{isolate}; Log('DXProt', $call . " Disconnected"); $ref->del() if $ref; @@ -711,6 +797,83 @@ sub finish # # some active measures # +sub send_dx_spot +{ + my $self = shift; + my $line = shift; + my @dxchan = DXChannel->get_all(); + my $dxchan; + + # send it if it isn't the except list and isn't isolated and still has a hop count + # taking into account filtering and so on + foreach $dxchan (@dxchan) { + my $routeit; + my ($filter, $hops) = Filter::it($dxchan->{spotfilter}, @_, $self->{call} ) if $dxchan->{spotfilter}; + if ($dxchan->is_ak1a) { + next if $dxchan == $self; + if ($hops) { + $routeit = $line; + $routeit =~ s/\^H\d+\^\~$/\^H$hops\^\~/; + } else { + $routeit = adjust_hops($dxchan, $line); # adjust its hop count by node name + next unless $routeit; + } + if ($filter) { + $dxchan->send($routeit) if $routeit; + } else { + $dxchan->send($routeit) unless $dxchan->{isolate} || $self->{isolate}; + + } + } elsif ($dxchan->is_user && $dxchan->{dx}) { + my $buf = Spot::formatb($_[0], $_[1], $_[2], $_[3], $_[4]); + $buf .= "\a\a" if $dxchan->{beep}; + if ($dxchan->{state} eq 'prompt' || $dxchan->{state} eq 'convers') { + $dxchan->send($buf) if !$hops || ($hops && $filter); + } else { + $dxchan->delay($buf) if !$hops || ($hops && $filter); + } + } + } +} + +sub send_wwv_spot +{ + my $self = shift; + my $line = shift; + my @dxchan = DXChannel->get_all(); + my $dxchan; + + # send it if it isn't the except list and isn't isolated and still has a hop count + # taking into account filtering and so on + foreach $dxchan (@dxchan) { + my $routeit; + my ($filter, $hops) = Filter::it($dxchan->{wwvfilter}, @_, $self->{call} ) if $dxchan->{wwvfilter}; + if ($dxchan->is_ak1a) { + next if $dxchan == $self; + if ($hops) { + $routeit = $line; + $routeit =~ s/\^H\d+\^\~$/\^H$hops\^\~/; + } else { + $routeit = adjust_hops($dxchan, $line); # adjust its hop count by node name + next unless $routeit; + } + if ($filter) { + $dxchan->send($routeit) if $routeit; + } else { + $dxchan->send($routeit) unless $dxchan->{isolate} || $self->{isolate}; + + } + } elsif ($dxchan->is_user && $dxchan->{wwv}) { + my $buf = "WWV de $_[6] <$_[1]>: SFI=$_[2], A=$_[3], K=$_[4], $_[5]"; + $buf .= "\a\a" if $dxchan->{beep}; + if ($dxchan->{state} eq 'prompt' || $dxchan->{state} eq 'convers') { + $dxchan->send($buf) if !$hops || ($hops && $filter); + } else { + $dxchan->delay($buf) if !$hops || ($hops && $filter); + } + } + } +} sub send_local_config { @@ -728,9 +891,10 @@ sub send_local_config # and are not themselves isolated, this to make sure that isolated nodes # don't appear outside of this node @nodes = DXNode::get_all(); - @nodes = grep { $_->dxchan != $self } @nodes; @nodes = grep { $_->{call} ne $main::mycall } @nodes; - @localnodes = grep { $_->dxchan->{call} eq $_->{call} && !$_->dxchan->{isolate} } @nodes if @nodes; + @nodes = grep { $_->dxchan != $self } @nodes if @nodes; + @nodes = grep { !$_->dxchan->{isolate} } @nodes if @nodes; + @localnodes = grep { $_->dxchan->{call} eq $_->{call} } @nodes if @nodes; unshift @localnodes, DXCluster->get_exact($main::mycall); @remotenodes = grep { $_->dxchan->{call} ne $_->{call} } @nodes if @nodes; } @@ -773,7 +937,8 @@ sub route } } -# broadcast a message to all clusters [except those mentioned after buffer] +# broadcast a message to all clusters taking into account isolation +# [except those mentioned after buffer] sub broadcast_ak1a { my $s = shift; # the line to be rebroadcast @@ -789,6 +954,23 @@ sub broadcast_ak1a } } +# broadcast a message to all clusters ignoring isolation +# [except those mentioned after buffer] +sub broadcast_all_ak1a +{ + my $s = shift; # the line to be rebroadcast + my @except = @_; # to all channels EXCEPT these (dxchannel refs) + my @dxchan = get_all_ak1a(); + my $dxchan; + + # send it if it isn't the except list and isn't isolated and still has a hop count + foreach $dxchan (@dxchan) { + next if grep $dxchan == $_, @except; + my $routeit = adjust_hops($dxchan, $s); # adjust its hop count by node name + $dxchan->send($routeit); + } +} + # broadcast to all users # storing the spot or whatever until it is in a state to receive it sub broadcast_users @@ -882,7 +1064,7 @@ sub get_all_user_calls sub get_hops { - my ($pcno) = @_; + my $pcno = shift; my $hops = $DXProt::hopcount{$pcno}; $hops = $DXProt::def_hopcount if !$hops; return "H$hops";