The libDNS.rts library (written by Matthew Geldert and enhanced by Matthias Scheler) attached below provides a means to interrogate and modify DNS traffic from a TrafficScript rule, and to respond directly to DNS request when desired.
You can use it to:
Note: This library allows you to inspect and modify DNS traffic as it is balanced by Traffic Manager. If you want to issue DNS requests from Traffic Manager, check out the net.dns.resolveHost() and net.dns.resolveIP() TrafficScript functions for this purpose.
This rule can be used when Traffic Manager receives DNS lookups (UDP or TCP) using a Virtual Server, and optionally uses a pool to forward these requests to a real DNS server:
Typically, a listen IP addresses of the Traffic Manager will be published as the NS record for a subdomain so that clients explicitly direct DNS lookups to the Traffic Manager, although you can use the software in a transparent mode and intercept DNS lookups that are routed through Traffic Manager using the iptables configuration described in Transparent Load Balancing with Traffic Manager.
Configure Traffic Manager with a virtual server listening on port 53; you can use the 'discard' pool to get started:
If you use 'dig' to send a DNS request to that virtual server (listening on 192.168.35.10 in this case), you won't get a response because the virtual server will discard all requests:
$ dig @192.168.35.10 www.zeus.com ; <<>> DiG 9.8.3-P1 <<>> @192.168.35.10 www.zeus.com ; (1 server found) ;; global options: +cmd ;; connection timed out; no servers could be reached
Create a new rule named libDNS.rts using the attached TrafficScript Source:
This rule is used as a library, so you don't need to associate it with a Virtual Server.
Then associate the following request rule with your virtual server:
import libDNS.rts as dns; $request = request.get(); $packet = dns.convertRawDataToObject($request, "udp"); # Ignore unparsable packets and query responses to avoid # attacks like the one described in CVE-2004-0789. if( hash.count( $packet ) == 0 || $packet["qr"] == "1" ) { break; } log.info( "DNS request: " . lang.dump( $packet ) ); $host = dns.getQuestion( $packet )["host"]; $packet = dns.addResponse($packet, "answer", $host, "www.site.com.", "CNAME", "IN", "60", []);
$packet["qr"] = 1; log.info( "DNS response: " . lang.dump( $packet ) ); request.sendResponse( dns.convertObjectToRawData($packet, "udp"));
This request rule will catch all DNS requests and respond directly with a CNAME directing the client to look up 'www.site.com' instead:
$ dig @192.168.35.10 www.zeus.com ; <<>> DiG 9.8.3-P1 <<>> @192.168.35.10 www.zeus.com ; (1 server found) ;; global options: +cmd ;; Got answer: ;; ->>HEADER<
This DNS library provides a set of helper functions to query and modify DNS packets. Internally, a decoded DNS packet is represented as a TrafficScript datastructure and when you are writing and debugging rules, it may be useful to use log.info( lang.dump( $packet ) ) as in the rule above to help understand the internal structures.
$packet = dns.newDnsObject(); log.info( lang.dump( $packet ) );
Creates a data structure model of a DNS request/response packet. The default flags are set for an A record lookup. Returns the internal hash data structure used for the DNS packet.
$packet = dns.setQuestion( $packet, $host, $type, $class );
Sets the question in a data structure representation of a DNS packet.
$q = dns.getQuestion( $packet ); $host = $q["host"]; $type = $q["type"]; $class = $q["class"];
Gets the question from a data structure representing a DNS request/response. Returns a hash of the host and type in the data structure's question section.
$packet = dns.addResponse($packet, $section, $name, $host, $type, $class, $ttl, $additional);
Adds an answer to the specified RR section:
$packet = dns.removeResponse( $packet, $section, $answer );
while( $packet["additionalcount"] > 0 ){
$packet = dns.removeResponse( $packet, "additional", 0);
}
Removes an answer from the specified RR section.
$data = dns.convertObjectToRawData( $packet, $protocol ); request.sendResponse( $data );
Converts a data structure into the raw data suitable to send in a DNS request or response.
$data = request.get(); $packet = dns.convertRawDatatoObject( $data, $protocol );
Converts a raw DNS request/response to a manipulatable data structure.
You will need to use EDNS and libDNS version 2.2 when using custom Trafficscript in a GLB service that uses the client IP location for load balancing, with request.getRemoteIP() and glb.preferLocation().
You will not need to use libDNS for purely GEO load balancing is used, which has built-in support for the EDNS client subnet info.
In the DNS (UDP) Virtual Server listening on UDP port 53, go to
Rules > Request Rules > Manage > Create new > Name: "EDNS UDP" and
Use TrafficScript Language > Create, then copy/paste this into it:
import libDNS.rts as dns; $packet = dns.convertRawDataToObject (request.get(), "udp"); $ednsClientSubnet = dns.getClientSubnet ($packet); if ($ednsClientSubnet["subnet"]) { log.info ("got cso: " . $ednsClientSubnet["subnet"]); request.setRemoteIP ($ednsClientSubnet["subnet"]); }
In the other DNS (TCP) Virtual Server listening on TCP port 53, go to
Rules > Request Rules > Manage > Create new > Name: "EDNS TCP" and
Use TrafficScript Language > Create, then copy/paste this into it:
import libDNS.rts as dns; $packet = dns.convertRawDataToObject (request.get(), "tcp"); $ednsClientSubnet = dns.getClientSubnet ($packet); if ($ednsClientSubnet["subnet"]) { log.info ("got cso: " . $ednsClientSubnet["subnet"]); request.setRemoteIP ($ednsClientSubnet["subnet"]); }
Kudos to Matthew Geldert, original author of this library, and Matthias Scheler, author of version 2.0.
January 2015: libDNS version 2.1:
Fixes multiple bugs in parsing of DNS records
December 2020: libDNS version 2.2:
Added support for EDNS client subnet info
The library has been updated to v1.2, adding a fix to handle DNS requests that contain Answer, Authority or Additional sections. These are generated by some Exchange servers, and by versions of dig >= 9.9.1.
Updated December 2020: libDNS 2.2 released, including EDNS support