Getting MX, SPF, DMARC, DKIM, and SMTP Banners With PowerShell
Posted on February 17, 2021
- and tagged as
- powershell
When diagnosing email issues I tend to start with some basics: MX records, SPF, DMARC and DKIM data. There are a multitude of ways to get these, but I tend to like working in PowerShell, so I wanted to write a function to quickly pull this data. Further, I also like to ensure I can connect to the SMTP server and see the banner as this can reveal errors, such as 421 4.3.2 Service not available
.
As with other functions I’ve posted, I want PowerShell to do the cleanup work - that is, I want to be able to paste in a domain, email address, or even URL and have it work out what domain I’m interested in without me having to backspace and clean up the pasted string myself.
Getting the Domain from an Email address or URL with PowerShell
We’re going to start with a function that when given a string in the form of a domain, email address, or URL, it will return the domain - with the exception of www.
, which will be stripped.
function Get-Domain {
[CmdletBinding()]
Param (
[Parameter(Mandatory,ValueFromPipeline)]
[string]$InputString
)
try {
$Domain = ([Net.Mail.MailAddress]$InputString).Host
}
catch {
$Domain = ([System.Uri]$InputString).Host
}
if (($null -eq $Domain) -or ($Domain -eq "")) {$Domain = $InputString}
$Domain = $Domain -replace '^www\.',''
Write-Output $Domain
}
Let’s see this in action with a few examples
PS C:\> Get-Domain "test@microsoft.com"
microsoft.com
PS C:\> Get-Domain "microsoft.com"
microsoft.com
PS C:\> Get-Domain "www.microsoft.com"
microsoft.com
PS C:\> Get-Domain "https://www.microsoft.com/en-au/microsoft-365?rtc=1"
microsoft.com
When dealing with email issues we’re more likely to have an email address in the clipboard than a URL, but having this function support both allows us to use it elsewhere in the future.
Getting Email DNS Records and SMTP Banners with PowerShell
With the domain extraction out the way, let’s move to the main function. What I’m looking to achieve is a single command that will give me:
- MX Record(s)
- SPF Record
- DMARC Record
- DKIM Record, if a DKIM selector is specified
- SMTP Server Banner, if requested
function Get-MXConfig {
[CmdletBinding()]
Param (
[Parameter(Mandatory,ValueFromPipeline)]
[string]$DomainRequest,
[string]$DKIMSelector,
[switch]$GetMXBanner
)
$Domain = Get-Domain $DomainRequest
$MX = Resolve-DnsName -Type MX -Name $Domain | ? Type -eq MX | Select @{L="Host"; E={$_.NameExchange}}, Preference | Sort-Object Preference
$SPF = Resolve-DnsName -Name $Domain -Type TXT -Erroraction SilentlyContinue | ? {$_.Strings -match "v=spf1"} | Select -ExpandProperty Strings
$DMARC = Resolve-DnsName -Name "_dmarc.$Domain" -Type TXT -Erroraction SilentlyContinue | ? {$_.Strings -match "v=DMARC1"} | Select -ExpandProperty Strings
$MXDiag = [PSCustomObject]@{
Domain = $Domain
MX = $MX
SPF = $SPF
DMARC = $DMARC
}
if ($DKIMSelector) {
$DKIM = Resolve-DnsName -Name "$DKIMSelector._domainkey.$Domain" -Type TXT -Erroraction SilentlyContinue | ? Type -eq TXT
$DKIM = $DKIM | % {$_.Strings -join ""}
$MXDiag | Add-Member -MemberType NoteProperty -Name DKIM -Value $DKIM
}
if (($GetMXBanner) -and ($null -ne $MXDiag.MX)) {
$MXDiag.MX | % {
try {
$TCPClient = [System.Net.Sockets.TcpClient]::new($_.Host,25)
$Stream = $TCPClient.GetStream()
$Data = [System.Byte[]]::new(2048)
$Stream.Read($Data,0,$Data.Length) | Out-Null
$Banner = ([System.Text.Encoding]::ASCII.GetString($Data)).Trim([char]$null)
$_ | Add-Member -MemberType NoteProperty -Name Banner -Value $Banner
$Stream.Dispose()
$TCPClient.Dispose()
}
catch {
Write-warning $_.Exception.Message
}
}
}
Write-Output $MXDiag
}
We can now create a shorter alias for this.
New-Alias -Name mxconf -Value Get-MXConfig
Let’s see some examples.
PS C:\> mxconf test@microsoft.com | fl
Domain : microsoft.com
MX : @{Host=microsoft-com.mail.protection.outlook.com; Preference=10}
SPF : v=spf1 include:_spf-a.microsoft.com include:_spf-b.microsoft.com include:_spf-c.microsoft.com include:_spf-ssg-a.microsoft.com include:spf-a.hotmail.com include:_spf1-meo.microsoft.com -all
DMARC : v=DMARC1; p=reject; pct=100; rua=mailto:d@rua.agari.com; ruf=mailto:d@ruf.agari.com; fo=1
PS C:\> mxconf https://skykick.com -DKIMSelector hs1 -GetMXBanner | fl
Domain : skykick.com
MX : @{Host=skykick-com.mail.protection.outlook.com; Preference=0; Banner=220 DM6NAM12FT060.mail.protection.outlook.com Microsoft ESMTP MAIL Service ready at Fri, 19 Feb 2021 11:56:29 +0000}
SPF : v=spf1 include:spf.protection.outlook.com include:em.skykick.com include:sendgrid.net include:mailsenders.netsuite.com include:mg-spf.greenhouse.io include:2047087.spf07.hubspotemail.net -all
DMARC : v=DMARC1; p=quarantine; pct=100; rua=mailto:q3qbwnac@ag.dmarcian.com; ruf=mailto:d@skykick.com; fo=1
DKIM : k=rsa;t=s;p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3GLUlMOPxULXqVBaSqPs8lxGcSTvvuK4sXrVaSHuBITd5Vah7torpGR8lkS7iwR/uF1HIqXsf76n6wgawlJ68cuuffS/5tBKRbN/AhY2ZRx2Bv4fr+q6N0mOvG1UQmqykBIdNhNIMWOOujE4vDEfh+aJ6BPThNPZHNN5p7JgQ4swAlHT+6Tn73p6yVuLDR2guNTPQ8mgOfu0+kjhS+RaiJiSB31d6zXRFROUZlzCamc22iGCs7eyX3OuUj0Em4PLDSca/DzlCp5spzkQi+MzMq4kIh2tPk++MYJE2M+tTlqvHLMLiW6VFldAuwxEUifAg0P9I0aJvh7jxFWpr5FRvQIDAQAB
PS C:\> "cisco.com" | mxconf -GetMXBanner | Select -ExpandProperty MX | fl
Host : alln-mx-01.cisco.com
Preference : 10
Banner : 220-alln-inbound-l.cisco.com ESMTP
220 Unknown (X.X.X.X) is being throttled due to an unknown or low email reputation score. See https://talosintelligence.com/reputation_center/lookup?search=X.X.X.X for details.
Host : rcdn-mx-01.cisco.com
Preference : 20
Banner : 220-rcdn-inbound-n.cisco.com ESMTP
220 Unknown (X.X.X.X) is being throttled due to an unknown or low email reputation score. See https://talosintelligence.com/reputation_center/lookup?search=X.X.X.X for details.
Host : aer-mx-01.cisco.com
Preference : 30
Banner : 220-aer-inbound-j.cisco.com ESMTP
220 Unknown (X.X.X.X) is being throttled due to an unknown or low email reputation score. See https://talosintelligence.com/reputation_center/lookup?search=X.X.X.X for details.
Guess Cisco doesn’t like my IP 😅.
That’s it, just another couple of quick functions for the $Profile
to make things a bit more efficient.