cancel
Showing results for 
Search instead for 
Did you mean: 

libDNS.rts: Interrogating and managing DNS traffic in Traffic Manager

Updated December 2020: libDNS 2.2 released, including EDNS support

 

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.

 

Overview

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:

 

dns1.png

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.

 

Installation

 

Step 1:

Configure Traffic Manager with a virtual server listening on port 53; you can use the 'discard' pool to get started:

 

dns2.png


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

 

Step 2:

Create a new rule named libDNS.rts using the attached TrafficScript Source:

 

DNS3.png
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<

 

Documentation

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.

 

Create a new DNS packet

$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.

 

Set the question in a DNS packet

$packet = dns.setQuestion( $packet, $host, $type, $class );

 

Sets the question in a data structure representation of a DNS packet. 

  • $packet - the packet data structure to manipulate.
  • $host - the host to lookup.
  • $type - the type of RR to request.
  • Returns the modified packet data structure, or -1 if the type is unknown.

 

Get the question in the 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.

 

Add an answer to the DNS packet

$packet = dns.addResponse($packet, $section, $name, $host, $type, $class, $ttl, $additional);

 

Adds an answer to the specified RR section:

  • $packet - the packet data structure to manipulate.
  • $section - name of the section ("answer", "authority" or "additional") to add the answer to
  • $name, $host, $type, $class, $ttl, $additional - the answer data
  • Returns the modified packet data structure.

 

Remove an answer from the DNS packet

$packet = dns.removeResponse( $packet, $section, $answer );
while( $packet["additionalcount"] > 0 ){
$packet = dns.removeResponse( $packet, "additional", 0);
}

 

Removes an answer from the specified RR section.

 

  • $packet - the packet data structure to manipulate.
  • $section - name of the section ("answer", "authority" or "additional") to remove the entry from
  • $answer - the array position of the answer to remove (0 removes the first).
  • Returns the modified packet data structure, or -1 if the specified array key is out of range.

 

Convert the datastructure packet object to a raw packet

 

$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.

 

  • $packet - data structure to convert.
  • $protocol - transport protocol of data ("udp" or "tcp").
  • Returns the raw packet data.

 

 

Convert a raw packet to  an internal datastructure object

 

$data = request.get();
$packet = dns.convertRawDatatoObject( $data, $protocol );

 

 

Converts a raw DNS request/response to a manipulatable data structure.

 

  • $data - raw DNS packet
  • $protocol - transport protocol of data ("udp" or "tcp")
  • Returns trafficscript data structure (hash).

 

Using EDNS

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.

 

Using EDNS: Set up a DNS (UDP) Virtual Server

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"]);
}
 

 

Using EDNS: Set up a DNS (TCP) Virtual Server

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

Kudos to Matthew Geldert, original author of this library, and Matthias Scheler, author of version 2.0.

 

Version History

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

 

 

Version history
Revision #:
4 of 4
Last update:
‎12-21-2020 07:34:AM
Updated by:
 
Labels (1)
Contributors
Comments

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