Почему иногда происходит сбой передачи зоны DNSPython

Итак, я все еще на уровне нуба, когда дело доходит до python. Я знаю... Я знаю... возможно, есть более эффективный способ делать то, что я пытаюсь сделать, но я все еще учусь, и, надеюсь, со временем я стану лучше.

Для учебного проекта я пишу сценарий для выполнения различных операций DNS с доменом. Я нашел DNSPython, и мне показалось, что это именно то, что мне нужно было использовать, и я думал, что с этим покончено, но когда я попробовал его на другом домене, он продолжал давать сбой при передаче зоны.

У меня есть два домена, жестко закодированные прямо сейчас для тестирования. Домен megacorpone iw работал, как я и ожидал, однако теперь он терпит неудачу (без изменения кода), чтобы заставить его работать, мне пришлось отфильтровать первую возвращенную запись '@', иначе она также не удалась.

Тем не менее, домен zonetransfer.me иногда завершает скрипт с ошибкой, но также время от времени выдает ошибки, но по какой-то причине он никогда не отображает записи хоста, и я пока не смог понять, как это исправить, ломал голову против этого какое-то время.

Мегакопроновый пробег раньше каждый раз работал, сейчас вообще не работает. Единственное, о чем я могу думать до сих пор, это то, что это может быть проблема времени.

Беги с мегакопроном

Attempting zone transfers for megacorpone.com
Traceback (most recent call last):
  File "/home/kali/Exercises/Module_7/dns-axfer.py", line 56, in zoneXFR
    zone = dns.zone.from_xfr(dns.query.xfr(str(server).rstrip('.'), domain))
  File "/usr/lib/python3/dist-packages/dns/zone.py", line 1106, in from_xfr
    for r in xfr:
  File "/usr/lib/python3/dist-packages/dns/query.py", line 627, in xfr
    raise TransferError(rcode)
dns.query.TransferError: Zone transfer error: REFUSED

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/kali/Exercises/Module_7/dns-axfer.py", line 73, in <module>
    zoneXFR()
  File "/home/kali/Exercises/Module_7/dns-axfer.py", line 66, in zoneXFR
    print ("\nResults for",server, "\nZone origin:", str(zone.origin).rstrip('.'))
UnboundLocalError: local variable 'zone' referenced before assignment

Запустите 1 с zonetransfer.me

Attempting zone transfers for zonetransfer.me

Results for nsztm1.digi.ninja. 
Zone origin: zonetransfer.me
---------------------------------------------------------------------------

Results for nsztm1.digi.ninja. 
Zone origin: zonetransfer.me
---------------------------------------------------------------------------
[*]  Error: <class 'dns.resolver.NoAnswer'> The DNS response does not contain an answer to the question: _acme-challenge.zonetransfer.me. IN A

Results for nsztm2.digi.ninja. 
Zone origin: zonetransfer.me
---------------------------------------------------------------------------

Results for nsztm2.digi.ninja. 
Zone origin: zonetransfer.me
---------------------------------------------------------------------------
[*]  Error: <class 'dns.resolver.NoAnswer'> The DNS response does not contain an answer to the question: _acme-challenge.zonetransfer.me. IN A

Запустите 2 без изменения кода (zonetransfer.me)

Attempting zone transfers for zonetransfer.me
Traceback (most recent call last):
  File "/home/kali/Exercises/Module_7/dns-axfer.py", line 56, in zoneXFR
    zone = dns.zone.from_xfr(dns.query.xfr(str(server).rstrip('.'), domain))
  File "/usr/lib/python3/dist-packages/dns/zone.py", line 1106, in from_xfr
    for r in xfr:
  File "/usr/lib/python3/dist-packages/dns/query.py", line 596, in xfr
    _net_write(s, tcpmsg, expiration)
  File "/usr/lib/python3/dist-packages/dns/query.py", line 364, in _net_write
    current += sock.send(data[current:])
ConnectionRefusedError: [Errno 111] Connection refused

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/kali/Exercises/Module_7/dns-axfer.py", line 73, in <module>
    zoneXFR()
  File "/home/kali/Exercises/Module_7/dns-axfer.py", line 66, in zoneXFR
    print ("\nResults for",server, "\nZone origin:", str(zone.origin).rstrip('.'))
UnboundLocalError: local variable 'zone' referenced before assignment

Мой сценарий: отмахиваться... Я всегда могу принять конструктивную критику.

#!/usr/bin/python3

import sys, argparse
import dns.query
import dns.zone
import dns.resolver
from colorama import Fore, Style

bracket = f"{Fore.BLUE}[{Fore.GREEN}*{Fore.BLUE}]{Style.RESET_ALL} "
bracket_err = f"{Fore.BLUE}[{Fore.RED}*{Fore.BLUE}]{Style.RESET_ALL} "
'''
parser = argparse.ArgumentParser()
parser.add_argument('domain')
args = parser.parse_args()
'''
# domain = (sys.argv[1])
domain = 'megacorpone.com'
#domain = 'zonetransfer.me'

def line():
    print ('-' * 75)
    return None

def resolveDNS(system):
    resolver = dns.resolver.Resolver()
    results = resolver.query(system , "A")
    return results

def getNS ():
    name_servers = dns.resolver.query(domain, 'NS')
    print ("\nThe name servers for " + domain + " are:")
    line()
    for system in name_servers:
        A_records = resolveDNS(str(system))
        for item in A_records:
            answer = ','.join([str(item)])
        print (bracket, "{:30}".format(str(system).rstrip('.')), "{:15}".format(answer))
    return name_servers

def getMX():
    mail_server = dns.resolver.query(domain, 'MX')
    print("\nMail servers for", domain)
    line()    
    for system in mail_server:
        A_records = resolveDNS(str(system.exchange))
        for item in A_records:
            answer = ','.join([str(item)])          
        print(bracket, "{:30}".format(str(system.exchange).rstrip('.')), "{:15}".format(str(answer)), '\t', "{:5}".format("Preference:"), str(system.preference))
    return None

def zoneXFR():
    print ("\nAttempting zone transfers for", domain,)

    for server in name_servers:
        try:
            zone = dns.zone.from_xfr(dns.query.xfr(str(server).rstrip('.'), domain))
            print ("\nResults for",server, "\nZone origin:", str(zone.origin).rstrip('.'))
            line()
            for host in zone:
                if str(host) != '@':
                    A_records = resolveDNS(str(host) + "." + domain)
                    for item in A_records:
                        answer = ','.join([str(item)])   
                    print(bracket, "{:30}".format(str(host) + "." + domain), answer)
        except Exception as e:
            print ("\nResults for",server, "\nZone origin:", str(zone.origin).rstrip('.'))
            line()
            print (bracket_err, f"{Fore.RED}Error:{Style.RESET_ALL}", e.__class__, e)


name_servers = getNS()
getMX()
zoneXFR()
print("\n")

person digital_alchemy    schedule 21.03.2020    source источник
comment
В вашем коде есть несколько проблем, но одна из главных вещей, которую вы должны помнить в мире DNS, заключается в том, что для любого запроса, который вы получаете, вы можете вернуть что-то другое, чем ожидаемая запись, например, вы запрашиваете A, но вы получаете CNAME взамен. Вы должны разобраться с этим должным образом, иначе у вас будет много предположений, которые могут работать в одних случаях, но не в других.   -  person Patrick Mevzek    schedule 24.03.2020


Ответы (2)


Я вижу, что вы пытаетесь использовать хорошо известные серверы имен, специально настроенные для тестирования. Однако для пользы других читателей я добавлю пару пояснений.

Как вы, вероятно, знаете, большинство серверов имен в настоящее время не разрешают передачу зон. При этом возможно, что каждый из серверов имен для данного доменного имени будет вести себя по-разному (они могут иметь разные конфигурации и даже работать с разным программным обеспечением).

В случае с megacorpone.com перечислены 3 сервера имен:

  • ns2.megacorpone.com.
  • ns3.megacorpone.com.
  • ns1.megacorpone.com.

ns2.megacorpone.com — единственный сайт, разрешающий перенос зоны.

Это сообщение

dns.query.TransferError: Zone transfer error: REFUSED

означает, что это означает: ваш запрос был отклонен. Вероятно, вы обратились не к тому серверу имен.

Затем у вас есть еще одна ошибка, которая предполагает проблему с областью видимости переменных:

UnboundLocalError: local variable 'zone' referenced before assignment

Вы вызываете функции в следующем порядке:

name_servers = getNS()
getMX()
zoneXFR()

Если name_servers завершится ошибкой, то и последующий вызов zoneXFR тоже завершится ошибкой. Потому что этот код:

for server in name_servers:

попытается перебрать пустой список.

Периодические сбои разрешения DNS являются обычным явлением, поэтому здесь требуется несколько проверок. Как минимум убедитесь, что список NS не пуст.

Еще одна проблема: вы запускаете цикл for за пределами блока try, поэтому ваша структура управления нарушается прямо посередине:

for server in name_servers:
    try:
        zone = dns.zone.from_xfr(dns.query.xfr(str(server).rstrip('.'), domain))
        print ("\nResults for",server, "\nZone origin:", str(zone.origin).rstrip('.'))
        line()

Сделайте это вместо этого:

try:
    for server in name_servers:
        zone = dns.zone.from_xfr(dns.query.xfr(str(server).rstrip('.'), domain))
        print ("\nResults for",server, "\nZone origin:", str(zone.origin).rstrip('.'))
        ...

Я подозреваю, что ваш сценарий периодически дает сбой, потому что список серверов имен не всегда возвращается в одном и том же порядке. Если первым возвращенным NS является ns1.megacorpone.com. или ns3.megacorpone.com. то код вылетает. Если скрипты начинаются с ns2.megacorpone.com (единственный NS, разрешающий передачу зон), то, похоже, он работает нормально.

Если этот код не работает (AXFR отклонен):

zone = dns.zone.from_xfr(dns.query.xfr(str(server).rstrip('.'), domain))

тогда zone не определено, и поэтому вы не можете распечатать его в своем блоке исключений. Вместо этого покажите доменное имя или некоторые другие переменные, которые, как вы знаете, определены и действительны.

Поэтому, если AXFR отклонен, ваш скрипт должен обрабатывать это исключение dns.query.TransferError и спокойно переходить к следующему NS, если таковой имеется, пока список не будет исчерпан.

Еще один совет: попробуйте разрешить имена ресурсов, отличные от '@'. Вместо этого обратите внимание на тип записи. Вы должны разрешать только CNAME, MX или NS. Другими распространенными типами являются TXT, A, AAAA, SOA. Остальные более экзотические, такие как NAPTR, LOC или SRV. Ничего, что должно быть решено, я думаю.

person Anonymous    schedule 21.03.2020
comment
Все это имеет смысл. У меня не было времени изменить и протестировать его, но я считаю, что он должен работать. - person digital_alchemy; 22.03.2020
comment
список серверов имен не всегда возвращается в одном и том же порядке. Это по дизайну DNS. Он работает не со списками (упорядоченными), а с наборами (неупорядоченными) записей. И рекурсивные серверы имен часто возвращают записи в другом порядке каждый раз, когда вы их запрашиваете, именно для того, чтобы клиенты не придерживались одного и того же. - person Patrick Mevzek; 24.03.2020

Исправил ваш код, пока выглядит не очень, но работает

#!/usr/bin/python3
# you might want to run python3 -m pip install dnspython before running this script

import sys
import dns.query
import dns.zone
import dns.resolver

# formatting setup
from colorama import Fore, Style
bracket = f"{Fore.BLUE}[{Fore.GREEN}*{Fore.BLUE}]{Style.RESET_ALL} "
bracket_err = f"{Fore.BLUE}[{Fore.RED}*{Fore.BLUE}]{Style.RESET_ALL} "
def drawLine():
    print ('-' * 75)
    
# read arguments    
try:
    domain = (sys.argv[1])
except:
    print("[!] USAGE: python3 zt.py DOMAIN_NAME")
    sys.exit(0)

# DNS functions
def resolveDNS(name):
    resolver = dns.resolver.Resolver()
    results = resolver.query(name , "A")
    return results

def getNS (domain):
    mapping = {}
    name_servers = dns.resolver.query(domain, 'NS')
    print ("\nThe name servers for " + domain + " are:")
    drawLine()
    for name_server in name_servers:
        A_records = resolveDNS(str(name_server))
        for item in A_records:
            answer = ','.join([str(item)])
        mapping[str(name_server)] = answer
        print (bracket, "{:30}".format(str(name_server).rstrip('.')), "{:15}".format(answer))       
    return mapping
    
def zoneXFR(server):
    try:
        zone = dns.zone.from_xfr(dns.query.xfr(str(server).rstrip('.'), domain))
    except Exception as e:
        print (bracket_err, f"{Fore.RED}Error:{Style.RESET_ALL}", e.__class__, e)
    else:
        print ("\nResults for",server, "\nZone origin:", str(zone.origin).rstrip('.'))
        drawLine()
        for host in zone:
            if str(host) != '@':
                A_records = resolveDNS(str(host) + "." + domain)
                for item in A_records:
                    answer = ','.join([str(item)])   
                print(bracket, "{:30}".format(str(host) + "." + domain), answer)
        drawLine()
        
name_servers = getNS(domain)
for server in name_servers:
    print ("\nAttempting zone transfers for", server,name_servers[server])
    zoneXFR(name_servers[server])
person Swagger 68    schedule 25.08.2020