#!/usr/bin/env perl #ver 0.11 (C)Cyr use strict; use warnings; use diagnostics; use IO::Socket; use Convert::ASN1; #use Data::Dumper; use Carp; use POSIX; my $CSTAapdu; my $socket; my @devices; my @calls; sub deviceNumber { my $deviceNumber = shift; if ( defined $deviceNumber->{deviceNumber} ) { $deviceNumber = $deviceNumber->{deviceNumber}; my $deviceType = $deviceNumber & 0xFFFF0000; if ( $deviceType == 0x1310000 ) { $deviceType = 'CO'; } elsif ( $deviceType == 0x1210000 ) { $deviceType = 'PS'; } elsif ( $deviceType == 0x1110000 ) { $deviceType = 'EXT'; } $deviceNumber = $deviceNumber & 0x0000FFFF; return $deviceType . sprintf( "%03s", $deviceNumber ); } elsif ( defined $deviceNumber->{dialingNumber} ) { return $deviceNumber->{dialingNumber}; } } sub localConnectionInfo { my $localConnectionInfo = shift; if ( $localConnectionInfo == 0 ) { return 'null'; } elsif ( $localConnectionInfo == 1 ) { return 'initiated'; } elsif ( $localConnectionInfo == 2 ) { return 'alerting'; } elsif ( $localConnectionInfo == 3 ) { return 'connected'; } elsif ( $localConnectionInfo == 4 ) { return 'hold'; } elsif ( $localConnectionInfo == 5 ) { return 'queued'; } elsif ( $localConnectionInfo == 6 ) { return 'fail'; } else { return $localConnectionInfo } } sub decode_msg_header { my $bin = shift; my $len = unpack( 'n', $bin ); return $len; } sub encode_msg_header { my $len = shift; die "Message larger than allowed!" unless ( $len <= 240 ); my $bin = pack( 'n', $len ); return $bin; } sub convert_to_hex { my $pdu = $_[0]; my $hexdata = unpack( 'H*', $pdu ); $hexdata =~ tr/a-z/A-Z/; $hexdata =~ s/(..)/$1 /g; $hexdata =~ s/ $//g; return $hexdata; } sub send_pdu { my $pdu = $_[0]; my $header = encode_msg_header( length($pdu) ); $socket->write($header); $socket->write($pdu); # my $hexdata = convert_to_hex($pdu); # print("SENT: [$hexdata]\n"); } sub csta_connect { my %args = %{ $_[0] }; open_csta_socket( $args{host}, $args{port} ); # A-ASSOCIATE Request my $pdu = "602380020780A10706052B0C00815ABE14281206072B0C00821D8148A007A0050303000800"; $pdu = pack( 'H*', $pdu ); send_pdu($pdu); # A-ASSOCIATE Result $pdu = receive_stuff(); my $hexdata = convert_to_hex($pdu); if ( $hexdata =~ /A2 03 02 01 01/ ) { print "rejected-permanent\n"; exit 0; } #SystemStatus Request $pdu = receive_stuff(); my $out = $CSTAapdu->decode($pdu); my $invokeID = $out->{svcRequest}->{invokeID}; SystemStatusResult($invokeID); } sub open_csta_socket { my $host = shift; my $port = shift; $socket = new IO::Socket::INET( PeerAddr => $host, PeerPort => $port, Blocking => 1, Proto => 'tcp' ) || die "Error creating socket: $!\n"; $socket->autoflush(1); print("opened a connection to $host on port $port\n"); } sub receive_stuff { my $header = ''; my $pdu = ''; my $nbytes = $socket->sysread( $header, 2 ); if ( $nbytes == 1 ) { # фрагмент пакета my $header2; my $nbytes2 = $socket->sysread( $header2, 1 ); $header = $header . $header2; $nbytes = 2; } croak "Didn't receive the specified amount of data (2 bytes)!\n" . chr(7) unless ( $nbytes == 2 ); my $len = decode_msg_header($header); $nbytes = $socket->sysread( $pdu, $len ); if ( $nbytes < $len ) { # фрагмент пакета my $pdu2; my $nbytes2 = $socket->sysread( $pdu2, $len - $nbytes ); $pdu = $pdu . $pdu2; $nbytes = $nbytes + $nbytes2; } croak "Didn't receive the specified amount of data ($len bytes)!\n" . chr(7) unless ( $nbytes == $len ); # my $hexdata = convert_to_hex($pdu); # print("RECEIVED:[$hexdata]\n"); return $pdu; } sub GetSystemData { my $DeviceCategory = shift; #EXT=5 #CO =2 my $pdu = $CSTAapdu->encode( { svcRequest => { invokeID => 4, serviceID => 51, serviceArgs => { privateData => { private => { kmeSystemData => { getSystemData => { request => { deviceList => { category => { standardDevice => $DeviceCategory } } } } } } } } } } ); #my $pdu = "A11602020602020133300DA40BA009A407A105A0030A010".$DeviceCategory; #$pdu = pack('H*', $pdu); send_pdu($pdu); # getSystemDataPosAck $pdu = receive_stuff(); my $out = $CSTAapdu->decode($pdu); my $crossRefID = $out->{svcResult}->{result}->{serviceResult}->{extensions} ->{privateData}[0]->{private}->{kmeSystemData}->{getSystemDataPosAck}; my $lastSegment = 0; # systemDataLinkedReply while ( !$lastSegment ) { $pdu = receive_stuff(); my $out = $CSTAapdu->decode($pdu); my $systemDataLinkedReply = $out->{svcRequest}->{serviceArgs}->{privateData}->{private} ->{kmeSystemData}->{systemDataLinkedReply}; if ( defined $systemDataLinkedReply ) { if ( $systemDataLinkedReply->{crossRefID} cmp $crossRefID ) { next } $lastSegment = $systemDataLinkedReply->{lastSegment}; foreach my $KmeDeviceStateEntry ( @{ $systemDataLinkedReply->{sysData}->{deviceList} } ) { if ( $KmeDeviceStateEntry->{status} == 0 ) { print "device:", $KmeDeviceStateEntry->{number}, "\n"; push @devices, ( $KmeDeviceStateEntry->{device}->{deviceIdentifier} ->{deviceNumber} ); } } } } } sub SnapshotDeviceRequest { my $device = shift; my $pdu = $CSTAapdu->encode( { svcRequest => { invokeID => 9, serviceID => 74, serviceArgs => { snapshotObject => { deviceIdentifier => { deviceNumber => $device } } } } } ); #$device = sprintf("%08X",$device); #print("SnapshotDeviceRequest: $device\n"); #my $pdu = "A11002010202014A300830068104".$device; #$pdu = pack('H*', $pdu); send_pdu($pdu); } sub SnapshotDeviceResult { my $refSnapshotDeviceResult = shift; my $refsnapshotData = ( $refSnapshotDeviceResult->{crossRefIDorSnapshotData}->{snapshotData} ); foreach my $SnapshotDeviceResponseInfo ( @{$refsnapshotData} ) { my $callID = $SnapshotDeviceResponseInfo->{connectionIdentifier}->{both}->{callID}; my $deviceID = deviceNumber( $SnapshotDeviceResponseInfo->{connectionIdentifier}->{both} ->{deviceID}->{staticID}->{deviceIdentifier} ); foreach my $LocalConnectionState ( @{ $SnapshotDeviceResponseInfo->{localCallState} ->{compoundCallState} } ) { my $ConnectionState = localConnectionInfo($LocalConnectionState); push @calls, [ $callID, $deviceID, $ConnectionState ]; } } } sub SystemStatusResult { my $invokeID = shift; my $pdu = $CSTAapdu->encode( { svcResult => { invokeID => $invokeID, result => { serviceID => 211, serviceResult => { noData => 1 } } } } ); send_pdu($pdu); # send SystemStatus Result } # parse ASN.1 desciptions my $asn = Convert::ASN1->new; #$asn->configure(tagdefault=>'EXPLICIT'); $asn->prepare(<error; CSTAapdu ::= CHOICE { svcRequest ROIVapdu, svcResult RORSapdu -- svcError ROERapdu, -- svcReject RORJapdu } ROIVapdu ::= [1] IMPLICIT SEQUENCE { invokeID INTEGER, serviceID INTEGER, serviceArgs ANY DEFINED BY serviceID } RORSapdu ::= [2] IMPLICIT SEQUENCE { invokeID INTEGER, result SEQUENCE { serviceID INTEGER, serviceResult ANY DEFINED BY serviceID OPTIONAL } } EscapeArgument ::= SEQUENCE { --escapeRegisterID EscapeRegisterID OPTIONAL, --security CSTASecurityData OPTIONAL, privateData CSTAPrivateData } CSTAPrivateData ::= CHOICE { string OCTET STRING, private KmeSpecificPrivateData } KmeSpecificPrivateData ::= CHOICE { kmeSystemData [4] KmeSystemData } KmeSystemData ::= CHOICE { getSystemData [0] KmeGetSystemData, systemDataLinkedReply [3] EXPLICIT KmeSystemDataLinkedReply, getSystemDataPosAck [4] EXPLICIT KmeGetSystemDataPosAck } KmeGetSystemData ::= CHOICE { request KmeGetSystemDataReq --! --result KmeGetSystemDataRsp } KmeGetSystemDataReq ::= CHOICE { deviceList [4] KmeRequestedDevice --! } KmeRequestedDevice ::= CHOICE -- for GetSystemData.deviceList { --device [0] DeviceID, category [1] KmeDeviceCategory} --! KmeDeviceCategory ::= CHOICE { standardDevice [0] EXPLICIT DeviceCategory--! -- kmeDevice [1] KmeOtherDevice } DeviceCategory ::= ENUMERATED { acd (0), group (1), networkInterface (2), --! park (3), routeingDevice (4), station (5), --! voiceUnit (6), other (7) } KmeSystemDataLinkedReply ::= SEQUENCE { crossRefID [0] EXPLICIT ServiceCrossRefID, segmentID [1] EXPLICIT INTEGER, lastSegment [2] EXPLICIT BOOLEAN, sysData [3] EXPLICIT KmeGetSystemDataRsp OPTIONAL } ServiceCrossRefID ::= OCTET STRING KmeGetSystemDataPosAck ::= ServiceCrossRefID KmeGetSystemDataRsp ::= SEQUENCE { deviceList [38] EXPLICIT KmeDeviceStateList OPTIONAL } KmeDeviceStateList ::= SEQUENCE OF KmeDeviceStateEntry KmeDeviceStateEntry ::= SEQUENCE { device DeviceID, number IA5String OPTIONAL, -- Ext No, CO No, Park Area No. status KmeDeviceState } KmeDeviceState ::= ENUMERATED { ins (0), ous (1) } EscapeResult ::= CHOICE { extensions CSTACommonArguments, noData NULL } CSTACommonArguments ::= [APPLICATION 30] IMPLICIT SEQUENCE { privateData [1] IMPLICIT SEQUENCE OF CSTAPrivateData OPTIONAL } CSTAPrivateData ::= CHOICE { string OCTET STRING, private KmeSpecificPrivateData } --snapshotDevice OPERATION ::= --{ ARGUMENT SnapshotDeviceArgument, -- RESULT SnapshotDeviceResult -- ERRORS {universalFailure} -- CODE local: 74} SnapshotDeviceArgument ::= SEQUENCE { snapshotObject DeviceID} SnapshotDeviceResult ::= SEQUENCE { crossRefIDorSnapshotData CHOICE { serviceCrossRefID ServiceCrossRefID, snapshotData SnapshotDeviceData } } ServiceCrossRefID ::= OCTET STRING SnapshotDeviceData ::= [APPLICATION 22] IMPLICIT SEQUENCE OF SnapshotDeviceResponseInfo SnapshotDeviceResponseInfo ::= SEQUENCE { connectionIdentifier ConnectionID, localCallState CallState} ConnectionID ::= [APPLICATION 11] CHOICE { deviceID [1] LocalDeviceID, both SEQUENCE { callID [0] IMPLICIT CallID, deviceID [1] LocalDeviceID } } CallID ::= OCTET STRING LocalDeviceID ::= CHOICE { staticID DeviceID} DeviceID ::= SEQUENCE { deviceIdentifier CHOICE { dialingNumber [0] IMPLICIT NumberDigits, deviceNumber [1] IMPLICIT DeviceNumber, other [6] IMPLICIT OtherPlan } } OtherPlan ::= OCTET STRING NumberDigits ::= IA5String DeviceNumber ::= INTEGER CallState ::= CHOICE { compoundCallState [0] IMPLICIT CompoundCallState} CompoundCallState ::= SEQUENCE OF LocalConnectionState LocalConnectionState ::= [APPLICATION 14] IMPLICIT ENUMERATED { null (0), initiated (1), alerting (2), connected (3), hold (4), queued (5), fail (6) } systemStatus ::= CHOICE { ARGUMENT SystemStatusArg, RESULT SystemStatusRes -- ERRORS {universalFailure} -- CODE local: 211 } SystemStatusArg ::= SEQUENCE { systemStatus SystemStatus} SystemStatusRes ::= CHOICE { noData NULL} SystemStatus ::= ENUMERATED { normal (2), messageLost (3), overloadReached (6) } ASN1 my %serviceArgs = ( 51 => 'EscapeArgument', 74 => 'SnapshotDeviceArgument', 211 => 'SystemStatusArg' ); foreach ( keys %serviceArgs ) { $asn->registertype( 'serviceArgs', $_, $asn->find( $serviceArgs{$_} ) ); } my %serviceResults = ( 51 => 'EscapeResult', 74 => 'SnapshotDeviceResult', 211 => 'SystemStatusRes' ); foreach ( keys %serviceResults ) { $asn->registertype( 'serviceResult', $_, $asn->find( $serviceResults{$_} ) ); } $CSTAapdu = $asn->find('CSTAapdu'); csta_connect( { 'host' => '192.168.240.20', 'port' => 33333 } ); GetSystemData(2); GetSystemData(5); my $maxCO = 0; while (1) { foreach my $number (@devices) { SnapshotDeviceRequest($number); } undef @calls; my $alldevices = $#devices + 1; while ($alldevices) { my $pdu = receive_stuff(); my $out = $CSTAapdu->decode($pdu); if ( defined $out->{svcRequest} ) { my $serviceID = $out->{svcRequest}->{serviceID}; my $invokeID = $out->{svcRequest}->{invokeID}; if ( $serviceID == 211 ) { # if SystemStatus Request SystemStatusResult($invokeID); } } elsif ( defined $out->{svcResult} ) { my $serviceResult = $out->{svcResult}->{result}->{serviceResult}; my $serviceID = $out->{svcResult}->{result}->{serviceID}; if ( $serviceID == 74 ) { SnapshotDeviceResult($serviceResult); $alldevices = $alldevices - 1; } } } my $CO = 0; for (@calls) { $CO++ if $_->[1] =~ /^CO/; } if ( $maxCO < $CO ) { $maxCO = $CO; print chr(7); } #system('CLS'); # for windows, 'clear' for linux print strftime( "%H:%M:%S ", localtime ), "maxCO:", $maxCO, " CO:", $CO, "\n"; print "callID: deviceID: CallState:\n"; printf( "%-8s%-10s%-10s\n", $_->[0], $_->[1], $_->[2] ) for sort { $a->[0] cmp $b->[0] } @calls; sleep 1; }