File Coverage

blib/lib/Nagios/NRPE/Packet.pm
Criterion Covered Total %
statement 116 141 82.2
branch 12 22 54.5
condition 6 11 54.5
subroutine 18 19 94.7
pod 9 9 100.0
total 161 202 79.7


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             Nagios::NRPE::Packet - Assembly and disassembly of an NRPE packet
4              
5             =head1 SYNOPSIS
6              
7             use IO::Socket;
8             use IO::Socket::INET;
9             # Import necessary constants into Namespace
10             use Nagios::NRPE::Packet qw(NRPE_PACKET_VERSION_3
11             NRPE_PACKET_QUERY
12             MAX_PACKETBUFFER_LENGTH
13             STATE_UNKNOWN
14             STATE_CRITICAL
15             STATE_WARNING
16             STATE_OK);
17              
18             my $packet = Nagios::NRPE::Packet->new();
19              
20             my $socket = IO::Socket::INET->new(
21             PeerAddr => $host,
22             PeerPort => $port,
23             Proto => 'tcp',
24             Type => SOCK_STREAM) or die "ERROR: $@ \n";
25              
26             print $socket $packet->assemble(type => NRPE_PACKET_QUERY,
27             buffer => "check_load 1 2 3",
28             version => NRPE_PACKET_VERSION_3 );
29              
30             my $data = <$socket>;
31             my $response = $packet->disassemble($data);
32              
33             print $response->{buffer};
34             exit $response->{result_code};
35              
36             =head1 DESCRIPTION
37              
38             This class is meant to be used when an active connection exists and is ready to send the
39             packet.
40              
41             =head1 CONSTRUCTION
42              
43             =over
44              
45             =item new
46              
47             Takes the following options as a hashref
48              
49             =back
50              
51             =head1 SUBROUTINES
52              
53             Following functions can be used after the creation of the packet
54              
55             =over 2
56              
57             =item assemble()
58              
59             Takes a hash of options defining the packet to be sent and returns the assembled packet. You can print this
60             to an open socket and send it to either a server or the client depending on your situation.
61              
62             * check
63              
64             A string defining the check to be run or the output of a check eg: "check_cpu"
65             NOTE: Nagios can accept arguments appended to the check in the form: "check_somecheck!ARG1!ARG2!ARG..."
66              
67             * version
68              
69             The NRPE version you want to use (only V2 and V3 work V1 is not supported, deafult is V3).
70              
71             See CONSTANTS for options here.
72              
73             * type
74              
75             The TYPE of packet you wish to send, which is either QUERY or RESPONSE.
76              
77             See L for options here.
78              
79             * result_code
80              
81             This is the exit code of the check script that is run, and check_nrpe.pl will exit with this value from the
82             RESPONSE packet.
83              
84             A set value for the QUERY type packet is 2324.
85              
86             =item assemble_v2()
87              
88             A helper function to assemble a V2 packet.
89              
90             =item assemble_v3()
91              
92             A helper function to assemble a V3 packet.
93              
94             =item disassemble()
95              
96             Takes a packet recieved by either client or server and disassembles them. The returned hashref contains
97             the following values for a V3 packet:
98              
99             packet_version
100             packet_type
101             crc32_value
102             result_code
103             alignment
104             buffer_length
105             buffer
106              
107             and the following values for a V2 packet:
108              
109             packet_version
110             packet_type
111             crc32_value
112             result_code
113             buffer
114              
115             =item disassemble_v2()
116              
117             Helper function for disassembleing a V2 packet
118              
119             =item disassemble_v3()
120              
121             Helper function for disassembleing a V3 packet
122              
123             =item validate($packet)
124              
125             Validates the contents of a packet using CRC32 checksumming. Returns undef
126             if not succesful.
127              
128              
129             =item packet_dump
130              
131             Debugging function for hexdumping a binary string.
132              
133             =back
134              
135             =head1 CONSTANTS
136              
137             These constants can be exported upon request with the 'use' pragma like this:
138              
139             # Will only import the constant NRPE_PACKET_VERSION_3 into your namespace
140             use Nagios::NRPE::Packet qw(NRPE_PACKET_VERSION_3);
141              
142             =over 2
143              
144             =item * NRPE_PACKET_VERSION_3
145             NRPE_PACKET_VERSION_2
146             NRPE_PACKET_VERSION_1
147              
148             The value of the NRPE version you want/need to use.
149              
150             =item * NRPE_PACKET_QUERY
151             NRPE_PACKET_RESPONSE
152              
153             The packet type you want to send or recieve
154              
155             =item * MAX_PACKETBUFFER_LENGTH
156             MAX_COMMAND_ARGUMENTS
157              
158             A threshhold on the send data
159              
160             =item * NRPE_HELLO_COMMAND
161              
162             unknown
163              
164             =item * DEFAULT_SOCKET_TIMEOUT
165             DEFAULT_CONNECTION_TIMEOUT
166              
167             The default timeout for a connection and its corresponding socket
168              
169             =item * STATE_UNKNOWN
170             STATE_CRITICAL
171             STATE_WARNING
172             STATE_OK
173              
174             States returned by the check
175              
176             =back
177              
178             =head1 COPYRIGHT AND LICENSE
179              
180             This software is copyright (c) 2013-2018 by the authors (see L file).
181              
182             This is free software; you can redistribute it and/or modify it under
183             the same terms as the Perl 5 programming language system itself.
184              
185             =cut
186              
187             package Nagios::NRPE::Packet;
188              
189             our $VERSION = '2.0.13';
190              
191 1     1   81614 use 5.010_000;
  1         5  
192             require Exporter;
193             require overload;
194              
195             BEGIN
196             {
197 1     1   19 @ISA = qw(Exporter);
198 1         25 @EXPORT_OK = qw(NRPE_PACKET_VERSION_3
199             NRPE_PACKET_VERSION_2
200             NRPE_PACKET_VERSION_1
201             NRPE_PACKET_QUERY
202             NRPE_PACKET_RESPONSE
203             MAX_PACKETBUFFER_LENGTH
204             MAX_COMMAND_ARGUMENTS
205             NRPE_HELLO_COMMAND
206             DEFAULT_SOCKET_TIMEOUT
207             DEFAULT_CONNECTION_TIMEOUT
208             STATE_UNKNOWN
209             STATE_CRITICAL
210             STATE_WARNING
211             STATE_OK);
212             }
213              
214 1     1   9 use warnings;
  1         2  
  1         41  
215 1     1   6 use strict;
  1         2  
  1         20  
216              
217 1     1   4 use Carp;
  1         2  
  1         65  
218 1     1   1070 use Convert::Binary::C;
  1         1223  
  1         30  
219 1     1   482 use Digest::CRC 'crc32';
  1         2561  
  1         67  
220 1     1   475 use Nagios::NRPE::Utils qw(return_error);
  1         3  
  1         77  
221              
222             use constant {
223              
224             # packet version identifier
225 1         382 NRPE_PACKET_VERSION_3 => 3,
226             NRPE_PACKET_VERSION_2 => 2,
227             NRPE_PACKET_VERSION_1 => 1,
228              
229             # id code for queries and responses to queries
230             NRPE_PACKET_QUERY => 1,
231             NRPE_PACKET_RESPONSE => 2,
232              
233             # max amount of data we'll send in one query/response
234             MAX_PACKETBUFFER_LENGTH => 1024,
235             MAX_COMMAND_ARGUMENTS => 16,
236             NRPE_HELLO_COMMAND => "_NRPE_CHECK",
237             DEFAULT_SOCKET_TIMEOUT => 10,
238             DEFAULT_CONNECTION_TIMEOUT => 300,
239              
240             # /* service state return codes */
241             STATE_UNKNOWN => 3,
242             STATE_CRITICAL => 2,
243             STATE_WARNING => 1,
244             STATE_OK => 0,
245 1     1   7 };
  1         2  
246              
247             sub new
248             {
249 2     2 1 1791 my ($class, %options) = @_;
250 2         5 my $self = {};
251              
252 2         22 bless $self, $class;
253             }
254              
255             sub assemble
256             {
257 4     4 1 4059 my ($self, %options) = @_;
258              
259             # taken with modifications from common.h in nagios-nrpe
260 4         87 my $c = Convert::Binary::C->new(ByteOrder => 'BigEndian', Alignment => 0);
261 4         75 $self->{c} = $c;
262              
263             croak "ERROR: Cannot send Packet with empty buffer!"
264 4 50       15 if (not defined $options{check});
265 4         7 my $packed;
266 4 100       13 if ($options{version} eq NRPE_PACKET_VERSION_2)
267             {
268 2         9 $packed = $self->assemble_v2(%options);
269             }
270             else
271             {
272 2         9 $packed = $self->assemble_v3(%options);
273             }
274 4         16 return $packed;
275              
276             }
277              
278             sub assemble_v3
279             {
280 2     2 1 7 my ($self, %options) = @_;
281 2         4 my $buffer = $options{check};
282 2         4 my $len = length($buffer);
283              
284             # In order for crc32 calculation to be correct we need to pad the buffer with \0
285             # It seems that the buffer must be in multiples of 1024 so to achive this we use
286             # some integer arithmetic to find the next multiple of 1024 that can hold our message
287 2         3 my $packLen;
288             {
289 1     1   601 use integer;
  1         15  
  1         5  
  2         3  
290 2         6 $packLen = (($len / 1024) * 1024) + 1024;
291             }
292 2         13 $buffer = pack("Z$packLen", $buffer);
293 2         4 $len = length( $buffer) + 1;
294              
295 2         4 my $unpacked;
296 2         5 $unpacked->{alignment} = 0;
297 2         3 $unpacked->{buffer_length} = $len;
298 2         5 $unpacked->{buffer} = $buffer;
299 2         3 $unpacked->{crc32_value} = "\x00\x00\x00\x00";
300 2   50     7 $unpacked->{packet_type} = $options{type} // NRPE_PACKET_QUERY;
301 2         3 $unpacked->{packet_version} = NRPE_PACKET_VERSION_3;
302 2   100     10 $unpacked->{result_code} = $options{result_code} // 2324;
303              
304 2         242 $self->{c}->parse(<
305             struct Packet{
306             unsigned short packet_version;
307             unsigned short packet_type;
308             unsigned int crc32_value;
309             unsigned short result_code;
310             unsigned short alignment;
311             int buffer_length;
312             char buffer[$len];
313             };
314             PACKET_STRUCT
315 2         21 $self->{c}->tag('Packet.buffer', Format => 'String');
316              
317 2         21 my $packed = $self->{c}->pack('Packet', $unpacked);
318              
319 2         8 $unpacked->{crc32_value} = crc32($packed);
320 2         90 $packed = $self->{c}->pack('Packet', $unpacked);
321 2         9 return $packed;
322              
323             }
324              
325             sub assemble_v2
326             {
327              
328 2     2 1 8 my ($self, %options) = @_;
329 2         3 my $unpacked = {};
330              
331 2         6 $unpacked->{buffer} = $options{check};
332 2         4 $unpacked->{crc32_value} = "\x00\x00\x00\x00";
333 2   50     7 $unpacked->{packet_type} = $options{type} // NRPE_PACKET_QUERY;
334 2         4 $unpacked->{packet_version} = NRPE_PACKET_VERSION_2;
335 2   100     10 $unpacked->{result_code} = $options{result_code} // 2324;
336              
337 2         267 $self->{c}->parse(<
338             struct Packet{
339             unsigned short packet_version;
340             unsigned short packet_type;
341             unsigned int crc32_value;
342             unsigned short result_code;
343             char buffer[1024];
344             };
345             PACKET_STRUCT
346 2         27 $self->{c}->tag('Packet.buffer', Format => 'String');
347              
348 2         24 my $packed = $self->{c}->pack('Packet', $unpacked);
349              
350 2         13 $unpacked->{crc32_value} = crc32($packed);
351 2         127 $packed = $self->{c}->pack('Packet', $unpacked);
352 2         10 return $packed;
353              
354             }
355              
356             sub validate
357             {
358 2     2 1 5 my ($self, $packet) = @_;
359 2         7 my $unpacked = $self->disassemble($packet, 1);
360 2 50       5 if (!$unpacked->{packet_version})
361             {
362             # If version is missing this is probably not an NRPE Packet.
363 0         0 return undef;
364             }
365 2         4 my $checksum = $unpacked->{crc32_value};
366 2         3 $unpacked->{crc32_value} = "\x00\x00\x00\x00";
367             my $packed = $self->assemble(
368             %{
369 2         4 {
370             check => $unpacked->{buffer},
371             version => $unpacked->{packet_version},
372             type => $unpacked->{packet_type},
373             result_code => $unpacked->{result_code}
374             }
375 2         21 }
376             );
377 2 50       8 if (crc32($packed) != $checksum)
378             {
379 2         75 return undef;
380             }
381             else
382             {
383 0         0 return 1;
384             }
385             }
386              
387             sub disassemble
388             {
389 4     4 1 14 my ($self, $packet, $novalidate) = @_;
390 4 50       10 if (!$packet)
391             {
392 0         0 return_error("Could not disassemble packet, it seems empty");
393             }
394 4 100       8 unless ($novalidate)
395             {
396 2 50       6 unless ($self->validate($packet))
397             {
398 2         8 return_error("Packet had invalid CRC32.");
399             }
400             }
401 4         18 my $ver = unpack("n", $packet);
402 4         8 my $unpacked = {};
403 4 50       10 if ($ver)
404             {
405 4 100       11 if ($ver eq NRPE_PACKET_VERSION_2)
406             {
407 2         5 $unpacked = $self->disassemble_v2($packet);
408             }
409             else
410             {
411 2         6 $unpacked = $self->disassemble_v3($packet);
412             }
413             }
414             else
415             {
416 0         0 return undef;
417             }
418              
419 4         11 return $unpacked;
420             }
421              
422             sub disassemble_v3
423             {
424 2     2 1 5 my ($self, $packet) = @_;
425 2         8 my @arr = unpack("n2 N n2 N Z*", $packet);
426 2         5 my $unpacked = {};
427 2         4 $unpacked->{packet_version} = $arr[0];
428 2         3 $unpacked->{packet_type} = $arr[1];
429 2         4 $unpacked->{crc32_value} = $arr[2];
430 2         3 $unpacked->{result_code} = $arr[3];
431 2         4 $unpacked->{alignment} = $arr[4];
432 2         3 $unpacked->{buffer_length} = $arr[5];
433 2         4 $unpacked->{buffer} = $arr[6];
434 2         5 return $unpacked;
435             }
436              
437             sub disassemble_v2
438             {
439 2     2 1 5 my ($self, $packet) = @_;
440 2         11 my @arr = unpack("n2 N n Z*", $packet);
441 2         4 my $unpacked = {};
442 2         5 $unpacked->{packet_version} = $arr[0];
443 2         4 $unpacked->{packet_type} = $arr[1];
444 2         4 $unpacked->{crc32_value} = $arr[2];
445 2         3 $unpacked->{result_code} = $arr[3];
446 2         4 $unpacked->{buffer} = $arr[4];
447 2         5 return $unpacked;
448             }
449              
450             sub packet_dump
451             {
452 0     0 1   my $packet = shift;
453 0           my $i;
454             my $k;
455 0           my $dump;
456 0           my $l;
457 0           my $ascii;
458 0           my $c;
459 0           for ($i = 0 ; $i < length($packet) ; $i += 16)
460             {
461 0           $l = $i + 16;
462 0 0         if ($l > length($packet))
463             {
464 0           $l = length($packet);
465             }
466 0           $dump = sprintf("%04d - %04d: ", $i, $l);
467 0           $ascii = "";
468 0           for ($k = $i ; $k < $l ; $k++)
469             {
470 0           $c = (ord(substr($packet, $k, 1)));
471 0           $dump .= sprintf("%02x ", $c);
472 0 0 0       if (($c >= 32) && ($c <= 126))
473             {
474 0           $ascii .= chr($c);
475             }
476             else
477             {
478 0           $ascii .= ".";
479             }
480             }
481 0           for ($k = 0 ; $k < ($i + 16 - $l) ; $k++)
482             {
483 0           $dump .= " ";
484             }
485 0           print("packet_dump() " . $dump . " [" . $ascii . "]" . "\n");
486             }
487             }
488              
489             1;