DNS Decoding
Problem this snippet solves:
Sample iRule that does DNS decoding, for DNS protocol debugging and studying :)
June 3 2014 : We have got few customers using this script in production and having system rebooted due to possible indefinite loop in this script. There are possible risks with the while loops without any protection. As the original contributor kindly mentions this is debugging and studying sample, those who want to use this script in their production should understand what it is doing and improve security by their selves. Kimihito.
Code :
when RULE_INIT {
array set ::type2name {
1 A
2 NS
3 MD
4 MF
5 CNAME
6 SOA
7 MB
8 MG
9 MR
10 NULL
11 WKS
12 PTR
13 HINFO
14 MINFO
15 MX
16 TXT
17 RP
18 AFSDB
19 X25
20 ISDN
21 RS
22 NSAP
23 NSAP_PTR
24 SIG
25 KEY
26 PX
27 GPOS
28 AAAA
29 LOC
30 NXT
31 EID
32 NIMLOC
33 SRV
34 ATMA
35 NAPTR
36 KX
37 CERT
38 A6
39 DNAME
40 SINK
41 OPT
42 APL
43 DS
44 SSHFP
45 IPSECKEY
46 RRSIG
47 NSEC
48 DNSKEY
49 DHCID
55 HIP
99 SPF
100 UINFO
101 UID
102 GID
103 UNSPEC
249 TKEY
250 TSIG
251 IXFR
252 AXFR
253 MAILB
254 MAILA
32768 TA
32769 DLV
65281 WINS
65282 WINS_R
}
array set ::class2name {
1 IN
2 CS
3 CH
4 HS
254 NONE
255 ANY
}
array set ::qr2txt {
0 "query"
1 "response"
}
array set ::opcode2txt {
0 "Standard query"
1 "Inverse query"
2 "Server status request"
4 "Zone change notification"
5 "Dynamic update"
}
array set ::rcode2txt {
0 "No error condition"
1 "Format error"
2 "Server failure"
3 "Name Error"
4 "Not Implemented"
5 "Refused"
6 "Name exists"
7 "RRset exists"
8 "RRset does not exist"
9 "Not authoritative"
10 "Name out of zone"
16 "TSIG Signature Failure"
17 "Key not recognized"
18 "Signature out of time window"
19 "Bad TKEY Mode"
20 "uplicate key name"
21 "Algorithm not supported"
22 "Bad Truncation"
}
}
when CLIENT_DATA {
binary scan [UDP::payload] SSSSSS id flags qdcount ancount nscount arcount
set qr [expr ($flags >> 15)&0x1]
set opcode [expr ($flags >> 11)& 0xf]
set aa [expr ($flags >> 10)& 0x1]
set tc [expr ($flags >> 9)& 0x1]
set rd [expr ($flags >> 8)& 0x1]
set ra [expr ($flags >> 7)& 0x1]
set z [expr ($flags >> 4)& 0x7]
set rcode [expr $flags & 0xf]
#log local0. "DEBUG: ID=$id Query=$qr Opcode=$opcode Authoritative=$aa Truncate=$tc Recursion_Desired=$rd \
Recursion_Available=$ra Reserve(000)=$z Response_Code=$rcode Questions=$qdcount Answers=$ancount Name_Servers=$nscount \
Additionals=$arcount"
# Total Header length = 12 bytes
set index 12
# Question Section: it is usually 1 (qdcount=1), we may not need to loop here actually.
while { $qdcount > 0 } {
binary scan [UDP::payload] @${index}c count
incr index
while { $count != 0 } {
binary scan [UDP::payload] @${index}A${count}c name new_count
incr index $count
incr index
set count $new_count
if { [info exists dname] } {
set dname $dname.$name
} else {
set dname $name
}
}
binary scan [UDP::payload] @${index}SS qtype qclass
log local0. "Question: $dname qtype=$::type2name($qtype) qclass=$::class2name($qclass)"
unset dname
incr index 4
incr qdcount -1
}
}
when SERVER_DATA {
binary scan [UDP::payload] SSSSSS id flags qdcount ancount nscount arcount
set qr [expr ($flags >> 15)&0x1]
set opcode [expr ($flags >> 11)& 0xf]
set aa [expr ($flags >> 10)& 0x1]
set tc [expr ($flags >> 9)& 0x1]
set rd [expr ($flags >> 8)& 0x1]
set ra [expr ($flags >> 7)& 0x1]
set z [expr ($flags >> 4)& 0x7]
set rcode [expr $flags & 0xf]
#log local0. "DEBUG: ID=$id Query=$qr Opcode=$opcode Authoritative=$aa Truncate=$tc Recursion_Desired=$rd \
Recursion_Available=$ra Reserve(000)=$z Response_Code=$rcode Questions=$qdcount Answers=$ancount Name_Servers=$nscount \
Additionals=$arcount"
# Total Header length = 12 bytes
set index 12
# Question Section: it is usually 1 (qdcount=1), we may not need to loop here actually.
while { $qdcount > 0 } {
binary scan [UDP::payload] @${index}c count
incr index
while { $count != 0 } {
binary scan [UDP::payload] @${index}A${count}c name new_count
incr index $count
incr index
set count $new_count
if { [info exists dname] } {
set dname $dname.$name
} else {
set dname $name
}
}
binary scan [UDP::payload] @${index}SS qtype qclass
log local0. "Question: $dname qtype=$::type2name($qtype) qclass=$::class2name($qclass)"
unset dname
incr index 4
incr qdcount -1
}
# The Answer, Authority and Additional Sections
while { $ancount > 0 || $nscount > 0 || $arcount > 0} {
binary scan [UDP::payload] @${index}S pointer
if { [expr ($pointer >> 14) & 0x3] == 3 } {
set saveindex $index
set index [expr $pointer & 0x3fff]
}
binary scan [UDP::payload] @${index}c count
incr index
while { $count != 0 } {
binary scan [UDP::payload] @${index}A${count}S name pointer
incr index $count
if { [expr ($pointer >> 14) & 0x3] == 3 } {
if { not [info exists saveindex] } {
set saveindex $index
}
set index [expr $pointer & 0x3fff]
}
binary scan [UDP::payload] @${index}c count
incr index
if { [info exists dname] } {
set dname $dname.$name
} else {
set dname $name
}
}
if { [info exists saveindex] } {
set index $saveindex
incr index 2
unset saveindex
}
binary scan [UDP::payload] @${index}SSIS type class ttl rdlength
incr index 10
if { [info exists dname] } { set savename $dname; unset dname }
switch $type {
1 {
# A
binary scan [UDP::payload] @${index}cccc a b c d
set addr [expr ($a+0x100)%0x100].[expr ($b+0x100)%0x100].[expr ($c+0x100)%0x100].[expr ($d+0x100)%0x100]
log local0. "$::type2name($type): $savename $addr"
incr index $rdlength
}
12 -
9 -
8 -
7 -
5 -
4 -
3 -
2 {
# NS, MD, MF, CNAME, MB, MG, MR, PTR, MINFO
binary scan [UDP::payload] @${index}S pointer
if { [expr ($pointer >> 14) & 0x3] == 3 } {
set saveindex $index
set index [expr $pointer & 0x3fff]
}
binary scan [UDP::payload] @${index}c count
incr index
while { $count != 0 } {
binary scan [UDP::payload] @${index}A${count}S name pointer
incr index $count
if { [expr ($pointer >> 14) & 0x3] == 3 } {
if { not [info exists saveindex] } {
set saveindex $index
}
set index [expr $pointer & 0x3fff]
}
binary scan [UDP::payload] @${index}c count
incr index
if { [info exists dname] } {
set dname $dname.$name
} else {
set dname $name
}
}
if { [info exists saveindex] } {
set index $saveindex
incr index 2
unset saveindex
}
log local0. "$::type2name($type): $savename $dname"
unset dname
}
6 {
# SOA
foreach i { mname rname } {
binary scan [UDP::payload] @${index}S pointer
if { [expr ($pointer >> 14) & 0x3] == 3 } {
set saveindex $index
set index [expr $pointer & 0x3fff]
}
binary scan [UDP::payload] @${index}c count
incr index
while { $count != 0 } {
binary scan [UDP::payload] @${index}A${count}S name pointer
incr index $count
if { [expr ($pointer >> 14) & 0x3] == 3 } {
if { not [info exists saveindex] } {
set saveindex $index
}
set index [expr $pointer & 0x3fff]
}
binary scan [UDP::payload] @${index}c count
incr index
if { [info exists dname] } {
set dname $dname.$name
} else {
set dname $name
}
}
if { [info exists saveindex] } {
set index $saveindex
incr index 2
unset saveindex
}
set $i $dname
unset dname
}
# SERIAL, REFRESH, RETRY, EXPIRE
binary scan [UDP::payload] @${index}IIIII serial refresh retry expire minimum
incr index 20
log local0. "SOA: $mname $rname serial=$serial, refresh=$refresh, retry=$retry, \
expire=$expire minimum=$minimum"
}
13 {
foreach i { cpu os } {
binary scan [UDP::payload] @${index}c length
incr index
binary scan [UDP::payload] @${index}A${length} string
incr index $length
set $i $string
}
log local0. "$::type2name($type): $cpu $os"
}
14 {
# MINFO
# RMAILBX, EMAILBX
foreach i { rmailbx emailbx } {
binary scan [UDP::payload] @${index}S pointer
if { [expr ($pointer >> 14) & 0x3] == 3 } {
set saveindex $index
set index [expr $pointer & 0x3fff]
}
binary scan [UDP::payload] @${index}c count
incr index
while { $count != 0 } {
binary scan [UDP::payload] @${index}A${count}S name pointer
incr index $count
if { [expr ($pointer >> 14) & 0x3] == 3 } {
if { not [info exists saveindex] } {
set saveindex $index
}
set index [expr $pointer & 0x3fff]
}
binary scan [UDP::payload] @${index}c count
incr index
if { [info exists dname] } {
set dname $dname.$name
} else {
set dname $name
}
}
if { [info exists saveindex] } {
set index $saveindex
incr index 2
unset saveindex
}
set $i $dname
unset dname
}
unset dname
log local0. "$::type2name($type): $savename $rmailbx $emailbx"
}
15 {
# MX
binary scan [UDP::payload] @${index}SS preference pointer
incr index 2
if { [expr ($pointer >> 14) & 0x3] == 3 } {
set saveindex $index
set index [expr $pointer & 0x3fff]
}
binary scan [UDP::payload] @${index}c count
incr index
while { $count != 0 } {
binary scan [UDP::payload] @${index}A${count}S name pointer
incr index $count
if { [expr ($pointer >> 14) & 0x3] == 3 } {
if { not [info exists saveindex] } {
set saveindex $index
}
set index [expr $pointer & 0x3fff]
}
binary scan [UDP::payload] @${index}c count
incr index
if { [info exists dname] } {
set dname $dname.$name
} else {
set dname $name
}
}
if { [info exists saveindex] } {
set index $saveindex
incr index 2
unset saveindex
}
log local0. "$::type2name($type): $savename $dname $preference"
unset dname
}
16 {
set maxlen [expr $index + $rdlength ]
while { $index < $maxlen } {
binary scan [UDP::payload] @${index}c length
incr index
binary scan [UDP::payload] @${index}A${length} string
incr index $length
log local0. "$::type2name($type): $string"
}
}
28 {
# AAAA
binary scan [UDP::payload] @${index}H4H4H4H4H4H4H4H4 a b c d e f g h
log local0. "$::type2name($type): $savename $a:$b:$c:$d:$e:$f:$g:$h"
incr index $rdlength
}
33 {
# SRV
binary scan [UDP::payload] @${index}SSSS priority weight port pointer
incr index 6
if { [expr ($pointer >> 14) & 0x3] == 3 } {
set saveindex $index
set index [expr $pointer & 0x3fff]
}
binary scan [UDP::payload] @${index}c count
incr index
while { $count != 0 } {
binary scan [UDP::payload] @${index}A${count}S name pointer
incr index $count
if { [expr ($pointer >> 14) & 0x3] == 3 } {
if { not [info exists saveindex] } {
set saveindex $index
}
set index [expr $pointer & 0x3fff]
}
binary scan [UDP::payload] @${index}c count
incr index
if { [info exists dname] } {
set dname $dname.$name
} else {
set dname $name
}
}
if { [info exists saveindex] } {
set index $saveindex
incr index 2
unset saveindex
}
log local0. "$::type2name($type): $savename $priority $weight $port $dname"
unset dname
}
default {
binary scan [UDP::payload] @${index}H[expr $rdlength * 2] rdata
log local0. "$::type2name($type): data=0x$rdata"
incr index $rdlength
}
}
if { $ancount > 0 } {
incr ancount -1
} elseif { $nscount > 0 } {
incr nscount -1
} else {
incr arcount -1
}
}
}
# Introducing loop limit counters, $limitqdcount and $limitname to avoid tmm. It'd be better to apply something similar not only to CLIENT_DATA but also SERVER_DATA side.
# Line 123 ~
# Total Header length = 12 bytes
set index 12
# Question Section: it is usually 1 (qdcount=1), we may not need to loop here actually.
# This is a protection for many Questions in a single query packet
set limitqdcount 0
while { $qdcount > 0 } {
# qdcount is generally 1.
if { $limitqdcount > 0 } {
log local0. "limitqdcount exceeded "
return
}
binary scan [UDP::payload] @${index}c count
incr index
# This is a protection for too long domain name
set limitname 0
while { $count != 0 } {
# How long domain name of the query you allow. Setting it to 2, you allow "www.example.com" but "www.my.example.com".
if { $limitname > 2 } {
log local0. "limitname exceeded : too long domain name "
return
}
binary scan [UDP::payload] @${index}A${count}c name new_count
incr index $count
incr index
set count $new_count
if { [info exists dname] } {
set dname $dname.$name
} else {
set dname $name
}
incr limitname 1
}
binary scan [UDP::payload] @${index}SS qtype qclass opcode rcode
log local0. "Question : $dname qtype=$::type2name($qtype) qclass=$::class2name($qclass)"
unset dname
incr index 4
incr qdcount -1
incr limitqdcount 1
}
}Published Mar 17, 2015
Version 1.0Nat_Thirasuttakorn
Employee
Joined September 25, 2004
Nat_Thirasuttakorn
Employee
Joined September 25, 2004
No CommentsBe the first to comment