Add another funkensturm example

This time a reverse proxy.

Funkensturm itself can also use a rewrite to make it nicer
This commit is contained in:
Miek Gieben 2011-07-31 15:36:07 +02:00
parent faaf0d6b8f
commit 7cf37d9d6f
5 changed files with 173 additions and 48 deletions

View File

@ -0,0 +1,10 @@
# 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=funkensturm
GOFILES=funkensturm.go\
config_rproxy.go\
DEPS=../../
include $(GOROOT)/src/Make.cmd

View File

@ -0,0 +1,95 @@
package main
// Keep a local cache of DNS packets. Match incoming
// qname,qclass,qtype and return the saved packet.
// On a cache miss consult the nameserver
import (
"dns"
)
// Keep everything in the cache for 60 seconds
const (
CACHETTL = 60
_CLASS = 2 << 16
)
// Number in the second map denotes the class + type.
func intval(c, t uint16) int {
return int(c)*_CLASS + int(t)
}
// Ala Zone in zone.go, but slightly different
type Cache map[string]map[int][]byte
func NewCache() Cache {
c := make(Cache)
return c
}
// Remove an entry from the cache
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) {
qname := q.Question[0].Name
i := intval(q.Question[0].Qclass, q.Question[0].Qtype)
if c[qname] == nil {
im := make(map[int][]byte)
c[qname] = im
}
buf, _ := q.Pack()
im := c[qname]
im[i] = buf
}
// Lookup an entry in the cache. Returns null
// when nothing found.
func (c Cache) lookup(q *dns.Msg) []byte {
// Use the question section for looking up
i := intval(q.Question[0].Qclass, q.Question[0].Qtype)
if im, ok := c[q.Question[0].Name]; ok {
// we have the name
if d, ok := im[i]; ok {
return d
}
}
return nil
}
func checkcache(m *dns.Msg, ok bool) (o []byte) {
// Check if we have the packet in Cache
// if so, return it. Otherwise ask the
// server, return that answer and put it
// in the cache.
o = cache.lookup(m)
if o != nil {
// octet 1 and 2 contain the Id, set the one for the current pkt
o[0] = byte(m.MsgHdr.Id >> 8)
o[1] = byte(m.MsgHdr.Id)
return
}
var p *dns.Msg
// nothing found
for _, c := range qr {
p = c.Client.Exchange(m, c.Addr)
}
cache.add(p)
o, _ = p.Pack()
return
}
var cache Cache
// Return the configration
func funkensturm() *Funkensturm {
f := new(Funkensturm)
f.Setup = func() bool { cache = NewCache(); return true }
f.ActionsRaw = make([]ActionRaw, 1)
f.ActionsRaw[0].FuncRaw = checkcache
return f
}

View File

@ -8,13 +8,13 @@ package main
import (
"os"
"log"
"log"
"flag"
"fmt"
"dns"
"os/signal"
"strings"
"runtime/pprof"
"runtime/pprof"
)
var qr []*Funk
@ -24,8 +24,8 @@ var verbose *bool
// A small wrapper to keep the address together
// with a client.
type Funk struct {
Client *dns.Client
Addr string
Client *dns.Client
Addr string
}
// Where does the packet come from?
@ -59,6 +59,10 @@ type Action struct {
Func func(*dns.Msg, bool) *dns.Msg
}
type ActionRaw struct {
FuncRaw func(*dns.Msg, bool) []byte
}
// A complete config for Funkensturm. All matches in the Matches slice are
// chained together: incoming dns.Msg -> Match[0] -> dns.Msg -> Match[1] -> dns.Msg -> ...
// The dns.Msg output of Match[n] is the input for Match[n+1].
@ -68,16 +72,17 @@ type Action struct {
// 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
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
}
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()
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) {
@ -115,7 +120,7 @@ func doFunkensturm(pkt *dns.Msg) ([]byte, os.Error) {
resultpkt = a.Func(pkt1, ok)
}
if *verbose { //orignal out
if *verbose { //orignal out
verboseprint(resultpkt, "< ")
}
@ -123,17 +128,29 @@ func doFunkensturm(pkt *dns.Msg) ([]byte, os.Error) {
// for some last minute packet changing. Note the boolean return
// code isn't used any more, i.e No more actions are allowed
// anymore
pkt1 = resultpkt
for _, m := range f.Matches {
pkt1, _ = m.Func(pkt1, OUT)
}
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 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
if *verbose { // modified out
verboseprint(pkt1, "<< ")
}
@ -146,47 +163,47 @@ func doFunkensturm(pkt *dns.Msg) ([]byte, os.Error) {
}
func serve(w dns.ResponseWriter, req *dns.Msg) {
out, err := doFunkensturm(req)
if err != nil {
out, err := doFunkensturm(req)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %s\n", err.String())
return
}
if out != nil {
w.Write(out)
}
}
if out != nil {
w.Write(out)
}
}
func listenAndServe(add, net string) {
err := dns.ListenAndServe(add, net, nil)
if err != nil {
fmt.Printf("Failed to setup: " + net + " " + add + "\n")
}
err := dns.ListenAndServe(add, net, nil)
if err != nil {
fmt.Printf("Failed to setup: " + net + " " + add + "\n")
}
}
func main() {
sserver := flag.String("sserver", "127.0.0.1:8053", "set the listener address")
rserver := flag.String("rserver", "127.0.0.1:53", "remote server address(es), seperate with commas")
cpuprofile := flag.String("cpuprofile", "", "write cpu profile to file")
cpuprofile := flag.String("cpuprofile", "", "write cpu profile to file")
verbose = flag.Bool("verbose", false, "Print packet as it flows through")
flag.Usage = func() {
fmt.Fprintf(os.Stderr, "Usage: %s\n", os.Args[0])
flag.PrintDefaults()
}
flag.Parse()
if *cpuprofile != "" {
f, err := os.Create(*cpuprofile)
if err != nil {
log.Fatal(err)
}
pprof.StartCPUProfile(f)
defer pprof.StopCPUProfile()
}
if *cpuprofile != "" {
f, err := os.Create(*cpuprofile)
if err != nil {
log.Fatal(err)
}
pprof.StartCPUProfile(f)
defer pprof.StopCPUProfile()
}
clients := strings.Split(*rserver, ",", -1)
qr = make([]*Funk, len(clients))
for i, ra := range clients {
qr[i] = new(Funk)
qr[i].Client = dns.NewClient()
qr[i] = new(Funk)
qr[i].Client = dns.NewClient()
qr[i].Addr = ra
}
@ -197,13 +214,13 @@ func main() {
return
}
dns.HandleFunc(".", serve)
go listenAndServe(*sserver, "tcp")
go listenAndServe(*sserver, "udp")
dns.HandleFunc(".", serve)
go listenAndServe(*sserver, "tcp")
go listenAndServe(*sserver, "udp")
forever:
for {
select {
select {
case <-signal.Incoming:
fmt.Printf("Signal received, stopping\n")
break forever

View File

@ -117,7 +117,7 @@ func handleQuery(w dns.ResponseWriter, req *dns.Msg) {
m.Answer = append(m.Answer, r)
}
}
if *debug { println(m.String()) }
if *debug { println(m.Question[0].String()) }
send(w, m)
}

View File

@ -33,7 +33,10 @@ func NewZRRset() *ZRRset {
// type Zone map[string]map[int]*ZRRset
// Zone implements the concept of RFC 1035 master zone files.
// This will be converted to some kind of tree structure
// We store the zone contents in a map where the ownername is
// the key. In that map we have another map with integers
// (class * _CLASS + type) that has the RRset:
// map[<ownername>] -> map[<int>] -> ZRRset
type Zone struct {
Zone map[string]map[int]*ZRRset // the contents of the zone
Nxt *QnameString // sorted list of owernames in the zone