Net-DHCPv6
view release on metacpan or search on metacpan
lib/Net/DHCPv6/Option/ClientFqdn.pm view on Meta::CPAN
#!/bin/false
# ABSTRACT: Client FQDN option (code 39) -- flags + domain name
# PODNAME: Net::DHCPv6::Option::ClientFqdn
use strictures 2;
package Net::DHCPv6::Option::ClientFqdn;
$Net::DHCPv6::Option::ClientFqdn::VERSION = '0.003';
use Net::DHCPv6::OptionList ();
use Net::DHCPv6::Option ();
use Carp qw( croak );
use Net::DHCPv6::Constants qw(
$DN_COMPRESS_MASK $DN_LABEL_MASK $OPTION_CLIENT_FQDN
);
use Net::DHCPv6::X::Truncated ();
use Net::DHCPv6::X::BadOption ();
use parent 'Net::DHCPv6::Option';
use namespace::clean;
my $EMPTY = q();
my $MAX_BYTE = 255; ## no critic (ValuesAndExpressions::ProhibitMagicNumbers)
my $MAX_PTR_DEPTH = 255; ## no critic (ValuesAndExpressions::ProhibitMagicNumbers)
sub _encode_domain {
my ( $domain ) = @_;
return chr( 0 ) unless defined $domain && CORE::length( $domain );
my @labels = split m/[.]/, $domain;
return join( $EMPTY, map { pack( 'C', CORE::length ) . $_ } @labels ) . chr( 0 );
}
sub _read_labels_at {
my ( $payload, $offset_ref, $len, $depth ) = @_;
$depth //= 0;
my @labels;
while ( ${$offset_ref} < $len ) {
my $llen = unpack( 'C', substr( $payload, ${$offset_ref}, 1 ) );
if ( $llen == 0 ) {
++${$offset_ref};
last;
}
if ( ( $llen & $DN_COMPRESS_MASK ) == $DN_COMPRESS_MASK ) { ## no critic (Bangs::ProhibitBitwiseOperators ValuesAndExpressions::ProhibitMagicNumbers)
if ( $Net::DHCPv6::Option::FOLLOW_COMPRESSION ) {
Net::DHCPv6::X::Truncated->throw( message => 'Truncated compression pointer' )
if ${$offset_ref} + 2 > $len;
my $ptr =
( ( $llen & $DN_LABEL_MASK ) << 8 ) | unpack( 'C', substr( $payload, ${$offset_ref} + 1, 1 ) ); ## no critic (Bangs::ProhibitBitwiseOperators ValuesAndExpressions::ProhibitMagicNumbers)
Net::DHCPv6::X::BadOption->throw( message => 'Compression pointer out of range' )
if $ptr >= $len;
Net::DHCPv6::X::BadOption->throw( message => 'Compression pointer depth exceeded' )
if $depth > $MAX_PTR_DEPTH;
${$offset_ref} += 2;
my $ptr_ref = \$ptr;
push @labels, _read_labels_at( $payload, $ptr_ref, $len, $depth + 1 );
last;
}
Net::DHCPv6::X::BadOption->throw( message => 'Compression pointer in domain name' );
}
Net::DHCPv6::X::BadOption->throw( message => 'Invalid domain label length' ) if $llen > $DN_LABEL_MASK;
++${$offset_ref};
Net::DHCPv6::X::Truncated->throw( message => 'Truncated domain label' )
if ${$offset_ref} + $llen > $len;
push @labels, substr( $payload, ${$offset_ref}, $llen );
${$offset_ref} += $llen;
}
return @labels;
}
sub _decode_domain {
my ( $payload ) = @_;
return $EMPTY unless CORE::length( $payload );
my $offset = 0;
my @labels = _read_labels_at( $payload, \$offset, CORE::length( $payload ) );
return join( q{.}, @labels );
}
sub new {
my ( $class, %args ) = @_;
croak 'ClientFqdn requires flags' unless defined $args{flags};
croak 'ClientFqdn flags must be 0-255'
if $args{flags} < 0 || $args{flags} > $MAX_BYTE;
$args{code} = $OPTION_CLIENT_FQDN;
my $domain = _encode_domain( $args{domain_name} // $EMPTY );
$args{data} = pack( 'C', $args{flags} ) . $domain;
my $self = $class->SUPER::new( %args );
$self->{flags} = $args{flags};
$self->{domain_name} = $args{domain_name} // $EMPTY;
return bless $self, $class;
( run in 0.574 second using v1.01-cache-2.11-cpan-71847e10f99 )