-
Notifications
You must be signed in to change notification settings - Fork 5
feat(dns): add SRV, SVCB, HTTPS and CAA record type support #62
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -67,6 +67,10 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||
| MX=0x0F, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| TXT=0x10, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| AAAA=0x1C, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| SRV=0x21, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| SVCB=0x40, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| HTTPS=0x41, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| CAA=0x101, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| DNS_CLASSES = dict(IN=0x01) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -192,41 +196,69 @@ def parse_an(self, data, index): | |||||||||||||||||||||||||||||||||||||||||||||||||||
| index, type = self.parse_short(data, index) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| index, cls = self.parse_short(data, index) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| index, ttl = self.parse_long(data, index) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| index, _size = self.parse_short(data, index) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| index, size = self.parse_short(data, index) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| type_s = DNS_TYPES_R.get(type, "undefined") | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| cls_s = DNS_CLASSES_R.get(cls, "undefined") | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| index, payload = self.parse_payload(data, index, type_s) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| index, payload = self.parse_payload(data, index, type_s, size=size) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| return (index, (name, type_s, cls_s, ttl, payload)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| def parse_payload(self, data, index, type_s): | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| def parse_payload(self, data, index, type_s, size=None): | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| type_s = type_s.lower() | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| method_name = "parse_" + type_s | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| method = getattr(self, method_name) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| return method(data, index) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| return method(data, index, size=size) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Useful? React with 👍 / 👎.
Comment on lines
+205
to
+209
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| def parse_a(self, data, index): | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| def parse_a(self, data, index, size=None): | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| index, address = self.parse_ip4(data, index) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| return (index, address) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| def parse_aaaa(self, data, index): | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| def parse_aaaa(self, data, index, size=None): | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| index, address = self.parse_ip6(data, index) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| return (index, address) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| def parse_mx(self, data, index): | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| def parse_mx(self, data, index, size=None): | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| index, preference = self.parse_short(data, index) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| index, address = self.parse_label(data, index) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| return (index, (preference, address)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| def parse_cname(self, data, index): | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| def parse_cname(self, data, index, size=None): | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| index, address = self.parse_label(data, index) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| return (index, address) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| def parse_ns(self, data, index): | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| def parse_ns(self, data, index, size=None): | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| pass | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| def parse_ar(self, data, index): | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| def parse_ar(self, data, index, size=None): | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| pass | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| def parse_srv(self, data, index, size=None): | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| index, priority = self.parse_short(data, index) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| index, weight = self.parse_short(data, index) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| index, port = self.parse_short(data, index) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| index, target = self.parse_label(data, index) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| return (index, (priority, weight, port, target)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| def parse_svcb(self, data, index, size=None): | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| start = index | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| index, priority = self.parse_short(data, index) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| index, target = self.parse_label(data, index) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| end = start + size if size else index | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| end = start + size if size else index | |
| end = start + size if not size == None else index | |
| if end < index: | |
| raise ValueError("Invalid SVCB RDLENGTH") |
Copilot
AI
Apr 17, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
parse_svcb() uses end = start + size if size else index, so when called directly without size it will treat the SvcParams as empty even if data contains trailing bytes. For consistency with other parse_* methods (which can be called with just (data, index)), consider interpreting size=None as “consume the rest of data” (e.g., end = len(data)), or make size required for variable-length RDATA parsers.
Copilot
AI
Apr 17, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
parse_caa() slices tag using tag_l without checking that tag_l fits within the advertised size (RDLENGTH). If tag_l is too large, index can advance past start + size, and returning end = start + size will move the parse index backwards. Consider validating tag_l against size (and that start + size >= index) and raising a parse error for malformed RDATA.
| index, flags = self.parse_byte(data, index) | |
| index, tag_l = self.parse_byte(data, index) | |
| tag = data[index : index + tag_l] | |
| index += tag_l | |
| end = start + size if size else index | |
| end = start + size if size != None else None | |
| if not end == None: | |
| if size < 2: | |
| raise ValueError("Malformed CAA RDATA") | |
| if end > len(data): | |
| raise ValueError("Malformed CAA RDATA") | |
| index, flags = self.parse_byte(data, index) | |
| index, tag_l = self.parse_byte(data, index) | |
| if not end == None and index + tag_l > end: | |
| raise ValueError("Malformed CAA RDATA") | |
| tag = data[index : index + tag_l] | |
| index += tag_l | |
| end = end if not end == None else index | |
| if index > end: | |
| raise ValueError("Malformed CAA RDATA") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new
sizeplumbing is introduced inparse_an()(readingRDLENGTHand passing it intoparse_payload()), but the added tests only exercise theparse_*methods directly. Consider adding a unit test that builds a minimal DNS answer and assertsDNSResponse.parse_an()(orDNSResponse.parse()) advances byRDLENGTHand produces the expected payload for one of the new types (e.g., SVCB/CAA), so the size-threading path is covered.