X-Git-Url: http://dxcluster.net/gitweb/gitweb.cgi?a=blobdiff_plain;f=perl%2FDXProt.pm;h=ba958cfad0418bad5deac0c279c5ea9f5c620527;hb=98b0984d0ab735a1ae2d96e36728c7e334cf5fd7;hp=5028e2f93d049fad78f22f958364d7f62b347f7e;hpb=ab10b5e76b152d41273281b30b019e4c82ed22d8;p=spider.git diff --git a/perl/DXProt.pm b/perl/DXProt.pm index 5028e2f9..ba958cfa 100644 --- a/perl/DXProt.pm +++ b/perl/DXProt.pm @@ -199,6 +199,19 @@ sub init { do "$main::data/hop_table.pl" if -e "$main::data/hop_table.pl"; confess $@ if $@; + + my $user = DXUser->get($main::mycall); + $DXProt::myprot_version += $main::version*100; + $main::me = DXProt->SUPER::alloc($main::mycall, 0, $user); + $main::me->{here} = 1; + $main::me->{state} = "indifferent"; + $main::me->{sort} = 'S'; # S for spider + $main::me->{priv} = 9; + $main::me->{metric} = 0; + $main::me->{pingave} = 0; + $main::me->{registered} = 1; + $main::me->{version} = $main::version; + $main::me->{build} = $main::build; } # @@ -212,8 +225,12 @@ sub new # add this node to the table, the values get filled in later my $pkg = shift; my $call = shift; - $main::routeroot->add($call, '5000', Route::here(1)) if $call ne $main::mycall; + my $uref = Route::Node::get($call) || Route::Node->new($call); + $uref->here(1); + $uref->conf(0); + $uref->version(5000); + $main::routeroot->link_node($uref, $self); return $self; } @@ -304,36 +321,17 @@ sub sendinit sub removepc90 { $_[0] =~ s/^PC90\^[-A-Z0-9]+\^\d+\^//; + $_[0] =~ s/^PC91\^[-A-Z0-9]+\^\d+\^[-A-Z0-9]+\^//; } -sub send -{ - my $self = shift; - my $line = shift; - if ($self->user->wantpc90) { - $line = mungepc90($line); - } else { - removepc90($line); - } - $self->SUPER::send($line); -} - -my $pc90msgid = 0; - -sub nextpc90 -{ - $pc90msgid = 0 if $pc90msgid > 9999; - return $pc90msgid++; -} - -sub mungepc90 -{ - unless ($_[0] =~ /^PC90/) { - my $id = nextpc90(); - return "PC90^$main::mycall^$id^" . $_[0]; - } - return $_[0]; -} +#sub send +#{ +# my $self = shift; +# while (@_) { +# my $line = shift; +# $self->SUPER::send($line); +# } +#} # # This is the normal pcxx despatcher @@ -341,6 +339,10 @@ sub mungepc90 sub normal { my ($self, $line) = @_; + + # remove any incoming PC90 frames + removepc90($line); + my @field = split /\^/, $line; return unless @field; @@ -351,8 +353,10 @@ sub normal # process PC frames, this will fail unless the frame starts PCnn my ($pcno) = $field[0] =~ /^PC(\d\d)/; # just get the number - return unless $pcno; - return if $pcno < 10 || $pcno > 99; + unless (defined $pcno && $pcno >= 10 && $pcno <= 99) { + dbg("PCPROT: unknown protocol") if isdbg('chanerr'); + return; + } # check for and dump bad protocol messages my $n = check($pcno, @field); @@ -361,72 +365,10 @@ sub normal return; } - # handle PC90 frames in a special way. - # - # PC90 frames are normal frames that that are wrapped in inside a PC90 - # The extra fields are "originating node" and a sequence number. - # The sequence number is checked against the nodes 'last one' to see if - # it is a duplicate and, if so, is dropped at this stage; before any - # other processing. - # - # This is done here simply for efficiency. Adding another function would - # add more copying and so on. - # - - my $origin = $self->call; - - if ($pcno >= 90) { - $origin = $field[1]; - if ($origin eq $main::mycall) { - dbg("PCPROT: loop dupe") if isdbg('chanerr'); - return; - } - my $seq = $field[2]; - my $node = Route::Node::get($origin); - if ($node) { - if (my $lid = $node->lid) { - my $cmp = $seq >= $lid ? $seq : $seq + 9999; - if ($cmp <= $lid) { - dbg("PCPROT: sequence dupe $seq ($cmp) <= $lid") if isdbg('chanerr'); - return; - } - } - $node->lid($seq); - } - - # do a recheck on the contents of the PC90 - if ($pcno == 90) { - shift @field; - shift @field; - shift @field; - - ($pcno) = $field[0] =~ /^PC(\d\d)/; # just get the number - return unless $pcno; - return if $pcno < 10 || $pcno > 99; - - # check for and dump bad protocol messages - my $n = check($pcno, @field); - if ($n) { - dbg("PCPROT: bad field $n, dumped (" . parray($checklist[$pcno-10]) . ")") if isdbg('chanerr'); - return; - } - } - } - - # local processing 1 - my $pcr; - eval { - $pcr = Local::pcprot($self, $pcno, @field); - }; -# dbg("Local::pcprot error $@") if isdbg('local') if $@; - return if $pcr; - + my $origin = $self->{call}; no strict 'subs'; my $sub = "handle_$pcno"; - # add missing PC90 if not present (for ongoing distribution) - $line = mungepc90($line) if $pcno < 90; - if ($self->can($sub)) { $self->$sub($pcno, $line, $origin, @field); } else { @@ -502,7 +444,7 @@ sub handle_10 if ($ref = Route::get($to)) { $vref = Route::Node::get($via) if $via; $vref = undef unless $vref && grep $to eq $_, $vref->users; - $ref->dxchan->talk($from, $to, $vref ? $via : undef, $_[3], $_[6]); + $ref->bestdxchan->talk($from, $to, $vref ? $via : undef, $_[3], $_[6]); return; } @@ -511,7 +453,7 @@ sub handle_10 $ref = Route::get($from); $vref = $ref = Route::Node::get($_[6]) unless $ref; if ($ref) { - $dxchan = $ref->dxchan; + $dxchan = $ref->bestdxchan; $dxchan->talk($main::mycall, $from, $vref ? $vref->call : undef, $dxchan->msg('talknh', $to) ); } } @@ -630,7 +572,7 @@ sub handle_11 if ($send_opernam && $to && $to ne $main::mycall && $main::systime > $last + $DXUser::lastoperinterval && ($node = Route::Node::get($to)) ) { my $cmd = "forward/opernam $spot[4]"; # send the rcmd but we aren't interested in the replies... - my $dxchan = $node->dxchan; + my $dxchan = $node->bestdxchan; if ($dxchan && $dxchan->is_clx) { route(undef, $to, pc84($main::mycall, $to, $main::mycall, $cmd)); } else { @@ -640,7 +582,7 @@ sub handle_11 $to = $_[7]; $node = Route::Node::get($to); if ($node) { - $dxchan = $node->dxchan; + $dxchan = $node->bestdxchan; if ($dxchan && $dxchan->is_clx) { route(undef, $to, pc84($main::mycall, $to, $main::mycall, $cmd)); } else { @@ -724,7 +666,7 @@ sub handle_12 if ($call) { my $ref = Route::get($call); if ($ref) { - $dxchan = $ref->dxchan; + $dxchan = $ref->bestdxchan; $dxchan->talk($_[1], $call, undef, $_[3], $_[5]) if $dxchan != $self; return; } @@ -746,10 +688,6 @@ sub handle_16 my $line = shift; my $origin = shift; - if (eph_dup($line)) { - dbg("PCPROT: dup PC16 detected") if isdbg('chanerr'); - return; - } # general checks my $dxchan; @@ -761,83 +699,34 @@ sub handle_16 dbg("PCPROT: don't send users to $self->{call}") if isdbg('chanerr'); return; } + # is it me? if ($ncall eq $main::mycall) { dbg("PCPROT: trying to alter config on this node from outside!") if isdbg('chanerr'); return; } - my $parent = Route::Node::get($ncall); - # if there is a parent, proceed, otherwise if there is a latent PC19 in the PC19list, - # fix it up in the routing tables and issue it forth before the PC16 - unless ($parent) { - my $nl = $pc19list{$ncall}; - - if ($nl && @_ > 3) { # 3 because of the hop count! - - # this is a new (remembered) node, now attach it to me if it isn't in filtered - # and we haven't disallowed it - my $user = DXUser->get_current($ncall); - if (!$user) { - $user = DXUser->new($ncall); - $user->sort('A'); - $user->priv(1); # I have relented and defaulted nodes - $user->lockout(1); - $user->homenode($ncall); - $user->node($ncall); - } + # do we believe this call? + unless ($ncall eq $self->{call} || $self->is_believed($ncall)) { + dbg("PCPROT: We don't believe $ncall on $self->{call}"); + return; + } - my $wantpc19 = $user->wantroutepc19; - if ($wantpc19 || !defined $wantpc19) { - my $new = Route->new($ncall); # throw away - if ($self->in_filter_route($new)) { - my @nrout; - for (@$nl) { - $parent = Route::Node::get($_->[0]); - $dxchan = $parent->dxchan if $parent; - if ($dxchan && $dxchan ne $self) { - dbg("PCPROT: PC19 from $self->{call} trying to alter locally connected $ncall, ignored!") if isdbg('chanerr'); - $parent = undef; - } - if ($parent) { - my $r = $parent->add($ncall, $_->[1], $_->[2]); - push @nrout, $r unless @nrout; - } - } - $user->wantroutepc19(1) unless defined $wantpc19; # for now we work on the basis that pc16 = real route - $user->lastin($main::systime) unless DXChannel->get($ncall); - $user->put; - - # route the pc19 - this will cause 'stuttering PC19s' for a while - $self->route_pc19(@nrout) if @nrout ; - $parent = Route::Node::get($ncall); - unless ($parent) { - dbg("PCPROT: lost $ncall after sending PC19 for it?"); - return; - } - } else { - return; - } - delete $pc19list{$ncall}; - } - } else { - dbg("PCPROT: Node $ncall not in config") if isdbg('chanerr'); - return; - } - } else { - - $dxchan = $parent->dxchan; - if ($dxchan && $dxchan ne $self) { - dbg("PCPROT: PC16 from $self->{call} trying to alter locally connected $ncall, ignored!") if isdbg('chanerr'); - return; - } + my $node = Route::Node::get($ncall); + unless ($node) { + dbg("PCPROT: Node $ncall not in config") if isdbg('chanerr'); + return; + } - # input filter if required - return unless $self->in_filter_route($parent); + # dedupe only that which we potentially process + if (eph_dup($line)) { + dbg("PCPROT: dup PC16 detected") if isdbg('chanerr'); + return; } my $i; my @rout; + my @new; for ($i = 2; $i < $#_; $i++) { my ($call, $conf, $here) = $_[$i] =~ /^(\S+) (\S) (\d)/o; next unless $call && $conf && defined $here && is_callsign($call); @@ -854,33 +743,24 @@ sub handle_16 dbg("PCPROT: $call is a node") if isdbg('chanerr'); next; } - - $r = Route::User::get($call); - my $flags = Route::here($here)|Route::conf($conf); - - if ($r) { - my $au = $r->addparent($parent); - if ($r->flags != $flags) { - $r->flags($flags); - $au = $r; - } - push @rout, $r if $au; - } else { - push @rout, $parent->add_user($call, $flags); - } - + + $r = Route::User::get($call) || Route::User->new($call); + $r->here($here); + $r->conf($conf); + $node->lastseen($main::systime); + + push @new, $node->add_user($r); # add this station to the user database, if required $call =~ s/-\d+$//o; # remove ssid for users my $user = DXUser->get_current($call); $user = DXUser->new($call) if !$user; - $user->homenode($parent->call) if !$user->homenode; - $user->node($parent->call); + $user->homenode($node->call) if !$user->homenode; + $user->node($node->call); $user->lastin($main::systime) unless DXChannel->get($call); $user->put; } - - $self->route_pc16($parent, @rout) if @rout; + $self->route_pc16($origin, $line, $node, @new) if @new; } # remove a user @@ -906,34 +786,38 @@ sub handle_17 return; } + # do we believe this call? + unless ($ncall eq $self->{call} || $self->is_believed($ncall)) { + dbg("PCPROT: We don't believe $ncall on $self->{call}"); + return; + } + my $uref = Route::User::get($ucall); unless ($uref) { dbg("PCPROT: Route::User $ucall not in config") if isdbg('chanerr'); - return; } - my $parent = Route::Node::get($ncall); - unless ($parent) { + my $node = Route::Node::get($ncall); + unless ($node) { dbg("PCPROT: Route::Node $ncall not in config") if isdbg('chanerr'); - return; } - $dxchan = $parent->dxchan; - if ($dxchan && $dxchan ne $self) { - dbg("PCPROT: PC17 from $self->{call} trying to alter locally connected $ncall, ignored!") if isdbg('chanerr'); - return; + return unless $node && $uref; + + my @rout; + my @new; + if ($self->in_filter_route($node)) { + + if (eph_dup($line)) { + dbg("PCPROT: dup PC17 detected") if isdbg('chanerr'); + return; + } + push @new, $node->del_user($uref); } - # input filter if required - return unless $self->in_filter_route($parent); - - $parent->del_user($uref); - - if (eph_dup($line)) { - dbg("PCPROT: dup PC17 detected") if isdbg('chanerr'); - return; - } + $self->route_pc17($origin, $line, $node, $uref) if @new; - $self->route_pc17($parent, $uref); + # get rid of orphaned users; + $_->delete for @new; } # link request @@ -961,11 +845,23 @@ sub handle_18 $self->version($_[2] / 100) if $_[2] && $_[2] =~ /^\d+$/; $self->user->version($self->version); } + $self->newroute( $_[1] =~ /NewRoute/ ); # first clear out any nodes on this dxchannel - my $parent = Route::Node::get($self->{call}); - my @rout = $parent->del_nodes; - $self->route_pc21(@rout, $parent) if @rout; + my $node = Route::Node::get($self->{call}) ; + my @rout; + foreach my $n ($node->nodes) { + next if $n eq $main::mycall; + next if $n eq $self->{call}; + my $nref = Route::Node::get($n); + push @rout, $node->remove_route($nref, $self) if $nref; + } + $self->route_pc21($origin, $line, @rout) if @rout; + for (@rout) { + $_->delete; + }; + + # send the new config $self->send_local_config(); $self->send(pc20()); } @@ -988,6 +884,8 @@ sub handle_19 # new routing list my @rout; + + # first get the INTERFACE node my $parent = Route::Node::get($self->{call}); unless ($parent) { dbg("DXPROT: my parent $self->{call} has disappeared"); @@ -995,6 +893,8 @@ sub handle_19 return; } + my @new; + # parse the PC19 for ($i = 1; $i < $#_-1; $i += 4) { my $here = $_[$i]; @@ -1003,21 +903,20 @@ sub handle_19 my $ver = $_[$i+3]; next unless defined $here && defined $conf && is_callsign($call); - eph_del_regex("^PC(?:21\\^$call|17\\^[^\\^]+\\^$call)"); - # check for sane parameters # $ver = 5000 if $ver eq '0000'; next if $ver < 5000; # only works with version 5 software next if length $call < 3; # min 3 letter callsigns next if $call eq $main::mycall; - # check that this PC19 isn't trying to alter the wrong dxchan - my $dxchan = DXChannel->get($call); - if ($dxchan && $dxchan != $self) { - dbg("PCPROT: PC19 from $self->{call} trying to alter wrong locally connected $call, ignored!") if isdbg('chanerr'); + # do we believe this call? + unless ($call eq $self->{call} || $self->is_believed($call)) { + dbg("PCPROT: We don't believe $call on $self->{call}"); next; } + eph_del_regex("^PC(?:21\\^$call|17\\^[^\\^]+\\^$call)"); + # add this station to the user database, if required (don't remove SSID from nodes) my $user = DXUser->get_current($call); if (!$user) { @@ -1028,43 +927,17 @@ sub handle_19 $user->homenode($call); $user->node($call); } + $user->wantroutepc19(1) unless defined $user->wantroutepc19; - my $r = Route::Node::get($call); - my $flags = Route::here($here)|Route::conf($conf); - - # modify the routing table if it is in it, otherwise store it in the pc19list for now - if ($r) { - my $ar; - if ($call ne $parent->call) { - if ($self->in_filter_route($r)) { - $ar = $parent->add($call, $ver, $flags); - push @rout, $ar if $ar; - } else { - next; - } - } - if ($r->version ne $ver || $r->flags != $flags) { - $r->version($ver); - $r->flags($flags); - push @rout, $r unless $ar; - } - } else { + my $r = Route::Node::get($call) || Route::Node->new($call); + $r->here($here); + $r->conf($conf); + $r->version($ver); + $r->lastseen($main::systime); - # if he is directly connected or allowed then add him, otherwise store him up for later - if ($call eq $self->{call} || $user->wantroutepc19) { - my $new = Route->new($call); # throw away - if ($self->in_filter_route($new)) { - my $ar = $parent->add($call, $ver, $flags); - $user->wantroutepc19(1) unless defined $user->wantroutepc19; - push @rout, $ar if $ar; - } else { - next; - } - } else { - $pc19list{$call} = [] unless exists $pc19list{$call}; - my $nl = $pc19list{$call}; - push @{$pc19list{$call}}, [$self->{call}, $ver, $flags] unless grep $_->[0] eq $self->{call}, @$nl; - } + if ($self->in_filter_route($r)) { + push @new, $parent->link_node($r, $self); + push @rout, $r; } # unbusy and stop and outgoing mail (ie if somehow we receive another PC19 without a disconnect) @@ -1075,8 +948,9 @@ sub handle_19 $user->put; } - - $self->route_pc19(@rout) if @rout; + # route out new nodes to legacy nodes + $self->route_pc19($origin, $line, @new) if @new; + $self->route_pc59('A', 0, $self->{call}, @rout) if @rout; } # send local configuration @@ -1101,6 +975,13 @@ sub handle_21 my $origin = shift; my $call = uc $_[1]; + return if $call eq $main::mycall; # don't allow malicious buggers to disconnect me (or ignore loops)! + + unless ($call eq $self->{call} || $self->is_believed($call)) { + dbg("PCPROT: We don't believe $call on $self->{call}"); + return; + } + eph_del_regex("^PC1[679].*$call"); # if I get a PC21 from the same callsign as self then treat it @@ -1110,43 +991,31 @@ sub handle_21 return; } - # check to see if we are in the pc19list, if we are then don't bother with any of - # this routing table manipulation, just remove it from the list and dump it my @rout; - if (my $nl = $pc19list{$call}) { - $pc19list{$call} = [ grep {$_->[0] ne $self->{call}} @$nl ]; - delete $pc19list{$call} unless @{$pc19list{$call}}; - } else { - - my $parent = Route::Node::get($self->{call}); - unless ($parent) { - dbg("DXPROT: my parent $self->{call} has disappeared"); - $self->disconnect; - return; - } - if ($call ne $main::mycall) { # don't allow malicious buggers to disconnect me! - my $node = Route::Node::get($call); - if ($node) { - - my $dxchan = DXChannel->get($call); - if ($dxchan && $dxchan != $self) { - dbg("PCPROT: PC21 from $self->{call} trying to alter locally connected $call, ignored!") if isdbg('chanerr'); - return; - } - - # input filter it - return unless $self->in_filter_route($node); - - # routing objects - push @rout, $node->del($parent); - } - } else { - dbg("PCPROT: I WILL _NOT_ be disconnected!") if isdbg('chanerr'); - return; - } + my @new; + my $parent = Route::Node::get($self->{call}); + unless ($parent) { + dbg("DXPROT: my parent $self->{call} has disappeared"); + $self->disconnect; + return; } + $parent->lastseen; - $self->route_pc21(@rout) if @rout; + my $node = Route::Node::get($call); + if ($node) { + $node->lastseen($main::systime); + + # input filter it + return unless $self->in_filter_route($node); + push @rout, $node; + push @new, $node->link_node($parent, $self); + } + + $self->route_pc21($origin, $line, @new) if @new; + $self->route_pc59('D', 0, $self->{call}, @rout) if @rout; + + # get rid of orphaned nodes; + $_->delete for @new; } @@ -1176,7 +1045,10 @@ sub handle_23 } } - return if $rspfcheck and !$self->rspfcheck(1, $_[8], $_[7]); + # only do a rspf check on PC23 (not 27) + if ($pcno == 23) { + return if $rspfcheck and !$self->rspfcheck(1, $_[8], $_[7]) + } # do some de-duping my $d = cltounix($_[1], sprintf("%02d18Z", $_[2])); @@ -1234,7 +1106,7 @@ sub handle_24 my $ref = $nref || $uref; return unless $self->in_filter_route($ref); - $self->route_pc24($ref, $_[3]); + $self->route_pc24($origin, $line, $ref, $_[3]); } # merge request @@ -1332,7 +1204,13 @@ sub handle_37 my $pcno = shift; my $line = shift; my $origin = shift; - DXDb::process($self, $line); + if ($_[1] eq $main::mycall) { + no strict 'refs'; + my $sub = "DXDb::handle_$pcno"; + &$sub($self, @_); + } else { + $self->route($_[1], $line) unless $self->is_clx; + } } # node connected list from neighbour @@ -1474,7 +1352,7 @@ sub handle_50 # input filter if required return unless $self->in_filter_route($node); - $self->route_pc50($node, $_[2], $_[3]) unless eph_dup($line); + $self->route_pc50($origin, $line, $node, $_[2], $_[3]) unless eph_dup($line); } } @@ -1541,6 +1419,169 @@ sub handle_51 } } +# New style routing handler +sub handle_59 +{ + my $self = shift; + my $pcno = shift; + my $line = shift; + my $origin = shift; + + my ($sort, $hextime, $ncall) = @_[1,2,3]; + if ($ncall eq $main::mycall) { + dbg("PCPROT: ignoring PC59 for me") if isdbg('chan'); + return; + } + + # do this once for filtering with a throwaway routing entry if a new node + my $fnode = Route::Node::get($ncall) || Route::new($ncall); + return unless $self->in_filter_route($fnode); + + return if eph_dup($line); + + # mark myself as NewRoute if I get a PC59 + $self->{newroute} = 1 if $ncall eq $self->{call}; + + # now do it properly for actions + my $node = Route::Node::get($ncall) || Route::Node->new($ncall); + $node->newroute(1); + + # find each of the entries (or create new ones) + my @refs; + for my $ent (@_[4..$#_]) { + next if $ent =~ /^H\d+$/; + + my ($esort, $ehere, $ecall) = unpack "A A A*", $ent; + my $ref; + + next unless $esort && defined $ehere && $ecall; + + # create user, if required + my $user = DXUser->get_current($ecall); + unless ($user) { + $user = DXUser->new($ecall); + $user->sort(); + $user->priv(1); # I have relented and defaulted nodes + $user->lockout(1); + $user->homenode($ncall); + $user->node($ncall); + } + if ($esort eq 'U') { + $ref = Route::User::get($ecall); + unless ($ref) { + # create user, if required + my $user = DXUser->get_current($ecall); + unless ($user) { + $user = DXUser->new($ecall); + $user->sort('U'); + $user->homenode($ncall); + $user->node($ncall); + $user->put; + } + $ref = Route::User->new($ecall, 0); + } + } elsif ($esort eq 'N') { + $ref = Route::Node::get($ecall); + unless ($ref) { + # create user, if required + my $user = DXUser->get_current($ecall); + unless ($user) { + $user = DXUser->new($ecall); + $user->priv(1); # I have relented and defaulted nodes + $user->lockout(1); + $user->sort('A'); + $user->homenode($ncall); + $user->node($ncall); + $user->put; + } + $ref = Route::Node->new($ecall, 0); + } + } else { + dbg("DXPROT: unknown entity type '$esort' on $ecall for node $ncall") if isdbg('chan'); + next; + } + $ref->here($ehere); # might as well set this here + $ref->lastseen($main::systime); + push @refs, $ref; + } + + # if it is a delete, disconnect all the entries mentioned + # from this node (which is a parent in this context). + my @delnode; + my @deluser; + if ($sort eq 'D') { + for my $ref (@refs) { + next if $ref->call eq $ncall; + next if $ref->call eq $main::mycall; + if ($ref->isa('Route::Node')) { + push @delnode, $node->unlink_node($ref, $self); + } elsif ($ref->isa('Route::User')) { + push @deluser, $node->del_user($ref); + } + } + } + + # if it is an add, connect all the entries + my @addnode; + my @adduser; + if ($sort eq 'A') { + for my $ref (@refs) { + next if $ref->call eq $ncall; + next if $ref->call eq $main::mycall; + if ($ref->isa('Route::Node')) { + my $new = $node->link_node($ref, $self); + push @addnode, $new if $new; + } elsif ($ref->isa('Route::User')) { + push @adduser, $node->del_user($ref); + } + } + } + + # if it is a configure, unlink all the nodes and users that + # are not in @refs but are in the node, then add all the + # nodes and users that are @refs but not in the node. + # + if ($sort eq 'C') { + my @dn; + my @du; + my @an; + my @au; + for my $r (map {Route::Node::get($_)} $node->nodes) { + next unless $r; + next if $r->call eq $ncall; + next if $r->call eq $main::mycall; + push @dn, $r unless grep $_->call eq $r->call, @refs; + } + for my $r (map {Route::User::get($_)} $node->users) { + next unless $r; + push @du, $r unless grep $_->call eq $r->call, @refs; + } + for my $r (@refs) { + next unless $r; + next if $r->call eq $ncall; + next if $r->call eq $main::mycall; + if ($r->isa('Route::Node')) { + push @an, $r unless grep $r->call eq $_, $node->nodes; + } elsif ($r->isa('Route::User')) { + push @au, $r unless grep $r->call eq $_, $node->users; + } + } + push @delnode, $node->unlink_node($_, $self) for @dn; + push @deluser, $node->del_user($_) for @du; + push @addnode, $node->link_node($_, $self) for @an; + push @adduser, $node->add_user($_) for @au; + } + + $self->route_pc21($origin, $line, @delnode) if @delnode; + $self->route_pc19($origin, $line, @addnode) if @addnode; + $self->route_pc17($origin, $line, @deluser) if @deluser; + $self->route_pc16($origin, $line, @adduser) if @adduser; + + $self->route_pc59($sort, $hextime, $ncall, @refs) if @refs; + $_->delete for @delnode, @deluser; +} + + # dunno but route it sub handle_75 { @@ -1749,20 +1790,8 @@ sub send_wwv_spot my $line = shift; my @dxchan = DXChannel->get_all(); my $dxchan; - my ($wwv_dxcc, $wwv_itu, $wwv_cq, $org_dxcc, $org_itu, $org_cq) = (0..0); - my @dxcc = Prefix::extract($_[6]); - if (@dxcc > 0) { - $wwv_dxcc = $dxcc[1]->dxcc; - $wwv_itu = $dxcc[1]->itu; - $wwv_cq = $dxcc[1]->cq; - } - @dxcc = Prefix::extract($_[7]); - if (@dxcc > 0) { - $org_dxcc = $dxcc[1]->dxcc; - $org_itu = $dxcc[1]->itu; - $org_cq = $dxcc[1]->cq; - } - + my @dxcc = ((Prefix::cty_data($_[6]))[0..2], (Prefix::cty_data($_[7]))[0..2]); + # 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) { @@ -1771,9 +1800,8 @@ sub send_wwv_spot my $routeit; my ($filter, $hops); - $dxchan->wwv($line, $self->{isolate}, @_, $self->{call}, $wwv_dxcc, $wwv_itu, $wwv_cq, $org_dxcc, $org_itu, $org_cq); + $dxchan->wwv($line, $self->{isolate}, @_, $self->{call}, @dxcc); } - } sub wwv @@ -1796,19 +1824,7 @@ sub send_wcy_spot my $line = shift; my @dxchan = DXChannel->get_all(); my $dxchan; - my ($wcy_dxcc, $wcy_itu, $wcy_cq, $org_dxcc, $org_itu, $org_cq) = (0..0); - my @dxcc = Prefix::extract($_[10]); - if (@dxcc > 0) { - $wcy_dxcc = $dxcc[1]->dxcc; - $wcy_itu = $dxcc[1]->itu; - $wcy_cq = $dxcc[1]->cq; - } - @dxcc = Prefix::extract($_[11]); - if (@dxcc > 0) { - $org_dxcc = $dxcc[1]->dxcc; - $org_itu = $dxcc[1]->itu; - $org_cq = $dxcc[1]->cq; - } + my @dxcc = ((Prefix::cty_data($_[10]))[0..2], (Prefix::cty_data($_[11]))[0..2]); # 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 @@ -1816,7 +1832,7 @@ sub send_wcy_spot next if $dxchan == $main::me; next if $dxchan == $self; - $dxchan->wcy($line, $self->{isolate}, @_, $self->{call}, $wcy_dxcc, $wcy_itu, $wcy_cq, $org_dxcc, $org_itu, $org_cq); + $dxchan->wcy($line, $self->{isolate}, @_, $self->{call}, @dxcc); } } @@ -1860,28 +1876,13 @@ sub send_announce # obtain country codes etc - my ($ann_dxcc, $ann_itu, $ann_cq, $org_dxcc, $org_itu, $org_cq) = (0..0); - my ($ann_state, $org_state) = ("", ""); - my @dxcc = Prefix::extract($_[0]); - if (@dxcc > 0) { - $ann_dxcc = $dxcc[1]->dxcc; - $ann_itu = $dxcc[1]->itu; - $ann_cq = $dxcc[1]->cq; - $ann_state = $dxcc[1]->state; - } - @dxcc = Prefix::extract($_[4]); - if (@dxcc > 0) { - $org_dxcc = $dxcc[1]->dxcc; - $org_itu = $dxcc[1]->itu; - $org_cq = $dxcc[1]->cq; - $org_state = $dxcc[1]->state; - } - + my @a = Prefix::cty_data($_[0]); + my @b = Prefix::cty_data($_[4]); if ($self->{inannfilter}) { my ($filter, $hops) = $self->{inannfilter}->it(@_, $self->{call}, - $ann_dxcc, $ann_itu, $ann_cq, - $org_dxcc, $org_itu, $org_cq, $ann_state, $org_state); + @a[0..2], + @b[0..2], $a[3], $b[3]); unless ($filter) { dbg("PCPROT: Rejected by input announce filter") if isdbg('chanerr'); return; @@ -1900,7 +1901,8 @@ sub send_announce foreach $dxchan (@dxchan) { next if $dxchan == $main::me; next if $dxchan == $self && $self->is_node; - $dxchan->announce($line, $self->{isolate}, $to, $target, $text, @_, $self->{call}, $ann_dxcc, $ann_itu, $ann_cq, $org_dxcc, $org_itu, $org_cq); + $dxchan->announce($line, $self->{isolate}, $to, $target, $text, @_, $self->{call}, + @a[0..2], @b[0..2]); } } @@ -1930,28 +1932,13 @@ sub send_chat } # obtain country codes etc - my ($ann_dxcc, $ann_itu, $ann_cq, $org_dxcc, $org_itu, $org_cq) = (0..0); - my ($ann_state, $org_state) = ("", ""); - my @dxcc = Prefix::extract($_[0]); - if (@dxcc > 0) { - $ann_dxcc = $dxcc[1]->dxcc; - $ann_itu = $dxcc[1]->itu; - $ann_cq = $dxcc[1]->cq; - $ann_state = $dxcc[1]->state; - } - @dxcc = Prefix::extract($_[4]); - if (@dxcc > 0) { - $org_dxcc = $dxcc[1]->dxcc; - $org_itu = $dxcc[1]->itu; - $org_cq = $dxcc[1]->cq; - $org_state = $dxcc[1]->state; - } - + my @a = Prefix::cty_data($_[0]); + my @b = Prefix::cty_data($_[4]); if ($self->{inannfilter}) { my ($filter, $hops) = $self->{inannfilter}->it(@_, $self->{call}, - $ann_dxcc, $ann_itu, $ann_cq, - $org_dxcc, $org_itu, $org_cq, $ann_state, $org_state); + @a[0..2], + @b[0..2], $a[3], $b[3]); unless ($filter) { dbg("PCPROT: Rejected by input announce filter") if isdbg('chanerr'); return; @@ -1981,7 +1968,8 @@ sub send_chat } } - $dxchan->chat($is_ak1a ? $ak1a_line : $line, $self->{isolate}, $target, $_[1], $text, @_, $self->{call}, $ann_dxcc, $ann_itu, $ann_cq, $org_dxcc, $org_itu, $org_cq); + $dxchan->chat($is_ak1a ? $ak1a_line : $line, $self->{isolate}, $target, $_[1], + $text, @_, $self->{call}, @a[0..2], @b[0..2]); } } @@ -2017,35 +2005,50 @@ sub send_local_config my @remotenodes; dbg('DXProt::send_local_config') if isdbg('trace'); - - # send our nodes - if ($self->{isolate}) { - @localnodes = ( $main::routeroot ); - } else { - # create a list of all the nodes that are not connected to this connection - # and are not themselves isolated, this to make sure that isolated nodes - # don't appear outside of this node - my @dxchan = grep { $_->call ne $main::mycall && $_ != $self && !$_->{isolate} } DXChannel::get_all_nodes(); - @localnodes = map { my $r = Route::Node::get($_->{call}); $r ? $r : () } @dxchan if @dxchan; - my @intcalls = map { $_->nodes } @localnodes if @localnodes; - my $ref = Route::Node::get($self->{call}); - my @rnodes = $ref->nodes; - for my $node (@intcalls) { - push @remotenodes, Route::Node::get($node) unless grep $node eq $_, @rnodes; - } - unshift @localnodes, $main::routeroot; - } - - $self->send_route(\&pc19, scalar(@localnodes)+scalar(@remotenodes), @localnodes, @remotenodes); - - # get all the users connected on the above nodes and send them out - foreach $node (@localnodes, @remotenodes) { - if ($node) { - my @rout = map {my $r = Route::User::get($_); $r ? ($r) : ()} $node->users; - $self->send_route(\&pc16, 1, $node, @rout) if @rout && $self->user->wantsendpc16; + if ($self->{newroute}) { + my @nodes = $self->{isolate} ? ($main::routeroot) : grep { $_->call ne $main::mycall && $_ != $self && !$_->{isolate} } DXChannel::get_all_nodes(); + my @users = DXChannel::get_all_users(); + @localnodes = map { Route::Node::get($_->{call}) } @nodes; + my @localusers = map { Route::User::get($_->{call}) } @users; + $self->send_route($main::mycall, \&pc59, @nodes+@users+4, 'C', 0, $main::mycall, $main::routeroot, @localnodes, @localusers); + } else { + # send our nodes + if ($self->{isolate}) { + @localnodes = ( $main::routeroot ); + $self->send_route($main::mycall, \&pc19, 1, $main::routeroot); } else { - dbg("sent a null value") if isdbg('chanerr'); + # create a list of all the nodes that are not connected to this connection + # and are not themselves isolated, this to make sure that isolated nodes + # don't appear outside of this node + + # send locally connected nodes + my @dxchan = grep { $_->call ne $main::mycall && $_ != $self && !$_->{isolate} } DXChannel::get_all_nodes(); + @localnodes = map { my $r = Route::Node::get($_->{call}); $r ? $r : () } @dxchan if @dxchan; + $self->send_route($main::mycall, \&pc19, scalar(@localnodes)+1, $main::routeroot, @localnodes); + + my $node; + my @rawintcalls = map { $_->nodes } @localnodes if @localnodes; + my @intcalls; + for $node (@rawintcalls) { + push @intcalls, $node unless grep $node eq $_, @intcalls; + } + my $ref = Route::Node::get($self->{call}); + my @rnodes = $ref->nodes; + for $node (@intcalls) { + push @remotenodes, Route::Node::get($node) unless grep $node eq $_, @rnodes, @remotenodes; + } + $self->send_route($main::mycall, \&pc19, scalar(@remotenodes), @remotenodes); + } + + # get all the users connected on the above nodes and send them out + foreach $node ($main::routeroot, @localnodes, @remotenodes) { + if ($node) { + my @rout = map {my $r = Route::User::get($_); $r ? ($r) : ()} $node->users; + $self->send_route($main::mycall, \&pc16, 1, $node, @rout) if @rout && $self->user->wantsendpc16; + } else { + dbg("sent a null value") if isdbg('chanerr'); + } } } } @@ -2068,7 +2071,7 @@ sub route my $dxchan = DXChannel->get($call); unless ($dxchan) { my $cl = Route::get($call); - $dxchan = $cl->dxchan if $cl; + $dxchan = $cl->bestdxchan if $cl; if (ref $dxchan) { if (ref $self && $dxchan eq $self) { dbg("PCPROT: Trying to route back to source, dropped") if isdbg('chanerr'); @@ -2244,7 +2247,7 @@ sub addrcmd $rcmds{$to} = $r; my $ref = Route::Node::get($to); - my $dxchan = $ref->dxchan; + my $dxchan = $ref->bestdxchan; if ($dxchan && $dxchan->is_clx) { route(undef, $to, pc84($main::mycall, $to, $self->{call}, $cmd)); } else { @@ -2271,37 +2274,31 @@ sub disconnect my $node = Route::Node::get($call); my @rout; if ($node) { - @rout = $node->del($main::routeroot); - - # and all my ephemera as well + + # remove the route from this node and return a list + # of nodes that have become orphanned as a result. + push @rout, $main::routeroot->remove_route($node, $self); + + # remove all my ephemera as well for (@rout) { my $c = $_->call; eph_del_regex("^PC1[679].*$c"); } } - # remove them from the pc19list as well - while (my ($k,$v) = each %pc19list) { - my @l = grep {$_->[0] ne $call} @{$pc19list{$k}}; - if (@l) { - $pc19list{$k} = \@l; - } else { - delete $pc19list{$k}; - } - - # and the ephemera - eph_del_regex("^PC1[679].*$k"); - } - # unbusy and stop and outgoing mail my $mref = DXMsg::get_busy($call); $mref->stop_msg($call) if $mref; # broadcast to all other nodes that all the nodes connected to via me are gone unless ($pc39flag && $pc39flag == 2) { - $self->route_pc21(@rout) if @rout; + $self->route_pc21($main::mycall, undef, @rout) if @rout; + $self->route_pc59('D', 0, $main::mycall, $node); } + # delete all the unwanted nodes + $_->delete for @rout; + # remove outstanding pings delete $pings{$call}; @@ -2331,9 +2328,11 @@ sub talk # 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 + sub send_route { my $self = shift; + my $origin = shift; my $generate = shift; my $no = shift; # the no of things to filter on my $routeit; @@ -2342,6 +2341,12 @@ sub send_route for (; @_ && $no; $no--) { my $r = shift; + + # deal with non routing parameters + unless (ref $r && $r->isa('Route')) { + push @rin, $r; + next; + } if (!$self->{isolate} && $self->{routefilter}) { $filter = undef; @@ -2368,6 +2373,7 @@ sub send_route $routeit = adjust_hops($self, $line); # adjust its hop count by node name next unless $routeit; } + $self->send($routeit); } } @@ -2376,10 +2382,11 @@ sub send_route sub broadcast_route { my $self = shift; + my $origin = shift; my $generate = shift; + my $line = shift; my @dxchan = DXChannel::get_all_nodes(); my $dxchan; - my $line; unless ($self->{isolate}) { foreach $dxchan (@dxchan) { @@ -2387,8 +2394,14 @@ sub broadcast_route next if $dxchan == $main::me; next unless $dxchan->isa('DXProt'); next if ($generate == \&pc16 || $generate==\&pc17) && !$dxchan->user->wantsendpc16; + if ($dxchan->{newroute}) { + next if ($generate == \&pc19 || $generate==\&pc21); + } else { + next if ($generate == \&pc19 || $generate==\&pc21) && !$dxchan->user->wantroutepc19; + next if ($generate == \&pc59); + } - $dxchan->send_route($generate, @_); + $dxchan->send_route($origin, $generate, @_); } } } @@ -2397,44 +2410,67 @@ sub route_pc16 { my $self = shift; return unless $self->user->wantpc16; - broadcast_route($self, \&pc16, 1, @_); + my $origin = shift; + my $line = shift; + broadcast_route($self, $origin, \&pc16, $line, 1, @_); } sub route_pc17 { my $self = shift; return unless $self->user->wantpc16; - broadcast_route($self, \&pc17, 1, @_); + my $origin = shift; + my $line = shift; + broadcast_route($self, $origin, \&pc17, $line, 1, @_); } sub route_pc19 { my $self = shift; - broadcast_route($self, \&pc19, scalar @_, @_); + my $origin = shift; + my $line = shift; + broadcast_route($self, $origin, \&pc19, $line, scalar @_, @_); } sub route_pc21 { my $self = shift; - broadcast_route($self, \&pc21, scalar @_, @_); + my $origin = shift; + my $line = shift; + broadcast_route($self, $origin, \&pc21, $line, scalar @_, @_); } sub route_pc24 { my $self = shift; - broadcast_route($self, \&pc24, 1, @_); + my $origin = shift; + my $line = shift; + broadcast_route($self, $origin, \&pc24, $line, 1, @_); } sub route_pc41 { my $self = shift; - broadcast_route($self, \&pc41, 1, @_); + my $origin = shift; + my $line = shift; + broadcast_route($self, $origin, \&pc41, $line, 1, @_); } sub route_pc50 { my $self = shift; - broadcast_route($self, \&pc50, 1, @_); + my $origin = shift; + my $line = shift; + broadcast_route($self, $origin, \&pc50, $line, 1, @_); +} + +sub route_pc59 +{ + my $self = shift; + my $origin = shift; + my $line = shift; + + broadcast_route($self, $origin, \&pc59, $line, scalar @_, @_); } sub in_filter_route @@ -2458,7 +2494,6 @@ sub eph_dup # chop the end off $s =~ s/\^H\d\d?\^?\~?$//; - removepc90($s); $r = 1 if exists $eph{$s}; # pump up the dup if it keeps circulating $eph{$s} = $main::systime + $t; return $r;