DNS Non-English Domain Name Detection

Problem this snippet solves:

This iRule scans domain name in the query section of the DNS message. If it founds character that has value higher than 127 (according to ASCII, printable english character are number 127 or lower), it selects pool of DNS server that supports chinese name resolution. otherwise, send to pool of DNS server that only supports english. This iRule skip DNS header and start checking each character in the QNAME one-by-one. (see RFC1035 for detail of DNS protocol format) This iRule can be applied to any other language that uses value higher than 127.

Code :

when CLIENT_DATA {
    binary scan [UDP::payload] @3c sflags
    set rcode  [expr $sflags & 0xf]
    if { $rcode == 0} {
        # skip the DNS header, jump to the QNAME part of first QUESTION
        # byte contains the first part length
        binary scan [UDP::payload] @12c byte
        # make the byte an unsigned integer
        set byte [expr ($byte + 0x100)%0x100]
        # initialize our posisition in the QNAME parsing and the text QNAME
        set offset 12
        set i 12
        ############# /extract QNAME from QUESTION header #############
        while {$byte > 0 && $offset < [UDP::payload length]} {
            # grab a part and put it in our text QNAME section
            set offset [expr $offset + $byte + 1]
            #check each charactor one by one, see if it's bigger than 127
            incr i
            while {$i < $offset} {
                binary scan [UDP::payload] @${i}c char
                #log local0. "debug: char=$char"
                if { $char < 0 } {
                    #log "send to Chinses dns Pool"
                    pool dns_chinese_pool
                    #if one char is bigger than 127 (signed char mean negative), then end the rule execution
                    return
                }
                incr i
            }
            # grab the length of the next part, and make it an unsigned integer
            binary scan [UDP::payload] @${offset}c byte
            # make the byte an unsigned integer 
            set byte [expr ($byte + 0x100)%0x100]
        }
        #log "send to Chinses dns Pool"
        pool dns_english_pool
    }
}
Published Mar 17, 2015
Version 1.0

Was this article helpful?

1 Comment

  • ep's avatar
    ep
    Icon for Nimbostratus rankNimbostratus
    Forgive me if I'm confused, but . . . Wouldn't it be better if this code binary scan [UDP::payload] @3c sflags set rcode [expr $sflags & 0xf] if { $rcode == 0} { said this instead? binary scan [UDP::payload] @2c sflags set qr [expr $sflags & 0x80] if { $qr == 0} { My thought is that @3c selects the 4th byte of the UDP::payload, which is actually the RA, Z, and RCODE. When you & 0xf, you do get 0 if it is a query, but only because a query should always have a 0000 RCODE. Instead, if you select the 3rd byte with @2c, you should get the QR, OPCODE, AA, TC, and RD. A QR of 0 indicates that this is a query, and something we want to inspect. By doing a & 0x80, you should only get a nonzero when it is not a query (128 = 0b10000000). Is there a reason why you prefer looking at the RCODE and not the QR? I've learned a lot by reading this code, thanks! ep