line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# Copyright (c) 2009 David Caldwell, All Rights Reserved. -*- cperl -*- |
2
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
1004
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
38
|
|
4
|
1
|
|
|
1
|
|
6
|
use warnings; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
56
|
|
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
package Net::NAT::PMP; |
7
|
|
|
|
|
|
|
our $VERSION = '0.9.4'; |
8
|
|
|
|
|
|
|
|
9
|
1
|
|
|
1
|
|
999
|
use IO::Socket::INET; |
|
1
|
|
|
|
|
36716
|
|
|
1
|
|
|
|
|
9
|
|
10
|
0
|
|
|
0
|
0
|
|
sub Port { 5351 } |
11
|
0
|
|
|
0
|
0
|
|
sub Version { 0 } # protocol version we support |
12
|
|
|
|
|
|
|
|
13
|
0
|
|
|
0
|
0
|
|
sub socket { $_[0]->{socket} } |
14
|
0
|
|
|
0
|
0
|
|
sub router_ip { $_[0]->{router_ip} } |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
sub error { |
17
|
0
|
|
|
0
|
1
|
|
my ($self, $message) = @_; |
18
|
0
|
0
|
|
|
|
|
return $self->{error} unless defined $message; |
19
|
0
|
|
|
|
|
|
$self->{error} = $message; |
20
|
|
|
|
|
|
|
undef |
21
|
0
|
|
|
|
|
|
} |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
sub get_router_address { |
24
|
0
|
|
|
0
|
0
|
|
my $gateway; |
25
|
0
|
0
|
|
|
|
|
if ($^O eq 'darwin') { |
|
|
0
|
|
|
|
|
|
26
|
|
|
|
|
|
|
# Would be much better to use the sysctl interface here, natively. |
27
|
0
|
0
|
|
|
|
|
open NETSTAT, '-|', 'netstat', '-rlnf', 'inet' or die "Couldn't run netstat: $!"; |
28
|
0
|
|
|
|
|
|
while () { |
29
|
0
|
0
|
|
|
|
|
$gateway = $1 if /^default\s+(\d+\.\d+\.\d+\.\d+)/; |
30
|
|
|
|
|
|
|
} |
31
|
0
|
|
|
|
|
|
close NETSTAT; |
32
|
|
|
|
|
|
|
} elsif ($^O eq 'linux') { |
33
|
0
|
0
|
|
|
|
|
open ROUTE, '<', '/proc/net/route' or die "Couldn't open /proc/net/route: $!"; |
34
|
0
|
|
|
|
|
|
while () { |
35
|
0
|
0
|
|
|
|
|
$gateway = $1 if /^\S+\s+00000000\s+([0-9A-F]+)/; |
36
|
|
|
|
|
|
|
} |
37
|
0
|
|
|
|
|
|
close ROUTE; |
38
|
|
|
|
|
|
|
# Stupid linux prints it as a hex number in network byte order. The following should work on both |
39
|
|
|
|
|
|
|
# big and little endian machines. I don't have any big endian's on hand to test though. |
40
|
0
|
0
|
|
|
|
|
$gateway = join(".", unpack "CCCC", pack "L", hex $gateway) if $gateway; |
41
|
0
|
|
|
|
|
|
} else { die "Automatically discovering the gateway address is not supported on $^O yet! Please pass the address of your router to Net:NAT::PMP::new()" } |
42
|
0
|
|
|
|
|
|
$gateway; |
43
|
|
|
|
|
|
|
} |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
sub new { |
46
|
0
|
|
|
0
|
1
|
|
my ($class, $router_ip) = @_; |
47
|
0
|
|
0
|
|
|
|
$router_ip ||= get_router_address(); |
48
|
0
|
|
|
|
|
|
my $self = bless { |
49
|
|
|
|
|
|
|
router_ip => $router_ip, |
50
|
|
|
|
|
|
|
socket => IO::Socket::INET->new(PeerAddr => $router_ip, PeerPort => Net::NAT::PMP::Port, Proto=>'udp'),#, Timeout => .25), |
51
|
|
|
|
|
|
|
}, $class; |
52
|
0
|
0
|
|
|
|
|
$self->{socket} ? $self : undef; |
53
|
|
|
|
|
|
|
} |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
sub external_address { |
56
|
0
|
|
|
0
|
1
|
|
my ($self) = @_; |
57
|
0
|
|
|
|
|
|
my $op = 0; |
58
|
0
|
0
|
|
|
|
|
return $self->error("send: $!") unless defined $self->socket->send(pack("CC", Version, $op)); |
59
|
0
|
|
|
|
|
|
my $packet; |
60
|
0
|
0
|
|
|
|
|
return $self->error("recv: $!") unless defined $self->socket->recv($packet, 12); |
61
|
0
|
|
|
|
|
|
my (%response, @external_address); |
62
|
0
|
|
|
|
|
|
(@response{qw(vers op result_code time)}, @external_address) = unpack("CCnNCCCC", $packet); |
63
|
0
|
0
|
|
|
|
|
return $self->error("Got unexpected op $response{op} instead of @{[128 + $op]}") unless $response{op} == 128 + $op; |
|
0
|
|
|
|
|
|
|
64
|
0
|
0
|
|
|
|
|
return $self->error("Got unexpected result_code $response{result_code} instead of 0") unless $response{result_code} == 0; |
65
|
0
|
|
|
|
|
|
my $dotted = join('.', @external_address); |
66
|
0
|
|
|
|
|
|
return $dotted; |
67
|
|
|
|
|
|
|
} |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
sub create_mapping { |
70
|
0
|
|
|
0
|
1
|
|
my ($self, $internal_port, $external_port, $lifetime_seconds, $udp) = @_; |
71
|
0
|
0
|
|
|
|
|
$external_port = $internal_port unless defined $external_port; # wheres my //= !!! |
72
|
0
|
0
|
|
|
|
|
$lifetime_seconds = 3600 unless defined $lifetime_seconds; |
73
|
0
|
0
|
|
|
|
|
my $op = $udp ? 1 : 2; |
74
|
0
|
0
|
|
|
|
|
return $self->error("send: $!") unless defined $self->socket->send(pack ("CCnnnN", Version, $op, 0, $internal_port, $external_port, $lifetime_seconds)); |
75
|
0
|
|
|
|
|
|
my $packet; |
76
|
0
|
0
|
|
|
|
|
return $self->error("recv: $!") unless defined $self->socket->recv($packet, 16); |
77
|
0
|
|
|
|
|
|
my %response; |
78
|
0
|
|
|
|
|
|
@response{qw(vers op result_code time internal_port external_port lifetime_seconds)} = unpack "CCnNnnN", $packet; |
79
|
0
|
0
|
|
|
|
|
return $self->error("Got unexpected op $response{op} instead of @{[128 + $op]}") unless $response{op} == 128 + $op; |
|
0
|
|
|
|
|
|
|
80
|
0
|
0
|
|
|
|
|
return $self->error("Got unexpected result_code $response{result_code} instead of 0") unless $response{result_code} == 0; |
81
|
0
|
|
|
|
|
|
return $external_port; |
82
|
|
|
|
|
|
|
} |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
sub destroy_mapping { |
85
|
0
|
|
|
0
|
1
|
|
my ($self, $internal_port, $udp) = @_; |
86
|
0
|
|
|
|
|
|
$self->create_mapping($internal_port, 0, 0, $udp); |
87
|
|
|
|
|
|
|
} |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
1; |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
__END__ |