Remove fingerprinting
This commit is contained in:
parent
90228852e1
commit
53dffab2a8
|
@ -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 dns.go
|
||||
GOFILES=q.go
|
||||
DEPS=../../
|
||||
include $(GOROOT)/src/Make.cmd
|
||||
|
|
|
@ -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
|
||||
}
|
||||
*/
|
324
examples/q/fp.go
324
examples/q/fp.go
|
@ -1,324 +0,0 @@
|
|||
// 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
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -30,7 +30,6 @@ func main() {
|
|||
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()
|
||||
|
@ -116,15 +115,10 @@ Flags:
|
|||
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)
|
||||
|
|
Loading…
Reference in New Issue