68 lines
2.3 KiB
Python
68 lines
2.3 KiB
Python
"""One household device's DNS traffic, stdlib only.
|
|
|
|
usage: traffic.py PROFILE bulk send the profile's lookup budget fast, then exit
|
|
traffic.py PROFILE live MEAN_S one lookup every ~MEAN_S seconds, forever
|
|
"""
|
|
import os
|
|
import random
|
|
import socket
|
|
import struct
|
|
import sys
|
|
import time
|
|
|
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
from profiles import PROFILES # noqa: E402
|
|
|
|
SERVER = (os.environ.get("NXDNS", "192.168.50.2"), 53)
|
|
|
|
|
|
def build_query(qid, name, qtype):
|
|
header = struct.pack("!HHHHHH", qid, 0x0100, 1, 0, 0, 1)
|
|
qname = b"".join(bytes([len(label)]) + label.encode() for label in name.split(".")) + b"\0"
|
|
opt = b"\0" + struct.pack("!HHIH", 41, 1232, 0, 0)
|
|
return header + qname + struct.pack("!HH", qtype, 1) + opt
|
|
|
|
|
|
def pick_types(type_sets):
|
|
sets, weights = zip(*type_sets)
|
|
return random.choices(sets, weights)[0]
|
|
|
|
|
|
def main():
|
|
name, mode = sys.argv[1], sys.argv[2]
|
|
profile = PROFILES[name]
|
|
domains, weights = zip(*profile["domains"])
|
|
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
sock.settimeout(6)
|
|
sent = answered = failed = 0
|
|
lookups = profile["lookups"] if mode == "bulk" else None
|
|
mean = float(sys.argv[3]) if mode == "live" else 0.0
|
|
done = 0
|
|
while lookups is None or done < lookups:
|
|
domain = random.choices(domains, weights)[0]
|
|
for qtype in pick_types(profile["types"]):
|
|
qid = random.randrange(65536)
|
|
sock.sendto(build_query(qid, domain, qtype), SERVER)
|
|
sent += 1
|
|
try:
|
|
while True:
|
|
data, _ = sock.recvfrom(4096)
|
|
if struct.unpack("!H", data[:2])[0] == qid:
|
|
answered += 1
|
|
break
|
|
except socket.timeout:
|
|
failed += 1
|
|
print(f"{name}: no answer for {domain} type {qtype}", flush=True)
|
|
done += 1
|
|
if mode == "bulk":
|
|
time.sleep(random.uniform(0.05, 0.25))
|
|
else:
|
|
time.sleep(random.expovariate(1.0 / mean))
|
|
if done % 500 == 0:
|
|
print(f"{name}: {done} lookups, {sent} sent, {answered} answered, {failed} unanswered", flush=True)
|
|
print(f"{name}: finished {done} lookups, {sent} sent, {answered} answered, {failed} unanswered", flush=True)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|