Acme-UPnP
view release on metacpan or search on metacpan
# NAME
Acme::UPnP - Cheap UPnP Port Mapping (IGD)
# SYNOPSIS
```perl
use Acme::UPnP;
my $mapper = Acme::UPnP->new( );
# Discovery
if ( $mapper->discover_device( ) ) {
say 'Found router: ' . $mapper->upnp_device->getfriendlyname( );
# Map a port
$mapper->map_port( 8080, 8080, 'TCP', 'My Silly App' );
}
# Clean up on exit
$mapper->unmap_port( 8080, 'TCP' );
```
# DESCRIPTION
`Acme::UPnP` provides a high-level, easy-to-use interface for the UPnP Internet Gateway Device (IGD) protocol. It
automates the complex task of requesting port forwarding from residential routers, which is often required for
peer-to-peer applications and local servers to be accessible from the internet.
# PUBLIC METHODS
## `discover_device()`
Scans the local network for UPnP-capable gateway devices. Returns true if a device was successfully identified.
## `map_port( $internal, $external, $protocol, $description )`
Requests a new port mapping.
- `$protocol`: 'TCP' or 'UDP'.
- `$description`: A label for the router's mapping table.
- Triggers `map_success` or `map_failed` events.
## `unmap_port( $external, $protocol )`
Removes an existing port mapping.
## `get_external_ip( )`
Queries the router for its public WAN IP address.
## `is_available( )`
Returns boolean indicating if the required [Net::UPnP](https://metacpan.org/pod/Net%3A%3AUPnP) dependency is installed.
# AUTHOR
lib/Acme/UPnP.pm view on Meta::CPAN
$ctrl_url = $base . $ctrl_url;
}
}
}
$control_url = $ctrl_url;
$service_type = $svc_type;
$self->_emit( device_found => { name => 'UPnP Gateway' } );
return 1;
}
method map_port ( $int_port, $ext_port, $proto, $desc ) {
return 0 unless $control_url;
my $local_ip = $self->_get_local_ip();
my $args = {
NewRemoteHost => '',
NewExternalPort => $ext_port,
NewProtocol => $proto,
NewInternalPort => $int_port,
NewInternalClient => $local_ip,
NewEnabled => 1,
NewPortMappingDescription => $desc,
NewLeaseDuration => 0
};
if ( $self->_send_soap( AddPortMapping => $args ) ) {
$self->_emit( map_success => { int_p => $int_port, ext_p => $ext_port, proto => $proto, desc => $desc } );
return 1;
}
else {
$self->_emit( map_failed => { err_c => 500, err_d => 'SOAP Failed' } );
return 0;
}
}
method unmap_port ( $ext_port, $proto ) {
return 0 unless $control_url;
my $args = { NewRemoteHost => '', NewExternalPort => $ext_port, NewProtocol => $proto };
if ( $self->_send_soap( DeletePortMapping => $args ) ) {
$self->_emit( unmap_success => { ext_p => $ext_port, proto => $proto } );
return 1;
}
$self->_emit( unmap_failed => { err_c => 500, err_d => 'SOAP Failed' } );
return 0;
}
method get_external_ip () {
return undef unless $control_url;
my $action = 'GetExternalIPAddress';
my $res = $self->_send_soap_response( $action, {} );
return $1 if $res && $res =~ m{<NewExternalIPAddress>(.*?)</NewExternalIPAddress>}s;
return undef;
}
lib/Acme/UPnP.pod view on Meta::CPAN
=encoding utf-8
=head1 NAME
Acme::UPnP - Cheap UPnP Port Mapping (IGD)
=head1 SYNOPSIS
use Acme::UPnP;
my $mapper = Acme::UPnP->new( );
# Discovery
if ( $mapper->discover_device( ) ) {
say 'Found router: ' . $mapper->upnp_device->getfriendlyname( );
# Map a port
$mapper->map_port( 8080, 8080, 'TCP', 'My Silly App' );
}
# Clean up on exit
$mapper->unmap_port( 8080, 'TCP' );
=head1 DESCRIPTION
C<Acme::UPnP> provides a high-level, easy-to-use interface for the UPnP Internet Gateway Device (IGD) protocol. It
automates the complex task of requesting port forwarding from residential routers, which is often required for
peer-to-peer applications and local servers to be accessible from the internet.
=head1 PUBLIC METHODS
=head2 C<discover_device()>
Scans the local network for UPnP-capable gateway devices. Returns true if a device was successfully identified.
=head2 C<map_port( $internal, $external, $protocol, $description )>
Requests a new port mapping.
=over
=item C<$protocol>: 'TCP' or 'UDP'.
=item C<$description>: A label for the router's mapping table.
=item Triggers C<map_success> or C<map_failed> events.
=back
=head2 C<unmap_port( $external, $protocol )>
Removes an existing port mapping.
=head2 C<get_external_ip( )>
Queries the router for its public WAN IP address.
=head2 C<is_available( )>
Returns boolean indicating if the required L<Net::UPnP> dependency is installed.
=head1 AUTHOR
XML
my $upnp = Acme::UPnP->new();
T2->ok( $upnp->discover_device(), 'discover_device returns true' );
}
);
T2->subtest(
'Port Mapping' => sub {
$main::socket_recv_data = "HTTP/1.1 200 OK\r\nLocation: http://192.168.1.1:5000/desc.xml\r\n\r\n";
my $upnp = Acme::UPnP->new();
$upnp->discover_device();
T2->ok( $upnp->map_port( 6881, 6881, 'TCP', 'Test' ), 'map_port returns true' );
T2->ok( $upnp->unmap_port( 6881, 'TCP' ), 'unmap_port returns true' );
}
);
T2->subtest(
'External IP' => sub {
$main::socket_recv_data = "HTTP/1.1 200 OK\r\nLocation: http://192.168.1.1:5000/desc.xml\r\n\r\n";
my $upnp = Acme::UPnP->new();
$upnp->discover_device();
T2->is( $upnp->get_external_ip(), '4.3.2.1', 'get_external_ip returns mocked IP' );
}
);
( run in 1.003 second using v1.01-cache-2.11-cpan-13bb782fe5a )