Search code examples
pythondnstwisted

Python Twisted Custom DNS


I am new to the Twisted framework for python. I am playing around with a customer DNS server and I want to know how to located the SRC_IP of the DNS Request.

I am using the following script: http://twisted.readthedocs.org/en/latest/names/howto/custom-server.html

Specifically the one that dynamically resolves and want to know how I can located the source IP of the Request. Thanks


Solution

  • Not being overly familiar with twisted, I don't know if this is the best way. I suspect that what I propose below is not because operating directly on the sockets doesn't feel right, but here it goes.

    Subclass server.DNSServerFactory and override the handleQuery() method, for example:

    import socket
    from twisted.internet.address import IPv4Address
    
    class MyDNSServerFactory(server.DNSServerFactory):
    
        def handleQuery(self, message, protocol, address):
            if protocol.transport.socket.type == socket.SOCK_STREAM:
                self.peer_address = protocol.transport.getPeer()
            elif protocol.transport.socket.type == socket.SOCK_DGRAM:
                self.peer_address = IPv4Address('UDP', *address)
            else:
                print "Unexpected socket type %r" % protocol.transport.socket.type
    
            print "Got message from : %r" % self.peer_address
            return server.DNSServerFactory.handleQuery(self, message, protocol, address)
    .
    .
    .
    
    factory = MyDNSServerFactory(
        clients=[DynamicResolver(), client.Resolver(resolv='/etc/resolv.conf')]
    )
    

    Things to worry about:

    1. IPV6 addresses
    2. Direct use of socket seems underhanded.
    3. Does socket.SOCK_STREAM always imply a TCP connection?

    There are other ways to do it, e.g. subclass dns.DNSDatagramProtocol and override datagramReceived(self, data, addr) to get the client UDP address. TCP client addresses would be gotten by subclassing and overriding server.DNSServerFactory.connectionMade(self, protocol) and getting the peer's address with protocol.transport.getPeer().


    Updated answer to make peer address available in DynamicResolver

    Modify MyDNSServerFactory.handleQuery() to set peer_address for resolvers that have a peer_address attribute:

    class MyDNSServerFactory(server.DNSServerFactory):
    
        def handleQuery(self, message, protocol, address):
            if protocol.transport.socket.type == socket.SOCK_STREAM:
                self.peer_address = protocol.transport.getPeer()
            elif protocol.transport.socket.type == socket.SOCK_DGRAM:
                self.peer_address = IPv4Address('UDP', *address)
            else:
                print "Unexpected socket type %r" % protocol.transport.socket.type
    
            print "Got message from : %r" % self.peer_address
    
            # Make peer_address available to resolvers that support that attribute
            for resolver in self.resolver.resolvers:
                if hasattr(resolver, 'peer_address'):
                    resolver.peer_address = self.peer_address
    
            return server.DNSServerFactory.handleQuery(self, message, protocol, address)
    

    Add the following peer_address property to class DynamicResolver:

        def __init__(self):
            self._peer_address = None
    
        @property
        def peer_address(self):
            return self._peer_address
    
        @peer_address.setter
        def peer_address(self, value):
            self._peer_address = value
    

    Now you can access peer_address in DynamicResolver.query(), e.g.

        def query(self, query, timeout=None):
            print "In DynamicResolver.query(): self.peer_address = %r" % self.peer_address
            if self._dynamicResponseRequired(query):
                return defer.succeed(self._doDynamicResponse(query))
            else:
                return defer.fail(error.DomainError())