File Coverage

blib/lib/Net/Write/Layer.pm
Criterion Covered Total %
statement 91 155 58.7
branch 11 44 25.0
condition 14 53 26.4
subroutine 23 26 88.4
pod n/a
total 139 278 50.0


line stmt bran cond sub pod time code
1             #
2             # $Id: Layer.pm 2014 2015-03-04 06:16:57Z gomor $
3             #
4             package Net::Write::Layer;
5 4     4   16905 use strict;
  4         9  
  4         169  
6 4     4   21 use warnings;
  4         7  
  4         159  
7              
8 4     4   19 use base qw(Exporter Class::Gomor::Array);
  4         4  
  4         2561  
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   10 my $val = 0;
22 4 50 33     52 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   44 eval "use constant NW_IPPROTO_IP => $val;";
  4         4  
  4         208  
  4         296  
34             }
35              
36             sub _setIpProtoIpv6Constant {
37 4     4   7 my $val = 0;
38 4 50 33     31 if (defined(&IPPROTO_IPv6)) {
    50          
39 0         0 $val = &IPPROTO_IPv6;
40             }
41             elsif ($^O eq 'linux'
42             || $^O eq 'freebsd') {
43 4         8 $val = 41;
44             }
45 4     4   14 eval "use constant NW_IPPROTO_IPv6 => $val;";
  4         4  
  4         114  
  4         226  
46             }
47              
48             sub _setIpProtoRawConstant {
49 4     4   5 my $val = 255;
50 4 50 33     82 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         6 $val = 255;
60             }
61 4     4   17 eval "use constant NW_IPPROTO_RAW => $val;";
  4         5  
  4         132  
  4         271  
62             }
63              
64             sub _setIpHdrInclConstant {
65 4     4   5 my $val = 2;
66 4 50 33     77 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         8 $val = 2;
77             }
78             elsif ($^O eq 'hpux') {
79 0         0 $val = 0x1002;
80             }
81 4     4   15 eval "use constant NW_IP_HDRINCL => $val;";
  4         4  
  4         108  
  4         217  
82             }
83              
84             sub _setAfinet6Constant {
85 4     4   5 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         2360 require Socket;
89 4         14240 Socket->import(qw(AF_INET6));
90             };
91 4 50       22 if (! $@) { # AF_INET6 constant found in Socket module.
92 4         14 $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   25 eval "use constant NW_AF_INET6 => $val;";
  4         5  
  4         183  
  4         288  
106             }
107              
108             sub _setInetPtonSub {
109 4     4   46581 no strict 'refs';
  4         8  
  4         1067  
110              
111 4     4   7 eval {
112 4         15 require Socket;
113 4         86 Socket->import(qw(AF_INET6 inet_pton));
114             };
115 4 50       14 if (! $@) { # Socket supports AF_INET6 family and inet_pton.
116 4         7 *{__PACKAGE__.'::nw_inet_pton'} = \&Socket::inet_pton;
  4         12  
117              
118 4         8 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   19 no strict 'refs';
  4         4  
  4         2331  
137              
138             # Try to use getaddrinfo() from main Socket module.
139 4     4   5 eval {
140 4         13 require Socket;
141 4         74 Socket->import(qw(AF_INET AF_INET6 getaddrinfo));
142             };
143 4 50       12 if (! $@) { # Socket supports AF_INET6 family and getaddrinfo.
144 4         12 *{__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         19 };
167              
168 4         106 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             # If still not found, we rely on Socket::GetAddrInfo()
197 0           eval("use Socket::GetAddrInfo qw(getaddrinfo);");
198 0 0         if ($@) {
199 0           chomp($@);
200 0           die("[-] Net::Write: getaddrinfo: not supported, try installing ".
201             "Socket::GetAddrInfo [$@]\n");
202             }
203             else {
204 0           *{__PACKAGE__.'::nw_getsaddr'} = sub {
205 0     0     my ($dest, $family, $protocol, $socktype) = @_;
206              
207             #print STDERR "*** Fallback on Socket::GetAddrInfo support\n";
208              
209 0           my %hints = ( family => $family );
210 0           my ($err, @res) = Socket::GetAddrInfo::getaddrinfo($dest, "", \%hints);
211 0 0         if ($err) {
212 0           return _croak("@{[(caller(0))[3]]}: getaddrinfo: $err");
  0            
213             }
214              
215 0           my $ai = $res[0];
216 0 0         if (! defined($ai)) {
217 0           return _croak("@{[(caller(0))[3]]}: getaddrinfo: ai undefined");
  0            
218             }
219              
220 0           return $ai->{addr}; # return sockaddr struct
221 0           };
222             }
223              
224 0           return 1;
225             }
226              
227             BEGIN {
228 4     4   32 my $osname = {
229             cygwin => \&_checkWin32,
230             MSWin32 => \&_checkWin32,
231             };
232              
233             {
234 4     4   21 no strict 'refs';
  4         5  
  4         297  
  4         10  
235 4   50     37 *{__PACKAGE__.'::_check'} = $osname->{$^O} || \&_checkOther;
  4         17  
236             }
237              
238 4         12 _setIpProtoIpConstant();
239 4         12 _setIpProtoIpv6Constant();
240 4         9 _setIpProtoRawConstant();
241 4         10 _setIpHdrInclConstant();
242 4         10 _setAfinet6Constant();
243 4         10 _setInetPtonSub();
244 4         9 _setGetaddrinfoSub();
245             }
246              
247 4     4   18 no strict 'vars';
  4         14  
  4         126  
248              
249 4     4   16 use Socket qw(SOCK_RAW);
  4         5  
  4         143  
250 4     4   2177 use IO::Socket;
  4         63463  
  4         17  
251 4     4   6229 use Net::Pcap;
  0         0  
  0         0  
252              
253             use constant NW_AF_INET => AF_INET();
254             use constant NW_AF_UNSPEC => AF_UNSPEC();
255              
256             use constant NW_IPPROTO_ICMPv4 => 1;
257             use constant NW_IPPROTO_TCP => 6;
258             use constant NW_IPPROTO_UDP => 17;
259             use constant NW_IPPROTO_ICMPv6 => 58;
260              
261             our %EXPORT_TAGS = (
262             constants => [qw(
263             NW_AF_INET
264             NW_AF_INET6
265             NW_AF_UNSPEC
266             NW_IPPROTO_IP
267             NW_IPPROTO_IPv6
268             NW_IPPROTO_ICMPv4
269             NW_IPPROTO_TCP
270             NW_IPPROTO_UDP
271             NW_IPPROTO_ICMPv6
272             NW_IP_HDRINCL
273             NW_IPPROTO_RAW
274             )],
275             subs => [qw(
276             nw_inet_pton
277             nw_getsaddr
278             )],
279             );
280              
281             our @EXPORT_OK = (
282             @{$EXPORT_TAGS{constants}},
283             @{$EXPORT_TAGS{subs}},
284             );
285              
286             sub _checkWin32 {
287             return 1;
288             }
289              
290             sub _checkOther {
291             if ($>) {
292             print STDERR "[-] Must be EUID 0 (or equivalent) to open a device for ".
293             "writing.\n";
294             return;
295             }
296              
297             return 1;
298             }
299              
300             sub new {
301             my $self = shift->SUPER::new(
302             @_,
303             );
304              
305             return $self;
306             }
307              
308             sub _croak {
309             my ($msg) = @_;
310             print STDERR "[-] $msg\n";
311             return;
312             }
313              
314             sub open {
315             my $self = shift;
316             my ($hdrincl) = @_;
317              
318             _check() or return;
319              
320             my $saddr = nw_getsaddr($self->[$__dst], $self->[$__family], $self->[$__protocol])
321             or return _croak("@{[(caller(0))[3]]}: nw_getsaddr: error");
322              
323             $self->[$___sockaddr] = $saddr;
324              
325             socket(my $s, $self->[$__family], SOCK_RAW(), $self->[$__protocol])
326             or return _croak("@{[(caller(0))[3]]}: socket: $!");
327              
328             my $fd = fileno($s)
329             or return _croak("@{[(caller(0))[3]]}: fileno: $!");
330              
331             if ($hdrincl) {
332             $self->_setIpHdrincl($s, $self->[$__family])
333             or return _croak("@{[(caller(0))[3]]}: setsockopt: $!");
334             }
335              
336             my $io = IO::Socket->new;
337             $io->fdopen($fd, 'w')
338             or return _croak("@{[(caller(0))[3]]}: fdopen: $!");
339              
340             $self->[$___io] = $io;
341              
342             return 1;
343             }
344              
345             sub send {
346             my $self = shift;
347             my ($raw) = @_;
348              
349             while (1) {
350             my $ret = CORE::send($self->_io, $raw, 0, $self->_sockaddr);
351             unless ($ret) {
352             if ($!{ENOBUFS}) {
353             $self->cgDebugPrint(2, "ENOBUFS returned, sleeping for 1 second");
354             sleep 1;
355             next;
356             }
357             elsif ($!{EHOSTDOWN}) {
358             $self->cgDebugPrint(2, "host is down");
359             last;
360             }
361             print STDERR "[!] @{[(caller(0))[3]]}: $!\n";
362             return;
363             }
364             last;
365             }
366              
367             return 1;
368             }
369              
370             sub close { shift->_io->close }
371              
372             1;
373              
374             __END__