File Coverage

blib/lib/Net/SAP.pm
Criterion Covered Total %
statement 16 18 88.8
branch n/a
condition n/a
subroutine 6 6 100.0
pod n/a
total 22 24 91.6


line stmt bran cond sub pod time code
1             package Net::SAP;
2              
3             ################
4             #
5             # SAP: Session Announcement Protocol (RFC2974)
6             #
7             # Nicholas J Humfrey
8             # njh@cpan.org
9             #
10              
11 2     2   44880 use strict;
  2         4  
  2         73  
12 2     2   10 use Carp;
  2         4  
  2         272  
13              
14 2     2   2670 use Net::SAP::Packet;
  2         6  
  2         70  
15 2     2   11 use Socket qw/ unpack_sockaddr_in /;
  2         4  
  2         97  
16 2     2   9 use Socket6 qw/ inet_ntop inet_pton unpack_sockaddr_in6 /;
  2         4  
  2         87  
17 2     2   1015 use IO::Socket::Multicast6;
  0            
  0            
18              
19             use vars qw/$VERSION/;
20             our $VERSION="0.10";
21              
22              
23              
24             # User friendly names for multicast groups
25             my %groups = (
26             'ipv4'=> '224.2.127.254',
27             'ipv4-local'=> '239.255.255.255',
28             'ipv4-org'=> '239.195.255.255',
29             'ipv4-global'=> '224.2.127.254',
30            
31             'ipv6-node'=> 'FF01::2:7FFE',
32             'ipv6-link'=> 'FF02::2:7FFE',
33             'ipv6-site'=> 'FF05::2:7FFE',
34             'ipv6-org'=> 'FF08::2:7FFE',
35             'ipv6-global'=> 'FF0E::2:7FFE',
36             );
37              
38             my $SAP_PORT = 9875;
39              
40              
41              
42             sub new {
43             my $class = shift;
44             my ($group) = @_;
45            
46            
47             # Work out the multicast group to use
48             croak "Missing group parameter" unless defined $group;
49             if (exists $groups{$group}) {
50             $group = $groups{$group};
51             }
52              
53              
54             # Store parameters
55             my $self = {
56             'group' => $group,
57             'port' => $SAP_PORT
58             };
59            
60            
61             # Create Multicast Socket
62             $self->{'socket'} = new IO::Socket::Multicast6(
63             LocalAddr => $self->{'group'},
64             LocalPort => $SAP_PORT )
65             || return undef;
66            
67             # Set the TTL for transmitted packets
68             $self->{'socket'}->mcast_ttl( 127 );
69            
70             # Join the multicast group
71             $self->{'socket'}->mcast_add( $self->{'group'} ) ||
72             die "Failed to join multicast group: $!";
73            
74              
75             bless $self, $class;
76             return $self;
77             }
78              
79              
80             #
81             # Returns the multicast group the socket is bound to
82             #
83             sub group {
84             my $self = shift;
85             return $self->{'group'};
86             }
87              
88              
89             #
90             # Sets the TTL for packets sent
91             #
92             sub ttl {
93             my $self = shift;
94             my ($ttl) = @_;
95            
96             # Set new TTL if specified
97             if (defined $ttl) {
98             return undef if ($ttl<0 or $ttl>127);
99             $self->{'socket'}->mcast_ttl($ttl);
100             }
101              
102             return $self->{'socket'}->mcast_ttl();
103             }
104              
105              
106             #
107             # Blocks until a valid SAP packet is received
108             #
109             sub receive {
110             my $self = shift;
111             my $sap_packet = undef;
112            
113            
114             while(!defined $sap_packet) {
115            
116             # Receive a packet
117             my $data = undef;
118             my $from = $self->{'socket'}->recv( $data, 1500 );
119             die "Failed to receive packet: $!" unless (defined $from);
120             next unless (defined $data and length($data));
121            
122             # Create new packet object from the data we received
123             $sap_packet = new Net::SAP::Packet( $data );
124             next unless (defined $sap_packet);
125            
126             # Correct the origin on Stupid packets !
127             if ($sap_packet->origin_address() eq '' or
128             $sap_packet->origin_address() eq '0.0.0.0' or
129             $sap_packet->origin_address() eq '1.2.3.4' )
130             {
131             if (sockaddr_family($from)==AF_INET) {
132             my ($from_port, $from_ip) = unpack_sockaddr_in( $from );
133             $from = inet_ntop( AF_INET, $from_ip );
134             } elsif (sockaddr_family($from)==AF_INET6) {
135             my ($from_port, $from_ip) = unpack_sockaddr_in6( $from );
136             $from = inet_ntop( AF_INET6, $from_ip );
137             } else {
138             warn "Unknown address family (family=".sockaddr_family($from).")\n";
139             }
140             $sap_packet->origin_address( $from );
141             }
142             }
143              
144             return $sap_packet;
145             }
146              
147              
148             sub send {
149             my $self = shift;
150             my ($packet) = @_;
151            
152             croak "Missing data to send." unless defined $packet;
153              
154              
155             # If it isn't a packet object, turn it into one
156             if (ref $packet eq 'Net::SDP') {
157             my $data = $packet->generate();
158             $packet = new Net::SAP::Packet();
159             $packet->payload( $data );
160             }
161             elsif (ref $packet ne 'Net::SAP::Packet') {
162             my $data = $packet;
163             $packet = new Net::SAP::Packet();
164             $packet->payload( $data );
165             }
166              
167            
168             # Assemble and send the packet
169             my $data = $packet->generate();
170             if (!defined $data) {
171             warn "Failed to create binary packet.";
172             return -1;
173             } elsif (length $data > 1024) {
174             warn "Packet is more than 1024 bytes, not sending.";
175             return -1;
176             } else {
177             return $self->{'socket'}->mcast_send( $data, $self->{'group'}, $self->{'port'} );
178             }
179             }
180              
181              
182             sub close {
183             my $self=shift;
184            
185             # Close the multicast socket
186             $self->{'socket'}->close();
187             undef $self->{'socket'};
188            
189             }
190              
191              
192             sub DESTROY {
193             my $self=shift;
194            
195             if (exists $self->{'socket'} and defined $self->{'socket'}) {
196             $self->close();
197             }
198             }
199              
200              
201             1;
202              
203             __END__