xDhcpServerOption119.psm1
using namespace System.Collections.Generic function ConvertTo-DhcpSearchSuffixList { <# .SYNOPSIS Convert to a binary encoded DHCP search suffix list .DESCRIPTION Convert to a binary encoded DHCP search suffix list. Returns an array of strings suitable for Set-DhcpServerv4OptionValue. .PARAMETER SearchDomains Array of search domains #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidAssignmentToAutomaticVariable', '', Scope='Function')] [OutputType([string[]])] [CmdletBinding()] param( [Parameter( Mandatory, Position=1 )] [string[]] $SearchDomains ) # this function outputs the length of the string followed by the # integer values for each character function __ConvertToOption119BinaryData ( [string]$String ) { # if the string is empty we don't want to return anything if ( [string]::IsNullOrWhiteSpace( $String ) ) { return } $String.Split('.').ForEach({ $_.Length $_.ToCharArray().ForEach({ [int]$_ }) }) } # holds domains while processing $DomainList = [List[pscustomobject]]::new() # holds binary data prior to conversion $BinaryData = [List[int]]::new() $SearchDomains | ForEach-Object { # create a PSCustomObject with the needed attributes $DomainObject = $_.ToLower() | Select-Object ` @{ N = 'Name'; E = { $_ } }, # the next line triggers PSAvoidAssignmentToAutomaticVariable, however # this is the most efficient way to get all segments of the domain # i.e. host.domain.tld ends up a list containing: host.domain.tld, domain.tld, tld @{ N = 'Parts'; E = { do { $_ } while ( $_ = $_.Split('.',2)[1] ) } }, @{ N = 'Linked'; E = { $null } }, @{ N = 'Offset'; E = { $null } }, @{ N = 'StartIndex'; E = { $BinaryData.Count } } # if there is a domain in $DomainList that has a matching part # link the domain to the current $DomainObject $DomainObject.Linked = $DomainList | Where-Object { $_.Parts | Where-Object { $_ -in $DomainObject.Parts } | Select-Object -First 1 } | Select-Object -First 1 # if there is a linked domain we process the link first if ( $DomainObject.Linked ) { # find the matching part $MatchingPart = $DomainObject.Linked.Parts | Where-Object { $_ -in $DomainObject.Parts } | Select-Object -First 1 # find the offset of the matched part in the full domain name $DomainObject.Offset = $DomainObject.Linked.Name.IndexOf( $MatchingPart ) # domain has a unique part, encode that and then add a pointer if ( $DomainObject.Name -ne $MatchingPart ) { $UniquePart = $DomainObject.Name -replace "\.$MatchingPart$" __ConvertToOption119BinaryData $UniquePart | ForEach-Object { $BinaryData.Add( $_ ) } } # add the pointer $BinaryData.Add( 192 ) $BinaryData.Add( ( $DomainObject.Linked.StartIndex + $DomainObject.Offset ) ) } # if there is no linked domain we just output the encoded domain else { __ConvertToOption119BinaryData $DomainObject.Name | ForEach-Object { $BinaryData.Add( $_ ) } $BinaryData.Add( 0 ) } $DomainList.Add( $DomainObject ) } # output the encoded search suffix list $BinaryData | ForEach-Object { '0x{0:x2}' -f $_ } } function ConvertFrom-DhcpSearchSuffixList { <# .SYNOPSIS Convert from a binary encoded DHCP search suffix list .DESCRIPTION Convert from a binary encoded DHCP search suffix list. Returns an array of strings. .PARAMETER SearchList The binary encoded search list #> [OutputType([string[]])] [CmdletBinding()] param( [Parameter( Mandatory, Position = 1 )] [int[]] $SearchList ) $Index = 0 # where we are in the array $RealIndex = 0 # where we are in the array, if we have followed a pointer $StartIndex = $null # where the current domain starts (just for verbosity) $EndIndex = $null # where the current domain ends $LastIndex = $SearchList.Count - 1 # where the array ends # holds the current domain's chars while being collected $CurrentDomainChars = [List[char]]::new() # note that the ORDER of the operations inside this while loop is critical while ( $true ) { Write-Debug "$Index - start value $($SearchList[$Index])" # if the current index indicates the end of the domain (0x00) return # and reset or exit if ( $SearchList[ $Index ] -eq 0x00 ) { Write-Debug "$Index - found end of domain" # output the current domain and reset $CurrentDomainChars -join '' $CurrentDomainChars.Clear() # check if $RealIndex -gt $Index, if so it means we followed a # pointer and $Index should be reset if ( $RealIndex -gt $Index ) { Write-Debug "$Index - setting `$Index to `$RealIndex" $Index = $RealIndex } # check for $LastIndex and exit if found if ( $Index -eq $LastIndex ) { Write-Debug "$Index - `$Index -eq `$LastIndex, done" return } $Index ++ } # if the current index indicates a pointer (0Xc0) we set index to the # value of the next index if ( $SearchList[ $Index ] -eq 0xc0 ) { Write-Debug "$Index - found pointer" $RealIndex = $Index + 1 $Index = $SearchList[ $RealIndex ] } # if $EndIndex is not set we set to value of $Index + the value # contained in the current index if ( -not $EndIndex ) { Write-Debug "$Index - setting `$EndIndex" $StartIndex = $Index + 1 $EndIndex = $StartIndex + $SearchList[ $Index ] # we append a '.' if the $CurrentDomainChars has any items since setting # the $EndIndex indicates that we are at a sub-domain boundry if ( $CurrentDomainChars.Count -gt 0 ) { $CurrentDomainChars.Add( '.' ) } $Index ++ } # while the value of $Index is less than $EndIndex we append the value # to $CurrentDomainChars if ( $Index -le $EndIndex ) { Write-Debug "$Index - appending value $($SearchList[ $Index ])" $CurrentDomainChars.Add( $SearchList[ $Index ] ) $Index ++ } # if the current index is equal to $EndIndex we reset $EndIndex if ( $Index -eq $EndIndex ) { Write-Debug "$Index - resetting `$EndIndex" $EndIndex = $null } Write-Debug "$Index - end value $($SearchList[$Index])" } } |