summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAnton Luka Šijanec <anton@sijanec.eu>2024-05-22 01:37:00 +0200
committerAnton Luka Šijanec <anton@sijanec.eu>2024-05-22 01:37:00 +0200
commit2bf4df61facd82a95faed4b5a60abfd64d96760b (patch)
tree1af55a8c5654478d19712bedef19b93a7cb70d39
parentladn8 (diff)
downloadr-2bf4df61facd82a95faed4b5a60abfd64d96760b.tar
r-2bf4df61facd82a95faed4b5a60abfd64d96760b.tar.gz
r-2bf4df61facd82a95faed4b5a60abfd64d96760b.tar.bz2
r-2bf4df61facd82a95faed4b5a60abfd64d96760b.tar.lz
r-2bf4df61facd82a95faed4b5a60abfd64d96760b.tar.xz
r-2bf4df61facd82a95faed4b5a60abfd64d96760b.tar.zst
r-2bf4df61facd82a95faed4b5a60abfd64d96760b.zip
-rwxr-xr-xskripti/zone/axfr.py85
-rwxr-xr-xskripti/zone/update.py174
2 files changed, 259 insertions, 0 deletions
diff --git a/skripti/zone/axfr.py b/skripti/zone/axfr.py
new file mode 100755
index 0000000..fe508f1
--- /dev/null
+++ b/skripti/zone/axfr.py
@@ -0,0 +1,85 @@
+#!/usr/bin/python3
+import dns.zone
+import dns.resolver
+import json
+import sys
+domena = sys.argv[1]
+strežniki = [dns.resolver.resolve(domena, "SOA")[0].mname]
+for i in dns.resolver.resolve(domena, "NS"):
+ strežniki.append(i.target)
+naslovi = []
+for strežnik in strežniki:
+ for i in dns.resolver.resolve(strežnik, "AAAA"):
+ naslovi.append(i.address)
+ for i in dns.resolver.resolve(strežnik, "A"):
+ naslovi.append(i.address)
+for naslov in naslovi: # opcijsko dodaj tule kakšen try catch
+ zone = None
+ zone = dns.zone.from_xfr(dns.query.xfr(naslov, domena))
+ if zone != None:
+ break
+config = None
+try:
+ config = json.loads(b''.join(dns.resolver.resolve("_urejevalnik." + domena, "TXT")[0].strings).decode())
+except dns.resolver.NXDOMAIN:
+ pass
+except json.decoder.JSONDecodeError:
+ pass
+if config == None:
+ berime = """; Dobrodošli v preprost urejevalnik DNS zapisov.
+; Komentarji se shranijo v DNS strežnik in so javni. Morajo biti na samostojnih vrsticah.
+; Te komentarje z navodili lahko izbrišete -- ne bodo se ponovno pojavili.
+; Nove zapise naložite na strežnik z ukazom zone/update.py zonefile.db
+; Zapise prenesete iz strežnika z ukazom zone/axfr.py domena > zonefile.db
+; Prva vrstica je konfiguracijski zapis v JSON obliki. Naslednje podatke lahko spremenite:
+; "t": privzeti TTL, ki se uporabi, če zapis v datoteki nima TTLja
+; "+": koliko naj prištejem serijski številki pred nalaganjem na strežnik
+
+"""
+ config = {"v": 0, "d": domena, "c": {"@ SOA": berime}, "t": 1, "+": 100, "i": {}}
+configout = config.copy()
+del configout["c"]
+del configout["i"]
+print(f"{json.dumps(configout)}")
+for r in zone.iterate_rdatas():
+ if r[0].to_unicode() == "_urejevalnik" or r[2].rdtype in [dns.rdatatype.RRSIG, dns.rdatatype.NSEC, dns.rdatatype.NSEC3, dns.rdatatype.DNSKEY]:
+ continue
+ commentkey = r[0].to_unicode() + " " + r[2].rdtype.name
+ if commentkey in config["c"].keys():
+ print(config["c"][commentkey], end="")
+ del config["c"][commentkey]
+ konec = "\t"
+ if r[0].to_unicode() in config["i"].keys():
+ konec = config["i"][r[0].to_unicode()]
+ print(r[0].to_unicode(), end=konec)
+ if r[1] != config["t"]:
+ print(r[1], end="\t")
+ if r[2].rdclass != dns.rdataclass.IN:
+ print(r[2].rdataclass.name, end="\t")
+ print(r[2].rdtype.name, end="\t")
+ if r[2].rdtype == dns.rdatatype.TXT:
+ prvič = True
+ for string in r[2].strings:
+ if prvič:
+ prvič = False
+ else:
+ print(" ", end="")
+ bajti = b''
+ for char in string:
+ if char < ord(b' '):
+ bajti += b'\\' + ("%03d" % ord(char)).encode()
+ else:
+ bajti += bytes([char])
+ niz = ""
+ for znak in bajti.replace(b'\\', b'\\\\').replace(b'"', b'\\"').decode('utf-8', errors='surrogateescape'):
+ if '\udc80' <= znak <= '\udcff':
+ niz += '\\'+("%03d" % (ord(znak)-0xdc00))
+ else:
+ niz += znak
+
+ print('"' + niz + '"', end="")
+ else:
+ print(r[2].to_text(), end="")
+ print()
+for i in config["c"].items():
+ print(i[1], end="")
diff --git a/skripti/zone/update.py b/skripti/zone/update.py
new file mode 100755
index 0000000..34a4e39
--- /dev/null
+++ b/skripti/zone/update.py
@@ -0,0 +1,174 @@
+#!/usr/bin/python3
+import dns.zone
+import dns.resolver
+import dns.update
+import dns.tsigkeyring
+import json
+import sys
+import math
+with open(sys.argv[1], "r") as db:
+ lines = db.readlines()
+newconfig = json.loads(lines.pop(0)[:-1])
+domena = newconfig["d"]
+strežniki = [dns.resolver.resolve(domena, "SOA")[0].mname]
+naslovi = []
+for strežnik in strežniki:
+ for i in dns.resolver.resolve(strežnik, "AAAA"):
+ naslovi.append(i.address)
+ for i in dns.resolver.resolve(strežnik, "A"):
+ naslovi.append(i.address)
+for naslov in naslovi: # opcijsko dodaj tule kakšen try catch
+ zone = None
+ zone = dns.zone.from_xfr(dns.query.xfr(naslov, domena))
+ if zone != None:
+ break
+config = None
+try:
+ config = json.loads(b''.join(dns.resolver.resolve("_urejevalnik." + domena, "TXT")[0].strings).decode())
+except dns.resolver.NXDOMAIN:
+ pass
+except json.decoder.JSONDecodeError:
+ pass
+if config == None:
+ config = {"v": 0, "d": domena, "c": {}, "t": 1, "+": 100, "i": {}}
+rrs = []
+for r in zone.iterate_rdatas():
+ if r[0].to_unicode() == "_urejevalnik" or r[2].rdtype in [dns.rdatatype.RRSIG, dns.rdatatype.NSEC, dns.rdatatype.NSEC3, dns.rdatatype.DNSKEY]:
+ continue
+ commentkey = r[0].to_unicode() + " " + r[2].rdtype.name
+ komentar = ""
+ if commentkey in config["c"].keys():
+ komentar = config["c"][commentkey]
+ del config["c"][commentkey]
+ konec = "\t"
+ if r[0].to_unicode() in config["i"].keys():
+ konec = config["i"][r[0].to_unicode()]
+ vrednost = ""
+ if r[2].rdtype == dns.rdatatype.TXT:
+ for string in r[2].strings:
+ bajti = b''
+ for char in string:
+ if char < ord(b' '):
+ bajti += b'\\' + ("%03d" % ord(char)).encode()
+ else:
+ bajti += bytes([char])
+ niz = ""
+ for znak in bajti.replace(b'\\', b'\\\\').replace(b'"', b'\\"').decode('utf-8', errors='surrogateescape'):
+ if '\udc80' <= znak <= '\udcff':
+ niz += '\\'+("%03d" % (ord(znak)-0xdc00))
+ else:
+ niz += znak
+ vrednost = '"' + niz + '"'
+ else:
+ vrednost = r[2].to_text()
+ rrs.append((r[1], r[0].to_unicode(), komentar, konec, r[2].rdclass, r[2].rdtype, vrednost))
+komentar = ""
+lineno = 1
+novikomentarji = {}
+novikonci = {}
+keyring = None
+plus = 0
+minus = 0
+if len(sys.argv) == 3:
+ with open(sys.argv[2]) as file:
+ ključ = file.read()
+ keyring = dns.tsigkeyring.from_text({ključ.split()[ključ.split().index("key")+1].replace('"', "").replace(";", ""): ključ.split()[ključ.split().index("secret")+1].replace('"', "").replace(";", "")})
+update = dns.update.Update(domena, keyring=keyring)
+while True:
+ try:
+ lineno += 1
+ line = lines.pop(0)[:-1] # odstranimo zadnji \n, ki ga zraven da .readlines
+ if len(line.split()) == 0 or line[0] == ';' or line[0] == '#' or line[0] == '/':
+ komentar += line + "\n"
+ continue
+ ime = line.split()[0]
+ konec = ""
+ index = len(ime)
+ while line[index] in ["\t", " "]:
+ konec += line[index]
+ index += 1
+ nizi = line.split()[1:]
+ tip = None
+ razred = None
+ ttl = None
+ while tip == None:
+ try:
+ ttl = int(nizi[0])
+ nizi.pop(0)
+ except ValueError:
+ pass
+ try:
+ razred = dns.rdataclass.from_text(nizi[0])
+ nizi.pop(0)
+ except dns.rdataclass.UnknownRdataclass:
+ pass
+ try:
+ tip = dns.rdatatype.from_text(nizi[0])
+ for i in [" ", "\t"]:
+ for j in [" ", "\t"]:
+ try:
+ datastart = line.index(i+nizi[0]+j)+len(i+nizi[0]+i)
+ except ValueError:
+ continue
+ break
+ else:
+ continue
+ break
+ nizi.pop(0)
+ except dns.rdatatype.UnknownRdatatype:
+ pass
+ if tip == None:
+ print(f"NAPAKA: na vrstici {lineno} ne najdem tipa zapisa. Vrstica je lahko bodisi komentar, ki se začne z ';', bodisi je v obliki IME [TTL={newconfig["t"]}] [CLASS=IN] TIP PODATKI.")
+ print(f"Vsebina neveljavne vrstice: " + line)
+ sys.exit(1)
+ while line[datastart] in [" ", "\t"]:
+ datastart += 1
+ data = line[datastart:]
+ if razred == None:
+ razred = dns.rdataclass.IN
+ if ttl == None:
+ ttl = newconfig["t"]
+ ime = dns.name.from_unicode(ime, dns.name.from_unicode(domena)).choose_relativity(dns.name.from_unicode(domena), True).to_unicode()
+ if tip == dns.rdatatype.SOA:
+ data = data.split()
+ data[2] = str(int(data[2])+newconfig["+"])
+ data = " ".join(data)
+ tapl = (ttl, ime, komentar, konec, razred, tip, data)
+ if komentar != "":
+ novikomentarji[ime + " " + tip.to_text(tip)] = komentar
+ if konec != "\t":
+ novikonci[ime] = konec
+ if not tapl in rrs:
+ print("+ " + komentar.replace("\n", "\n+ ") + ime + konec + str(ttl) + "\t" + razred.to_text(razred) + "\t" + tip.to_text(tip) + "\t" + data)
+ plus += 1
+ update.add(ime, ttl, tip, data)
+ else:
+ rrs.remove(tapl)
+ komentar = ""
+ except IndexError:
+ break
+obstoječ = "" # zadnji komentar
+for komentar in config["c"].values():
+ obstoječ += komentar
+for rr in rrs:
+ print("- " + komentar.replace("\n", "\n- ") + rr[1] + konec + str(rr[0]) + "\t" + rr[4].to_text(rr[4]) + "\t" + rr[5].to_text(rr[5]) + "\t" + rr[6])
+ minus += 1
+ update.delete(rr[1], rr[5].to_text(rr[5]), rr[6])
+if obstoječ != komentar:
+ print("- " + "\n+ ".join(obstoječ.split("\n")))
+ print("+ " + "\n+ ".join(komentar.split("\n")))
+ plus += 1
+ minus += 1
+novikomentarji["z"] = komentar
+newconfig["c"] = novikomentarji
+newconfig["i"] = novikonci
+odziv = input(f"(-{minus}/+{plus}) Ali želite te spremembe poslati na strežnik? [Y/D/J/n] ")
+if len(odziv) != 0 and odziv[0] in ["n", "N", "0", "f", "F"]:
+ print("Prekinjam. Nasvidenje!")
+ sys.exit(0)
+jason = json.dumps(newconfig)
+jasonsplit = " ".join(['"' + jason[i*255:i*255+255].replace("\\", "\\\\").replace('"', '\\"') + '"' for i in range(math.ceil(len(jason)/255))])
+update.replace("_urejevalnik", 1, dns.rdatatype.TXT, jasonsplit)
+response = dns.query.tcp(update, naslov)
+print("Poslal zahtevo. Odziv strežnika:")
+print(response)