Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Twisted.names : Query is re-created in ResolverChain #10189

Open
twisted-trac opened this issue May 4, 2021 · 0 comments
Open

Twisted.names : Query is re-created in ResolverChain #10189

twisted-trac opened this issue May 4, 2021 · 0 comments

Comments

@twisted-trac
Copy link

Benouare's avatar @Benouare reported
Trac ID trac#10189
Type enhancement
Created 2021-05-04 07:24:18Z

Hi,

My use case :
I would like to build kind of filter based on tlds.
To do that, I ve build my own resolver that is responding defer.fail(error.DomainError()), when the domain is allowed (the defer.fail will send the query to the next resolver, thanks to the resolverChain ) or defer.fail(dns.AuthoritativeDomainError(query.name.name)) that will send a final NXDOMAIN to the client and will block the domain.

This part work well.
Now, I want to "adapt" my filter regarding devices of my network. For example, if the ip is 192.168.0.4 (the one of my child of 10 years), i want to block youtube from 8pm to ... 8 Am (for example), but i would like that on other devices (192.168.0.50) to be able to go on youtube all the time i want to.

So the solution was to get the ip address of the client. To do that :

The solution it should works(but it doesnt) :

   factory = DNSServerFactory2( # DNSServerFactory2 that inherit DNSServerFactory
        caches=[
            # cache.CacheResolver()
        ],
        clients=[
            Archify(),# Custom resolver 1
            Hector(),# Custom resolver 2
            client.Resolver(
                servers=[("89.234.141.66", 53), ("80.67.169.12", 53)])
        ]
    )
    protocol = dns.DNSDatagramProtocol(controller=factory)
    reactor.listenUDP(port, protocol)
    reactor.listenTCP(port, factory)
    reactor.run()

class DNSServerFactory2(server.DNSServerFactory):
    def handleQuery(self, message, protocol, address):
        query = message.queries[0]
        # Here, i am just adding a "custom" param on the query to be able to use it in my resolver in the def query of Hector()/Archify()
        query.address = address 
        return (
            self.resolver.query(query) # Here i am calling ResolverChain.query()
            .addCallback(self.gotResolverResponse, protocol, message, address)
            .addErrback(self.gotResolverError, protocol, message, address)
        )


But, ...that doesnt work...
When i am printing the query in the DnsServerFactory2 I get something like

2021-05-02 08:47:58+0200 [-] {'address': ('127.0.0.1', 60750),
2021-05-02 08:47:58+0200 [-]  'cls': 1,
2021-05-02 08:47:58+0200 [-]  'name': <twisted.names.dns.Name object at 0x13c8e38b0>,
2021-05-02 08:47:58+0200 [-]  'type': 1}

That is good.

But, if i am doing the same in the custom resolvers, i get that :

2021-05-02 08:47:58+0200 [-] {'cls': 1, 'name': <twisted.names.dns.Name object at 0x13c8e3880>, 't
ype': 1}

So I ve lost the address param during the transfer from the factory to my resolever.

After trying to understand what i am doing wrong, i finnaly found the issue/need to refact.

The current solution :

Same (quiet the same) as before :

class DNSServerFactory2(server.DNSServerFactory):
    def __init__(self, authorities=None, caches=None, clients=None, verbose=0):
        super().__init__(authorities, caches, clients, verbose)
        resolvers = []
        if authorities is not None:
            resolvers.extend(authorities)
        if caches is not None:
            resolvers.extend(caches)
        if clients is not None:
            resolvers.extend(clients)
        self.resolver = ResolverChain2(resolvers) # Here the ResolverChain2 !

    def handleQuery(self, message, protocol, address): # Same as before
        query = message.queries[0]
        query.address = address
        return (
            self.resolver.query(query) # Here, self.resolver is the resolverChain
            .addCallback(self.gotResolverResponse, protocol, message, address)
            .addErrback(self.gotResolverError, protocol, message, address)
        )

The issue ResolverChain vs ResolverChain2

Original :

def _lookup(self, name, cls, type, timeout): #Here the query is "splited" with the params
        if not self.resolvers:
            return defer.fail(error.DomainError())
        q = dns.Query(name, type, cls) # /!\/!\/!\ THIS IS THE REASON OF RE-CREATING THE QUERY /!\/!\/!\
        d = self.resolvers[0].query(q, timeout)
        for r in self.resolvers[1:]:
            d = d.addErrback(FailureHandler(r.query, q, timeout))
        return d

def query(self, query, timeout=None):
        try:
            method = self.typeToMethod[query.type]
        except KeyError:
            self._log.debug(
                "Query of unknown type {query.type} for {query.name.name!r}",
                query=query,
            )
            return defer.maybeDeferred(
                self._lookup, query.name.name, dns.IN, query.type, timeout
            )
        else: 
            return defer.maybeDeferred(method, query.name.name, timeout)

ResolverChain2

class ResolverChain2(resolve.ResolverChain):
    
    def query(self, query, timeout=None):
        # here, i am not sending a new query or something else, just transmitting the original query nothing else
        return defer.maybeDeferred(self._lookupbyquery, query, timeout)

    def _lookupbyquery(self, query, timeout=None):
        if not self.resolvers:
            return defer.fail(error.DomainError())
        # Same behavior than in the original version, except that i didnt create a new query, i am using the original one with the address param. 
        d = self.resolvers[0].query(query, timeout)
        for r in self.resolvers[1:]:
            d = d.addErrback(client.resolve.FailureHandler(r.query, query, timeout))
        return d

Finaly :
I think the main problem is that the query is not sended/consumed by the resolver directly.
At the resolver chain level, it needs to consume the query directly, without doing anything on it. Just serve the original query...

What do you think about that?

Hope it's clear...

Cheers

Benoît

Searchable metadata
trac-id__10189 10189
type__enhancement enhancement
reporter__Benouare Benouare
priority__normal normal
milestone__None None
branch__ 
branch_author__Benouare Benouare
status__new new
resolution__None None
component__names names
keywords__None None
time__1620113058923072 1620113058923072
changetime__1620113225360908 1620113225360908
version__None None
owner__None None
cc__Benouare
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant