Skip to content

Instantly share code, notes, and snippets.

@JohnLBevan
Last active February 10, 2023 12:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JohnLBevan/b2e55d348462a49ef52bc195ebaca3ea to your computer and use it in GitHub Desktop.
Save JohnLBevan/b2e55d348462a49ef52bc195ebaca3ea to your computer and use it in GitHub Desktop.
Script to run whois queries to fetch the registrar for a given domain. Note: TCP code stolen from my SMTPS script (https://gist.github.com/JohnLBevan/c7974c2839e1486345d63ab6bd76523c) - hence some redundant code (not yet cleaned up)
function Receive-TcpServerResponse {
[CmdletBinding()]
Param (
[Parameter(Mandatory = $true)]
[System.IO.StreamReader]$StreamReader
)
# useful notes on SMTP https://www.rfc-editor.org/rfc/rfc5321.html / http://www.tcpipguide.com/free/t_SMTPRepliesandReplyCodes-3.htm / FTP on https://www.w3.org/Protocols/rfc959/4_FileTransfer.html
$return = [PSCustomObject]@{PSTypeName='TcpResponse';Code=$null;Text=''} # don't need code, but no harm in leaving it (it was here from: )
$hasMoreData = $true
#Write-Verbose ': Awaiting Server Response'
while ($hasMoreData -and ($StreamReader.EndOfStream -ne $true)) { # had odd issues where EndOfStream was null, despite being a non-nullable bool :/ - hence `-ne $true`
$response = $StreamReader.ReadLine();
if ($null -eq $response){break;} # I don't think we'd get this... but doing defensive coding
Write-Verbose "<-[$response]"
if ($response -match '^(?<Code>\d{3})(?<Cont>[-\s])(?<Text>.*)$') {
if ($null -ne $return.Code) {$return}
#$return.Code = $Matches['Code'] # code from SMTPS monitor is not relevant here
$return.Text = $Matches['Text']
$hasMoreData = $Matches['Cont'] -eq '-'
} else {
$return.Text += "`r`n$response" # maybe we should trim the space from the start... couldn't find documentation (it just says where the line begins with numbers use neutral text like space(s) to show it's not a command)... :/
}
}
#Write-Verbose ": End of Server Response (end of stream = [$($StreamReader.EndOfStream)])" # There's always a delay when reading EndOfStream, and it often turns out to be null :S
$return
}
function Send-TcpClientMessage {
[CmdletBinding()]
Param (
[Parameter(Mandatory = $true)]
[System.IO.StreamWriter]$StreamWriter
,
[Parameter(Mandatory = $true)]
[AllowEmptyString()]
[string]$Message
)
Write-Verbose "->[$Message]"
$StreamWriter.WriteLine($Message)
}
# This is used for TCP scenarios where we setup a client, send a single message, get a single response, then can't reuse the client, like a burner phone
function Send-TcpBurnerMessage {
[CmdletBinding()]
Param (
[Parameter(Mandatory = $true)]
[string]$ComputerName
,
[Parameter(Mandatory = $true)]
[int]$Port
,
[Parameter(Mandatory = $true)]
[AllowEmptyString()]
[string]$Message
,
[Parameter()]
[int]$TimeoutMS = 10000
)
try {
$client = [System.Net.Sockets.TcpClient]::new($ComputerName, $Port)
$clientStream = $client.GetStream()
$clientStream.ReadTimeout = $TimeoutMS
$clientStream.WriteTimeout = $TimeoutMS
$r = [System.IO.StreamReader]::new($clientStream)
$w = [System.IO.StreamWriter]::new($clientStream)
$w.AutoFlush = $true
Send-TcpClientMessage -StreamWriter $w -Message $Message
Receive-TcpServerResponse -StreamReader $r
} finally {
if ($w.Disposee){ $w.Dispose() }
if ($r.Dispose){ $r.Dispose() }
if ($clientStream.Dispose){ $clientStream.Dispose() }
if ($client.Dispose){ $client.Dispose() }
}
}
function Get-TldFromDomain {
[CmdletBinding()]
Param (
[Parameter(Mandatory)]
[string]$Domain
)
# regex heavily borrowed from https://stackoverflow.com/a/26987741/361842
# note: here I assume the domain is valid / don't extract the domain from a longer fqdn, as that's not relevant to my use case & just adds overhead
$Domain -replace '^(?:(?!-))(?:xn--)?[a-z0-9][a-z0-9-_]{0,61}[a-z0-9]{0,1}\.((?:xn--)?(?:[a-z0-9\-]{1,61}|[a-z0-9-]{1,30}\.[a-z]{2,}))$', '$1'
}
function Get-DomainRegistrar {
[CmdletBinding()]
Param (
[Parameter(Mandatory, ValueFromPipeline)]
[string]$Domain
,
[Parameter()]
[string]$WhoisServer = 'whois.iana.org'
,
[Parameter()]
[int]$WhoIsPort = 43
)
Process {
$tld = Get-TldFromDomain -Domain $Domain
$response = Send-TcpBurnerMessage -ComputerName $WhoIsServer -Port $WhoIsPort -Message $tld
$tldRegistrar = $response.Text -replace '(?ms).*\n[wW][hH][oO][iI][sS]:\s*(\S+).*', '$1'
write-verbose "TLD [$tld] registrar is [$tldRegisrar]. Now fetching [$Domain]"
$response = Send-TcpBurnerMessage -ComputerName $tldRegistrar -Port $WhoIsPort -Message $Domain
$registrar = (($response.Text + "`n.") -replace '(?ms).*^([^\S\r\n]*)[rR][eE][gG][iI][sS][tT][rR][aA][rR]((?:\:[^\S\r\n]*[^\r\n]+)|(?:\:?[\r\n]+\1[^\S\r\n]+[^\r\n]+)+).*', '$2') -replace '(?ms)^[:\s]+', '' # this solution is really hacky, but as the formatting of these records is pretty inconsistent it's the best I could come up with
([PSCustomObject][ordered]@{
Domain = $Domain
Registrar = ($registrar.ToCharArray() | Where-Object{(-not [Char]::IsControl($_)) -or ($_ -in @("`t","`r","`n"))}) -join '' # found NUL chars in data
TLD = $tld # useful for debugging
TldRegisrar = $tldRegistrar # useful for debugging
WhoIs = $response # useful for debugging (leave any \0 chars in in case they give clues to the issue)
})
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment