use Route::Node;
use strict;
+
+use vars qw($VERSION $BRANCH);
+$VERSION = sprintf( "%d.%03d", q$Revision$ =~ /(\d+)\.(\d+)/ );
+$BRANCH = sprintf( "%d.%03d", q$Revision$ =~ /\d+\.\d+\.(\d+)\.(\d+)/ ) || 0;
+$main::build += $VERSION;
+$main::branch += $BRANCH;
+
use vars qw($me $pc11_max_age $pc23_max_age
$last_hour $last10 %eph %pings %rcmds
%nodehops $baddx $badspotter $badnode $censorpc
# remember type of connection
$self->{consort} = $line;
$self->{outbound} = $sort eq 'O';
- $self->{priv} = $user->priv || 1; # other clusters can always be 'normal' users
+ my $priv = $user->priv;
+ $priv = $user->priv(1) unless $priv;
+ $self->{priv} = $priv; # other clusters can always be 'normal' users
$self->{lang} = $user->lang || 'en';
$self->{isolate} = $user->{isolate};
$self->{consort} = $line; # save the connection type
my $user = DXUser->get_current($spot[4]);
if ($user) {
my $qra = $user->qra;
- unless ($qra && DXBearing::is_qra($qra)) {
+ unless ($qra && is_qra($qra)) {
my $lat = $user->lat;
my $long = $user->long;
if (defined $lat && defined $long) {
my $node;
my $to = $user->homenode;
my $last = $user->lastoper || 0;
- if ($to ne $main::mycall && $send_opernam && $main::systime > $last + $DXUser::lastoperinterval && $to && ($node = Route::Node::get($to)) ) {
+ 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 ($call, $conf, $here) = $field[$i] =~ /^(\S+) (\S) (\d)/o;
next unless $call && $conf && defined $here && is_callsign($call);
next if $call eq $main::mycall;
+
+ eph_del_regex("^PC17\^$call\^$ncall");
$conf = $conf eq '*';
$user->put;
}
+ if (eph_dup($line)) {
+ dbg("PCPROT: dup PC16 detected") if isdbg('chanerr');
+ return;
+ }
# queue up any messages (look for privates only)
DXMsg::queue_msg(1) if $self->state eq 'normal';
my $dxchan;
my $ncall = $field[2];
my $ucall = $field[1];
+
+ eph_del_regex("^PC16.*$ncall.*$ucall");
+
if ($ncall eq $main::mycall) {
dbg("PCPROT: trying to alter config on this node from outside!") if isdbg('chanerr');
return;
return unless $self->in_filter_route($parent);
my @rout = $parent->del_user($ucall);
+
+ if (eph_dup($line)) {
+ dbg("PCPROT: dup PC17 detected") if isdbg('chanerr');
+ return;
+ }
+
$self->route_pc17($parent, @rout) if @rout;
return;
}
my $conf = $field[$i+2];
my $ver = $field[$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
$user = DXUser->new($call);
$user->sort('A');
$user->priv(1); # I have relented and defaulted nodes
- $self->{priv} = 1; # to user RCMDs allowed
+ $user->lockout(1);
$user->homenode($call);
$user->node($call);
}
$user->put;
}
+ if (eph_dup($line)) {
+ dbg("PCPROT: dup PC19 detected") if isdbg('chanerr');
+ return;
+ }
+
$self->route_pc19(@rout) if @rout;
return;
}
if ($pcno == 21) { # delete a cluster from the list
my $call = uc $field[1];
+
+ eph_del_regex("^PC1[79].*$call");
+
my @rout;
my $parent = Route::Node::get($self->{call});
unless ($parent) {
dbg("PCPROT: I WILL _NOT_ be disconnected!") if isdbg('chanerr');
return;
}
+
+ if (eph_dup($line)) {
+ dbg("PCPROT: dup PC21 detected") if isdbg('chanerr');
+ return;
+ }
+
$self->route_pc21(@rout) if @rout;
return;
}
return;
}
- Log('DXProt', "Merge request for $field[3] spots and $field[4] WWV from $field[1]");
+ Log('DXProt', "Merge request for $field[3] spots and $field[4] WWV from $field[2]");
# spots
if ($field[3] > 0) {
}
if ($pcno == 34 || $pcno == 36) { # remote commands (incoming)
- if ($field[1] eq $main::mycall) {
- my $ref = DXUser->get_current($field[2]);
- my $cref = Route::Node::get($field[2]);
- Log('rcmd', 'in', $ref->{priv}, $field[2], $field[3]);
- unless (!$cref || !$ref || $cref->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:your attempt is logged, Tut tut tut...!"));
- }
- } else {
- my $ref = DXUser->get_current($field[1]);
- if ($ref && $ref->is_clx) {
- $self->route($field[1], pc84($field[2], $field[1], $field[2], $field[3]));
- } else {
- $self->route($field[1], $line);
- }
- }
+ $self->process_rcmd($field[1], $field[2], $field[2], $field[3]);
return;
}
if ($pcno == 35) { # remote command replies
- if ($field[1] eq $main::mycall) {
- my $s = $rcmds{$field[2]};
- if ($s) {
- my $dxchan = DXChannel->get($s->{call});
- $dxchan->send($field[3]) if $dxchan;
- delete $rcmds{$field[2]} if !$dxchan;
- } else {
- # send unsolicited ones to the sysop
- my $dxchan = DXChannel->get($main::myalias);
- $dxchan->send($field[3]) if $dxchan;
- }
- } else {
- my $ref = DXUser->get_current($field[1]);
- if ($ref && $ref->is_clx) {
- $self->route($field[1], pc85($field[2], $field[1], $field[2], $field[3]));
- } else {
- $self->route($field[1], $line);
- }
- }
+ $self->process_rcmd_reply($field[1], $field[2], $field[1], $field[3]);
return;
}
if ($pcno == 39) { # incoming disconnect
if ($field[1] eq $self->{call}) {
$self->disconnect(1);
+ eph_del_regex("^PC(?:1[679]|21).*$field[1]");
} else {
dbg("PCPROT: came in on wrong channel") if isdbg('chanerr');
}
}
if ($pcno == 41) { # user info
- # add this station to the user database, if required
my $call = $field[1];
+
+ # input filter if required
+# my $ref = Route::get($call) || Route->new($call);
+# return unless $self->in_filter_route($ref);
+
+ if ($field[3] eq $field[2]) {
+ dbg('PCPROT: invalid value') if isdbg('chanerr');
+ return;
+ }
+
+ # add this station to the user database, if required
my $user = DXUser->get_current($call);
$user = DXUser->new($call) if !$user;
} elsif ($field[2] == 2) {
$user->qth($field[3]);
} elsif ($field[2] == 3) {
- my ($lat, $long) = DXBearing::stoll($field[3]);
- $user->lat($lat);
- $user->long($long);
- $user->qra(DXBearing::lltoqra($lat, $long)) unless $user->qra && DXBearing::is_qra($user->qra);
+ if (is_latlong($field[3])) {
+ my ($lat, $long) = DXBearing::stoll($field[3]);
+ $user->lat($lat);
+ $user->long($long);
+ $user->qra(DXBearing::lltoqra($lat, $long));
+ } else {
+ dbg('PCPROT: not a valid lat/long') if isdbg('chanerr');
+ return;
+ }
} elsif ($field[2] == 4) {
$user->homenode($field[3]);
+ } elsif ($field[2] == 5) {
+ if (is_qra($field[3])) {
+ my ($lat, $long) = DXBearing::qratoll($field[3]);
+ $user->lat($lat);
+ $user->long($long);
+ $user->qra($field[3]);
+ } else {
+ dbg('PCPROT: not a valid QRA locator') if isdbg('chanerr');
+ return;
+ }
}
$user->lastoper($main::systime); # to cut down on excessive for/opers being generated
$user->put;
- my $ref = Route::get($call) || Route->new($call);
- # input filter if required
- return unless $self->in_filter_route($ref);
-
- $self->route_pc41($ref, $call, $field[2], $field[3], $field[4]) if $ref && !eph_dup($line);
- return;
+# perhaps this IS what we want after all
+# $self->route_pc41($ref, $call, $field[2], $field[3], $field[4]);
+# return;
}
if ($pcno == 43) {
my $to = $field[1];
my $from = $field[2];
my $flag = $field[3];
+
# is it for us?
if ($to eq $main::mycall) {
}
}
} else {
+ if (eph_dup($line)) {
+ dbg("PCPROT: dup PC51 detected") if isdbg('chanerr');
+ return;
+ }
# route down an appropriate thingy
$self->route($to, $line);
}
}
if ($pcno == 84) { # remote commands (incoming)
- my $call = $field[1];
- if ($call eq $main::mycall) {
- my $ref = DXUser->get_current($field[2]);
- my $cref = Route::Node::get($field[2]);
- Log('rcmd', 'in', $ref->{priv}, $field[2], $field[4]);
- unless ($field[4] =~ /rcmd/i || !$cref || !$ref || $cref->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[4]));
- $self->{priv} = $oldpriv;
- for (@in) {
- s/\s*$//og;
- $self->send(pc85($main::mycall, $field[2], $field[3], "$main::mycall:$_"));
- Log('rcmd', 'out', $field[2], $_);
- }
- delete $self->{remotecmd};
- } else {
- $self->send(pc85($main::mycall, $field[2], $field[3], "$main::mycall:sorry...!"));
- }
- } else {
- $self->send(pc85($main::mycall, $field[2], $field[3],"$main::mycall:your attempt is logged, Tut tut tut...!"));
- }
- } else {
- my $ref = DXUser->get_current($call);
- if ($ref && $ref->is_clx) {
- $self->route($call, $line);
- } else {
- $self->route($call, pc34($field[2], $call, $field[4]));
- }
- }
+ $self->process_rcmd($field[1], $field[2], $field[3], $field[4]);
return;
}
if ($pcno == 85) { # remote command replies
- my $call = $field[1];
- if ($call eq $main::mycall) {
- my $dxchan = DXChannel->get($field[3]);
- if ($dxchan) {
- $dxchan->send($field[4]);
- } else {
- my $s = $rcmds{$field[2]};
- if ($s) {
- $dxchan = DXChannel->get($s->{call});
- $dxchan->send($field[4]) if $dxchan;
- delete $rcmds{$field[2]} if !$dxchan;
- } else {
- # send unsolicited ones to the sysop
- my $dxchan = DXChannel->get($main::myalias);
- $dxchan->send($field[4]) if $dxchan;
- }
- }
- } else {
- my $ref = DXUser->get_current($call);
- if ($ref && $ref->is_clx) {
- $self->route($call, $line);
- } else {
- $self->route($call, pc35($field[2], $call, $field[4]));
- }
- }
+ $self->process_rcmd_reply($field[1], $field[2], $field[3], $field[4]);
+
return;
}
}
# taking into account filtering and so on
foreach $dxchan (@dxchan) {
next if $dxchan == $me;
- next if $dxchan == $self;
+ next if $dxchan == $self && $self->is_node;
$dxchan->dx_spot($line, $self->{isolate}, @_, $self->{call});
}
}
($filter, $hops) = $self->{spotsfilter}->it(@_);
return unless $filter;
}
- send_prot_line($self, $filter, $hops, $isolate, $line)
+ send_prot_line($self, $filter, $hops, $isolate, $line);
}
sub send_prot_line
$routeit =~ s/\^H\d+\^\~$/\^H$hops\^\~/;
} else {
$routeit = adjust_hops($self, $line); # adjust its hop count by node name
- next unless $routeit;
+ return unless $routeit;
}
if ($filter) {
- $self->send($routeit) if $routeit;
+ $self->send($routeit);
} else {
$self->send($routeit) unless $self->{isolate} || $isolate;
}
# 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;
next if $dxchan == $me;
+ next if $dxchan == $self && $self->is_node;
my $routeit;
my ($filter, $hops);
# 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;
next if $dxchan == $me;
+ next if $dxchan == $self && $self->is_node;
my $routeit;
my ($filter, $hops);
$pings{$to} = $ref;
}
+sub process_rcmd
+{
+ my ($self, $tonode, $fromnode, $user, $cmd) = @_;
+ if ($tonode eq $main::mycall) {
+ my $ref = DXUser->get_current($fromnode);
+ my $cref = Route::Node::get($fromnode);
+ Log('rcmd', 'in', $ref->{priv}, $fromnode, $cmd);
+ if ($cmd !~ /^\s*rcmd/i && $cref && $ref && $cref->call eq $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, $cmd));
+ $self->{priv} = $oldpriv;
+ $self->send_rcmd_reply($main::mycall, $fromnode, $user, @in);
+ delete $self->{remotecmd};
+ } else {
+ $self->send_rcmd_reply($main::mycall, $fromnode, $user, "sorry...!");
+ }
+ } else {
+ $self->send_rcmd_reply($main::mycall, $fromnode, $user, "your attempt is logged, Tut tut tut...!");
+ }
+ } else {
+ my $ref = DXUser->get_current($tonode);
+ if ($ref && $ref->is_clx) {
+ $self->route($tonode, pc84($fromnode, $tonode, $user, $cmd));
+ } else {
+ $self->route($tonode, pc34($fromnode, $tonode, $cmd));
+ }
+ }
+}
+
+sub process_rcmd_reply
+{
+ my ($self, $tonode, $fromnode, $user, $line) = @_;
+ if ($tonode eq $main::mycall) {
+ my $s = $rcmds{$fromnode};
+ if ($s) {
+ my $dxchan = DXChannel->get($s->{call});
+ my $ref = $user eq $tonode ? $dxchan : (DXChannel->get($user) || $dxchan);
+ $ref->send($line) if $ref;
+ delete $rcmds{$fromnode} if !$dxchan;
+ } else {
+ # send unsolicited ones to the sysop
+ my $dxchan = DXChannel->get($main::myalias);
+ $dxchan->send($line) if $dxchan;
+ }
+ } else {
+ my $ref = DXUser->get_current($tonode);
+ if ($ref && $ref->is_clx) {
+ $self->route($tonode, pc85($fromnode, $tonode, $user, $line));
+ } else {
+ $self->route($tonode, pc35($fromnode, $tonode, $line));
+ }
+ }
+}
+
+sub send_rcmd_reply
+{
+ my $self = shift;
+ my $tonode = shift;
+ my $fromnode = shift;
+ my $user = shift;
+ while (@_) {
+ my $line = shift;
+ $line =~ s/\s*$//;
+ Log('rcmd', 'out', $fromnode, $line);
+ if ($self->is_clx) {
+ $self->send(pc85($main::mycall, $fromnode, $user, "$main::mycall:$line"));
+ } else {
+ $self->send(pc35($main::mycall, $fromnode, "$main::mycall:$line"));
+ }
+ }
+}
+
# add a rcmd request to the rcmd queues
sub addrcmd
{
my $node = Route::Node::get($call);
my @rout;
if ($node) {
- @rout = $node->del_nodes; # at the next level
+# @rout = $node->del_nodes; # at the next level
@rout = $node->del($main::routeroot);
}
$routeit = adjust_hops($self, $line); # adjust its hop count by node name
next unless $routeit;
}
- $self->send($routeit) if $self->{routefilter} || !$self->{isolate};
+ $self->send($routeit);
}
}
}
foreach $dxchan (@dxchan) {
next if $dxchan == $self;
next if $dxchan == $me;
- if ($dxchan->{routefilter} || !$self->{isolate}) {
- $dxchan->send_route($generate, @_)
+ if ($dxchan->{routefilter}) {
+ $dxchan->send_route($generate, @_);
} else {
- dbg('DXPROT: isolated') if isdbg('chanerr');
+ $dxchan->send_route($generate, @_) unless $self->{isolate} || $dxchan->{isolate};
}
}
}
my $s = shift;
# chop the end off
- $s =~ s/\^H\d\d?\^?~?$//;
+ $s =~ s/\^H\d\d?\^?\~?$//;
return 1 if exists $eph{$s};
$eph{$s} = $main::systime;
return undef;
}
+sub eph_del_regex
+{
+ my $regex = shift;
+ my ($key, $val);
+ while (($key, $val) = each %eph) {
+ if ($key =~ m{$regex}) {
+ delete $eph{$key};
+ }
+ }
+}
+
sub eph_clean
{
my ($key, $val);