NestedModules/TcpConnectivityTester/TcpConnectivityTester.psm1
Set-StrictMode -Version 4 Function Get-ErrorMessage() { <# .SYNOPSIS Gets a formatted error message from an error record. .DESCRIPTION Gets a formatted error message from an error record. .EXAMPLE Get-ErrorMessage -ErrorRecords $_ #> [CmdletBinding()] [OutputType([string])] Param( [Parameter(Mandatory=$true, HelpMessage='The PowerShell error record object to get information from')] [ValidateNotNullOrEmpty()] [System.Management.Automation.ErrorRecord]$ErrorRecord ) Process { $msg = [System.Environment]::NewLine,'Exception Message: ',$ErrorRecord.Exception.Message -join '' if($null -ne $ErrorRecord.Exception.HResult) { $msg = $msg,[System.Environment]::NewLine,'Exception HRESULT: ',('{0:X}' -f $ErrorRecord.Exception.HResult),$ErrorRecord.Exception.HResult -join '' } if($null -ne $ErrorRecord.Exception.StackTrace) { $msg = $msg,[System.Environment]::NewLine,'Exception Stacktrace: ',$ErrorRecord.Exception.StackTrace -join '' } if ($null -ne ($ErrorRecord.Exception | Get-Member | Where-Object { $_.Name -eq 'WasThrownFromThrowStatement'})) { $msg = $msg,[System.Environment]::NewLine,'Explicitly Thrown: ',$ErrorRecord.Exception.WasThrownFromThrowStatement -join '' } if ($null -ne $ErrorRecord.Exception.InnerException) { if ($ErrorRecord.Exception.InnerException.Message -ne $ErrorRecord.Exception.Message) { $msg = $msg,[System.Environment]::NewLine,'Inner Exception: ',$ErrorRecord.Exception.InnerException.Message -join '' } if($null -ne $ErrorRecord.Exception.InnerException.HResult) { $msg = $msg,[System.Environment]::NewLine,'Inner Exception HRESULT: ',('{0:X}' -f $ErrorRecord.Exception.InnerException.HResult),$ErrorRecord.Exception.InnerException.HResult -join '' } } $msg = $msg,[System.Environment]::NewLine,'Call Site: ',$ErrorRecord.InvocationInfo.PositionMessage -join '' if ($null -ne ($ErrorRecord | Get-Member | Where-Object { $_.Name -eq 'ScriptStackTrace'})) { $msg = $msg,[System.Environment]::NewLine,"Script Stacktrace: ",$ErrorRecord.ScriptStackTrace -join '' } return $msg } } Function Get-IPAddress() { <# .SYNOPSIS Gets the IP address(es) for a hostname. .DESCRIPTION Gets the IP address(es) for a hostname. .EXAMPLE Get-IPAddress -Hostname www.site.com #> [CmdletBinding()] [OutputType([string[]])] Param ( [Parameter(Mandatory=$true, HelpMessage='The Hostname to get the IP address for.')] [ValidateNotNullOrEmpty()] [Alias("Url")] [String]$Hostname ) $addresses = [string[]]@() $dnsResults = $null $dnsResults = @(Resolve-DnsName -Name $Hostname -NoHostsFile -Type A_AAAA -QuickTimeout -ErrorAction SilentlyContinue | Where-Object {$_.Type -eq 'A'}) $addresses = [string[]]@($dnsResults | ForEach-Object { try { $_.IpAddress } catch [System.Management.Automation.PropertyNotFoundException] {Write-Verbose "No IP in Object."} }) # IpAddress results in a PropertyNotFoundException when a URL is blocked upstream return [string[]](,$addresses) } Function Get-IPAlias() { <# .SYNOPSIS Gets DNS alias for a Hostname. .DESCRIPTION Gets DNS alias for a Hostname. .EXAMPLE Get-IPAlias -Hostname http://www.site.com #> [CmdletBinding()] [OutputType([string[]])] Param ( [Parameter(Mandatory=$true, HelpMessage='The Hostname to get the alias address for.')] [ValidateNotNullOrEmpty()] [Alias("Url")] [String]$Hostname ) $aliases = [string[]]@() $dnsResults = $null $dnsResults = @(Resolve-DnsName -Name $Hostname -NoHostsFile -QuickTimeout -ErrorAction SilentlyContinue | Where-Object { $_.Type -eq 'CNAME' }) $aliases = [string[]]@($dnsResults | ForEach-Object { $_.NameHost }) return [string[]](,$aliases) } Function Get-TcpConnectivity() { <# .SYNOPSIS Gets TCP connectivity information for a hostname and port. .DESCRIPTION Gets TCP connectivity information for a hostname and port. .EXAMPLE Get-TcpConnectivity -TestHostname "www.site.com" -TestPort 111 .EXAMPLE Get-TcpConnectivity -TestHostname "www.site.com" -TestPort 111 -HostnamePattern "*.site.com" -Description 'A site that does something' #> [CmdletBinding()] [OutputType([void])] Param( [Parameter(Mandatory=$true, HelpMessage='The hostname to test.')] [ValidateNotNullOrEmpty()] [String]$TestHostname, [Parameter(Mandatory=$true, HelpMessage='The TCP port to test.')] [ValidateNotNullOrEmpty()] [Int32]$TestPort, [Parameter(Mandatory=$true, HelpMessage='The Expected status code.')] [Int32]$ExpectedStatusCode, [Parameter(Mandatory=$false, HelpMessage='The hostname pattern to unblock when the hostname to unblock is not a literal hostname.')] [ValidateNotNullOrEmpty()] [string]$HostnamePattern, [Parameter(Mandatory=$false, HelpMessage='A description of the connectivity test or purpose of the hostname.')] [ValidateNotNullOrEmpty()] [string]$Description ) $parameters = $PSBoundParameters $isVerbose = $verbosePreference -eq 'Continue' $TestHostname = $TestHostname.ToLower() if($parameters.ContainsKey('HostnamePattern')) { $UnblockHostname = $HostnamePattern } else { $UnblockHostname = $TestHostname } $newLine = [System.Environment]::NewLine Write-Verbose -Message ('{0}*************************************************{1}Testing {2}{3}*************************************************{4}' -f $newLine,$newLine,$TestHostname,$newLine,$newLine) $statusCode = 0 $statusMessage = '' $response = $null try { $response = Test-NetConnection -ComputerName $TestHostname -Port $TestPort -Verbose:$isVerbose if($response.TcpTestSucceeded){ $statusCode = 1 $statusMessage = "Tcp test succeeded" } elseif($response.PingSucceeded){ $statusCode = 2 $statusMessage = "Ping test succeeded" } elseif($response.NameResolutionSucceeded){ $statusCode = 3 $statusMessage = "Name resolution succeeded" }else { $statusCode = 5 $statusMessage = "Unknown error" } } catch { $statusMessage = Get-ErrorMessage -ErrorRecord $_ } $address = Get-IPAddress -Hostname $TestHostname -Verbose:$false $alias = Get-IPAlias -Hostname $TestHostname -Verbose:$false $resolved = (@($address)).Length -ge 1 -or (@($alias)).Length -ge 1 $actualStatusCode = [int]$statusCode $isBlocked = $statusCode -eq 1 -and $resolved $urlType = if ($HostnamePattern.Contains('*')) { 'Pattern' } else { 'Literal' } $isUnexpectedStatus = $statusCode -ne 1 $simpleStatusMessage = if ($isUnexpectedStatus) { $statusMessage } else { '' } $connectivitySummary = ('{0}Test Hostname: {1}{2}Hostname to Unblock: {3}{4}Hostname Type: {5}{6}Description: {7}{8}Resolved: {9}{10}IP Addresses: {11}{12}DNS Aliases: {13}{14}Actual Status Code: {15}{16}Expected Status Code: {17}{18}Is Unexpected Status Code: {19}{20}Status Message: {21}{22}Blocked: {23}{24}{25}' -f $newLine,$TestHostname,$newLine,$HostnamePattern,$newLine,$urlType,$newLine,$Description,$newLine,$resolved,$newLine,($address -join ', '),$newLine,($alias -join ', '),$newLine,$actualStatusCode,$newLine,$ExpectedStatusCode,$newLine,$isUnexpectedStatus,$newLine,$simpleStatusMessage,$newLine,$isBlocked,$newLine,$newLine) Write-Verbose -Message $connectivitySummary $connectivity = [pscustomobject]@{ TestUrl = $TestHostname; UnblockUrl = $UnblockHostname; UrlType = $urlType; Resolved = $resolved; IpAddresses = [string[]]$address; DnsAliases = [string[]]$alias; Description = $Description; ActualStatusCode = [int]$actualStatusCode; ExpectedStatusCode = $ExpectedStatusCode; UnexpectedStatus = $isUnexpectedStatus; StatusMessage = $simpleStatusMessage; DetailedStatusMessage = $statusMessage; Blocked = $isBlocked; ServerCertificate = $null; } return $connectivity } Function Save-TcpConnectivity() { <# .SYNOPSIS Saves TCP connectivity objects to a JSON file. .DESCRIPTION Saves TCP connectivity objects to a JSON file. .EXAMPLE Save-TcpConnectivity -FileName 'Connectivity' -Objects $connectivity .EXAMPLE Save-TcpConnectivity -FileName 'Connectivity' -Objects $connectivity -OutputPath "$env:userprofile\Documents\ConnectivityTestResults" .EXAMPLE Save-TcpConnectivity -FileName 'Connectivity' -Objects $connectivity -Compress #> [CmdletBinding()] [OutputType([void])] Param( [Parameter(Mandatory=$true, HelpMessage='The filename without the extension.')] [ValidateNotNullOrEmpty()] [string]$FileName, [Parameter(Mandatory=$true, HelpMessage='The connectivity object(s) to save.')] [System.Collections.Generic.List[pscustomobject]]$Objects, [Parameter(Mandatory=$false, HelpMessage="The path to save the file to. Defaults to the user's Desktop folder.")] [string]$OutputPath, [Parameter(Mandatory=$false, HelpMessage='Compress the JSON text output.')] [switch]$Compress ) $parameters = $PSBoundParameters if (-not($parameters.ContainsKey('OutputPath'))) { $OutputPath = $env:USERPROFILE,'Desktop' -join [System.IO.Path]::DirectorySeparatorChar } $OutputPath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($OutputPath) if (-not(Test-Path -Path $OutputPath)) { New-Item -Path $OutputPath -ItemType Directory } $json = $Objects | ConvertTo-Json -Depth 3 -Compress:$Compress $json | Out-File -FilePath "$OutputPath\$FileName.json" -NoNewline -Force } # SIG # Begin signature block # MIIXxQYJKoZIhvcNAQcCoIIXtjCCF7ICAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB # gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR # AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUVMIMexVy9GLu6YY0xmBVtZpB # QPGgghL4MIID7jCCA1egAwIBAgIQfpPr+3zGTlnqS5p31Ab8OzANBgkqhkiG9w0B # AQUFADCBizELMAkGA1UEBhMCWkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTEUMBIG # A1UEBxMLRHVyYmFudmlsbGUxDzANBgNVBAoTBlRoYXd0ZTEdMBsGA1UECxMUVGhh # d3RlIENlcnRpZmljYXRpb24xHzAdBgNVBAMTFlRoYXd0ZSBUaW1lc3RhbXBpbmcg # Q0EwHhcNMTIxMjIxMDAwMDAwWhcNMjAxMjMwMjM1OTU5WjBeMQswCQYDVQQGEwJV # UzEdMBsGA1UEChMUU3ltYW50ZWMgQ29ycG9yYXRpb24xMDAuBgNVBAMTJ1N5bWFu # dGVjIFRpbWUgU3RhbXBpbmcgU2VydmljZXMgQ0EgLSBHMjCCASIwDQYJKoZIhvcN # AQEBBQADggEPADCCAQoCggEBALGss0lUS5ccEgrYJXmRIlcqb9y4JsRDc2vCvy5Q # WvsUwnaOQwElQ7Sh4kX06Ld7w3TMIte0lAAC903tv7S3RCRrzV9FO9FEzkMScxeC # i2m0K8uZHqxyGyZNcR+xMd37UWECU6aq9UksBXhFpS+JzueZ5/6M4lc/PcaS3Er4 # ezPkeQr78HWIQZz/xQNRmarXbJ+TaYdlKYOFwmAUxMjJOxTawIHwHw103pIiq8r3 # +3R8J+b3Sht/p8OeLa6K6qbmqicWfWH3mHERvOJQoUvlXfrlDqcsn6plINPYlujI # fKVOSET/GeJEB5IL12iEgF1qeGRFzWBGflTBE3zFefHJwXECAwEAAaOB+jCB9zAd # BgNVHQ4EFgQUX5r1blzMzHSa1N197z/b7EyALt0wMgYIKwYBBQUHAQEEJjAkMCIG # CCsGAQUFBzABhhZodHRwOi8vb2NzcC50aGF3dGUuY29tMBIGA1UdEwEB/wQIMAYB # Af8CAQAwPwYDVR0fBDgwNjA0oDKgMIYuaHR0cDovL2NybC50aGF3dGUuY29tL1Ro # YXd0ZVRpbWVzdGFtcGluZ0NBLmNybDATBgNVHSUEDDAKBggrBgEFBQcDCDAOBgNV # HQ8BAf8EBAMCAQYwKAYDVR0RBCEwH6QdMBsxGTAXBgNVBAMTEFRpbWVTdGFtcC0y # MDQ4LTEwDQYJKoZIhvcNAQEFBQADgYEAAwmbj3nvf1kwqu9otfrjCR27T4IGXTdf # plKfFo3qHJIJRG71betYfDDo+WmNI3MLEm9Hqa45EfgqsZuwGsOO61mWAK3ODE2y # 0DGmCFwqevzieh1XTKhlGOl5QGIllm7HxzdqgyEIjkHq3dlXPx13SYcqFgZepjhq # IhKjURmDfrYwggSjMIIDi6ADAgECAhAOz/Q4yP6/NW4E2GqYGxpQMA0GCSqGSIb3 # DQEBBQUAMF4xCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3Jh # dGlvbjEwMC4GA1UEAxMnU3ltYW50ZWMgVGltZSBTdGFtcGluZyBTZXJ2aWNlcyBD # QSAtIEcyMB4XDTEyMTAxODAwMDAwMFoXDTIwMTIyOTIzNTk1OVowYjELMAkGA1UE # BhMCVVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMTQwMgYDVQQDEytT # eW1hbnRlYyBUaW1lIFN0YW1waW5nIFNlcnZpY2VzIFNpZ25lciAtIEc0MIIBIjAN # BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAomMLOUS4uyOnREm7Dv+h8GEKU5Ow # mNutLA9KxW7/hjxTVQ8VzgQ/K/2plpbZvmF5C1vJTIZ25eBDSyKV7sIrQ8Gf2Gi0 # jkBP7oU4uRHFI/JkWPAVMm9OV6GuiKQC1yoezUvh3WPVF4kyW7BemVqonShQDhfu # ltthO0VRHc8SVguSR/yrrvZmPUescHLnkudfzRC5xINklBm9JYDh6NIipdC6Anqh # d5NbZcPuF3S8QYYq3AhMjJKMkS2ed0QfaNaodHfbDlsyi1aLM73ZY8hJnTrFxeoz # C9Lxoxv0i77Zs1eLO94Ep3oisiSuLsdwxb5OgyYI+wu9qU+ZCOEQKHKqzQIDAQAB # o4IBVzCCAVMwDAYDVR0TAQH/BAIwADAWBgNVHSUBAf8EDDAKBggrBgEFBQcDCDAO # BgNVHQ8BAf8EBAMCB4AwcwYIKwYBBQUHAQEEZzBlMCoGCCsGAQUFBzABhh5odHRw # Oi8vdHMtb2NzcC53cy5zeW1hbnRlYy5jb20wNwYIKwYBBQUHMAKGK2h0dHA6Ly90 # cy1haWEud3Muc3ltYW50ZWMuY29tL3Rzcy1jYS1nMi5jZXIwPAYDVR0fBDUwMzAx # oC+gLYYraHR0cDovL3RzLWNybC53cy5zeW1hbnRlYy5jb20vdHNzLWNhLWcyLmNy # bDAoBgNVHREEITAfpB0wGzEZMBcGA1UEAxMQVGltZVN0YW1wLTIwNDgtMjAdBgNV # HQ4EFgQURsZpow5KFB7VTNpSYxc/Xja8DeYwHwYDVR0jBBgwFoAUX5r1blzMzHSa # 1N197z/b7EyALt0wDQYJKoZIhvcNAQEFBQADggEBAHg7tJEqAEzwj2IwN3ijhCcH # bxiy3iXcoNSUA6qGTiWfmkADHN3O43nLIWgG2rYytG2/9CwmYzPkSWRtDebDZw73 # BaQ1bHyJFsbpst+y6d0gxnEPzZV03LZc3r03H0N45ni1zSgEIKOq8UvEiCmRDoDR # EfzdXHZuT14ORUZBbg2w6jiasTraCXEQ/Bx5tIB7rGn0/Zy2DBYr8X9bCT2bW+IW # yhOBbQAuOA2oKY8s4bL0WqkBrxWcLC9JG9siu8P+eJRRw4axgohd8D20UaF5Mysu # e7ncIAkTcetqGVvP6KUwVyyJST+5z3/Jvz4iaGNTmr1pdKzFHTx/kuDDvBzYBHUw # ggUnMIIED6ADAgECAhAJT00SLqoJkIvAj67NF8OqMA0GCSqGSIb3DQEBCwUAMHIx # CzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3 # dy5kaWdpY2VydC5jb20xMTAvBgNVBAMTKERpZ2lDZXJ0IFNIQTIgQXNzdXJlZCBJ # RCBDb2RlIFNpZ25pbmcgQ0EwHhcNMTYwNjA2MDAwMDAwWhcNMTkwNjExMTIwMDAw # WjBkMQswCQYDVQQGEwJDSDESMBAGA1UECBMJU29sb3RodXJuMREwDwYDVQQHDAhE # w6RuaWtlbjEWMBQGA1UEChMNYmFzZVZJU0lPTiBBRzEWMBQGA1UEAxMNYmFzZVZJ # U0lPTiBBRzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJ+YpjWmBGJ6 # 6p3mACb/iu1w1oUOFAPZVNSZ8nPOY2MNtzi8d2RRSf16+VVSBhy4wv5sg0QAu76I # 1B5mwWA73gjDERH4LRvisNLrd5cR/CyS1DLZvHY01g7Ck7MtNSekjPEHIc6LFK/4 # 5gQ28nAPcanR2wo+RPGxu34QXKg3ceBH92POm1GDGGUMsTjP7ME7ZOeLKLScJD/V # rmMH/B6K7ApfAF2O/szxFXrEo+5VcloWoCRHmbFe7nLnAC8k5I63ZBmiSi6EBc89 # ID+XaVWLYvVCNwI/PVEanmDxBG9SAxRnJtcUAYg62S84ClXNj2y53xPUbdZvz3mC # RTivIlhjH9ECAwEAAaOCAcUwggHBMB8GA1UdIwQYMBaAFFrEuXsqCqOl6nEDwGD5 # LfZldQ5YMB0GA1UdDgQWBBR6hPT/LYCRb+slld/aUoR4eQYCQDAOBgNVHQ8BAf8E # BAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwMwdwYDVR0fBHAwbjA1oDOgMYYvaHR0 # cDovL2NybDMuZGlnaWNlcnQuY29tL3NoYTItYXNzdXJlZC1jcy1nMS5jcmwwNaAz # oDGGL2h0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9zaGEyLWFzc3VyZWQtY3MtZzEu # Y3JsMEwGA1UdIARFMEMwNwYJYIZIAYb9bAMBMCowKAYIKwYBBQUHAgEWHGh0dHBz # Oi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwCAYGZ4EMAQQBMIGEBggrBgEFBQcBAQR4 # MHYwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBOBggrBgEF # BQcwAoZCaHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0U0hBMkFz # c3VyZWRJRENvZGVTaWduaW5nQ0EuY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcN # AQELBQADggEBAI5wXkMjGctA2E/fchGVptw2Qzdp1a3C1ApX4STqxhkKaQMMJao7 # cHarrQctdjRo2YHEsEsPpOKpQcB2gEUnhWInaghmq618MC/UYZtL/hUcGraEhRO6 # PEDoM/2Xz1+EJJbgmS812YOih1xXrbzfgKE3Zl01VsoNjPvsD4XtEuD0Utjrwsh/ # Qy3gD9Wb925oYOuIz9hp1+jmnQu7hlRaVr7TtxR4aTtTqQdAv35FKPqJdXXUZ9Y9 # otWAWBgWb8YFqMTw6gig3EUORB+MyPXN/zCdwrbAcXlrMIPHhKsvJ6UkxfQkfb4Z # oztVtMUBChHanEVcX4bVFQwNnDVcrlt8w6IwggUwMIIEGKADAgECAhAECRgbX9W7 # ZnVTQ7VvlVAIMA0GCSqGSIb3DQEBCwUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQK # EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNV # BAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAeFw0xMzEwMjIxMjAwMDBa # Fw0yODEwMjIxMjAwMDBaMHIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2Vy # dCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xMTAvBgNVBAMTKERpZ2lD # ZXJ0IFNIQTIgQXNzdXJlZCBJRCBDb2RlIFNpZ25pbmcgQ0EwggEiMA0GCSqGSIb3 # DQEBAQUAA4IBDwAwggEKAoIBAQD407Mcfw4Rr2d3B9MLMUkZz9D7RZmxOttE9X/l # qJ3bMtdx6nadBS63j/qSQ8Cl+YnUNxnXtqrwnIal2CWsDnkoOn7p0WfTxvspJ8fT # eyOU5JEjlpB3gvmhhCNmElQzUHSxKCa7JGnCwlLyFGeKiUXULaGj6YgsIJWuHEqH # CN8M9eJNYBi+qsSyrnAxZjNxPqxwoqvOf+l8y5Kh5TsxHM/q8grkV7tKtel05iv+ # bMt+dDk2DZDv5LVOpKnqagqrhPOsZ061xPeM0SAlI+sIZD5SlsHyDxL0xY4PwaLo # LFH3c7y9hbFig3NBggfkOItqcyDQD2RzPJ6fpjOp/RnfJZPRAgMBAAGjggHNMIIB # yTASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBhjATBgNVHSUEDDAK # BggrBgEFBQcDAzB5BggrBgEFBQcBAQRtMGswJAYIKwYBBQUHMAGGGGh0dHA6Ly9v # Y3NwLmRpZ2ljZXJ0LmNvbTBDBggrBgEFBQcwAoY3aHR0cDovL2NhY2VydHMuZGln # aWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNydDCBgQYDVR0fBHow # eDA6oDigNoY0aHR0cDovL2NybDQuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJl # ZElEUm9vdENBLmNybDA6oDigNoY0aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0Rp # Z2lDZXJ0QXNzdXJlZElEUm9vdENBLmNybDBPBgNVHSAESDBGMDgGCmCGSAGG/WwA # AgQwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzAK # BghghkgBhv1sAzAdBgNVHQ4EFgQUWsS5eyoKo6XqcQPAYPkt9mV1DlgwHwYDVR0j # BBgwFoAUReuir/SSy4IxLVGLp6chnfNtyA8wDQYJKoZIhvcNAQELBQADggEBAD7s # DVoks/Mi0RXILHwlKXaoHV0cLToaxO8wYdd+C2D9wz0PxK+L/e8q3yBVN7Dh9tGS # dQ9RtG6ljlriXiSBThCk7j9xjmMOE0ut119EefM2FAaK95xGTlz/kLEbBw6RFfu6 # r7VRwo0kriTGxycqoSkoGjpxKAI8LpGjwCUR4pwUR6F6aGivm6dcIFzZcbEMj7uo # +MUSaJ/PQMtARKUT8OZkDCUIQjKyNookAv4vcn4c10lFluhZHen6dGRrsutmQ9qz # sIzV6Q3d9gEgzpkxYz0IGhizgZtPxpMQBvwHgfqL2vmCSfdibqFT+hKUGIUukpHq # aGxEMrJmoecYpJpkUe8xggQ3MIIEMwIBATCBhjByMQswCQYDVQQGEwJVUzEVMBMG # A1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMTEw # LwYDVQQDEyhEaWdpQ2VydCBTSEEyIEFzc3VyZWQgSUQgQ29kZSBTaWduaW5nIENB # AhAJT00SLqoJkIvAj67NF8OqMAkGBSsOAwIaBQCgeDAYBgorBgEEAYI3AgEMMQow # CKACgAChAoAAMBkGCSqGSIb3DQEJAzEMBgorBgEEAYI3AgEEMBwGCisGAQQBgjcC # AQsxDjAMBgorBgEEAYI3AgEVMCMGCSqGSIb3DQEJBDEWBBR5yeRdH4CfHz/EJy2X # mY6Wf5H9qDANBgkqhkiG9w0BAQEFAASCAQAp4iS20mkCjEQPoCbH1SICBjWULSBE # tViouhuxZJ8xNatiGsN+BOpT+mFO0Ucf2aVv5aphyWQkebxBpZJR5VxmH9YSIcBD # UHBTclJSgxXjgXZP06QIU07gMF689mTr2L8BFhV1iPFFCFIVI8i6LT2NURk43amW # azYEWs7KNdUnpo0zE8ZvI8vJBgdNJ2MjBQCLs3hx9mn0l9T/Ki+5G3ETAYQTrbkq # uoDb5pOwu2dlax55DduDCEk839xGAGajeu+fg7vEnYyPTV8v5YdxiwSKNZypOs0/ # gB36CxrLrUiMLgMI+vtU86U/opLGl9a5QrZ+NE3zDlP9O7yJVzBbWDRuoYICCzCC # AgcGCSqGSIb3DQEJBjGCAfgwggH0AgEBMHIwXjELMAkGA1UEBhMCVVMxHTAbBgNV # BAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMTAwLgYDVQQDEydTeW1hbnRlYyBUaW1l # IFN0YW1waW5nIFNlcnZpY2VzIENBIC0gRzICEA7P9DjI/r81bgTYapgbGlAwCQYF # Kw4DAhoFAKBdMBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkF # MQ8XDTE5MDEwOTE1MjgxOFowIwYJKoZIhvcNAQkEMRYEFD7BSpgiBDgjTo5fDiD/ # gsAVvo/nMA0GCSqGSIb3DQEBAQUABIIBAJzjGjjEYrouMPWdHQNP6oii1/Tl3VKJ # TM3ci9jVsxQzOmQ07sjYywoGqw8yoVtv8eeHNeNQ9QL5vWhorh2wARMkedm2fFiC # F8To5P2RVarjgPqGRtyQUVjoXQe2tTrJN2jp6k9OFahlMB6QPd3QSmNzdu3pleGt # qkP8k2oxHo/3tzubQOz4PfNcrsIqvb/g5vnYwNTj19wdYiCEhfprAYumZ/nPraU9 # zIOQV6DWUERPs1D/v+NEe/CvgHayVsEZ0jQRQmj6KxfukxRXbztKNpHuBtPueDhy # gEZpOXPMVZPwt++YamVOBXPh6T1Acns8fh8OUSb1JQeIO34wuNqfoyU= # SIG # End signature block |