%PDF- %PDF-
| Direktori : /lib/python3/dist-packages/dnslib/ |
| Current File : //lib/python3/dist-packages/dnslib/digparser.py |
# -*- coding: utf-8 -*-
"""
digparser
---------
Encode/decode DNS packets from DiG textual representation. Parses
question (if present: +qr flag) & answer sections and returns list
of DNSRecord objects.
Unsupported RR types are skipped (this is different from the packet
parser which will store and encode the RDATA as a binary blob)
>>> dig = os.path.join(os.path.dirname(__file__),"test","dig","google.com-A.dig")
>>> with open(dig) as f:
... l = DigParser(f)
... for record in l:
... print('---')
... print(repr(record))
---
<DNS Header: id=0x5c9a type=QUERY opcode=QUERY flags=RD rcode='NOERROR' q=1 a=0 ns=0 ar=0>
<DNS Question: 'google.com.' qtype=A qclass=IN>
---
<DNS Header: id=0x5c9a type=RESPONSE opcode=QUERY flags=RD,RA rcode='NOERROR' q=1 a=16 ns=0 ar=0>
<DNS Question: 'google.com.' qtype=A qclass=IN>
<DNS RR: 'google.com.' rtype=A rclass=IN ttl=299 rdata='62.252.169.183'>
<DNS RR: 'google.com.' rtype=A rclass=IN ttl=299 rdata='62.252.169.152'>
<DNS RR: 'google.com.' rtype=A rclass=IN ttl=299 rdata='62.252.169.172'>
<DNS RR: 'google.com.' rtype=A rclass=IN ttl=299 rdata='62.252.169.177'>
<DNS RR: 'google.com.' rtype=A rclass=IN ttl=299 rdata='62.252.169.157'>
<DNS RR: 'google.com.' rtype=A rclass=IN ttl=299 rdata='62.252.169.153'>
<DNS RR: 'google.com.' rtype=A rclass=IN ttl=299 rdata='62.252.169.182'>
<DNS RR: 'google.com.' rtype=A rclass=IN ttl=299 rdata='62.252.169.168'>
<DNS RR: 'google.com.' rtype=A rclass=IN ttl=299 rdata='62.252.169.178'>
<DNS RR: 'google.com.' rtype=A rclass=IN ttl=299 rdata='62.252.169.162'>
<DNS RR: 'google.com.' rtype=A rclass=IN ttl=299 rdata='62.252.169.187'>
<DNS RR: 'google.com.' rtype=A rclass=IN ttl=299 rdata='62.252.169.167'>
<DNS RR: 'google.com.' rtype=A rclass=IN ttl=299 rdata='62.252.169.148'>
<DNS RR: 'google.com.' rtype=A rclass=IN ttl=299 rdata='62.252.169.173'>
<DNS RR: 'google.com.' rtype=A rclass=IN ttl=299 rdata='62.252.169.158'>
<DNS RR: 'google.com.' rtype=A rclass=IN ttl=299 rdata='62.252.169.163'>
>>> dig = os.path.join(os.path.dirname(__file__),"test","dig","google.com-ANY.dig")
>>> with open(dig) as f:
... l = DigParser(f)
... for record in l:
... print('---')
... print(repr(record))
---
<DNS Header: id=0xb294 type=QUERY opcode=QUERY flags=RD rcode='NOERROR' q=1 a=0 ns=0 ar=0>
<DNS Question: 'google.com.' qtype=ANY qclass=IN>
---
<DNS Header: id=0xb294 type=RESPONSE opcode=QUERY flags=RD,RA rcode='NOERROR' q=1 a=14 ns=0 ar=0>
<DNS Question: 'google.com.' qtype=ANY qclass=IN>
<DNS RR: 'google.com.' rtype=A rclass=IN ttl=299 rdata='216.58.212.110'>
<DNS RR: 'google.com.' rtype=AAAA rclass=IN ttl=299 rdata='2a00:1450:4009:807::200e'>
<DNS RR: 'google.com.' rtype=CAA rclass=IN ttl=86399 rdata='0 issue "symantec.com"'>
<DNS RR: 'google.com.' rtype=MX rclass=IN ttl=599 rdata='40 alt3.aspmx.l.google.com.'>
<DNS RR: 'google.com.' rtype=MX rclass=IN ttl=599 rdata='10 aspmx.l.google.com.'>
<DNS RR: 'google.com.' rtype=NS rclass=IN ttl=86399 rdata='ns2.google.com.'>
<DNS RR: 'google.com.' rtype=MX rclass=IN ttl=599 rdata='20 alt1.aspmx.l.google.com.'>
<DNS RR: 'google.com.' rtype=SOA rclass=IN ttl=59 rdata='ns2.google.com. dns-admin.google.com. 144578247 900 900 1800 60'>
<DNS RR: 'google.com.' rtype=NS rclass=IN ttl=86399 rdata='ns1.google.com.'>
<DNS RR: 'google.com.' rtype=NS rclass=IN ttl=86399 rdata='ns4.google.com.'>
<DNS RR: 'google.com.' rtype=NS rclass=IN ttl=86399 rdata='ns3.google.com.'>
<DNS RR: 'google.com.' rtype=MX rclass=IN ttl=599 rdata='50 alt4.aspmx.l.google.com.'>
<DNS RR: 'google.com.' rtype=TXT rclass=IN ttl=3599 rdata='"v=spf1 include:_spf.google.com ~all"'>
<DNS RR: 'google.com.' rtype=MX rclass=IN ttl=599 rdata='30 alt2.aspmx.l.google.com.'>
"""
from __future__ import print_function
import glob,os.path,string,re
from dnslib.lex import WordLexer
from dnslib.dns import (DNSRecord,DNSHeader,DNSQuestion,DNSError,
RR,RD,RDMAP,QR,RCODE,CLASS,QTYPE,EDNS0)
class DigParser:
"""
Parse Dig output
"""
def __init__(self,dig,debug=False):
self.debug = debug
self.l = WordLexer(dig)
self.l.commentchars = ';'
self.l.nltok = ('NL',None)
self.i = iter(self.l)
def parseHeader(self,l1,l2):
_,_,_,opcode,_,status,_,_id = l1.split()
_,flags,_ = l2.split(';')
header = DNSHeader(id=int(_id),bitmap=0)
header.opcode = getattr(QR,opcode.rstrip(','))
header.rcode = getattr(RCODE,status.rstrip(','))
for f in ('qr','aa','tc','rd','ra','ad','cd'):
if f in flags:
setattr(header,f,1)
return header
def expect(self,expect):
t,val = next(self.i)
if t != expect:
raise ValueError("Invalid Token: %s (expecting: %s)" % (t,expect))
return val
def parseQuestions(self,q,dns):
for qname,qclass,qtype in q:
dns.add_question(DNSQuestion(qname,
getattr(QTYPE,qtype),
getattr(CLASS,qclass)))
def parseAnswers(self,a,auth,ar,dns):
sect_map = {'a':'add_answer','auth':'add_auth','ar':'add_ar'}
for sect in 'a','auth','ar':
f = getattr(dns,sect_map[sect])
for rr in locals()[sect]:
rname,ttl,rclass,rtype = rr[:4]
rdata = rr[4:]
rd = RDMAP.get(rtype,RD)
try:
if rd == RD and \
any([ x not in string.hexdigits for x in rdata[-1]]):
# Only support hex encoded data for fallback RD
pass
else:
f(RR(rname=rname,
ttl=int(ttl),
rtype=getattr(QTYPE,rtype),
rclass=getattr(CLASS,rclass),
rdata=rd.fromZone(rdata)))
except DNSError as e:
if self.debug:
print("DNSError:",e,rr)
else:
# Skip records we dont understand
pass
def parseEDNS(self,edns,dns):
args = {}
m = re.search('version: (\d+),',edns)
if m:
args['version'] = int(m.group(1))
m = re.search('flags:\s*(.*?);',edns)
if m:
args['flags'] = m.group(1)
m = re.search('udp: (\d+)',edns)
if m:
args['udp_len'] = int(m.group(1))
dns.add_ar(EDNS0(**args))
def __iter__(self):
return self.parse()
def parse(self):
dns = None
section = None
paren = False
rr = []
try:
while True:
tok,val = next(self.i)
if tok == 'COMMENT':
if val.startswith('; ->>HEADER<<-'):
# Start new record
if dns:
# If we have a current record complete this
self.parseQuestions(q,dns)
self.parseAnswers(a,auth,ar,dns)
yield(dns)
dns = DNSRecord()
q,a,auth,ar = [],[],[],[]
self.expect('NL')
val2 = self.expect('COMMENT')
dns.header = self.parseHeader(val,val2)
elif val.startswith('; QUESTION'):
section = q
elif val.startswith('; ANSWER'):
section = a
elif val.startswith('; AUTHORITY'):
section = auth
elif val.startswith('; ADDITIONAL'):
section = ar
elif val.startswith('; OPT'):
# Only partial support for parsing EDNS records
self.expect('NL')
val2 = self.expect('COMMENT')
self.parseEDNS(val2,dns)
elif val.startswith(';') or tok[1].startswith('<<>>'):
pass
elif dns and section == q:
q.append(val.split())
elif tok == 'ATOM':
if val == '(':
paren = True
elif val == ')':
paren = False
else:
rr.append(val)
elif tok == 'NL' and not paren and rr:
if self.debug:
print(">>",rr)
section.append(rr)
rr = []
except StopIteration:
if rr:
self.section.append(rr)
if dns:
self.parseQuestions(q,dns)
self.parseAnswers(a,auth,ar,dns)
yield(dns)
if __name__ == '__main__':
import argparse,doctest,sys
p = argparse.ArgumentParser(description="DigParser Test")
p.add_argument("--dig",action='store_true',default=False,
help="Parse DiG output (stdin)")
p.add_argument("--debug",action='store_true',default=False,
help="Debug output")
args = p.parse_args()
if args.dig:
l = DigParser(sys.stdin,args.debug)
for record in l:
print(repr(record))
else:
sys.exit(0 if doctest.testmod().failed == 0 else 1)