# 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


