Beispiel #1
0
    def testName(self):
        for n in self.names:
            # encode the name
            f = StringIO()
            dns.Name(n).encode(f)

            # decode the name
            f.seek(0, 0)
            result = dns.Name()
            result.decode(f)
            self.assertEquals(result.name, n)
Beispiel #2
0
    def _fetchServers(self, host):
        try:
            service = "_matrix._tcp.%s" % host
            answers, auth, add = yield client.lookupService(service)
        except DNSNameError:
            answers = []

        if (len(answers) == 1
                and answers[0].type == dns.SRV
                and answers[0].payload
                and answers[0].payload.target == dns.Name(".")):
            raise DNSNameError("Service %s unavailable", service)

        servers = []

        for answer in answers:
            if answer.type != dns.SRV or not answer.payload:
                continue
            payload = answer.payload
            servers.append(ThreepidBinder._Server(
                host=str(payload.target),
                port=int(payload.port),
                priority=int(payload.priority),
                weight=int(payload.weight)
            ))

        servers.sort()
        defer.returnValue(servers)
Beispiel #3
0
    def fetch_servers(self):
        try:
            answers, auth, add = yield client.lookupService(self.service_name)
        except DNSNameError:
            answers = []

        if (len(answers) == 1 and answers[0].type == dns.SRV
                and answers[0].payload
                and answers[0].payload.target == dns.Name('.')):
            raise ConnectError("Service %s unavailable", self.service_name)

        self.servers = []
        self.used_servers = []

        for answer in answers:
            if answer.type != dns.SRV or not answer.payload:
                continue
            payload = answer.payload
            self.servers.append(
                self._Server(host=str(payload.target),
                             port=int(payload.port),
                             priority=int(payload.priority),
                             weight=int(payload.weight)))

        self.servers.sort()
Beispiel #4
0
    async def resolve_service(self, service_name: bytes) -> List["Server"]:
        """Look up a SRV record

        :param service_name: The record to look up.

        :returns a list of the SRV records, or an empty list if none found.
        """
        now = int(self._get_time())

        cache_entry = self._cache.get(service_name, None)
        if cache_entry:
            if all(s.expires > now for s in cache_entry):
                servers = list(cache_entry)
                return servers

        try:
            answers, _, _ = await self._lookup_service(service_name.decode())
        except DNSNameError:
            # TODO: cache this. We can get the SOA out of the exception, and use
            # the negative-TTL value.
            return []
        except DomainError as e:
            # We failed to resolve the name (other than a NameError)
            # Try something in the cache, else rereaise
            cache_entry = self._cache.get(service_name, None)
            if cache_entry:
                logger.warning(
                    "Failed to resolve %r, falling back to cache. %r",
                    service_name, e)
                return list(cache_entry)
            else:
                raise e

        if (len(answers) == 1 and answers[0].type == dns.SRV
                and answers[0].payload
                and answers[0].payload.target == dns.Name(b".")):
            raise ConnectError("Service %s unavailable" %
                               service_name.decode())

        servers = []

        for answer in answers:
            if answer.type != dns.SRV or not answer.payload:
                continue

            payload = answer.payload

            servers.append(
                Server(
                    host=payload.target.name,
                    port=payload.port,
                    priority=payload.priority,
                    weight=payload.weight,
                    expires=now + answer.ttl,
                ))

        self._cache[service_name] = list(servers)
        return servers
Beispiel #5
0
def resolve_service(service_name, dns_client=client, cache=SERVER_CACHE, clock=time):
    cache_entry = cache.get(service_name, None)
    if cache_entry:
        if all(s.expires > int(clock.time()) for s in cache_entry):
            servers = list(cache_entry)
            defer.returnValue(servers)

    servers = []

    try:
        try:
            answers, _, _ = yield dns_client.lookupService(service_name)
        except DNSNameError:
            defer.returnValue([])

        if (len(answers) == 1
                and answers[0].type == dns.SRV
                and answers[0].payload
                and answers[0].payload.target == dns.Name('.')):
            raise ConnectError("Service %s unavailable" % service_name)

        for answer in answers:
            if answer.type != dns.SRV or not answer.payload:
                continue

            payload = answer.payload

            hosts = yield _get_hosts_for_srv_record(
                dns_client, str(payload.target)
            )

            for (ip, ttl) in hosts:
                host_ttl = min(answer.ttl, ttl)

                servers.append(_Server(
                    host=ip,
                    port=int(payload.port),
                    priority=int(payload.priority),
                    weight=int(payload.weight),
                    expires=int(clock.time()) + host_ttl,
                ))

        servers.sort()
        cache[service_name] = list(servers)
    except DomainError as e:
        # We failed to resolve the name (other than a NameError)
        # Try something in the cache, else rereaise
        cache_entry = cache.get(service_name, None)
        if cache_entry:
            logger.warn(
                "Failed to resolve %r, falling back to cache. %r",
                service_name, e
            )
            servers = list(cache_entry)
        else:
            raise e

    defer.returnValue(servers)
Beispiel #6
0
def resolve_service(service_name, dns_client=client, cache=SERVER_CACHE):
    servers = []

    try:
        try:
            answers, _, _ = yield dns_client.lookupService(service_name)
        except DNSNameError:
            defer.returnValue([])

        if (len(answers) == 1 and answers[0].type == dns.SRV
                and answers[0].payload
                and answers[0].payload.target == dns.Name('.')):
            raise ConnectError("Service %s unavailable", service_name)

        for answer in answers:
            if answer.type != dns.SRV or not answer.payload:
                continue

            payload = answer.payload

            host = str(payload.target)

            try:
                answers, _, _ = yield dns_client.lookupAddress(host)
            except DNSNameError:
                continue

            ips = [
                answer.payload.dottedQuad() for answer in answers
                if answer.type == dns.A and answer.payload
            ]

            for ip in ips:
                servers.append(
                    _Server(host=ip,
                            port=int(payload.port),
                            priority=int(payload.priority),
                            weight=int(payload.weight)))

        servers.sort()
        cache[service_name] = list(servers)
    except DomainError as e:
        # We failed to resolve the name (other than a NameError)
        # Try something in the cache, else rereaise
        cache_entry = cache.get(service_name, None)
        if cache_entry:
            logger.warn("Failed to resolve %r, falling back to cache. %r",
                        service_name, e)
            servers = list(cache_entry)
        else:
            raise e

    defer.returnValue(servers)
Beispiel #7
0
 def nameFromRawData(self, labels, offset=None):
     name = b'.'.join(labels)
     if offset is None:
         return dns.Name(name=name)
     visited = set()
     visited.add(offset)
     while 1:
         l = ord(self.data[offset])
         offset += 1
         if l == 0:
             return dns.Name(name)
         if (l >> 6) == 3:
             offset = (l & 63) << 8 | ord(self.data[offset])
             if offset in visited:
                 raise ValueError("Compression loop in compressed name")
             visited.add(offset)
             continue
         label = self.data[offset:offset + l]
         offset += l
         if name == b'':
             name = label
         else:
             name = name + b'.' + label
Beispiel #8
0
    def _cbGotServers(self, result):
        answers, auth, add = result
        if len(answers) == 1 and answers[0].type == dns.SRV \
                             and answers[0].payload \
                             and answers[0].payload.target == dns.Name(b'.'):
            # decidedly not available
            raise error.DNSLookupError("Service %s not available for domain %s."
                                       % (repr(self.service), repr(self.domain)))

        self.servers = []
        self.orderedServers = []
        for a in answers:
            if a.type != dns.SRV or not a.payload:
                continue

            self.orderedServers.append(a.payload)
Beispiel #9
0
class SRVConnector:
    """A connector that looks up DNS SRV records. See RFC2782."""

    implements(interfaces.IConnector)

    stopAfterDNS = 0

    def __init__(
            self,
            reactor,
            service,
            domain,
            factory,
            protocol='tcp',
            connectFuncName='connectTCP',
            connectFuncArgs=(),
            connectFuncKwArgs={},
            defaultPort=None,
    ):
        """
        @param domain: The domain to connect to.  If passed as a unicode
            string, it will be encoded using C{idna} encoding.
        @type domain: L{bytes} or L{unicode}
        @param defaultPort: Optional default port number to be used when SRV
            lookup fails and the service name is unknown. This should be the
            port number associated with the service name as defined by the IANA
            registry.
        @type defaultPort: C{int}
        """
        self.reactor = reactor
        self.service = service
        if isinstance(domain, unicode):
            domain = domain.encode('idna')
        self.domain = domain
        self.factory = factory

        self.protocol = protocol
        self.connectFuncName = connectFuncName
        self.connectFuncArgs = connectFuncArgs
        self.connectFuncKwArgs = connectFuncKwArgs
        self._defaultPort = defaultPort

        self.connector = None
        self.servers = None
        self.orderedServers = None  # list of servers already used in this round

    def connect(self):
        """Start connection to remote server."""
        self.factory.doStart()
        self.factory.startedConnecting(self)

        if not self.servers:
            if self.domain is None:
                self.connectionFailed(
                    error.DNSLookupError("Domain is not defined."))
                return
            d = client.lookupService(
                '_%s._%s.%s' % (self.service, self.protocol, self.domain))
            d.addCallbacks(self._cbGotServers, self._ebGotServers)
            d.addCallback(lambda x, self=self: self._reallyConnect())
            if self._defaultPort:
                d.addErrback(self._ebServiceUnknown)
            d.addErrback(self.connectionFailed)
        elif self.connector is None:
            self._reallyConnect()
        else:
            self.connector.connect()

    def _ebGotServers(self, failure):
        failure.trap(DNSNameError)

        # Some DNS servers reply with NXDOMAIN when in fact there are
        # just no SRV records for that domain. Act as if we just got an
        # empty response and use fallback.

        self.servers = []
        self.orderedServers = []

    def _cbGotServers(self, (answers, auth, add)):
        if len(answers) == 1 and answers[0].type == dns.SRV \
                             and answers[0].payload \
                             and answers[0].payload.target == dns.Name('.'):
            # decidedly not available
            raise error.DNSLookupError(
                "Service %s not available for domain %s." %
                (repr(self.service), repr(self.domain)))

        self.servers = []
        self.orderedServers = []
        for a in answers:
            if a.type != dns.SRV or not a.payload:
                continue

            self.orderedServers.append((a.payload.priority, a.payload.weight,
                                        str(a.payload.target), a.payload.port))
Beispiel #10
0
class SRVMediaRelayBase(object):
    def __init__(self):
        self.shutting_down = False
        self.srv_monitor = RecurrentCall(RelayConfig.dns_check_interval,
                                         self._do_lookup)
        self._do_lookup()

    def _do_lookup(self):
        defers = []
        for addr, port, is_domain in RelayConfig.dispatchers:
            if is_domain:
                defer = lookupService("_sip._udp.%s" % addr)
                defer.addCallback(self._cb_got_srv, port)
                defer.addErrback(self._eb_no_srv, addr, port)
                defers.append(defer)
            else:
                defers.append(succeed((addr, port)))
        defer = DeferredList(defers)
        defer.addCallback(self._cb_got_all)
        return KeepRunning

    def _cb_got_srv(self, (answers, auth, add), port):
        for answer in answers:
            if answer.type == dns.SRV and answer.payload and answer.payload.target != dns.Name(
                    "."):
                return str(answer.payload.target), port
        raise DomainError
Beispiel #11
0
    def resolve_service(self, service_name):
        """Look up a SRV record

        :param service_name: The record to look up.
        :type service_name: bytes

        :returns a list of the SRV records, or an empty list if none found.
        :rtype: Deferred[list[Server]]
        """
        now = int(self._get_time())

        if not isinstance(service_name, bytes):
            raise TypeError("%r is not a byte string" % (service_name, ))

        cache_entry = self._cache.get(service_name, None)
        if cache_entry:
            if all(s.expires > now for s in cache_entry):
                servers = list(cache_entry)
                defer.returnValue(servers)

        try:
            answers, _, _ = yield self._dns_client.lookupService(service_name)
        except DNSNameError:
            # TODO: cache this. We can get the SOA out of the exception, and use
            # the negative-TTL value.
            defer.returnValue([])
        except DomainError as e:
            # We failed to resolve the name (other than a NameError)
            # Try something in the cache, else rereaise
            cache_entry = self._cache.get(service_name, None)
            if cache_entry:
                logger.warn("Failed to resolve %r, falling back to cache. %r",
                            service_name, e)
                defer.returnValue(list(cache_entry))
            else:
                raise e

        if (len(answers) == 1 and answers[0].type == dns.SRV
                and answers[0].payload
                and answers[0].payload.target == dns.Name(b'.')):
            raise ConnectError("Service %s unavailable" % service_name)

        servers = []

        for answer in answers:
            if answer.type != dns.SRV or not answer.payload:
                continue

            payload = answer.payload

            servers.append(
                Server(
                    host=payload.target.name,
                    port=payload.port,
                    priority=payload.priority,
                    weight=payload.weight,
                    expires=now + answer.ttl,
                ))

        self._cache[service_name] = list(servers)
        defer.returnValue(servers)
Beispiel #12
0
    def lookupAllRecords(self, name, timeout=None):
        """
        @see: twisted.names.client.lookupAllRecords
        """
        return self._lookup(name, dns.IN, dns.ALL_RECORDS, timeout)

    def getHostByName(self, name, timeout=None, effort=10):
        """
        @see: twisted.names.client.getHostByName
        """
        # XXX - respect timeout
        return self.lookupAllRecords(name, timeout).addCallback(
            self._cbRecords, name, effort)

    def _cbRecords(self, (ans, auth, add), name, effort):
        result = extractRecord(self, dns.Name(name), ans + auth + add, effort)
        if not result:
            raise error.DNSLookupError(name)
        return result


def extractRecord(resolver, name, answers, level=10):
    if not level:
        return None
    if hasattr(socket, 'inet_ntop'):
        for r in answers:
            if r.name == name and r.type == dns.A6:
                return socket.inet_ntop(socket.AF_INET6, r.payload.address)
        for r in answers:
            if r.name == name and r.type == dns.AAAA:
                return socket.inet_ntop(socket.AF_INET6, r.payload.address)
Beispiel #13
0
class SRVConnector:
    """A connector that looks up DNS SRV records. See RFC2782."""

    implements(interfaces.IConnector)

    stopAfterDNS=0

    def __init__(self, reactor, service, domain, factory,
                 protocol='tcp', connectFuncName='connectTCP',
                 connectFuncArgs=(),
                 connectFuncKwArgs={},
                 ):
        self.reactor = reactor
        self.service = service
        self.domain = domain
        self.factory = factory

        self.protocol = protocol
        self.connectFuncName = connectFuncName
        self.connectFuncArgs = connectFuncArgs
        self.connectFuncKwArgs = connectFuncKwArgs

        self.connector = None
        self.servers = None
        self.orderedServers = None # list of servers already used in this round

    def connect(self):
        """Start connection to remote server."""
        self.factory.doStart()
        self.factory.startedConnecting(self)

        if not self.servers:
            if self.domain is None:
                self.connectionFailed(error.DNSLookupError("Domain is not defined."))
                return
            d = client.lookupService('_%s._%s.%s' % (self.service,
                                                     self.protocol,
                                                     self.domain))
            d.addCallbacks(self._cbGotServers, self._ebGotServers)
            d.addCallback(lambda x, self=self: self._reallyConnect())
            d.addErrback(self.connectionFailed)
        elif self.connector is None:
            self._reallyConnect()
        else:
            self.connector.connect()

    def _ebGotServers(self, failure):
        failure.trap(DNSNameError)

        # Some DNS servers reply with NXDOMAIN when in fact there are
        # just no SRV records for that domain. Act as if we just got an
        # empty response and use fallback.

        self.servers = []
        self.orderedServers = []

    def _cbGotServers(self, (answers, auth, add)):
        if len(answers) == 1 and answers[0].type == dns.SRV \
                             and answers[0].payload \
                             and answers[0].payload.target == dns.Name('.'):
            # decidedly not available
            raise error.DNSLookupError("Service %s not available for domain %s."
                                       % (repr(self.service), repr(self.domain)))

        self.servers = []
        self.orderedServers = []
        for a in answers:
            if a.type != dns.SRV or not a.payload:
                continue

            self.orderedServers.append((a.payload.priority, a.payload.weight,
                                        str(a.payload.target), a.payload.port))
Beispiel #14
0
 def _cbRecords(self, records, name, effort):
     (ans, auth, add) = records
     result = extractRecord(self, dns.Name(name), ans + auth + add, effort)
     if not result:
         raise error.DNSLookupError(name)
     return result
Beispiel #15
0
 def __init__(self, name=b'', type=dns.A, cls=dns.IN, hostname=None):
     self.name = dns.Name(name)
     self.type = type
     self.cls = cls
     self.hostname = hostname
Beispiel #16
0
    _initResolver()

    lookup = "{}._tcp.{}".format(
        service,
        domain,
    )
    log.debug("DNS SRV: lookup: {l}", l=lookup)
    try:
        answers = (yield DebugResolver.lookupService(lookup))[0]
    except (DomainError, AuthoritativeDomainError), e:
        log.debug("DNS SRV: lookup failed: {exc}", exc=e)
        returnValue(None)

    if (len(answers) == 1 and answers[0].type == dns.SRV and answers[0].payload
            and answers[0].payload.target == dns.Name('.')):
        # decidedly not available
        log.debug("DNS SRV: disabled: {l}", l=lookup)
        returnValue(None)

    servers = []
    for a in answers:

        if a.type != dns.SRV or not a.payload:
            continue

        servers.append((a.payload.priority, a.payload.weight,
                        str(a.payload.target), a.payload.port))

    log.debug("DNS SRV: lookup results: {l}\n{s}", l=lookup, s=servers)
 def fix_names(result):
     # Make sure names in response match what the client asked format
     for answer in result[0]:
         answer.name = dns.Name(real_name)
     print("RESULT: {}".format(result))
     return result
Beispiel #18
0
    async def resolve_service(self, service_name: bytes) -> List[Server]:
        """Look up a SRV record

        Args:
            service_name: record to look up

        Returns:
            a list of the SRV records, or an empty list if none found
        """
        now = int(self._get_time())

        if not isinstance(service_name, bytes):
            raise TypeError("%r is not a byte string" % (service_name, ))

        cache_entry = self._cache.get(service_name, None)
        if cache_entry:
            if all(s.expires > now for s in cache_entry):
                servers = list(cache_entry)
                return _sort_server_list(servers)

        try:
            answers, _, _ = await make_deferred_yieldable(
                self._dns_client.lookupService(service_name))
        except DNSNameError:
            # TODO: cache this. We can get the SOA out of the exception, and use
            # the negative-TTL value.
            return []
        except DomainError as e:
            # We failed to resolve the name (other than a NameError)
            # Try something in the cache, else rereaise
            cache_entry = self._cache.get(service_name, None)
            if cache_entry:
                logger.warning(
                    "Failed to resolve %r, falling back to cache. %r",
                    service_name, e)
                return list(cache_entry)
            else:
                raise e

        if (len(answers) == 1 and answers[0].type == dns.SRV
                and answers[0].payload
                and answers[0].payload.target == dns.Name(b".")):
            raise ConnectError(f"Service {service_name!r} unavailable")

        servers = []

        for answer in answers:
            if answer.type != dns.SRV or not answer.payload:
                continue

            payload = answer.payload

            servers.append(
                Server(
                    host=payload.target.name,
                    port=payload.port,
                    priority=payload.priority,
                    weight=payload.weight,
                    expires=now + answer.ttl,
                ))

        self._cache[service_name] = list(servers)
        return _sort_server_list(servers)