File Coverage

blib/lib/Audio/Daemon.pm
Criterion Covered Total %
statement 9 169 5.3
branch 0 52 0.0
condition 0 18 0.0
subroutine 3 20 15.0
pod 0 17 0.0
total 12 276 4.3


line stmt bran cond sub pod time code
1             package Audio::Daemon;
2              
3 1     1   139795 use IO::Socket::INET;
  1         33460  
  1         9  
4 1     1   394080 use IO::Select;
  1         2236  
  1         189  
5 1     1   10 use vars qw($VERSION);
  1         8  
  1         2614  
6             $VERSION='0.99Beta';
7              
8             sub new {
9 0     0 0   my ($proto, %arg) = @_;
10 0   0       my $class = ref($proto) || $proto;
11 0           my $self = {};
12 0           foreach my $k (qw/Allow Deny Server Port Log Pass/) {
13 0 0         $self->{$k} = $arg{$k} if (defined $arg{$k});
14             }
15             # if you feel like changing the main seperator value:
16 0           $self->{sep} = sprintf("%c", 255);
17             # or changing the secondary seperator value:
18 0           $self->{subsep} = sprintf("%c", 254);
19 0           bless($self, $class);
20 0 0         $self->parse_acl('Allow') if ($self->{Allow});
21 0 0         $self->parse_acl('Deny') if ($self->{Deny});
22 0           return $self;
23             }
24              
25 0     0 0   sub debug { my $self = shift; return $self->log('debug', @_); }
  0            
26 0     0 0   sub info { my $self = shift; return $self->log('info', @_); }
  0            
27 0     0 0   sub error { my $self = shift; return $self->log('error', @_); }
  0            
28 0     0 0   sub crit { my $self = shift; return $self->log('crit', @_); }
  0            
29 0     0 0   sub warn { my $self = shift; return $self->log('warn', @_); }
  0            
30              
31             sub log {
32 0     0 0   my $self = shift;
33 0           my @caller = caller(2);
34             # print "caller line is ".$caller[2]."\n";
35             # ($package, $filename, $line, $subroutine, $hasargs,
36             # $wantarray, $evaltext, $is_require, $hints, $bitmask)
37 0 0         if (defined $self->{Log}) {
38 0           &{$self->{Log}}(@_, @caller);
  0            
39 0           return 1;
40             } else {
41 0           return 0;
42             }
43             }
44              
45             sub socket {
46 0     0 0   my $self = shift;
47 0 0         if (ref $self->{socket} eq 'IO::Socket::INET') {
48 0           $self->debug('caller requested existing socket');
49 0           return $self->{socket}
50             }
51 0           my $package = (split '::', (ref $self))[-1];
52 0 0         if ($package ne 'Client') {
53 0 0         if (! defined $self->{Port}) {
54 0           $self->crit("No Port defined for socket creation");
55 0           return;
56             }
57 0           $self->{socket} = IO::Socket::INET->new(LocalPort => $self->{Port}, Proto=>'udp');
58             } else {
59 0 0 0       if (! defined $self->{Port} || ! defined $self->{Server}) {
60 0           $self->crit("Need both the Port and Server defined for socket creation");
61 0           return;
62             }
63 0           $self->{socket} = IO::Socket::INET->new(PeerPort => $self->{Port}, PeerAddr => $self->{Server}, Proto=>'udp');
64             }
65 0 0         if (! defined $self->{socket}) {
66 0           $self->crit("Failed to initialize Socket: $!");
67 0           return;
68             }
69 0           return $self->{socket};
70             }
71              
72             # randomze takes in an array reference and returns an array of randomzied indixes
73             sub randomize {
74 0     0 0   my $self = shift;
75 0           my $start = shift;
76 0 0         return unless (ref $start eq 'ARRAY');
77 0           my @stack;
78 0           my $count = 0;
79 0           while (scalar @$start) {
80 0           my $pick = int (rand() * (scalar @$start));
81 0           push @stack, $start->[$pick];
82 0           splice(@{$start}, $pick, 1);
  0            
83             }
84 0           $self->{random} = \@stack;
85 0           my @revstack;
86 0           foreach my $t (0..$#stack) {
87 0           $revstack[$stack[$t]] = $t
88             }
89 0           $self->{revrandom} = \@revstack;
90             }
91              
92             # Generic read routine, should return a hash ref of values read from client.
93             # will return undef if request comes from unallowed IP.
94             sub read_client {
95 0     0 0   my $self = shift;
96 0           my $socket = $self->socket;
97 0           my ($newmsg, $remote, $iaddr);
98 0           my $from_addr = $socket->recv($newmsg, 1024, undef);
99 0           ($remote->{port}, $iaddr) = sockaddr_in($from_addr);
100 0           $remote->{ip} = inet_ntoa($iaddr);
101             # $self->debug("Remote :".$remote->{ip}." : ".$remote->{port});
102            
103 0 0         return unless ($self->allowed_host($remote));
104 0 0         unless (length $newmsg > 0) {
105 0           $self->warn("Message length was ".(length $newmsg)." returning...");
106 0           return;
107             }
108 0           my @a;
109 0           ($remote->{cmd}, @a) = split $self->{sep}, $newmsg;
110 0           $remote->{args} = \@a;
111 0           return $remote;
112             }
113              
114             # called from read_client to verify client is an allowed IP
115             sub allowed_host {
116 0     0 0   my $self = shift;
117 0           my $remote = shift;
118 0           my $ip = $self->convert_ip($remote->{ip});
119 0           $ip = join '', sprintf("%03d%03d%03d%03d", unpack("CCCC", $ip));
120 0 0 0       if (defined $self->{Deny} && ref $self->{Deny}) {
121 0           foreach my $ref (@{$self->{Deny}}) {
  0            
122 0 0 0       if ($ref->{low} <= $ip && $ip <= $ref->{high}) {
123 0           $self->crit('Host '.$remote->{ip}.' is denied by Deny Rule');
124 0           return 0;
125             }
126             }
127             }
128 0 0 0       if (defined $self->{Allow} && ref $self->{Allow}) {
129 0           foreach my $ref (@{$self->{Allow}}) {
  0            
130 0 0 0       if ($ref->{low} <= $ip && $ip <= $ref->{high}) {
131             # $self->debug('Host '.$remote->{ip}.' is allowed by Allow rule');
132 0           return 1;
133             }
134             }
135 0           $self->crit('Host '.$remote->{ip}.' is denied by Allow Rule');
136 0           return 0;
137             }
138             # else, it's not denied and no allow only is specified, c'mon in.
139 0           return 1;
140             }
141            
142             # setting up IP ranges, called when new socket initiated.
143             sub parse_acl {
144 0     0 0   my $self = shift;
145 0           my $baseacl = shift;
146             # $self->debug("Parsing $baseacl");
147 0           $self->{$baseacl}=~s/\s+//g;
148 0           my @acl = split ',', $self->{$baseacl};
149 0           my @section;
150 0           foreach my $string (@acl) {
151             # $self->debug("Looking at $string");
152 0           push @section, $self->set_ip_range($string);
153             # $self->debug('low -> '.($section[$#section]{low}).' high -> '.($section[$#section]{high}));
154 0 0         pop @section if (! defined $section[$#section]);
155             }
156 0           $self->{$baseacl} = \@section;
157             # $self->debug("Leaving");
158             }
159              
160             # sets a low and high IP comparison index
161             sub set_ip_range {
162 0     0 0   my $self = shift;
163 0           my $string = shift;
164 0           my ($low, $high);
165 0 0         if ($string=~/\-/) {
166 0           my @ip = split '-', $string;
167 0 0         if ($#ip != 1) {
168 0           $self->error("Unknown string: $string");
169 0           return;
170             }
171 0           foreach (@ip) {
172 0 0         if (! $self->valid_ip($_)) {
173 0           $self->error("Invalid IP address: $_");
174 0           return;
175             }
176             }
177 0           $low = sprintf("%03d%03d%03d%03d", unpack("CCCC", $self->convert_ip($ip[0])));
178 0           $high = sprintf("%03d%03d%03d%03d", unpack("CCCC", $self->convert_ip($ip[1])));
179             } else {
180 0           my ($addr, $mask) = split '/', $string;
181 0 0         unless ($self->valid_ip($addr)) {
182 0           $self->error("Invalid IP address: $addr");
183 0           return;
184             }
185 0 0         $mask = 32 if (! defined $mask);
186 0           my $addr_bin = $self->convert_ip($addr);
187 0           my $mask_bin = $self->convert_mask($mask);
188 0           my $inv_mask = $self->inv_mask($mask_bin);
189 0           my $broadcast = $inv_mask | $addr_bin;
190 0           my $network = $broadcast ^ $inv_mask;
191 0           $low = sprintf("%03d%03d%03d%03d", unpack("CCCC", $network));
192 0           $high = sprintf("%03d%03d%03d%03d", unpack("CCCC", $broadcast));
193             }
194 0           return {low=>$low, high=>$high};
195             }
196              
197             # just returns true if value passed in is a valid IP
198             # I got sick of typing this regex.
199             sub valid_ip {
200 0     0 0   my $self = shift;
201 0 0         return 1 if ($_[0]=~/\d{0,3}(\.\d{0,3}){3}/);
202 0           return;
203             }
204              
205             # invert subnet mask
206             sub inv_mask {
207 0     0 0   my $self = shift;
208 0           my @sects = split '', shift;
209 0           my $overall = '';
210 0           foreach my $pos (0..$#sects) {
211 0           my $current = '';
212 0           vec($current, 0, 8) = 0;
213 0           for (my $c = 1; $c < 255; $c*=2) {
214 0           my $one = '';
215 0           vec($one, 0, 8) = $c;
216 0 0         if (! unpack("C", ($sects[$pos] & $one))) {
217 0           $current = $current | $one;
218             }
219             }
220 0           $overall .= $current;
221             }
222 0           return $overall;
223             }
224              
225             sub convert_ip {
226 0     0 0   my $self = shift;
227 0           return join '', map {pack("C", $_)} (split /\./, shift);
  0            
228             }
229              
230             sub convert_mask {
231 0     0 0   my $self = shift;
232 0           my $mask = shift;
233 0 0         return $self->convert_ip($mask) if ($self->valid_ip($mask));
234 0           my $overall;
235 0           my $string = reverse sprintf("%032s", '1' x $mask);
236 0           for (my $c=0; $c<32; $c+=8) {
237 0           my $out = 0;
238 0           my @wank = split '', substr($string, $c, 8);
239 0           for (my $m=1; $m<255; $m*=2) {
240 0 0         $out += $m if (pop @wank);
241             }
242 0           $overall .= pack("C", $out);
243             }
244 0           return $overall;
245             }
246             1;
247              
248             __END__