- #!/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;
- }