0

I need to check mail servers' IP addresses from a list of domains to see if they match a certain IP address. Specifically:

  1. Build a list of the domains I want to query
  2. Dig the MX record(s) of each domain
  3. Dig the A record(s) of the results of the MX record query for the IP address
  4. If any of the IPs match a specific IP, return a "yes" or "no"

I'm stuck at step 3.

Here's the relevant portion of my script so far

#!/bin/bash
# Bulk DNS Lookup
#
# File name/path of domain list:
domain_list='domains.txt' # One FQDN per line in file.

# File name of output text
output='ns_output.txt'

# Clears previous output
> $output

# IP address of the nameserver used for lookups:
ns_ip='192.168.250.67'
#
# Seconds to wait between lookups:
loop_wait='1' # Is set to 1 second.

for domain in `cat $domain_list` # Start looping through domains
do
    echo $domain "Mail servers" >> $output
    MX=$(dig @$ns_ip MX $domain +short) #query MX records from domain list and store it as varial $MX
    echo $MX >> $output;
    echo " " >> $output
    echo " " >> $output
    sleep $loop_wait # Pause before the next lookup to avoid flooding NS
done;

The problem is I don't know how to turn the output into a variable so that I can run another A record dig.

c****s.com Name Servers
c****s.com. 14400 IN NS ns1.a****l.com. yes

c****s.com Mail servers
10 mail.c*****s.com. 20 mail2.c****s.com.

Is there any way to query the results to return an IP address for each of the servers returned from the MX query?

Edit: I tried everyone's answer and while they all would have worked, I just found Gilles' easiest to implement. Here's my final code:

    MX=$(dig @$ns_ip MX $domain +short) #query MX records from domain list and store it as variable $MX
    arr=( $MX ) #creates array variable for the MX record answers
    for ((i=1; i<${#arr[@]}; i+=2)); #since MX records have multiple answers, for loop goes through each answer
      do
        echo ${arr[i]} >> $output; #outputs each A record from above MX dig
        dig A +short "${arr[i]}" >> $output #queries A record for IP and writes answer
        MX_IP=$(dig A +short "${arr[i]}") #sets IP address from the dig to variable MX_IP
        if [[ "${arr[i]}" == *"a****d"* ]] #if the mail server host name contains a***d
          then
            echo "yes - spam filter" >> $output
          else
          if [[ $MX_IP == $CHECK_IP ]] #if not, check to see if the mail server's IP matches ours.
            then
              echo "yes - mail server"  >> $output
            else
              echo "no" >> $output
          fi
        fi

Here's sample output (domains and IPs censored in a fit of paranoia):

a***l.com Mail servers  lastmx.a****d.net. 
85.x.x.x 
209.x.x.x
95.x.x.x yes - spamfilter
....
mail.b***c.com.
72.x.x.x yes - mail server

backup.b***c.com.
50.x.x.x no

mail2.b***c.com.
50.x.x.x no
2
  • Why not pipe the output of host to egrep -oe "(([0-9.]{1,3}){3}[0-9]{1,3})". And use a while loop for line by line reading i.e. while read line; do dig +short "$line" @8.8.8.8 | egrep -oe "(([0-9.]{1,3}){3}[0-9]{1,3})" ; done < domains.txt . Here I'm assuming the file that contains the domains is called domains.txt and the nameserver is 8.8.8.8. Your solution seems wee convoluted and a likely drain on resources.. all those calls to grep and awk while a single regexp will suffice.... – endrias Dec 16 '19 at 21:54
  • dig has a --yaml flag to change output format and hence make its parsing far easier because the default output is for human consumption and not easy to parse properly – Patrick Mevzek Jan 28 at 22:55
2

The way to go :

arr=( $MX )
for ((i=1; i<${#arr[@]}; i+=2)); do dig A +short "${arr[i]}"; done

 Output:

108.177.15.26
209.85.233.27
172.253.118.27
108.177.97.26
173.194.202.26
1
  • I ended up going with this solution and it works great. Thanks! – pooter03 Dec 17 '19 at 20:41
0

The following command will return just a list of hostnames (it trims out the weight and the trailing period):

MX_HOSTS=$(dig MX google.com +short | sed 's/.* \(.*\)\.$/\1/')

You can then do a for loop on that:

for h in ${MX_HOSTS} ; do
  MX_IPS="${MX_IPS} $(dig $h +short)"
done

And test with:

[[ "${MX_IPS}" =~ "${CHECK_IP}" ]] && echo "yes" || echo "no"
0

BIND's dig is not the only tool in existence, of course.

With Daniel J. Bernstein's djbdns toolset, this is a one-liner:

% cat domains.txt
freebsd.org.
% 
% xargs dnsmx < domains.txt | sed -E -e 's/[[:digit:]]+ //' | xargs dnsip
96.47.72.85
96.47.72.80
%

Further reading

  • Daniel J. Bernstein (1999). dnsmx. djbwares.
  • Daniel J. Bernstein (1999). dnsip. djbwares.
0

File domains.txt:

freebsd.org
redhat.com
yahoo.com
google.com

One line command to receive detailed info:

NAMESERVER="4.2.2.2"; for i in $(cat domains.txt|xargs); do IFS=$'\n'; for LINE in $(host -t mx ${i} ${NAMESERVER}|grep 'mail is handled by'); do IP=$(host -t A $(echo "${LINE}"|awk '{print $NF}') ${NAMESERVER}|grep 'has address'|awk '{print $NF}'|xargs); echo "${LINE} [${IP}]"; done; IFS=' '; done

Output, one line per MX record (a domain may have mutiple MX records, also one MX name may correspond to multiple IPs):

freebsd.org mail is handled by 10 mx1.freebsd.org. [96.47.72.80]
freebsd.org mail is handled by 30 mx66.freebsd.org. [96.47.72.85]
redhat.com mail is handled by 10 us-smtp-inbound-1.mimecast.com. [207.211.30.107 207.211.30.237 205.139.110.221 205.139.110.242 205.139.110.141 205.139.110.145 205.139.110.181 205.139.110.102 205.139.110.107 205.139.110.177 207.211.30.221 207.211.30.242 207.211.30.141 207.211.30.145 207.211.30.181 207.211.30.102]
redhat.com mail is handled by 10 us-smtp-inbound-2.mimecast.com. [205.139.110.145 207.211.30.237 205.139.110.141 207.211.30.221 207.211.30.145 205.139.110.221 207.211.30.181 205.139.110.177 205.139.110.102 207.211.30.141 207.211.30.242 205.139.110.242 205.139.110.181 205.139.110.107 207.211.30.102 207.211.30.107]
yahoo.com mail is handled by 1 mta6.am0.yahoodns.net. [67.195.204.73 67.195.228.106 67.195.228.110 67.195.228.111 67.195.204.79 98.136.96.75 67.195.204.77 67.195.204.72]
yahoo.com mail is handled by 1 mta7.am0.yahoodns.net. [98.136.96.75 67.195.228.111 67.195.204.74 67.195.228.94 67.195.204.77 98.136.96.76 67.195.228.110 67.195.228.109]
yahoo.com mail is handled by 1 mta5.am0.yahoodns.net. [98.136.96.91 67.195.204.77 67.195.228.110 98.136.96.77 98.136.96.74 67.195.204.74 67.195.204.73 67.195.204.79]
google.com mail is handled by 20 alt1.aspmx.l.google.com. [64.233.177.27]
google.com mail is handled by 30 alt2.aspmx.l.google.com. [173.194.68.26]
google.com mail is handled by 10 aspmx.l.google.com. [74.125.69.26]
google.com mail is handled by 50 alt4.aspmx.l.google.com. [64.233.186.26]
google.com mail is handled by 40 alt3.aspmx.l.google.com. [173.194.214.27]

One line command to receive only list of IPs:

NAMESERVER="4.2.2.2"; for i in $(cat domains.txt|xargs); do IFS=$'\n'; for LINE in $(host -t mx ${i} ${NAMESERVER}|grep 'mail is handled by'); do host -t A $(echo "${LINE}"|awk '{print $NF}') ${NAMESERVER}|grep 'has address'|awk '{print $NF}'; done; IFS=' '; done

Output, just collect IPs and print them one per line:

96.47.72.80
96.47.72.85
207.211.30.181
205.139.110.242
205.139.110.102
205.139.110.107
205.139.110.141
...

there is long list of ips 63 in total.

0
0

Changed it a bit to address MX record URL instead of IP, but thought I'd share in case others could benefit.

#!/usr/bin/env bash
# Bulk DNS Lookup

# File name/path of domain list:
domain_list='domains.txt' # One FQDN per line in file.

# File name of output text
output='ns_output.txt'

# Clears previous output
> $output

# IP address of the nameserver used for lookups:
ns_ip='192.168.85.54'
#
# Seconds to wait between lookups:
loop_wait='1' # Is set to 1 second.

for domain in `cat $domain_list` # Start looping through domains
  do
    MX=$(dig @$ns_ip MX $domain +short) #query MX records from domain list and store it as variable $MX
    #echo $MX >> $output;
    #echo $domain >> $output;
    arr=( $MX ) #creates array variable for the MX record answers
    echo ${arr[1]} >> $output; #outputs only one record from above MX dig
    
    : '
    for ((i=1; i<${#arr[@]}; i+=2)); #since MX records have multiple answers, for loop goes through each answer
      do
        #echo $domain >> $output;
        echo ${arr[i]} >> $output; #outputs each A record from above MX dig
        #dig A +short "${arr[i]}" >> $output #queries A record for IP and writes answer
      done
    '

  done;

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.