Create fp as a type
This commit is contained in:
parent
1b96a4413a
commit
fb68e29eb1
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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
20
msg.go
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue