TXT-записи в dnsPython
Я реализовал простой DNS-сервер. Он просто отвечает записью TXT. Я размещаю скрипт в качестве сервера NS для example.com. Сервер NS - xyzk. Он отлично работает, когда я выдаю что-то вроде:
$ dig demo.example.com @x.y.z.k
; <<>> DiG 9.3.6-P1-RedHat-9.3.6-4.P1.el5_4.1 <<>> demo.example.com @x.y.z.k
;; global options: printcmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 10028
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 2, ADDITIONAL: 2
;; QUESTION SECTION:
;demo.example.com. IN A
;; ANSWER SECTION:
demo.example.com. 1000 IN TXT "test message"
но он не работает, когда я задаю тот же вопрос с одного из публичных серверов имен (например, DNS DNS в 4.2.2.1):
$ dig demo.example.com @4.2.2.1
(Я получаю то же самое, когда я выдаю $ dig demo.example.com @4.2.2.1 TXT
)
; <<>> DiG 9.3.6-P1-RedHat-9.3.6-4.P1.el5_4.1 <<>> demo.example.com
;; global options: printcmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 10905
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;demo.example.com. IN A
;; Query time: 5837 msec
;; SERVER: 172.16.1.1#53(172.16.1.1)
;; WHEN: Tue Jul 20 04:01:27 2010
;; MSG SIZE rcvd: 75
Ребята, вы понимаете, что не так? Интересно, что если я изменю тип ответа на что-то вроде CNAME, а не на TXT, он будет работать нормально.
def dns_respond(resp, q, data_type):
rrset = dns.rrset.from_text(q.name, 1000,
dns.rdataclass.IN, dns.rdatatype.TXT, 'test message')
resp.answer.append(rrset)
return resp
def dns_ok(resp, q, data = None, msg = ''):
return dns_respond(resp = resp, q = q, data_type = 'OK')
def dns_error(resp, q):
return dns_respond(resp = resp, q = q, data_type = 'Error')
def requestHandler(address, message):
resp = None
message_id = ord(message[0]) * 256 + ord(message[1])
logging.debug('msg id = ' + str(message_id))
if message_id in serving_ids:
# the request is already taken, drop this message
logging.debug('I am already serving this request.')
return
serving_ids.append(message_id)
msg = dns.message.from_wire(message)
op = msg.opcode()
if op == 0:
# standard and inverse query
qs = msg.question
if len(qs) > 0:
q = qs[0]
logging.debug('request is ' + str(q))
if q.rdtype == dns.rdatatype.A:
resp = std_qry(msg)
else:
# not implemented
#resp = std_qry(msg)
resp = make_response(qry=msg, RCODE=4)
else:
# not implemented
resp = make_response(qry=msg, RCODE=4)
if resp:
s.sendto(resp.to_wire(), address)
def std_qry(msg):
qs = msg.question
logging.debug(str(len(qs)) + ' questions.')
answers = []
nxdomain = False
for q in qs:
resp = make_response(qry=msg)
logging.critical('Sending...')
return dns_ok(resp, q)
def make_response(qry=None, id=None, RCODE=0):
if qry is None and id is None:
raise Exception, 'bad use of make_response'
if qry is None:
resp = dns.message.Message(id)
# QR = 1
resp.flags |= dns.flags.QR
if RCODE != 1:
raise Exception, 'bad use of make_response'
else:
resp = dns.message.make_response(qry)
#resp.flags |= dns.flags.AA
resp.flags |= dns.flags.RA
resp.set_rcode(RCODE)
return resp
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(('', 53))
logging.debug('binded to UDP port 53.')
serving_ids = []
while True:
logging.debug('waiting requests.')
message, address = s.recvfrom(1024)
logging.debug('serving a request.')
requestHandler(address, message)
1 ответ
Я вижу несколько реальных ошибок, которые остановят эту работу:
- Вы возвращаете
TXT
записать вA
Запись запроса - это, вероятно, пробка шоу. - Вы должны возвращать
AA
флаг (авторитетный ответ) вместоRA
- Ваши заголовки говорят, что есть два ответа в авторитетных и дополнительных разделах, но нет
- Вы не удаляете обслуживаемые идентификаторы из списка после того, как они действительно были обработаны
Кроме того, есть несколько проблем, связанных с протоколом, которые необходимо исправить:
- Вы должны иметь возможность вернуться
NS
записи иSOA
записи по запросу, или ваша делегация не может работать надежно - При сопоставлении запросов следует использовать целый (исходный IP-адрес, исходный порт, queryID) кортеж, а не только (queryID)
- Если имя вопроса находится в правильном домене, но не в правильном поддомене, вы должны вернуть
NXDOMAIN
(код = 3) - Если имя вопроса соответствует правильному поддомену, но неправильному типу записи, вы должны вернуть
NOERROR
(rcode = 0) вместоNOTIMPL
- Если имя вопроса вообще не относится к нужному домену, вам следует вернуться
REFUSED
(код = 5)