| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package CDR::Parser::SI3000; |
|
2
|
|
|
|
|
|
|
|
|
3
|
2
|
|
|
2
|
|
41523
|
use 5.10.0; |
|
|
2
|
|
|
|
|
7
|
|
|
4
|
2
|
|
|
2
|
|
10
|
use strict; |
|
|
2
|
|
|
|
|
3
|
|
|
|
2
|
|
|
|
|
47
|
|
|
5
|
2
|
|
|
2
|
|
9
|
use warnings FATAL => 'all'; |
|
|
2
|
|
|
|
|
11
|
|
|
|
2
|
|
|
|
|
77
|
|
|
6
|
2
|
|
|
2
|
|
1521
|
use IO::File (); |
|
|
2
|
|
|
|
|
19973
|
|
|
|
2
|
|
|
|
|
6958
|
|
|
7
|
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
=head1 NAME |
|
9
|
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
CDR::Parser::SI3000 - parser for binary CDR files (*.ama) produced by Iskratel SI3000 MSCN telephony product |
|
11
|
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
CDR = Call Detail Records |
|
13
|
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
=head1 VERSION |
|
15
|
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
Version 0.01 |
|
17
|
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
=cut |
|
19
|
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
our $VERSION = '0.03'; |
|
21
|
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
our $VERBOSE = 0; |
|
23
|
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
25
|
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
Whis module parses the binary file format and returns it as Perl data. |
|
27
|
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
Usage example |
|
29
|
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
use CDR::Parser::SI3000; |
|
31
|
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
my ($cdr_list, $num_failed) = CDR::Parser::SI3000->parse_file('somefile.ama'); |
|
33
|
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
There |
|
35
|
|
|
|
|
|
|
$cdr_list is a array-reference containing individual records as hash-ref. |
|
36
|
|
|
|
|
|
|
$num_failed is a number of unparseable records |
|
37
|
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
=head1 SUBROUTINES/METHODS |
|
40
|
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
=head2 parse_file |
|
42
|
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
Get filename as input, open it, read it, returns the parsed result |
|
44
|
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
=cut |
|
46
|
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
#-- |
|
49
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
# $| = 1; |
|
51
|
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
sub _log { |
|
53
|
931
|
|
|
931
|
|
2138
|
my($format, @args) = @_; |
|
54
|
931
|
50
|
|
|
|
3109
|
return if(! $VERBOSE); |
|
55
|
0
|
|
|
|
|
0
|
printf $format."\n", @args; |
|
56
|
|
|
|
|
|
|
} |
|
57
|
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
# public |
|
59
|
|
|
|
|
|
|
sub parse_file { |
|
60
|
1
|
|
|
1
|
1
|
14
|
my($class, $filename) = @_; |
|
61
|
1
|
50
|
|
|
|
5
|
die "No filename argument" if(! $filename); |
|
62
|
1
|
|
|
|
|
5
|
_log('Parsing file %s', $filename); |
|
63
|
|
|
|
|
|
|
|
|
64
|
1
|
|
50
|
|
|
8
|
my $fh = IO::File->new($filename) || die "Failed to open $filename - $!"; |
|
65
|
1
|
|
|
|
|
99
|
binmode($fh, ':bytes'); |
|
66
|
|
|
|
|
|
|
|
|
67
|
1
|
|
|
|
|
3
|
my @records = (); |
|
68
|
|
|
|
|
|
|
|
|
69
|
1
|
|
|
|
|
2
|
my $rows = 0; |
|
70
|
1
|
|
|
|
|
3
|
my $failed = 0; |
|
71
|
|
|
|
|
|
|
|
|
72
|
1
|
|
|
|
|
2
|
while(1) { |
|
73
|
16
|
|
|
|
|
35
|
my $call = parse_record($fh); |
|
74
|
16
|
100
|
|
|
|
39
|
if($call) { |
|
75
|
15
|
|
|
|
|
30
|
push @records, $call; |
|
76
|
|
|
|
|
|
|
} |
|
77
|
|
|
|
|
|
|
else { |
|
78
|
1
|
50
|
|
|
|
6
|
last if($call == 0); |
|
79
|
0
|
|
|
|
|
0
|
$failed++; |
|
80
|
|
|
|
|
|
|
} |
|
81
|
|
|
|
|
|
|
} |
|
82
|
|
|
|
|
|
|
|
|
83
|
1
|
|
|
|
|
12
|
$fh->close; |
|
84
|
|
|
|
|
|
|
|
|
85
|
1
|
|
|
|
|
20
|
return (\@records, $failed); |
|
86
|
|
|
|
|
|
|
} |
|
87
|
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
#------ private implementation ------ |
|
89
|
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
# 100. Called number |
|
91
|
|
|
|
|
|
|
sub block_100 { |
|
92
|
15
|
|
|
15
|
0
|
46
|
my($call,$variable) = @_; |
|
93
|
|
|
|
|
|
|
|
|
94
|
15
|
|
|
|
|
31
|
_log('100. Called number'); |
|
95
|
15
|
|
|
|
|
23
|
my $cld_len; |
|
96
|
15
|
|
|
|
|
72
|
($cld_len, $$variable) = unpack('C a*', $$variable); |
|
97
|
15
|
|
|
|
|
41
|
my $cut = $cld_len; |
|
98
|
15
|
50
|
|
|
|
54
|
$cut++ if($cld_len % 2 == 1); |
|
99
|
15
|
|
|
|
|
20
|
my $cld; |
|
100
|
15
|
|
|
|
|
78
|
($cld, $$variable) = unpack("H$cut a*", $$variable); |
|
101
|
15
|
50
|
|
|
|
56
|
if($cut > $cld_len) { |
|
102
|
15
|
|
|
|
|
35
|
$cld = substr($cld, 0, -1); |
|
103
|
|
|
|
|
|
|
} |
|
104
|
15
|
|
|
|
|
36
|
_log(' CLD: %s', $cld); |
|
105
|
15
|
|
|
|
|
93
|
$call->{cld} = $cld; |
|
106
|
|
|
|
|
|
|
} |
|
107
|
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
# 101. Call accepting party number |
|
109
|
|
|
|
|
|
|
# 102. Start Date and Time |
|
110
|
|
|
|
|
|
|
sub block_102 { |
|
111
|
15
|
|
|
15
|
0
|
29
|
my($call, $var) = @_; |
|
112
|
15
|
|
|
|
|
32
|
_log("102. Start Date and Time"); |
|
113
|
15
|
|
|
|
|
23
|
my($year,$month,$day,$hour,$min,$sec,$msec,$reserved); |
|
114
|
15
|
|
|
|
|
166
|
($year,$month,$day,$hour,$min,$sec,$msec,$reserved,$$var) = unpack('CCCCCCC H2 a*', $$var); |
|
115
|
15
|
|
|
|
|
56
|
$year += 2000; |
|
116
|
15
|
|
|
|
|
86
|
my $start_time = sprintf "%04d-%02d-%02d %02d:%02d:%02d.%02d", $year,$month,$day,$hour,$min,$sec,$msec; |
|
117
|
15
|
|
|
|
|
34
|
_log(' Start Time: %s', $start_time); |
|
118
|
15
|
|
|
|
|
116
|
$call->{start_time} = $start_time; |
|
119
|
|
|
|
|
|
|
} |
|
120
|
|
|
|
|
|
|
# 103. End Date and Time |
|
121
|
|
|
|
|
|
|
sub block_103 { |
|
122
|
15
|
|
|
15
|
0
|
31
|
my($call, $var) = @_; |
|
123
|
15
|
|
|
|
|
25
|
_log("103. End Date and Time"); |
|
124
|
15
|
|
|
|
|
25
|
my($year,$month,$day,$hour,$min,$sec,$msec,$reliable); |
|
125
|
15
|
|
|
|
|
135
|
($year,$month,$day,$hour,$min,$sec,$msec,$reliable,$$var) = unpack('CCCCCCC H2 a*', $$var); |
|
126
|
15
|
|
|
|
|
54
|
$year += 2000; |
|
127
|
15
|
|
|
|
|
79
|
my $end_time = sprintf "%04d-%02d-%02d %02d:%02d:%02d.%02d", $year,$month,$day,$hour,$min,$sec,$msec; |
|
128
|
15
|
|
|
|
|
40
|
_log(' End Time: %s', $end_time); |
|
129
|
15
|
|
|
|
|
103
|
$call->{end_time} = $end_time; |
|
130
|
|
|
|
|
|
|
} |
|
131
|
|
|
|
|
|
|
# 104. Number of charging units |
|
132
|
|
|
|
|
|
|
sub block_104 { |
|
133
|
15
|
|
|
15
|
0
|
30
|
my($call, $var) = @_; |
|
134
|
15
|
|
|
|
|
33
|
_log("104. Number of charging units"); |
|
135
|
15
|
|
|
|
|
28
|
my($unit1,$unit2,$unit3); |
|
136
|
15
|
|
|
|
|
89
|
($unit1,$unit2,$unit3,$$var) = unpack('CCC a*', $$var); |
|
137
|
15
|
|
|
|
|
64
|
my $units = ($unit1 << 16) + ($unit2 << 8) + $unit3; |
|
138
|
15
|
|
|
|
|
34
|
_log(' Units: %d', $units); |
|
139
|
15
|
|
|
|
|
91
|
$call->{charging_units} = $units; |
|
140
|
|
|
|
|
|
|
} |
|
141
|
|
|
|
|
|
|
# 105. Basic service |
|
142
|
|
|
|
|
|
|
sub block_105 { |
|
143
|
15
|
|
|
15
|
0
|
28
|
my($call, $var) = @_; |
|
144
|
15
|
|
|
|
|
31
|
_log("105. Basic service"); |
|
145
|
15
|
|
|
|
|
66
|
my %bearer_label = ( |
|
146
|
|
|
|
|
|
|
0 => '64 kbit/s for speech information transfer', |
|
147
|
|
|
|
|
|
|
8 => '64 kbit/s unrestricted', |
|
148
|
|
|
|
|
|
|
16 => '64 kbit/s for 3.1kHz audio information transfer', |
|
149
|
|
|
|
|
|
|
); |
|
150
|
15
|
|
|
|
|
32
|
my %service_label = ( |
|
151
|
|
|
|
|
|
|
1 => 'Telephony', |
|
152
|
|
|
|
|
|
|
# .... TODO |
|
153
|
|
|
|
|
|
|
); |
|
154
|
15
|
|
|
|
|
19
|
my($bearer,$service); |
|
155
|
15
|
|
|
|
|
76
|
($bearer,$service,$$var) = unpack('C C a*', $$var); |
|
156
|
15
|
|
50
|
|
|
74
|
_log(" Bearer: %d / %s", $bearer, $bearer_label{ $bearer } // 'UNKNOWN'); |
|
157
|
15
|
|
50
|
|
|
55
|
_log(" Service: %d / %s", $service, $service_label{ $service } // 'UNKNOWN'); |
|
158
|
15
|
|
50
|
|
|
55
|
$call->{bearer} = $bearer_label{ $bearer } // 'UNKNOWN'; |
|
159
|
15
|
|
|
|
|
45
|
$call->{bearer_code} = $bearer; |
|
160
|
15
|
|
50
|
|
|
48
|
$call->{service} = $service_label{ $service } // 'UNKNOWN'; |
|
161
|
15
|
|
|
|
|
109
|
$call->{service_code} = $service; |
|
162
|
|
|
|
|
|
|
} |
|
163
|
|
|
|
|
|
|
# 106. Supplementary service used by calling subscriber |
|
164
|
|
|
|
|
|
|
sub block_106 { |
|
165
|
0
|
|
|
0
|
0
|
0
|
my($call, $var) = @_; |
|
166
|
0
|
|
|
|
|
0
|
_log('106. Supplementary service used by calling subscriber'); |
|
167
|
0
|
|
|
|
|
0
|
my($sup_service); |
|
168
|
0
|
|
|
|
|
0
|
($sup_service, $$var) = unpack('C a*', $$var); |
|
169
|
0
|
|
|
|
|
0
|
_log(' Supplementary calling service: %d', $sup_service); |
|
170
|
0
|
|
|
|
|
0
|
$call->{supplementary_calling_service} = $sup_service; |
|
171
|
|
|
|
|
|
|
} |
|
172
|
|
|
|
|
|
|
# 107. Supplementary service used by called subscriber |
|
173
|
|
|
|
|
|
|
sub block_107 { |
|
174
|
0
|
|
|
0
|
0
|
0
|
my($call, $var) = @_; |
|
175
|
0
|
|
|
|
|
0
|
_log('106. Supplementary service used by called subscriber'); |
|
176
|
0
|
|
|
|
|
0
|
my($sup_service); |
|
177
|
0
|
|
|
|
|
0
|
($sup_service, $$var) = unpack('C a*', $$var); |
|
178
|
0
|
|
|
|
|
0
|
_log(' Supplementary called service: %d', $sup_service); |
|
179
|
0
|
|
|
|
|
0
|
$call->{supplementary_called_service} = $sup_service; |
|
180
|
|
|
|
|
|
|
} |
|
181
|
|
|
|
|
|
|
# 108. Subscriber controlled input |
|
182
|
|
|
|
|
|
|
# 109. Dialed digits |
|
183
|
|
|
|
|
|
|
# 110. Origin category |
|
184
|
|
|
|
|
|
|
sub block_110 { |
|
185
|
15
|
|
|
15
|
0
|
30
|
my($call, $var) = @_; |
|
186
|
15
|
|
|
|
|
34
|
_log("110. Origin category"); |
|
187
|
15
|
|
|
|
|
25
|
my($origin); |
|
188
|
15
|
|
|
|
|
65
|
($origin,$$var) = unpack('C a*', $$var); |
|
189
|
15
|
|
|
|
|
43
|
_log(" Origin category: %d", $origin); |
|
190
|
15
|
|
|
|
|
84
|
$call->{origin_category} = $origin; |
|
191
|
|
|
|
|
|
|
} |
|
192
|
|
|
|
|
|
|
# 111. Tariff direction |
|
193
|
|
|
|
|
|
|
sub block_111 { |
|
194
|
15
|
|
|
15
|
0
|
25
|
my($call, $var) = @_; |
|
195
|
15
|
|
|
|
|
32
|
_log("111. Tariff direction"); |
|
196
|
15
|
|
|
|
|
24
|
my($tariff); |
|
197
|
15
|
|
|
|
|
67
|
($tariff,$$var) = unpack('C a*', $$var); |
|
198
|
15
|
|
|
|
|
43
|
_log(" Tariff direction: %d", $tariff); |
|
199
|
15
|
|
|
|
|
106
|
$call->{tariff_direction} = $tariff; |
|
200
|
|
|
|
|
|
|
} |
|
201
|
|
|
|
|
|
|
# 112. Call failure cause |
|
202
|
|
|
|
|
|
|
# 113. Incoming trunk data |
|
203
|
|
|
|
|
|
|
sub block_113 { |
|
204
|
15
|
|
|
15
|
0
|
29
|
my($call, $var) = @_; |
|
205
|
15
|
|
|
|
|
30
|
_log("113. Incoming trunk data"); |
|
206
|
15
|
|
|
|
|
21
|
my($group,$id,$shelf,$port,$channel); |
|
207
|
15
|
|
|
|
|
123
|
($group,$id,$shelf,$port,$channel,$$var) = unpack('n n C n C a*', $$var); |
|
208
|
15
|
|
|
|
|
54
|
_log(" Trunk group: %d\n Trunk identification: %d\n Shelf identification: %d\n Port identification: %d\n Channel identification: %d", |
|
209
|
|
|
|
|
|
|
$group,$id,$shelf,$port,$channel); |
|
210
|
15
|
|
|
|
|
37
|
$call->{incoming_trunk_group} = $group; |
|
211
|
15
|
|
|
|
|
36
|
$call->{incoming_trunk} = $id; |
|
212
|
15
|
|
|
|
|
34
|
$call->{incoming_shelf} = $shelf; |
|
213
|
15
|
|
|
|
|
34
|
$call->{incoming_port} = $port; |
|
214
|
15
|
|
|
|
|
89
|
$call->{incoming_channel} = $channel; |
|
215
|
|
|
|
|
|
|
} |
|
216
|
|
|
|
|
|
|
# 114. Outgoing trunk data |
|
217
|
|
|
|
|
|
|
sub block_114 { |
|
218
|
15
|
|
|
15
|
0
|
30
|
my($call, $var) = @_; |
|
219
|
15
|
|
|
|
|
31
|
_log("114. Outgoing trunk data"); |
|
220
|
15
|
|
|
|
|
22
|
my($group,$id,$shelf,$port,$channel); |
|
221
|
15
|
|
|
|
|
137
|
($group,$id,$shelf,$port,$channel,$$var) = unpack('n n C n C a*', $$var); |
|
222
|
15
|
|
|
|
|
54
|
_log(" Trunk group: %d\n Trunk identification: %d\n Shelf identification: %d\n Port identification: %d\n Channel identification: %d", |
|
223
|
|
|
|
|
|
|
$group,$id,$shelf,$port,$channel); |
|
224
|
15
|
|
|
|
|
41
|
$call->{outgoing_trunk_group} = $group; |
|
225
|
15
|
|
|
|
|
33
|
$call->{outgoing_trunk} = $id; |
|
226
|
15
|
|
|
|
|
36
|
$call->{outgoing_shelf} = $shelf; |
|
227
|
15
|
|
|
|
|
33
|
$call->{outgoing_port} = $port; |
|
228
|
15
|
|
|
|
|
91
|
$call->{outgoing_channel} = $channel; |
|
229
|
|
|
|
|
|
|
} |
|
230
|
|
|
|
|
|
|
# 115. Call duration |
|
231
|
|
|
|
|
|
|
sub block_115 { |
|
232
|
15
|
|
|
15
|
0
|
29
|
my($call, $var) = @_; |
|
233
|
15
|
|
|
|
|
29
|
_log("115. Call duration"); |
|
234
|
15
|
|
|
|
|
24
|
my($duration); |
|
235
|
15
|
|
|
|
|
63
|
($duration,$$var) = unpack('N a*', $$var); |
|
236
|
15
|
|
|
|
|
57
|
_log(" Call duration: %d msec / %.5f sec", $duration, $duration / 1000.0); |
|
237
|
15
|
|
|
|
|
119
|
$call->{call_duration} = sprintf '%.5f', $duration / 1000.0; |
|
238
|
15
|
|
|
|
|
86
|
$call->{call_duration_ms} = $duration; |
|
239
|
|
|
|
|
|
|
} |
|
240
|
|
|
|
|
|
|
# 116. Checksum |
|
241
|
|
|
|
|
|
|
sub block_116 { |
|
242
|
15
|
|
|
15
|
0
|
31
|
my($call, $var) = @_; |
|
243
|
15
|
|
|
|
|
29
|
_log("116. Checksum"); |
|
244
|
15
|
|
|
|
|
25
|
my($len,$checksum); |
|
245
|
15
|
|
|
|
|
81
|
($len,$checksum,$$var) = unpack('C a2 a*', $$var); |
|
246
|
15
|
|
|
|
|
54
|
_log(" Checksum: 0x%s", unpack('H*', $checksum));; |
|
247
|
15
|
|
|
|
|
94
|
$call->{checksum} = unpack('H*', $checksum); |
|
248
|
|
|
|
|
|
|
} |
|
249
|
|
|
|
|
|
|
# 117. Business and Centrex group ID |
|
250
|
|
|
|
|
|
|
sub block_117 { |
|
251
|
0
|
|
|
0
|
0
|
0
|
my($call, $var) = @_; |
|
252
|
0
|
|
|
|
|
0
|
_log("117. Business and Centrex group ID"); |
|
253
|
0
|
|
|
|
|
0
|
my($len,$business,$centrex); |
|
254
|
0
|
|
|
|
|
0
|
($len,$business,$centrex,$$var) = unpack('C N N a*', $$var); |
|
255
|
0
|
|
|
|
|
0
|
_log(" Business group ID: %d", $business); |
|
256
|
0
|
|
|
|
|
0
|
_log(" Centrex group ID: %d", $centrex); |
|
257
|
0
|
|
|
|
|
0
|
$call->{business_group} = $business; |
|
258
|
0
|
|
|
|
|
0
|
$call->{centrex_group} = $centrex; |
|
259
|
|
|
|
|
|
|
} |
|
260
|
|
|
|
|
|
|
# 118. Carrier access code |
|
261
|
|
|
|
|
|
|
# 119. Original calling party number |
|
262
|
|
|
|
|
|
|
sub block_119 { |
|
263
|
0
|
|
|
0
|
0
|
0
|
my($call, $var) = @_; |
|
264
|
0
|
|
|
|
|
0
|
_log("119. Original calling party number"); |
|
265
|
0
|
|
|
|
|
0
|
my($len,$num_len); |
|
266
|
0
|
|
|
|
|
0
|
($len,$num_len,$$var) = unpack('C C a*', $$var); |
|
267
|
0
|
|
|
|
|
0
|
my $cut = $num_len; |
|
268
|
0
|
0
|
|
|
|
0
|
$cut++ if($num_len %2 == 1); |
|
269
|
0
|
|
|
|
|
0
|
my $num; |
|
270
|
0
|
|
|
|
|
0
|
($num,$$var) = unpack("H$cut a*", $$var); |
|
271
|
0
|
0
|
|
|
|
0
|
if($cut > $num_len) { |
|
272
|
0
|
|
|
|
|
0
|
$num = substr($num, 0, -1); |
|
273
|
|
|
|
|
|
|
} |
|
274
|
0
|
|
|
|
|
0
|
_log(" Original CLI: %s", $num); |
|
275
|
0
|
|
|
|
|
0
|
$call->{original_cli} = $num; |
|
276
|
|
|
|
|
|
|
} |
|
277
|
|
|
|
|
|
|
# 120. Prepaid account recharge data |
|
278
|
|
|
|
|
|
|
# 121. Call release cause |
|
279
|
|
|
|
|
|
|
sub block_121 { |
|
280
|
15
|
|
|
15
|
0
|
29
|
my($call, $var) = @_; |
|
281
|
15
|
|
|
|
|
28
|
_log("121. Call release cause"); |
|
282
|
15
|
|
|
|
|
51
|
my %cause_label = ( |
|
283
|
|
|
|
|
|
|
16 => 'normal call clearing', |
|
284
|
|
|
|
|
|
|
41 => 'temporary failure', |
|
285
|
|
|
|
|
|
|
); |
|
286
|
15
|
|
|
|
|
31
|
my %coding_label = ( |
|
287
|
|
|
|
|
|
|
0 => 'ITU-T standard', |
|
288
|
|
|
|
|
|
|
); |
|
289
|
15
|
|
|
|
|
72
|
my %location_label = ( |
|
290
|
|
|
|
|
|
|
0 => 'user', |
|
291
|
|
|
|
|
|
|
1 => 'private network serving the local user', |
|
292
|
|
|
|
|
|
|
2 => 'public network serving the local user', |
|
293
|
|
|
|
|
|
|
3 => 'transit network', |
|
294
|
|
|
|
|
|
|
4 => 'public network serving the remote user', |
|
295
|
|
|
|
|
|
|
5 => 'private network serving the remote user', |
|
296
|
|
|
|
|
|
|
7 => 'international network', |
|
297
|
|
|
|
|
|
|
10 => 'network beyond interworking point', |
|
298
|
|
|
|
|
|
|
); |
|
299
|
15
|
|
|
|
|
18
|
my($len,$cause,$flag); |
|
300
|
15
|
|
|
|
|
90
|
($len,$cause,$flag,$$var) = unpack('C n C a*', $$var); |
|
301
|
15
|
|
|
|
|
54
|
my $coding = (($flag & 0xF0) >> 4); |
|
302
|
15
|
|
|
|
|
32
|
my $location = ($flag & 0x0F); |
|
303
|
15
|
|
50
|
|
|
60
|
_log(" Cause: %d / %s", $cause, $cause_label{ $cause } // 'UNKNOWN');; |
|
304
|
15
|
|
50
|
|
|
58
|
_log(" Coding standard: %d / %s", $coding, $coding_label{ $coding } // 'UNKNOWN'); |
|
305
|
15
|
|
50
|
|
|
65
|
_log(" Location: %d / %s", $location, $location_label{ $location } // 'UNKNOWN'); |
|
306
|
15
|
|
50
|
|
|
55
|
$call->{call_release_cause} = $cause_label{ $cause } // 'UNKNOWN'; |
|
307
|
15
|
|
|
|
|
39
|
$call->{call_release_cause_code} = $cause; |
|
308
|
15
|
|
50
|
|
|
53
|
$call->{call_coding_standard} = $coding_label{ $coding } // 'UNKNOWN'; |
|
309
|
15
|
|
|
|
|
73
|
$call->{call_coding_standard_code} = $coding; |
|
310
|
15
|
|
50
|
|
|
54
|
$call->{call_location} = $location_label{ $location } // 'UNKNOWN'; |
|
311
|
15
|
|
|
|
|
126
|
$call->{call_location_code} = $location; |
|
312
|
|
|
|
|
|
|
} |
|
313
|
|
|
|
|
|
|
# 122. CBNO (Charge Band Number) |
|
314
|
|
|
|
|
|
|
# 123. Common call ID |
|
315
|
|
|
|
|
|
|
# 124. Durations before answer |
|
316
|
|
|
|
|
|
|
# 125. VoIP Info (old) |
|
317
|
|
|
|
|
|
|
# 126. Amount of Transferred Data (old) |
|
318
|
|
|
|
|
|
|
# 127. IP Address |
|
319
|
|
|
|
|
|
|
sub block_127 { |
|
320
|
15
|
|
|
15
|
0
|
27
|
my($call, $var) = @_; |
|
321
|
15
|
|
|
|
|
29
|
_log("127. IP Address"); |
|
322
|
15
|
|
|
|
|
31
|
my($len,$ip_data); |
|
323
|
15
|
|
|
|
|
61
|
($len,$$var) = unpack('C a*', $$var); |
|
324
|
15
|
|
|
|
|
44
|
$len -= 2; |
|
325
|
15
|
|
|
|
|
79
|
($ip_data,$$var) = unpack("a$len a*", $$var); |
|
326
|
15
|
|
|
|
|
36
|
my($flag,$reserved,@ip); |
|
327
|
15
|
|
|
|
|
67
|
($flag,$reserved,@ip) = unpack('C C N*', $ip_data); |
|
328
|
15
|
|
|
|
|
32
|
@ip = map { join '.', unpack 'C4', pack 'N', $_ } @ip; |
|
|
30
|
|
|
|
|
220
|
|
|
329
|
15
|
50
|
|
|
|
61
|
if($flag & 0x1) { |
|
330
|
15
|
|
|
|
|
34
|
my $ip = shift(@ip); |
|
331
|
15
|
|
|
|
|
37
|
_log(" Origin side remote RTP IP: %s", $ip); |
|
332
|
15
|
|
|
|
|
46
|
$call->{origin_remote_rtp} = $ip; |
|
333
|
|
|
|
|
|
|
} |
|
334
|
15
|
50
|
|
|
|
47
|
if($flag & 0x2) { |
|
335
|
0
|
|
|
|
|
0
|
my $ip = shift(@ip); |
|
336
|
0
|
|
|
|
|
0
|
_log(" Origin side local RTP IP: %s", $ip); |
|
337
|
0
|
|
|
|
|
0
|
$call->{origin_local_rtp} = $ip; |
|
338
|
|
|
|
|
|
|
} |
|
339
|
15
|
50
|
|
|
|
47
|
if($flag & 0x4) { |
|
340
|
15
|
|
|
|
|
32
|
my $ip = shift(@ip); |
|
341
|
15
|
|
|
|
|
36
|
_log(" Terminating side remote RTP IP: %s", $ip); |
|
342
|
15
|
|
|
|
|
45
|
$call->{terminating_remote_rtp} = $ip; |
|
343
|
|
|
|
|
|
|
} |
|
344
|
15
|
50
|
|
|
|
50
|
if($flag & 0x8) { |
|
345
|
0
|
|
|
|
|
0
|
my $ip = shift(@ip); |
|
346
|
0
|
|
|
|
|
0
|
_log(" Terminating side local RTP IP: %s", $ip); |
|
347
|
0
|
|
|
|
|
0
|
$call->{terminating_local_rtp} = $ip; |
|
348
|
|
|
|
|
|
|
} |
|
349
|
15
|
50
|
|
|
|
44
|
if($flag & 0x10) { |
|
350
|
0
|
|
|
|
|
0
|
my $ip = shift(@ip); |
|
351
|
0
|
|
|
|
|
0
|
_log(' Origin side remote signaling IP: %s', $ip); |
|
352
|
0
|
|
|
|
|
0
|
$call->{origin_remote_signaling} = $ip; |
|
353
|
|
|
|
|
|
|
} |
|
354
|
15
|
50
|
|
|
|
47
|
if($flag & 0x20) { |
|
355
|
0
|
|
|
|
|
0
|
my $ip = shift(@ip); |
|
356
|
0
|
|
|
|
|
0
|
_log(' Origin side local signaling IP: %s', $ip); |
|
357
|
0
|
|
|
|
|
0
|
$call->{origin_local_signaling} = $ip; |
|
358
|
|
|
|
|
|
|
} |
|
359
|
15
|
50
|
|
|
|
42
|
if($flag & 0x40) { |
|
360
|
0
|
|
|
|
|
0
|
my $ip = shift(@ip); |
|
361
|
0
|
|
|
|
|
0
|
_log(' Terminating side remote signaling IP: %s', $ip); |
|
362
|
0
|
|
|
|
|
0
|
$call->{terminating_remote_signaling} = $ip; |
|
363
|
|
|
|
|
|
|
} |
|
364
|
15
|
50
|
|
|
|
113
|
if($flag & 0x80) { |
|
365
|
0
|
|
|
|
|
0
|
my $ip = shift(@ip); |
|
366
|
0
|
|
|
|
|
0
|
_log(' Terminating side local signaling IP: %s', $ip); |
|
367
|
0
|
|
|
|
|
0
|
$call->{terminating_local_signaling} = $ip; |
|
368
|
|
|
|
|
|
|
} |
|
369
|
|
|
|
|
|
|
} |
|
370
|
|
|
|
|
|
|
# 128. VoIP info |
|
371
|
|
|
|
|
|
|
sub block_128 { |
|
372
|
15
|
|
|
15
|
0
|
29
|
my($call, $var) = @_; |
|
373
|
15
|
|
|
|
|
31
|
_log("128. VoIP info"); |
|
374
|
15
|
|
|
|
|
20
|
my($len,$rx_codec,$tx_codec,$rx_period,$tx_period,$rx_bandwidth,$tx_bandwidth,$max_jitter,$flag); |
|
375
|
15
|
|
|
|
|
205
|
($len,$rx_codec,$tx_codec,$rx_period,$tx_period,$rx_bandwidth,$tx_bandwidth,$max_jitter,$flag,$$var) = unpack('CCCCCnnnC a*', $$var); |
|
376
|
15
|
|
|
|
|
139
|
my %codec_label = ( |
|
377
|
|
|
|
|
|
|
0 => 'Undefined', |
|
378
|
|
|
|
|
|
|
8 => 'G711Alaw64k', |
|
379
|
|
|
|
|
|
|
9 => 'G711Ulaw64k', |
|
380
|
|
|
|
|
|
|
66 => 'G728', |
|
381
|
|
|
|
|
|
|
67 => 'G729', |
|
382
|
|
|
|
|
|
|
68 => 'G729annexA', |
|
383
|
|
|
|
|
|
|
70 => 'G729wAnnexB', |
|
384
|
|
|
|
|
|
|
71 => 'G729AnnexAwAnnexB', |
|
385
|
|
|
|
|
|
|
72 => 'GsmFullRate', |
|
386
|
|
|
|
|
|
|
80 => 'G7231A5_3k', |
|
387
|
|
|
|
|
|
|
81 => 'G7231A6_3k', |
|
388
|
|
|
|
|
|
|
129 => 'FaxT38', |
|
389
|
|
|
|
|
|
|
); |
|
390
|
15
|
|
|
|
|
38
|
my %side_label = ( 0 => 'origin side', 1 => 'terminating side'); |
|
391
|
15
|
|
|
|
|
38
|
my %type_label = ( |
|
392
|
|
|
|
|
|
|
0 => 'Undefined', |
|
393
|
|
|
|
|
|
|
1 => 'Audio', |
|
394
|
|
|
|
|
|
|
2 => 'Data', |
|
395
|
|
|
|
|
|
|
3 => 'Fax', |
|
396
|
|
|
|
|
|
|
); |
|
397
|
|
|
|
|
|
|
|
|
398
|
15
|
|
50
|
|
|
58
|
_log(" Rx codec: %d / %s", $rx_codec, $codec_label{ $rx_codec } // 'UNKNOWN'); |
|
399
|
15
|
|
50
|
|
|
85
|
_log(" Tx codec: %d / %s", $tx_codec, $codec_label{ $tx_codec } // 'UNKNOWN'); |
|
400
|
15
|
|
|
|
|
34
|
_log(" Rx packetization period: %d ms", $rx_period); |
|
401
|
15
|
|
|
|
|
34
|
_log(" Tx packetization period: %d ms", $tx_period); |
|
402
|
15
|
|
|
|
|
35
|
_log(" Rx bandwidth: %d kbit/s", $rx_bandwidth); |
|
403
|
15
|
|
|
|
|
33
|
_log(" Tx bandwidth: %d kbit/s", $tx_bandwidth); |
|
404
|
15
|
|
|
|
|
32
|
_log(" Max. jitter buffer size: %d ms", $max_jitter); |
|
405
|
15
|
|
|
|
|
37
|
my $call_side = ($flag & 0x80) >> 7; |
|
406
|
15
|
|
|
|
|
33
|
my $call_type = $flag & 0x7F; |
|
407
|
15
|
|
50
|
|
|
62
|
_log(" Call side: %d / %s", $call_side, $side_label{ $call_side } // 'UNKNOWN'); |
|
408
|
15
|
|
50
|
|
|
59
|
_log(" VoIP call type: %d / %s", $call_type, $type_label{ $call_type } // 'UNKNOWN'); |
|
409
|
|
|
|
|
|
|
|
|
410
|
15
|
|
50
|
|
|
54
|
$call->{voip_rx_codec} = $codec_label{ $rx_codec } // 'UNKNOWN'; |
|
411
|
15
|
|
|
|
|
39
|
$call->{voip_rx_codec_code} = $rx_codec; |
|
412
|
15
|
|
50
|
|
|
49
|
$call->{voip_tx_codec} = $codec_label{ $tx_codec } // 'UNKNOWN'; |
|
413
|
15
|
|
|
|
|
35
|
$call->{voip_tx_codec_code} = $tx_codec; |
|
414
|
15
|
|
|
|
|
35
|
$call->{voip_rx_packetization} = $rx_period; |
|
415
|
15
|
|
|
|
|
39
|
$call->{voip_tx_packetization} = $tx_period; |
|
416
|
15
|
|
|
|
|
34
|
$call->{voip_rx_bandwidth} = $rx_bandwidth; |
|
417
|
15
|
|
|
|
|
38
|
$call->{voip_tx_bandwidth} = $tx_bandwidth; |
|
418
|
15
|
|
|
|
|
33
|
$call->{voip_max_jitter} = $max_jitter; |
|
419
|
15
|
|
50
|
|
|
64
|
$call->{voip_call_side} = $side_label{ $call_side } // 'UNKNOWN'; |
|
420
|
15
|
|
|
|
|
37
|
$call->{voip_call_side_code} = $call_side; |
|
421
|
15
|
|
50
|
|
|
47
|
$call->{voip_call_type} = $type_label{ $call_type } // 'UNKNOWN'; |
|
422
|
15
|
|
|
|
|
154
|
$call->{voip_call_type_code} = $call_type; |
|
423
|
|
|
|
|
|
|
} |
|
424
|
|
|
|
|
|
|
# 129. Amount of transferred data |
|
425
|
|
|
|
|
|
|
sub block_129 { |
|
426
|
15
|
|
|
15
|
0
|
28
|
my($call, $var) = @_; |
|
427
|
15
|
|
|
|
|
30
|
_log("129. Amount of transferred data"); |
|
428
|
15
|
|
|
|
|
23
|
my($len,$side,$rx_packets,$tx_packets,$rx_octets,$tx_octets,$lost,$jitter,$latency); |
|
429
|
15
|
|
|
|
|
178
|
($len,$side,$rx_packets,$tx_packets,$rx_octets,$tx_octets,$lost,$jitter,$latency,$$var) = unpack('CCNNNNNCC a*', $$var); |
|
430
|
15
|
|
|
|
|
69
|
my %side_label = ( 0 => 'origin side', 1 => 'terminating side'); |
|
431
|
15
|
|
50
|
|
|
60
|
_log(" Call side: %d / %s", $side, $side_label{ $side } // 'UNKNOWN'); |
|
432
|
15
|
|
|
|
|
33
|
_log(" Rx packets: %d", $rx_packets); |
|
433
|
15
|
|
|
|
|
32
|
_log(" Tx packets: %d", $tx_packets); |
|
434
|
15
|
|
|
|
|
31
|
_log(" Rx octets: %d", $rx_octets); |
|
435
|
15
|
|
|
|
|
33
|
_log(" Tx octets: %d", $tx_octets); |
|
436
|
15
|
|
|
|
|
33
|
_log(" Packets lost: %d", $lost); |
|
437
|
15
|
|
|
|
|
33
|
_log(" Average jitter: %d ms", $jitter); |
|
438
|
15
|
|
|
|
|
30
|
_log(" Average latency: %d ms", $latency); |
|
439
|
15
|
|
50
|
|
|
55
|
$call->{call_side} = $side_label{ $side } // 'UNKNOWN'; |
|
440
|
15
|
|
|
|
|
33
|
$call->{rx_packets} = $rx_packets; |
|
441
|
15
|
|
|
|
|
33
|
$call->{tx_packets} = $tx_packets; |
|
442
|
15
|
|
|
|
|
35
|
$call->{rx_octets} = $rx_octets; |
|
443
|
15
|
|
|
|
|
43
|
$call->{tx_octets} = $tx_octets; |
|
444
|
15
|
|
|
|
|
34
|
$call->{packets_lost} = $lost; |
|
445
|
15
|
|
|
|
|
36
|
$call->{average_jitter} = $jitter; |
|
446
|
15
|
|
|
|
|
111
|
$call->{everage_latency} = $latency; |
|
447
|
|
|
|
|
|
|
} |
|
448
|
|
|
|
|
|
|
# 130. Service control data |
|
449
|
|
|
|
|
|
|
# 131. New destination number |
|
450
|
|
|
|
|
|
|
# 132. VoIP Quality of Service data (QoS VoIP Data) |
|
451
|
|
|
|
|
|
|
# 133. Additional Centrex data |
|
452
|
|
|
|
|
|
|
# 134. Additional statistics data |
|
453
|
|
|
|
|
|
|
sub block_134 { |
|
454
|
15
|
|
|
15
|
0
|
32
|
my($call, $var) = @_; |
|
455
|
15
|
|
|
|
|
30
|
_log("134. Additional statistics data"); |
|
456
|
15
|
|
|
|
|
22
|
my($len,$stats); |
|
457
|
15
|
|
|
|
|
60
|
($len,$$var) = unpack('C a*', $$var); |
|
458
|
15
|
|
|
|
|
35
|
$len -= 2; |
|
459
|
15
|
|
|
|
|
79
|
($stats,$$var) = unpack("a$len a*", $$var); |
|
460
|
15
|
|
|
|
|
62
|
_log(" Stats: 0x%s (len %d)", unpack('H*', $stats), $len); |
|
461
|
|
|
|
|
|
|
# no useful info? |
|
462
|
|
|
|
|
|
|
} |
|
463
|
|
|
|
|
|
|
# TODO NOT IMPLEMENTED YET: |
|
464
|
|
|
|
|
|
|
# 135. IMS charging identifier |
|
465
|
|
|
|
|
|
|
# 136. Inter Operator Identifiers – IOI) |
|
466
|
|
|
|
|
|
|
# 137. Supplementary service additional info |
|
467
|
|
|
|
|
|
|
# 138. Calling Party Number |
|
468
|
|
|
|
|
|
|
# 139. Additional calling number |
|
469
|
|
|
|
|
|
|
# 140. Called party number |
|
470
|
|
|
|
|
|
|
# 141. Sent called party number |
|
471
|
|
|
|
|
|
|
# 142. Third party number |
|
472
|
|
|
|
|
|
|
# 143. Redirecting party number |
|
473
|
|
|
|
|
|
|
# 144. Incoming trunk data - Name |
|
474
|
|
|
|
|
|
|
# 145. Outgoing trunk data - name |
|
475
|
|
|
|
|
|
|
# 146. Node info |
|
476
|
|
|
|
|
|
|
# 147. Global call reference |
|
477
|
|
|
|
|
|
|
# 148. MLPP Data |
|
478
|
|
|
|
|
|
|
# 149. Customer Data |
|
479
|
|
|
|
|
|
|
# 150. Received Called Party Number |
|
480
|
|
|
|
|
|
|
# 151. Call Type |
|
481
|
|
|
|
|
|
|
# 152. IN Service Data |
|
482
|
|
|
|
|
|
|
# 153. URI (Universal Resource Identification) |
|
483
|
|
|
|
|
|
|
# 154. Free Format Operator Specific Data |
|
484
|
|
|
|
|
|
|
# 155. ----- |
|
485
|
|
|
|
|
|
|
# 156. Additional Numbers |
|
486
|
|
|
|
|
|
|
|
|
487
|
|
|
|
|
|
|
sub dump_var { |
|
488
|
0
|
|
|
0
|
0
|
0
|
my $var = shift; |
|
489
|
0
|
|
|
|
|
0
|
_log("---- Variable data (%d) ----", length($var)); |
|
490
|
0
|
|
|
|
|
0
|
_log('%s', unpack('H*', $var)); |
|
491
|
|
|
|
|
|
|
} |
|
492
|
|
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
# Parse individual record |
|
494
|
|
|
|
|
|
|
# |
|
495
|
|
|
|
|
|
|
# Each record has fixed part 16+(2..19) bytes |
|
496
|
|
|
|
|
|
|
# and optional set of additional blocks |
|
497
|
|
|
|
|
|
|
sub parse_record { |
|
498
|
16
|
|
|
16
|
0
|
25
|
my $fh = shift; |
|
499
|
|
|
|
|
|
|
|
|
500
|
16
|
|
|
|
|
21
|
my $type_id; |
|
501
|
16
|
100
|
|
|
|
104
|
sysread($fh, $type_id, 1) || return 0; |
|
502
|
15
|
|
|
|
|
44
|
my $code = unpack('H2', $type_id); |
|
503
|
|
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
# Recort type: |
|
505
|
|
|
|
|
|
|
# d2 -- Record at date and time changes (parsed but ignored) |
|
506
|
|
|
|
|
|
|
# d3 -- Record of the loss of a certain amount of records |
|
507
|
|
|
|
|
|
|
# d4 -- Restart record |
|
508
|
|
|
|
|
|
|
# c8 -- Call record |
|
509
|
15
|
50
|
|
|
|
72
|
if($code eq 'd2') { |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
510
|
|
|
|
|
|
|
# record of date and time changes |
|
511
|
0
|
|
|
|
|
0
|
parse_time_change_record($fh, $code); |
|
512
|
0
|
|
|
|
|
0
|
return parse_record($fh); |
|
513
|
|
|
|
|
|
|
} |
|
514
|
|
|
|
|
|
|
elsif($code eq 'd3'){ |
|
515
|
|
|
|
|
|
|
# record of the loss of a certain amount of records |
|
516
|
0
|
|
|
|
|
0
|
parse_loss_record($fh, $code); |
|
517
|
0
|
|
|
|
|
0
|
return parse_record($fh); |
|
518
|
|
|
|
|
|
|
} |
|
519
|
|
|
|
|
|
|
elsif($code eq 'd4') { |
|
520
|
|
|
|
|
|
|
# restart/reboot |
|
521
|
0
|
|
|
|
|
0
|
parse_reboot_record($fh, $code); |
|
522
|
0
|
|
|
|
|
0
|
return parse_record($fh); |
|
523
|
|
|
|
|
|
|
} |
|
524
|
|
|
|
|
|
|
elsif($code ne 'c8') { |
|
525
|
0
|
|
|
|
|
0
|
die "Unknown record type: $code"; |
|
526
|
|
|
|
|
|
|
} |
|
527
|
15
|
|
|
|
|
32
|
_log('Found Call Record marker %s', $code); |
|
528
|
|
|
|
|
|
|
|
|
529
|
15
|
|
|
|
|
30
|
my %call = (); |
|
530
|
|
|
|
|
|
|
|
|
531
|
|
|
|
|
|
|
# Statis header |
|
532
|
|
|
|
|
|
|
# 1 - c8 |
|
533
|
|
|
|
|
|
|
# 2 - record length |
|
534
|
|
|
|
|
|
|
# 4 - record index (in file?) |
|
535
|
|
|
|
|
|
|
# 4 - call identifier (sequentially incremented number, unique - |
|
536
|
|
|
|
|
|
|
# but incomplete calls can have call-id repeated again in later file) |
|
537
|
|
|
|
|
|
|
# 3 - flags |
|
538
|
|
|
|
|
|
|
# 1 - Sequence (4bits) / Charge status (4bits) |
|
539
|
|
|
|
|
|
|
# 1 - Area code length (3bits) / Subscriber number length (5bits) |
|
540
|
|
|
|
|
|
|
# ... - Area code and subscriber number of record owner |
|
541
|
15
|
|
|
|
|
19
|
my $len; |
|
542
|
15
|
50
|
|
|
|
72
|
sysread($fh, $len, 2) || die $!; |
|
543
|
15
|
|
|
|
|
45
|
$len = unpack('n', $len); |
|
544
|
|
|
|
|
|
|
#printf "Record lengh: %d bytes\n", $len; |
|
545
|
|
|
|
|
|
|
|
|
546
|
15
|
|
|
|
|
29
|
my $data; |
|
547
|
15
|
50
|
|
|
|
87
|
sysread($fh, $data, $len - 3) || die $!; |
|
548
|
|
|
|
|
|
|
#print unpack('H*', $data), "\n"; |
|
549
|
|
|
|
|
|
|
|
|
550
|
15
|
|
|
|
|
97
|
my($rec_index,$call_id,$flags,$seq,$area,$variable) = unpack('N N H6 H2 C a*', $data); |
|
551
|
15
|
|
|
|
|
44
|
_log("Header"); |
|
552
|
15
|
|
|
|
|
31
|
_log(" Record index: %d", $rec_index); |
|
553
|
15
|
|
|
|
|
52
|
_log(" Call ID: %d / %s", $call_id, unpack('H*', pack('N', $call_id))); |
|
554
|
15
|
|
|
|
|
137
|
_log(" Flags: 0x%s", unpack('H6', pack('H6', $flags))); |
|
555
|
15
|
|
|
|
|
51
|
_log(" Record sequence: %d", (($seq & 0xF0) >> 4)); |
|
556
|
15
|
|
|
|
|
37
|
_log(" Charge status: %d", ($seq & 0x0F)); |
|
557
|
|
|
|
|
|
|
|
|
558
|
15
|
|
|
|
|
42
|
$call{record_index} = $rec_index; |
|
559
|
15
|
|
|
|
|
36
|
$call{call_id} = $call_id; |
|
560
|
15
|
|
|
|
|
28
|
$call{flags} = $flags; |
|
561
|
15
|
|
|
|
|
60
|
$call{record_sequence} = (($seq & 0xF0) >> 4); |
|
562
|
15
|
|
|
|
|
58
|
$call{charge_status} = ($seq & 0x0F); |
|
563
|
|
|
|
|
|
|
|
|
564
|
15
|
|
|
|
|
38
|
my($area_len) = ($area & 0xE0) >> 5; |
|
565
|
15
|
|
|
|
|
40
|
my($subscriber_len) = ($area & 0x1F); |
|
566
|
15
|
|
|
|
|
32
|
_log(" Area code length: %d", $area_len); |
|
567
|
15
|
|
|
|
|
42
|
_log(" Subscriber num len: %d", $subscriber_len); |
|
568
|
|
|
|
|
|
|
#my($subscriber_len) = ($area & 0x07); |
|
569
|
|
|
|
|
|
|
#printf "Area/Subscriber: %s / %d . %d\n", $area, $area_len, $subscriber_len; |
|
570
|
|
|
|
|
|
|
#printf " Area code length: %d\n", $area_len; |
|
571
|
|
|
|
|
|
|
#printf " Subscriber number length: %d\n", $subscriber_len; |
|
572
|
|
|
|
|
|
|
|
|
573
|
|
|
|
|
|
|
#printf "---- Variable data (%d) ----\n", length($variable); |
|
574
|
|
|
|
|
|
|
#print unpack('H*', $variable), "\n"; |
|
575
|
|
|
|
|
|
|
|
|
576
|
15
|
|
|
|
|
35
|
my $cli_len = $area_len + $subscriber_len; |
|
577
|
15
|
50
|
|
|
|
50
|
if($cli_len % 2 == 1) { |
|
578
|
|
|
|
|
|
|
# padding, each octet for cli contains 2 digits |
|
579
|
15
|
|
|
|
|
31
|
$cli_len++; |
|
580
|
|
|
|
|
|
|
} |
|
581
|
15
|
|
|
|
|
21
|
my $cli; |
|
582
|
15
|
|
|
|
|
88
|
($cli, $variable) = unpack("H$cli_len a*", $variable); |
|
583
|
15
|
50
|
|
|
|
68
|
if($cli_len > ($area_len + $subscriber_len)) { |
|
584
|
15
|
|
|
|
|
72
|
$cli = substr($cli, 0, -1); |
|
585
|
|
|
|
|
|
|
} |
|
586
|
15
|
|
|
|
|
31
|
_log(" CLI: %s", $cli); |
|
587
|
15
|
|
|
|
|
105
|
$call{cli} = $cli; |
|
588
|
|
|
|
|
|
|
|
|
589
|
|
|
|
|
|
|
# dynamic part: |
|
590
|
15
|
|
|
|
|
23
|
my $block_marker; |
|
591
|
15
|
|
|
|
|
48
|
while( length($variable) > 0) { |
|
592
|
|
|
|
|
|
|
# each block has type marker + variable data, which depends on type |
|
593
|
240
|
|
|
|
|
1038
|
($block_marker, $variable) = unpack('C a*', $variable); |
|
594
|
|
|
|
|
|
|
#_log('Found block marker: %s', $block_marker); |
|
595
|
|
|
|
|
|
|
{ |
|
596
|
2
|
|
|
2
|
|
20
|
no strict 'refs'; |
|
|
2
|
|
|
|
|
4
|
|
|
|
2
|
|
|
|
|
1297
|
|
|
|
240
|
|
|
|
|
502
|
|
|
597
|
240
|
|
|
|
|
615
|
my $sub = 'block_' . $block_marker; |
|
598
|
240
|
|
|
|
|
673
|
$sub->(\%call, \$variable); |
|
599
|
|
|
|
|
|
|
} |
|
600
|
|
|
|
|
|
|
#dump_var($variable); |
|
601
|
|
|
|
|
|
|
} |
|
602
|
|
|
|
|
|
|
|
|
603
|
15
|
|
|
|
|
31
|
_log(''); |
|
604
|
15
|
|
|
|
|
83
|
return \%call; |
|
605
|
|
|
|
|
|
|
} |
|
606
|
|
|
|
|
|
|
|
|
607
|
|
|
|
|
|
|
# d2 - time change event |
|
608
|
|
|
|
|
|
|
sub parse_time_change_record { |
|
609
|
0
|
|
|
0
|
0
|
|
my ($fh, $code) = @_; |
|
610
|
|
|
|
|
|
|
|
|
611
|
0
|
|
|
|
|
|
_log('Found Date Time Change marker %s', $code); |
|
612
|
|
|
|
|
|
|
|
|
613
|
0
|
|
|
|
|
|
my $data; |
|
614
|
|
|
|
|
|
|
# 7 - old date and time |
|
615
|
|
|
|
|
|
|
# 7 - new date and time |
|
616
|
|
|
|
|
|
|
# 1 - cause of change |
|
617
|
0
|
0
|
|
|
|
|
sysread($fh, $data, 15) || die $!; |
|
618
|
0
|
|
|
|
|
|
my($year,$month,$day,$hour,$min,$sec,$msec,$reason); |
|
619
|
0
|
|
|
|
|
|
($year,$month,$day,$hour,$min,$sec,$msec,$data) = unpack('CCCCCCC a*', $data); |
|
620
|
0
|
|
|
|
|
|
$year += 2000; |
|
621
|
0
|
|
|
|
|
|
my $dtime = sprintf "%04d-%02d-%02d %02d:%02d:%02d.%02d", $year,$month,$day,$hour,$min,$sec,$msec; |
|
622
|
0
|
|
|
|
|
|
_log(' Old Time: %s', $dtime); |
|
623
|
|
|
|
|
|
|
|
|
624
|
0
|
|
|
|
|
|
($year,$month,$day,$hour,$min,$sec,$msec,$reason) = unpack('CCCCCCC C', $data); |
|
625
|
0
|
|
|
|
|
|
$year += 2000; |
|
626
|
0
|
|
|
|
|
|
$dtime = sprintf "%04d-%02d-%02d %02d:%02d:%02d.%02d", $year,$month,$day,$hour,$min,$sec,$msec; |
|
627
|
0
|
|
|
|
|
|
_log(' New Time: %s', $dtime); |
|
628
|
|
|
|
|
|
|
|
|
629
|
|
|
|
|
|
|
# 1 - The real-time clock correction |
|
630
|
|
|
|
|
|
|
# 2 - Summer / winter time changes |
|
631
|
0
|
|
|
|
|
|
_log(' Change reason: %s', $reason); |
|
632
|
|
|
|
|
|
|
} |
|
633
|
|
|
|
|
|
|
|
|
634
|
|
|
|
|
|
|
# d3 - loss record |
|
635
|
|
|
|
|
|
|
sub parse_loss_record { |
|
636
|
0
|
|
|
0
|
0
|
|
my ($fh, $code) = @_; |
|
637
|
|
|
|
|
|
|
|
|
638
|
0
|
|
|
|
|
|
_log('Found Loss marker %s', $code); |
|
639
|
|
|
|
|
|
|
|
|
640
|
0
|
|
|
|
|
|
my $data; |
|
641
|
|
|
|
|
|
|
# 7 - start date and time |
|
642
|
|
|
|
|
|
|
# 7 - end date and time |
|
643
|
|
|
|
|
|
|
# 4 - number of records lost |
|
644
|
0
|
0
|
|
|
|
|
sysread($fh, $data, 18) || die $!; |
|
645
|
0
|
|
|
|
|
|
my($year,$month,$day,$hour,$min,$sec,$msec,$amount); |
|
646
|
0
|
|
|
|
|
|
($year,$month,$day,$hour,$min,$sec,$msec,$data) = unpack('CCCCCCC a*', $data); |
|
647
|
0
|
|
|
|
|
|
$year += 2000; |
|
648
|
0
|
|
|
|
|
|
my $dtime = sprintf "%04d-%02d-%02d %02d:%02d:%02d.%02d", $year,$month,$day,$hour,$min,$sec,$msec; |
|
649
|
0
|
|
|
|
|
|
_log(' Start Time: %s', $dtime); |
|
650
|
|
|
|
|
|
|
|
|
651
|
0
|
|
|
|
|
|
($year,$month,$day,$hour,$min,$sec,$msec,$amount) = unpack('CCCCCCC L', $data); |
|
652
|
0
|
|
|
|
|
|
$year += 2000; |
|
653
|
0
|
|
|
|
|
|
$dtime = sprintf "%04d-%02d-%02d %02d:%02d:%02d.%02d", $year,$month,$day,$hour,$min,$sec,$msec; |
|
654
|
0
|
|
|
|
|
|
_log(' End Time: %s', $dtime); |
|
655
|
|
|
|
|
|
|
|
|
656
|
0
|
|
|
|
|
|
_log(' Amount of record loss: %s', $amount); |
|
657
|
|
|
|
|
|
|
} |
|
658
|
|
|
|
|
|
|
|
|
659
|
|
|
|
|
|
|
# d4 - System was rebooted |
|
660
|
|
|
|
|
|
|
sub parse_reboot_record { |
|
661
|
0
|
|
|
0
|
0
|
|
my ($fh, $code) = @_; |
|
662
|
0
|
|
|
|
|
|
_log('Found Restart marker %s', $code); |
|
663
|
|
|
|
|
|
|
|
|
664
|
0
|
|
|
|
|
|
my $data; |
|
665
|
|
|
|
|
|
|
# 7 - restart date and time |
|
666
|
|
|
|
|
|
|
# 4 - reserved |
|
667
|
0
|
0
|
|
|
|
|
sysread($fh, $data, 7) || die $!; |
|
668
|
0
|
|
|
|
|
|
my($year,$month,$day,$hour,$min,$sec,$msec); |
|
669
|
0
|
|
|
|
|
|
($year,$month,$day,$hour,$min,$sec,$msec) = unpack('CCCCCCC', $data); |
|
670
|
0
|
|
|
|
|
|
$year += 2000; |
|
671
|
0
|
|
|
|
|
|
my $reboot_time = sprintf "%04d-%02d-%02d %02d:%02d:%02d.%02d", $year,$month,$day,$hour,$min,$sec,$msec; |
|
672
|
0
|
|
|
|
|
|
_log(' Restart time: %s', $reboot_time); |
|
673
|
|
|
|
|
|
|
|
|
674
|
|
|
|
|
|
|
# ignored... |
|
675
|
0
|
0
|
|
|
|
|
sysread($fh, $data, 4) || die $!; |
|
676
|
|
|
|
|
|
|
} |
|
677
|
|
|
|
|
|
|
|
|
678
|
|
|
|
|
|
|
1; |
|
679
|
|
|
|
|
|
|
#-- |
|
680
|
|
|
|
|
|
|
|
|
681
|
|
|
|
|
|
|
=head1 AUTHOR |
|
682
|
|
|
|
|
|
|
|
|
683
|
|
|
|
|
|
|
Sergey Leschenko, C<< >> |
|
684
|
|
|
|
|
|
|
|
|
685
|
|
|
|
|
|
|
=head1 BUGS |
|
686
|
|
|
|
|
|
|
|
|
687
|
|
|
|
|
|
|
Please note that some blocks are not implemented, as I haven't seen them in real data files. |
|
688
|
|
|
|
|
|
|
|
|
689
|
|
|
|
|
|
|
Please report any bugs or feature requests to C, or through |
|
690
|
|
|
|
|
|
|
the web interface at L. I will be notified, and then you'll |
|
691
|
|
|
|
|
|
|
automatically be notified of progress on your bug as I make changes. |
|
692
|
|
|
|
|
|
|
|
|
693
|
|
|
|
|
|
|
|
|
694
|
|
|
|
|
|
|
|
|
695
|
|
|
|
|
|
|
|
|
696
|
|
|
|
|
|
|
=head1 SUPPORT |
|
697
|
|
|
|
|
|
|
|
|
698
|
|
|
|
|
|
|
You can find documentation for this module with the perldoc command. |
|
699
|
|
|
|
|
|
|
|
|
700
|
|
|
|
|
|
|
perldoc CDR::Parser::SI3000 |
|
701
|
|
|
|
|
|
|
|
|
702
|
|
|
|
|
|
|
|
|
703
|
|
|
|
|
|
|
You can also look for information at: |
|
704
|
|
|
|
|
|
|
|
|
705
|
|
|
|
|
|
|
=over 4 |
|
706
|
|
|
|
|
|
|
|
|
707
|
|
|
|
|
|
|
=item * RT: CPAN's request tracker (report bugs here) |
|
708
|
|
|
|
|
|
|
|
|
709
|
|
|
|
|
|
|
L |
|
710
|
|
|
|
|
|
|
|
|
711
|
|
|
|
|
|
|
=item * AnnoCPAN: Annotated CPAN documentation |
|
712
|
|
|
|
|
|
|
|
|
713
|
|
|
|
|
|
|
L |
|
714
|
|
|
|
|
|
|
|
|
715
|
|
|
|
|
|
|
=item * CPAN Ratings |
|
716
|
|
|
|
|
|
|
|
|
717
|
|
|
|
|
|
|
L |
|
718
|
|
|
|
|
|
|
|
|
719
|
|
|
|
|
|
|
=item * Search CPAN |
|
720
|
|
|
|
|
|
|
|
|
721
|
|
|
|
|
|
|
L |
|
722
|
|
|
|
|
|
|
|
|
723
|
|
|
|
|
|
|
=back |
|
724
|
|
|
|
|
|
|
|
|
725
|
|
|
|
|
|
|
|
|
726
|
|
|
|
|
|
|
=head1 ACKNOWLEDGEMENTS |
|
727
|
|
|
|
|
|
|
|
|
728
|
|
|
|
|
|
|
|
|
729
|
|
|
|
|
|
|
=head1 LICENSE AND COPYRIGHT |
|
730
|
|
|
|
|
|
|
|
|
731
|
|
|
|
|
|
|
Copyright 2013 Sergey Leschenko. |
|
732
|
|
|
|
|
|
|
|
|
733
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify it |
|
734
|
|
|
|
|
|
|
under the terms of the the Artistic License (2.0). You may obtain a |
|
735
|
|
|
|
|
|
|
copy of the full license at: |
|
736
|
|
|
|
|
|
|
|
|
737
|
|
|
|
|
|
|
L |
|
738
|
|
|
|
|
|
|
|
|
739
|
|
|
|
|
|
|
Any use, modification, and distribution of the Standard or Modified |
|
740
|
|
|
|
|
|
|
Versions is governed by this Artistic License. By using, modifying or |
|
741
|
|
|
|
|
|
|
distributing the Package, you accept this license. Do not use, modify, |
|
742
|
|
|
|
|
|
|
or distribute the Package, if you do not accept this license. |
|
743
|
|
|
|
|
|
|
|
|
744
|
|
|
|
|
|
|
If your Modified Version has been derived from a Modified Version made |
|
745
|
|
|
|
|
|
|
by someone other than you, you are nevertheless required to ensure that |
|
746
|
|
|
|
|
|
|
your Modified Version complies with the requirements of this license. |
|
747
|
|
|
|
|
|
|
|
|
748
|
|
|
|
|
|
|
This license does not grant you the right to use any trademark, service |
|
749
|
|
|
|
|
|
|
mark, tradename, or logo of the Copyright Holder. |
|
750
|
|
|
|
|
|
|
|
|
751
|
|
|
|
|
|
|
This license includes the non-exclusive, worldwide, free-of-charge |
|
752
|
|
|
|
|
|
|
patent license to make, have made, use, offer to sell, sell, import and |
|
753
|
|
|
|
|
|
|
otherwise transfer the Package with respect to any patent claims |
|
754
|
|
|
|
|
|
|
licensable by the Copyright Holder that are necessarily infringed by the |
|
755
|
|
|
|
|
|
|
Package. If you institute patent litigation (including a cross-claim or |
|
756
|
|
|
|
|
|
|
counterclaim) against any party alleging that the Package constitutes |
|
757
|
|
|
|
|
|
|
direct or contributory patent infringement, then this Artistic License |
|
758
|
|
|
|
|
|
|
to you shall terminate on the date that such litigation is filed. |
|
759
|
|
|
|
|
|
|
|
|
760
|
|
|
|
|
|
|
Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER |
|
761
|
|
|
|
|
|
|
AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. |
|
762
|
|
|
|
|
|
|
THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR |
|
763
|
|
|
|
|
|
|
PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY |
|
764
|
|
|
|
|
|
|
YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR |
|
765
|
|
|
|
|
|
|
CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR |
|
766
|
|
|
|
|
|
|
CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, |
|
767
|
|
|
|
|
|
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
768
|
|
|
|
|
|
|
|
|
769
|
|
|
|
|
|
|
|
|
770
|
|
|
|
|
|
|
=cut |
|
771
|
|
|
|
|
|
|
|
|
772
|
|
|
|
|
|
|
1; # End of CDR::Parser::SI3000 |