File Coverage

blib/lib/Net/Write/Layer.pm
Criterion Covered Total %
statement 91 138 65.9
branch 11 38 28.9
condition 14 53 26.4
subroutine 23 25 92.0
pod n/a
total 139 254 54.7


line stmt bran cond sub pod time code
1             #
2             # $Id: Layer.pm 2011 2015-02-15 17:07:47Z gomor $
3             #
4             package Net::Write::Layer;
5 4     4   17848 use strict;
  4         7  
  4         162  
6 4     4   19 use warnings;
  4         6  
  4         132  
7              
8 4     4   15 use base qw(Exporter Class::Gomor::Array);
  4         6  
  4         2545  
9             our @AS = qw(
10             dev
11             dst
12             protocol
13             family
14             _io
15             _sockaddr
16             );
17             __PACKAGE__->cgBuildIndices;
18             __PACKAGE__->cgBuildAccessorsScalar(\@AS);
19              
20             sub _setIpProtoIpConstant {
21 4     4   6 my $val = 0;
22 4 50 33     49 if (defined(&IPPROTO_IP)) {
    50 33        
      33        
      0        
      0        
23 0         0 $val = &IPPROTO_IP;
24             }
25             elsif ($^O eq 'darwin'
26             || $^O eq 'linux'
27             || $^O eq 'freebsd'
28             || $^O eq 'openbsd'
29             || $^O eq 'netbsd'
30             || $^O eq 'aix') {
31 4         6 $val = 0;
32             }
33 4     4   41 eval "use constant NW_IPPROTO_IP => $val;";
  4         5  
  4         199  
  4         275  
34             }
35              
36             sub _setIpProtoIpv6Constant {
37 4     4   4 my $val = 0;
38 4 50 33     26 if (defined(&IPPROTO_IPv6)) {
    50          
39 0         0 $val = &IPPROTO_IPv6;
40             }
41             elsif ($^O eq 'linux'
42             || $^O eq 'freebsd') {
43 4         7 $val = 41;
44             }
45 4     4   18 eval "use constant NW_IPPROTO_IPv6 => $val;";
  4         4  
  4         121  
  4         205  
46             }
47              
48             sub _setIpProtoRawConstant {
49 4     4   6 my $val = 255;
50 4 50 33     53 if (defined(&IPPROTO_RAW)) {
    50 33        
      33        
      0        
      0        
51 0         0 $val = &IPPROTO_RAW;
52             }
53             elsif ($^O eq 'darwin'
54             || $^O eq 'linux'
55             || $^O eq 'freebsd'
56             || $^O eq 'openbsd'
57             || $^O eq 'netbsd'
58             || $^O eq 'aix') {
59 4         5 $val = 255;
60             }
61 4     4   44 eval "use constant NW_IPPROTO_RAW => $val;";
  4         5  
  4         134  
  4         233  
62             }
63              
64             sub _setIpHdrInclConstant {
65 4     4   4 my $val = 2;
66 4 50 33     83 if (defined(&IP_HDRINCL)) {
    50 33        
    0 33        
      33        
      33        
      33        
67 0         0 $val = &IP_HDRINCL;
68             }
69             elsif ($^O eq 'darwin'
70             || $^O eq 'freebsd'
71             || $^O eq 'openbsd'
72             || $^O eq 'netbsd'
73             || $^O eq 'linux'
74             || $^O eq 'aix'
75             || $^O eq 'cygwin') {
76 4         5 $val = 2;
77             }
78             elsif ($^O eq 'hpux') {
79 0         0 $val = 0x1002;
80             }
81 4     4   16 eval "use constant NW_IP_HDRINCL => $val;";
  4         4  
  4         122  
  4         240  
82             }
83              
84             sub _setAfinet6Constant {
85 4     4   6 my $val = 10; # Default value, in case we don't know.
86             # This is the value from a Ubuntu 14.10 system.
87 4         5 eval {
88 4         2403 require Socket;
89 4         14191 Socket->import(qw(AF_INET6));
90             };
91 4 50       22 if (! $@) { # AF_INET6 constant found in Socket module.
92 4         12 $val = Socket::AF_INET6();
93             }
94             else { # No AF_INET6 in Socket module, we try with Socket6.
95 0         0 eval {
96 0         0 require Socket6;
97 0         0 Socket6->import(qw(AF_INET6));
98             };
99 0 0       0 if (! $@) { # AF_INET6 constant found in Socket6 module.
100 0         0 $val = Socket6::AF_INET6();
101             }
102             }
103              
104             # If constant is not found, we stick to the default value.
105 4     4   31 eval "use constant NW_AF_INET6 => $val;";
  4         7  
  4         230  
  4         323  
106             }
107              
108             sub _setInetPtonSub {
109 4     4   45438 no strict 'refs';
  4         9  
  4         669  
110              
111 4     4   7 eval {
112 4         20 require Socket;
113 4         100 Socket->import(qw(AF_INET6 inet_pton));
114             };
115 4 50       21 if (! $@) { # Socket supports AF_INET6 family and inet_pton.
116 4         10 *{__PACKAGE__.'::nw_inet_pton'} = \&Socket::inet_pton;
  4         19  
117              
118 4         9 return 1;
119             }
120              
121 0         0 eval {
122 0         0 require Socket6;
123 0         0 Socket6->import(qw(AF_INET6 inet_pton));
124             };
125 0 0       0 if (! $@) { # Socket6 supports AF_INET6 family and inet_pton.
126 0         0 *{__PACKAGE__.'::nw_inet_pton'} = \&Socket6::inet_pton;
  0         0  
127              
128 0         0 return 1;
129             }
130              
131 0         0 die("[-] Net::Write: inet_pton: not supported by Socket nor Socket6: ".
132             "try upgrading your Perl version or Socket/Socket6 modules.\n");
133             }
134              
135             sub _setGetaddrinfoSub {
136 4     4   23 no strict 'refs';
  4         7  
  4         1825  
137              
138             # Try to use getaddrinfo() from main Socket module.
139 4     4   8 eval {
140 4         18 require Socket;
141 4         93 Socket->import(qw(AF_INET AF_INET6 getaddrinfo));
142             };
143 4 50       15 if (! $@) { # Socket supports AF_INET6 family and getaddrinfo.
144 4         17 *{__PACKAGE__.'::nw_getsaddr'} = sub {
145 0     0   0 my ($dest, $family, $protocol, $socktype) = @_;
146              
147             #print STDERR "*** Socket support OK\n";
148              
149 0         0 my %hints = (
150             family => $family,
151             # If we activate that, it breaks on some OS like Mac OS X
152             #protocol => $protocol,
153             #socktype => $socktype,
154             );
155 0         0 my ($err, @res) = Socket::getaddrinfo($dest, "", \%hints);
156 0 0       0 if ($err) {
157 0         0 return _croak("@{[(caller(0))[3]]}: getaddrinfo: $err");
  0         0  
158             }
159              
160 0 0       0 if (@res > 0) {
161 0         0 my $h = $res[0];
162 0         0 return $h->{addr};
163             }
164              
165 0         0 return _croak("@{[(caller(0))[3]]}: getaddrinfo: error: $!");
  0         0  
166 4         30 };
167              
168 4         132 return 1;
169             }
170            
171             # Main Socket module does not support getaddrinfo(), we try using Socket6
172 0           eval {
173 0           require Socket6;
174 0           Socket6->import(qw(AF_INET AF_INET6 getaddrinfo));
175             };
176 0 0         if (! $@) {
177 0           *{__PACKAGE__.'::nw_getsaddr'} = sub {
178 0     0     my ($dest, $family, $protocol, $socktype) = @_;
179              
180             #print STDERR "*** Fallback on Socket6 support\n";
181              
182 0 0         my @res = Socket6::getaddrinfo($dest, "", $family, $socktype)
183 0           or return _croak("@{[(caller(0))[3]]}: getaddrinfo: $!");
184              
185 0 0         if (@res >= 5) {
186 0           my $saddr = $res[3];
187 0           return $saddr;
188             }
189              
190 0           return _croak("@{[(caller(0))[3]]}: getaddrinfo: error: $!");
  0            
191 0           };
192              
193 0           return 1;
194             }
195              
196 0           return 1;
197             }
198              
199             BEGIN {
200 4     4   26 my $osname = {
201             cygwin => \&_checkWin32,
202             MSWin32 => \&_checkWin32,
203             };
204              
205             {
206 4     4   23 no strict 'refs';
  4         5  
  4         302  
  4         4  
207 4   50     34 *{__PACKAGE__.'::_check'} = $osname->{$^O} || \&_checkOther;
  4         16  
208             }
209              
210 4         10 _setIpProtoIpConstant();
211 4         9 _setIpProtoIpv6Constant();
212 4         7 _setIpProtoRawConstant();
213 4         12 _setIpHdrInclConstant();
214 4         8 _setAfinet6Constant();
215 4         15 _setInetPtonSub();
216 4         12 _setGetaddrinfoSub();
217             }
218              
219 4     4   23 no strict 'vars';
  4         15  
  4         148  
220              
221 4     4   20 use Socket qw(SOCK_RAW);
  4         6  
  4         182  
222 4     4   2419 use IO::Socket;
  4         66255  
  4         23  
223 4     4   5645 use Net::Pcap;
  0         0  
  0         0  
224              
225             use constant NW_AF_INET => AF_INET();
226             use constant NW_AF_UNSPEC => AF_UNSPEC();
227              
228             use constant NW_IPPROTO_ICMPv4 => 1;
229             use constant NW_IPPROTO_TCP => 6;
230             use constant NW_IPPROTO_UDP => 17;
231             use constant NW_IPPROTO_ICMPv6 => 58;
232              
233             our %EXPORT_TAGS = (
234             constants => [qw(
235             NW_AF_INET
236             NW_AF_INET6
237             NW_AF_UNSPEC
238             NW_IPPROTO_IP
239             NW_IPPROTO_IPv6
240             NW_IPPROTO_ICMPv4
241             NW_IPPROTO_TCP
242             NW_IPPROTO_UDP
243             NW_IPPROTO_ICMPv6
244             NW_IP_HDRINCL
245             NW_IPPROTO_RAW
246             )],
247             subs => [qw(
248             nw_inet_pton
249             nw_getsaddr
250             )],
251             );
252              
253             our @EXPORT_OK = (
254             @{$EXPORT_TAGS{constants}},
255             @{$EXPORT_TAGS{subs}},
256             );
257              
258             sub _checkWin32 {
259             return 1;
260             }
261              
262             sub _checkOther {
263             if ($>) {
264             print STDERR "[-] Must be EUID 0 (or equivalent) to open a device for ".
265             "writing.\n";
266             return;
267             }
268              
269             return 1;
270             }
271              
272             sub new {
273             my $self = shift->SUPER::new(
274             @_,
275             );
276              
277             return $self;
278             }
279              
280             sub _croak {
281             my ($msg) = @_;
282             print STDERR "[-] $msg\n";
283             return;
284             }
285              
286             sub open {
287             my $self = shift;
288             my ($hdrincl) = @_;
289              
290             _check() or return;
291              
292             my $saddr = nw_getsaddr($self->[$__dst], $self->[$__family], $self->[$__protocol])
293             or return _croak("@{[(caller(0))[3]]}: nw_getsaddr: error");
294              
295             $self->[$___sockaddr] = $saddr;
296              
297             socket(my $s, $self->[$__family], SOCK_RAW(), $self->[$__protocol])
298             or return _croak("@{[(caller(0))[3]]}: socket: $!");
299              
300             my $fd = fileno($s)
301             or return _croak("@{[(caller(0))[3]]}: fileno: $!");
302              
303             if ($hdrincl) {
304             $self->_setIpHdrincl($s, $self->[$__family])
305             or return _croak("@{[(caller(0))[3]]}: setsockopt: $!");
306             }
307              
308             my $io = IO::Socket->new;
309             $io->fdopen($fd, 'w')
310             or return _croak("@{[(caller(0))[3]]}: fdopen: $!");
311              
312             $self->[$___io] = $io;
313              
314             return 1;
315             }
316              
317             sub send {
318             my $self = shift;
319             my ($raw) = @_;
320              
321             while (1) {
322             my $ret = CORE::send($self->_io, $raw, 0, $self->_sockaddr);
323             unless ($ret) {
324             if ($!{ENOBUFS}) {
325             $self->cgDebugPrint(2, "ENOBUFS returned, sleeping for 1 second");
326             sleep 1;
327             next;
328             }
329             elsif ($!{EHOSTDOWN}) {
330             $self->cgDebugPrint(2, "host is down");
331             last;
332             }
333             print STDERR "[!] @{[(caller(0))[3]]}: $!\n";
334             return;
335             }
336             last;
337             }
338              
339             return 1;
340             }
341              
342             sub close { shift->_io->close }
343              
344             1;
345              
346             __END__