refactor dnscrypt parse

add test
This commit is contained in:
bgme 2025-02-07 16:50:00 +08:00
parent ee37b9e95e
commit a53b9c7a8e
10 changed files with 608 additions and 540 deletions

91
dnscrypt/parser.py Normal file
View file

@ -0,0 +1,91 @@
def props(b: bytes, i: int):
'''
``props`` is a little-endian 64 bit value that represents informal properties about the resolver. It is a logical OR
combination of the following values:
- ``1``: the server supports DNSSEC
- ``2``: the server doesnt keep logs
- ``4``: the server doesnt intentionally block domains
For example, a server that supports DNSSEC, stores logs, but doesnt block anything on its own should set ``props``
as the following 8 bytes sequence: ``[ 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]``.
'''
props = b[i]
dnssec = not not ((props >> 0) & 1)
nolog = not not ((props >> 1) & 1)
nofilter = not not ((props >> 2) & 1)
i = i + 8
return i, dnssec, nolog, nofilter
def LP(b: bytes, i: int):
'''
``a || b`` is the concatenation of ``a`` and ``b``
``len(x)`` is a single byte representation of the length of ``x``, in bytes. Strings dont have to be zero-terminated
and do not require invidual encoding.
``LP(x)`` is ``len(x) || x``, i.e ``x`` prefixed by its length.
'''
x_len = b[i]
i = i + 1
x = b[i:i + x_len]
i = i + x_len
return i, x
def LP_decode(b: bytes, i: int, encoding='utf-8'):
i, x = LP(b, i)
return i, x.decode(encoding)
def LP_pk(b: bytes, i: int):
i, x = LP(b, i)
if len(x) != 32:
raise ValueError("LP(pk)")
hpk = x.hex().upper()
hpks = []
for j in range(0, 16):
hpks.append(hpk[j * 4: j * 4 + 4])
pk = ":".join(hpks)
return i, pk
def VLP(b: bytes, i: int):
'''
``a | b`` is the result of the logical ``OR`` operation between ``a`` and ``b``.
``vlen(x)`` is equal to ``len(x)`` if ``x`` is the last element of a set, and ``0x80 | len(x)`` if there are more
elements in the set.
``VLP(x1, x2, ...xn)`` encodes a set, as ``vlen(x1) || x1 || vlen(x2) || x2 ... || vlen(xn) || xn``.
Since ``vlen(xn) == len(xn)`` (length of the last element doesnt have the high bit set), for a set with a single
element, we have ``VLP(x) == LP(x)``.
'''
xx = []
last_element = False
while True:
if b[i] & 0x80 == 0:
last_element = True
x_len = b[i]
else:
x_len = b[i] ^ 0x80
i = i + 1
if x_len > 0:
x = b[i:i + x_len]
xx.append(x)
i = i + x_len
if last_element:
return i, xx
def VLP_hashs(b: bytes, i: int):
i, xx = VLP(b, i)
hashs = list(map(lambda x: x.hex(), xx))
return i, hashs
def VLP_bootstrap_ipi(b: bytes, i: int):
i, xx = VLP(b, i)
bootstrap_ipi = list(map(lambda x: x.decode("utf-8"), xx))
return i, bootstrap_ipi