-# 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,
socket_factory = socket.socket
class UnexpectedSource(dns.exception.DNSException):
-
"""A DNS query response came from an unexpected address or port."""
class BadResponse(dns.exception.FormError):
-
"""A DNS query response does not respond to the question asked."""
else:
return time.time() + timeout
+# This module can use either poll() or select() as the "polling backend".
+#
+# A backend function takes an fd, bools for readability, writablity, and
+# error detection, and a timeout.
def _poll_for(fd, readable, writable, error, timeout):
- """Poll polling backend.
- @param fd: File descriptor
- @type fd: int
- @param readable: Whether to wait for readability
- @type readable: bool
- @param writable: Whether to wait for writability
- @type writable: bool
- @param timeout: Deadline timeout (expiration time, in seconds)
- @type timeout: float
- @return True on success, False on timeout
- """
+ """Poll polling backend."""
+
event_mask = 0
if readable:
event_mask |= select.POLLIN
def _select_for(fd, readable, writable, error, timeout):
- """Select polling backend.
- @param fd: File descriptor
- @type fd: int
- @param readable: Whether to wait for readability
- @type readable: bool
- @param writable: Whether to wait for writability
- @type writable: bool
- @param timeout: Deadline timeout (expiration time, in seconds)
- @type timeout: float
- @return True on success, False on timeout
- """
+ """Select polling backend."""
+
rset, wset, xset = [], [], []
if readable:
def _wait_for(fd, readable, writable, error, expiration):
+ # Use the selected polling backend to wait for any of the specified
+ # events. An "expiration" absolute time is converted into a relative
+ # timeout.
+
done = False
while not done:
if expiration is None:
def _set_polling_backend(fn):
- """
- Internal API. Do not use.
- """
+ # Internal API. Do not use.
+
global _polling_backend
_polling_backend = fn
# Convert the first value of the tuple, which is a textual format
# address into binary form, so that we are not confused by different
# textual representations of the same address
- n1 = dns.inet.inet_pton(af, a1[0])
- n2 = dns.inet.inet_pton(af, a2[0])
+ try:
+ n1 = dns.inet.inet_pton(af, a1[0])
+ n2 = dns.inet.inet_pton(af, a2[0])
+ except dns.exception.SyntaxError:
+ return False
return n1 == n2 and a1[1:] == a2[1:]
def send_udp(sock, what, destination, expiration=None):
"""Send a DNS message to the specified UDP socket.
- @param sock: the socket
- @type sock: socket.socket
- @param what: the message to send
- @type what: wire format bytes or a dns.message.Message
- @param destination: where to send the query
- @type destination: tuple appropriate for the address family of the socket
- @param expiration: The absolute time at which a timeout exception should
- be raised.
- @type expiration: float
- @rtype: (int, double) tuple of bytes sent and the sent time.
+ *sock*, a ``socket``.
+
+ *what*, a ``binary`` or ``dns.message.Message``, the message to send.
+
+ *destination*, a destination tuple appropriate for the address family
+ of the socket, specifying where to send the query.
+
+ *expiration*, a ``float`` or ``None``, the absolute time at which
+ a timeout exception should be raised. If ``None``, no timeout will
+ occur.
+
+ Returns an ``(int, float)`` tuple of bytes sent and the sent time.
"""
+
if isinstance(what, dns.message.Message):
what = what.to_wire()
_wait_for_writable(sock, expiration)
return (n, sent_time)
-def receive_udp(sock, destination, expiration=None, af=None,
+def receive_udp(sock, destination, expiration=None,
ignore_unexpected=False, one_rr_per_rrset=False,
keyring=None, request_mac=b''):
"""Read a DNS message from a UDP socket.
- @param sock: the socket
- @type sock: socket.socket
- @param af: the address family to use. The default is None, which
- causes the address family to use to be inferred from the form of
- destination. If the inference attempt fails, AF_INET is used.
- @type af: int
- @param ignore_unexpected: If True, ignore responses from unexpected
- sources. The default is False.
- @type ignore_unexpected: bool
- @param one_rr_per_rrset: Put each RR into its own RRset
- @type one_rr_per_rrset: bool
- @param keyring: the keyring to use for TSIG
- @type keyring: keyring dict
- @param request_mac: the MAC of the request (for TSIG)
- @type request_mac: bytes
- @rtype: dns.message.Message object
+ *sock*, a ``socket``.
+
+ *destination*, a destination tuple appropriate for the address family
+ of the socket, specifying where the associated query was sent.
+
+ *expiration*, a ``float`` or ``None``, the absolute time at which
+ a timeout exception should be raised. If ``None``, no timeout will
+ occur.
+
+ *ignore_unexpected*, a ``bool``. If ``True``, ignore responses from
+ unexpected sources.
+
+ *one_rr_per_rrset*, a ``bool``. If ``True``, put each RR into its own
+ RRset.
+
+ *keyring*, a ``dict``, the keyring to use for TSIG.
+
+ *request_mac*, a ``binary``, the MAC of the request (for TSIG).
+
+ Raises if the message is malformed, if network errors occur, of if
+ there is a timeout.
+
+ Returns a ``dns.message.Message`` object.
"""
- if af is None:
- try:
- af = dns.inet.af_for_address(destination[0])
- except Exception:
- af = dns.inet.AF_INET
+
wire = b''
while 1:
_wait_for_readable(sock, expiration)
(wire, from_address) = sock.recvfrom(65535)
- if _addresses_equal(af, from_address, destination) or \
+ if _addresses_equal(socket.family, from_address, destination) or \
(dns.inet.is_multicast(destination[0]) and
from_address[1:] == destination[1:]):
break
ignore_unexpected=False, one_rr_per_rrset=False):
"""Return the response obtained after sending a query via UDP.
- @param q: the query
- @type q: dns.message.Message
- @param where: where to send the message
- @type where: string containing an IPv4 or IPv6 address
- @param timeout: The number of seconds to wait before the query times out.
- If None, the default, wait forever.
- @type timeout: float
- @param port: The port to which to send the message. The default is 53.
- @type port: int
- @param af: the address family to use. The default is None, which
- causes the address family to use to be inferred from the form of where.
- If the inference attempt fails, AF_INET is used.
- @type af: int
- @param source: source address. The default is the wildcard address.
- @type source: string
- @param source_port: The port from which to send the message.
+ *q*, a ``dns.message.message``, the query to send
+
+ *where*, a ``text`` containing an IPv4 or IPv6 address, where
+ to send the message.
+
+ *timeout*, a ``float`` or ``None``, the number of seconds to wait before the
+ query times out. If ``None``, the default, wait forever.
+
+ *port*, an ``int``, the port send the message to. The default is 53.
+
+ *af*, an ``int``, the address family to use. The default is ``None``,
+ which causes the address family to use to be inferred from the form of
+ *where*. If the inference attempt fails, AF_INET is used. This
+ parameter is historical; you need never set it.
+
+ *source*, a ``text`` containing an IPv4 or IPv6 address, specifying
+ the source address. The default is the wildcard address.
+
+ *source_port*, an ``int``, the port from which to send the message.
The default is 0.
- @type source_port: int
- @param ignore_unexpected: If True, ignore responses from unexpected
- sources. The default is False.
- @type ignore_unexpected: bool
- @param one_rr_per_rrset: Put each RR into its own RRset
- @type one_rr_per_rrset: bool
- @rtype: dns.message.Message object
+
+ *ignore_unexpected*, a ``bool``. If ``True``, ignore responses from
+ unexpected sources.
+
+ *one_rr_per_rrset*, a ``bool``. If ``True``, put each RR into its own
+ RRset.
+
+ Returns a ``dns.message.Message``.
"""
wire = q.to_wire()
def send_tcp(sock, what, expiration=None):
"""Send a DNS message to the specified TCP socket.
- @param sock: the socket
- @type sock: socket.socket
- @param what: the message to send
- @type what: wire format bytes or a dns.message.Message
- @param expiration: The absolute time at which a timeout exception should
- be raised.
- @type expiration: float
- @rtype: (int, double) tuple of bytes sent and the sent time.
+ *sock*, a ``socket``.
+
+ *what*, a ``binary`` or ``dns.message.Message``, the message to send.
+
+ *expiration*, a ``float`` or ``None``, the absolute time at which
+ a timeout exception should be raised. If ``None``, no timeout will
+ occur.
+
+ Returns an ``(int, float)`` tuple of bytes sent and the sent time.
"""
+
if isinstance(what, dns.message.Message):
what = what.to_wire()
l = len(what)
keyring=None, request_mac=b''):
"""Read a DNS message from a TCP socket.
- @param sock: the socket
- @type sock: socket.socket
- @param one_rr_per_rrset: Put each RR into its own RRset
- @type one_rr_per_rrset: bool
- @param keyring: the keyring to use for TSIG
- @type keyring: keyring dict
- @param request_mac: the MAC of the request (for TSIG)
- @type request_mac: bytes
- @rtype: dns.message.Message object
+ *sock*, a ``socket``.
+
+ *expiration*, a ``float`` or ``None``, the absolute time at which
+ a timeout exception should be raised. If ``None``, no timeout will
+ occur.
+
+ *one_rr_per_rrset*, a ``bool``. If ``True``, put each RR into its own
+ RRset.
+
+ *keyring*, a ``dict``, the keyring to use for TSIG.
+
+ *request_mac*, a ``binary``, the MAC of the request (for TSIG).
+
+ Raises if the message is malformed, if network errors occur, of if
+ there is a timeout.
+
+ Returns a ``dns.message.Message`` object.
"""
+
ldata = _net_read(sock, 2, expiration)
(l,) = struct.unpack("!H", ldata)
wire = _net_read(sock, l, expiration)
one_rr_per_rrset=False):
"""Return the response obtained after sending a query via TCP.
- @param q: the query
- @type q: dns.message.Message object
- @param where: where to send the message
- @type where: string containing an IPv4 or IPv6 address
- @param timeout: The number of seconds to wait before the query times out.
- If None, the default, wait forever.
- @type timeout: float
- @param port: The port to which to send the message. The default is 53.
- @type port: int
- @param af: the address family to use. The default is None, which
- causes the address family to use to be inferred from the form of where.
- If the inference attempt fails, AF_INET is used.
- @type af: int
- @rtype: dns.message.Message object
- @param source: source address. The default is the wildcard address.
- @type source: string
- @param source_port: The port from which to send the message.
+ *q*, a ``dns.message.message``, the query to send
+
+ *where*, a ``text`` containing an IPv4 or IPv6 address, where
+ to send the message.
+
+ *timeout*, a ``float`` or ``None``, the number of seconds to wait before the
+ query times out. If ``None``, the default, wait forever.
+
+ *port*, an ``int``, the port send the message to. The default is 53.
+
+ *af*, an ``int``, the address family to use. The default is ``None``,
+ which causes the address family to use to be inferred from the form of
+ *where*. If the inference attempt fails, AF_INET is used. This
+ parameter is historical; you need never set it.
+
+ *source*, a ``text`` containing an IPv4 or IPv6 address, specifying
+ the source address. The default is the wildcard address.
+
+ *source_port*, an ``int``, the port from which to send the message.
The default is 0.
- @type source_port: int
- @param one_rr_per_rrset: Put each RR into its own RRset
- @type one_rr_per_rrset: bool
+
+ *one_rr_per_rrset*, a ``bool``. If ``True``, put each RR into its own
+ RRset.
+
+ Returns a ``dns.message.Message``.
"""
wire = q.to_wire()
use_udp=False, keyalgorithm=dns.tsig.default_algorithm):
"""Return a generator for the responses to a zone transfer.
- @param where: where to send the message
- @type where: string containing an IPv4 or IPv6 address
- @param zone: The name of the zone to transfer
- @type zone: dns.name.Name object or string
- @param rdtype: The type of zone transfer. The default is
- dns.rdatatype.AXFR.
- @type rdtype: int or string
- @param rdclass: The class of the zone transfer. The default is
- dns.rdataclass.IN.
- @type rdclass: int or string
- @param timeout: The number of seconds to wait for each response message.
- If None, the default, wait forever.
- @type timeout: float
- @param port: The port to which to send the message. The default is 53.
- @type port: int
- @param keyring: The TSIG keyring to use
- @type keyring: dict
- @param keyname: The name of the TSIG key to use
- @type keyname: dns.name.Name object or string
- @param relativize: If True, all names in the zone will be relativized to
- the zone origin. It is essential that the relativize setting matches
- the one specified to dns.zone.from_xfr().
- @type relativize: bool
- @param af: the address family to use. The default is None, which
- causes the address family to use to be inferred from the form of where.
- If the inference attempt fails, AF_INET is used.
- @type af: int
- @param lifetime: The total number of seconds to spend doing the transfer.
- If None, the default, then there is no limit on the time the transfer may
- take.
- @type lifetime: float
- @rtype: generator of dns.message.Message objects.
- @param source: source address. The default is the wildcard address.
- @type source: string
- @param source_port: The port from which to send the message.
+ *where*. If the inference attempt fails, AF_INET is used. This
+ parameter is historical; you need never set it.
+
+ *zone*, a ``dns.name.Name`` or ``text``, the name of the zone to transfer.
+
+ *rdtype*, an ``int`` or ``text``, the type of zone transfer. The
+ default is ``dns.rdatatype.AXFR``. ``dns.rdatatype.IXFR`` can be
+ used to do an incremental transfer instead.
+
+ *rdclass*, an ``int`` or ``text``, the class of the zone transfer.
+ The default is ``dns.rdataclass.IN``.
+
+ *timeout*, a ``float``, the number of seconds to wait for each
+ response message. If None, the default, wait forever.
+
+ *port*, an ``int``, the port send the message to. The default is 53.
+
+ *keyring*, a ``dict``, the keyring to use for TSIG.
+
+ *keyname*, a ``dns.name.Name`` or ``text``, the name of the TSIG
+ key to use.
+
+ *relativize*, a ``bool``. If ``True``, all names in the zone will be
+ relativized to the zone origin. It is essential that the
+ relativize setting matches the one specified to
+ ``dns.zone.from_xfr()`` if using this generator to make a zone.
+
+ *af*, an ``int``, the address family to use. The default is ``None``,
+ which causes the address family to use to be inferred from the form of
+ *where*. If the inference attempt fails, AF_INET is used. This
+ parameter is historical; you need never set it.
+
+ *lifetime*, a ``float``, the total number of seconds to spend
+ doing the transfer. If ``None``, the default, then there is no
+ limit on the time the transfer may take.
+
+ *source*, a ``text`` containing an IPv4 or IPv6 address, specifying
+ the source address. The default is the wildcard address.
+
+ *source_port*, an ``int``, the port from which to send the message.
The default is 0.
- @type source_port: int
- @param serial: The SOA serial number to use as the base for an IXFR diff
- sequence (only meaningful if rdtype == dns.rdatatype.IXFR).
- @type serial: int
- @param use_udp: Use UDP (only meaningful for IXFR)
- @type use_udp: bool
- @param keyalgorithm: The TSIG algorithm to use; defaults to
- dns.tsig.default_algorithm
- @type keyalgorithm: string
+
+ *serial*, an ``int``, the SOA serial number to use as the base for
+ an IXFR diff sequence (only meaningful if *rdtype* is
+ ``dns.rdatatype.IXFR``).
+
+ *use_udp*, a ``bool``. If ``True``, use UDP (only meaningful for IXFR).
+
+ *keyalgorithm*, a ``dns.name.Name`` or ``text``, the TSIG algorithm to use.
+
+ Returns a generator of ``dns.message.Message`` objects.
"""
if isinstance(zone, string_types):