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:
parent
faaf0d6b8f
commit
7cf37d9d6f
|
@ -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
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
5
zone.go
5
zone.go
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue