Net-DHCPv6

 view release on metacpan or  search on metacpan

lib/Net/DHCPv6/Option/AftrName.pm  view on Meta::CPAN

#!/bin/false
# ABSTRACT: AFTR Name option (code 88) -- RFC 6334 domain name
# PODNAME: Net::DHCPv6::Option::AftrName
use strictures 2;

package Net::DHCPv6::Option::AftrName;
$Net::DHCPv6::Option::AftrName::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_AFTR_NAME
);
use Net::DHCPv6::X::Truncated ();
use Net::DHCPv6::X::BadOption ();
use parent 'Net::DHCPv6::Option';
use namespace::clean;
my $EMPTY         = q();
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 'AftrName requires domain_name' unless defined $args{domain_name};
    $args{code} = $OPTION_AFTR_NAME;
    $args{data} = _encode_domain( $args{domain_name} );
    my $self = $class->SUPER::new( %args );
    $self->{domain_name} = $args{domain_name};
    return bless $self, $class;
}

sub domain_name { return shift->{domain_name} }



( run in 1.291 second using v1.01-cache-2.11-cpan-71847e10f99 )