def _question_line(self, section): """Process one line from the text format question section.""" token = self.tok.get(want_leading=True) if not token.is_whitespace(): self.last_name = mname.from_text(token.value, None) name = self.last_name token = self.tok.get() if not token.is_identifier(): raise exception.SyntaxError # Class try: rdclass = rdataclass.from_text(token.value) token = self.tok.get() if not token.is_identifier(): raise exception.SyntaxError except exception.SyntaxError: raise exception.SyntaxError except: rdclass = rdataclass.IN # Type rdtype = rdatatype.from_text(token.value) self.message.find_rrset(self.message.question, name, rdclass, rdtype, create=True, force_unique=True) if self.updating: self.zone_rdclass = rdclass self.tok.get_eol()
def _question_line(self, section): """Process one line from the text format question section.""" token = self.tok.get(want_leading = True) if not token.is_whitespace(): self.last_name = mname.from_text(token.value, None) name = self.last_name token = self.tok.get() if not token.is_identifier(): raise exception.SyntaxError # Class try: rdclass = rdataclass.from_text(token.value) token = self.tok.get() if not token.is_identifier(): raise exception.SyntaxError except exception.SyntaxError: raise exception.SyntaxError except: rdclass = rdataclass.IN # Type rdtype = rdatatype.from_text(token.value) self.message.find_rrset(self.message.question, name, rdclass, rdtype, create=True, force_unique=True) if self.updating: self.zone_rdclass = rdclass self.tok.get_eol()
def make_query(qname, rdtype, rdclass=rdataclass.IN, use_edns=None, want_dnssec=False, ednsflags=0, payload=1280, request_payload=None, options=None): """Make a query message. The query name, type, and class may all be specified either as objects of the appropriate type, or as strings. The query will have a randomly choosen query id, and its DNS flags will be set to flags.RD. @param qname: The query name. @type qname: mname.Name object or string @param rdtype: The desired rdata type. @type rdtype: int @param rdclass: The desired rdata class; the default is class IN. @type rdclass: int @param use_edns: The EDNS level to use; the default is None (no EDNS). See the description of message.Message.use_edns() for the possible values for use_edns and their meanings. @type use_edns: int or bool or None @param want_dnssec: Should the query indicate that DNSSEC is desired? @type want_dnssec: bool @param ednsflags: EDNS flag values. @type ednsflags: int @param payload: The EDNS sender's payload field, which is the maximum size of UDP datagram the sender can handle. @type payload: int @param request_payload: The EDNS payload size to use when sending this message. If not specified, defaults to the value of payload. @type request_payload: int or None @param options: The EDNS options @type options: None or list of edns.Option objects @see: RFC 2671 @rtype: message.Message object""" if isinstance(qname, (str, unicode)): qname = mname.from_text(qname) if isinstance(rdtype, (str, unicode)): rdtype = rdatatype.from_text(rdtype) if isinstance(rdclass, (str, unicode)): rdclass = rdataclass.from_text(rdclass) m = Message() m.flags |= flags.RD m.find_rrset(m.question, qname, rdclass, rdtype, create=True, force_unique=True) m.use_edns(use_edns, ednsflags, payload, request_payload, options) m.want_dnssec(want_dnssec) return m
def _rr_line(self, section): """Process one line from the text format answer, authority, or additional data sections. """ deleting = None # Name token = self.tok.get(want_leading = True) if not token.is_whitespace(): self.last_name = mname.from_text(token.value, None) name = self.last_name token = self.tok.get() if not token.is_identifier(): raise exception.SyntaxError # TTL try: ttl = int(token.value, 0) token = self.tok.get() if not token.is_identifier(): raise exception.SyntaxError except exception.SyntaxError: raise exception.SyntaxError except: ttl = 0 # Class try: rdclass = rdataclass.from_text(token.value) token = self.tok.get() if not token.is_identifier(): raise exception.SyntaxError if rdclass == rdataclass.ANY or rdclass == rdataclass.NONE: deleting = rdclass rdclass = self.zone_rdclass except exception.SyntaxError: raise exception.SyntaxError except: rdclass = rdataclass.IN # Type rdtype = rdatatype.from_text(token.value) token = self.tok.get() if not token.is_eol_or_eof(): self.tok.unget(token) rd = rdata.from_text(rdclass, rdtype, self.tok, None) covers = rd.covers() else: rd = None covers = rdatatype.NONE rrset = self.message.find_rrset(section, name, rdclass, rdtype, covers, deleting, True, self.updating) if not rd is None: rrset.add(rd, ttl)
def _rr_line(self, section): """Process one line from the text format answer, authority, or additional data sections. """ deleting = None # Name token = self.tok.get(want_leading=True) if not token.is_whitespace(): self.last_name = mname.from_text(token.value, None) name = self.last_name token = self.tok.get() if not token.is_identifier(): raise exception.SyntaxError # TTL try: ttl = int(token.value, 0) token = self.tok.get() if not token.is_identifier(): raise exception.SyntaxError except exception.SyntaxError: raise exception.SyntaxError except: ttl = 0 # Class try: rdclass = rdataclass.from_text(token.value) token = self.tok.get() if not token.is_identifier(): raise exception.SyntaxError if rdclass == rdataclass.ANY or rdclass == rdataclass.NONE: deleting = rdclass rdclass = self.zone_rdclass except exception.SyntaxError: raise exception.SyntaxError except: rdclass = rdataclass.IN # Type rdtype = rdatatype.from_text(token.value) token = self.tok.get() if not token.is_eol_or_eof(): self.tok.unget(token) rd = rdata.from_text(rdclass, rdtype, self.tok, None) covers = rd.covers() else: rd = None covers = rdatatype.NONE rrset = self.message.find_rrset(section, name, rdclass, rdtype, covers, deleting, True, self.updating) if not rd is None: rrset.add(rd, ttl)
def make_query(qname, rdtype, rdclass = rdataclass.IN, use_edns=None, want_dnssec=False, ednsflags=0, payload=1280, request_payload=None, options=None): """Make a query message. The query name, type, and class may all be specified either as objects of the appropriate type, or as strings. The query will have a randomly choosen query id, and its DNS flags will be set to flags.RD. @param qname: The query name. @type qname: mname.Name object or string @param rdtype: The desired rdata type. @type rdtype: int @param rdclass: The desired rdata class; the default is class IN. @type rdclass: int @param use_edns: The EDNS level to use; the default is None (no EDNS). See the description of message.Message.use_edns() for the possible values for use_edns and their meanings. @type use_edns: int or bool or None @param want_dnssec: Should the query indicate that DNSSEC is desired? @type want_dnssec: bool @param ednsflags: EDNS flag values. @type ednsflags: int @param payload: The EDNS sender's payload field, which is the maximum size of UDP datagram the sender can handle. @type payload: int @param request_payload: The EDNS payload size to use when sending this message. If not specified, defaults to the value of payload. @type request_payload: int or None @param options: The EDNS options @type options: None or list of edns.Option objects @see: RFC 2671 @rtype: message.Message object""" if isinstance(qname, (str, unicode)): qname = mname.from_text(qname) if isinstance(rdtype, (str, unicode)): rdtype = rdatatype.from_text(rdtype) if isinstance(rdclass, (str, unicode)): rdclass = rdataclass.from_text(rdclass) m = Message() m.flags |= flags.RD m.find_rrset(m.question, qname, rdclass, rdtype, create=True, force_unique=True) m.use_edns(use_edns, ednsflags, payload, request_payload, options) m.want_dnssec(want_dnssec) return m
def from_text_list(rdclass, rdtype, ttl, text_rdatas): """Create an rdataset with the specified class, type, and TTL, and with the specified list of rdatas in text format. @rtype: rdataset.Rdataset object """ if isinstance(rdclass, (str, unicode)): rdclass = rdataclass.from_text(rdclass) if isinstance(rdtype, (str, unicode)): rdtype = rdatatype.from_text(rdtype) r = Rdataset(rdclass, rdtype) r.update_ttl(ttl) for t in text_rdatas: rd = rdata.from_text(r.rdclass, r.rdtype, t) r.add(rd) return r
def __init__(self, zone, rdclass=rdataclass.IN, keyring=None, keyname=None, keyalgorithm=tsig.default_algorithm): """Initialize a new DNS Update object. @param zone: The zone which is being updated. @type zone: A name.Name or string @param rdclass: The class of the zone; defaults to rdataclass.IN. @type rdclass: An int designating the class, or a string whose value is the name of a class. @param keyring: The TSIG keyring to use; defaults to None. @type keyring: dict @param keyname: The name of the TSIG key to use; defaults to None. The key must be defined in the keyring. If a keyring is specified but a keyname is not, then the key used will be the first key in the keyring. Note that the order of keys in a dictionary is not defined, so applications should supply a keyname when a keyring is used, unless they know the keyring contains only one key. @type keyname: name.Name or string @param keyalgorithm: The TSIG algorithm to use; defaults to tsig.default_algorithm. Constants for TSIG algorithms are defined in tsig, and the currently implemented algorithms are HMAC_MD5, HMAC_SHA1, HMAC_SHA224, HMAC_SHA256, HMAC_SHA384, and HMAC_SHA512. @type keyalgorithm: string """ super(Update, self).__init__() self.flags |= opcode.to_flags(opcode.UPDATE) if isinstance(zone, (str, unicode)): zone = name.from_text(zone) self.origin = zone if isinstance(rdclass, str): rdclass = rdataclass.from_text(rdclass) self.zone_rdclass = rdclass self.find_rrset(self.question, self.origin, rdclass, rdatatype.SOA, create=True, force_unique=True) if not keyring is None: self.use_tsig(keyring, keyname, algorithm=keyalgorithm)
def query(self, qname, rdtype=rdatatype.A, rdclass=rdataclass.IN, tcp=False, source=None, raise_on_no_answer=True, source_port=0): """Query nameservers to find the answer to the question. The I{qname}, I{rdtype}, and I{rdclass} parameters may be objects of the appropriate type, or strings that can be converted into objects of the appropriate type. E.g. For I{rdtype} the integer 2 and the the string 'NS' both mean to query for records with DNS rdata type NS. @param qname: the query name @type qname: name.Name object or string @param rdtype: the query type @type rdtype: int or string @param rdclass: the query class @type rdclass: int or string @param tcp: use TCP to make the query (default is False). @type tcp: bool @param source: bind to this IP address (defaults to machine default IP). @type source: IP address in dotted quad notation @param raise_on_no_answer: raise NoAnswer if there's no answer (defaults is True). @type raise_on_no_answer: bool @param source_port: The port from which to send the message. The default is 0. @type source_port: int @rtype: resolver.Answer instance @raises Timeout: no answers could be found in the specified lifetime @raises NXDOMAIN: the query name does not exist @raises YXDOMAIN: the query name is too long after DNAME substitution @raises NoAnswer: the response did not contain an answer and raise_on_no_answer is True. @raises NoNameservers: no non-broken nameservers are available to answer the question.""" #import pdb #pdb.set_trace() if isinstance(qname, (str, unicode)): qname = name.from_text(qname, None) if isinstance(rdtype, (str, unicode)): rdtype = rdatatype.from_text(rdtype) if rdatatype.is_metatype(rdtype): raise NoMetaqueries if isinstance(rdclass, (str, unicode)): rdclass = rdataclass.from_text(rdclass) if rdataclass.is_metaclass(rdclass): raise NoMetaqueries qnames_to_try = [] if qname.is_absolute(): qnames_to_try.append(qname) else: if len(qname) > 1: qnames_to_try.append(qname.concatenate(name.root)) if self.search: for suffix in self.search: qnames_to_try.append(qname.concatenate(suffix)) else: qnames_to_try.append(qname.concatenate(self.domain)) all_nxdomain = True start = time.time() for qname in qnames_to_try: if self.cache: answer = self.cache.get((qname, rdtype, rdclass)) if not answer is None: if answer.rrset is None and raise_on_no_answer: raise NoAnswer else: return answer request = message.make_query(qname, rdtype, rdclass) if not self.keyname is None: request.use_tsig(self.keyring, self.keyname, algorithm=self.keyalgorithm) request.use_edns(self.edns, self.ednsflags, self.payload) if self.flags is not None: request.flags = self.flags response = None # # make a copy of the servers list so we can alter it later. # nameservers = self.nameservers[:] if self.rotate: random.shuffle(nameservers) backoff = 0.10 while response is None: if len(nameservers) == 0: raise NoNameservers for nameserver in nameservers[:]: timeout = self._compute_timeout(start) try: if tcp: response = mquery.tcp(request, nameserver, timeout, self.port, source=source, source_port=source_port) else: response = mquery.udp(request, nameserver, timeout, self.port, source=source, source_port=source_port) if response.flags & flags.TC: # Response truncated; retry with TCP. timeout = self._compute_timeout(start) response = mquery.tcp(request, nameserver, timeout, self.port, source=source, source_port=source_port) except (socket.error, exception.Timeout): # # Communication failure or timeout. Go to the # next server # response = None continue except mquery.UnexpectedSource: # # Who knows? Keep going. # response = None continue except exception.FormError: # # We don't understand what this server is # saying. Take it out of the mix and # continue. # nameservers.remove(nameserver) response = None continue except EOFError: # # We're using TCP and they hung up on us. # Probably they don't support TCP (though # they're supposed to!). Take it out of the # mix and continue. # nameservers.remove(nameserver) response = None continue rcode = response.rcode() if rcode == mrcode.YXDOMAIN: raise YXDOMAIN if rcode == mrcode.NOERROR or \ rcode == mrcode.NXDOMAIN: break # # We got a response, but we're not happy with the # rcode in it. Remove the server from the mix if # the rcode isn't SERVFAIL. # if rcode != mrcode.SERVFAIL or not self.retry_servfail: nameservers.remove(nameserver) response = None if not response is None: break # # All nameservers failed! # if len(nameservers) > 0: # # But we still have servers to try. Sleep a bit # so we don't pound them! # timeout = self._compute_timeout(start) sleep_time = min(timeout, backoff) backoff *= 2 time.sleep(sleep_time) if response.rcode() == mrcode.NXDOMAIN: continue all_nxdomain = False break if all_nxdomain: raise NXDOMAIN answer = Answer(qname, rdtype, rdclass, response, raise_on_no_answer) if self.cache: self.cache.put((qname, rdtype, rdclass), answer) return answer
def _generate_line(self): # range lhs [ttl] [class] type rhs [ comment ] """Process one line containing the GENERATE statement from a DNS master file.""" if self.current_origin is None: raise UnknownOrigin token = self.tok.get() # Range (required) try: start, stop, step = grange.from_text(token.value) token = self.tok.get() if not token.is_identifier(): raise exception.SyntaxError except: raise exception.SyntaxError # lhs (required) try: lhs = token.value token = self.tok.get() if not token.is_identifier(): raise exception.SyntaxError except: raise exception.SyntaxError # TTL try: ttl = ttl.from_text(token.value) token = self.tok.get() if not token.is_identifier(): raise exception.SyntaxError except ttl.BadTTL: ttl = self.ttl # Class try: rdclass = rdataclass.from_text(token.value) token = self.tok.get() if not token.is_identifier(): raise exception.SyntaxError except exception.SyntaxError: raise exception.SyntaxError except: rdclass = self.zone.rdclass if rdclass != self.zone.rdclass: raise exception.SyntaxError("RR class is not zone's class") # Type try: rdtype = rdatatype.from_text(token.value) token = self.tok.get() if not token.is_identifier(): raise exception.SyntaxError except: raise exception.SyntaxError("unknown rdatatype '%s'" % token.value) # lhs (required) try: rhs = token.value except: raise exception.SyntaxError lmod, lsign, loffset, lwidth, lbase = self._parse_modify(lhs) rmod, rsign, roffset, rwidth, rbase = self._parse_modify(rhs) for i in range(start, stop + 1, step): # +1 because bind is inclusive and python is exclusive if lsign == '+': lindex = i + int(loffset) elif lsign == '-': lindex = i - int(loffset) if rsign == '-': rindex = i - int(roffset) elif rsign == '+': rindex = i + int(roffset) lzfindex = str(lindex).zfill(int(lwidth)) rzfindex = str(rindex).zfill(int(rwidth)) name = lhs.replace('$%s' % (lmod), lzfindex) rdata = rhs.replace('$%s' % (rmod), rzfindex) self.last_name = name.from_text(name, self.current_origin) name = self.last_name if not name.is_subdomain(self.zone.origin): self._eat_line() return if self.relativize: name = name.relativize(self.zone.origin) n = self.zone.nodes.get(name) if n is None: n = self.zone.node_factory() self.zone.nodes[name] = n try: rd = rdata.from_text(rdclass, rdtype, rdata, self.current_origin, False) except exception.SyntaxError: # Catch and reraise. (ty, va) = sys.exc_info()[:2] raise va except: # All exceptions that occur in the processing of rdata # are treated as syntax errors. This is not strictly # correct, but it is correct almost all of the time. # We convert them to syntax errors so that we can emit # helpful filename:line info. (ty, va) = sys.exc_info()[:2] raise exception.SyntaxError("caught exception %s: %s" % (str(ty), str(va))) rd.choose_relativity(self.zone.origin, self.relativize) covers = rd.covers() rds = n.find_rdataset(rdclass, rdtype, covers, True) rds.add(rd, ttl)
def _rr_line(self): """Process one line from a DNS master file.""" # Name if self.current_origin is None: raise UnknownOrigin token = self.tok.get(want_leading=True) if not token.is_whitespace(): self.last_name = name.from_text(token.value, self.current_origin) else: token = self.tok.get() if token.is_eol_or_eof(): # treat leading WS followed by EOL/EOF as if they were EOL/EOF. return self.tok.unget(token) name = self.last_name if not name.is_subdomain(self.zone.origin): self._eat_line() return if self.relativize: name = name.relativize(self.zone.origin) token = self.tok.get() if not token.is_identifier(): raise exception.SyntaxError # TTL try: ttl = ttl.from_text(token.value) token = self.tok.get() if not token.is_identifier(): raise exception.SyntaxError except ttl.BadTTL: ttl = self.ttl # Class try: rdclass = rdataclass.from_text(token.value) token = self.tok.get() if not token.is_identifier(): raise exception.SyntaxError except exception.SyntaxError: raise exception.SyntaxError except: rdclass = self.zone.rdclass if rdclass != self.zone.rdclass: raise exception.SyntaxError("RR class is not zone's class") # Type try: rdtype = rdatatype.from_text(token.value) except: raise exception.SyntaxError("unknown rdatatype '%s'" % token.value) n = self.zone.nodes.get(name) if n is None: n = self.zone.node_factory() self.zone.nodes[name] = n try: rd = rdata.from_text(rdclass, rdtype, self.tok, self.current_origin, False) except exception.SyntaxError: # Catch and reraise. (ty, va) = sys.exc_info()[:2] raise va except: # All exceptions that occur in the processing of rdata # are treated as syntax errors. This is not strictly # correct, but it is correct almost all of the time. # We convert them to syntax errors so that we can emit # helpful filename:line info. (ty, va) = sys.exc_info()[:2] raise exception.SyntaxError("caught exception %s: %s" % (str(ty), str(va))) rd.choose_relativity(self.zone.origin, self.relativize) covers = rd.covers() rds = n.find_rdataset(rdclass, rdtype, covers, True) rds.add(rd, ttl)
def _rr_line(self): """Process one line from a DNS master file.""" # Name if self.current_origin is None: raise UnknownOrigin token = self.tok.get(want_leading = True) if not token.is_whitespace(): self.last_name = name.from_text(token.value, self.current_origin) else: token = self.tok.get() if token.is_eol_or_eof(): # treat leading WS followed by EOL/EOF as if they were EOL/EOF. return self.tok.unget(token) name = self.last_name if not name.is_subdomain(self.zone.origin): self._eat_line() return if self.relativize: name = name.relativize(self.zone.origin) token = self.tok.get() if not token.is_identifier(): raise exception.SyntaxError # TTL try: ttl = ttl.from_text(token.value) token = self.tok.get() if not token.is_identifier(): raise exception.SyntaxError except ttl.BadTTL: ttl = self.ttl # Class try: rdclass = rdataclass.from_text(token.value) token = self.tok.get() if not token.is_identifier(): raise exception.SyntaxError except exception.SyntaxError: raise exception.SyntaxError except: rdclass = self.zone.rdclass if rdclass != self.zone.rdclass: raise exception.SyntaxError("RR class is not zone's class") # Type try: rdtype = rdatatype.from_text(token.value) except: raise exception.SyntaxError("unknown rdatatype '%s'" % token.value) n = self.zone.nodes.get(name) if n is None: n = self.zone.node_factory() self.zone.nodes[name] = n try: rd = rdata.from_text(rdclass, rdtype, self.tok, self.current_origin, False) except exception.SyntaxError: # Catch and reraise. (ty, va) = sys.exc_info()[:2] raise va except: # All exceptions that occur in the processing of rdata # are treated as syntax errors. This is not strictly # correct, but it is correct almost all of the time. # We convert them to syntax errors so that we can emit # helpful filename:line info. (ty, va) = sys.exc_info()[:2] raise exception.SyntaxError("caught exception %s: %s" % (str(ty), str(va))) rd.choose_relativity(self.zone.origin, self.relativize) covers = rd.covers() rds = n.find_rdataset(rdclass, rdtype, covers, True) rds.add(rd, ttl)