Code Monkey home page Code Monkey logo

dnslib's People

Contributors

brianmaissy avatar dsundstrom avatar felixonmars avatar hkpatterson avatar jkl-caliber avatar klnikita avatar mgorny avatar niallm90 avatar nikize avatar nolanl avatar norox avatar paulc avatar prologic avatar rmbolger avatar robinlandstrom avatar samuelcolvin avatar spencerisgiddy avatar tugzrida avatar valentinesd avatar wxsbsd avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

dnslib's Issues

exceptions leave socks open

Exceptions thrown from the socket class in dns.py leave the socket open. Eventually your process will have too many open file descriptors:

OSError: [Errno 24] Too many open files

Rename README to README.md?

Is there any reason not to rename README to README.md so it would get rendered nicely by the Github page renderer?

threading.Thread.isAlive has been depreacated and removed in Python 3.9

The dnslib/server.py implements isAlive but checks for self.thread.isAlive which should be self.thread.is_alive. The isAlive method on DNSserver class can remain for backwards compatibility reasons.

dnslib/shellresolver.py
114:    while udp_server.isAlive():

dnslib/server.py
358:    def isAlive(self):
359:        return self.thread.isAlive()

dnslib/fixedresolver.py
96:    while udp_server.isAlive():

dnslib/zoneresolver.py
116:    while udp_server.isAlive():

dnslib/proxy.py
156:    while udp_server.isAlive():

dnslib/intercept.py
161:    while udp_server.isAlive():

Distribute wheels

Hi, thanks for dnslib, it's great.

It would be great if dnslib could upload wheels to pypi to speedup install and also make the library available on more platforms.

Is this something you'd be interested in supporting?

DNSLabels are not hashed case-insensitively

I'm not sure if there's a reason behind this, but the __eq__ method of DNSLabel allows for case-insensitive comparison, whereas __hash__, matchSuffix and stripSuffix are case sensitive.

My use case is storing some records in a dict keyed by name and to get it to work properly now requires manually converting everything to lowercase before searching.

0.9.21 Raises TypeError instead of DNSError when failing to parse HTTPS records

Hi Paul and thanks for a great library!

It seems like the change in dns.py ce07017 breaks the error reporting on parse errors on HTTPS records. hexlify return bytes and hex() that was used earlier returns a string.

Here is some code to reproduce, possibly there are some issues with the HTTPS parsing in general since this packet is parsed okay in Wireshark.

Anything I can do to help? Not that familiar with the HTTPS record type thou..

Python 3.10.6 (main, Aug  3 2022, 17:39:45) [GCC 12.1.1 20220730] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import dnslib
>>> import binascii
>>> dnslib.DNSRecord.parse(binascii.unhexlify("93088410000100020000000107646973636f726403636f6d0000410001c00c004100010000012c002b0001000001000c0268330568332d323902683200040014a29f80e9a29f87e8a29f88e8a29f89e8a29f8ae8c00c002e00010000012c005f00410d020000012c632834e5632575c586c907646973636f726403636f6d0044d488ce4a5b9085289c671f0296b2b06cffaca28880c57643befd43d6de433d84ae078b282fc2cdd744f3bea2f201042a7a0d6f3e17ebd887b082bbe30dfda100002904d0000080000000"))
Traceback (most recent call last):
  File "/home/lanrob1702/.cache/pypoetry/virtualenvs/nmsg-processor-l4-TU0Er-py3.10/lib/python3.10/site-packages/dnslib/dns.py", line 1837, in parse
    v = bytearray(buffer.get(n))
  File "/home/lanrob1702/.cache/pypoetry/virtualenvs/nmsg-processor-l4-TU0Er-py3.10/lib/python3.10/site-packages/dnslib/buffer.py", line 63, in get
    raise BufferError("Not enough bytes [offset=%d,remaining=%d,requested=%d]" %
dnslib.buffer.BufferError: Not enough bytes [offset=138,remaining=64,requested=40039]

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/lanrob1702/.cache/pypoetry/virtualenvs/nmsg-processor-l4-TU0Er-py3.10/lib/python3.10/site-packages/dnslib/dns.py", line 109, in parse
    rr.append(RR.parse(buffer))
  File "/home/lanrob1702/.cache/pypoetry/virtualenvs/nmsg-processor-l4-TU0Er-py3.10/lib/python3.10/site-packages/dnslib/dns.py", line 814, in parse
    rdata = RDMAP.get(QTYPE.get(rtype),RD).parse(
  File "/home/lanrob1702/.cache/pypoetry/virtualenvs/nmsg-processor-l4-TU0Er-py3.10/lib/python3.10/site-packages/dnslib/dns.py", line 1841, in parse
    raise DNSError("Error unpacking HTTPS: " + str(e) + binascii.hexlify(buffer.data[buffer.offset:]))
TypeError: can only concatenate str (not "bytes") to str

image

Add Type Hints

It would be good if dnslib added and exposed typing information so that calling libraries can check they are passing the correct types.

To support inline type hints would require dropping support for python<3.5, however given the security support schedule it may be desirable to drop support up to 3.7. Checking PyPI Stats, versions <=3.6 make up less than 1% of daily downloads.

I'd be willing to help out with this.

Adding DKIM signature raises TXT record too long

If I want to add a DKIM signature as a RR for a domain, it raises the following stack trace. In BIND/ NSD, one can separate the TXT record in quotes to accommodate this case. But what's the workaround for this in dnslib?

Traceback (most recent call last):
  File "ddns.py", line 56, in <module>
    D._domainkey.mail: [TXT(DKIM)],
  File "python3.8/site-packages/dnslib/dns.py", line 1114, in __init__
    raise DNSError("TXT record too long: %s" % self.data)

SyntaxWarning with Python 3.12

You can see from the following test log that there are now syntax warning with Python 3.12:

=== digparser.py
Testing: Python 3.12.1
/<<PKGBUILDDIR>>/dnslib/digparser.py:144: SyntaxWarning: invalid escape sequence '\d'
  m = re.search('version: (\d+),',edns)
/<<PKGBUILDDIR>>/dnslib/digparser.py:147: SyntaxWarning: invalid escape sequence '\s'
  m = re.search('flags:\s*(.*?);',edns)
/<<PKGBUILDDIR>>/dnslib/digparser.py:150: SyntaxWarning: invalid escape sequence '\d'
  m = re.search('udp: (\d+)',edns)
Testing: Python 3.11.7
=== ranges.py
Testing: Python 3.12.1
Testing: Python 3.11.7
=== test_decode.py
Testing: Python 3.12.1
/<<PKGBUILDDIR>>/dnslib/digparser.py:144: SyntaxWarning: invalid escape sequence '\d'
  m = re.search('version: (\d+),',edns)
/<<PKGBUILDDIR>>/dnslib/digparser.py:147: SyntaxWarning: invalid escape sequence '\s'
  m = re.search('flags:\s*(.*?);',edns)
/<<PKGBUILDDIR>>/dnslib/digparser.py:150: SyntaxWarning: invalid escape sequence '\d'
  m = re.search('udp: (\d+)',edns)
..................................................................
----------------------------------------------------------------------
Ran 66 tests in 0.134s

Looking at what's new for 3.12, in the 'Other Language Changes' section, this seems to be relevant:

A backslash-character pair that is not a valid escape sequence now generates a [SyntaxWarning](https://docs.python.org/dev/library/exceptions.html#SyntaxWarning), instead of [DeprecationWarning](https://docs.python.org/dev/library/exceptions.html#DeprecationWarning). For example, re.compile("\d+\.\d+") now emits a [SyntaxWarning](https://docs.python.org/dev/library/exceptions.html#SyntaxWarning) ("\d" is an invalid escape sequence, use raw strings for regular expression: re.compile(r"\d+\.\d+")). In a future Python version, [SyntaxError](https://docs.python.org/dev/library/exceptions.html#SyntaxError) will eventually be raised, instead of [SyntaxWarning](https://docs.python.org/dev/library/exceptions.html#SyntaxWarning). (Contributed by Victor Stinner in [gh-98401](https://github.com/python/cpython/issues/98401).)

DNAME Record

Hello,

Can you provide an example of how to send a DNAME Record? I have seen that DNAME is listed in QTYPE, but there's no RD sub-class for DNAME.

Thank you!

Nice2Have: Define a function for output inside of DNSLogger instead of print, would make it easy to send to custom loggers

Not an issue as such, but

Needed to attach a custom logger to DNSLogger, format was fine, but needed to overload every method as print was used...
It would have been so much better if there was a log_print or _print or _output method in the DNSLogger class instead.
Then using a customer logger w/o format change would be much simpler.
If you could implement this would be great. :) Great code, btw!

dnslib/dnslib/server.py

Lines 178 to 302 in 729a655

class DNSLogger:
"""
The class provides a default set of logging functions for the various
stages of the request handled by a DNSServer instance which are
enabled/disabled by flags in the 'log' class variable.
To customise logging create an object which implements the DNSLogger
interface and pass instance to DNSServer.
The methods which the logger instance must implement are:
log_recv - Raw packet received
log_send - Raw packet sent
log_request - DNS Request
log_reply - DNS Response
log_truncated - Truncated
log_error - Decoding error
log_data - Dump full request/response
"""
def __init__(self,log="",prefix=True):
"""
Selectively enable log hooks depending on log argument
(comma separated list of hooks to enable/disable)
- If empty enable default log hooks
- If entry starts with '+' (eg. +send,+recv) enable hook
- If entry starts with '-' (eg. -data) disable hook
- If entry doesn't start with +/- replace defaults
Prefix argument enables/disables log prefix
"""
default = ["request","reply","truncated","error"]
log = log.split(",") if log else []
enabled = set([ s for s in log if s[0] not in '+-'] or default)
[ enabled.add(l[1:]) for l in log if l.startswith('+') ]
[ enabled.discard(l[1:]) for l in log if l.startswith('-') ]
for l in ['log_recv','log_send','log_request','log_reply',
'log_truncated','log_error','log_data']:
if l[4:] not in enabled:
setattr(self,l,self.log_pass)
self.prefix = prefix
def log_pass(self,*args):
pass
def log_prefix(self,handler):
if self.prefix:
return "%s [%s:%s] " % (time.strftime("%Y-%m-%d %X"),
handler.__class__.__name__,
handler.server.resolver.__class__.__name__)
else:
return ""
def log_recv(self,handler,data):
print("%sReceived: [%s:%d] (%s) <%d> : %s" % (
self.log_prefix(handler),
handler.client_address[0],
handler.client_address[1],
handler.protocol,
len(data),
binascii.hexlify(data)))
def log_send(self,handler,data):
print("%sSent: [%s:%d] (%s) <%d> : %s" % (
self.log_prefix(handler),
handler.client_address[0],
handler.client_address[1],
handler.protocol,
len(data),
binascii.hexlify(data)))
def log_request(self,handler,request):
print("%sRequest: [%s:%d] (%s) / '%s' (%s)" % (
self.log_prefix(handler),
handler.client_address[0],
handler.client_address[1],
handler.protocol,
request.q.qname,
QTYPE[request.q.qtype]))
self.log_data(request)
def log_reply(self,handler,reply):
if reply.header.rcode == RCODE.NOERROR:
print("%sReply: [%s:%d] (%s) / '%s' (%s) / RRs: %s" % (
self.log_prefix(handler),
handler.client_address[0],
handler.client_address[1],
handler.protocol,
reply.q.qname,
QTYPE[reply.q.qtype],
",".join([QTYPE[a.rtype] for a in reply.rr])))
else:
print("%sReply: [%s:%d] (%s) / '%s' (%s) / %s" % (
self.log_prefix(handler),
handler.client_address[0],
handler.client_address[1],
handler.protocol,
reply.q.qname,
QTYPE[reply.q.qtype],
RCODE[reply.header.rcode]))
self.log_data(reply)
def log_truncated(self,handler,reply):
print("%sTruncated Reply: [%s:%d] (%s) / '%s' (%s) / RRs: %s" % (
self.log_prefix(handler),
handler.client_address[0],
handler.client_address[1],
handler.protocol,
reply.q.qname,
QTYPE[reply.q.qtype],
",".join([QTYPE[a.rtype] for a in reply.rr])))
self.log_data(reply)
def log_error(self,handler,e):
print("%sInvalid Request: [%s:%d] (%s) :: %s" % (
self.log_prefix(handler),
handler.client_address[0],
handler.client_address[1],
handler.protocol,
e))
def log_data(self,dnsobj):
print("\n",dnsobj.toZone(" "),"\n",sep="")

DNSLabels matchSuffix are not case-insensitive (From #22)

In #22 case-insensitive hashing was mentioned and resolved.

matchSuffix should (at least most of the time) be case insensitive https://datatracker.ietf.org/doc/html/rfc4343

>>> from dnslib.dns import DNSLabel
>>> l = DNSLabel("test.example.Com")
>>> l
<DNSLabel: 'test.example.Com.'>
>>> l.matchSuffix("example.Com")
True
>>> l.matchSuffix("example.com")
False
>>>

The last command is False, expected True

It is seen in the wild that requests are coming in especially as .ip6.arpa. and .IP6.ARPA.

Originally posted by @NiKiZe in #22 (comment)

Possible Fix:

@@ -113,14 +115,14 @@ class DNSLabel(object):
             Return True if label suffix matches
         """
         suffix = DNSLabel(suffix)
-        return self.label[-len(suffix.label):] == suffix.label
+        return DNSLabel(self.label[-len(suffix.label):]) == suffix

Request: add NULL to QTYPE

Would you mind adding the null record (experimental)?

in dnslib.QTYPE, add 10:'NULL'

I need it in one of my Python tools. I added it on my machine, but it would be easier for maintenance if it was in the master code.

Thanks,

Didier

DNSRecord parse failing, when DNS over TCP is used.

When dns payload is sent over TCP (possibly DNS over HTTPS or it could be DNS over TCP when response is large), the data payload extracted from TCP protocol and passed to DNSRecord.parse() throws error related to parsing of data where data is not available when offset is set.

DNSLogger: Add option to not print logs

Hi,

Thank you for this great project!

When running an instance of DNSServer, the output is quite verbose. I've looked through the documentation and did not find a way to mute the logs completely with one single option.

It would be nice to have this option.

Regards

Support for SRV Records for High Service Availability (multiple entries of same service with different priorities)

From https://en.wikipedia.org/wiki/SRV_record

Provisioning for high service availability

The priority field determines the precedence of use of the record's data. Clients should use the SRV records with the lowest-numbered priority value first, and fall back to records of higher value if the connection fails. If a service has multiple SRV records with the same priority value, clients should load balance them in proportion to the values of their weight fields. In the following example, both the priority and weight fields are used to provide a combination of load balancing and backup service.

# _service._proto.name.  TTL   class SRV priority weight port target.
_sip._tcp.example.com.   86400 IN    SRV 10       60     5060 bigbox.example.com.
_sip._tcp.example.com.   86400 IN    SRV 10       20     5060 smallbox1.example.com.
_sip._tcp.example.com.   86400 IN    SRV 10       20     5060 smallbox2.example.com.
_sip._tcp.example.com.   86400 IN    SRV 20       0      5060 backupbox.example.com.

Is this supported in dnslib?

Exception not treated in server

I'm getting exceptions like this:

Exception happened during processing of request from ('xxxxxxxxxxx', 48736) 
Traceback (most recent call last): 
  File "/usr/lib/python3.6/socketserver.py", line 654, in process_request_thread 
    self.finish_request(request, client_address) 
  File "/usr/lib/python3.6/socketserver.py", line 364, in finish_request 
    self.RequestHandlerClass(request, client_address, self) 
  File "/usr/lib/python3.6/socketserver.py", line 724, in __init__ 
    self.handle() 
  File "/usr/local/lib/python3.6/dist-packages/dnslib/server.py", line 135, in handle 
    new_data = self.request.recv(8192) 
ConnectionResetError: [Errno 104] Connection reset by peer 

I suggest a try/except on the first part of https://github.com/paulc/dnslib/blob/master/dnslib/server.py#L126 to handle this.

Might be related to #16.

Basic Parsing Usage

In your example usage, you parse a 'packet' using 'dnslib.DNSRecord.parse'. What is a 'packet'? I.e. what layer of the communication stack? Also, what data-type is 'packet'?

What I'm trying to do is read DNS traffic from pcaps (I know how to filter out things that aren't DNS traffic), with a library like dpkt. dpkt can give me any layer of the stack I want, but neither the full packet nor solely the application layer seem to work as input to 'dnslib.DNSRecord.parse'.

How do I update dns dynamically?

I used to update dns manually with using nsupdate command with bind9 as DNS Server

Now I'm succeed using DNS Server/Client with this library, thank you so much.

I'm wonder should I make my own algorithm or own program,
or there is already feature in this library?

In nsupdate command, there was update section instead of addition section nor answer section. So there's special section to update like that, but that readme.me didn't mention it. Instead, it mention DNSSec which it's one component for updating?

DNAME records are broken

DNAME records are broken as this example shows:
example_2_done

To compare here is the result from dig:
example_1_done

I think it is a matter of DNAME missing from RDMAP. Should I open a PR to fix it?

Bimap to Enum

Hello, I was wondering if you would be open to using Enum instead of Bimap? I will happily make a PR if you are open to the change.

Error in malformed requests

I get a bunch of these errors after running the server for a while:

Traceback (most recent call last):                                                                                        
File "/usr/lib/python3.6/socketserver.py", line 654, in process_request_thread
self.finish_request(request, client_address)     
File "/usr/lib/python3.6/socketserver.py", line 364, in finish_request
self.RequestHandlerClass(request, client_address, self)                                                                             
File "/usr/lib/python3.6/socketserver.py", line 724, in __init__
self.handle()
File "/usr/local/lib/python3.6/dist-packages/dnslib/server.py", line 130, in handle                                                     length = struct.unpack("!H",bytes(data[:2]))[0]                                                                                   struct.error: unpack requires a buffer of 2 bytes                                                                                     

Apparently they are harmless, but I wonder if dnslib is handling something in the DNS protocol wrongly. If not it'd be nice to have a try/catch to avoid logging these. Any ideas of what is causing them?

I also get a few like these:

Error unpacking DNSQuestion [offset=344]: Not enough bytes [offset=344,remaining=59,requested=79]

DNS Record Type 65 Unsupported

File "/home/matt/Programming/Python/nsfilter/venv/lib/python3.7/site-packages/dnslib/bimap.py", line 68, in __getitem__
raise self.error("%s: Invalid forward lookup: [%s]" % (self.name,k))
File "/home/matt/Programming/Python/nsfilter/rules.py", line 525, in __call__
qtype = dnslib.dns.QTYPE[nsquery.q.qtype]
dnslib.dns.DNSError: QTYPE: Invalid forward lookup: [65]
File "/home/matt/Programming/Python/nsfilter/venv/lib/python3.7/site-packages/dnslib/bimap.py", line 68, in __getitem__
raise self.error("%s: Invalid forward lookup: [%s]" % (self.name,k))
----------------------------------------
dnslib.dns.DNSError: QTYPE: Invalid forward lookup: [65]

Is there any expectation that this will get added?

Records with empty rdata causes pack() to fail

A DNSRecord instance such as

<DNS Header: id=0x9d1a type=QUERY opcode=UPDATE flags= rcode='NOERROR' zo=1 pr=1 up=3 ad=0>
<DNS Question: 'xxx.net.' qtype=SOA qclass=IN>
<DNS RR: 'yyy.xxx.net.' rtype=CNAME rclass=None ttl=0 rdata=''>
<DNS RR: 'yyy.xxx.net.' rtype=AAAA rclass=* ttl=0 rdata=''>
<DNS RR: 'yyy.xxx.net.' rtype=A rclass=* ttl=0 rdata=''>
<DNS RR: 'yyy.xxx.net.' rtype=A rclass=IN ttl=1200 rdata='192.168.1.2'>

will fail to pack() with the following error:

>>> r.pack()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/peter/.local/lib/python3.12/site-packages/dnslib/dns.py", line 335, in pack
    rr.pack(buffer)
  File "/home/peter/.local/lib/python3.12/site-packages/dnslib/dns.py", line 888, in pack
    self.rdata.pack(buffer)
    ^^^^^^^^^^^^^^^

AttributeError: 'str' object has no attribute 'pack'

I believe the source of the error is

rdata = ''

Avoiding misconceptions with hexdump

Hello, can you please add into README a note that RDATA format like this "\# 15 0761617263683634067562756E7475" is byte's hexdump format, and way to bring it back is unhex it via: binascii.unhexlify(data)?

Client does not validate DNS transaction id

Hi,

dnslib client does not validate DNS transaction id (TXID) as specified in the RFC.
This considered as implementation bug. Attackers can use this to redirect users to their malicious name servers.
I know the client created for testing but other projects using dnslib as a dependency might be affected as well.

I suggest to add a simple validate:

#... request code
a_pkt = q.send(address,port,tcp=args.tcp)
a = DNSRecord.parse(a_pkt)

if q.header.id != a.header.id:
    raise DNSError('Response transaction id does not match query transaction id')

Dnslib fails to handle unknown RR types in NSEC RD type bitmap

I parsed a bunch of DNS traffic with dnslib and noticed that some NSEC response records contains a TYPE65534 record that causes dnslib to fail.

Example:

;; Sending:
;; QUERY: 822601000001000000000000066f696c70726f02636800002f0001
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 33318
;; flags: rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;oilpro.ch.                     IN      NSEC

;; Got answer:
;; RESPONSE: 822681800001000100000000066f696c70726f02636800002f0001c00c002f000100000e100036066f696c70726f02636800000722000000000380ff200000000000000000000000000000000000000000000000000000000000000002
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 33318
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;oilpro.ch.                     IN      NSEC
;; ANSWER SECTION:
oilpro.ch.              3600    IN      NSEC    oilpro.ch. NS SOA RRSIG NSEC DNSKEY TYPE65534

TYPE65534 is an internal type used by BIND to keep track of signing, not sure why it shows up in the NSEC data, but it does.
https://www.dns.cam.ac.uk/news/2020-04-27-nsdiff.html
https://ftp.iij.ad.jp/pub/network/isc/bind9/9.14.6/doc/arm/Bv9ARM.ch04.html

Also possible to query for the TYPE65534 record on the domain

$ dig oilpro.ch TYPE65534

; <<>> DiG 9.18.6 <<>> oilpro.ch TYPE65534
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 16416
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; MBZ: 0x0005, udp: 512
;; QUESTION SECTION:
;oilpro.ch.                     IN      TYPE65534

;; ANSWER SECTION:
oilpro.ch.              5       IN      TYPE65534 \# 5 0D2A680001
oilpro.ch.              5       IN      TYPE65534 \# 5 0DD5C60001

;; Query time: 63 msec
;; SERVER: 192.168.42.2#53(192.168.42.2) (UDP)
;; WHEN: Mon Oct 24 13:50:23 CEST 2022
;; MSG SIZE  rcvd: 72

Quick and dirty fix for my needs for decoding the NSEC part

--- dnslib/dns.py
+++ dnslib/dns.py
@@ -1634,7 +1636,7 @@ def decode_type_bitmap(type_bitmap):
             for i in range(8):
                 if (value << i) & 0x80:
                     bitpos = (256*winnum) + (8*pos) + i
-                    rrlist.append(QTYPE[bitpos])
+                    rrlist.append(QTYPE.get(bitpos, "TYPE" + str(bitpos)))

This only helps for unknown types in the type bitmap thou, and only for decoding. Dnslib still fails to parse unknown RR types in general

$ ./test_decode.py --new oilpro.ch TYPE65534
Traceback (most recent call last):
  File "/home/lanrob1702/development/dnslib/dnslib/bimap.py", line 75, in __getattr__
    return self.reverse[k]
KeyError: 'TYPE65534'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/lanrob1702/development/dnslib/dnslib/./test_decode.py", line 264, in <module>
    new_test(*args.new,nodig=args.nodig,dnssec=args.dnssec)
  File "/home/lanrob1702/development/dnslib/dnslib/./test_decode.py", line 72, in new_test
    q = DNSRecord.question(domain,qtype)
  File "/home/lanrob1702/development/dnslib/dnslib/dns.py", line 140, in question
    return DNSRecord(q=DNSQuestion(qname,getattr(QTYPE,qtype),
  File "/home/lanrob1702/development/dnslib/dnslib/bimap.py", line 77, in __getattr__
    raise self.error("%s: Invalid reverse lookup: [%s]" % (self.name,k))
dnslib.dns.DNSError: QTYPE: Invalid reverse lookup: [TYPE65534]

A better solution would probably be to implement support for RFC3597: Handling of Unknown DNS Resource Record (RR) Types

[5](https://datatracker.ietf.org/doc/html/rfc3597#section-5).  Text Representation

   In the "type" field of a master file line, an unknown RR type is
   represented by the word "TYPE" immediately followed by the decimal RR
   type number, with no intervening whitespace.  In the "class" field,
   an unknown class is similarly represented as the word "CLASS"
   immediately followed by the decimal class number.

   This convention allows types and classes to be distinguished from
   each other and from TTL values, allowing the "[<TTL>] [<class>]
   <type> <RDATA>" and "[<class>] [<TTL>] <type> <RDATA>" forms of
   [[RFC1035](https://datatracker.ietf.org/doc/html/rfc1035)] to both be unambiguously parsed.

   The RDATA section of an RR of unknown type is represented as a
   sequence of white space separated words as follows:

      The special token \# (a backslash immediately followed by a hash
      sign), which identifies the RDATA as having the generic encoding
      defined herein rather than a traditional type-specific encoding.

      An unsigned decimal integer specifying the RDATA length in octets.

      Zero or more words of hexadecimal data encoding the actual RDATA
      field, each containing an even number of hexadecimal digits.

   If the RDATA is of zero length, the text representation contains only
   the \# token and the single zero representing the length.

Most of the parts needed seems to already be available in the RR and RD base classes.

@paulc Would a PR with necessary updates to RR, RD and the Bimap be interesting if I decide to take a look at it?

Having trouble adding DS record subclass

I've been working on a change to add a DS record RD subclass over in rmbolger/dnslib@801edf7. It has a very similar structure to the DNSKEY record. The main difference is that the DNSKEY has the public key encoded as Base64 whereas the DS has the digest value encoded as Hex.

At a basic level, it seems to be working. But when I attempt to test it in a way that adds multiple answers to a DNSRecord something is happening where all of the instances of the record get overwritten with a copy of first one seen. The easiest demonstration is comparing an actual dig query to the dnslib.client output. For example:

>dig example.org. DS

; <<>> DiG 9.17.15 <<>> example.org. DS
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 60
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 6, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;example.org.                   IN      DS

;; ANSWER SECTION:
example.org.            2121    IN      DS      3397 8 1 DEE10345942C98711EB058B25A749EE342FCE1DC
example.org.            2121    IN      DS      3397 8 2 ED1168604BC6A14068B9905401E62698BB3663B6EC2073EBD3599B88 2A785BF6
example.org.            2121    IN      DS      31589 8 1 7B8370002875DDA781390A8E586C31493847D9BC
example.org.            2121    IN      DS      31589 8 2 3FDC4C11FA3AD3535EA8C1CE3EAF7BFA5CA9AE8A834D98FEE10085CF AEB625AA
example.org.            2121    IN      DS      37780 8 1 B4A5CCE8D82DC585E327E5896EAE82E0B9A76DC6
example.org.            2121    IN      DS      37780 8 2 D96AFA9022000D368B5F497877DF289A1E9A13A1AB1F97BC1BF4D5DE 16879134

Six unique results where the digest values may have whitespace within the hex text (allowed but not mandated by the RFC).

>python -m dnslib.client --query --hex --dig example.org. DS
;; Sending:
;; QUERY: ef2b01000001000000000000076578616d706c65036f726700002b0001
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 61227
;; flags: rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;example.org.                   IN      DS

;; Got answer:
;; RESPONSE: ef2b81800001000600000000076578616d706c65036f726700002b0001c00c002b000100000e1000187b6508017b8370002875dda781390a8e586c31493847d9bcc00c002b000100000e10002493940802d96afa9022000d368b5f497877df289a1e9a13a1ab1f97bc1bf4d5de16879134c00c002b000100000e10001893940801b4a5cce8d82dc585e327e5896eae82e0b9a76dc6c00c002b000100000e1000240d450802ed1168604bc6a14068b9905401e62698bb3663b6ec2073ebd3599b882a785bf6c00c002b000100000e1000180d450801dee10345942c98711eb058b25a749ee342fce1dcc00c002b000100000e1000247b6508023fdc4c11fa3ad3535ea8c1ce3eaf7bfa5ca9ae8a834d98fee10085cfaeb625aa
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 61227
;; flags: qr rd ra; QUERY: 1, ANSWER: 6, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;example.org.                   IN      DS
;; ANSWER SECTION:
example.org.            3600    IN      DS      3397 8 1 DEE10345942C98711EB058B25A749EE342FCE1DC
example.org.            3600    IN      DS      3397 8 1 DEE10345942C98711EB058B25A749EE342FCE1DC
example.org.            3600    IN      DS      3397 8 1 DEE10345942C98711EB058B25A749EE342FCE1DC
example.org.            3600    IN      DS      3397 8 1 DEE10345942C98711EB058B25A749EE342FCE1DC
example.org.            3600    IN      DS      3397 8 1 DEE10345942C98711EB058B25A749EE342FCE1DC
example.org.            3600    IN      DS      3397 8 1 DEE10345942C98711EB058B25A749EE342FCE1DC

The first result matches the first result in the dig output. But the next 5 results are just clones of the first instead of their individual rdata. I've tried adding some debug logging in the DS.parse method but it seems to be parsing the unique packets normally.

I feel like I must be doing something wrong here, but I can't figure out what it is.

Accessing request ip in resolve()

Is it possible to get the request IP in resolve()? The use case is that I'm running a small DNS server for a domain, and I noticed it's being used as a full DNS server by somebody. I want to resolve any requests to *.mydomain.com for any IP except for a whitelist.

ID mismatch with dig

I'm trying to use dnslib for a DNS cache server. The minimal environment is as:

import socket
from dns_resolve import *

IP = '127.0.0.1'
PORT = 8123


def main():
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    sock.bind((IP, PORT))
    print('DNS is listening on {0}:{1} ...'.format(IP, PORT))

    while True:
        data, addr = sock.recvfrom(1024)
        q = DNSRecord.parse(data)
        sock.sendto(str(q).encode('utf-8'), addr)


if __name__ == '__main__':
    main()

And when I do dig in shell as:

dig @127.0.0.1 -p 8123 google.com

The reply is:

;; Warning: ID mismatch: expected ID 39351, got 15163
;; Warning: query response not set

I print q, and the ID is actually the expected ID 39351. I've tried to manually set the ID, new a DNSRecord with specific values, new a DNSHeader, restart the Python server, but I still got the warning which says expected ID xxx, got 15163. I have no idea where 15163 comes from.

PS. I also tried to change the while part to:

   while True:
        data, addr = sock.recvfrom(1024)
        sock.sendto(data, addr)

In this case, dig can get the reply with the correct ID number.

TXT records with Unicode control chars in them

I'm not sure if it's a problem or not, but I've been recently comparing some dig output to dnslib output for TXT records and I'm not sure what to think about TXT records with bytes in them that translate to unicode chars - for example:

$ dig -t TXT @8.8.8.8 smartjailmail.com
....
smartjailmail.com.	3600	IN	TXT	"google-site-verification=7Avm2jKuluvrgko_FgTUqYqlYpvYu6hMf\005\000\000\000\000\000\000\000DQ"
....

And via dnslib.client

$ python -m dnslib.client --server 8.8.8.8:53 smartjailmail.com TXT
...
smartjailmail.com.      3600    IN      TXT     "google-site-verification=7Avm2jKuluvrgko_FgTUqYqlYpvYu6hMfDQ"
...

and via repr on the RD.data property:

b'google-site-verification=7Avm2jKuluvrgko_FgTUqYqlYpvYu6hMf\x05\x00\x00\x00\x00\x00\x00\x00DQ'

As I read the code dnslib took the bytes and tried to parse it as utf-8 and discards any non-utf8 chars - and dig escapes anything outside of ascii perhaps?

When I read the RFC \DDD indeed is allowed:

\DDD where each D is a digit is the octet corresponding to
the decimal number described by DDD. The resulting
octet is assumed to be text and is not checked for
special meaning.

but I'm not quite sure what to expect of it with regards to dnslib - As I read the RFC encoding of TXT records is not prescribed ๐Ÿคท I would worry that a TXT record with some kind of esoteric encoding would break the dnslib way of turning the TXT records into text again

Missing tags for releases past 0.9.7

According to pypi, the newest release is 0.9.12 but the newest tag on GitHub is 0.9.7. Could you please push the missing tags? Monitoring new releases via GitHub is much more convenient since it provides ATOM feeds.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.