PSBlackListChecker.psm1
function Format-FirstXChars { <# .SYNOPSIS Short description .DESCRIPTION Long description .PARAMETER Text Parameter description .PARAMETER NumberChars Parameter description .EXAMPLE Format-FirstChars -Text "VERBOSE: Loading module from path 'C:\Users\pklys\.vscode\extensions\ms-vs" -NumberChars 15 .NOTES General notes #> param([string] $Text, [int] $NumberChars) return ($Text.ToCharArray() | Select-Object -First $NumberChars) -join '' } function New-Runspace { [cmdletbinding()] param ([int] $minRunspaces = 1, [int] $maxRunspaces = [int]$env:NUMBER_OF_PROCESSORS + 1) $RunspacePool = [RunspaceFactory]::CreateRunspacePool($minRunspaces, $maxRunspaces) $RunspacePool.Open() return $RunspacePool } function Send-Email { [CmdletBinding(SupportsShouldProcess = $true)] param ([alias('EmailParameters')][System.Collections.IDictionary] $Email, [string] $Body, [string[]] $Attachment, [System.Collections.IDictionary] $InlineAttachments, [string] $Subject, [string[]] $To, [PSCustomObject] $Logger) try { if ($Email.EmailTo) { $EmailParameters = $Email.Clone() $EmailParameters.EmailEncoding = $EmailParameters.EmailEncoding -replace "-", '' $EmailParameters.EmailEncodingSubject = $EmailParameters.EmailEncodingSubject -replace "-", '' $EmailParameters.EmailEncodingBody = $EmailParameters.EmailEncodingSubject -replace "-", '' $EmailParameters.EmailEncodingAlternateView = $EmailParameters.EmailEncodingAlternateView -replace "-", '' } else { $EmailParameters = @{EmailFrom = $Email.From EmailTo = $Email.To EmailCC = $Email.CC EmailBCC = $Email.BCC EmailReplyTo = $Email.ReplyTo EmailServer = $Email.Server EmailServerPassword = $Email.Password EmailServerPasswordAsSecure = $Email.PasswordAsSecure EmailServerPasswordFromFile = $Email.PasswordFromFile EmailServerPort = $Email.Port EmailServerLogin = $Email.Login EmailServerEnableSSL = $Email.EnableSsl EmailEncoding = $Email.Encoding -replace "-", '' EmailEncodingSubject = $Email.EncodingSubject -replace "-", '' EmailEncodingBody = $Email.EncodingBody -replace "-", '' EmailEncodingAlternateView = $Email.EncodingAlternateView -replace "-", '' EmailSubject = $Email.Subject EmailPriority = $Email.Priority EmailDeliveryNotifications = $Email.DeliveryNotifications EmailUseDefaultCredentials = $Email.UseDefaultCredentials } } } catch { return @{Status = $False Error = $($_.Exception.Message) SentTo = '' } } $SmtpClient = [System.Net.Mail.SmtpClient]::new() if ($EmailParameters.EmailServer) { $SmtpClient.Host = $EmailParameters.EmailServer } else { return @{Status = $False Error = "Email Server Host is not set." SentTo = '' } } if ($EmailParameters.EmailServerPort) { $SmtpClient.Port = $EmailParameters.EmailServerPort } else { return @{Status = $False Error = "Email Server Port is not set." SentTo = '' } } if ($EmailParameters.EmailServerLogin) { $Credentials = Request-Credentials -UserName $EmailParameters.EmailServerLogin -Password $EmailParameters.EmailServerPassword -AsSecure:$EmailParameters.EmailServerPasswordAsSecure -FromFile:$EmailParameters.EmailServerPasswordFromFile -NetworkCredentials $SmtpClient.Credentials = $Credentials } if ($EmailParameters.EmailServerEnableSSL) { $SmtpClient.EnableSsl = $EmailParameters.EmailServerEnableSSL } $MailMessage = [System.Net.Mail.MailMessage]::new() $MailMessage.From = $EmailParameters.EmailFrom if ($To) { foreach ($T in $To) { $MailMessage.To.add($($T)) } } else { if ($EmailParameters.Emailto) { foreach ($To in $EmailParameters.Emailto) { $MailMessage.To.add($($To)) } } } if ($EmailParameters.EmailCC) { foreach ($CC in $EmailParameters.EmailCC) { $MailMessage.CC.add($($CC)) } } if ($EmailParameters.EmailBCC) { foreach ($BCC in $EmailParameters.EmailBCC) { $MailMessage.BCC.add($($BCC)) } } if ($EmailParameters.EmailReplyTo) { $MailMessage.ReplyTo = $EmailParameters.EmailReplyTo } $MailMessage.IsBodyHtml = $true if ($Subject -eq '') { $MailMessage.Subject = $EmailParameters.EmailSubject } else { $MailMessage.Subject = $Subject } $MailMessage.Priority = [System.Net.Mail.MailPriority]::$($EmailParameters.EmailPriority) if ($EmailParameters.EmailEncodingSubject) { $MailMessage.SubjectEncoding = [System.Text.Encoding]::$($EmailParameters.EmailEncodingSubject) } elseif ($EmailParameters.EmailEncoding) { $MailMessage.SubjectEncoding = [System.Text.Encoding]::$($EmailParameters.EmailEncoding) } if ($EmailParameters.EmailEncodingBody) { $MailMessage.BodyEncoding = [System.Text.Encoding]::$($EmailParameters.EmailEncodingBody) } elseif ($EmailParameters.EmailEncoding) { $MailMessage.BodyEncoding = [System.Text.Encoding]::$($EmailParameters.EmailEncoding) } if ($EmailParameters.EmailUseDefaultCredentials) { $SmtpClient.UseDefaultCredentials = $EmailParameters.EmailUseDefaultCredentials } if ($EmailParameters.EmailDeliveryNotifications) { $MailMessage.DeliveryNotificationOptions = $EmailParameters.EmailDeliveryNotifications } if ($PSBoundParameters.ContainsKey('InlineAttachments')) { if ($EmailParameters.EmailEncodingAlternateView) { $BodyPart = [Net.Mail.AlternateView]::CreateAlternateViewFromString($Body, [System.Text.Encoding]::$($EmailParameters.EmailEncodingAlternateView) , 'text/html') } else { $BodyPart = [Net.Mail.AlternateView]::CreateAlternateViewFromString($Body, [System.Text.Encoding]::UTF8, 'text/html') } $MailMessage.AlternateViews.Add($BodyPart) foreach ($Entry in $InlineAttachments.GetEnumerator()) { try { $FilePath = $Entry.Value Write-Verbose $FilePath if ($Entry.Value.StartsWith('http')) { $FileName = $Entry.Value.Substring($Entry.Value.LastIndexOf("/") + 1) $FilePath = Join-Path $env:temp $FileName Invoke-WebRequest -Uri $Entry.Value -OutFile $FilePath } $ContentType = Get-MimeType -FileName $FilePath $InAttachment = [Net.Mail.LinkedResource]::new($FilePath, $ContentType) $InAttachment.ContentId = $Entry.Key $BodyPart.LinkedResources.Add($InAttachment) } catch { $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " " Write-Error "Error inlining attachments: $ErrorMessage" } } } else { $MailMessage.Body = $Body } if ($PSBoundParameters.ContainsKey('Attachment')) { foreach ($Attach in $Attachment) { if (Test-Path -LiteralPath $Attach) { try { $File = [Net.Mail.Attachment]::new($Attach) $MailMessage.Attachments.Add($File) } catch { $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " " if ($Logger) { $Logger.AddErrorRecord("Error attaching file $Attach`: $ErrorMessage") } else { Write-Error "Error attaching file $Attach`: $ErrorMessage" } } } } } try { $MailSentTo = "$($MailMessage.To) $($MailMessage.CC) $($MailMessage.BCC)".Trim() if ($pscmdlet.ShouldProcess("$MailSentTo", "Send-Email")) { $SmtpClient.Send($MailMessage) $MailMessage.Dispose() return [PSCustomObject] @{Status = $True Error = "" SentTo = $MailSentTo } } } catch { $MailMessage.Dispose() return [PSCustomObject] @{Status = $False Error = $($_.Exception.Message) SentTo = "" } } } function Start-Runspace { [cmdletbinding()] param ([ScriptBlock] $ScriptBlock, [System.Collections.IDictionary] $Parameters, [System.Management.Automation.Runspaces.RunspacePool] $RunspacePool) if ($ScriptBlock -ne '') { $runspace = [PowerShell]::Create() $null = $runspace.AddScript($ScriptBlock) if ($null -ne $Parameters) { $null = $runspace.AddParameters($Parameters) } $runspace.RunspacePool = $RunspacePool [PSCustomObject]@{Pipe = $runspace Status = $runspace.BeginInvoke() } } } function Stop-Runspace { [cmdletbinding()] param([Array] $Runspaces, [string] $FunctionName, [System.Management.Automation.Runspaces.RunspacePool] $RunspacePool, [switch] $ExtendedOutput) [Array] $List = While (@($Runspaces | Where-Object -FilterScript { $null -ne $_.Status }).count -gt 0) { foreach ($Runspace in $Runspaces | Where-Object { $_.Status.IsCompleted -eq $true }) { $Errors = foreach ($e in $($Runspace.Pipe.Streams.Error)) { Write-Error -ErrorRecord $e $e } foreach ($w in $($Runspace.Pipe.Streams.Warning)) { Write-Warning -Message $w } foreach ($v in $($Runspace.Pipe.Streams.Verbose)) { Write-Verbose -Message $v } if ($ExtendedOutput) { @{Output = $Runspace.Pipe.EndInvoke($Runspace.Status) Errors = $Errors } } else { $Runspace.Pipe.EndInvoke($Runspace.Status) } $Runspace.Status = $null } } $RunspacePool.Close() $RunspacePool.Dispose() if ($List.Count -eq 1) { return , $List } else { return $List } } function Get-MimeType { [CmdletBinding()] param ([Parameter(Mandatory = $true)] [string] $FileName) $MimeMappings = @{'.jpeg' = 'image/jpeg' '.jpg' = 'image/jpeg' '.png' = 'image/png' } $Extension = [System.IO.Path]::GetExtension($FileName) $ContentType = $MimeMappings[ $Extension ] if ([string]::IsNullOrEmpty($ContentType)) { return New-Object System.Net.Mime.ContentType } else { return New-Object System.Net.Mime.ContentType($ContentType) } } function Request-Credentials { [CmdletBinding()] param([string] $UserName, [string] $Password, [switch] $AsSecure, [switch] $FromFile, [switch] $Output, [switch] $NetworkCredentials, [string] $Service) if ($FromFile) { if (($Password -ne '') -and (Test-Path $Password)) { Write-Verbose "Request-Credentials - Reading password from file $Password" $Password = Get-Content -Path $Password } else { if ($Output) { return @{Status = $false; Output = $Service; Extended = 'File with password unreadable.' } } else { Write-Warning "Request-Credentials - Secure password from file couldn't be read. File not readable. Terminating." return } } } if ($AsSecure) { try { $NewPassword = $Password | ConvertTo-SecureString -ErrorAction Stop } catch { $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " " if ($ErrorMessage -like '*Key not valid for use in specified state*') { if ($Output) { return @{Status = $false; Output = $Service; Extended = "Couldn't use credentials provided. Most likely using credentials from other user/session/computer." } } else { Write-Warning -Message "Request-Credentials - Couldn't use credentials provided. Most likely using credentials from other user/session/computer." return } } else { if ($Output) { return @{Status = $false; Output = $Service; Extended = $ErrorMessage } } else { Write-Warning -Message "Request-Credentials - $ErrorMessage" return } } } } else { $NewPassword = $Password } if ($UserName -and $NewPassword) { if ($AsSecure) { $Credentials = New-Object System.Management.Automation.PSCredential($Username, $NewPassword) } else { Try { $SecurePassword = $Password | ConvertTo-SecureString -AsPlainText -Force -ErrorAction Stop } catch { $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " " if ($ErrorMessage -like '*Key not valid for use in specified state*') { if ($Output) { return @{Status = $false; Output = $Service; Extended = "Couldn't use credentials provided. Most likely using credentials from other user/session/computer." } } else { Write-Warning -Message "Request-Credentials - Couldn't use credentials provided. Most likely using credentials from other user/session/computer." return } } else { if ($Output) { return @{Status = $false; Output = $Service; Extended = $ErrorMessage } } else { Write-Warning -Message "Request-Credentials - $ErrorMessage" return } } } $Credentials = New-Object System.Management.Automation.PSCredential($Username, $SecurePassword) } } else { if ($Output) { return @{Status = $false; Output = $Service; Extended = 'Username or/and Password is empty' } } else { Write-Warning -Message 'Request-Credentials - UserName or Password are empty.' return } } if ($NetworkCredentials) { return $Credentials.GetNetworkCredential() } else { return $Credentials } } function Set-EmailBody($TableData, $TableWelcomeMessage) { $body = @("<p><i>$TableWelcomeMessage</i>" if ($($TableData | Measure-Object).Count -gt 0) { $TableData | ConvertTo-Html -Fragment | Out-String $body = $body -replace ' Added', "<font color=`"green`"><b> Added</b></font>" $body = $body -replace ' Removed', "<font color=`"red`"><b> Removed</b></font>" $body = $body -replace ' Deleted', "<font color=`"red`"><b> Deleted</b></font>" $body = $body -replace ' Changed', "<font color=`"blue`"><b> Changed</b></font>" $body = $body -replace ' Change', "<font color=`"blue`"><b> Change</b></font>" $body = $body -replace ' Disabled', "<font color=`"red`"><b> Disabled</b></font>" $body = $body -replace ' Enabled', "<font color=`"green`"><b> Enabled</b></font>" $body = $body -replace ' Locked out', "<font color=`"red`"><b> Locked out</b></font>" $body = $body -replace ' Lockouts', "<font color=`"red`"><b> Lockouts</b></font>" $body = $body -replace ' Unlocked', "<font color=`"green`"><b> Unlocked</b></font>" $body = $body -replace ' Reset', "<font color=`"blue`"><b> Reset</b></font>" '</p>' } else { '<br><i>No changes happend during that period.</i></p>' }) return $body } function Set-EmailHead { [cmdletBinding()] param([System.Collections.IDictionary] $FormattingOptions) $head = @" <!DOCTYPE html> <head> <meta charset="utf-8" /> <meta content="width=device-width, initial-scale=1" name="viewport" /> <style> BODY { background-color: white; font-family: $($FormattingOptions.FontFamily); font-size: $($FormattingOptions.FontSize); } TABLE { border-width: 1px; border-style: solid; border-color: black; border-collapse: collapse; font-family: $($FormattingOptions.FontTableDataFamily); font-size: $($FormattingOptions.FontTableDataSize); } TH { border-width: 1px; padding: 3px; border-style: solid; border-color: black; background-color: #00297A; color: white; font-family: $($FormattingOptions.FontTableHeadingFamily); font-size: $($FormattingOptions.FontTableHeadingSize); } TR { font-family: $($FormattingOptions.FontTableDataFamily); font-size: $($FormattingOptions.FontTableDataSize); } UL { font-family: $($FormattingOptions.FontFamily); font-size: $($FormattingOptions.FontSize); } LI { font-family: $($FormattingOptions.FontFamily); font-size: $($FormattingOptions.FontSize); } TD { border-width: 1px; padding-right: 2px; padding-left: 2px; padding-top: 0px; padding-bottom: 0px; border-style: solid; border-color: black; background-color: white; font-family: $($FormattingOptions.FontTableDataFamily); font-size: $($FormattingOptions.FontTableDataSize); } H2 { font-family: $($FormattingOptions.FontHeadingFamily); font-size: $($FormattingOptions.FontHeadingSize); } P { font-family: $($FormattingOptions.FontFamily); font-size: $($FormattingOptions.FontSize); } </style> </head> "@ return $Head } function Set-EmailReportBranding { param([alias('FormattingOptions')] $FormattingParameters) if ($FormattingParameters.CompanyBranding.Link) { $Report = "<a style=`"text-decoration:none`" href=`"$($FormattingParameters.CompanyBranding.Link)`" class=`"clink logo-container`">" } else { $Report = '' } if ($FormattingParameters.CompanyBranding.Inline) { $Report += "<img width=<fix> height=<fix> src=`"cid:logo`" border=`"0`" class=`"company-logo`" alt=`"company-logo`"></a>" } else { $Report += "<img width=<fix> height=<fix> src=`"$($FormattingParameters.CompanyBranding.Logo)`" border=`"0`" class=`"company-logo`" alt=`"company-logo`"></a>" } if ($FormattingParameters.CompanyBranding.Width -ne "") { $Report = $Report -replace "width=<fix>", "width=$($FormattingParameters.CompanyBranding.Width)" } else { $Report = $Report -replace "width=<fix>", "" } if ($FormattingParameters.CompanyBranding.Height -ne "") { $Report = $Report -replace "height=<fix>", "height=$($FormattingParameters.CompanyBranding.Height)" } else { $Report = $Report -replace "height=<fix>", "" } return $Report } function Set-EmailReportDetails { param($FormattingOptions, $ReportOptions, $TimeToGenerate) $DateReport = Get-Date $Report = @("<p style=`"background-color:white;font-family:$($FormattingOptions.FontFamily);font-size:$($FormattingOptions.FontSize)`">" "<strong>Report Time:</strong> $DateReport <br>" "<strong>Time to generate:</strong> $($TimeToGenerate.Hours) hours, $($TimeToGenerate.Minutes) minutes, $($TimeToGenerate.Seconds) seconds, $($TimeToGenerate.Milliseconds) milliseconds <br>" if ($PSVersionTable.Platform -ne 'Unix') { "<strong>Account Executing Report :</strong> $env:userdomain\$($env:username.toupper()) on $($env:ComputerName.toUpper()) <br>" } else {} '<strong>Checking for monitored IPs :</strong>' '<ul>' foreach ($ip in $ReportOptions.MonitoredIps.Values) { "<li>ip:</strong> $ip</li>" } '</ul>' '</p>') return $Report } [string[]] $Script:BlackLists = @('b.barracudacentral.org' 'spam.rbl.msrbl.net' 'zen.spamhaus.org' 'bl.deadbeef.com' 'bl.spamcop.net' 'blackholes.five-ten-sg.com' 'blacklist.woody.ch' 'bogons.cymru.com' 'cbl.abuseat.org' 'combined.abuse.ch' 'combined.rbl.msrbl.net' 'db.wpbl.info' 'dnsbl-1.uceprotect.net' 'dnsbl-2.uceprotect.net' 'dnsbl-3.uceprotect.net' 'dnsbl.cyberlogic.net' 'dnsbl.inps.de' 'dnsbl.sorbs.net' 'drone.abuse.ch' 'drone.abuse.ch' 'duinv.aupads.org' 'dul.dnsbl.sorbs.net' 'dul.ru' 'dyna.spamrats.com' 'http.dnsbl.sorbs.net' 'images.rbl.msrbl.net' 'ips.backscatterer.org' 'ix.dnsbl.manitu.net' 'korea.services.net' 'misc.dnsbl.sorbs.net' 'noptr.spamrats.com' 'ohps.dnsbl.net.au' 'omrs.dnsbl.net.au' 'orvedb.aupads.org' 'osps.dnsbl.net.au' 'osrs.dnsbl.net.au' 'owfs.dnsbl.net.au' 'owps.dnsbl.net.au' 'pbl.spamhaus.org' 'phishing.rbl.msrbl.net' 'probes.dnsbl.net.au' 'proxy.bl.gweep.ca' 'proxy.block.transip.nl' 'psbl.surriel.com' 'rbl.interserver.net' 'rdts.dnsbl.net.au' 'relays.bl.gweep.ca' 'relays.bl.kundenserver.de' 'relays.nether.net' 'residential.block.transip.nl' 'ricn.dnsbl.net.au' 'rmst.dnsbl.net.au' 'sbl.spamhaus.org' 'short.rbl.jp' 'smtp.dnsbl.sorbs.net' 'socks.dnsbl.sorbs.net' 'spam.abuse.ch' 'spam.dnsbl.sorbs.net' 'spam.spamrats.com' 'spamlist.or.kr' 'spamrbl.imp.ch' 't3direct.dnsbl.net.au' 'ubl.lashback.com' 'ubl.unsubscore.com' 'virbl.bit.nl' 'virus.rbl.jp' 'virus.rbl.msrbl.net' 'web.dnsbl.sorbs.net' 'wormrbl.imp.ch' 'xbl.spamhaus.org' 'zombie.dnsbl.sorbs.net') $Script:ScriptBlockNetDNS = { param ([string] $Server, [string] $IP, [bool] $QuickTimeout, [bool] $Verbose) if ($Verbose) { $verbosepreference = 'continue' } $ReversedIP = ($IP -split '\.')[3..0] -join '.' $FQDN = "$ReversedIP.$Server" try { $DnsCheck = [Net.DNS]::GetHostAddresses($fqdn) } catch { $DnsCheck = $null } if ($null -ne $DnsCheck) { $ServerData = [PSCustomObject] @{IP = $IP FQDN = $FQDN BlackList = $Server IsListed = if ($null -eq $DNSCheck.IPAddressToString) { $false } else { $true } Answer = $DnsCheck.IPAddressToString -join ', ' TTL = '' } } else { $ServerData = [PSCustomObject] @{IP = $IP FQDN = $FQDN BlackList = $Server IsListed = $false Answer = "" TTL = '' } } return $ServerData } $Script:ScriptBlockResolveDNS = { param ([string] $Server, [string] $IP, [bool] $QuickTimeout, [bool] $Verbose, [string[]] $DNSServer = '') if ($Verbose) { $verbosepreference = 'continue' } [string] $ReversedIP = ($IP -split '\.')[3..0] -join '.' [string] $FQDN = "$ReversedIP.$Server" [int] $Count = 0 [bool] $Loaded = $false Do { try { Import-Module -Name 'DnsClient' -Verbose:$false $Loaded = $true } catch { Write-Warning "DNSClient Import Error ($Server / $FQDN / $IP): $_. Retrying." } $Count++ if ($Loaded -eq $false -and $Count -eq 5) { Write-Warning "DNSClient Import failed. Skipping check on $Server / $FQDN / $IP" } } until ($Loaded -eq $false -or $Count -eq 5) if ($DNSServer -ne '') { $DnsCheck = Resolve-DnsName -Name $fqdn -ErrorAction SilentlyContinue -NoHostsFile -QuickTimeout:$QuickTimeout -Server $DNSServer -DnsOnly } else { $DnsCheck = Resolve-DnsName -Name $fqdn -ErrorAction SilentlyContinue -NoHostsFile -QuickTimeout:$QuickTimeout -DnsOnly } if ($null -ne $DnsCheck) { $ServerData = [PSCustomObject] @{IP = $IP FQDN = $FQDN BlackList = $Server IsListed = if ($null -eq $DNSCheck.IpAddress) { $false } else { $true } Answer = $DnsCheck.IPAddress -join ', ' TTL = $DnsCheck.TTL -join ', ' } } else { $ServerData = [PSCustomObject] @{IP = $IP FQDN = $FQDN BlackList = $Server IsListed = $false Answer = '' TTL = '' } } return $ServerData } $Script:ScriptBlockNetDNSSlow = { param ([string[]] $Servers, [string[]] $IPs, [bool] $QuickTimeout, [bool] $Verbose) if ($Verbose) { $verbosepreference = 'continue' } $Blacklisted = foreach ($Server in $Servers) { foreach ($IP in $IPS) { [string] $ReversedIP = ($IP -split '\.')[3..0] -join '.' [string] $FQDN = "$ReversedIP.$Server" try { $DnsCheck = [Net.DNS]::GetHostAddresses($FQDN) } catch { $DnsCheck = $null } if ($null -ne $DnsCheck) { [PSCustomObject] @{IP = $ip FQDN = $fqdn BlackList = $server IsListed = if ($null -eq $DNSCheck.IPAddressToString) { $false } else { $true } Answer = $DnsCheck.IPAddressToString -join ', ' TTL = '' } } else { [PSCustomObject] @{IP = $IP FQDN = $FQDN BlackList = $Server IsListed = $false Answer = '' TTL = '' } } } } return $Blacklisted } $Script:ScriptBlockResolveDNSSlow = { param ([string[]] $Servers, [string[]] $IPs, [bool] $QuickTimeout, [bool] $Verbose, [string[]] $DNSServer = '') if ($Verbose) { $verbosepreference = 'continue' } $Blacklisted = foreach ($Server in $Servers) { foreach ($IP in $IPS) { $ReversedIP = ($IP -split '\.')[3..0] -join '.' $FQDN = "$ReversedIP.$Server" if ($DNSServer -ne '') { $DnsCheck = Resolve-DnsName -Name $fqdn -ErrorAction SilentlyContinue -NoHostsFile -QuickTimeout:$QuickTimeout -Server $DNSServer -DnsOnly } else { $DnsCheck = Resolve-DnsName -Name $fqdn -ErrorAction SilentlyContinue -NoHostsFile -QuickTimeout:$QuickTimeout -DnsOnly } if ($null -ne $DnsCheck) { [PSCustomObject] @{IP = $IP FQDN = $FQDN BlackList = $Server IsListed = if ($null -eq $DNSCheck.IpAddress) { $false } else { $true } Answer = $DnsCheck.IPAddress -join ', ' TTL = $DnsCheck.TTL -join ', ' } } else { [PSCustomObject] @{IP = $IP FQDN = $FQDN BlackList = $Server IsListed = $false Answer = '' TTL = '' } } } } return $Blacklisted } function Search-BlackList { <# .SYNOPSIS Search-Blacklist searches if particular IP is blacklisted on DNSBL Blacklists. .DESCRIPTION Long description .PARAMETER IPs Parameter description .PARAMETER BlacklistServers Parameter description .PARAMETER ReturnAll Parameter description .PARAMETER RunType Parameter description .PARAMETER SortBy Parameter description .PARAMETER SortDescending Parameter description .PARAMETER QuickTimeout Parameter description .PARAMETER MaxRunspaces Parameter description .PARAMETER ExtendedOutput Parameter description .EXAMPLE Search-BlackList -IP '89.25.253.1' | Format-Table .EXAMPLE Search-BlackList -IP '89.25.253.1' -SortBy Blacklist | Format-Table .EXAMPLE Search-BlackList -IP '89.25.253.1','195.55.55.55' -SortBy Ip -ReturnAll | Format-Table .NOTES General notes #> [cmdletbinding()] param ([alias('IP')][string[]] $IPs, [string[]] $BlacklistServers = $Script:BlackLists, [switch] $ReturnAll, [ValidateSet('NoWorkflowAndRunSpaceNetDNS', 'NoWorkflowAndRunSpaceResolveDNS', 'RunSpaceWithResolveDNS', 'RunSpaceWithNetDNS', 'WorkflowResolveDNS', 'WorkflowWithNetDNS')] [string]$RunType, [ValidateSet('IP', 'BlackList', 'IsListed', 'Answer', 'FQDN')][string] $SortBy = 'IsListed', [switch] $SortDescending, [switch] $QuickTimeout, [int] $MaxRunspaces = 10, [string[]] $DNSServer = '', [switch] $ExtendedOutput) if ($PSCmdlet.MyInvocation.BoundParameters["Verbose"].IsPresent) { $Verbose = $true } else { $Verbose = $false } if ($RunType -eq 'WorkflowResolveDNS') { Write-Warning 'Worflows are not supported anymore due to PowerShell 6 complaining. Please use other modes.' Exit } elseif ($RunType -eq 'WorkflowWithNetDNS') { Write-Warning 'Worflows are not supported anymore due to PowerShell 6 complaining. Please use other modes.' Exit } if ($RunType -eq '') { if ($PSVersionTable.Platform -eq 'Unix') { $RunType = 'RunSpaceWithNetDNS' } else { $RunType = 'RunSpaceWithResolveDNS' } } if ($PSVersionTable.Platform -eq 'Unix') { if ($RunType -eq 'RunSpaceWithResolveDNS') { $RunType = 'RunSpaceWithNetDNS' Write-Warning 'Search-BlackList - changing RunType to RunSpaceWithNetDNS since Resolve-DNSName is not available on Linux/MacOS' } elseif ($RunType -eq 'NoWorkflowAndRunSpaceResolveDNS') { $RunType = 'NoWorkflowAndRunSpaceNetDNS' Write-Warning 'Search-BlackList - changing RunType to RunSpaceWithNetDNS since Resolve-DNSName is not available on Linux/MacOS' } } if ($DNSServer -ne '' -and $RunType -like 'NetDNS') { Write-Warning 'Search-BlackList - Setting DNSServer is not supported for Net.DNS. Resetting to default values.' $DNSServer = '' } Write-Verbose "Search-Blacklist - Runtype: $RunType ReturnAll: $ReturnAll, SortBy: $SortBy MaxRunspaces: $MaxRunspaces SortDescending: $SortDescending" If ($RunType -eq 'NoWorkflowAndRunSpaceNetDNS') { $Table = Invoke-Command -ScriptBlock $Script:ScriptBlockNetDNSSlow -ArgumentList $BlacklistServers, $IPs, $QuickTimeout, $Verbose } elseif ($RunType -eq 'NoWorkflowAndRunSpaceResolveDNS') { $Table = Invoke-Command -ScriptBlock $Script:ScriptBlockResolveDNSSlow -ArgumentList $BlacklistServers, $IPs, $QuickTimeout, $Verbose, $DNSServer } elseif ($RunType -eq 'RunSpaceWithResolveDNS') { $pool = New-Runspace -maxRunspaces $maxRunspaces -Verbose:$Verbose $runspaces = foreach ($Server in $BlacklistServers) { foreach ($IP in $IPs) { $Parameters = @{Server = $Server IP = $IP QuickTimeout = $QuickTimeout Verbose = $Verbose DNSServer = $DNSServer } Start-Runspace -ScriptBlock $Script:ScriptBlockResolveDNS -Parameters $Parameters -RunspacePool $pool -Verbose:$Verbose } } $Output = Stop-Runspace -Runspaces $runspaces -FunctionName 'Search-BlackList' -RunspacePool $pool -Verbose:$Verbose -ErrorAction Continue -ErrorVariable MyErrors -ExtendedOutput:$ExtendedOutput if ($ExtendedOutput) { $Output Exit } else { $Table = $Output } } elseif ($RunType -eq 'RunSpaceWithNetDNS') { $pool = New-Runspace -maxRunspaces $maxRunspaces -Verbose:$Verbose $runspaces = foreach ($server in $BlacklistServers) { foreach ($ip in $IPs) { $Parameters = @{Server = $Server IP = $IP QuickTimeout = $QuickTimeout Verbose = $Verbose } Start-Runspace -ScriptBlock $Script:ScriptBlockNetDNS -Parameters $Parameters -RunspacePool $pool -Verbose:$Verbose } } $Output = Stop-Runspace -Runspaces $runspaces -FunctionName 'Search-BlackList' -RunspacePool $pool -Verbose:$Verbose -ExtendedOutput:$ExtendedOutput if ($ExtendedOutput) { $Output Exit } else { $Table = $Output } } if ($SortDescending -eq $true) { $Table = $Table | Sort-Object $SortBy -Descending } else { $Table = $Table | Sort-Object $SortBy } if ($ReturnAll -eq $true) { return $Table | Select-Object IP, FQDN, BlackList, IsListed, Answer, TTL } else { return $Table | Where-Object { $_.IsListed -eq $true } | Select-Object IP, FQDN, BlackList, IsListed, Answer, TTL } } function Start-ReportBlackLists { [cmdletbinding()] param([System.Collections.IDictionary] $EmailParameters, [System.Collections.IDictionary] $FormattingParameters, [System.Collections.IDictionary] $ReportOptions, [switch] $OutputErrors) $Errors = @{Teams = $false Slack = $false Discord = $false } $TeamID = Format-FirstXChars -Text $ReportOptions.NotificationsTeams.TeamsID -NumberChars 25 $SlackID = Format-FirstXChars -Text $ReportOptions.NotificationsSlack.Uri -NumberChars 25 $DiscordID = Format-FirstXChars -Text $ReportOptions.NotificationsDiscord.Uri -NumberChars 25 Write-Verbose "Start-ReportBlackLists - TeamsID: $TeamID" Write-Verbose "Start-ReportBlackLists - SlackID: $SlackID" Write-Verbose "Start-ReportBlackLists - DiscordID: $DiscordID" $Ips = foreach ($ip in $ReportOptions.MonitoredIps.Values) { $ip } if ($null -eq $ReportOptions.NotificationsEmail) { $ReportOptions.NotificationsEmail = @{Use = $true EmailPriorityWhenBlacklisted = $ReportOptions.EmailPriorityWhenBlacklisted EmailPriorityStandard = $ReportOptions.EmailPriorityStandard EmailAllResults = $ReportOptions.EmailAllResults EmailAlways = $ReportOptions.EmailAlways } } $Time = Measure-Command -Expression { if ($null -eq $ReportOptions.SortBy) { $ReportOptions.SortBy = 'IsListed' } if ($null -eq $ReportOptions.SortDescending) { $ReportOptions.SortDescending = $true } if ($ReportOptions.NotificationsEmail.EmailAllResults) { $BlackListCheck = Search-BlackList -IP $Ips -SortBy $ReportOptions.SortBy -SortDescending:$ReportOptions.SortDescending -ReturnAll -Verbose } else { $BlackListCheck = Search-BlackList -IP $Ips -SortBy $ReportOptions.SortBy -SortDescending:$ReportOptions.SortDescending -Verbose } } $EmailBody = @(Set-EmailHead -FormattingOptions $FormattingParameters Set-EmailReportBranding -FormattingOptions $FormattingParameters Set-EmailReportDetails -FormattingOptions $FormattingParameters -ReportOptions $ReportOptions -TimeToGenerate $Time Set-EmailBody -TableData $BlackListCheck -TableWelcomeMessage 'Following blacklisted servers') if ($BlackListCheck.IsListed -contains $true) { $EmailParameters.EmailPriority = $ReportOptions.NotificationsEmail.EmailPriorityWhenBlacklisted } else { $EmailParameters.EmailPriority = $ReportOptions.NotificationsEmail.EmailPriorityStandard } [string] $Email = $EmailBody | Out-String if ($ReportOptions.NotificationsEmail.Use) { if ($ReportOptions.NotificationsEmail.EmailAlways -eq $true -or $BlackListCheck.IsListed -contains $true) { if ($FormattingParameters.CompanyBranding.Inline) { $SendMail = Send-Email -EmailParameters $EmailParameters -Body $Email -InlineAttachments @{logo = $FormattingParameters.CompanyBranding.Logo } -Verbose } else { $SendMail = Send-Email -EmailParameters $EmailParameters -Body $Email } } } if ($BlackListCheck.IsListed -contains $true) { $BlackListLimited = $BlackListCheck | Where-Object { $_.IsListed -eq $true } if ($ReportOptions.NotificationsTeams.Use) { [string] $MessageTitle = $ReportOptions.NotificationsTeams.MessageTitle [string] $ActivityImageLink = $ReportOptions.NotificationsTeams.MessageImageLink [RGBColors] $Color = [RGBColors]::Red $Sections = @(foreach ($Server in $BlackListLimited) { [string] $ActivityTitle = "Blacklisted IP **$($Server.IP)**" if ($ReportOptions.NotificationsTeams.MessageButtons) { $Button1 = New-TeamsButton -Name "Check BlackList" -Link "https://mxtoolbox.com/SuperTool.aspx?action=blacklist%3a$($Server.Ip)&run=toolpage" $Button2 = New-TeamsButton -Name "Check SMTP" -Link "https://mxtoolbox.com/SuperTool.aspx?action=smtp%3a$($Server.Ip)&run=toolpage" New-TeamsSection -ActivityTitle $ActivityTitle -ActivitySubtitle "Found on blacklist **$($Server.Blacklist)**" -ActivityImageLink $ActivityImageLink -ActivityText "Everybody panic!" -Buttons $Button1, $Button2 } else { New-TeamsSection -ActivityTitle $ActivityTitle -ActivitySubtitle "Found on blacklist **$($Server.Blacklist)**" -ActivityImageLink $ActivityImageLink -ActivityText "Responses: $($Server.Answer)" } }) try { $TeamsOutput = Send-TeamsMessage -Uri $ReportOptions.NotificationsTeams.TeamsID -MessageTitle $MessageTitle -Color $Color -Sections $Sections -Supress $false } catch { $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " " Write-Warning "Couldn't send to Teams - Error occured: $ErrorMessage" $Errors.Teams = $true } } if ($ReportOptions.NotificationsSlack.Use) { $MessageTitle = $ReportOptions.NotificationsSlack.MessageTitle [string] $ActivityImageLink = $ReportOptions.NotificationsSlack.MessageImageLink $Attachments = @(foreach ($Server in $BlackListLimited) { New-SlackMessageAttachment -Color $_PSSlackColorMap.red -Title "IP $($Server.IP) is Blacklisted" -TitleLink "https://mxtoolbox.com/SuperTool.aspx?action=blacklist%3a$($Server.Ip)&run=toolpage" -Text $ReportOptions.NotificationsSlack.MessageText -Pretext "Found on blacklist $($Server.Blacklist)" -Fallback 'Your client is bad' }) try { $SlackOutput = New-SlackMessage -Attachments $Attachments -Channel $ReportOptions.NotificationsSlack.Channel -IconEmoji $ReportOptions.NotificationsSlack.MessageEmoji -AsUser -Username $ReportOptions.NotificationsSlack.MessageAsUser | Send-SlackMessage -Uri $ReportOptions.NotificationsSlack.URI } catch { $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " " Write-Warning "Couldn't send to Slack - Error occured: $ErrorMessage" $Errors.Slack = $true } } if ($ReportOptions.NotificationsDiscord.Use) { if ($null -eq $ReportOptions.NotificationsDiscord.MessageInline) { $ReportOptions.NotificationsDiscord.MessageInline = $false } try { $Facts = foreach ($Server in $BlackListLimited) { [string] $ActivityTitle = "Blacklisted IP $($Server.IP)" [string] $ActivityValue = "Found on blacklist $($Server.Blacklist)" New-DiscordFact -Name $ActivityTitle -Value $ActivityValue -Inline $ReportOptions.NotificationsDiscord.MessageInline } $Thumbnail = New-DiscordThumbnail -Url $ReportOptions.NotificationsDiscord.MessageImageLink $Author = New-DiscordAuthor -Name 'PSBlacklistChecker' -IconUrl $ReportOptions.NotificationsDiscord.MessageImageLink $Section = New-DiscordSection -Title $ReportOptions.NotificationsDiscord.MessageText -Description '' -Facts $Facts -Color $ReportOptions.NotificationsDiscord.MessageColor -Author $Author -Thumbnail $Thumbnail Send-DiscordMessage -WebHookUrl $ReportOptions.NotificationsDiscord.Uri -Sections $Section -AvatarName $ReportOptions.NotificationsDiscord.MessageAsUser -AvatarUrl $ReportOptions.NotificationsDiscord.MessageAsUserImage -Verbose } catch { $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " " Write-Warning "Couldn't send to Discord - Error occured: $ErrorMessage" $Errors.Discord = $true } } if ($OutputErrors) { return $Errors } } } Export-ModuleMember -Function @('Search-BlackList', 'Start-ReportBlackLists') -Alias @() # SIG # Begin signature block # MIIgQAYJKoZIhvcNAQcCoIIgMTCCIC0CAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB # gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR # AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUZ9iXBqkqU+8XcU4raLnPiaIs # DOCgghtvMIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0B # AQUFADBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYD # VQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVk # IElEIFJvb3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzExMTEwMDAwMDAwWjBlMQsw # CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu # ZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3Qg # Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg # +XESpa7cJpSIqvTO9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lT # XDGEKvYPmDI2dsze3Tyoou9q+yHyUmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5 # a3/UsDg+wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW/lmci3Zt1/GiSw0r/wty2p5g # 0I6QNcZ4VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpyoeb6pNnVFzF1 # roV9Iq4/AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whf # GHdPAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0G # A1UdDgQWBBRF66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLL # gjEtUYunpyGd823IDzANBgkqhkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3 # cmbYMuRCdWKuh+vy1dneVrOfzM4UKLkNl2BcEkxY5NM9g0lFWJc1aRqoR+pWxnmr # EthngYTffwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38FnSbNd67IJKusm7Xi+ # fT8r87cmNW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i8b5Q # Z7dsvfPxH2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu # 838fYxAe+o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw # 8jCCBTAwggQYoAMCAQICEAQJGBtf1btmdVNDtW+VUAgwDQYJKoZIhvcNAQELBQAw # ZTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQ # d3d3LmRpZ2ljZXJ0LmNvbTEkMCIGA1UEAxMbRGlnaUNlcnQgQXNzdXJlZCBJRCBS # b290IENBMB4XDTEzMTAyMjEyMDAwMFoXDTI4MTAyMjEyMDAwMFowcjELMAkGA1UE # BhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2lj # ZXJ0LmNvbTExMC8GA1UEAxMoRGlnaUNlcnQgU0hBMiBBc3N1cmVkIElEIENvZGUg # U2lnbmluZyBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPjTsxx/ # DhGvZ3cH0wsxSRnP0PtFmbE620T1f+Wondsy13Hqdp0FLreP+pJDwKX5idQ3Gde2 # qvCchqXYJawOeSg6funRZ9PG+yknx9N7I5TkkSOWkHeC+aGEI2YSVDNQdLEoJrsk # acLCUvIUZ4qJRdQtoaPpiCwgla4cSocI3wz14k1gGL6qxLKucDFmM3E+rHCiq85/ # 6XzLkqHlOzEcz+ryCuRXu0q16XTmK/5sy350OTYNkO/ktU6kqepqCquE86xnTrXE # 94zRICUj6whkPlKWwfIPEvTFjg/BougsUfdzvL2FsWKDc0GCB+Q4i2pzINAPZHM8 # np+mM6n9Gd8lk9ECAwEAAaOCAc0wggHJMBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYD # VR0PAQH/BAQDAgGGMBMGA1UdJQQMMAoGCCsGAQUFBwMDMHkGCCsGAQUFBwEBBG0w # azAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEMGCCsGAQUF # BzAChjdodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1cmVk # SURSb290Q0EuY3J0MIGBBgNVHR8EejB4MDqgOKA2hjRodHRwOi8vY3JsNC5kaWdp # Y2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3JsMDqgOKA2hjRodHRw # Oi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3Js # ME8GA1UdIARIMEYwOAYKYIZIAYb9bAACBDAqMCgGCCsGAQUFBwIBFhxodHRwczov # L3d3dy5kaWdpY2VydC5jb20vQ1BTMAoGCGCGSAGG/WwDMB0GA1UdDgQWBBRaxLl7 # KgqjpepxA8Bg+S32ZXUOWDAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823I # DzANBgkqhkiG9w0BAQsFAAOCAQEAPuwNWiSz8yLRFcgsfCUpdqgdXRwtOhrE7zBh # 134LYP3DPQ/Er4v97yrfIFU3sOH20ZJ1D1G0bqWOWuJeJIFOEKTuP3GOYw4TS63X # X0R58zYUBor3nEZOXP+QsRsHDpEV+7qvtVHCjSSuJMbHJyqhKSgaOnEoAjwukaPA # JRHinBRHoXpoaK+bp1wgXNlxsQyPu6j4xRJon89Ay0BEpRPw5mQMJQhCMrI2iiQC # /i9yfhzXSUWW6Fkd6fp0ZGuy62ZD2rOwjNXpDd32ASDOmTFjPQgaGLOBm0/GkxAG # /AeB+ova+YJJ92JuoVP6EpQYhS6SkepobEQysmah5xikmmRR7zCCBT0wggQloAMC # AQICEATV3B9I6snYUgC6zZqbKqcwDQYJKoZIhvcNAQELBQAwcjELMAkGA1UEBhMC # VVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0 # LmNvbTExMC8GA1UEAxMoRGlnaUNlcnQgU0hBMiBBc3N1cmVkIElEIENvZGUgU2ln # bmluZyBDQTAeFw0yMDA2MjYwMDAwMDBaFw0yMzA3MDcxMjAwMDBaMHoxCzAJBgNV # BAYTAlBMMRIwEAYDVQQIDAnFmmzEhXNraWUxETAPBgNVBAcTCEthdG93aWNlMSEw # HwYDVQQKDBhQcnplbXlzxYJhdyBLxYJ5cyBFVk9URUMxITAfBgNVBAMMGFByemVt # eXPFgmF3IEvFgnlzIEVWT1RFQzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC # ggEBAL+ygd4sga4ZC1G2xXvasYSijwWKgwapZ69wLaWaZZIlY6YvXTGQnIUnk+Tg # 7EoT7mQiMSaeSPOrn/Im6N74tkvRfQJXxY1cnt3U8//U5grhh/CULdd6M3/Z4h3n # MCq7LQ1YVaa4MYub9F8WOdXO84DANoNVG/t7YotL4vzqZil3S9pHjaidp3kOXGJc # vxrCPAkRFBKvUmYo23QPFa0Rd0qA3bFhn97WWczup1p90y2CkOf28OVOOObv1fNE # EqMpLMx0Yr04/h+LPAAYn6K4YtIu+m3gOhGuNc3B+MybgKePAeFIY4EQzbqvCMy1 # iuHZb6q6ggRyqrJ6xegZga7/gV0CAwEAAaOCAcUwggHBMB8GA1UdIwQYMBaAFFrE # uXsqCqOl6nEDwGD5LfZldQ5YMB0GA1UdDgQWBBQYsTUn6BxQICZOCZA0CxS0TZSU # ZjAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwMwdwYDVR0fBHAw # bjA1oDOgMYYvaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL3NoYTItYXNzdXJlZC1j # cy1nMS5jcmwwNaAzoDGGL2h0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9zaGEyLWFz # c3VyZWQtY3MtZzEuY3JsMEwGA1UdIARFMEMwNwYJYIZIAYb9bAMBMCowKAYIKwYB # BQUHAgEWHGh0dHBzOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwCAYGZ4EMAQQBMIGE # BggrBgEFBQcBAQR4MHYwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0 # LmNvbTBOBggrBgEFBQcwAoZCaHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0Rp # Z2lDZXJ0U0hBMkFzc3VyZWRJRENvZGVTaWduaW5nQ0EuY3J0MAwGA1UdEwEB/wQC # MAAwDQYJKoZIhvcNAQELBQADggEBAJq9bM+JbCwEYuMBtXoNAfH1SRaMLXnLe0py # VK6el0Z1BtPxiNcF4iyHqMNVD4iOrgzLEVzx1Bf/sYycPEnyG8Gr2tnl7u1KGSjY # enX4LIXCZqNEDQCeTyMstNv931421ERByDa0wrz1Wz5lepMeCqXeyiawqOxA9fB/ # 106liR12vL2tzGC62yXrV6WhD6W+s5PpfEY/chuIwVUYXp1AVFI9wi2lg0gaTgP/ # rMfP1wfVvaKWH2Bm/tU5mwpIVIO0wd4A+qOhEia3vn3J2Zz1QDxEprLcLE9e3Gmd # G5+8xEypTR23NavhJvZMgY2kEXBEKEEDaXs0LoPbn6hMcepR2A4wggZqMIIFUqAD # AgECAhADAZoCOv9YsWvW1ermF/BmMA0GCSqGSIb3DQEBBQUAMGIxCzAJBgNVBAYT # AlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2Vy # dC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IEFzc3VyZWQgSUQgQ0EtMTAeFw0xNDEw # MjIwMDAwMDBaFw0yNDEwMjIwMDAwMDBaMEcxCzAJBgNVBAYTAlVTMREwDwYDVQQK # EwhEaWdpQ2VydDElMCMGA1UEAxMcRGlnaUNlcnQgVGltZXN0YW1wIFJlc3BvbmRl # cjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKNkXfx8s+CCNeDg9sYq # 5kl1O8xu4FOpnx9kWeZ8a39rjJ1V+JLjntVaY1sCSVDZg85vZu7dy4XpX6X51Id0 # iEQ7Gcnl9ZGfxhQ5rCTqqEsskYnMXij0ZLZQt/USs3OWCmejvmGfrvP9Enh1DqZb # FP1FI46GRFV9GIYFjFWHeUhG98oOjafeTl/iqLYtWQJhiGFyGGi5uHzu5uc0LzF3 # gTAfuzYBje8n4/ea8EwxZI3j6/oZh6h+z+yMDDZbesF6uHjHyQYuRhDIjegEYNu8 # c3T6Ttj+qkDxss5wRoPp2kChWTrZFQlXmVYwk/PJYczQCMxr7GJCkawCwO+k8IkR # j3cCAwEAAaOCAzUwggMxMA4GA1UdDwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMBYG # A1UdJQEB/wQMMAoGCCsGAQUFBwMIMIIBvwYDVR0gBIIBtjCCAbIwggGhBglghkgB # hv1sBwEwggGSMCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5kaWdpY2VydC5jb20v # Q1BTMIIBZAYIKwYBBQUHAgIwggFWHoIBUgBBAG4AeQAgAHUAcwBlACAAbwBmACAA # dABoAGkAcwAgAEMAZQByAHQAaQBmAGkAYwBhAHQAZQAgAGMAbwBuAHMAdABpAHQA # dQB0AGUAcwAgAGEAYwBjAGUAcAB0AGEAbgBjAGUAIABvAGYAIAB0AGgAZQAgAEQA # aQBnAGkAQwBlAHIAdAAgAEMAUAAvAEMAUABTACAAYQBuAGQAIAB0AGgAZQAgAFIA # ZQBsAHkAaQBuAGcAIABQAGEAcgB0AHkAIABBAGcAcgBlAGUAbQBlAG4AdAAgAHcA # aABpAGMAaAAgAGwAaQBtAGkAdAAgAGwAaQBhAGIAaQBsAGkAdAB5ACAAYQBuAGQA # IABhAHIAZQAgAGkAbgBjAG8AcgBwAG8AcgBhAHQAZQBkACAAaABlAHIAZQBpAG4A # IABiAHkAIAByAGUAZgBlAHIAZQBuAGMAZQAuMAsGCWCGSAGG/WwDFTAfBgNVHSME # GDAWgBQVABIrE5iymQftHt+ivlcNK2cCzTAdBgNVHQ4EFgQUYVpNJLZJMp1KKnka # g0v0HonByn0wfQYDVR0fBHYwdDA4oDagNIYyaHR0cDovL2NybDMuZGlnaWNlcnQu # Y29tL0RpZ2lDZXJ0QXNzdXJlZElEQ0EtMS5jcmwwOKA2oDSGMmh0dHA6Ly9jcmw0 # LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRENBLTEuY3JsMHcGCCsGAQUF # BwEBBGswaTAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEG # CCsGAQUFBzAChjVodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRB # c3N1cmVkSURDQS0xLmNydDANBgkqhkiG9w0BAQUFAAOCAQEAnSV+GzNNsiaBXJuG # ziMgD4CH5Yj//7HUaiwx7ToXGXEXzakbvFoWOQCd42yE5FpA+94GAYw3+puxnSR+ # /iCkV61bt5qwYCbqaVchXTQvH3Gwg5QZBWs1kBCge5fH9j/n4hFBpr1i2fAnPTgd # KG86Ugnw7HBi02JLsOBzppLA044x2C/jbRcTBu7kA7YUq/OPQ6dxnSHdFMoVXZJB # 2vkPgdGZdA0mxA5/G7X1oPHGdwYoFenYk+VVFvC7Cqsc21xIJ2bIo4sKHOWV2q7E # LlmgYd3a822iYemKC23sEhi991VUQAOSK2vCUcIKSK+w1G7g9BQKOhvjjz3Kr2qN # e9zYRDCCBs0wggW1oAMCAQICEAb9+QOWA63qAArrPye7uhswDQYJKoZIhvcNAQEF # BQAwZTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UE # CxMQd3d3LmRpZ2ljZXJ0LmNvbTEkMCIGA1UEAxMbRGlnaUNlcnQgQXNzdXJlZCBJ # RCBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTIxMTExMDAwMDAwMFowYjELMAkG # A1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRp # Z2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNlcnQgQXNzdXJlZCBJRCBDQS0xMIIB # IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6IItmfnKwkKVpYBzQHDSnlZU # XKnE0kEGj8kz/E1FkVyBn+0snPgWWd+etSQVwpi5tHdJ3InECtqvy15r7a2wcTHr # zzpADEZNk+yLejYIA6sMNP4YSYL+x8cxSIB8HqIPkg5QycaH6zY/2DDD/6b3+6LN # b3Mj/qxWBZDwMiEWicZwiPkFl32jx0PdAug7Pe2xQaPtP77blUjE7h6z8rwMK5nQ # xl0SQoHhg26Ccz8mSxSQrllmCsSNvtLOBq6thG9IhJtPQLnxTPKvmPv2zkBdXPao # 8S+v7Iki8msYZbHBc63X8djPHgp0XEK4aH631XcKJ1Z8D2KkPzIUYJX9BwSiCQID # AQABo4IDejCCA3YwDgYDVR0PAQH/BAQDAgGGMDsGA1UdJQQ0MDIGCCsGAQUFBwMB # BggrBgEFBQcDAgYIKwYBBQUHAwMGCCsGAQUFBwMEBggrBgEFBQcDCDCCAdIGA1Ud # IASCAckwggHFMIIBtAYKYIZIAYb9bAABBDCCAaQwOgYIKwYBBQUHAgEWLmh0dHA6 # Ly93d3cuZGlnaWNlcnQuY29tL3NzbC1jcHMtcmVwb3NpdG9yeS5odG0wggFkBggr # BgEFBQcCAjCCAVYeggFSAEEAbgB5ACAAdQBzAGUAIABvAGYAIAB0AGgAaQBzACAA # QwBlAHIAdABpAGYAaQBjAGEAdABlACAAYwBvAG4AcwB0AGkAdAB1AHQAZQBzACAA # YQBjAGMAZQBwAHQAYQBuAGMAZQAgAG8AZgAgAHQAaABlACAARABpAGcAaQBDAGUA # cgB0ACAAQwBQAC8AQwBQAFMAIABhAG4AZAAgAHQAaABlACAAUgBlAGwAeQBpAG4A # ZwAgAFAAYQByAHQAeQAgAEEAZwByAGUAZQBtAGUAbgB0ACAAdwBoAGkAYwBoACAA # bABpAG0AaQB0ACAAbABpAGEAYgBpAGwAaQB0AHkAIABhAG4AZAAgAGEAcgBlACAA # aQBuAGMAbwByAHAAbwByAGEAdABlAGQAIABoAGUAcgBlAGkAbgAgAGIAeQAgAHIA # ZQBmAGUAcgBlAG4AYwBlAC4wCwYJYIZIAYb9bAMVMBIGA1UdEwEB/wQIMAYBAf8C # AQAweQYIKwYBBQUHAQEEbTBrMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdp # Y2VydC5jb20wQwYIKwYBBQUHMAKGN2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNv # bS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcnQwgYEGA1UdHwR6MHgwOqA4oDaG # NGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RD # QS5jcmwwOqA4oDaGNGh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFz # c3VyZWRJRFJvb3RDQS5jcmwwHQYDVR0OBBYEFBUAEisTmLKZB+0e36K+Vw0rZwLN # MB8GA1UdIwQYMBaAFEXroq/0ksuCMS1Ri6enIZ3zbcgPMA0GCSqGSIb3DQEBBQUA # A4IBAQBGUD7Jtygkpzgdtlspr1LPUukxR6tWXHvVDQtBs+/sdR90OPKyXGGinJXD # UOSCuSPRujqGcq04eKx1XRcXNHJHhZRW0eu7NoR3zCSl8wQZVann4+erYs37iy2Q # wsDStZS9Xk+xBdIOPRqpFFumhjFiqKgz5Js5p8T1zh14dpQlc+Qqq8+cdkvtX8JL # FuRLcEwAiR78xXm8TBJX/l/hHrwCXaj++wc4Tw3GXZG5D2dFzdaD7eeSDY2xaYxP # +1ngIw/Sqq4AfO6cQg7PkdcntxbuD8O9fAqg7iwIVYUiuOsYGk38KiGtSTGDR5V3 # cdyxG0tLHBCcdxTBnU8vWpUIKRAmMYIEOzCCBDcCAQEwgYYwcjELMAkGA1UEBhMC # VVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0 # LmNvbTExMC8GA1UEAxMoRGlnaUNlcnQgU0hBMiBBc3N1cmVkIElEIENvZGUgU2ln # bmluZyBDQQIQBNXcH0jqydhSALrNmpsqpzAJBgUrDgMCGgUAoHgwGAYKKwYBBAGC # NwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor # BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAjBgkqhkiG9w0BCQQxFgQUIQfwBdWG # R6Z1tXOi/G/glim68aQwDQYJKoZIhvcNAQEBBQAEggEANJSXauixD72h8+vkZYtD # k2v9NRzDfkwonHmUxiptyvNELUyCsUDZtn95n0NZKSc8bZTkbnRFezODO51UEM7Z # GG7bLELwwTA5kvEn1cum+rEosPngoO8dT9kyKjEJnkrKRbtpc3oR3iiuwX/W/r2c # 9zoKXLpEnJI3Ph6IjnPWvObZvQGyhFV215dvZCOWPUUbDRoe6dsjzHprTLBJoPvl # SsXdjgPHD6wcJdDl3nWQfvlgtG+/xl5KOCugTdJbyCktLrcngKtbRufDtvFQW7Ns # Hl/VxIM1IaLF/wlEFUTB4NOSncVgoa4UCZb/TMWmMKwRQS1lbadmM6PGU7EvtJ2W # PaGCAg8wggILBgkqhkiG9w0BCQYxggH8MIIB+AIBATB2MGIxCzAJBgNVBAYTAlVT # MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j # b20xITAfBgNVBAMTGERpZ2lDZXJ0IEFzc3VyZWQgSUQgQ0EtMQIQAwGaAjr/WLFr # 1tXq5hfwZjAJBgUrDgMCGgUAoF0wGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAc # BgkqhkiG9w0BCQUxDxcNMjAxMDAzMTYyMzM5WjAjBgkqhkiG9w0BCQQxFgQUzQxx # BjCC/FcPtSmlOyIDQgSdziMwDQYJKoZIhvcNAQEBBQAEggEAPCVB2D7c5WYWo5UG # U8Ad2hDzKI53eYtDx4XaxHl+OjskKVnLVS8RGLMs/wrNUiUx5eHPRUATAo23myD0 # 1hmsUV8CS0AmdEhXvzx5z/QXF/TtNEaLTXDYy3Hr24Su92wp7D84Wq+vFHc8LtOv # Ko6XuT/BbXDQ8d7uBFDW5aw/bS5enW2TKloUkFyMJq2N1dGt0VMMp79eaRVG3Xez # FUZ+Aft9tsPdTEk7HJncbIrLgAkEG+HLgrnoxS5/txglN4R7zrCbKeANi0OuvME8 # sUU2C2TRPiT1+4IbMDR2kq2gJ2POKhUzQg6TgMW5whYiaRtNAoDf7wZX72tQiys5 # BlfXCw== # SIG # End signature block |