############################################################################### # # Calculate a DHCP option 119 value. # # Parameters: # - List of domains to be listed in the option # # Returns: # - Value of the DHCP option, expressed as hex digits sub encode_dhcp119_hex(@) { my $out = '' ; # output as raw bytes my %pos = () ; # domain => position within $out while ( my $name = ( shift || '' ) ) { ######################################## # Make sure the name is a valid DNS domain name if ( length($name) > 255 ) { die "ERROR: name \"$name\" is longer than 255 characters\n" ; } if ( $name =~ m|[^a-z0-9\-\.]|i ) { die "ERROR: name \"$name\" contains invalid characters\n" ; } ######################################## # Process each label within the name my $remain = $name ; my $compressed = 0 ; while ( $remain ne '' ) { ######################################## # If the remaining name has already been seen, # * add a reference to it to the output # * remember that it's a compressed name # (so we don't end with "00") # * we're finished with this name if ( exists $pos{$remain} ) { $out .= "\xC0" . chr( $pos{$remain} ) ; $remain = '' ; $compressed = 1 ; last ; } ######################################## # Remember the name we're adding, in case # a later name needs to refer back to it $pos{$remain} = length( $out ) ; ######################################## # Extract the first label from the remaining name my ( $label , $x ) = split( /\./ , $remain , 2 ) ; $remain = ( $x || '' ) ; if ( length($label) > 63 ) { die "ERROR: label \"$label\" is longer than 63 characters\n" ; } ######################################## # Add the length followed by the label itself to the output string $out .= chr( length( $label ) ) . $label ; } ######################################## # If we didn't end with a compression reference, # add a zero byte to end this name. unless ( $compressed ) { $out .= "\x00" ; } } if ( length( $out ) > 255 ) { die "ERROR: the output string is larger than 255 bytes\n" ; } ######################################## # Convert $out to printable hex string my $out_hex = '' ; map { $out_hex .= sprintf( '%02X' , ord $_ ) } split( // , $out ) ; return $out_hex ; } ############################################################################### # # Decodes a DHCP option 119 value. # # Parameters: # - Value of the DHCP option, expressed as hex digits # # Returns: # - List of domains to be listed in the option sub decode_dhcp119_hex($) { my $hstr = shift ; my @rv = () ; ######################################## # Make sure hex string is valid $hstr =~ s|^0x||i ; unless ( $hstr =~ m|^[0-9a-fA-f]+$| ) { die "ERROR: invalid hex string \"$hstr\"\n" ; } ######################################## # Make array of byte values my @byte = () ; while ( $hstr =~ s|^(..)|| ) { push( @byte , hex( $1 ) ) ; } ######################################## # Make array of fragments while showing output strings my %frag = () ; my $n = 0 ; my $out = '' ; while ( $n < $#byte ) { if ( $byte[$n] < 1 ) { push( @rv ,$out ) ; $out = '' ; $n ++ ; } elsif ( $byte[$n] < 64 ) { for my $b ( 1 .. $byte[$n] ) { $frag{$n} .= chr( $byte[$n+$b] ) ; } $frag{$n} .= '.' ; $out .= $frag{$n} ; $n += $byte[$n] + 1 ; } elsif ( $byte[$n] > 191 ) { my $x = $byte[$n+1] ; unless ( exists ( $frag{$x} ) ) { die "ERROR: invalid pointer n=$n x=$x\n" ; } my $t = $x ; while ( $byte[$t] > 0 ) { $frag{$n} .= $frag{$t} ; $t += $byte[$t] + 1 ; } $out .= $frag{$n} ; push( @rv ,$out ) ; $out = '' ; $n += 2 ; } else { die "ERROR: invalid length code $n (" , sprintf('%02x',$n) , ")\n" ; } } if ( $out ne '' ) { push( @rv , $out ) ; } return @rv ; }