-
Notifications
You must be signed in to change notification settings - Fork 13
/
Copy pathsetptr
executable file
·136 lines (115 loc) · 4.26 KB
/
setptr
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#!/usr/bin/env python3
# setptr -- change rDNS PTR records using DNS UPDATE
import argparse
import dns.name
import dns.resolver
import ipaddress
import logging
import subprocess
def confirm(text):
return input(f"{text} ").startswith("y")
def chase_one_cname(name):
try:
ans = dns.resolver.resolve(name, "CNAME")
except dns.resolver.NoAnswer:
return name
except dns.resolver.NXDOMAIN:
return name
else:
return ans.rrset[0].target
def send_nsupdate(zone, cmds, *, gssapi=True,
debug=False):
cmds = [f"zone {zone}\n",
*cmds,
f"send\n"]
nsupdate_args = ["nsupdate"]
if debug:
nsupdate_args += ["-d"]
if gssapi:
nsupdate_args += ["-g"]
subprocess.run(nsupdate_args,
input="".join(cmds).encode(),
check=True)
parser = argparse.ArgumentParser()
parser.add_argument("-e", "--edit", action="store_true",
help="interactively edit the zone")
parser.add_argument("-z", "--zone",
help="override automatically determined rDNS zone")
parser.add_argument("-l", "--ttl", type=int, default=3600,
help="set the TTL for created records")
parser.add_argument("-n", "--dry-run", action="store_true",
help="only show updates that would be done")
parser.add_argument("-x", "--no-gss", action="store_true",
help="disable Kerberos (GSS-TSIG) authentication")
parser.add_argument("-d", "--debug", action="count", default=0,
help="enable nsupdate debugging")
parser.add_argument("-v", "--verbose", action="store_true",
help="show detailed information")
parser.add_argument("address",
help="IP address to update the PTR for")
parser.add_argument("target", nargs="?",
help="PTR target domain name, or \".\" to remove")
args = parser.parse_args()
logging.basicConfig(level=[logging.INFO, logging.DEBUG][args.verbose],
format="%(message)s")
if args.edit:
if args.target:
exit(f"setptr: target cannot be specified when using interactive mode")
cmd = ["vireverse"]
if args.debug:
cmd += ["--debug"]
if args.verbose:
cmd += ["--verbose"]
if args.no_gss:
cmd += ["--no-gss"]
cmd += [args.address]
exit(subprocess.call(cmd))
else:
if not args.target:
exit(f"setptr: target must be specified when not using interactive mode")
if "/" in args.address:
addrs = ipaddress.ip_network(args.address)
target = dns.name.from_text(args.target)
if addrs.prefixlen < 24:
# Safety check to prevent excessive deletions
exit(f"setptr: address prefix too short")
if str(target) != ".":
exit(f"setptr: prefixes only allowed when removing records")
rnames = [dns.name.from_text(addr.reverse_pointer)
for addr in addrs]
if args.zone:
zone = dns.name.from_text(args.zone)
else:
# Detect IPv4 classless delegations
rname = chase_one_cname(rnames[0])
zone = dns.resolver.zone_for_name(rname)
logging.debug(f"Updating zone \"{zone}\"")
cmds = [f"del {rname} {args.ttl} PTR\n"
for rname in rnames]
print(f"Removing {len(rnames)} PTRs for {addrs}")
if not confirm("Continue?"):
exit("Changes discarded.")
else:
addr = ipaddress.ip_address(args.address)
rname = dns.name.from_text(addr.reverse_pointer)
target = dns.name.from_text(args.target)
if args.zone:
zone = dns.name.from_text(args.zone)
else:
# Detect IPv4 classless delegations
rname = chase_one_cname(rname)
zone = dns.resolver.zone_for_name(rname)
logging.debug(f"Updating zone \"{zone}\"")
if str(target) == ".":
print(f"Removing PTR for [{addr}]")
cmds = [f"del {rname} {args.ttl} PTR\n"]
else:
print(f"Changing PTR for [{addr}] to \"{target}\"")
cmds = [f"del {rname} {args.ttl} PTR\n",
f"add {rname} {args.ttl} PTR {target}\n"]
if args.debug >= 1:
print("Commands:")
print("\t" + "\t".join(cmds), end="")
if not args.dry_run:
send_nsupdate(zone, cmds, gssapi=not args.no_gss,
debug=(args.debug >= 2))