| 313 | |
| 314 | class SOA(DNSResponse): |
| 315 | def __init__(self, query, config_location): |
| 316 | super(SOA, self).__init__(query) |
| 317 | |
| 318 | # TODO: pre-read and cache all the config files for the rules for speed. |
| 319 | config = ConfigParser.ConfigParser(inline_comment_prefixes=";") |
| 320 | config.read(config_location) |
| 321 | |
| 322 | # handle cases where we want the serial to be random |
| 323 | serial = config.get(query.domain.decode(), "serial") |
| 324 | if serial.lower() == "random": |
| 325 | serial = int(random.getrandbits(32)) |
| 326 | else: |
| 327 | # serial is still a str, cast to int. |
| 328 | serial = int(serial) |
| 329 | |
| 330 | self.type = b"\x00\x06" |
| 331 | self.mname = config.get(query.domain.decode(), "mname") # name server that was original or primary source for this zone |
| 332 | self.rname = config.get(query.domain.decode(), "rname") # domain name which specified mailbox of person responsible for zone |
| 333 | self.serial = serial # 32-bit long version number of the zone copy |
| 334 | self.refresh = config.getint(query.domain.decode(), "refresh")# 32-bit time interval before zone refresh |
| 335 | self.retry = config.getint(query.domain.decode(), "retry") # 32-bit time interval before retrying failed refresh |
| 336 | self.expire = config.getint(query.domain.decode(), "expire") # 32-bit time interval after which the zone is not authoritative |
| 337 | self.minimum = config.getint(query.domain.decode(), "minimum")# The unsigned 32 bit minimum TTL for any RR from this zone. |
| 338 | |
| 339 | # convert the config entries into DNS format. Convenient conversion function will be moved up to module later. |
| 340 | def convert(fqdn): |
| 341 | tmp = b"" |
| 342 | for domain in fqdn.split('.'): |
| 343 | tmp += chr(len(domain)).encode() + domain.encode() |
| 344 | tmp += b"\xc0\x0c" |
| 345 | return tmp |
| 346 | |
| 347 | self.data = b"" |
| 348 | |
| 349 | self.mname = convert(self.mname) |
| 350 | self.data += self.mname |
| 351 | |
| 352 | self.rname = convert(self.rname) |
| 353 | self.data += self.rname # already is a bytes object. |
| 354 | |
| 355 | # pack the rest of the structure |
| 356 | self.data += struct.pack('>I', self.serial) |
| 357 | self.data += struct.pack('>I', self.refresh) |
| 358 | self.data += struct.pack('>I', self.retry) |
| 359 | self.data += struct.pack('>I', self.refresh) |
| 360 | self.data += struct.pack('>I', self.minimum) |
| 361 | |
| 362 | # get length of the answers area |
| 363 | self.length = chr(len(self.data)) |
| 364 | |
| 365 | # length is always two bytes - add the extra blank byte if we're not large enough for two bytes. |
| 366 | if self.length < "0xff": |
| 367 | self.length = b"\x00" + self.length.encode() |
| 368 | |
| 369 | |
| 370 | |