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
AdminBee
  • 21,637
  • 21
  • 47
  • 71
pooter03
  • 103
  • 1
  • 5
  • 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 '21 at 22:55

5 Answers5

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
Gilles Quénot
  • 31,569
  • 7
  • 64
  • 82
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"
Jim Trigg
  • 31
  • 4
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.
JdeBP
  • 66,967
  • 12
  • 159
  • 343
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.

Yurko
  • 688
  • 3
  • 4
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;