#!/usr/bin/perl use strict; use warnings; use IO::Socket::INET; use IO::Socket qw(AF_INET AF_UNIX SOCK_STREAM SHUT_WR); use Socket qw(:crlf); use IO::Select; use IO::Socket::UNIX qw( SOCK_STREAM SOMAXCONN ); use XML::LibXML; use Data::Dump qw(dump); use Data::Dumper; use autodie; use lib 'lib'; binmode STDERR, ':utf8'; #This is a self service proxy server for communication between the Koha REST "sipmessages" endpoint #and between a sipserver via Socket::INET sockets. #Sipserver's service parameter must be set: client_timeout="0" in SIPconfig.xml for this script to work properly! #Self check device handles timeouts. #While we attempt to write things to read-side through socket, if the socket of read-side already closed, #the write-side would got a signal SIGPIPE, that causes write-side killed by SIGPIPE. #????Prevents server from shutting down $SIG{PIPE} = 'IGNORE'; $SIG{'TSTP'} = 'IGNORE'; # Ctrl-Z disabled if (defined $ENV{'DEBUG'}) { *STDERR = $ENV{'DEBUG'}; print STDERR "DEBUG found \n"; } else { print STDERR "$ENV{'DEBUG'} no log var found.\n"; } #Get command line argument (the sipdevice name = xml login parameter) my $device = shift or die "Usage: $0 SIPDEVICELOGINNAME\n"; print STDERR "Starting proxy server for device $device: \n"; my ( $proxyhost, $proxyport, $siphost, $sipport ) = getConfig($device); #TODO change into var/spool/ in sipdevices.xml $| = 1; # Autoflush #Needs to be a plain IO::Socket so we can use $client_socket->shutdown(SHUT_RD) and (SHUT_WR)to end #reading/writing to sipserver without problems and preserving socket connection my $server = IO::Socket->new( Domain => AF_INET, Type => SOCK_STREAM, Proto => 'tcp', LocalHost => $proxyhost, LocalPort => $proxyport, ReusePort => 1, KeepAlive => 0, Listen => 5 ) || die "Can't open proxy socket for $device: $@"; #handle signals #TODO CTRL+C sends an emtpy message to sipserver $SIG{TERM} = $SIG{INT} = $SIG{HUP} = sub { print STDERR ("SIGTERM - External termination request. Leaving..."); if ($server) { print STDERR "Closing server socket. \n"; $server->shutdown(SHUT_RDWR); $server->close; exit; } else { exit; } }; #Socket:INET to sipserver my $sipsocket = IO::Socket::INET->new( PeerHost => $siphost, PeerPort => $sipport, Proto => 'tcp', KeepAlive => 1, Reuse => 1 ) or die "Couldn't be a tcp server on port '$sipport' : $@\n"; print STDERR "Waiting for tcp to connect to $proxyport\n"; while (1) { my $client_socket = $server->accept(); #? #my $sip_socket = $sipsocket->accept(); print STDERR "Socket has connected\n"; connection( $client_socket, $sipsocket ); } $server->close; sub connection { my $client_socket = shift; $client_socket->autoflush(1); my $sipsock = shift; $sipsock->autoflush(1); if ( $sipsock->connected ) { print STDERR "Connection to SIP socket OK. \n"; } else { #??????????????????????? #How to test if sipserver socket has disconnected print STDERR "Sip socket closed!"; my $sipsocket = IO::Socket::INET->new( PeerHost => '10.0.3.217', PeerPort => 6009, Proto => 'tcp', KeepAlive => 1, Reuse => 1 ) or die "Couldn't be a tcp server on port 6009 : $@\n"; } while (1) { if ( $sipsock->connected ) { #Still connected to SIP socket my $data = ""; my $respdata = ""; $data = <$client_socket>; #end reading from socket. $CR etc do not work. $client_socket->shutdown(SHUT_RD); $client_socket->flush; if ( $data eq "" ) { print STDERR "Empty request!" #return; } print STDERR ">>>>>> Sending: $data\n"; print $sipsock $data; $sipsock->recv( $respdata, 1024 ); $sipsock->flush; print STDERR "<<<<<< Received from SIPserver: $respdata\n\n"; ######Handle empty message ---_> next message needs a fresh connection ######Is this a failsafe in sipserver? if ( $respdata eq "" ) { print STDERR "Sip server returned no data (bad device login mes/sipserver down?) $data\n"; my $errordata = "Disconnected!"; #Send disconnect info to REST print $client_socket $errordata . $CR; #end writing to socket $client_socket->shutdown(SHUT_WR); #? $client_socket->shutdown(SHUT_RDWR) ; # we stopped using this socket $client_socket->close; print STDERR "Sipserver disconnected. Exiting...\n"; exit; } else { print $client_socket $respdata . $CR; } ######Handle empty message -> next message needs a fresh connection -> restart proxy server #end writing to socket $client_socket->shutdown(SHUT_WR); $client_socket->shutdown(SHUT_RDWR); # we stopped using this socket $client_socket->close; print STDERR "Sipserver response message passed to REST endpoint. Done. Listening... \n\n"; return; } else { print STDERR "Sipserver socket Disconnected\n"; #Try to establish a new fresh connection or die? exit; } } } sub getConfig { #reads sip server info from config file and returns socket setup parameters my $device = shift; my ( $proxyhost, $proxyport, $host, $port ); my $dom = XML::LibXML->load_xml( location => "/home/koha/Koha/koha-tmpl/sipdevices.xml" ); foreach my $sipserver ( $dom->findnodes( '//' . $device ) ) { $proxyhost = $sipserver->findvalue('./proxyhost'); $proxyport = $sipserver->findvalue('./proxyport'); $host = $sipserver->findvalue('./host'); $port = $sipserver->findvalue('./port'); if ( $host && $port ) { print STDERR "Found config: '$host' '$port' in sipdevices.xml. \n"; } else { die "Missing parameters for '$device' in sipconfig.xml \n"; } } return $proxyhost, $proxyport, $host, $port; }