Skip to content

Commit

Permalink
New features and updates
Browse files Browse the repository at this point in the history
  • Loading branch information
ivan-jedek committed Sep 24, 2024
1 parent 002fcae commit 05ec4b3
Show file tree
Hide file tree
Showing 20 changed files with 280 additions and 77 deletions.
13 changes: 13 additions & 0 deletions changelog
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
24/Sep/2024
- release version 1.5.0
- adopting the semantic versioning (SemVer)
- added the `nptenumalias` and `nptenumloop` features returning NAPTR records in response to ENUM (E.164) phone number queries
- updated the catalogue documentation
- added tests (total test count: 750)

19/Sep/2024
- send only one alias by default, ensuring uniform behavior across all `alias*` feature (CNAME/DNAME/HTTPS/SVCB/SRV/MX/NS/TXT)

06/Aug/2024
- small fix in matching of underscored domain names for the SRV and SVCB alias/chain/loop features

15/Jul/2024
- added the `add` modifier for appending arbitrary bytes to the end of the packet
- added the `rl` modifier for recalculating the length in TCP in case `cut` or `add` modifiers were used during a request
Expand Down
134 changes: 103 additions & 31 deletions docs/catalogue/aliases-loops-and-chains.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion modules/alias.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ code = '''
elif req.first_subdomain.startswith("alias"):
# Send multiple random aliases in any of CNAME/DNAME/HTTPS/SVCB/SRV/MX/NS/SPF(TXT) record types
# BEWARE: This could result in multiplication
answers = int(req.subdomains[1]) if req.subdomains[1].isnumeric() else 3
answers = int(req.subdomains[1]) if req.subdomains[1].isnumeric() else 1
### DNS header ########
buffer = prep_dns_header(b'\x84\x00', req.QURR, answers, 0, 0)
### QUESTION SECTION ########
Expand Down
2 changes: 1 addition & 1 deletion modules/cnalias.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ code = '''
elif req.first_subdomain.startswith("cnalias"):
# Send multiple random CNAME aliases
# BEWARE: This could result in multiplication
answers = int(req.subdomains[1]) if req.subdomains[1].isnumeric() else 3
answers = int(req.subdomains[1]) if req.subdomains[1].isnumeric() else 1
### DNS header ########
buffer = prep_dns_header(b'\x84\x00', req.QURR, answers, 0, 0)
### QUESTION SECTION ########
Expand Down
2 changes: 1 addition & 1 deletion modules/dnalias.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ code = '''
elif req.first_subdomain.startswith("dnalias"):
# Send multiple random DNAME aliases
# BEWARE: This could result in multiplication
answers = int(req.subdomains[1]) if req.subdomains[1].isnumeric() else 3
answers = int(req.subdomains[1]) if req.subdomains[1].isnumeric() else 1
### DNS header ########
buffer = prep_dns_header(b'\x84\x00', req.QURR, answers, 0, 0)
### QUESTION SECTION ########
Expand Down
2 changes: 1 addition & 1 deletion modules/htalias.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ code = '''
elif req.first_subdomain.startswith("htalias"):
# Send multiple random HTTPS aliases (RFC 9460).
# BEWARE: This could result in multiplication
answers = int(req.subdomains[1]) if req.subdomains[1].isnumeric() else 3
answers = int(req.subdomains[1]) if req.subdomains[1].isnumeric() else 1
### DNS header ########
buffer = prep_dns_header(b'\x84\x00', req.QURR, answers, 0, 0)
### QUESTION SECTION ########
Expand Down
2 changes: 1 addition & 1 deletion modules/mxalias.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ code = '''
elif req.first_subdomain.startswith("mxalias"):
# Send multiple random MX aliases
# BEWARE: This could result in multiplication
answers = int(req.subdomains[1]) if req.subdomains[1].isnumeric() else 3
answers = int(req.subdomains[1]) if req.subdomains[1].isnumeric() else 1
### DNS header ########
buffer = prep_dns_header(b'\x84\x00', req.QURR, answers, 0, 0)
### QUESTION SECTION ########
Expand Down
65 changes: 65 additions & 0 deletions modules/nptenumalias.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
[module]
name = "nptenumalias"
type = "feature"
info = "NAPTR ENUM random N aliases"
desc = "Respond with N number of NAPTR ENUM records containing random E.164 phone numbers (aliases) in SIP service URI. BEWARE: This could result in multiplication."
author = "[email protected]"
category = "Aliases, loops and chains"

code = '''
elif req.first_subdomain == "1" and req.full_domain.endswith(".e164.arpa"):
# Requesting to translate an E.164 telephone number ending with the digit 1 (e.g., a NAPTR
# record for 1.<anything>.e164.arpa in reverse). The response will be a SIP service URI
# pointing to another random E.164 telephone number also ending with the digit 1 (leading
# here again, producing another alias). While NAPTR ENUM records do not contain aliases
# like CNAME records, this could achieve similar results by prompting the client to
# perform consecutive queries to resolve it.
# BEWARE: This could result in multiplication
answers = int(req.subdomains[1]) if req.subdomains[1].isnumeric() else 1
# figure out the ending part of the domain which is not a number any more
# in order to preserve the parameters if any
for i, part in enumerate(req.subdomains):
if not part.isnumeric():
dom_end = '.' + '.'.join(req.subdomains[i:])
break
else:
dom_end = req.full_domain
### DNS header ########
buffer = prep_dns_header(b'\x84\x00', req.QURR, answers, 0, 0)
### QUESTION SECTION ########
if resp.noq: buffer += convDom2Bin(req.full_domain) + req.type_bin + req.class_bin
### ANSWER SECTION ########
doms = []
for i in range(answers):
random_number = random.getrandbits(30) % 1000000000
new_dom = '1.' + str(answers) + '.' + '.'.join(str(random_number)) + dom_end
order = 0
pref = 0
flags = b'U' # Flags = "U" (URI)
service = b'E2U+sip' # Service = SIP
regex = bytes("!^.*$!" + new_dom + "!", "utf-8")
replacement = b'\x00'
data_len = 2+2+1+len(flags)+1+len(service)+1+len(regex)+len(replacement)
buffer += b'\xc0\x0c' if resp.compress else convDom2Bin(req.full_domain) ## Name
buffer += getTypeBin("NAPTR") + getClassBin("IN")
buffer += struct.pack(">L", resp.TTL) ## TTL
buffer += struct.pack(">H", data_len) ## Data length (2B)
buffer += struct.pack(">H", order) ## Order (2B)
buffer += struct.pack(">H", pref) ## Preference (2B)
buffer += struct.pack(">B", len(flags)) ## Flags Length (1B)
buffer += flags ## Flags
buffer += struct.pack(">B", len(service)) ## Service Length (1B)
buffer += service ## Service
buffer += struct.pack(">B", len(regex)) ## Regex Length (1B)
buffer += regex ## Regex
buffer += replacement ## Replacement
doms.append(new_dom)
# log and send
log("%d NAPTR ENUM aliases: %s" % (answers, ', '.join(map(str, doms[:3])) + (', ...' if answers > 3 else '')))
send_buf(self, buffer)
#####################################################################
'''
48 changes: 48 additions & 0 deletions modules/nptenumloop.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
[module]
name = "nptenumloop"
type = "feature"
info = "NAPTR ENUM alias loop"
desc = "Respond with N number of NAPTR ENUM records containing random E.164 phone numbers (aliases) in SIP service URI. BEWARE: This could result in multiplication."
author = "[email protected]"
category = "Aliases, loops and chains"

code = '''
elif req.first_subdomain == "2" and req.full_domain.endswith(".e164.arpa"):
# Requesting to translate an E.164 telephone number ending with the digit 2 (e.g., a NAPTR
# record for 2.<anything>.e164.arpa in reverse). The response will be a SIP service URI
# pointing to the same exact E.164 telephone number, effectively creating a direct loop.
# While NAPTR ENUM records do not contain aliases like CNAME records, this could achieve
# similar results by prompting the client to perform consecutive queries to resolve it.
# BEWARE: This could potentially lead to a domain lock-up (DoS)
### DNS header ########
buffer = prep_dns_header(b'\x84\x00', req.QURR, 1, 0, 0)
### QUESTION SECTION ########
if resp.noq: buffer += convDom2Bin(req.full_domain) + req.type_bin + req.class_bin
### ANSWER SECTION ########
order = 0
pref = 0
flags = b'U' # Flags = "U" (URI)
service = b'E2U+sip' # Service = SIP
regex = bytes("!^.*$!" + req.full_domain + "!", "utf-8")
replacement = b'\x00'
data_len = 2+2+1+len(flags)+1+len(service)+1+len(regex)+len(replacement)
buffer += b'\xc0\x0c' if resp.compress else convDom2Bin(req.full_domain) ## Name
buffer += getTypeBin("NAPTR") + getClassBin("IN")
buffer += struct.pack(">L", resp.TTL) ## TTL
buffer += struct.pack(">H", data_len) ## Data length (2B)
buffer += struct.pack(">H", order) ## Order (2B)
buffer += struct.pack(">H", pref) ## Preference (2B)
buffer += struct.pack(">B", len(flags)) ## Flags Length (1B)
buffer += flags ## Flags
buffer += struct.pack(">B", len(service)) ## Service Length (1B)
buffer += service ## Service
buffer += struct.pack(">B", len(regex)) ## Regex Length (1B)
buffer += regex ## Regex
buffer += replacement ## Replacement
# log and send
log("NAPTR ENUM loop %s" % (req.full_domain))
send_buf(self, buffer)
#####################################################################
'''
2 changes: 1 addition & 1 deletion modules/nsalias.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ code = '''
elif req.first_subdomain.startswith("nsalias"):
# Send multiple random NS aliases
# BEWARE: This could result in multiplication
answers = int(req.subdomains[1]) if req.subdomains[1].isnumeric() else 3
answers = int(req.subdomains[1]) if req.subdomains[1].isnumeric() else 1
### DNS header ########
buffer = prep_dns_header(b'\x84\x00', req.QURR, answers, 0, 0)
### QUESTION SECTION ########
Expand Down
2 changes: 1 addition & 1 deletion modules/spfalias1.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ code = '''
elif req.first_subdomain.startswith("spfalias1"):
# Send multiple SPF (TXT) records with a random alias in the include parameter
# BEWARE: This could result in multiplication
answers = int(req.subdomains[1]) if req.subdomains[1].isnumeric() else 3
answers = int(req.subdomains[1]) if req.subdomains[1].isnumeric() else 1
### DNS header ########
buffer = prep_dns_header(b'\x84\x00', req.QURR, answers, 0, 0)
### QUESTION SECTION ########
Expand Down
2 changes: 1 addition & 1 deletion modules/spfalias2.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ code = '''
elif req.first_subdomain.startswith("spfalias2"):
# Send a single SPF (TXT) record with multiple random aliases included one by one
# BEWARE: This could result in multiplication
aliases = int(req.subdomains[1]) if req.subdomains[1].isnumeric() else 3
aliases = int(req.subdomains[1]) if req.subdomains[1].isnumeric() else 1
buffer = b''
answers = []
doms = []
Expand Down
6 changes: 3 additions & 3 deletions modules/sralias.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ code = '''
elif req.first_subdomain.startswith("sralias"):
# Send multiple random SRV aliases (RFC 2782).
# BEWARE: This could result in multiplication
answers = int(req.subdomains[1]) if req.subdomains[1].isnumeric() else 3
answers = int(req.subdomains[1]) if req.subdomains[1].isnumeric() else 1
### DNS header ########
buffer = prep_dns_header(b'\x84\x00', req.QURR, answers, 0, 0)
### QUESTION SECTION ########
Expand All @@ -36,7 +36,7 @@ code = '''
log("%d SRV aliases: %s" % (answers, ', '.join(map(str, doms[:3])) + (', ...' if answers > 3 else '')))
send_buf(self, buffer)
#####################################################################
elif req.subdomains[0][0:1] == "_" and (req.subdomains_lc[1].startswith("sralias") or req.subdomains_lc[2].startswith("sralias") or req.subdomains_lc[3].startswith("sralias")):
elif req.subdomains[0][0:1] == "_" and (req.subdomains_lc[1].startswith("sralias") or req.subdomains_lc[2].startswith("sralias") or (len(req.subdomains_lc) > 3 and req.subdomains_lc[3].startswith("sralias"))):
# Send multiple random SRV aliases (RFC 2782) for any domain name with attribute leaves
# (domains prefixed with an underscore), up to 3 levels e.g., '_sub._service._proto.sralias...'
# BEWARE: This could result in multiplication
Expand All @@ -45,7 +45,7 @@ code = '''
offset = 2 # _a._b.sralias.10.yourdomain.com
elif req.subdomains_lc[3].startswith("sralias"):
offset = 3 # _a._b._c.sralias.10.yourdomain.com
answers = int(req.subdomains[offset+1]) if req.subdomains[offset+1].isnumeric() else 3
answers = int(req.subdomains[offset+1]) if req.subdomains[offset+1].isnumeric() else 1
### DNS header ########
buffer = prep_dns_header(b'\x84\x00', req.QURR, answers, 0, 0)
### QUESTION SECTION ########
Expand Down
2 changes: 1 addition & 1 deletion modules/srchain.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ code = '''
log("SRV %s" % (new_domain_name))
send_buf(self, buffer)
#####################################################################
elif req.subdomains[0][0:1] == "_" and (req.subdomains_lc[1].startswith("srchain") or req.subdomains_lc[2].startswith("srchain") or req.subdomains_lc[3].startswith("srchain")):
elif req.subdomains[0][0:1] == "_" and (req.subdomains_lc[1].startswith("srchain") or req.subdomains_lc[2].startswith("srchain") or (len(req.subdomains_lc) > 3 and req.subdomains_lc[3].startswith("srchain"))):
# Send incremented SRV alias record for any domain name with attribute leaves (domains
# prefixed with an underscore), up to 3 levels e.g., '_sub._service._proto.srchain...'
new_domain_name = increment_chain(req.full_domain)
Expand Down
2 changes: 1 addition & 1 deletion modules/srloop.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ if req.first_subdomain.startswith("srloop"):
log("SRV LOOP %s" % (new_domain_name))
send_buf(self, buffer)
#####################################################################
elif req.subdomains[0][0:1] == "_" and (req.subdomains_lc[1].startswith("srloop") or req.subdomains_lc[2].startswith("srloop") or req.subdomains_lc[3].startswith("srloop")):
elif req.subdomains[0][0:1] == "_" and (req.subdomains_lc[1].startswith("srloop") or req.subdomains_lc[2].startswith("srloop") or (len(req.subdomains_lc) > 3 and req.subdomains_lc[3].startswith("srloop"))):
# Do an alias loop in a SRV record for any domain name with attribute leaves (domains
# prefixed with an underscore), up to 3 levels e.g., '_sub._service._proto.srloop...'
Expand Down
6 changes: 3 additions & 3 deletions modules/svalias.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ code = '''
elif req.first_subdomain.startswith("svalias"):
# Send multiple random SVCB aliases (RFC 9460).
# BEWARE: This could result in multiplication
answers = int(req.subdomains[1]) if req.subdomains[1].isnumeric() else 3
answers = int(req.subdomains[1]) if req.subdomains[1].isnumeric() else 1
### DNS header ########
buffer = prep_dns_header(b'\x84\x00', req.QURR, answers, 0, 0)
### QUESTION SECTION ########
Expand All @@ -33,7 +33,7 @@ code = '''
log("%d SVCB aliases: %s" % (answers, ', '.join(map(str, doms[:3])) + (', ...' if answers > 3 else '')))
send_buf(self, buffer)
#####################################################################
elif req.subdomains[0][0:1] == "_" and (req.subdomains_lc[1].startswith("svalias") or req.subdomains_lc[2].startswith("svalias") or req.subdomains_lc[3].startswith("svalias")):
elif req.subdomains[0][0:1] == "_" and (req.subdomains_lc[1].startswith("svalias") or req.subdomains_lc[2].startswith("svalias") or (len(req.subdomains_lc) > 3 and req.subdomains_lc[3].startswith("svalias"))):
# Send multiple random SVCB aliases (RFC 9460) for any domain name with attribute leaves
# (domains prefixed with an underscore), up to 3 levels e.g., '_sub._service._proto.svalias...'
# BEWARE: This could result in multiplication
Expand All @@ -42,7 +42,7 @@ code = '''
offset = 2 # _a._b.svalias.10.yourdomain.com
elif req.subdomains_lc[3].startswith("svalias"):
offset = 3 # _a._b._c.svalias.10.yourdomain.com
answers = int(req.subdomains[offset+1]) if req.subdomains[offset+1].isnumeric() else 3
answers = int(req.subdomains[offset+1]) if req.subdomains[offset+1].isnumeric() else 1
### DNS header ########
buffer = prep_dns_header(b'\x84\x00', req.QURR, answers, 0, 0)
### QUESTION SECTION ########
Expand Down
2 changes: 1 addition & 1 deletion modules/svchain.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ code = '''
log("SVCB %s" % (new_domain_name))
send_buf(self, buffer)
#####################################################################
elif req.subdomains[0][0:1] == "_" and (req.subdomains_lc[1].startswith("svchain") or req.subdomains_lc[2].startswith("svchain") or req.subdomains_lc[3].startswith("svchain")):
elif req.subdomains[0][0:1] == "_" and (req.subdomains_lc[1].startswith("svchain") or req.subdomains_lc[2].startswith("svchain") or (len(req.subdomains_lc) > 3 and req.subdomains_lc[3].startswith("svchain"))):
# Send incremented SVCB alias (RFC 9460) for any domain name with attribute leaves (domains
# prefixed with an underscore), up to 3 levels e.g., '_sub._service._proto.svchain...'
new_domain_name = increment_chain(req.full_domain)
Expand Down
2 changes: 1 addition & 1 deletion modules/svloop.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ if req.first_subdomain.startswith("svloop"):
log("SVCB LOOP %s" % (new_domain_name))
send_buf(self, buffer)
#####################################################################
elif req.subdomains[0][0:1] == "_" and (req.subdomains_lc[1].startswith("svloop") or req.subdomains_lc[2].startswith("svloop") or req.subdomains_lc[3].startswith("svloop")):
elif req.subdomains[0][0:1] == "_" and (req.subdomains_lc[1].startswith("svloop") or req.subdomains_lc[2].startswith("svloop") or (len(req.subdomains_lc) > 3 and req.subdomains_lc[3].startswith("svloop"))):
# Do an alias loop in a SVCB record (SvcPriority 0) for any domain name with attribute leaves
# (domains prefixed with an underscore), up to 3 levels e.g., '_sub._service._proto.svloop...'
Expand Down
4 changes: 2 additions & 2 deletions polardns.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import time
import os

polardns_version = "1.4"
polardns_version = "1.5.0"

################################

Expand Down Expand Up @@ -1361,7 +1361,7 @@ def process_DNS(self, req):
time.sleep(resp.sleep)
close_conn(self)
#####################################################################
elif req.sld_tld_domain not in OURDOMAINS and req.sld_tld_domain != "in-addr.arpa":
elif req.sld_tld_domain not in OURDOMAINS and req.tld != "arpa":
# We are NOT authoritative, send Refused
log("Refused")
### DNS header ########
Expand Down
Loading

0 comments on commit 05ec4b3

Please sign in to comment.