Acme-UPnP

 view release on metacpan or  search on metacpan

README.md  view on Meta::CPAN

# 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

t/basics.t  view on Meta::CPAN

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 )