2017-12-03 12:49:30 +01:00
|
|
|
#!/usr/bin/perl
|
|
|
|
|
|
|
|
# VDR SVDRP Peer Demo
|
|
|
|
#
|
2018-02-25 12:42:58 +01:00
|
|
|
# This script broadcasts an SVDRP discover datagram on the SVDRP UDP port and
|
|
|
|
# then listens for replies from peer VDRs on both the UDP and TCP port.
|
|
|
|
# It reacts properly to the SVDRP commands CONN, LSTT, POLL, PING and QUIT,
|
|
|
|
# and thus seems like a regular VDR to other VDRs.
|
|
|
|
#
|
2018-04-10 17:20:58 +02:00
|
|
|
# See the main source file 'vdr.c' for copyright information and
|
|
|
|
# how to reach the author.
|
2017-12-03 12:49:30 +01:00
|
|
|
|
|
|
|
use Getopt::Std;
|
|
|
|
use IO::Socket;
|
|
|
|
use IO::Select;
|
|
|
|
|
2018-02-25 21:50:18 +01:00
|
|
|
$DefaultSvdrpPort = 6419;
|
|
|
|
$DefaultSvdrpName = "peerdemo";
|
|
|
|
|
2017-12-03 12:49:30 +01:00
|
|
|
$Usage = qq{
|
|
|
|
Usage: $0 options
|
|
|
|
|
2018-02-25 21:50:18 +01:00
|
|
|
Options: -n name use the given VDR name (default: $DefaultSvdrpName)
|
|
|
|
-p port use the given TCP port (default: $DefaultSvdrpPort)
|
|
|
|
-v be verbose
|
2017-12-03 12:49:30 +01:00
|
|
|
};
|
|
|
|
|
2018-02-25 21:50:18 +01:00
|
|
|
die $Usage if (!getopts("n:p:v"));
|
2017-12-03 12:49:30 +01:00
|
|
|
|
2018-02-25 21:50:18 +01:00
|
|
|
$Name = $opt_n || $DefaultSvdrpName;
|
|
|
|
$Port = $opt_p || $DefaultSvdrpPort;
|
2018-02-25 12:42:58 +01:00
|
|
|
$Verbose = $opt_v || 0;
|
2017-12-03 12:49:30 +01:00
|
|
|
|
|
|
|
# Open TCP and UDP sockets:
|
|
|
|
|
2018-02-25 21:50:18 +01:00
|
|
|
$TcpPort = $Port;
|
|
|
|
$UdpPort = $DefaultSvdrpPort;
|
|
|
|
|
|
|
|
$TcpSocket = new IO::Socket::INET(Listen => 5, LocalPort => $TcpPort, Proto => "tcp", ReusePort => 1) || die "$!";
|
2019-03-11 10:28:22 +01:00
|
|
|
$UdpSocket = new IO::Socket::INET( LocalPort => $UdpPort, Proto => "udp", ReuseAddr => 1) || die "$!";
|
2017-12-03 12:49:30 +01:00
|
|
|
$SvdrpSelect = new IO::Select($TcpSocket);
|
|
|
|
setsockopt($UdpSocket, SOL_SOCKET, SO_RCVTIMEO, pack('L!L!', 0, 1000)); # 1ms timeout on UDP socket
|
|
|
|
|
|
|
|
# Send UDP broadcast:
|
|
|
|
|
2018-02-25 21:50:18 +01:00
|
|
|
$BcastSocket = new IO::Socket::INET(PeerAddr => '255.255.255.255', PeerPort => $UdpPort, Proto => "udp", Broadcast => 1) || die "$!";
|
|
|
|
$BcastMsg = "SVDRP:discover name:$Name port:$TcpPort vdrversion:20309 apiversion:20309 timeout:300";
|
2017-12-03 12:49:30 +01:00
|
|
|
Log('>', $BcastSocket, $BcastMsg);
|
|
|
|
print($BcastSocket $BcastMsg);
|
|
|
|
$BcastSocket->close();
|
|
|
|
|
|
|
|
# Listen on UDP and TCP socket:
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
if ($UdpSocket->recv($Request, 1024)) {
|
2018-02-25 21:50:18 +01:00
|
|
|
if (Extract($Request, "name") ne $Name) {
|
2017-12-03 12:49:30 +01:00
|
|
|
Log('<', $UdpSocket, $Request);
|
2018-02-25 12:42:58 +01:00
|
|
|
ReportVDR($Request, $UdpSocket->peerhost());
|
2017-12-03 12:49:30 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (my @Ready = $SvdrpSelect->can_read(0.01)) {
|
|
|
|
for my $fh (@Ready) {
|
|
|
|
if ($fh == $TcpSocket) {
|
|
|
|
# accept connection:
|
|
|
|
my $new = $TcpSocket->accept();
|
|
|
|
Log('<', $new, "incoming TCP connection");
|
|
|
|
# send mandatory response to simulate an SVDRP host:
|
2018-02-25 21:50:18 +01:00
|
|
|
my $Prompt = "220 $Name SVDRP VideoDiskRecorder 2.3.9; Wed Nov 29 17:00:29 2017; ISO-8859-1";
|
2017-12-03 12:49:30 +01:00
|
|
|
Log('>', $new, $Prompt);
|
2018-02-25 12:42:58 +01:00
|
|
|
print($new "$Prompt\n");
|
|
|
|
# add incoming connection to select:
|
|
|
|
$SvdrpSelect->add($new);
|
2017-12-03 12:49:30 +01:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
# process connection:
|
2018-02-25 12:42:58 +01:00
|
|
|
my $Request = "";
|
|
|
|
$fh->recv($Request, 1024);
|
|
|
|
chomp($Request);
|
2018-03-01 15:00:32 +01:00
|
|
|
Log('<', $fh, $Request) if ($Request);
|
2018-03-09 10:49:48 +01:00
|
|
|
if ($Request =~ /^CONN/i) {
|
2018-02-25 12:42:58 +01:00
|
|
|
Reply($fh, "250 OK");
|
|
|
|
ReportVDR($Request, $fh->peerhost());
|
|
|
|
}
|
2018-03-09 10:49:48 +01:00
|
|
|
elsif ($Request =~ /^LSTT/i) {
|
2018-02-25 12:42:58 +01:00
|
|
|
Reply($fh, "550 No timers defined");
|
|
|
|
}
|
2018-03-09 10:49:48 +01:00
|
|
|
elsif ($Request =~ /^POLL/i) {
|
2018-02-25 12:42:58 +01:00
|
|
|
Reply($fh, "250 OK");
|
|
|
|
}
|
2018-03-09 10:49:48 +01:00
|
|
|
elsif ($Request =~ /^PING/i) {
|
2018-02-25 21:50:18 +01:00
|
|
|
Reply($fh, "250 $Name is alive");
|
2018-02-25 12:42:58 +01:00
|
|
|
}
|
2018-03-09 10:49:48 +01:00
|
|
|
elsif ($Request =~ /^QUIT/i || !$Request) {
|
2018-02-25 12:42:58 +01:00
|
|
|
# close connection:
|
2018-03-01 15:00:32 +01:00
|
|
|
Log('<', $fh, "connection closed");
|
2018-02-25 12:42:58 +01:00
|
|
|
$SvdrpSelect->remove($fh);
|
|
|
|
$fh->close;
|
2017-12-03 12:49:30 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
# Tools:
|
|
|
|
|
2018-02-25 12:42:58 +01:00
|
|
|
sub Reply
|
|
|
|
{
|
|
|
|
my ($fh, $s) = @_;
|
|
|
|
Log('>', $fh, $s);
|
|
|
|
print($fh "$s\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
sub ReportVDR
|
|
|
|
{
|
|
|
|
my $s = shift;
|
|
|
|
my $PeerHost = shift;
|
|
|
|
$s .= " "; # for easier parsing
|
|
|
|
my $Name = Extract($s, "name");
|
|
|
|
my $Port = Extract($s, "port");
|
|
|
|
my $VdrVersion = Extract($s, "vdrversion");
|
|
|
|
my $ApiVersion = Extract($s, "apiversion");
|
|
|
|
my $Timeout = Extract($s, "timeout");
|
|
|
|
print("found VDR '$Name' at $PeerHost with SVDRP port '$Port'\n");
|
|
|
|
}
|
|
|
|
|
2017-12-03 12:49:30 +01:00
|
|
|
sub Extract
|
|
|
|
{
|
|
|
|
my ($s, $n) = @_;
|
|
|
|
return ($s =~ / $n:([^ ]*) /)[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
sub Log
|
|
|
|
{
|
|
|
|
return unless ($Verbose);
|
|
|
|
my ($Dir, $Socket, $Msg) = @_;
|
|
|
|
printf("SVDRP %s [%s:%s] %s\n", $Dir, $Socket->peerhost(), $Socket->peerport(), $Msg);
|
|
|
|
}
|