# Funkensturm: transparent proxy example A transparent proxy is a proxy that does nothing, but it serves as a nice introduction into Funkensturm. See [this post](/2011/january/23/funkensturm-a-versatile-dns-proxy/index.html) for an architectural overview of Funkensturm. Currently a configuration is compiled into the Funkensturm binary. As such it must look like a normal Go program. # Matching function The matching function checks a packet for features and can optionally modify it. This is the prototype of a matching function: func match(m *dns.Msg, d int) (*dns.Msg, bool) It receives a `*dns.Msg` which is a packet in Go DNS and a direction `d` which can be `IN` (first incoming packet) or `OUT` when the packet is on its way back to the client. It returns a DNS packet and a boolean value signaling a match. This being a *transparant* proxy, we don't do anything with the packet, but I've left the structure of the function intact: func match(m *dns.Msg, d int) (*dns.Msg, bool) { // Matching criteria switch d { case IN: // nothing case OUT: // nothing } // Packet Mangling functions switch d { case IN: // nothing case OUT: // nothing } return m, true } # Action function The only thing the action function needs to do is forwarding the packet to the server. The prototype of a matching function is: func send(m *dns.Msg, ok bool) *dns.Msg It receives a DNS packet and a boolean. The boolean comes from the match function. An action function needs to return a packet. In this case the returned packet is the reply from the server. func send(m *dns.Msg, ok bool) *dns.Msg { switch ok { case true, false: qr[0] <- resolver.Msg{m, nil, nil} in := <-qr[0] return in.Dns } return nil } The variable `qr` is global and is setup by Funkensturm: it is the communication channel for the default resolver. There can be multiple of these (Funkensturm can be used as a duplicator), but here we use the first: `qr[0]`. # Configure Funkensturm Once these functions are defined we can create a Funkensturm configuration. By default it will execute a function named `funkensturm()` which should return a pointer to a `Funkensturm` type: type Funkensturm struct { Setup func() bool Matches []Match Actions []Action } So let's setup the function that returns a pointer to the structure: func funkensturm() *Funkensturm { f := new(Funkensturm) // Nothing to set up. f.Setup = func() bool { return true } // 1 match function, use AND as op (doesn't matter in this case) f.Matches = make([]Match, 1) // make room for 1 function f.Matches[0].Op = AND // set the chaining boolean f.Matches[0].Func = match // set the match function // 1 action f.Actions = make([]Action, 1) // make room for 1 function f.Actions[0].Func = send // set the action function return f // return the pointer } [Full code listing](https://github.com/miekg/godns/raw/master/_examples/funkensturm/config.go). # Start me up The default setup compiles the above config. Funkensturm allows for some configuration on top of that: % ./funkensturm -h flag provided but not defined: -h Usage: ./funkensturm -sserver="127.0.0.1:8053": Set the listener address -verbose=false: Print packet as the flow through -rserver="127.0.0.1:53": Remote server address(es) The defaults look good, so we use those: % ./funkensturm We can now use `dig` to ask questions on port 8053, which get forwarded to my recursive server on port 53 and are then returned to `dig`: % dig +nocmd +noquestion +stats +noidentify -p 8053 @127.0.0.1 www.example.com ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 57601 ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 2, ADDITIONAL: 2 ;; ANSWER SECTION: www.example.com. 169603 IN A 192.0.32.10 ;; AUTHORITY SECTION: example.com. 169603 IN NS a.iana-servers.net. example.com. 169603 IN NS b.iana-servers.net. ;; ADDITIONAL SECTION: a.iana-servers.net. 25602 IN A 192.0.34.43 b.iana-servers.net. 25603 IN A 193.0.0.236 ;; Query time: 1 msec ;; SERVER: 127.0.0.1#8053(127.0.0.1) ;; WHEN: Sun Jan 23 18:52:22 2011 ;; MSG SIZE rcvd: 218