Fully transparant proxy in a few lines of Go

This commit is contained in:
Miek Gieben 2011-01-22 11:21:11 +01:00
parent 285c450fa8
commit 035dc7c1d0
4 changed files with 141 additions and 48 deletions

1
TODO
View File

@ -35,3 +35,4 @@ Funkensturm:
actions are defined in calling functions from funkensturm:
- sign(RRSET, privKEY), how do you specify WHAT gets signed
if rr.Header().Name == "miek.nl." -> sign header -> add sig
* use exp/eval - to inteprete the config file??

View File

@ -0,0 +1,60 @@
package main
// This is a transparant proxy config. All recevied pkt are just forwarded to the
// nameserver, hardcoded to 127.0.0.1 and then return to the original querier
import (
"dns"
"dns/resolver"
)
func matchOpcode(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 functions
switch d {
case IN:
// nothing
case OUT:
// nothing
}
return m, true
}
func actionSend(m *dns.Msg, ok bool) (*dns.Msg, bool) {
switch ok {
case true, false:
qr <- resolver.Msg{m, nil, nil}
in := <-qr
return in.Dns, true
}
return nil, false // Bug in Go, yes BUG IN GO
}
// qr is global and started by Funkensturm. If you
// need 2 or more resolvers, you'll need to start
// them yourself. This needs to be a global variable
//var qr1 chan resolver.Msg
// Return the configration
func funkensturm() *Funkensturm {
f := new(Funkensturm)
f.Setup = func() bool { return true }
f.Matches = make([]Match, 1)
f.Matches[0].Op = AND
f.Matches[0].Func = matchOpcode
f.Actions = make([]Action, 1)
f.Actions[0].Func = actionSend
return f
}

View File

@ -1,6 +1,6 @@
/*
Funkensturm rewrites DNS packets in the broadest sense of the word.
The features include delayed (re)sending of packets, (re)sending
The rewritting can include delayed (re)sending of packets, (re)sending
packets to multiple servers, rewriting the packet contents, for instance
by signing a packet, or the other way around, stripping the signatures.

View File

@ -9,6 +9,7 @@ import (
"net"
"fmt"
"dns"
"dns/resolver"
"dns/responder"
"os/signal"
)
@ -20,8 +21,8 @@ import (
// OUT: pkt as received back. Modifications here will reflect
// how the packet is send back to the original requester.
const (
IN = iota
OUT
IN = iota // set when receiving a packet
OUT // set when sending a packet
OR
AND
@ -40,7 +41,7 @@ type Match struct {
// An action is something that is done with a packet. Funkensturm
// does not impose any restriction on what this can be.
type Action struct {
Func func(in *dns.Msg) (*dns.Msg, bool)
Func func(*dns.Msg, bool) (*dns.Msg, bool)
}
// A complete config for Funkensturm. All matches in the Matches slice are
@ -51,12 +52,60 @@ type Action struct {
// If the final result is true the action(s) are called. Note that
// at least one of these action functions should send the actual message!
type Funkensturm struct {
Matches []Match
Actions []Action
Setup func() bool // Inital setup (for extra resolver or ...)
Matches []Match // Match- and mangle functions
Actions []Action // What to do wit the packets
}
type server responder.Server
func (s *server) ResponderUDP(c *net.UDPConn, a net.Addr, i []byte) {
pkt := reply(a, i)
if pkt == nil {
return
}
// 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
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 Actions.Func* and do something with the
// packet. Note there can only be one return packet. Intermidate
// action function should return nil, to signal ... bla bla
var resultpkt *dns.Msg
for _, a := range f.Actions {
resultpkt, ok1 = a.Func(pkt1, ok)
}
// 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
pkt1 = resultpkt
for _, m := range f.Matches {
pkt1, _ = m.Func(pkt1, IN)
}
out, ok1 := pkt1.Pack()
if !ok1 {
println("Failed to pack")
return
}
responder.SendUDP(out, c, a)
}
func (s *server) ResponderTCP(c *net.TCPConn, in []byte) {
}
// Small helper function
func reply(a net.Addr, in []byte) *dns.Msg {
inmsg := new(dns.Msg)
if !inmsg.Unpack(in) {
@ -69,56 +118,35 @@ func reply(a net.Addr, in []byte) *dns.Msg {
return inmsg
}
func (s *server) ResponderUDP(c *net.UDPConn, a net.Addr, i []byte) {
pkt := reply(a, i)
if pkt == nil {
return
}
// here I need to call funkensturm
matches := getMatches()
// Setup a responder takes takes care of the incoming queries.
type server responder.Server
ok, ok1 := true, true
pkt1 := pkt
for _, m := range matches {
pkt1, ok1 = m.Func(pkt1, IN)
switch m.Op {
case AND:
ok = ok && ok1
case OR:
ok = ok || ok1
}
}
// Setup a initial resolver for sending the queries somewhere else.
var qr chan resolver.Msg
if !ok {
fmt.Println("We doen niks")
return
}
println("uitkomst: ", ok)
fmt.Printf("%v\n", pkt1)
/*
out, ok := in.Dns.Pack()
if !ok {
println("Failed to pack")
return
}
responder.SendUDP(out, c, a)
*/
}
func (s *server) ResponderTCP(c *net.TCPConn, in []byte) {
}
// The configuration of Funkensturm
var f *Funkensturm
func main() {
// Start the stuff the needs started, call init()
Funkinit()
f = funkensturm()
ok := f.Setup()
if !ok {
fmt.Printf("Setup failed")
return
}
// The resolver
r := new(resolver.Resolver)
r.Servers = []string{"127.0.0.1"}
r.Port = "53"
qr = r.NewQuerier() // connect to global qr
// The responder
s := new(responder.Server)
s.Address = "127.0.0.1"
s.Port = "8053"
var srv *server
ch := make(chan bool)
go s.NewResponder(srv, ch)
rs := make(chan bool)
go s.NewResponder(srv, rs)
forever:
for {
@ -129,4 +157,8 @@ forever:
break forever
}
}
rs <- true // shut down responder
qr <- resolver.Msg{} // shut down resolver
<-rs
<-qr
}