diff --git a/README b/README index d808eb34..17308ef6 100644 --- a/README +++ b/README @@ -12,7 +12,7 @@ be build with: make examples (after the dns package has been installed) The major omission at the moment is parsing Resource Records from strings. (I.e. supporting the 1035 zone file format). -Everything else should be present and working. If not, drop me an mail. +Everything else should be present and working. If not, drop me an email. Have fun! Miek Gieben - 2010, 2011 - miek@miek.nl diff --git a/TODO b/TODO index 71329a64..4b3fe453 100644 --- a/TODO +++ b/TODO @@ -1,5 +1,6 @@ Todo: * NSEC and nsec3 closest encloser helper functions +* os.Error in Pack()/Unpack() * wildcards * Tsig testing * Private key file parsing use io.Reader (or the like) diff --git a/_examples/Makefile b/_examples/Makefile index 64e87cb6..c257827d 100644 --- a/_examples/Makefile +++ b/_examples/Makefile @@ -1,19 +1,15 @@ +EXAMPLES=mx \ +q \ +chaos \ +axfr \ +notify \ +reflect \ +rude \ +s \ +funkensturm \ + all: - gomake -C mx # query for MX records - gomake -C q # Dig-like tool - gomake -C chaos # show version.bind and hostname.bind - gomake -C axfr # perform an AXFR - gomake -C notify # send a notify msg - gomake -C reflect # a reflector nameserver - gomake -C rude # a rude nameserver (send formerr back) - goamke -C s # a complete nameserver + for i in $(EXAMPLES); do gomake -C $$i; done clean: - gomake -C mx clean - gomake -C q clean - gomake -C chaos clean - gomake -C axfr clean - gomake -C notify clean - gomake -C reflect clean - gomake -C rude clean - goamke -C s + for i in $(EXAMPLES); do gomake -C $$i clean; done diff --git a/_examples/funkensturm/Makefile b/_examples/funkensturm/Makefile new file mode 100644 index 00000000..9306d117 --- /dev/null +++ b/_examples/funkensturm/Makefile @@ -0,0 +1,8 @@ +# 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 +DEPS=../../ +include $(GOROOT)/src/Make.cmd diff --git a/_examples/funkensturm/doc.go b/_examples/funkensturm/doc.go new file mode 100644 index 00000000..7d97175b --- /dev/null +++ b/_examples/funkensturm/doc.go @@ -0,0 +1,19 @@ +/* +Funkensturm rewrites DNS packets in the broadest sense of the word. +The features 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. + +In its essence this is no different that a recursive nameserver, which also +receives and sends queries. The difference is the huge amount of tweaking +funkensturm offers. + +It includes a configuration language which makes setting up funkensturm +real easy. (It may be the case that this configuration language will be Go) + +Not sure if this is doable: +- support packet of death (TSIG signed) for stopping funkensturm +- support packet of config (TSIG signed) for configuring funkensturm on the fly + +*/ +package documentation diff --git a/_examples/funkensturm/funkensturm.go b/_examples/funkensturm/funkensturm.go new file mode 100644 index 00000000..177e50f3 --- /dev/null +++ b/_examples/funkensturm/funkensturm.go @@ -0,0 +1,118 @@ +/* + * Funkensturm + * Miek Gieben + */ + +package main + +import ( + "net" + _ "fmt" + "dns" + "strconv" + "dns/resolver" + "dns/responder" + "runtime" + "os/signal" +) + +type server responder.Server + +func reply(a net.Addr, in []byte, tcp bool) *dns.Msg { + inmsg := new(dns.Msg) + if !inmsg.Unpack(in) { + println("Unpacking failed") + return nil + } + + // it's valid mesg, return it + return inmsg + + if inmsg.MsgHdr.Response == true { + return nil // Don't answer responses + } + m := new(dns.Msg) + m.MsgHdr.Id = inmsg.MsgHdr.Id + m.MsgHdr.Authoritative = true + m.MsgHdr.Response = true + m.MsgHdr.Opcode = dns.OpcodeQuery + + m.MsgHdr.Rcode = dns.RcodeSuccess + m.Question = make([]dns.Question, 1) + m.Answer = make([]dns.RR, 1) + m.Extra = make([]dns.RR, 1) + + r := new(dns.RR_A) + r.Hdr = dns.RR_Header{Name: "whoami.miek.nl.", Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 0} + ip, _ := net.ResolveUDPAddr(a.String()) // No general variant for both upd and tcp + r.A = ip.IP.To4() // To4 very important + + t := new(dns.RR_TXT) + t.Hdr = dns.RR_Header{Name: "whoami.miek.nl.", Rrtype: dns.TypeTXT, Class: dns.ClassINET, Ttl: 0} + if tcp { + t.Txt = "Port: " + strconv.Itoa(ip.Port) + " (tcp)" + } else { + t.Txt = "Port: " + strconv.Itoa(ip.Port) + " (udp)" + } + + m.Question[0] = inmsg.Question[0] + m.Answer[0] = r + m.Extra[0] = t + + return m +} + +func (s *server) ResponderUDP(c *net.UDPConn, a net.Addr, i []byte) { + m := reply(a, i, false) + if m == nil { + return + } + // okay, send it using the resolver + qr <- resolver.Msg{m, nil, nil} + in := <-qr + + // Okay, not strip the additional section + if len(in.Dns.Extra) > 0 { + println("Stripping additional section") + in.Dns.Extra = []dns.RR{} + } + + // in may be nil + 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) { +} + +var qr chan resolver.Msg + +func main() { + runtime.GOMAXPROCS(5) + + r := new(resolver.Resolver) + r.Servers = []string{"127.0.0.1"} + r.Port = "53" + qr = r.NewQuerier() + + 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) + +forever: + for { + // Wait for a signal to stop + select { + case <-signal.Incoming: + println("Signal received, stopping") + break forever + } + } +} diff --git a/msg.go b/msg.go index c58032f1..c8885d6c 100644 --- a/msg.go +++ b/msg.go @@ -274,17 +274,27 @@ func packStructValue(val *reflect.StructValue, msg []byte, off int) (off1 int, o off += len(data) } case "A": - // It must be a slice of 4 - if fv.Len() > net.IPv4len || off+fv.Len() > len(msg) { + // It must be a slice of 4, even if it is 16, we encode + // only the first 4 + if off+net.IPv4len > len(msg) { + fmt.Fprintf(os.Stderr, "dns: overflow packing A") return len(msg), false } - msg[off] = byte(fv.Elem(0).(*reflect.UintValue).Get()) - msg[off+1] = byte(fv.Elem(1).(*reflect.UintValue).Get()) - msg[off+2] = byte(fv.Elem(2).(*reflect.UintValue).Get()) - msg[off+3] = byte(fv.Elem(3).(*reflect.UintValue).Get()) + if fv.Len() == net.IPv6len { + msg[off] = byte(fv.Elem(12).(*reflect.UintValue).Get()) + msg[off+1] = byte(fv.Elem(13).(*reflect.UintValue).Get()) + msg[off+2] = byte(fv.Elem(14).(*reflect.UintValue).Get()) + msg[off+3] = byte(fv.Elem(15).(*reflect.UintValue).Get()) + } else { + msg[off] = byte(fv.Elem(0).(*reflect.UintValue).Get()) + msg[off+1] = byte(fv.Elem(1).(*reflect.UintValue).Get()) + msg[off+2] = byte(fv.Elem(2).(*reflect.UintValue).Get()) + msg[off+3] = byte(fv.Elem(3).(*reflect.UintValue).Get()) + } off += net.IPv4len case "AAAA": if fv.Len() > net.IPv6len || off+fv.Len() > len(msg) { + fmt.Fprintf(os.Stderr, "dns: overflow packing AAAA") return len(msg), false } for j := 0; j < net.IPv6len; j++ { @@ -400,7 +410,7 @@ func unpackStructValue(val *reflect.StructValue, msg []byte, off int) (off1 int, switch fv := val.Field(i).(type) { default: BadType: - fmt.Fprintf(os.Stderr, "dns: unknown packing type %v", f.Type) + fmt.Fprintf(os.Stderr, "dns: unknown unpacking type %v", f.Type) return len(msg), false case *reflect.SliceValue: switch f.Tag { @@ -409,6 +419,7 @@ func unpackStructValue(val *reflect.StructValue, msg []byte, off int) (off1 int, return len(msg), false case "A": if off+net.IPv4len > len(msg) { + fmt.Fprintf(os.Stderr, "dns: overflow unpacking A") return len(msg), false } b := net.IPv4(msg[off], msg[off+1], msg[off+2], msg[off+3]) @@ -416,6 +427,7 @@ func unpackStructValue(val *reflect.StructValue, msg []byte, off int) (off1 int, off += net.IPv4len case "AAAA": if off+net.IPv6len > len(msg) { + fmt.Fprintf(os.Stderr, "dns: overflow unpacking AAAA") return len(msg), false } p := make(net.IP, net.IPv6len) @@ -425,6 +437,7 @@ func unpackStructValue(val *reflect.StructValue, msg []byte, off int) (off1 int, off += net.IPv6len case "OPT": // EDNS if off+2 > len(msg) { + fmt.Fprintf(os.Stderr, "dns: overflow unpacking OPT") // No room for anything else break } @@ -432,6 +445,7 @@ func unpackStructValue(val *reflect.StructValue, msg []byte, off int) (off1 int, opt[0].Code, off = unpackUint16(msg, off) optlen, off1 := unpackUint16(msg, off) if off1+int(optlen) > len(msg) { + fmt.Fprintf(os.Stderr, "dns: overflow unpacking OPT") return len(msg), false } opt[0].Data = hex.EncodeToString(msg[off1 : off1+int(optlen)]) @@ -448,6 +462,7 @@ func unpackStructValue(val *reflect.StructValue, msg []byte, off int) (off1 int, window := int(msg[off]) blocks := int(msg[off+1]) if off+blocks > len(msg) { + fmt.Fprintf(os.Stderr, "dns: overflow unpacking NSEC") return len(msg), false } off += 2 @@ -499,6 +514,7 @@ func unpackStructValue(val *reflect.StructValue, msg []byte, off int) (off1 int, goto BadType case reflect.Uint8: if off+1 > len(msg) { + fmt.Fprintf(os.Stderr, "dns: overflow unpacking uint8") return len(msg), false } i := uint8(msg[off]) @@ -507,12 +523,14 @@ func unpackStructValue(val *reflect.StructValue, msg []byte, off int) (off1 int, case reflect.Uint16: var i uint16 if off+2 > len(msg) { + fmt.Fprintf(os.Stderr, "dns: overflow unpacking uint16") return len(msg), false } i, off = unpackUint16(msg, off) fv.Set(uint64(i)) case reflect.Uint32: if off+4 > len(msg) { + fmt.Fprintf(os.Stderr, "dns: overflow unpacking uint32") return len(msg), false } i := uint32(msg[off])<<24 | uint32(msg[off+1])<<16 | uint32(msg[off+2])<<8 | uint32(msg[off+3]) @@ -522,6 +540,7 @@ func unpackStructValue(val *reflect.StructValue, msg []byte, off int) (off1 int, // This is *only* used in TSIG where the last 48 bits are occupied // So for now, assume a uint48 (6 bytes) if off+6 > len(msg) { + fmt.Fprintf(os.Stderr, "dns: overflow unpacking uint64") return len(msg), false } i := uint64(msg[off])<<40 | uint64(msg[off+1])<<32 | uint64(msg[off+2])<<24 | uint64(msg[off+3])<<16 | @@ -573,10 +592,12 @@ func unpackStructValue(val *reflect.StructValue, msg []byte, off int) (off1 int, case "domain-name": s, off, ok = unpackDomainName(msg, off) if !ok { + fmt.Fprintf(os.Stderr, "dns: failure unpacking domain-name") return len(msg), false } case "": if off >= len(msg) || off+1+int(msg[off]) > len(msg) { + fmt.Fprintf(os.Stderr, "dns: failure unpacking string") return len(msg), false } n := int(msg[off])