#!/usr/bin/perl -w # # dhcp119 # John Simpson 2016-07-31 # # Shows the raw value needed for DHCP option 119, aka "domain-search", for # one or more domain names. # # Implements DNS compression as described in RFC 3397, using the scheme # originally described in RFC 1035 section 4.1.4. # https://tools.ietf.org/search/rfc1035#section-4.1.4 # ############################################################################### # # MIT License # # Copyright (c) 2016 John Simpson # # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), # to deal in the Software without restriction, including without limitation # the rights to use, copy, modify, merge, publish, distribute, sublicense, # and/or sell copies of the Software, and to permit persons to whom the # Software is furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included # in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. # ############################################################################### require 5.003 ; use strict ; use Getopt::Std ; my %opt = () ; my $do_debug = 0 ; # -d my $allow_ext = 0 ; # -e ############################################################################### sub usage(;$) { my $msg = ( shift || '' ) ; print < position within $out my @seq = () ; # order that domains were added to $out (for debug output) if ( $#ARGV < 0 ) { usage "ERROR: no domain names specified\n" ; } while ( my $name = shift @ARGV ) { ######################################## # Make sure the name is a valid DNS domain name if ( length($name) > 255 ) { die "ERROR: name \"$name\" is longer than 255 characters\n" ; } unless ( $allow_ext ) { 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 ) ; push( @seq , $remain ) ; ######################################## # 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" ; } ######################################## # Show $out as a sequence of hex numbers map { printf '%02X' , ord $_ } split( // , $out ) ; print "\n" ; ######################################## # Debugging - show the entries in %pos if ( $do_debug ) { print "\n" ; map { printf "%02X %s\n" , $pos{$_} , $_ } @seq ; }