use AnnTalk;
use Geomag;
use WCY;
-use Time::HiRes qw(gettimeofday tv_interval);
use BadWords;
use DXHash;
use Route;
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,0));
-$main::build += $VERSION;
-$main::branch += $BRANCH;
-
use vars qw($pc11_max_age $pc23_max_age $last_pc50 $eph_restime $eph_info_restime $eph_pc34_restime
$last_hour $last10 %eph %pings %rcmds $ann_to_talk
$pingint $obscount %pc19list $chatdupeage $chatimportfn
$investigation_int $pc19_version $myprot_version
%nodehops $baddx $badspotter $badnode $censorpc $rspfcheck
$allowzero $decode_dk0wcy $send_opernam @checklist
- $handle_xml);
+ $eph_pc15_restime
+ );
$pc11_max_age = 1*3600; # the maximum age for an incoming 'real-time' pc11
$pc23_max_age = 1*3600; # the maximum age for an incoming 'real-time' pc23
$last_hour = time; # last time I did an hourly periodic update
-%pings = (); # outstanding ping requests outbound
%rcmds = (); # outstanding rcmd requests outbound
%nodehops = (); # node specific hop control
%pc19list = (); # list of outstanding PC19s that haven't had PC16s on them
$rspfcheck = 1;
$eph_restime = 180;
$eph_info_restime = 60*60;
+$eph_pc15_restime = 6*60;
$eph_pc34_restime = 30;
$pingint = 5*60;
$obscount = 2;
$chatimportfn = "$main::root/chat_import";
$investigation_int = 12*60*60; # time between checks to see if we can see this node
$pc19_version = 5466; # the visible version no for outgoing PC19s generated from pc59
-$handle_xml = 0; # handle XML sentences
@checklist =
(
[ qw(i c c m bp bc c) ], # pc10
- [ qw(i f bm d t m c c h) ], # pc11
+ [ qw(i f m d t m c c h) ], # pc11
[ qw(i c bm m bm bm p h) ], # pc12
[ qw(i c h) ], #
[ qw(i c h) ], #
# add this node to the table, the values get filled in later
my $pkg = shift;
my $call = shift;
+
+ # if we have an entry already, then send a PC21 to all connect
+ # old style connections, because we are about to get the real deal
+ if (my $ref = Route::Node::get($call)) {
+ dbg("ROUTE: $call is already in the routing table, deleting") if isdbg('route');
+ my @rout = $ref->delete;
+ $self->route_pc21($main::mycall, undef, @rout) if @rout;
+ }
$main::routeroot->add($call, '5000', Route::here(1)) if $call ne $main::mycall;
return $self;
$self->{pingave} = 999;
$self->{metric} ||= 100;
$self->{lastping} = $main::systime;
-
+
# send initialisation string
unless ($self->{outbound}) {
$self->sendinit;
{
my ($self, $line) = @_;
+ if ($line =~ '^<\w+\s' && $main::do_xml) {
+ DXXml::normal($self, $line);
+ return;
+ }
+
my @field = split /\^/, $line;
return unless @field;
# process PC frames, this will fail unless the frame starts PCnn
my ($pcno) = $field[0] =~ /^PC(\d\d)/; # just get the number
- unless (defined $pcno && $pcno >= 10 && $pcno <= 99) {
+ unless (defined $pcno && $pcno >= 10 && $pcno <= 89) { # reject PC9x messages
dbg("PCPROT: unknown protocol") if isdbg('chanerr');
return;
}
# rsfp check
# return if $rspfcheck and !$self->rspfcheck(1, $_[7], $_[6]);
+
+ # is the spotted callsign blank? This should really be trapped earlier but it
+ # could break other protocol sentences. Also check for lower case characters.
+ if ($_[2] =~ /^\s*$/) {
+ dbg("PCPROT: blank callsign, dropped") if isdbg('chanerr');
+ return;
+ }
+ if ($_[2] =~ /[a-z]/) {
+ dbg("PCPROT: lowercase characters, dropped") if isdbg('chanerr');
+ return;
+ }
+
# if this is a 'nodx' node then ignore it
if ($badnode->in($_[7])) {
$self->route($_[2], $line);
}
}
+
+
+sub handle_15
+{
+ my $self = shift;
+ my $pcno = shift;
+ my $line = shift;
+ my $origin = shift;
+
+ if (eph_dup($line, $eph_pc15_restime)) {
+ dbg("PCPROT: Ephemeral dup, dropped") if isdbg('chanerr');
+ } else {
+ unless ($self->{isolate}) {
+ DXChannel::broadcast_nodes($line, $self) if $line =~ /\^H\d+\^?~?$/; # send it to everyone but me
+ }
+ }
+}
# incoming user
sub handle_16
}
# is it me?
if ($ncall eq $main::mycall) {
- dbg("PCPROT: trying to alter config on this node from outside!") if isdbg('chanerr');
+ dbg("PCPROT: trying to alter my config from outside!") if isdbg('chanerr');
+ return;
+ }
+ if (DXChannel::get($ncall) && $ncall ne $self->{call}) {
+ dbg("PCPROT: trying to alter locally connected $ncall from $self->{call}, ignored") if isdbg('chanerr');
return;
}
push @rout, $parent->add_user($call, $flags);
}
+ # send info to all logged in thingies
+ $self->tell_login('loginu', "$ncall: $call") if DXUser->get_current($ncall)->is_local_node;
+ $self->tell_buddies('loginb', $call, $ncall);
# add this station to the user database, if required
- $call =~ s/-\d+$//o; # remove ssid for users
+# $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;
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) {
dbg("PCPROT: Route::Node $ncall not in config") if isdbg('chanerr');
+ return;
}
- $dxchan = $parent->dxchan if $parent;
+ $dxchan = DXChannel::get($ncall);
if ($dxchan && $dxchan ne $self) {
dbg("PCPROT: PC17 from $self->{call} trying to alter locally connected $ncall, ignored!") if isdbg('chanerr');
return;
}
# input filter if required and then remove user if present
- if ($parent) {
# return unless $self->in_filter_route($parent);
- $parent->del_user($uref) if $uref;
- } else {
- $parent = Route->new($ncall); # throw away
- }
+ $parent->del_user($uref);
+
+ # send info to all logged in thingies
+ $self->tell_login('logoutu', "$ncall: $ucall") if DXUser->get_current($ncall)->is_local_node;
+ $self->tell_buddies('logoutb', $ucall, $ncall);
if (eph_dup($line)) {
dbg("PCPROT: dup PC17 detected") if isdbg('chanerr');
return;
}
- $uref = Route->new($ucall) unless $uref; # throw away
$self->route_pc17($origin, $line, $parent, $uref);
}
$self->user->put;
$self->sort('S');
}
- $self->{handle_xml}++ if $_[1] =~ /\bxml\b/;
+ $self->{handle_xml}++ if DXXml::available() && $_[1] =~ /\bxml\b/;
} else {
$self->version(50.0);
$self->version($_[2] / 100) if $_[2] && $_[2] =~ /^\d+$/;
# check for sane parameters
# $ver = 5000 if $ver eq '0000';
+ next unless $ver && $ver =~ /^\d+$/;
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;
$user = DXUser->new($call) unless $user;
if ($_[2] == 1) {
+ if (($_[3] =~ /spotter/i || $_[3] =~ /self/i) && $user->name && $user->name ne $_[3]) {
+ dbg("PCPROT: invalid name") if isdbg('chanerr');
+ return;
+ }
$user->name($_[3]);
} elsif ($_[2] == 2) {
$user->qth($_[3]);
if ($flag == 1) {
$self->send(pc51($from, $to, '0'));
} else {
- # it's a reply, look in the ping list for this one
- my $ref = $pings{$from};
- if ($ref) {
- my $tochan = DXChannel::get($from);
- while (@$ref) {
- my $r = shift @$ref;
- my $dxchan = DXChannel::get($r->{call});
- next unless $dxchan;
- my $t = tv_interval($r->{t}, [ gettimeofday ]);
- if ($dxchan->is_user) {
- my $s = sprintf "%.2f", $t;
- my $ave = sprintf "%.2f", $tochan ? ($tochan->{pingave} || $t) : $t;
- $dxchan->send($dxchan->msg('pingi', $from, $s, $ave))
- } elsif ($dxchan->is_node) {
- if ($tochan) {
- my $nopings = $tochan->user->nopings || $obscount;
- push @{$tochan->{pingtime}}, $t;
- shift @{$tochan->{pingtime}} if @{$tochan->{pingtime}} > 6;
-
- # cope with a missed ping, this means you must set the pingint large enough
- if ($t > $tochan->{pingint} && $t < 2 * $tochan->{pingint} ) {
- $t -= $tochan->{pingint};
- }
-
- # calc smoothed RTT a la TCP
- if (@{$tochan->{pingtime}} == 1) {
- $tochan->{pingave} = $t;
- } else {
- $tochan->{pingave} = $tochan->{pingave} + (($t - $tochan->{pingave}) / 6);
- }
- $tochan->{nopings} = $nopings; # pump up the timer
- if (my $ivp = Investigate::get($from, $self->{call})) {
- $ivp->handle_ping;
- }
- } elsif (my $rref = Route::Node::get($r->{call})) {
- if (my $ivp = Investigate::get($from, $self->{call})) {
- $ivp->handle_ping;
- }
- }
- }
- }
- }
+ DXXml::Ping::handle_ping_reply($self, $from);
}
} else {
}
foreach $dxchan (@dxchan) {
- next unless $dxchan->is_node();
+ next unless $dxchan->is_node;
+ next if $dxchan->handle_xml;
next if $dxchan == $main::me;
# send the pc50
if ($dxchan->{nopings} <= 0) {
$dxchan->disconnect;
} else {
- addping($main::mycall, $dxchan->call);
+ DXXml::Ping::add($main::me, $dxchan->call);
$dxchan->{nopings} -= 1;
$dxchan->{lastping} = $t;
+ $dxchan->{lastping} += $dxchan->{pingint} / 2 unless @{$dxchan->{pingtime}};
}
}
}
return ();
}
-
-# add a ping request to the ping queues
-sub addping
-{
- my ($from, $to, $via) = @_;
- my $ref = $pings{$to} || [];
- my $r = {};
- $r->{call} = $from;
- $r->{t} = [ gettimeofday ];
- if ($via && (my $dxchan = DXChannel::get($via))) {
- $dxchan->send(pc51($to, $main::mycall, 1));
- } else {
- route(undef, $to, pc51($to, $main::mycall, 1));
- }
- push @$ref, $r;
- $pings{$to} = $ref;
- my $u = DXUser->get_current($to);
- if ($u) {
- $u->lastping(($via || $from), $main::systime);
- $u->put;
- }
-}
-
sub process_rcmd
{
my ($self, $tonode, $fromnode, $user, $cmd) = @_;