File Coverage

blib/lib/Net/Syslogd.pm
Criterion Covered Total %
statement 24 156 15.3
branch 0 60 0.0
condition 0 28 0.0
subroutine 8 22 36.3
pod 14 14 100.0
total 46 280 16.4


line stmt bran cond sub pod time code
1             package Net::Syslogd;
2              
3             ########################################################
4             # AUTHOR = Michael Vincent
5             # www.VinsWorld.com
6             ########################################################
7              
8 1     1   17168 use strict;
  1         3  
  1         50  
9 1     1   6 use warnings;
  1         2  
  1         45  
10 1     1   794 use Socket qw(AF_INET);
  1         4888  
  1         365  
11              
12             my $AF_INET6 = eval { Socket::AF_INET6() };
13              
14             our $VERSION = '0.13';
15             our @ISA;
16              
17             my $HAVE_IO_Socket_IP = 0;
18 1     1   973 eval "use IO::Socket::IP -register";
  1         42128  
  1         9  
19             if(!$@) {
20             $HAVE_IO_Socket_IP = 1;
21             push @ISA, "IO::Socket::IP"
22             } else {
23             require IO::Socket::INET;
24             push @ISA, "IO::Socket::INET";
25             }
26              
27             ########################################################
28             # Start Variables
29             ########################################################
30 1     1   11 use constant SYSLOGD_DEFAULT_PORT => 514;
  1         2  
  1         95  
31 1     1   6 use constant SYSLOGD_RFC_SIZE => 1024; # RFC Limit
  1         2  
  1         57  
32 1     1   6 use constant SYSLOGD_REC_SIZE => 2048; # Recommended size
  1         1  
  1         54  
33 1     1   6 use constant SYSLOGD_MAX_SIZE => 65467; # Actual limit (65535 - IP/UDP)
  1         1  
  1         2328  
34              
35             my @FACILITY = qw(kernel user mail system security internal printer news uucp clock security2 FTP NTP audit alert clock2 local0 local1 local2 local3 local4 local5 local6 local7);
36             my @SEVERITY = qw(Emergency Alert Critical Error Warning Notice Informational Debug);
37             our $LASTERROR;
38             ########################################################
39             # End Variables
40             ########################################################
41              
42             ########################################################
43             # Start Public Module
44             ########################################################
45              
46             sub new {
47 0     0 1   my $self = shift;
48 0   0       my $class = ref($self) || $self;
49              
50             # Default parameters
51 0           my %params = (
52             'Proto' => 'udp',
53             'LocalPort' => SYSLOGD_DEFAULT_PORT,
54             'Timeout' => 10,
55             'Family' => AF_INET
56             );
57              
58 0 0         if (@_ == 1) {
59 0           $LASTERROR = "Insufficient number of args - @_";
60             return undef
61 0           } else {
62 0           my %cfg = @_;
63 0           for (keys(%cfg)) {
64 0 0         if (/^-?localport$/i) {
    0          
    0          
    0          
65 0           $params{LocalPort} = $cfg{$_}
66             } elsif (/^-?localaddr$/i) {
67 0           $params{LocalAddr} = $cfg{$_}
68             } elsif (/^-?family$/i) {
69 0 0         if ($cfg{$_} =~ /^(?:(?:(:?ip)?v?(?:4|6))|${\AF_INET}|$AF_INET6)$/) {
  0            
70 0 0         if ($cfg{$_} =~ /^(?:(?:(:?ip)?v?4)|${\AF_INET})$/) {
  0            
71 0           $params{Family} = AF_INET
72             } else {
73 0 0         if (!$HAVE_IO_Socket_IP) {
74 0           $LASTERROR = "IO::Socket::IP required for IPv6";
75             return undef
76 0           }
77 0           $params{Family} = $AF_INET6;
78 0           $params{V6Only} = 1
79             }
80             } else {
81 0           $LASTERROR = "Invalid family - $cfg{$_}";
82             return undef
83 0           }
84             } elsif (/^-?timeout$/i) {
85 0 0         if ($cfg{$_} =~ /^\d+$/) {
86 0           $params{Timeout} = $cfg{$_}
87             } else {
88 0           $LASTERROR = "Invalid timeout - $cfg{$_}";
89             return undef
90 0           }
91             } else {
92 0           $params{$_} = $cfg{$_}
93             }
94             }
95             }
96              
97 0 0         if (my $udpserver = $class->SUPER::new(%params)) {
98 0           return bless {
99             %params, # merge user parameters
100             '_UDPSERVER_' => $udpserver
101             }, $class
102             } else {
103 0           $LASTERROR = "Error opening socket for listener: $@";
104             return undef
105 0           }
106             }
107              
108             sub get_message {
109 0     0 1   my $self = shift;
110 0   0       my $class = ref($self) || $self;
111              
112 0           my $message;
113              
114 0           foreach my $key (keys(%{$self})) {
  0            
115             # everything but '_xxx_'
116 0 0         $key =~ /^\_.+\_$/ and next;
117 0           $message->{$key} = $self->{$key}
118             }
119              
120 0           my $datagramsize = SYSLOGD_MAX_SIZE;
121 0 0         if (@_ == 1) {
122 0           $LASTERROR = "Insufficient number of args: @_";
123             return undef
124 0           } else {
125 0           my %args = @_;
126 0           for (keys(%args)) {
127             # -maxsize
128 0 0         if (/^-?(?:max)?size$/i) {
    0          
129 0 0         if ($args{$_} =~ /^\d+$/) {
    0          
    0          
130 0 0 0       if (($args{$_} >= 1) && ($args{$_} <= SYSLOGD_MAX_SIZE)) {
131 0           $datagramsize = $args{$_}
132             }
133             } elsif ($args{$_} =~ /^rfc$/i) {
134 0           $datagramsize = SYSLOGD_RFC_SIZE
135             } elsif ($args{$_} =~ /^rec(?:ommend)?(?:ed)?$/i) {
136 0           $datagramsize = SYSLOGD_REC_SIZE
137             } else {
138 0           $LASTERROR = "Not a valid size: $args{$_}";
139             return undef
140 0           }
141             # -timeout
142             } elsif (/^-?timeout$/i) {
143 0 0         if ($args{$_} =~ /^\d+$/) {
144 0           $message->{Timeout} = $args{$_}
145             } else {
146 0           $LASTERROR = "Invalid timeout - $args{$_}";
147             return undef
148 0           }
149             }
150             }
151             }
152              
153 0           my $Timeout = $message->{Timeout};
154 0           my $udpserver = $self->{_UDPSERVER_};
155 0           my $datagram;
156              
157 0 0         if ($Timeout != 0) {
158             # vars for IO select
159 0           my ($rin, $rout, $ein, $eout) = ('', '', '', '');
160 0           vec($rin, fileno($udpserver), 1) = 1;
161              
162             # check if a message is waiting
163 0 0         if (! select($rout=$rin, undef, $eout=$ein, $Timeout)) {
164 0           $LASTERROR = "Timed out waiting for datagram";
165 0           return(0)
166             }
167             }
168              
169             # read the message
170 0 0         if ($udpserver->recv($datagram, $datagramsize)) {
171              
172 0           $message->{_MESSAGE_}{PeerPort} = $udpserver->SUPER::peerport;
173 0           $message->{_MESSAGE_}{PeerAddr} = $udpserver->SUPER::peerhost;
174 0           $message->{_MESSAGE_}{datagram} = $datagram;
175              
176 0           return bless $message, $class
177             }
178              
179 0           $LASTERROR = sprintf "Socket RECV error: $!";
180             return undef
181 0           }
182              
183             sub process_message {
184 0     0 1   my $self = shift;
185 0   0       my $class = ref($self) || $self;
186              
187             ### Allow to be called as subroutine
188             # Net::Syslogd->process_message($data)
189 0 0 0       if (($self eq $class) && ($class eq __PACKAGE__)) {
190 0           my %th;
191 0           $self = \%th;
192 0           ($self->{_MESSAGE_}{datagram}) = @_
193             }
194             # Net::Syslogd::process_message($data)
195 0 0         if ($class ne __PACKAGE__) {
196 0           my %th;
197 0           $self = \%th;
198 0           ($self->{_MESSAGE_}{datagram}) = $class;
199 0           $class = __PACKAGE__
200             }
201              
202             # Syslog RFC 3164 correct format:
203             # <###>Mmm dd hh:mm:ss hostname tag msg
204             #
205             # NOTE: This module parses the tag and msg as a single field called msg
206             ######
207             # Cisco:
208             # service timestamps log uptime
209             # <189>82: 00:20:10: %SYS-5-CONFIG_I: Configured from console by cisco on vty0 (192.168.200.1)
210             # service timestamps log datetime
211             # <189>83: *Oct 16 21:41:00: %SYS-5-CONFIG_I: Configured from console by cisco on vty0 (192.168.200.1)
212             # service timestamps log datetime msec
213             # <189>88: *Oct 16 21:46:48.671: %SYS-5-CONFIG_I: Configured from console by cisco on vty0 (192.168.200.1)
214             # service timestamps log datetime year
215             # <189>86: *Oct 16 2010 21:45:56: %SYS-5-CONFIG_I: Configured from console by cisco on vty0 (192.168.200.1)
216             # service timestamps log datetime show-timezone
217             # <189>92: *Oct 16 21:49:30 UTC: %SYS-5-CONFIG_I: Configured from console by cisco on vty0 (192.168.200.1)
218             # service timestamps log datetime msec year
219             # <189>90: *Oct 16 2010 21:47:50.439: %SYS-5-CONFIG_I: Configured from console by cisco on vty0 (192.168.200.1)
220             # service timestamps log datetime msec show-timezone
221             # <189>93: *Oct 16 21:51:13.823 UTC: %SYS-5-CONFIG_I: Configured from console by cisco on vty0 (192.168.200.1)
222             # service timestamps log datetime year show-timezone
223             # <189>94: *Oct 16 2010 21:51:49 UTC: %SYS-5-CONFIG_I: Configured from console by cisco on vty0 (192.168.200.1)
224             # service timestamps log datetime msec year show-timezone
225             # <189>91: *Oct 16 2010 21:48:41.663 UTC: %SYS-5-CONFIG_I: Configured from console by cisco on vty0 (192.168.200.1)
226             # IPv4 only
227             # my $regex = '<(\d{1,3})>[\d{1,}: \*]*((?:[JFMASONDjfmasond]\w\w) {1,2}(?:\d+)(?: \d{4})* (?:\d{2}:\d{2}:\d{2}[\.\d{1,3}]*)(?: [A-Z]{1,3})*)?:*\s*(?:((?:[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})|(?:[a-zA-Z0-9\-]+)) )?(.*)';
228             # IPv6
229 0           my $regex = '<(\d{1,3})>[\d{1,}: \*]*((?:[JFMASONDjfmasond]\w\w) {1,2}(?:\d+)(?: \d{4})? (?:\d{2}:\d{2}:\d{2}[\.\d{1,3}]*)(?: [A-Z]{1,3}:)?)?:?\s*(?:((?:[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})|(?:[a-zA-Z0-9\-]+)|(?:(?:(?:[0-9A-Fa-f]{1,4}:){7}(?:[0-9A-Fa-f]{1,4}|:))|(?:(?:[0-9A-Fa-f]{1,4}:){6}(?::[0-9A-Fa-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(?:(?:[0-9A-Fa-f]{1,4}:){5}(?:(?:(?::[0-9A-Fa-f]{1,4}){1,2})|:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(?:(?:[0-9A-Fa-f]{1,4}:){4}(?:(?:(?::[0-9A-Fa-f]{1,4}){1,3})|(?:(?::[0-9A-Fa-f]{1,4})?:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9A-Fa-f]{1,4}:){3}(?:(?:(?::[0-9A-Fa-f]{1,4}){1,4})|(?:(?::[0-9A-Fa-f]{1,4}){0,2}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9A-Fa-f]{1,4}:){2}(?:(?:(?::[0-9A-Fa-f]{1,4}){1,5})|(?:(?::[0-9A-Fa-f]{1,4}){0,3}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9A-Fa-f]{1,4}:){1}(?:(?:(?::[0-9A-Fa-f]{1,4}){1,6})|(?:(?::[0-9A-Fa-f]{1,4}){0,4}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?::(?:(?:(?::[0-9A-Fa-f]{1,4}){1,7})|(?:(?::[0-9A-Fa-f]{1,4}){0,5}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(?:%.+)?) )?(.*)';
230              
231             # If more than 1 argument, parse the options
232 0 0         if (@_ != 1) {
233 0           my %args = @_;
234 0           for (keys(%args)) {
235             # -datagram
236 0 0 0       if ((/^-?data(?:gram)?$/i) || (/^-?pdu$/i)) {
237 0           $self->{_MESSAGE_}{datagram} = $args{$_}
238             }
239             # -regex
240 0 0         if (/^-?regex$/i) {
241 0 0         if ($args{$_} =~ /^rfc(?:3164)?$/i) {
242             # Strict RFC 3164
243 0           $regex = '<(\d{1,3})>((?:[JFMASONDjfmasond]\w\w) {1,2}(?:\d+)(?: \d{4})? (?:\d{2}:\d{2}:\d{2}))?:*\s*(?:((?:[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})|(?:[a-zA-Z0-9\-]+)) )?(.*)'
244             } else {
245 0           $regex = $args{$_};
246             # strip leading / if found
247 0           $regex =~ s/^\///;
248             # strip trailing / if found
249 0           $regex =~ s/\/$//
250             }
251             }
252             }
253             }
254              
255 0           my $Cregex = qr/$regex/;
256              
257             # Parse message
258 0           $self->{_MESSAGE_}{datagram} =~ /$Cregex/;
259              
260 0           $self->{_MESSAGE_}{priority} = $1;
261 0   0       $self->{_MESSAGE_}{time} = $2 || 0;
262 0   0       $self->{_MESSAGE_}{hostname} = $3 || 0;
263 0           $self->{_MESSAGE_}{message} = $4;
264 0           $self->{_MESSAGE_}{severity} = $self->{_MESSAGE_}{priority} % 8;
265 0           $self->{_MESSAGE_}{facility} = ($self->{_MESSAGE_}{priority} - $self->{_MESSAGE_}{severity}) / 8;
266              
267 0           $self->{_MESSAGE_}{hostname} =~ s/\s+//;
268 0           $self->{_MESSAGE_}{time} =~ s/:$//;
269              
270 0           return bless $self, $class
271             }
272              
273             sub server {
274 0     0 1   my $self = shift;
275 0           return $self->{_UDPSERVER_}
276             }
277              
278             sub datagram {
279 0     0 1   my $self = shift;
280 0           return $self->{_MESSAGE_}{datagram}
281             }
282              
283             sub remoteaddr {
284 0     0 1   my $self = shift;
285 0           return $self->{_MESSAGE_}{PeerAddr}
286             }
287              
288             sub remoteport {
289 0     0 1   my $self = shift;
290 0           return $self->{_MESSAGE_}{PeerPort}
291             }
292              
293             sub priority {
294 0     0 1   my $self = shift;
295 0           return $self->{_MESSAGE_}{priority}
296             }
297              
298             sub facility {
299 0     0 1   my ($self, $arg) = @_;
300              
301 0 0 0       if (defined($arg) && ($arg >= 1)) {
302 0           return $self->{_MESSAGE_}{facility}
303             } else {
304 0           return $FACILITY[$self->{_MESSAGE_}{facility}]
305             }
306             }
307              
308             sub severity {
309 0     0 1   my ($self, $arg) = @_;
310              
311 0 0 0       if (defined($arg) && ($arg >= 1)) {
312 0           return $self->{_MESSAGE_}{severity}
313             } else {
314 0           return $SEVERITY[$self->{_MESSAGE_}{severity}]
315             }
316             }
317              
318             sub time {
319 0     0 1   my $self = shift;
320 0           return $self->{_MESSAGE_}{time}
321             }
322              
323             sub hostname {
324 0     0 1   my $self = shift;
325 0           return $self->{_MESSAGE_}{hostname}
326             }
327              
328             sub message {
329 0     0 1   my $self = shift;
330 0           return $self->{_MESSAGE_}{message}
331             }
332              
333             sub error {
334 0     0 1   return($LASTERROR)
335             }
336              
337             ########################################################
338             # End Public Module
339             ########################################################
340              
341             1;
342              
343             __END__