Create fp as a type

This commit is contained in:
Miek Gieben 2011-09-19 22:27:56 +02:00
parent 1b96a4413a
commit fb68e29eb1
5 changed files with 259 additions and 43 deletions

View File

@ -3,6 +3,6 @@
# license that can be found in the LICENSE file.
include $(GOROOT)/src/Make.inc
TARG=q
GOFILES=q.go fp.go
GOFILES=q.go fp.go lex.go
DEPS=../../
include $(GOROOT)/src/Make.cmd

View File

@ -7,55 +7,202 @@ import (
"strings"
)
// fingerPrint creates a (short) string representation of a dns message.
// SendProbe creates a packet and sends it to the nameserver.
// Connection errors are returned as:
// ...
func sendProbe(c *dns.Client, addr string, f *fingerprint, q dns.Question) *fingerprint {
m := f.toProbe(q)
r, err := c.Exchange(m, addr)
if err != nil {
println(err.String())
// return "connection error"
return nil
}
return msgToFingerprint(r)
}
// This leads to strings like: "QUERY,NOERROR,qr,aa,tc,RD,ad,cd,z,1,0,0,1,DO,4096"
type fingerprint struct {
Error string
Opcode int
Rcode int
Response bool
Authoritative bool
Truncated bool
RecursionDesired bool
AuthenticatedData bool
CheckingDisabled bool
Zero bool
Question int
Answer int
Ns int
Extra int
Do bool
UDPSize int
}
// String creates a (short) string representation of a dns message.
// If a bit is set we uppercase the name 'AD' otherwise it's lowercase 'ad'.
func msgToFingerPrint(m *dns.Msg) string {
if m == nil {
// This leads to strings like: "QUERY,NOERROR,qr,aa,tc,RD,ad,cd,z,1,0,0,1,DO,4096"
func (f *fingerprint) String() string {
if f == nil {
return "<nil>"
}
h := m.MsgHdr
// Use the same order as in Perl's fpdns. But use more flags.
s := dns.Opcode_str[f.Opcode]
s += "," + dns.Rcode_str[f.Rcode]
s += valueOfBool(f.Response, ",qr")
s += valueOfBool(f.Authoritative, ",aa")
s += valueOfBool(f.Truncated, ",tc")
s += valueOfBool(f.RecursionDesired, ",rd")
s += valueOfBool(f.AuthenticatedData, ",ad")
s += valueOfBool(f.CheckingDisabled, ",cd")
s += valueOfBool(f.Zero, ",z")
// Use the same order as in Perl's fpdns.
// But use more flags.
s := dns.Opcode_str[h.Opcode]
s += "," + dns.Rcode_str[h.Rcode]
s += valueOf(h.Response, ",qr")
s += valueOf(h.Authoritative, ",aa")
s += valueOf(h.Truncated, ",tc")
s += valueOf(h.RecursionDesired, ",rd")
s += valueOf(h.AuthenticatedData, ",ad")
s += valueOf(h.CheckingDisabled, ",ad")
s += valueOf(h.Zero, ",z")
s += valueOfInt(f.Question)
s += valueOfInt(f.Answer)
s += valueOfInt(f.Ns)
s += valueOfInt(f.Extra)
s += "," + strconv.Itoa(len(m.Question))
s += "," + strconv.Itoa(len(m.Answer))
s += "," + strconv.Itoa(len(m.Ns))
s += "," + strconv.Itoa(len(m.Extra))
// EDNS0
// V0,DO,4096 (all on)
// v0,do,0 (all off)
for _, r := range m.Extra {
if r.Header().Rrtype == dns.TypeOPT {
// version is always 0 - and I cannot set it anyway
s += valueOf(r.(*dns.RR_OPT).Do(), ",do")
s += "," + strconv.Itoa(int(r.(*dns.RR_OPT).UDPSize()))
return s
}
}
s += ",do,0"
s += valueOfBool(f.Do, ",do")
s += valueOfInt(f.UDPSize)
return s
}
// Create a dns message from a fingerprint string
func fingerPrintToProbe(fp string, q dns.Question) *dns.Msg {
return nil
// SetString set the string to fp.. todo
func (f *fingerprint) SetString(str string) {
for i, s := range strings.Split(str, ",") {
switch i {
case 0:
f.Opcode = dns.Str_opcode[s]
case 1:
f.Rcode = dns.Str_rcode[s]
case 2:
f.Response = false
if s == strings.ToUpper("qr") {
f.Response = true
}
case 3:
f.Authoritative = false
if s == strings.ToUpper("aa") {
f.Authoritative = true
}
case 4:
f.Truncated = false
if s == strings.ToUpper("tc") {
f.Truncated = true
}
case 5:
f.RecursionDesired = false
if s == strings.ToUpper("rd") {
f.RecursionDesired = true
}
case 6:
f.AuthenticatedData = false
if s == strings.ToUpper("ad") {
f.AuthenticatedData = true
}
case 7:
f.CheckingDisabled = false
if s == strings.ToUpper("cd") {
f.CheckingDisabled = true
}
case 8:
f.Zero = false
if s == strings.ToUpper("z") {
f.Zero = true
}
case 9, 10, 11, 12:
// Can not set content of the message
case 13:
f.Do = false
if s == strings.ToUpper("do") {
f.Do = true
}
case 14:
f.UDPSize = 0
f.UDPSize = valueOfString(s)
default:
panic("unhandled fingerprint")
}
}
return
}
func valueOf(x bool, w string) string {
if x {
func msgToFingerprint(m *dns.Msg) *fingerprint {
if m == nil {
return nil
}
h := m.MsgHdr
f := new(fingerprint)
f.Opcode = h.Opcode
f.Rcode = h.Rcode
f.Response = h.Response
f.Authoritative = h.Authoritative
f.Truncated = h.Truncated
f.RecursionDesired = h.RecursionDesired
f.AuthenticatedData = h.AuthenticatedData
f.CheckingDisabled = h.CheckingDisabled
f.Zero = h.Zero
f.Question = len(m.Question)
f.Answer = len(m.Answer)
f.Ns = len(m.Ns)
f.Extra = len(m.Extra)
f.Do = false
f.UDPSize = 0
for _, r := range m.Extra {
if r.Header().Rrtype == dns.TypeOPT {
// version is always 0 - and I cannot set it anyway
f.Do = r.(*dns.RR_OPT).Do()
f.UDPSize = int(r.(*dns.RR_OPT).UDPSize())
}
}
return f
}
// Create a dns message from a fingerprint string and
// a DNS question. The order of a string is always the
// same.
// QUERY,NOERROR,qr,aa,tc,RD,ad,ad,z,1,0,0,1,DO,4096
func (f *fingerprint) toProbe(q dns.Question) *dns.Msg {
m := new(dns.Msg)
m.MsgHdr.Id = dns.Id()
m.Question = make([]dns.Question, 1)
m.Question[0] = q
m.MsgHdr.Opcode = f.Opcode
m.MsgHdr.Rcode = f.Rcode
m.MsgHdr.Response = f.Response
m.MsgHdr.Authoritative = f.Authoritative
m.MsgHdr.Truncated = f.Truncated
m.MsgHdr.RecursionDesired = f.RecursionDesired
m.MsgHdr.AuthenticatedData = f.AuthenticatedData
m.MsgHdr.CheckingDisabled = f.CheckingDisabled
m.MsgHdr.Zero = f.Zero
if f.Do {
// Add an OPT section.
m.SetEdns0(0, true)
// We have added an OPT RR, set the size.
m.Extra[0].(*dns.RR_OPT).SetUDPSize(uint16(f.UDPSize))
}
return m
}
func valueOfBool(b bool, w string) string {
if b {
return strings.ToUpper(w)
}
return strings.ToLower(w)
}
func valueOfInt(i int) string {
return "," + strconv.Itoa(i)
}
func valueOfString(s string) int {
i, _ := strconv.Atoi(s)
return i
}

54
_examples/q/lex.go Normal file
View File

@ -0,0 +1,54 @@
package main
import (
"dns"
)
type itemType int
// We handle the checking as a lexing program.
// We use the lexer like Rob Pike lectures about in this
// clip: http://www.youtube.com/watch?v=HxaD_trXwRE
type item struct {
typ itemType
val string
}
const (
itemError itemType = iota
itemVender // software vendor
itemSoftware // the name of the DNS server software
itemVersion // the version of the software (empty if not determined)
)
// stateFn represents the state of the scanner as a function that returns the next state.
type stateFn func(*lexer) stateFn
type lexer struct {
client *dns.Client // client used.
addr string // addr of the server being scanned.
fp *fingerprint // fingerprint to test.
q dns.Question // question to ask.
items chan item // channel of scanned items.
state stateFn // the next function to enter.
}
func (l *lexer) probe() *fingerprint {
return sendProbe(l.client, l.addr, l.fp, l.q)
}
func (l *lexer) emit() {
// send item on the channel
}
// "Lexer" functions
// Check if the server returns the DO-bit when set in the request.
func lexDoBitMirorred(l *lexer) stateFn {
// The important part here is that the DO bit is on
l.fp.SetString("QUERY,NOERROR,qr,aa,tc,RD,ad,cd,z,1,0,0,0,DO,0")
l.q = dns.Question{".", dns.TypeNS, dns.ClassINET}
l.probe()
return nil
}

View File

@ -114,6 +114,7 @@ Flags:
m.Question[0] = dns.Question{v, qtype, qclass}
m.Id = dns.Id()
if *query {
fmt.Printf("%s\n", msgToFingerprint(m))
fmt.Printf("%s\n", m.String())
}
c.Do(m, nameserver)
@ -134,7 +135,7 @@ forever:
r.Reply = shortMsg(r.Reply)
}
if *fp {
fmt.Printf("%s\n", msgToFingerPrint(r.Reply))
fmt.Printf("%s\n", msgToFingerprint(r.Reply))
}
fmt.Printf("%v", r.Reply)
}

20
msg.go
View File

@ -124,8 +124,14 @@ var Rr_str = map[uint16]string{
}
// Reverse, needed for string parsing.
var str_rr = reverse(Rr_str)
var str_class = reverse(Class_str)
var Str_rr = reverseInt16(Rr_str)
var Str_class = reverseInt16(Class_str)
// Map of opcodes strings.
var Str_opcode = reverseInt(Opcode_str)
// Map of rcodes strings.
var Str_rcode = reverseInt(Rcode_str)
// Map of strings for each CLASS wire type.
var Class_str = map[uint16]string{
@ -864,7 +870,7 @@ func unpackRR(msg []byte, off int) (rr RR, off1 int, ok bool) {
}
// Reverse a map
func reverse(m map[uint16]string) map[string]uint16 {
func reverseInt16(m map[uint16]string) map[string]uint16 {
n := make(map[string]uint16)
for u, s := range m {
n[s] = u
@ -872,6 +878,14 @@ func reverse(m map[uint16]string) map[string]uint16 {
return n
}
func reverseInt(m map[int]string) map[string]int {
n := make(map[string]int)
for u, s := range m {
n[s] = u
}
return n
}
// Convert a MsgHdr to a string, mimic the way Dig displays headers:
//;; opcode: QUERY, status: NOERROR, id: 48404
//;; flags: qr aa rd ra;