This is a bugfix version.
cs-parse-traffic_V0_0_6.zip (http)MD5: AED53E99D7BFF14EC45F573663A91780
SHA256: C73614FD69660C4D0E851414D86091E9E90DE9A92D58F9E6AC71D76B4A6EC638
This is a bugfix version.
cs-parse-traffic_V0_0_6.zip (http)Here are the tools I used to conduct my “Bytes over DNS” tests.
On the server side, I start my dnsresolver.py program with the following custom script:
LOGFILENAME = 'bod-dnsresolver-test.log'
def BoDTest(request, reply, dCommand):
if request.q.qtype == dnslib.QTYPE.A:
if len(request.q.qname.label[2]) == 1 and int(request.q.qname.label[1].decode(), 16) == ord(request.q.qname.label[2]):
with open(LOGFILENAME, 'a') as fOut:
print(f'BYTE_EQUAL {request.q.qname.label[1]} {request.q.qname.label[2]}', file=fOut)
qname = request.q.qname
answer = '. 60 IN A 127.0.0.1'
for rr in dnslib.RR.fromZone(answer):
a = copy.copy(rr)
a.rname = qname
reply.add_answer(a)
return False, None
else:
with open(LOGFILENAME, 'a') as fOut:
print(f'BYTE_DIFFERENT {request.q.qname.label[1]} {request.q.qname.label[2]}', file=fOut)
return True, None
Start it as follows: dnsresolver.py -s bod-dnsresolver-test.py type=resolve,label=bytes,function=BoDTest
And make sure your DNS glue records (e.g., for mydomain.com) point to your server.
Then you can do a small test: nslookup bytes.3D.=.mydomain.com.
This will return 127.0.0.1 when the request arrives unaltered, and NXDOMAIN when it is altered. The BoDTest function will also log the results in text file bod-dnsresolver-test.log.
Then, on your workstation, you can run the following script to test all bytes values in the DNS request via the API of your OS:
#!/usr/bin/env python3
import socket
import sys
DOMAIN = '.mydomain.com.'
def DNSResolveA(char: int):
hostname_ascii = 'bytes.%02x.%s' % (char, chr(char)) + DOMAIN
hostname_ascii = hostname_ascii.replace('\\', '\\\\')
print(hostname_ascii)
try:
results = socket.getaddrinfo(hostname_ascii, None, family=socket.AF_INET, type=0, proto=0, flags=socket.AI_CANONNAME)
except socket.gaierror as e:
print(f"Resolution failed: {e}")
return 1
except UnicodeError as e:
print(f"Resolution failed: {e}")
return 1
if not results:
print("No results returned by getaddrinfo.")
return 0
# Collect canonical name (may be empty) and addresses
canon_names = set()
addresses = []
for res in results:
family, socktype, proto, canonname, sockaddr = res
if canonname:
canon_names.add(canonname)
# sockaddr is a tuple; for IPv4 it's (addr, port), for IPv6 it's (addr, port, flowinfo, scopeid)
ip = sockaddr[0]
addresses.append((family, ip))
if canon_names:
print("Canonical name(s):")
for cn in sorted(canon_names):
print(" -", cn)
print()
# Deduplicate and group by family
unique_ips = {}
for fam, ip in addresses:
fam_name = "IPv4" if fam == socket.AF_INET else ("IPv6" if fam == socket.AF_INET6 else str(fam))
unique_ips.setdefault(fam_name, set()).add(ip)
for fam_name in sorted(unique_ips.keys()):
print(f"{fam_name} addresses ({len(unique_ips[fam_name])}):")
for ip in sorted(unique_ips[fam_name]):
print(" -", ip)
print()
# Optionally, try reverse DNS for each IP (may be slow / not always available)
print("Reverse DNS (PTR) lookups:")
for fam_name, ips in unique_ips.items():
for ip in sorted(ips):
try:
host, aliases, _ = socket.gethostbyaddr(ip)
print(f" {ip} -> {host}")
except Exception as e:
print(f" {ip} -> (no PTR) [{e}]")
return 0
if __name__ == "__main__":
for char in range(256):
DNSResolveA(char)
Use this script to perform the tests via the dnspython/dns.resolver Python module:
import dns.resolver
resolver = dns.resolver.Resolver()
DOMAIN = b'.mydomain.com.'
#resolver.nameservers = ['127.0.0.1']
#resolver.nameservers = ['1.1.1.1']
resolver.nameservers = ['8.8.8.8']
for i in range(256):
if i == 0x2E:
continue
if i == 0x5C:
byte = b'\\\\'
else:
byte = bytes([i])
try:
answer = resolver.resolve(((b'bytes.%02x.%s' + DOMAIN) % (i, byte)).decode('latin'), "A")
for rdata in answer:
print(i, rdata.to_text())
except (dns.name.LabelTooLong, dns.resolver.NXDOMAIN) as e:
print(i, e)
And use this script to perform the tests by crafting your own DNS packets:
import socket
DOMAIN = b'mydomain.com.'
DNS = '1.1.1.1'
DNS = '8.8.8.8'
def send_udp_payload(data: bytes, target_ip: str, port: int = 53) -> None:
"""
Send raw binary data via UDP to a target IP and port (default 53).
:param data: The binary payload to send (must be bytes).
:param target_ip: The destination IP address (string).
:param port: Destination UDP port (default = 53).
"""
# Create UDP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
sock.sendto(data, (target_ip, port))
print(f"Sent {len(data)} bytes to {target_ip}:{port}")
except Exception as e:
print(f"Error sending data: {e}")
finally:
sock.close()
def DNSEncodeDomain(domain):
labels = domain.split(b'.')
if labels[-1] != b'':
labels.append(b'')
data = bytearray()
for label in labels:
data += bytes([len(label)])
data += label
return data
data = bytearray([0x88, 0xea, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x02, 0x32, 0x65, 0x01, 0x2e]) + DNSEncodeDomain(DOMAIN) + bytearray([0x00, 0x01, 0x00, 0x01])
for i in range(256):
data[1] = i
data[22] = i
hexvalue = b'%02x' % i
data[19:21] = hexvalue
print(data)
send_udp_payload(data, DNS)
This update brings function= to the definition of a resolve command.
Key-value pair function is optional. If provided, the value will be interpreted as a Python function and called when there is a match. The function must inspect the request and update the reply. Arguments to the function are request, reply and dCommand. The function must return a list with 2 values: first one is True when NXDOMAIN must be returned (and False if there is an answer), second one is an integer with the rcode value, it must be None if there is no rcode set by the function.
An extra Python script (for example with function definitions) can be loaded using option -s.
dnsresolver_V0_0_4.zip (http)While attending a great presentation of Kaitai Struct at Hack.lu 2025, I noticed a binary numeric notation during the demo, that I had never seen before. Something like 0b1000_0001.
I’m familiar with notations in Python like 0b10000001, but not with an underscore thrown in to make the number more readable.
Turns out this exists for almost 10 years in Python (since Python 3.6), and that it is known as PEP 515 – Underscores in Numeric Literals.
And it exists in other programming languages too.
This is a bugfix version.
pdf-parser_V0_7_13.zip (http)In blog post “My Fridge & My Portable Power Station” I managed to get a maximum of 778 Wh out of my portable power station with a rated capacity of 1260 Wh.
Thinking that quite some power got lost in the AC inverter, I set out to measure the amount of power I can get with its 12V DC output.
I configured my electronic load to draw 5 A:

The portable power station displayed 65 W output:

And I only got 673 Wh out of it:

That’s even worse than for 230V with the inverter: 53% of the rated capacity.