Add seperate fp program
This commit is contained in:
parent
8f66b74c86
commit
90228852e1
|
@ -0,0 +1,8 @@
|
||||||
|
# Copyright 2009 The Go Authors. All rights reserved.
|
||||||
|
# Use of this source code is governed by a BSD-style
|
||||||
|
# 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
|
||||||
|
DEPS=../../
|
||||||
|
include $(GOROOT)/src/Make.cmd
|
|
@ -0,0 +1,292 @@
|
||||||
|
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
|
||||||
|
}
|
||||||
|
*/
|
|
@ -0,0 +1,324 @@
|
||||||
|
// Package main provides ...
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"dns"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// .,IN,NS,QUERY,NOERROR,qr,aa,tc,rd,ra,ad,cd,z,0,0,0,0,do,0,nsid
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Detected software types
|
||||||
|
NSD = "NSD"
|
||||||
|
BIND = "BIND"
|
||||||
|
POWERDNS = "PowerDNS"
|
||||||
|
WINDOWSDNS = "Windows DNS"
|
||||||
|
MARADNS = "MaraDNS"
|
||||||
|
NEUSTARDNS = "Neustar DNS"
|
||||||
|
ATLAS = "Atlas"
|
||||||
|
YADIFA = "Yadifa"
|
||||||
|
|
||||||
|
// Vendors
|
||||||
|
ISC = "ISC"
|
||||||
|
MARA = "MaraDNS.org" // check
|
||||||
|
NLNETLABS = "NLnet Labs"
|
||||||
|
MICROSOFT = "Microsoft"
|
||||||
|
POWER = "PowerDNS.com"
|
||||||
|
NEUSTAR = "Neustar"
|
||||||
|
VERISIGN = "Verisign"
|
||||||
|
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
|
||||||
|
// returns a fingerprint.
|
||||||
|
func sendProbe(c *dns.Client, addr string, f *fingerprint) *fingerprint {
|
||||||
|
m := f.toProbe()
|
||||||
|
r, err := c.Exchange(m, addr)
|
||||||
|
if err != nil {
|
||||||
|
return errorToFingerprint(err)
|
||||||
|
}
|
||||||
|
return msgToFingerprint(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"
|
||||||
|
type fingerprint struct {
|
||||||
|
Query dns.Question // Question to ask or Question of the reply
|
||||||
|
Error 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
|
||||||
|
Nsid bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
func (f *fingerprint) String() string {
|
||||||
|
if f == nil {
|
||||||
|
return "<nil>"
|
||||||
|
}
|
||||||
|
// Use the same order as in Perl's fpdns. But use much more flags.
|
||||||
|
var s string
|
||||||
|
// The Question.
|
||||||
|
if len(f.Query.Name) == 0 {
|
||||||
|
s = "."
|
||||||
|
} else {
|
||||||
|
s = f.Query.Name
|
||||||
|
}
|
||||||
|
if _, ok := dns.Class_str[f.Query.Qclass]; ok {
|
||||||
|
s += "," + dns.Class_str[f.Query.Qclass]
|
||||||
|
} else {
|
||||||
|
s += "," + "CLASS" + strconv.Itoa(int(f.Query.Qclass))
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := dns.Rr_str[f.Query.Qtype]; ok {
|
||||||
|
s += "," + dns.Rr_str[f.Query.Qtype]
|
||||||
|
} else {
|
||||||
|
s += "," + "TYPE" + strconv.Itoa(int(f.Query.Qtype))
|
||||||
|
}
|
||||||
|
|
||||||
|
if op, ok := dns.Opcode_str[f.Opcode]; ok {
|
||||||
|
s += "," + op
|
||||||
|
} else { // number
|
||||||
|
s += "," + valueOfInt(f.Opcode)
|
||||||
|
}
|
||||||
|
|
||||||
|
if op, ok := dns.Rcode_str[f.Rcode]; ok {
|
||||||
|
s += "," + op
|
||||||
|
} else { // number
|
||||||
|
s += "," + valueOfInt(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.RecursionAvailable, ",ra")
|
||||||
|
s += valueOfBool(f.AuthenticatedData, ",ad")
|
||||||
|
s += valueOfBool(f.CheckingDisabled, ",cd")
|
||||||
|
s += valueOfBool(f.Zero, ",z")
|
||||||
|
|
||||||
|
s += "," + valueOfInt(f.Question)
|
||||||
|
s += "," + valueOfInt(f.Answer)
|
||||||
|
s += "," + valueOfInt(f.Ns)
|
||||||
|
s += "," + valueOfInt(f.Extra)
|
||||||
|
|
||||||
|
s += valueOfBool(f.Do, ",do")
|
||||||
|
s += "," + valueOfInt(f.UDPSize)
|
||||||
|
s += valueOfBool(f.Nsid, ",nsid")
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// fingerStringNoSections returns the strings representation
|
||||||
|
// without the sections' count and the EDNS0 stuff and the query
|
||||||
|
func (f *fingerprint) StringNoSections() string {
|
||||||
|
s := strings.SplitN(f.String(), ",", 14)
|
||||||
|
return strings.Join(s[2:13], ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetString set the string to fp.. todo
|
||||||
|
func (f *fingerprint) setString(str string) {
|
||||||
|
for i, s := range strings.Split(str, ",") {
|
||||||
|
switch i {
|
||||||
|
case 0: // question section domain name
|
||||||
|
f.Query.Name = s
|
||||||
|
case 1: // Qclass
|
||||||
|
f.Query.Qclass = 0
|
||||||
|
if c, ok := dns.Str_class[s]; ok {
|
||||||
|
f.Query.Qclass = c
|
||||||
|
}
|
||||||
|
case 2: // Qtype
|
||||||
|
f.Query.Qtype = 0
|
||||||
|
if c, ok := dns.Str_rr[s]; ok {
|
||||||
|
f.Query.Qtype = c
|
||||||
|
}
|
||||||
|
case 3:
|
||||||
|
if op, ok := dns.Str_opcode[s]; ok {
|
||||||
|
f.Opcode = op
|
||||||
|
} else { // number
|
||||||
|
f.Opcode = valueOfString(s)
|
||||||
|
}
|
||||||
|
case 4:
|
||||||
|
if op, ok := dns.Str_rcode[s]; ok {
|
||||||
|
f.Rcode = op
|
||||||
|
} else { // number
|
||||||
|
f.Rcode = valueOfString(s)
|
||||||
|
}
|
||||||
|
case 5:
|
||||||
|
f.Response = s == strings.ToUpper("qr")
|
||||||
|
case 6:
|
||||||
|
f.Authoritative = s == strings.ToUpper("aa")
|
||||||
|
case 7:
|
||||||
|
f.Truncated = s == strings.ToUpper("tc")
|
||||||
|
case 8:
|
||||||
|
f.RecursionDesired = s == strings.ToUpper("rd")
|
||||||
|
case 9:
|
||||||
|
f.RecursionAvailable = s == strings.ToUpper("ra")
|
||||||
|
case 10:
|
||||||
|
f.AuthenticatedData = s == strings.ToUpper("ad")
|
||||||
|
case 11:
|
||||||
|
f.CheckingDisabled = s == strings.ToUpper("cd")
|
||||||
|
case 12:
|
||||||
|
f.Zero = s == strings.ToUpper("z")
|
||||||
|
case 13, 14, 15, 16:
|
||||||
|
// Can not set lenght of the section in the message
|
||||||
|
case 17:
|
||||||
|
f.Do = s == strings.ToUpper("do")
|
||||||
|
case 18:
|
||||||
|
f.UDPSize = valueOfString(s)
|
||||||
|
case 19:
|
||||||
|
f.Nsid = s == strings.ToUpper("nsid")
|
||||||
|
default:
|
||||||
|
panic("unhandled fingerprint")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *fingerprint) ok() bool {
|
||||||
|
return f.Error == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *fingerprint) error() string {
|
||||||
|
if f.Error == nil {
|
||||||
|
panic("error is nil")
|
||||||
|
}
|
||||||
|
return f.Error.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
func errorToFingerprint(e error) *fingerprint {
|
||||||
|
f := new(fingerprint)
|
||||||
|
f.Error = e
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
func msgToFingerprint(m *dns.Msg) *fingerprint {
|
||||||
|
if m == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
h := m.MsgHdr
|
||||||
|
f := new(fingerprint)
|
||||||
|
|
||||||
|
// Set the old query
|
||||||
|
if len(m.Question) > 0 {
|
||||||
|
f.Query.Name = m.Question[0].Name
|
||||||
|
f.Query.Qtype = m.Question[0].Qtype
|
||||||
|
f.Query.Qclass = m.Question[0].Qclass
|
||||||
|
}
|
||||||
|
|
||||||
|
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.RecursionAvailable = h.RecursionAvailable
|
||||||
|
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())
|
||||||
|
if len(r.(*dns.RR_OPT).Option) == 1 {
|
||||||
|
// Only support NSID atm
|
||||||
|
f.Nsid = r.(*dns.RR_OPT).Option[0].Code == dns.OptionCodeNSID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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,nsid
|
||||||
|
func (f *fingerprint) toProbe() *dns.Msg {
|
||||||
|
m := new(dns.Msg)
|
||||||
|
m.MsgHdr.Id = dns.Id()
|
||||||
|
m.Question = make([]dns.Question, 1)
|
||||||
|
m.Question[0] = dns.Question{f.Query.Name, f.Query.Qtype, f.Query.Qclass}
|
||||||
|
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))
|
||||||
|
if f.Nsid {
|
||||||
|
m.Extra[0].(*dns.RR_OPT).SetNsid("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,289 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"dns"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func q(w dns.RequestWriter, m *dns.Msg) {
|
||||||
|
w.Send(m)
|
||||||
|
r, err := w.Receive()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("%s\n", err.Error())
|
||||||
|
}
|
||||||
|
w.Write(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
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])
|
||||||
|
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
|
||||||
|
}
|
||||||
|
// 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())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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"
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
Loading…
Reference in New Issue