diff options
| author | Rob Pike <r@golang.org> | 2009-06-09 09:53:44 -0700 |
|---|---|---|
| committer | Rob Pike <r@golang.org> | 2009-06-09 09:53:44 -0700 |
| commit | d90e7cbac65c5792ce312ee82fbe03a5dfc98c6f (patch) | |
| tree | 7032a11d0cac2ae4d3e90f7a189b575b5a50f848 /src/lib/net/dnsmsg.go | |
| parent | bf5c0c957c3c3ea9add6cfd51b90c463cb4814b5 (diff) | |
| download | go-d90e7cbac65c5792ce312ee82fbe03a5dfc98c6f.tar.xz | |
mv src/lib to src/pkg
tests: all.bash passes, gobuild still works, godoc still works.
R=rsc
OCL=30096
CL=30102
Diffstat (limited to 'src/lib/net/dnsmsg.go')
| -rw-r--r-- | src/lib/net/dnsmsg.go | 679 |
1 files changed, 0 insertions, 679 deletions
diff --git a/src/lib/net/dnsmsg.go b/src/lib/net/dnsmsg.go deleted file mode 100644 index d7a467fc6d..0000000000 --- a/src/lib/net/dnsmsg.go +++ /dev/null @@ -1,679 +0,0 @@ -// 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. - -// DNS packet assembly. -// -// This is intended to support name resolution during net.Dial. -// It doesn't have to be blazing fast. -// -// Rather than write the usual handful of routines to pack and -// unpack every message that can appear on the wire, we use -// reflection to write a generic pack/unpack for structs and then -// use it. Thus, if in the future we need to define new message -// structs, no new pack/unpack/printing code needs to be written. -// -// The first half of this file defines the DNS message formats. -// The second half implements the conversion to and from wire format. -// A few of the structure elements have string tags to aid the -// generic pack/unpack routines. -// -// TODO(rsc) There are enough names defined in this file that they're all -// prefixed with _DNS_. Perhaps put this in its own package later. - -package net - -import ( - "fmt"; - "os"; - "reflect"; -) - -// Packet formats - -// Wire constants. -const ( - // valid _DNS_RR_Header.rrtype and _DNS_Question.qtype - _DNS_TypeA = 1; - _DNS_TypeNS = 2; - _DNS_TypeMD = 3; - _DNS_TypeMF = 4; - _DNS_TypeCNAME = 5; - _DNS_TypeSOA = 6; - _DNS_TypeMB = 7; - _DNS_TypeMG = 8; - _DNS_TypeMR = 9; - _DNS_TypeNULL = 10; - _DNS_TypeWKS = 11; - _DNS_TypePTR = 12; - _DNS_TypeHINFO = 13; - _DNS_TypeMINFO = 14; - _DNS_TypeMX = 15; - _DNS_TypeTXT = 16; - - // valid _DNS_Question.qtype only - _DNS_TypeAXFR = 252; - _DNS_TypeMAILB = 253; - _DNS_TypeMAILA = 254; - _DNS_TypeALL = 255; - - // valid _DNS_Question.qclass - _DNS_ClassINET = 1; - _DNS_ClassCSNET = 2; - _DNS_ClassCHAOS = 3; - _DNS_ClassHESIOD = 4; - _DNS_ClassANY = 255; - - // _DNS_Msg.rcode - _DNS_RcodeSuccess = 0; - _DNS_RcodeFormatError = 1; - _DNS_RcodeServerFailure = 2; - _DNS_RcodeNameError = 3; - _DNS_RcodeNotImplemented = 4; - _DNS_RcodeRefused = 5; -) - -// The wire format for the DNS packet header. -type __DNS_Header struct { - id uint16; - bits uint16; - qdcount, ancount, nscount, arcount uint16; -} - -const ( - // __DNS_Header.bits - _QR = 1<<15; // query/response (response=1) - _AA = 1<<10; // authoritative - _TC = 1<<9; // truncated - _RD = 1<<8; // recursion desired - _RA = 1<<7; // recursion available -) - -// DNS queries. -type _DNS_Question struct { - name string "domain-name"; // "domain-name" specifies encoding; see packers below - qtype uint16; - qclass uint16; -} - -// DNS responses (resource records). -// There are many types of messages, -// but they all share the same header. -type _DNS_RR_Header struct { - name string "domain-name"; - rrtype uint16; - class uint16; - ttl uint32; - rdlength uint16; // length of data after header -} - -func (h *_DNS_RR_Header) Header() *_DNS_RR_Header { - return h -} - -type _DNS_RR interface { - Header() *_DNS_RR_Header -} - - -// Specific DNS RR formats for each query type. - -type _DNS_RR_CNAME struct { - _DNS_RR_Header; - cname string "domain-name"; -} - -type _DNS_RR_HINFO struct { - _DNS_RR_Header; - cpu string; - os string; -} - -type _DNS_RR_MB struct { - _DNS_RR_Header; - mb string "domain-name"; -} - -type _DNS_RR_MG struct { - _DNS_RR_Header; - mg string "domain-name"; -} - -type _DNS_RR_MINFO struct { - _DNS_RR_Header; - rmail string "domain-name"; - email string "domain-name"; -} - -type _DNS_RR_MR struct { - _DNS_RR_Header; - mr string "domain-name"; -} - -type _DNS_RR_MX struct { - _DNS_RR_Header; - pref uint16; - mx string "domain-name"; -} - -type _DNS_RR_NS struct { - _DNS_RR_Header; - ns string "domain-name"; -} - -type _DNS_RR_PTR struct { - _DNS_RR_Header; - ptr string "domain-name"; -} - -type _DNS_RR_SOA struct { - _DNS_RR_Header; - ns string "domain-name"; - mbox string "domain-name"; - serial uint32; - refresh uint32; - retry uint32; - expire uint32; - minttl uint32; -} - -type _DNS_RR_TXT struct { - _DNS_RR_Header; - txt string; // not domain name -} - -type _DNS_RR_A struct { - _DNS_RR_Header; - a uint32 "ipv4"; -} - - -// Packing and unpacking. -// -// All the packers and unpackers take a (msg []byte, off int) -// and return (off1 int, ok bool). If they return ok==false, they -// also return off1==len(msg), so that the next unpacker will -// also fail. This lets us avoid checks of ok until the end of a -// packing sequence. - -// Map of constructors for each RR wire type. -var rr_mk = map[int] func()_DNS_RR { - _DNS_TypeCNAME: func() _DNS_RR { return new(_DNS_RR_CNAME) }, - _DNS_TypeHINFO: func() _DNS_RR { return new(_DNS_RR_HINFO) }, - _DNS_TypeMB: func() _DNS_RR { return new(_DNS_RR_MB) }, - _DNS_TypeMG: func() _DNS_RR { return new(_DNS_RR_MG) }, - _DNS_TypeMINFO: func() _DNS_RR { return new(_DNS_RR_MINFO) }, - _DNS_TypeMR: func() _DNS_RR { return new(_DNS_RR_MR) }, - _DNS_TypeMX: func() _DNS_RR { return new(_DNS_RR_MX) }, - _DNS_TypeNS: func() _DNS_RR { return new(_DNS_RR_NS) }, - _DNS_TypePTR: func() _DNS_RR { return new(_DNS_RR_PTR) }, - _DNS_TypeSOA: func() _DNS_RR { return new(_DNS_RR_SOA) }, - _DNS_TypeTXT: func() _DNS_RR { return new(_DNS_RR_TXT) }, - _DNS_TypeA: func() _DNS_RR { return new(_DNS_RR_A) }, -} - -// Pack a domain name s into msg[off:]. -// Domain names are a sequence of counted strings -// split at the dots. They end with a zero-length string. -func packDomainName(s string, msg []byte, off int) (off1 int, ok bool) { - // Add trailing dot to canonicalize name. - if n := len(s); n == 0 || s[n-1] != '.' { - s += "."; - } - - // Each dot ends a segment of the name. - // We trade each dot byte for a length byte. - // There is also a trailing zero. - // Check that we have all the space we need. - tot := len(s) + 1; - if off+tot > len(msg) { - return len(msg), false - } - - // Emit sequence of counted strings, chopping at dots. - begin := 0; - for i := 0; i < len(s); i++ { - if s[i] == '.' { - if i - begin >= 1<<6 { // top two bits of length must be clear - return len(msg), false - } - msg[off] = byte(i - begin); - off++; - for j := begin; j < i; j++ { - msg[off] = s[j]; - off++; - } - begin = i+1; - } - } - msg[off] = 0; - off++; - return off, true -} - -// Unpack a domain name. -// In addition to the simple sequences of counted strings above, -// domain names are allowed to refer to strings elsewhere in the -// packet, to avoid repeating common suffixes when returning -// many entries in a single domain. The pointers are marked -// by a length byte with the top two bits set. Ignoring those -// two bits, that byte and the next give a 14 bit offset from msg[0] -// where we should pick up the trail. -// Note that if we jump elsewhere in the packet, -// we return off1 == the offset after the first pointer we found, -// which is where the next record will start. -// In theory, the pointers are only allowed to jump backward. -// We let them jump anywhere and stop jumping after a while. -func unpackDomainName(msg []byte, off int) (s string, off1 int, ok bool) { - s = ""; - ptr := 0; // number of pointers followed -Loop: - for { - if off >= len(msg) { - return "", len(msg), false - } - c := int(msg[off]); - off++; - switch c&0xC0 { - case 0x00: - if c == 0x00 { - // end of name - break Loop - } - // literal string - if off+c > len(msg) { - return "", len(msg), false - } - s += string(msg[off:off+c]) + "."; - off += c; - case 0xC0: - // pointer to somewhere else in msg. - // remember location after first ptr, - // since that's how many bytes we consumed. - // also, don't follow too many pointers -- - // maybe there's a loop. - if off >= len(msg) { - return "", len(msg), false - } - c1 := msg[off]; - off++; - if ptr == 0 { - off1 = off - } - if ptr++; ptr > 10 { - return "", len(msg), false - } - off = (c^0xC0)<<8 | int(c1); - default: - // 0x80 and 0x40 are reserved - return "", len(msg), false - } - } - if ptr == 0 { - off1 = off - } - return s, off1, true -} - -// Pack a reflect.StructValue into msg. Struct members can only be uint16, uint32, string, -// and other (often anonymous) structs. -func packStructValue(val reflect.StructValue, msg []byte, off int) (off1 int, ok bool) { - for i := 0; i < val.Len(); i++ { - fld := val.Field(i); - name, typ, tag, xxx := val.Type().(reflect.StructType).Field(i); - switch fld.Kind() { - default: - fmt.Fprintf(os.Stderr, "net: dns: unknown packing type %v", fld.Type()); - return len(msg), false; - case reflect.StructKind: - off, ok = packStructValue(fld.(reflect.StructValue), msg, off); - case reflect.Uint16Kind: - i := fld.(reflect.Uint16Value).Get(); - if off+2 > len(msg) { - return len(msg), false - } - msg[off] = byte(i>>8); - msg[off+1] = byte(i); - off += 2; - case reflect.Uint32Kind: - i := fld.(reflect.Uint32Value).Get(); - if off+4 > len(msg) { - return len(msg), false - } - msg[off] = byte(i>>24); - msg[off+1] = byte(i>>16); - msg[off+2] = byte(i>>8); - msg[off+4] = byte(i); - off += 4; - case reflect.StringKind: - // There are multiple string encodings. - // The tag distinguishes ordinary strings from domain names. - s := fld.(reflect.StringValue).Get(); - switch tag { - default: - fmt.Fprintf(os.Stderr, "net: dns: unknown string tag %v", tag); - return len(msg), false; - case "domain-name": - off, ok = packDomainName(s, msg, off); - if !ok { - return len(msg), false - } - case "": - // Counted string: 1 byte length. - if len(s) > 255 || off + 1 + len(s) > len(msg) { - return len(msg), false - } - msg[off] = byte(len(s)); - off++; - for i := 0; i < len(s); i++ { - msg[off+i] = s[i]; - } - off += len(s); - } - } - } - return off, true -} - -func packStruct(any interface{}, msg []byte, off int) (off1 int, ok bool) { - val := reflect.NewValue(any).(reflect.PtrValue).Sub().(reflect.StructValue); - off, ok = packStructValue(val, msg, off); - return off, ok -} - -// Unpack a reflect.StructValue from msg. -// Same restrictions as packStructValue. -func unpackStructValue(val reflect.StructValue, msg []byte, off int) (off1 int, ok bool) { - for i := 0; i < val.Len(); i++ { - name, typ, tag, xxx := val.Type().(reflect.StructType).Field(i); - fld := val.Field(i); - switch fld.Kind() { - default: - fmt.Fprintf(os.Stderr, "net: dns: unknown packing type %v", fld.Type()); - return len(msg), false; - case reflect.StructKind: - off, ok = unpackStructValue(fld.(reflect.StructValue), msg, off); - case reflect.Uint16Kind: - if off+2 > len(msg) { - return len(msg), false - } - i := uint16(msg[off])<<8 | uint16(msg[off+1]); - fld.(reflect.Uint16Value).Set(i); - off += 2; - case reflect.Uint32Kind: - if off+4 > len(msg) { - return len(msg), false - } - i := uint32(msg[off])<<24 | uint32(msg[off+1])<<16 | uint32(msg[off+2])<<8 | uint32(msg[off+3]); - fld.(reflect.Uint32Value).Set(i); - off += 4; - case reflect.StringKind: - var s string; - switch tag { - default: - fmt.Fprintf(os.Stderr, "net: dns: unknown string tag %v", tag); - return len(msg), false; - case "domain-name": - s, off, ok = unpackDomainName(msg, off); - if !ok { - return len(msg), false - } - case "": - if off >= len(msg) || off+1+int(msg[off]) > len(msg) { - return len(msg), false - } - n := int(msg[off]); - off++; - b := make([]byte, n); - for i := 0; i < n; i++ { - b[i] = msg[off+i]; - } - off += n; - s = string(b); - } - fld.(reflect.StringValue).Set(s); - } - } - return off, true -} - -func unpackStruct(any interface{}, msg []byte, off int) (off1 int, ok bool) { - val := reflect.NewValue(any).(reflect.PtrValue).Sub().(reflect.StructValue); - off, ok = unpackStructValue(val, msg, off); - return off, ok -} - -// Generic struct printer. -// Doesn't care about the string tag "domain-name", -// but does look for an "ipv4" tag on uint32 variables, -// printing them as IP addresses. -func printStructValue(val reflect.StructValue) string { - s := "{"; - for i := 0; i < val.Len(); i++ { - if i > 0 { - s += ", "; - } - name, typ, tag, xxx := val.Type().(reflect.StructType).Field(i); - fld := val.Field(i); - if name != "" && name != "?" { // BUG? Shouldn't the reflect library hide "?" ? - s += name + "="; - } - kind := fld.Kind(); - switch { - case kind == reflect.StructKind: - s += printStructValue(fld.(reflect.StructValue)); - case kind == reflect.Uint32Kind && tag == "ipv4": - i := fld.(reflect.Uint32Value).Get(); - s += fmt.Sprintf("%d.%d.%d.%d", (i>>24)&0xFF, (i>>16)&0xFF, (i>>8)&0xFF, i&0xFF); - default: - s += fmt.Sprint(fld.Interface()) - } - } - s += "}"; - return s; -} - -func printStruct(any interface{}) string { - val := reflect.NewValue(any).(reflect.PtrValue).Sub().(reflect.StructValue); - s := printStructValue(val); - return s -} - -// Resource record packer. -func packRR(rr _DNS_RR, msg []byte, off int) (off2 int, ok bool) { - var off1 int; - // pack twice, once to find end of header - // and again to find end of packet. - // a bit inefficient but this doesn't need to be fast. - // off1 is end of header - // off2 is end of rr - off1, ok = packStruct(rr.Header(), msg, off); - off2, ok = packStruct(rr, msg, off); - if !ok { - return len(msg), false - } - // pack a third time; redo header with correct data length - rr.Header().rdlength = uint16(off2 - off1); - packStruct(rr.Header(), msg, off); - return off2, true -} - -// Resource record unpacker. -func unpackRR(msg []byte, off int) (rr _DNS_RR, off1 int, ok bool) { - // unpack just the header, to find the rr type and length - var h _DNS_RR_Header; - off0 := off; - if off, ok = unpackStruct(&h, msg, off); !ok { - return nil, len(msg), false - } - end := off+int(h.rdlength); - - // make an rr of that type and re-unpack. - // again inefficient but doesn't need to be fast. - mk, known := rr_mk[int(h.rrtype)]; - if !known { - return &h, end, true - } - rr = mk(); - off, ok = unpackStruct(rr, msg, off0); - if off != end { - return &h, end, true - } - return rr, off, ok -} - -// Usable representation of a DNS packet. - -// A manually-unpacked version of (id, bits). -// This is in its own struct for easy printing. -type __DNS_Msg_Top struct { - id uint16; - response bool; - opcode int; - authoritative bool; - truncated bool; - recursion_desired bool; - recursion_available bool; - rcode int; -} - -type _DNS_Msg struct { - __DNS_Msg_Top; - question []_DNS_Question; - answer []_DNS_RR; - ns []_DNS_RR; - extra []_DNS_RR; -} - - -func (dns *_DNS_Msg) Pack() (msg []byte, ok bool) { - var dh __DNS_Header; - - // Convert convenient _DNS_Msg into wire-like __DNS_Header. - dh.id = dns.id; - dh.bits = uint16(dns.opcode)<<11 | uint16(dns.rcode); - if dns.recursion_available { - dh.bits |= _RA; - } - if dns.recursion_desired { - dh.bits |= _RD; - } - if dns.truncated { - dh.bits |= _TC; - } - if dns.authoritative { - dh.bits |= _AA; - } - if dns.response { - dh.bits |= _QR; - } - - // Prepare variable sized arrays. - question := dns.question; - answer := dns.answer; - ns := dns.ns; - extra := dns.extra; - - dh.qdcount = uint16(len(question)); - dh.ancount = uint16(len(answer)); - dh.nscount = uint16(len(ns)); - dh.arcount = uint16(len(extra)); - - // Could work harder to calculate message size, - // but this is far more than we need and not - // big enough to hurt the allocator. - msg = make([]byte, 2000); - - // Pack it in: header and then the pieces. - off := 0; - off, ok = packStruct(&dh, msg, off); - for i := 0; i < len(question); i++ { - off, ok = packStruct(&question[i], msg, off); - } - for i := 0; i < len(answer); i++ { - off, ok = packStruct(answer[i], msg, off); - } - for i := 0; i < len(ns); i++ { - off, ok = packStruct(ns[i], msg, off); - } - for i := 0; i < len(extra); i++ { - off, ok = packStruct(extra[i], msg, off); - } - if !ok { - return nil, false - } - return msg[0:off], true -} - -func (dns *_DNS_Msg) Unpack(msg []byte) bool { - // Header. - var dh __DNS_Header; - off := 0; - var ok bool; - if off, ok = unpackStruct(&dh, msg, off); !ok { - return false - } - dns.id = dh.id; - dns.response = (dh.bits & _QR) != 0; - dns.opcode = int(dh.bits >> 11) & 0xF; - dns.authoritative = (dh.bits & _AA) != 0; - dns.truncated = (dh.bits & _TC) != 0; - dns.recursion_desired = (dh.bits & _RD) != 0; - dns.recursion_available = (dh.bits & _RA) != 0; - dns.rcode = int(dh.bits & 0xF); - - // Arrays. - dns.question = make([]_DNS_Question, dh.qdcount); - dns.answer = make([]_DNS_RR, dh.ancount); - dns.ns = make([]_DNS_RR, dh.nscount); - dns.extra = make([]_DNS_RR, dh.arcount); - - for i := 0; i < len(dns.question); i++ { - off, ok = unpackStruct(&dns.question[i], msg, off); - } - for i := 0; i < len(dns.answer); i++ { - dns.answer[i], off, ok = unpackRR(msg, off); - } - for i := 0; i < len(dns.ns); i++ { - dns.ns[i], off, ok = unpackRR(msg, off); - } - for i := 0; i < len(dns.extra); i++ { - dns.extra[i], off, ok = unpackRR(msg, off); - } - if !ok { - return false - } -// if off != len(msg) { -// println("extra bytes in dns packet", off, "<", len(msg)); -// } - return true -} - -func (dns *_DNS_Msg) String() string { - s := "DNS: "+printStruct(&dns.__DNS_Msg_Top)+"\n"; - if len(dns.question) > 0 { - s += "-- Questions\n"; - for i := 0; i < len(dns.question); i++ { - s += printStruct(&dns.question[i])+"\n"; - } - } - if len(dns.answer) > 0 { - s += "-- Answers\n"; - for i := 0; i < len(dns.answer); i++ { - s += printStruct(dns.answer[i])+"\n"; - } - } - if len(dns.ns) > 0 { - s += "-- Name servers\n"; - for i := 0; i < len(dns.ns); i++ { - s += printStruct(dns.ns[i])+"\n"; - } - } - if len(dns.extra) > 0 { - s += "-- Extra\n"; - for i := 0; i < len(dns.extra); i++ { - s += printStruct(dns.extra[i])+"\n"; - } - } - return s; -} |
