Jump to content

MX records for a specific domain


trancexx
 Share

Recommended Posts

Being new to AutoIt, I feel rush with every new script I make, do you?

Retrieving MX records for a specific domain is something that you will probably never need to do. Or would you?

In case you would...

There is few tools on the net that will do it for you, or you can use 'nslookup.exe' that ships with your windows. There is few scripts here on forum that is automating nslookup (chiefly unsuccessful scripts).

But that is not what I was thinking.

AutoIt is very powerful scripting language, and almost ideal for this kind of tasks.

MX records are retrieved after doing so called MX look up (this is smart, right?), and that is done by sending query, request to some DNS server. This communication is done on port 53, binary protocol is used by sending and receiving UDP packages.

It is very hard to find some documentation on this (written for average human being to understand). So, this script (what scrip?) is written mostly relying on RFC1034 and RFC1035.

It will not check for online status so if you run it off line it will just take 10-15 sec of your precious time.

Domain name should be in form of ...domain name lol (example.com).

It is ease to 'upgrade' it to check for online status, or to query MX records for IP-adress, or to query for example SOA records or ANAME or...

Main function is made of three (sub)functions, to keep thingh as transparent as possible. Each of them can be replaced with better ones. First one will connect to the DNS server, ask the question and provide us with answer, second one will extract interesting parts of that answer and last one will sort things up.

Commented only the interesting parts.

Script will not use any external program or whatever to get us MX records!

Question was what script. Well, this one:

Opt("MustDeclareVars", 1)


Dim $domain = "autoit.com" ; change it to domain of your interest

Dim $mx = MXRecords($domain)

If IsArray($mx) Then
    Local $au
    For $j = 1 To $mx[0][0]
        $au &= $mx[0][$j] & " - " & $mx[1][$j] & @CRLF
    Next

    MsgBox(0, "MX records for " & $domain, $au)
Else
    MsgBox(0, "MX records for " & $domain, "No Records")
EndIf



Func MXRecords($domain)
    
    Local $binary_data = MXQueryServer($domain)
    If $binary_data = -1 Then Return -1
    
    Local $output = ExtractMXServerData($binary_data)
    SortArray($output)
    
    Return $output
    
EndFunc   ;==>MXRecords


Func MXQueryServer($domain)

    Local $domain_array
    $domain_array = StringSplit($domain, ".", 1)
    
    Local $binarydom
    For $el = 1 To $domain_array[0]
        $binarydom &= Hex(BinaryLen($domain_array[$el]), 2) & StringTrimLeft(StringToBinary($domain_array[$el]), 2)
    Next
    $binarydom &= "00" ; for example, 'gmail.com' will be '05676D61696C03636F6D00' and 'autoit.com' will be '066175746F697403636F6D00'

    Local $identifier = Hex(Random(0, 1000, 1), 2) ; random hex number serving as a handle for the data that will be received
    Local $server_bin = "0x00" & $identifier & "01000001000000000000" & $binarydom & "000F0001" ; this is our query
    Local $num_time, $data

    For $num_time = 1 To 10
        Local $query_server ; ten(10) DNS servers, we'll start with one that is our's default, if no response or local one switch to public free servers
        Switch $num_time
            Case 1
                Local $loc_serv = StringSplit(RegRead("HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters", "DhcpNameServer"), " ", 1)
                If $loc_serv[0] > 0 Then
                    If StringLeft($loc_serv[1], 3) <> "192" Then ; this kind of server is not what we want
                        $query_server = $loc_serv[1]
                    EndIf
                EndIf
            Case 2
                $query_server = "4.2.2.1"
            Case 3
                $query_server = "67.138.54.100"
            Case 4
                $query_server = "208.67.222.222"
            Case 5
                $query_server = "4.2.2.2"
            Case 6
                $query_server = "4.2.2.3"
            Case 7
                $query_server = "208.67.220.220"
            Case 8
                $query_server = "4.2.2.4"
            Case 9
                $query_server = "4.2.2.5"
            Case 10
                $query_server = "4.2.2.6"
            Case 11
                Return -1
        EndSwitch
        
        If $query_server <> "" Then
            UDPStartup()

            Local $sock
            $sock = UDPOpen($query_server, 53)
            If $sock = -1 Then ; ok, that happens
                UDPCloseSocket($sock)
                UDPShutdown()
                ContinueLoop ; change server and try again
            EndIf
            
            UDPSend($sock, $server_bin) ; sending query

            Local $tik = 0
            Do
                $data = UDPRecv($sock, 512)
                $tik += 1
                Sleep(100)
            Until $data <> "" Or $tik = 8 ; waiting reasonable time for the response

            UDPShutdown() ; stopping service
            
            If $data <> "" And StringRight(BinaryMid($data, 2, 1), 2) = $identifier Then
                Return $data ; if there is data for us, return
            EndIf
        EndIf
    Next
    
    Return -1
    
EndFunc   ;==>MXQueryServer


Func ExtractMXServerData($binary_data)

    Local $num_answ = Dec(StringMid($binary_data, 15, 4)) ; representing number of answers provided by the server
    
    Local $arr = StringSplit($binary_data, "C00C000F0001", 1) ; splitting input; "C00C000F0001" - translated to human: "this is the answer for your MX query"

    If $num_answ <> $arr[0] - 1 Or $num_answ = 0 Then Return -1 ; dealing with possible options
    
    Local $pref[$arr[0]] ; preference number(s)
    Local $server[$arr[0]] ; server name(s)
    Local $output[2][$arr[0]] ; this goes out containing both server names and coresponding preference numbers
    $output[0][0] = $arr[0] - 1

    Local $offset = 10 ; initial offset

    For $i = 2 To $arr[0]
        
        $arr[$i] = "0x" & $arr[$i] ; well, it is binary data
        $pref[$i - 1] = Dec(StringRight(BinaryMid($arr[$i], 7, 2), 4))
        
        $offset += BinaryLen($arr[$i - 1]) + 6 ; adding lenght of every past part plus lenght of that "C00C000F0001" used for splitting
        
        Local $array = ReadBinary($binary_data, $offset) ; extraction of server names starts here

        While $array[1] = 192 ; dealing with special case
            $array = ReadBinary($binary_data, $array[6] + 2)
        WEnd

        $server[$i - 1] &= $array[2] & "."

        While $array[3] <> 0 ; the end will obviously be at $array[3] = 0

            If $array[3] = 192 Then
                
                $array = ReadBinary($array[0], $array[4] + 2)

                If $array[3] = 0 Then
                    $server[$i - 1] &= $array[2]
                    ExitLoop
                Else
                    $server[$i - 1] &= $array[2] & "."
                EndIf
                
            Else
                
                $array = ReadBinary($array[0], $array[5])
                
                If $array[3] = 0 Then
                    $server[$i - 1] &= $array[2]
                    ExitLoop
                Else
                    $server[$i - 1] &= $array[2] & "."
                EndIf
                
            EndIf

        WEnd

        $output[0][$i - 1] = $server[$i - 1] ; assigning
        $output[1][$i - 1] = $pref[$i - 1] ;   part

    Next

    Return $output ; two-dimensional array

EndFunc   ;==>ExtractMXServerData


Func ReadBinary($binary_data, $offset)
    
    Local $len = Dec(StringRight(BinaryMid($binary_data, $offset - 1, 1), 2))
    
    Local $data_bin = BinaryMid($binary_data, $offset, $len)
    
    Local $checker = Dec(StringRight(BinaryMid($data_bin, 1, 1), 2))
    
    Local $data = BinaryToString($data_bin)
    
    Local $triger = Dec(StringRight(BinaryMid($binary_data, $offset + $len, 1), 2))
    
    Local $new_offset = Dec(StringRight(BinaryMid($binary_data, $offset + $len + 1, 1), 2))
    
    Local $another_offset = $offset + $len + 1
    
    Local $array[7] = [$binary_data, $len, $data, $triger, $new_offset, $another_offset, $checker] ; bit of this and bit of that
    
    Return $array
    
EndFunc   ;==>ReadBinary


Func SortArray(ByRef $array) ; this will sort our $array by preferance numbers (in case of identical preferance, lower in order goes first - that's cool)
    
    If IsArray($array) Then
        ReDim $array[2][2 * $array[0][0] + 1]

        For $ij = $array[0][0] + 1 To 2 * $array[0][0]
            $array[1][$ij] = 1
        Next

        For $bf = 1 To $array[0][0]
            For $cf = 1 To $array[0][0]
                If $array[1][$cf] - $array[1][$bf] > 0 Then $array[1][$cf + $array[0][0]] += 1
            Next
        Next

        For $df = $array[0][0] + 1 To 2 * $array[0][0]
            For $ef = $array[0][0] + 1 To 2 * $array[0][0]
                If $ef = $df Then ContinueLoop
                If $array[1][$ef] - $array[1][$df] = 0 Then $array[1][$ef] += 1
            Next
        Next

        Dim $aux_array[2][$array[0][0] + 1]

        $aux_array[0][0] = $array[0][0]

        For $ff = 1 To $array[0][0]
            $aux_array[1][$array[1][$ff + $array[0][0]]] = $array[1][$ff]
            $aux_array[0][$array[1][$ff + $array[0][0]]] = $array[0][$ff]
        Next

        $array = $aux_array
        
        Return $aux_array
    EndIf

    Return -1
    
EndFunc   ;==>SortArray

Please comment if you see any flaws.

♡♡♡

.

eMyvnE

Link to comment
Share on other sites

  • 9 years later...

Cool code! Thx!!!

 

It' fine for all I need, but one question remains: Why do you "order" the found MX records in collumns, not rows? Just habit?

At a first glance I've expected to be Col[0] = MX and Col[1] = Prio, e.g.

 

Propably there is any benefit the way you've done it, and I miss it?

 

Regards, Rudi.

Earth is flat, pigs can fly, and Nuclear Power is SAFE!

Link to comment
Share on other sites

  • 6 years later...
On 5/25/2018 at 1:40 PM, rudi said:

It' fine for all I need, but one question remains: Why do you "order" the found MX records in collumns, not rows? Just habit?

At a first glance I've expected to be Col[0] = MX and Col[1] = Prio, e.g.

I agree. It was probably also the reason a whole manual sort function was used instead of _ArraySort.
I've revised this and other improvements into a central DNS function.

Edited by LWC
Rephrased
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...