-# Copyright (C) 2001-2007, 2009-2011 Nominum, Inc.
+# Copyright (C) 2001-2017 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
class Renderer(object):
-
"""Helper class for building DNS wire-format messages.
Most applications can use the higher-level L{dns.message.Message}
r.add_tsig(keyname, secret, 300, 1, 0, '', request_mac)
wire = r.get_wire()
- @ivar output: where rendering is written
- @type output: BytesIO object
- @ivar id: the message id
- @type id: int
- @ivar flags: the message flags
- @type flags: int
- @ivar max_size: the maximum size of the message
- @type max_size: int
- @ivar origin: the origin to use when rendering relative names
- @type origin: dns.name.Name object
- @ivar compress: the compression table
- @type compress: dict
- @ivar section: the section currently being rendered
- @type section: int (dns.renderer.QUESTION, dns.renderer.ANSWER,
- dns.renderer.AUTHORITY, or dns.renderer.ADDITIONAL)
- @ivar counts: list of the number of RRs in each section
- @type counts: int list of length 4
- @ivar mac: the MAC of the rendered message (if TSIG was used)
- @type mac: string
+ output, a BytesIO, where rendering is written
+
+ id: the message id
+
+ flags: the message flags
+
+ max_size: the maximum size of the message
+
+ origin: the origin to use when rendering relative names
+
+ compress: the compression table
+
+ section: an int, the section currently being rendered
+
+ counts: list of the number of RRs in each section
+
+ mac: the MAC of the rendered message (if TSIG was used)
"""
def __init__(self, id=None, flags=0, max_size=65535, origin=None):
- """Initialize a new renderer.
-
- @param id: the message id
- @type id: int
- @param flags: the DNS message flags
- @type flags: int
- @param max_size: the maximum message size; the default is 65535.
- If rendering results in a message greater than I{max_size},
- then L{dns.exception.TooBig} will be raised.
- @type max_size: int
- @param origin: the origin to use when rendering relative names
- @type origin: dns.name.Name or None.
- """
+ """Initialize a new renderer."""
self.output = BytesIO()
if id is None:
self.mac = ''
def _rollback(self, where):
- """Truncate the output buffer at offset I{where}, and remove any
+ """Truncate the output buffer at offset *where*, and remove any
compression table entries that pointed beyond the truncation
point.
-
- @param where: the offset
- @type where: int
"""
self.output.seek(where)
Sections must be rendered order: QUESTION, ANSWER, AUTHORITY,
ADDITIONAL. Sections may be empty.
- @param section: the section
- @type section: int
- @raises dns.exception.FormError: an attempt was made to set
+ Raises dns.exception.FormError if an attempt was made to set
a section value less than the current section.
"""
self.section = section
def add_question(self, qname, rdtype, rdclass=dns.rdataclass.IN):
- """Add a question to the message.
-
- @param qname: the question name
- @type qname: dns.name.Name
- @param rdtype: the question rdata type
- @type rdtype: int
- @param rdclass: the question rdata class
- @type rdclass: int
- """
+ """Add a question to the message."""
self._set_section(QUESTION)
before = self.output.tell()
Any keyword arguments are passed on to the rdataset's to_wire()
routine.
-
- @param section: the section
- @type section: int
- @param rrset: the rrset
- @type rrset: dns.rrset.RRset object
"""
self._set_section(section)
Any keyword arguments are passed on to the rdataset's to_wire()
routine.
-
- @param section: the section
- @type section: int
- @param name: the owner name
- @type name: dns.name.Name object
- @param rdataset: the rdataset
- @type rdataset: dns.rdataset.Rdataset object
"""
self._set_section(section)
self.counts[section] += n
def add_edns(self, edns, ednsflags, payload, options=None):
- """Add an EDNS OPT record to the message.
-
- @param edns: The EDNS level to use.
- @type edns: int
- @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 options: The EDNS options list
- @type options: list of dns.edns.Option instances
- @see: RFC 2671
- """
+ """Add an EDNS OPT record to the message."""
# make sure the EDNS version in ednsflags agrees with edns
ednsflags &= long(0xFF00FFFF)
def add_tsig(self, keyname, secret, fudge, id, tsig_error, other_data,
request_mac, algorithm=dns.tsig.default_algorithm):
- """Add a TSIG signature to the message.
-
- @param keyname: the TSIG key name
- @type keyname: dns.name.Name object
- @param secret: the secret to use
- @type secret: string
- @param fudge: TSIG time fudge
- @type fudge: int
- @param id: the message id to encode in the tsig signature
- @type id: int
- @param tsig_error: TSIG error code; default is 0.
- @type tsig_error: int
- @param other_data: TSIG other data.
- @type other_data: string
- @param request_mac: This message is a response to the request which
- had the specified MAC.
- @type request_mac: string
- @param algorithm: the TSIG algorithm to use
- @type algorithm: dns.name.Name object
- """
+ """Add a TSIG signature to the message."""
self._set_section(ADDITIONAL)
before = self.output.tell()
self.output.seek(0, 2)
def get_wire(self):
- """Return the wire format message.
-
- @rtype: string
- """
+ """Return the wire format message."""
return self.output.getvalue()
-# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
+# Copyright (C) 2003-2017 Nominum, Inc.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
class UngetBufferFull(dns.exception.DNSException):
-
"""An attempt was made to unget a token when the unget buffer was full."""
class Token(object):
-
"""A DNS master file format token.
- @ivar ttype: The token type
- @type ttype: int
- @ivar value: The token value
- @type value: string
- @ivar has_escape: Does the token value contain escapes?
- @type has_escape: bool
+ ttype: The token type
+ value: The token value
+ has_escape: Does the token value contain escapes?
"""
def __init__(self, ttype, value='', has_escape=False):
- """Initialize a token instance.
-
- @param ttype: The token type
- @type ttype: int
- @param value: The token value
- @type value: string
- @param has_escape: Does the token value contain escapes?
- @type has_escape: bool
- """
+ """Initialize a token instance."""
+
self.ttype = ttype
self.value = value
self.has_escape = has_escape
class Tokenizer(object):
-
"""A DNS master file format tokenizer.
- A token is a (type, value) tuple, where I{type} is an int, and
- I{value} is a string. The valid types are EOF, EOL, WHITESPACE,
- IDENTIFIER, QUOTED_STRING, COMMENT, and DELIMITER.
-
- @ivar file: The file to tokenize
- @type file: file
- @ivar ungotten_char: The most recently ungotten character, or None.
- @type ungotten_char: string
- @ivar ungotten_token: The most recently ungotten token, or None.
- @type ungotten_token: (int, string) token tuple
- @ivar multiline: The current multiline level. This value is increased
+ A token object is basically a (type, value) tuple. The valid
+ types are EOF, EOL, WHITESPACE, IDENTIFIER, QUOTED_STRING,
+ COMMENT, and DELIMITER.
+
+ file: The file to tokenize
+
+ ungotten_char: The most recently ungotten character, or None.
+
+ ungotten_token: The most recently ungotten token, or None.
+
+ multiline: The current multiline level. This value is increased
by one every time a '(' delimiter is read, and decreased by one every time
a ')' delimiter is read.
- @type multiline: int
- @ivar quoting: This variable is true if the tokenizer is currently
+
+ quoting: This variable is true if the tokenizer is currently
reading a quoted string.
- @type quoting: bool
- @ivar eof: This variable is true if the tokenizer has encountered EOF.
- @type eof: bool
- @ivar delimiters: The current delimiter dictionary.
- @type delimiters: dict
- @ivar line_number: The current line number
- @type line_number: int
- @ivar filename: A filename that will be returned by the L{where} method.
- @type filename: string
+
+ eof: This variable is true if the tokenizer has encountered EOF.
+
+ delimiters: The current delimiter dictionary.
+
+ line_number: The current line number
+
+ filename: A filename that will be returned by the where() method.
"""
def __init__(self, f=sys.stdin, filename=None):
"""Initialize a tokenizer instance.
- @param f: The file to tokenize. The default is sys.stdin.
+ f: The file to tokenize. The default is sys.stdin.
This parameter may also be a string, in which case the tokenizer
will take its input from the contents of the string.
- @type f: file or string
- @param filename: the name of the filename that the L{where} method
+
+ filename: the name of the filename that the where() method
will return.
- @type filename: string
"""
if isinstance(f, text_type):
def _get_char(self):
"""Read a character from input.
- @rtype: string
"""
if self.ungotten_char is None:
def where(self):
"""Return the current location in the input.
- @rtype: (string, int) tuple. The first item is the filename of
+ Returns a (string, int) tuple. The first item is the filename of
the input, the second is the current line number.
"""
an error to try to unget a character when the unget buffer is not
empty.
- @param c: the character to unget
- @type c: string
- @raises UngetBufferFull: there is already an ungotten char
+ c: the character to unget
+ raises UngetBufferFull: there is already an ungotten char
"""
if self.ungotten_char is not None:
If the tokenizer is in multiline mode, then newlines are whitespace.
- @rtype: int
+ Returns the number of characters skipped.
"""
skipped = 0
def get(self, want_leading=False, want_comment=False):
"""Get the next token.
- @param want_leading: If True, return a WHITESPACE token if the
+ want_leading: If True, return a WHITESPACE token if the
first character read is whitespace. The default is False.
- @type want_leading: bool
- @param want_comment: If True, return a COMMENT token if the
+
+ want_comment: If True, return a COMMENT token if the
first token read is a comment. The default is False.
- @type want_comment: bool
- @rtype: Token object
- @raises dns.exception.UnexpectedEnd: input ended prematurely
- @raises dns.exception.SyntaxError: input was badly formed
+
+ Raises dns.exception.UnexpectedEnd: input ended prematurely
+
+ Raises dns.exception.SyntaxError: input was badly formed
+
+ Returns a Token.
"""
if self.ungotten_token is not None:
an error to try to unget a token when the unget buffer is not
empty.
- @param token: the token to unget
- @type token: Token object
- @raises UngetBufferFull: there is already an ungotten token
+ token: the token to unget
+
+ Raises UngetBufferFull: there is already an ungotten token
"""
if self.ungotten_token is not None:
def next(self):
"""Return the next item in an iteration.
- @rtype: (int, string)
+
+ Returns a Token.
"""
token = self.get()
def get_int(self):
"""Read the next token and interpret it as an integer.
- @raises dns.exception.SyntaxError:
- @rtype: int
+ Raises dns.exception.SyntaxError if not an integer.
+
+ Returns an int.
"""
token = self.get().unescape()
"""Read the next token and interpret it as an 8-bit unsigned
integer.
- @raises dns.exception.SyntaxError:
- @rtype: int
+ Raises dns.exception.SyntaxError if not an 8-bit unsigned integer.
+
+ Returns an int.
"""
value = self.get_int()
"""Read the next token and interpret it as a 16-bit unsigned
integer.
- @raises dns.exception.SyntaxError:
- @rtype: int
+ Raises dns.exception.SyntaxError if not a 16-bit unsigned integer.
+
+ Returns an int.
"""
value = self.get_int()
"""Read the next token and interpret it as a 32-bit unsigned
integer.
- @raises dns.exception.SyntaxError:
- @rtype: int
+ Raises dns.exception.SyntaxError if not a 32-bit unsigned integer.
+
+ Returns an int.
"""
token = self.get().unescape()
def get_string(self, origin=None):
"""Read the next token and interpret it as a string.
- @raises dns.exception.SyntaxError:
- @rtype: string
+ Raises dns.exception.SyntaxError if not a string.
+
+ Returns a string.
"""
token = self.get().unescape()
return token.value
def get_identifier(self, origin=None):
- """Read the next token and raise an exception if it is not an identifier.
+ """Read the next token, which should be an identifier.
+
+ Raises dns.exception.SyntaxError if not an identifier.
- @raises dns.exception.SyntaxError:
- @rtype: string
+ Returns a string.
"""
token = self.get().unescape()
def get_name(self, origin=None):
"""Read the next token and interpret it as a DNS name.
- @raises dns.exception.SyntaxError:
- @rtype: dns.name.Name object"""
+ Raises dns.exception.SyntaxError if not a name.
+
+ Returns a dns.name.Name.
+ """
token = self.get()
if not token.is_identifier():
"""Read the next token and raise an exception if it isn't EOL or
EOF.
- @raises dns.exception.SyntaxError:
- @rtype: string
+ Returns a string.
"""
token = self.get()
return token.value
def get_ttl(self):
+ """Read the next token and interpret it as a DNS TTL.
+
+ Raises dns.exception.SyntaxError or dns.ttl.BadTTL if not an
+ identifier or badly formed.
+
+ Returns an int.
+ """
+
token = self.get().unescape()
if not token.is_identifier():
raise dns.exception.SyntaxError('expecting an identifier')