Fix funkensturm and make it simpler

This commit is contained in:
Miek Gieben 2011-08-01 13:15:15 +02:00
parent ba30caf3b3
commit 327c9870c4
6 changed files with 119 additions and 246 deletions

View File

@ -4,46 +4,27 @@ import (
"dns"
)
func match(m *dns.Msg, d int) (*dns.Msg, bool) {
// Matching criteria
switch d {
case IN:
// nothing
case OUT:
// Note that when sending back only the mangling is important
// the actual return code of these function isn't checked by
// funkensturm
}
// Packet Mangling
switch d {
case IN:
// nothing
case OUT:
// nothing
}
func match(m *dns.Msg) (*dns.Msg, bool) {
return m, true
}
func send(m *dns.Msg, ok bool) (o *dns.Msg) {
func send(m *dns.Msg) (buf []byte) {
var o *dns.Msg
for _, c := range qr {
o = c.Client.Exchange(m, c.Addr)
}
return
buf, _ = o.Pack()
return
}
// Return the configration
func funkensturm() *Funkensturm {
f := new(Funkensturm)
func NewFunkenSturm() *FunkenSturm {
f := new(FunkenSturm)
f.Funk = make([]*Funk, 1) // 1 Chain
f.Setup = func() bool { return true } // no setup
// 1 match function, use AND as op (doesn't matter in this case)
f.Matches = make([]Match, 1)
f.Matches[0].Op = AND
f.Matches[0].Func = match
// 1 action
f.Actions = make([]Action, 1)
f.Actions[0].Func = send
f.Funk[0] = NewFunk(1) // First chains with 1 match/action
f.Funk[0].Matches[0].Op = AND
f.Funk[0].Matches[0].Func = func(m *dns.Msg) (*dns.Msg, bool) { return m ,true }
f.Funk[0].Action = send
return f
}

View File

@ -3,9 +3,7 @@ package main
// This proxy delays pkt that have the RD bit set.
import (
"os"
"dns"
"fmt"
"time"
)
@ -25,62 +23,39 @@ func checkDelay() (ti int64, limitok bool) {
}
// the only matching we do is on the RD bit
func match(m *dns.Msg, d int) (*dns.Msg, bool) {
// Matching criteria
var ok bool
switch d {
case IN:
// only delay pkts with RD bit
ok = m.MsgHdr.RecursionDesired == true
case OUT:
// nothing
}
// Packet Mangling
switch d {
case IN:
// nothing
case OUT:
// nothing
}
return m, ok
func match(m *dns.Msg) (*dns.Msg, bool) {
// only delay pkts with RD bit
return m, m.MsgHdr.RecursionDesired == true
}
func delay(m *dns.Msg, ok bool) (o *dns.Msg) {
var ok1 bool
switch ok {
case true:
previous, ok1 = checkDelay()
if !ok1 {
fmt.Fprintf(os.Stderr, "Info: Dropping: too often\n")
time.Sleep(NSECDELAY)
return
} else {
fmt.Fprintf(os.Stderr, "Info: Ok: let it through\n")
for _, c := range qr {
o = c.Client.Exchange(m, c.Addr)
}
return
}
case false:
for _, c := range qr {
o = c.Client.Exchange(m, c.Addr)
}
return
}
return
func delay(m *dns.Msg) (buf []byte) {
var (
ok1 bool
o *dns.Msg
)
if previous, ok1 = checkDelay(); !ok1 {
println("Info: Dropping: too often")
time.Sleep(NSECDELAY)
return
}
println("Info: Ok: let it through")
for _, c := range qr {
o = c.Client.Exchange(m, c.Addr)
}
buf, _ = o.Pack()
return
}
// Return the configration
func funkensturm() *Funkensturm {
f := new(Funkensturm)
func NewFunkenSturm() *FunkenSturm {
f := new(FunkenSturm)
f.Funk = make([]*Funk, 1)
// Not concurrent save
f.Setup = func() bool { previous = time.Nanoseconds(); return true }
f.Matches = make([]Match, 1)
f.Matches[0].Op = AND
f.Matches[0].Func = match
f.Actions = make([]Action, 1)
f.Actions[0].Func = delay
f.Funk[0] = NewFunk(1)
f.Funk[0].Matches[0].Op = AND
f.Funk[0].Matches[0].Func = match
f.Funk[0].Action = delay
return f
}

View File

@ -32,7 +32,6 @@ func (c Cache) evict(q dns.Msg) {
// todo
}
// Add an entry from the cache. The old entry (if any) gets
// overwritten
func (c Cache) add(q *dns.Msg) {
@ -63,7 +62,7 @@ func (c Cache) lookup(q *dns.Msg) []byte {
return nil
}
func checkcache(m *dns.Msg, ok bool) (o []byte) {
func checkcache(m *dns.Msg) (o []byte) {
// Check if we have the packet in Cache
// if so, return it. Otherwise ask the
// server, return that answer and put it
@ -77,7 +76,6 @@ func checkcache(m *dns.Msg, ok bool) (o []byte) {
}
println("Cache miss")
var p *dns.Msg
// nothing found
for _, c := range qr {
p = c.Client.Exchange(m, c.Addr)
}
@ -89,10 +87,12 @@ func checkcache(m *dns.Msg, ok bool) (o []byte) {
var cache Cache
// Return the configration
func funkensturm() *Funkensturm {
f := new(Funkensturm)
func NewFunkenSturm() *FunkenSturm {
f := new(FunkenSturm)
f.Funk = make([]*Funk, 1)
f.Setup = func() bool { cache = NewCache(); return true }
f.ActionsRaw = make([]ActionRaw, 1)
f.ActionsRaw[0].FuncRaw = checkcache
f.Funk[0] = NewFunk(1)
f.Funk[0].Matches[0].Func = func(m *dns.Msg) (*dns.Msg, bool) { return m, true }
f.Funk[0].Action = checkcache
return f
}

View File

@ -18,13 +18,13 @@ func sign(m *dns.Msg) *dns.Msg {
sg.Inception = 1293942305 // date -u '+%s' -d"2011-01-02 04:25:05"
sg.KeyTag = pubkey.KeyTag() // Get the keyfrom the Key
sg.SignerName = pubkey.Hdr.Name
sg.Algorithm = dns.AlgRSASHA256
sg.Algorithm = dns.RSASHA256
if len(m.Answer) > 0 {
// sign the first record
an := m.Answer[0]
sg.TypeCovered = an.Header().Rrtype
sg.Labels = dns.LabelCount(an.Header().Name)
sg.Labels = dns.Labels(an.Header().Name)
sg.OrigTtl = an.Header().Ttl
switch p:=privkey.(type) {
case *rsa.PrivateKey:
@ -35,34 +35,16 @@ func sign(m *dns.Msg) *dns.Msg {
return m
}
func match(m *dns.Msg, d int) (*dns.Msg, bool) {
// Matching criteria
switch d {
case IN:
// nothing
case OUT:
// Note that when sending back only the mangling is important
// the actual return code of these function isn't checked by
// funkensturm
}
// Packet Mangling
switch d {
case IN:
// nothing
case OUT:
if m.Question[0].Name == "www.example.org." {
// On the way out sign the packet
m = sign(m) // keys are global
}
}
return m, true
func match(m *dns.Msg) (*dns.Msg, bool) {
return m, m.Question[0].Name == "www.example.org."
}
func send(m *dns.Msg, ok bool) (o *dns.Msg) {
func send(m *dns.Msg) (o []byte) {
var p *dns.Msg
for _, c := range qr {
o = c.Client.Exchange(m, c.Addr)
p = c.Client.Exchange(m, c.Addr)
}
o, _ = sign(p).Pack()
return
}
@ -92,16 +74,13 @@ Activate: 20110122104659`
}
// Return the configration
func funkensturm() *Funkensturm {
f := new(Funkensturm)
func NewFunkenSturm() *FunkenSturm {
f := new(FunkenSturm)
f.Funk = make([]*Funk, 1) // 1 Chain
f.Setup = setup
f.Matches = make([]Match, 1)
f.Matches[0].Op = AND
f.Matches[0].Func = match
f.Actions = make([]Action, 1)
f.Actions[0].Func = send
f.Funk[0] = NewFunk(1)
f.Funk[0].Matches[0].Func = match
f.Funk[0].Action = send
return f
}

View File

@ -17,29 +17,20 @@ import (
"runtime/pprof"
)
var qr []*Funk
var f *Funkensturm
var qr []*FunkClient
var f *FunkenSturm
var verbose *bool
// A small wrapper to keep the address together
// with a client.
type Funk struct {
type FunkClient struct {
Client *dns.Client
Addr string
}
// Where does the packet come from?
// IN: initial packet received by the Responder
// any modifications here will reflect what kind of
// pkt is sent through.
// OUT: pkt as received back from a server. Modifications here will reflect
// how the packet is send back to the original requester.
const (
IN = iota // set when receiving a packet
OUT // set when sending a packet
OR // chain match functions with logical 'or'
AND // chain match functions with logical 'and'
OR = iota // chain match functions with logical 'or'
AND // chain match functions with logical 'and'
)
// A Match function is used on a DNS packet and
@ -49,18 +40,21 @@ const (
// Op is used in chaining Match-functions together
type Match struct {
Op int // boolean op: OR, AND
Func func(*dns.Msg, int) (*dns.Msg, bool)
Func func(*dns.Msg) (*dns.Msg, bool)
}
// An action is something that is done with a packet. Funkensturm
// does not impose any restriction on what this can be, except that
// is must remain a valid DNS packet.
type Action struct {
Func func(*dns.Msg, bool) *dns.Msg
// A FunkAction combines a set of matches and an action. If
// the matches are successfull (return true) the action is
// performed
type Funk struct {
Matches []Match
Action func(*dns.Msg) []byte
}
type ActionRaw struct {
FuncRaw func(*dns.Msg, bool) []byte
func NewFunk(m int) *Funk {
f := new(Funk)
f.Matches = make([]Match, m)
return f
}
// A complete config for Funkensturm. All matches in the Matches slice are
@ -69,115 +63,58 @@ type ActionRaw struct {
//
// The final outcome (does a packet match or not?) is calculated as follows:
// true Match[0].Op Match[0].Func() Match[1].Op Match[1].Func() ...
// The result of this matching is given to the action function(s). They can then
// decide what to do with a packet in the 'true' and in the 'false' case.
type Funkensturm struct {
Setup func() bool // Inital setup (for extra resolvers, or loading keys, or ...)
Matches []Match // Match- and modify functions
Actions []Action // What to do with the packets
ActionsRaw []ActionRaw // Raw action, return []byte not *dns.Msg
// The result of this matching is given to the action function. That last
// function decides "what to do with the packet" is the match(es) return 'true'
// There is no NewFunkenSturm() because that is what needs to be done in the
// configuration file.
type FunkenSturm struct {
Setup func() bool // Inital setup (for extra resolvers, or loading keys, or ...)
Funk []*Funk // The configuration
}
func verboseprint(i *dns.Msg, indent string) {
for _, line := range strings.Split(i.String(), "\n", -1) {
fmt.Printf("%s%s\n", indent, line)
}
fmt.Println()
}
func doFunkensturm(pkt *dns.Msg) ([]byte, os.Error) {
if *verbose {
verboseprint(pkt, "> ")
}
func doFunkenSturm(pkt *dns.Msg) (ret []byte) {
// No matter what, we refuse to answer requests with the response bit set.
if pkt.MsgHdr.Response == true {
return nil, &dns.Error{Error: "Response bit set, not replying"}
return nil
}
// Loop through the Match* functions and decide what to do
// Note the packet can be changed by these function, this
// change is cumulative.
ok, ok1 := true, true
pkt1 := pkt
if len(f.Matches) > 0 {
for _, m := range f.Matches {
pkt1, ok1 = m.Func(pkt1, IN)
switch m.Op {
case AND:
ok = ok && ok1
case OR:
ok = ok || ok1
}
}
// Loop through the Funks and decide what to do with
// the packet.
for _, f := range f.Funk {
ok := true
for _, m := range f.Matches {
var ok1 bool
pkt, ok1 = m.Func(pkt)
switch m.Op {
case AND:
ok = ok && ok1
case OR:
ok = ok || ok1
}
}
if ok {
ret = f.Action(pkt)
return
}
}
// If still alive, non of the action did something.
// So we do it ourselves
var o *dns.Msg
for _, c := range qr {
o = c.Client.Exchange(pkt, c.Addr)
}
if *verbose { //modified
verboseprint(pkt1, ">> ")
}
// Loop through the Actions.Func* and do something with the
// packet. Note there can only be one returned packet.
// We use 'ok' to signal what the above match did: true or false
var resultpkt *dns.Msg
for _, a := range f.Actions {
resultpkt = a.Func(pkt1, ok)
}
if *verbose { //orignal out
verboseprint(resultpkt, "< ")
}
// loop again for matching, but now with OUT, this is done
// for some last minute packet changing. Note the boolean return
// code isn't used any more, i.e No more actions are allowed
// anymore
if len(f.Matches) > 0 {
pkt1 = resultpkt
for _, m := range f.Matches {
pkt1, _ = m.Func(pkt1, OUT)
}
if pkt1 == nil {
// don't need to send something back
return nil, nil
}
}
if len(f.ActionsRaw) > 0 {
var buf []byte
for _, r := range f.ActionsRaw {
buf = r.FuncRaw(pkt, ok)
}
if buf != nil {
// send the buffer back at once
return buf, nil
}
}
if *verbose { // modified out
verboseprint(pkt1, "<< ")
}
out, ok1 := pkt1.Pack()
if !ok1 {
return nil, &dns.Error{Error: "Packing packet failed"}
}
// Some final byte changing function here?
return out, nil
ret, _ = o.Pack()
return
}
func serve(w dns.ResponseWriter, req *dns.Msg) {
out, err := doFunkensturm(req)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %s\n", err.String())
return
}
if out != nil {
if out := doFunkenSturm(req); out != nil {
w.Write(out)
}
}
func listenAndServe(add, net string) {
err := dns.ListenAndServe(add, net, nil)
if err != nil {
if err := dns.ListenAndServe(add, net, nil); err != nil {
fmt.Printf("Failed to setup: " + net + " " + add + "\n")
}
}
@ -202,14 +139,14 @@ func main() {
}
clients := strings.Split(*rserver, ",", -1)
qr = make([]*Funk, len(clients))
qr = make([]*FunkClient, len(clients))
for i, ra := range clients {
qr[i] = new(Funk)
qr[i] = new(FunkClient)
qr[i].Client = dns.NewClient()
qr[i].Addr = ra
}
f = funkensturm()
f = NewFunkenSturm()
ok := f.Setup()
if !ok {
fmt.Fprintf(os.Stderr, "Setup failed")

View File

@ -17,8 +17,8 @@ type QueryHandler interface {
QueryDNS(w RequestWriter, q *Msg)
}
// A RequestWriter interface is used by an DNS query handler to
// construct an DNS request.
// The RequestWriter interface is used by a DNS query handler to
// construct a DNS request.
type RequestWriter interface {
WriteMessages([]*Msg)
Write(*Msg)
@ -36,6 +36,7 @@ type reply struct {
tsigTimersOnly bool
}
// A Request is a incoming message from a Client
type Request struct {
Request *Msg
Addr string