From 9c178a49c3d9ee7348f1e5c4478e758f2e95e1ef Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Sat, 28 Jan 2012 12:05:25 +0100 Subject: [PATCH] Lots of fixes and different setup --- examples/fp/Makefile | 2 +- examples/fp/dns.go | 292 ------------------------------------------- examples/fp/fp.go | 44 ++----- examples/fp/lex.go | 81 ------------ examples/fp/q.go | 263 +++----------------------------------- 5 files changed, 27 insertions(+), 655 deletions(-) delete mode 100644 examples/fp/dns.go delete mode 100644 examples/fp/lex.go diff --git a/examples/fp/Makefile b/examples/fp/Makefile index 42f8d013..14527a5d 100644 --- a/examples/fp/Makefile +++ b/examples/fp/Makefile @@ -3,6 +3,6 @@ # license that can be found in the LICENSE file. include $(GOROOT)/src/Make.inc TARG=fp -GOFILES=q.go fp.go lex.go dns.go +GOFILES=q.go fp.go DEPS=../../ include $(GOROOT)/src/Make.cmd diff --git a/examples/fp/dns.go b/examples/fp/dns.go deleted file mode 100644 index 4e233cbd..00000000 --- a/examples/fp/dns.go +++ /dev/null @@ -1,292 +0,0 @@ -package main - -import ( - "dns" -) - -const ( - QUERY_NOERROR string = "QUERY,NOERROR,qr,aa,tc,rd,ra,ad,cd,z,0,0,0,0,do,0,nsid" - QUERY_NOTIFY string = "NOTIFY,NOERROR,qr,AA,tc,RD,ra,ad,cd,Z,0,0,0,0,do,0,nsid" - QUERY_ALL string = "QUERY,NOERROR,QR,AA,TC,RD,RA,AD,CD,Z,0,0,0,0,DO,0,nsid" -) - -// Check if the server responds at all -func dnsAlive(l *lexer) stateFn { - l.verbose("Alive") - l.setString(".,IN,NS," + QUERY_NOERROR) - f := l.probe() - if f.ok() { - return dnsServer - } - l.emit(&item{itemError, f.error()}) - return nil -} - -// This is the starting test. Perform a bunch of queries, get the -// fingerprint a go into a general direction. NsdLike, BindLike, WindowsLike, MaraLike -func dnsServer(l *lexer) stateFn { - l.verbose("Server") - - // Set the DO bit - l.setString(".,CH,TXT,QUERY,NOERROR,qr,aa,tc,RD,ra,ad,cd,z,0,0,0,0,DO,4097,NSID") - f := l.probe() - switch { - case !f.Do && f.UDPSize == 4096 && f.Rcode == dns.RcodeSuccess: - // NSD clears DO bit, but sets UDPSize to 4096. NOERROR. - l.emit(&item{itemVendor, NLNETLABS}) - return dnsNsdLike - case !f.Do && f.UDPSize == 0 && f.Rcode == dns.RcodeRefused: - // MaraDNS clears DO BIT, UDPSize to 0. REFUSED - l.emit(&item{itemVendor, MARA}) - return dnsMaraLike - case f.Do && f.UDPSize == 2800 && f.Rcode == dns.RcodeSuccess: - // PowerDNS(SEC) set UDP bufsize to 2800, resets UDPSize. NOERROR - fallthrough - case !f.Do && f.UDPSize == 0 && f.Rcode == dns.RcodeSuccess: - // PowerDNS(SEC) clears DO bit, resets UDPSize. NOERROR - l.emit(&item{itemVendor, POWER}) - return dnsPowerdnsLike - case !f.Do && f.UDPSize == 0 && f.Rcode == dns.RcodeServerFailure: - // Neustar - l.emit(&item{itemVendor, NEUSTAR}) - return dnsNeustarLike - case !f.Do && f.UDPSize == 0 && f.Rcode == dns.RcodeNotImplemented: - // Altas? - l.emit(&item{itemVendor, VERISIGN}) - return dnsAtlasLike - case !f.Do && f.UDPSize == 4096 && f.Rcode == dns.RcodeServerFailure: - // BIND8 - fallthrough - case f.Do && f.UDPSize == 4096 && f.Rcode == dns.RcodeServerFailure: - // BIND9 OLD - fallthrough - case f.Do && f.UDPSize == 4096 && f.Rcode == dns.RcodeRefused: - // BIND9 leaves DO bit, but sets UDPSize to 4096. REFUSED. - l.emit(&item{itemVendor, ISC}) - return dnsBindLike - case f.Do && f.UDPSize == 4097 && f.Rcode == dns.RcodeFormatError: - // Microsoft leaves DO bit, but echo's the UDPSize. FORMERR. - l.emit(&item{itemVendor, MICROSOFT}) - return dnsWindowsLike - default: - return nil - } - panic("not reached") - return nil -} - -func dnsNsdLike(l *lexer) stateFn { - l.verbose("NsdLike") - l.setString("auThoRs.bInD.,CH,TXT," + QUERY_NOERROR) - l.probe() - - return nil -} - -func dnsBindLike(l *lexer) stateFn { - l.verbose("BindLike") - - l.emit(&item{itemSoftware, BIND}) - - // Repeat the query, as we get a lot of information from it - l.setString(".,CH,TXT,QUERY,NOERROR,qr,aa,tc,RD,ra,ad,cd,z,0,0,0,0,DO,4097,nsid") - f := l.probe() - switch { - case !f.Do && f.UDPSize == 4096 && f.Rcode == dns.RcodeServerFailure: - l.emit(&item{itemVersionMajor, "8"}) - case f.Do && f.UDPSize == 4096 && f.Rcode == dns.RcodeServerFailure: - l.emit(&item{itemVersionMajor, "9"}) - l.emit(&item{itemVersionMinor, "3"}) - case f.Do && f.UDPSize == 4096 && f.Rcode == dns.RcodeRefused: - // BIND9 leaves DO bit, but sets UDPSize to 4096. REFUSED. - l.emit(&item{itemVersionMajor, "9"}) - l.emit(&item{itemVersionMinor, "[7..]"}) - } - - // Try authors.bind - l.setString("auThoRs.bInD.,CH,TXT," + QUERY_NOERROR) - f = l.probe() - switch f.Rcode { - case dns.RcodeServerFailure: - // No authors.bind < 9 - l.emit(&item{itemVersionMajor, "8"}) - case dns.RcodeSuccess, dns.RcodeRefused: - // BIND 9 or BIND 10 - l.emit(&item{itemVersionMajor, "[9..10]"}) - } - // The three BIND (8, 9 and 10) behave differently when - // receiving a notify query - l.setString("bind.,NONE,SOA," + QUERY_NOTIFY) - f = l.probe() - switch { - case f.Opcode == dns.OpcodeNotify: - if f.Rcode == dns.RcodeRefused { - l.emit(&item{itemVersionMajor, "9"}) - } - if f.Rcode == dns.RcodeServerFailure { - l.emit(&item{itemVersionMajor, "8"}) - } - case f.Opcode == dns.OpcodeQuery && f.Rcode == dns.RcodeSuccess: - l.emit(&item{itemVersionMajor, "10"}) - if !f.Response { - // Cardinal sin - l.emit(&item{itemVersionMinor, "-devel"}) - l.emit(&item{itemVersionPatch, "20110809"}) - } - } - return nil -} - -func dnsWindowsLike(l *lexer) stateFn { - l.verbose("WindowsLike") - - return nil -} - -func dnsMaraLike(l *lexer) stateFn { - l.verbose("MaraLike") - - return nil -} - -func dnsPowerdnsLike(l *lexer) stateFn { - l.verbose("PowerdnsLike") - l.setString(".,CH,TXT,QUERY,NOERROR,qr,aa,tc,RD,ra,ad,cd,z,0,0,0,0,DO,4097,NSID") - f := l.probe() - if !f.Response && f.Query.Qclass == 0 && f.Query.Qtype == 0 && f.Rcode == dns.RcodeSuccess { - // Yadifa does not set the QR bit on this - l.emit(&item{itemVendor, EURID}) - return dnsYadifaLike - } - return nil -} - -func dnsYadifaLike(l *lexer) stateFn { - l.verbose("YadifaLike") - l.setString(".,CLASS0,TYPE0,QUERY,NOERROR,QR,aa,tc,rd,ra,ad,cd,z,0,0,0,0,do,0,nsid") - l.probe() - l.setString(".,CLASS42,TXT,QUERY,NOERROR,qr,aa,tc,rd,ra,ad,cd,z,0,0,0,0,do,0,nsid") - l.probe() - return nil -} - -func dnsNeustarLike(l *lexer) stateFn { - l.verbose("NeustarLike") - return nil -} - -func dnsAtlasLike(l *lexer) stateFn { - l.verbose("AtlasLike") - - l.setString("MieK.NL.,IN,TXT," + QUERY_NOERROR) - l.probe() - return nil -} - - -// Check if the server returns the DO-bit when set in the request. -func dnsDoBitMirror(l *lexer) stateFn { - l.verbose("DoBitMirror") - l.setString(".,IN,NS,QUERY,NOERROR,qr,aa,tc,RD,ra,ad,cd,z,0,0,0,0,DO,0,NSID") - f := l.probe() - // NSD doesn't set the DO bit, but does set the UDPMsg size to 4096. - if !f.Do && f.UDPSize == 4096 { - l.emit(&item{itemSoftware, NSD}) - return dnsEDNS0Mangler - } - return dnsEDNS0Mangler -} - -func dnsEDNS0Mangler(l *lexer) stateFn { - l.verbose("EDNS0Mangler") - l.setString("NOTIFY,NOERROR,qr,aa,tc,RD,ra,ad,cd,z,0,0,0,0,do,0,nsid") -// l.setQuestion("012345678901234567890123456789012345678901234567890123456789012.012345678901234567890123456789012345678901234567890123456789012.012345678901234567890123456789012345678901234567890123456789012.0123456789012345678901234567890123456789012345678901234567890.", dns.TypeA, dns.ClassINET) - f := l.probe() - // MaraDNS does not set the QR bit in the reply... but only with this question is seems - // QUERY,NOERROR,qr,aa,t - if !f.Response && f.Opcode == dns.OpcodeQuery && f.Rcode == dns.RcodeSuccess { - l.emit(&item{itemSoftware, MARADNS}) - } - return dnsTcEnable -} - -func dnsTcEnable(l *lexer) stateFn { - l.verbose("TcEnable") - l.setString(".,IN,NS,QUERY,NOERROR,qr,aa,TC,rd,ra,ad,cd,z,0,0,0,0,do,0,nsid") - l.probe() - return nil -} - -/* -func dnsUDPSize(l *lexer) stateFn { - l.verbose("UDPSize") - l.setString("QUERY,NOERROR,qr,aa,tc,rd,ra,ad,cd,z,0,0,0,0,DO,4097,nsid") - l.setQuestion(".", dns.TypeNS, dns.ClassINET) - l.probe() - return dnsZero -} - -func dnsZero(l *lexer) stateFn { - l.verbose("Zero") - l.setQuestion(".", dns.TypeNS, dns.ClassINET) - l.setString("QUERY,NOERROR,qr,aa,tc,rd,ra,ad,cd,Z,0,0,0,0,do,0,nsid") - l.probe() - return dnsAll -} - -func dnsAll(l *lexer) stateFn { - l.verbose("All") - l.setString("QUERY,NOERROR,qr,AA,TC,RD,RA,AD,CD,Z,0,0,0,0,DO,8192,nsid") - l.setQuestion(".", dns.TypeNS, dns.ClassINET) - l.probe() - return dnsIquery -} - -func dnsIquery(l *lexer) stateFn { - l.verbose("Iquery") - l.setString("IQUERY,NOERROR,qr,aa,tc,rd,ra,ad,cd,Z,0,0,0,0,do,0,nsid") - l.setQuestion(".", dns.TypeNS, dns.ClassINET) - l.probe() - return dnsUpdate -} - -func dnsUpdate(l *lexer) stateFn { - l.verbose("Update") - l.setString("UPDATE,NOERROR,qr,aa,tc,rd,ra,ad,cd,Z,0,0,0,0,do,0,nsid") - l.setQuestion(".", dns.TypeNS, dns.ClassINET) - l.probe() - return dnsStatus -} - -func dnsStatus(l *lexer) stateFn { - l.verbose("Status") - l.setString("STATUS,NOERROR,qr,aa,tc,rd,ra,ad,cd,Z,0,0,0,0,do,0,nsid") - l.setQuestion(".", dns.TypeNS, dns.ClassINET) - l.probe() - return dnsOpcodeWhacky -} - -func dnsOpcodeWhacky(l *lexer) stateFn { - l.verbose("OpcodeWhacky") - l.setString("12,NOERROR,qr,aa,tc,rd,ra,ad,cd,Z,0,0,0,0,do,0,nsid") - l.setQuestion(".", dns.TypeNS, dns.ClassINET) - l.probe() - return dnsRcodeWhacky -} - -func dnsRcodeWhacky(l *lexer) stateFn { - l.verbose("RcodeWhacky") - l.setString("QUERY,31,qr,aa,tc,rd,ra,ad,cd,Z,0,0,0,0,do,0,nsid") - l.setQuestion(".", dns.TypeNS, dns.ClassINET) - l.probe() - return dnsRcodeNotZone -} - -func dnsRcodeNotZone(l *lexer) stateFn { - l.verbose("RcodeNotZone") - l.setString("QUERY,NOTZONE,qr,aa,tc,rd,ra,ad,cd,z,0,0,0,0,do,0,nsid") - l.setQuestion(".", dns.TypeNS, dns.ClassINET) - l.probe() - return nil -} -*/ diff --git a/examples/fp/fp.go b/examples/fp/fp.go index 6e279ddb..a22b7e88 100644 --- a/examples/fp/fp.go +++ b/examples/fp/fp.go @@ -3,7 +3,6 @@ package main import ( "dns" - "fmt" "strconv" "strings" ) @@ -32,37 +31,15 @@ const ( EURID = "EurID" ) -func startParse(addr string) { - l := &lexer{ - addr: addr, - client: dns.NewClient(), - fp: new(fingerprint), - items: make(chan item), - state: dnsAlive, - debug: true, - } - - l.run() - - // Not completely sure about this code.. - for { - item := <-l.items - fmt.Printf("{%s %s}\n", itemString[item.typ], item.val) - if l.state == nil { - break - } - } -} - -// SendProbe creates a packet and sends it to the nameserver. It +// probe creates a packet and sends it to the nameserver. It // returns a fingerprint. -func sendProbe(c *dns.Client, addr string, f *fingerprint) *fingerprint { - m := f.toProbe() +func probe(c *dns.Client, addr string, f *fingerprint) *fingerprint { + m := f.msg() r, err := c.Exchange(m, addr) if err != nil { return errorToFingerprint(err) } - return msgToFingerprint(r) + return toFingerprint(r) } // This leads to strings like: "miek.nl.,IN,A,QUERY,NOERROR,qr,aa,tc,RD,ad,cd,z,1,0,0,1,DO,4096,NSID" @@ -90,7 +67,7 @@ type fingerprint struct { // 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'. -// This leads to strings like: "QUERY,NOERROR,qr,aa,tc,RD,ad,cd,z,1,0,0,1,DO,4096,NSID" // TODO fix doc +// This leads to strings like: ".,IN,NS,QUERY,NOERROR,qr,aa,tc,RD,ad,cd,z,1,0,0,1,DO,4096,NSID" func (f *fingerprint) String() string { if f == nil { return "" @@ -155,7 +132,7 @@ func (f *fingerprint) StringNoSections() string { return strings.Join(s[2:13], ",") } -// SetString set the string to fp.. todo +// SetString sets the strings str to the fingerprint *f. func (f *fingerprint) setString(str string) { for i, s := range strings.Split(str, ",") { switch i { @@ -208,7 +185,7 @@ func (f *fingerprint) setString(str string) { case 19: f.Nsid = s == strings.ToUpper("nsid") default: - panic("unhandled fingerprint") + panic("unhandled fingerprint field") } } return @@ -231,7 +208,8 @@ func errorToFingerprint(e error) *fingerprint { return f } -func msgToFingerprint(m *dns.Msg) *fingerprint { +// Convert a Msg to a fingerprint +func toFingerprint(m *dns.Msg) *fingerprint { if m == nil { return nil } @@ -279,8 +257,8 @@ func msgToFingerprint(m *dns.Msg) *fingerprint { // 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,nsid -func (f *fingerprint) toProbe() *dns.Msg { +// .,IN,NS,QUERY,NOERROR,qr,aa,tc,RD,ad,ad,z,1,0,0,1,DO,4096,nsid +func (f *fingerprint) msg() *dns.Msg { m := new(dns.Msg) m.MsgHdr.Id = dns.Id() m.Question = make([]dns.Question, 1) diff --git a/examples/fp/lex.go b/examples/fp/lex.go deleted file mode 100644 index 69529b7b..00000000 --- a/examples/fp/lex.go +++ /dev/null @@ -1,81 +0,0 @@ -// 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 - -package main - -import ( - "dns" - "fmt" -) - -type itemType int - -type item struct { - typ itemType - val string -} - -const ( - itemError itemType = iota - itemSoftware // the name of the DNS server software - itemVendor // vendor of the DNS software - itemVersionMajor // the major version of the software (empty if not determined) - itemVersionMinor // the minor version of the software (empty if not determined) - itemVersionPatch // the patch level of the software (empty if not determined) -) - -var itemString = map[itemType]string{ - itemError: "error", - itemSoftware: "software", - itemVendor: "vendor", - itemVersionMajor: "major", - itemVersionMinor: "minor", - itemVersionPatch: "patch", -} - -// 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. - 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 { - f := sendProbe(l.client, l.addr, l.fp) - if l.debug { - fmt.Printf(" QR fp: %s\n", f) - } - return f -} - -func (l *lexer) emit(i *item) { - l.items <- *i -} - -func (l *lexer) setString(s string) { - l.fp.setString(s) - if l.debug { - fmt.Printf(" Q fp: %s\n", s) - } -} - -func (l *lexer) run() { - go func() { - for l.state != nil { - l.state = l.state(l) - } - close(l.items) - }() -} - -func (l *lexer) verbose(s string) { - if l.debug { - fmt.Printf(" dns%s\n", s) - } -} diff --git a/examples/fp/q.go b/examples/fp/q.go index a22f40d4..fd8ea1fd 100644 --- a/examples/fp/q.go +++ b/examples/fp/q.go @@ -6,7 +6,6 @@ import ( "fmt" "os" "strconv" - "strings" ) func q(w dns.RequestWriter, m *dns.Msg) { @@ -19,271 +18,39 @@ func q(w dns.RequestWriter, m *dns.Msg) { } func main() { - dnssec := flag.Bool("dnssec", false, "request DNSSEC records") - query := flag.Bool("question", false, "show question") - short := flag.Bool("short", false, "abbreviate long DNSKEY and RRSIG RRs") - check := flag.Bool("check", false, "check internal DNSSEC consistency") port := flag.Int("port", 53, "port number to use") - aa := flag.Bool("aa", false, "set AA flag in query") - ad := flag.Bool("ad", false, "set AD flag in query") - cd := flag.Bool("cd", false, "set CD flag in query") - rd := flag.Bool("rd", true, "unset RD flag in query") - tcp := flag.Bool("tcp", false, "TCP mode") - nsid := flag.Bool("nsid", false, "ask for NSID") - fp := flag.Bool("fp", false, "enable server detection") flag.Usage = func() { - fmt.Fprintf(os.Stderr, "Usage: %s [@server] [qtype] [qclass] [name ...]\n", os.Args[0]) + fmt.Fprintf(os.Stderr, "Usage: %s [-port 53] [@server]\n", os.Args[0]) flag.PrintDefaults() } conf, _ := dns.ClientConfigFromFile("/etc/resolv.conf") nameserver := "@" + conf.Servers[0] - qtype := uint16(0) - qclass := uint16(dns.ClassINET) // Default qclass - var qname []string - flag.Parse() -Flags: for i := 0; i < flag.NArg(); i++ { // If it starts with @ it is a nameserver if flag.Arg(i)[0] == '@' { nameserver = flag.Arg(i) - continue Flags + break } - // First class, then type, to make ANY queries possible - // And if it looks like type, it is a type - if k, ok := dns.Str_rr[strings.ToUpper(flag.Arg(i))]; ok { - qtype = k - continue Flags - } - // If it looks like a class, it is a class - if k, ok := dns.Str_class[strings.ToUpper(flag.Arg(i))]; ok { - qclass = k - continue Flags - } - // If it starts with TYPExxx it is unknown rr - if strings.HasPrefix(flag.Arg(i), "TYPE") { - i, e := strconv.Atoi(string([]byte(flag.Arg(i))[4:])) - if e == nil { - qtype = uint16(i) - continue Flags - } - } - - // Anything else is a qname - qname = append(qname, flag.Arg(i)) - } - if len(qname) == 0 { - qname = make([]string, 1) - qname[0] = "." - qtype = dns.TypeNS - } - if qtype == 0 { - qtype = dns.TypeA - } - + } nameserver = string([]byte(nameserver)[1:]) // chop off @ nameserver += ":" + strconv.Itoa(*port) - - // ipv6 todo - // We use the async query handling, just to show how - // it is to be used. - dns.HandleQueryFunc(".", q) - dns.ListenAndQuery(nil, nil) - c := dns.NewClient() - if *tcp { - c.Net = "tcp" - } - - m := new(dns.Msg) - m.MsgHdr.Authoritative = *aa - m.MsgHdr.AuthenticatedData = *ad - m.MsgHdr.CheckingDisabled = *cd - m.MsgHdr.RecursionDesired = *rd - m.Question = make([]dns.Question, 1) - if *dnssec || *nsid { - o := new(dns.RR_OPT) - o.Hdr.Name = "." - o.Hdr.Rrtype = dns.TypeOPT - if *dnssec { - o.SetDo() - o.SetUDPSize(dns.DefaultMsgSize) - } - if *nsid { - o.SetNsid("") - } - m.Extra = append(m.Extra, o) - } - - if *fp { - startParse(nameserver) - return - } - for _, v := range qname { - 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) - } - - i := 0 -forever: - for { - select { - case r := <-dns.DefaultReplyChan: - if r.Reply != nil { - if r.Reply.Rcode == dns.RcodeSuccess { - if r.Request.Id != r.Reply.Id { - fmt.Printf("Id mismatch\n") - } - } - if *check { - sigCheck(r.Reply, nameserver, *tcp) - nsecCheck(r.Reply) - } - if *short { - r.Reply = shortMsg(r.Reply) - } - - fmt.Printf("%v", r.Reply) - } - i++ - if i == len(qname) { - break forever - } - } - } -} - -func sectionCheck(set []dns.RR, server string, tcp bool) { - for _, rr := range set { - if rr.Header().Rrtype == dns.TypeRRSIG { - rrset := getRRset(set, rr.Header().Name, rr.(*dns.RR_RRSIG).TypeCovered) - key := getKey(rr.(*dns.RR_RRSIG).SignerName, rr.(*dns.RR_RRSIG).KeyTag, server, tcp) - if key == nil { - fmt.Printf(";? DNSKEY %s/%d not found\n", rr.(*dns.RR_RRSIG).SignerName, rr.(*dns.RR_RRSIG).KeyTag) - continue - } - if err := rr.(*dns.RR_RRSIG).Verify(key, rrset); err != nil { - fmt.Printf(";- Bogus signature, %s does not validate (DNSKEY %s/%d)\n", shortSig(rr.(*dns.RR_RRSIG)), key.Header().Name, key.KeyTag()) - } else { - fmt.Printf(";+ Secure signature, %s validates (DNSKEY %s/%d)\n", shortSig(rr.(*dns.RR_RRSIG)), key.Header().Name, key.KeyTag()) - } - } - } -} - -// Check if we have nsec3 records and if so, check them -func nsecCheck(in *dns.Msg) { - for _, r := range in.Answer { - if r.Header().Rrtype == dns.TypeNSEC3 { - goto Check - } - } - for _, r := range in.Ns { - if r.Header().Rrtype == dns.TypeNSEC3 { - goto Check - } - } - for _, r := range in.Extra { - if r.Header().Rrtype == dns.TypeNSEC3 { - goto Check - } - } - return -Check: - w, err := in.Nsec3Verify(in.Question[0]) - switch w { - case dns.NSEC3_NXDOMAIN: - fmt.Printf(";+ Correct denial of existence (NSEC3/NXDOMAIN)\n") - case dns.NSEC3_NODATA: - fmt.Printf(";+ Correct denial of existence (NSEC3/NODATA)\n") - default: - // w == 0 - if err != nil { - fmt.Printf(";- Incorrect denial of existence (NSEC3): %s\n",err.Error()) - } + c := dns.NewClient() + fp := new(fingerprint) + for _, s := range fingerprints() { + fp.setString(s) + fp1 := probe(c, nameserver, fp) + println(s, "|", fp1.String()) } } -// Check the sigs in the msg, get the signer's key (additional query), get the -// rrset from the message, check the signature(s) -func sigCheck(in *dns.Msg, server string, tcp bool) { - sectionCheck(in.Answer, server, tcp) - sectionCheck(in.Ns, server, tcp) - sectionCheck(in.Extra, server, tcp) -} - -// Return the RRset belonging to the signature with name and type t -func getRRset(l []dns.RR, name string, t uint16) []dns.RR { - l1 := make([]dns.RR, 0) - for _, rr := range l { - if rr.Header().Name == name && rr.Header().Rrtype == t { - l1 = append(l1, rr) - } - } - return l1 -} - -// Get the key from the DNS (uses the local resolver) and return them. -// If nothing is found we return nil -func getKey(name string, keytag uint16, server string, tcp bool) *dns.RR_DNSKEY { - c := dns.NewClient() - if tcp { - c.Net = "tcp" +// A list of all the evil finger prints +func fingerprints() []string { + return []string { + ".,CH,TXT,QUERY,NOERROR,qr,aa,tc,RD,ra,ad,cd,z,0,0,0,0,DO,4097,NSID", // general + "auThoRs.bInD.,CH,TXT,QUERY,NOERROR,qr,aa,tc,rd,ra,ad,cd,z,0,0,0,0,do,0,nsid", // case + "bind.,NONE,SOA,NOTIFY,NOERROR,qr,AA,tc,RD,ra,ad,cd,Z,0,0,0,0,do,0,nsid", // notify } - m := new(dns.Msg) - m.SetQuestion(name, dns.TypeDNSKEY) - r, err := c.Exchange(m, server) - if err != nil { - return nil - } - for _, k := range r.Answer { - if k1, ok := k.(*dns.RR_DNSKEY); ok { - if k1.KeyTag() == keytag { - return k1 - } - } - } - return nil -} - -// shorten RRSIG to "miek.nl RRSIG(NS)" -func shortSig(sig *dns.RR_RRSIG) string { - return sig.Header().Name + " RRSIG(" + dns.Rr_str[sig.TypeCovered] + ")" -} - -// Walk trough message and short Key data and Sig data -func shortMsg(in *dns.Msg) *dns.Msg { - for i := 0; i < len(in.Answer); i++ { - in.Answer[i] = shortRR(in.Answer[i]) - } - for i := 0; i < len(in.Ns); i++ { - in.Ns[i] = shortRR(in.Ns[i]) - } - for i := 0; i < len(in.Extra); i++ { - in.Extra[i] = shortRR(in.Extra[i]) - } - return in -} - -func shortRR(r dns.RR) dns.RR { - switch t := r.(type) { - case *dns.RR_DS: - t.Digest = "..." - case *dns.RR_DNSKEY: - t.PublicKey = "..." - case *dns.RR_RRSIG: - t.Signature = "..." - case *dns.RR_NSEC3: - t.Salt = "-" // Nobody cares - if len(t.TypeBitMap) > 5 { - t.TypeBitMap = t.TypeBitMap[1:5] - } - } - return r }