diff --git a/_examples/q/Makefile b/_examples/q/Makefile index 0f280715..34a05582 100644 --- a/_examples/q/Makefile +++ b/_examples/q/Makefile @@ -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 lex.go +GOFILES=q.go fp.go lex.go dns.go DEPS=../../ include $(GOROOT)/src/Make.cmd diff --git a/_examples/q/dns.go b/_examples/q/dns.go new file mode 100644 index 00000000..32ddd2b6 --- /dev/null +++ b/_examples/q/dns.go @@ -0,0 +1,36 @@ +package main + +import ( + "dns" +) + +// Check if the server responds at all +func dnsAlive(l *lexer) stateFn { + l.verbose("Alive") + l.setString("QUERY,NOERROR,qr,aa,tc,rd,ra,ad,cd,z,1,0,0,0,do,0") + l.setQuestion(".", dns.TypeNS, dns.ClassINET) + + f := l.probe() + + if f.ok() { + return dnsDoBitMirror + } + l.emit(&item{itemError, f.error()}) + return nil +} + +// Check if the server returns the DO-bit when set in the request. +func dnsDoBitMirror(l *lexer) stateFn { + l.verbose("DoBitMirror") + // The important part here is that the DO bit is on in the reply + l.setString("QUERY,NOERROR,qr,aa,tc,RD,ra,ad,cd,z,1,0,0,0,DO,0") + l.setQuestion(".", dns.TypeNS, dns.ClassINET) + + f := l.probe() + if !f.Do { + l.emit(&item{itemSoftware, NSD}) + return nil + } + l.emit(&item{itemSoftware, BIND}) + return nil +} diff --git a/_examples/q/fp.go b/_examples/q/fp.go index ae0af793..1a0aacac 100644 --- a/_examples/q/fp.go +++ b/_examples/q/fp.go @@ -17,7 +17,7 @@ const ( // Vendors ISC = "ISC" NLNETLABS = "NLnet Labs" - MICROSOFT = "Microsoft" + MICROSOFT = "Microsoft" ) func startParse(addr string) { @@ -27,6 +27,7 @@ func startParse(addr string) { fp: new(fingerprint), items: make(chan item), state: dnsAlive, + debug: true, } l.run() @@ -54,22 +55,23 @@ func sendProbe(c *dns.Client, addr string, f *fingerprint, q dns.Question) *fing // This leads to strings like: "QUERY,NOERROR,qr,aa,tc,RD,ad,cd,z,1,0,0,1,DO,4096" type fingerprint struct { - Error os.Error - 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 + Error os.Error + Opcode int + Rcode int + Response bool + Authoritative bool + Truncated bool + RecursionDesired bool + RecursionAvailable 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. @@ -86,6 +88,7 @@ func (f *fingerprint) String() string { s += valueOfBool(f.Authoritative, ",aa") s += valueOfBool(f.Truncated, ",tc") s += valueOfBool(f.RecursionDesired, ",rd") + s += valueOfBool(f.RecursionAvailable, ",ra") s += valueOfBool(f.AuthenticatedData, ",ad") s += valueOfBool(f.CheckingDisabled, ",cd") s += valueOfBool(f.Zero, ",z") @@ -129,28 +132,33 @@ func (f *fingerprint) setString(str string) { f.RecursionDesired = true } case 6: + f.RecursionAvailable = false + if s == strings.ToUpper("ra") { + f.RecursionAvailable = true + } + case 7: f.AuthenticatedData = false if s == strings.ToUpper("ad") { f.AuthenticatedData = true } - case 7: + case 8: f.CheckingDisabled = false if s == strings.ToUpper("cd") { f.CheckingDisabled = true } - case 8: + case 9: f.Zero = false if s == strings.ToUpper("z") { f.Zero = true } - case 9, 10, 11, 12: + case 10, 11, 12, 13: // Can not set content of the message - case 13: + case 14: f.Do = false if s == strings.ToUpper("do") { f.Do = true } - case 14: + case 15: f.UDPSize = 0 f.UDPSize = valueOfString(s) default: @@ -190,6 +198,7 @@ func msgToFingerprint(m *dns.Msg) *fingerprint { f.Authoritative = h.Authoritative f.Truncated = h.Truncated f.RecursionDesired = h.RecursionDesired + f.RecursionAvailable = h.RecursionAvailable f.AuthenticatedData = h.AuthenticatedData f.CheckingDisabled = h.CheckingDisabled f.Zero = h.Zero diff --git a/_examples/q/lex.go b/_examples/q/lex.go index 2f568bdf..73d2fbd4 100644 --- a/_examples/q/lex.go +++ b/_examples/q/lex.go @@ -34,10 +34,15 @@ type lexer struct { q dns.Question // question to ask. items chan item // channel of scanned items. state stateFn // the next function to enter. + debug bool // if true, the fingerprints are printed. } func (l *lexer) probe() *fingerprint { - return sendProbe(l.client, l.addr, l.fp, l.q) + f := sendProbe(l.client, l.addr, l.fp, l.q) + if l.debug { + fmt.Printf(" QR fp: %s\n", f) + } + return f } func (l *lexer) emit(i *item) { @@ -46,6 +51,9 @@ func (l *lexer) emit(i *item) { func (l *lexer) setString(s string) { l.fp.setString(s) + if l.debug { + fmt.Printf(" Q fp: %s\n", s) + } } func (l *lexer) setQuestion(name string, t uint16, c uint16) { @@ -62,38 +70,5 @@ func (l *lexer) run() { } func (l *lexer) verbose(s string) { - fmt.Printf("running: dns%s\n", s) -} - -// "Lexer" functions, prefixed with dns - -// Check if the server responds at all -func dnsAlive(l *lexer) stateFn { - l.verbose("Alive") - l.setString("QUERY,NOERROR,qr,aa,tc,rd,ad,cd,z,1,0,0,0,do,0") - l.setQuestion(".", dns.TypeNS, dns.ClassINET) - - f := l.probe() - - if f.ok() { - return dnsDoBitMirror - } - l.emit(&item{itemError, f.error()}) - return nil -} - -// Check if the server returns the DO-bit when set in the request. -func dnsDoBitMirror(l *lexer) stateFn { - l.verbose("DoBitMirror") - // The important part here is that the DO bit is on in the reply - l.setString("QUERY,NOERROR,qr,aa,tc,RD,ad,cd,z,1,0,0,0,DO,0") - l.setQuestion(".", dns.TypeNS, dns.ClassINET) - - f := l.probe() - if !f.Do { - l.emit(&item{itemSoftware, NSD}) - return nil - } - l.emit(&item{itemSoftware, BIND}) - return nil + fmt.Printf("running: dns%s\n", s) }