added files
This commit is contained in:
commit
f67087a176
|
@ -0,0 +1,6 @@
|
||||||
|
Add resolver type and use the for querying
|
||||||
|
IPv6 support
|
||||||
|
DNSSEC support
|
||||||
|
uint8 support
|
||||||
|
base64 support (only for unpacking atm)
|
||||||
|
Split of the type definition into dnstypes.go
|
|
@ -0,0 +1,18 @@
|
||||||
|
# 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.$(GOARCH)
|
||||||
|
|
||||||
|
TARG=dns
|
||||||
|
GOFILES=\
|
||||||
|
parse.go\
|
||||||
|
dns.go\
|
||||||
|
dnsmsg.go\
|
||||||
|
dnsconfig.go\
|
||||||
|
dnstypes.go\
|
||||||
|
|
||||||
|
include $(GOROOT)/src/Make.pkg
|
||||||
|
|
||||||
|
restest: restest.go
|
||||||
|
6g -I _obj restest.go && 6l -L _obj -o restest restest.6
|
|
@ -0,0 +1,4 @@
|
||||||
|
EDNS -- add EDNS0 support
|
||||||
|
DNSSEC - validation and the remaining records
|
||||||
|
DS, RRSIG, NSEC, etc.
|
||||||
|
Generic version of TryOneName
|
|
@ -0,0 +1,311 @@
|
||||||
|
// 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 client: see RFC 1035.
|
||||||
|
// Has to be linked into package net for Dial.
|
||||||
|
|
||||||
|
// TODO(rsc):
|
||||||
|
// Check periodically whether /etc/resolv.conf has changed.
|
||||||
|
// Could potentially handle many outstanding lookups faster.
|
||||||
|
// Could have a small cache.
|
||||||
|
// Random UDP source port (net.Dial should do that for us).
|
||||||
|
// Random request IDs.
|
||||||
|
|
||||||
|
package dns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"rand"
|
||||||
|
"time"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DnsError represents a DNS lookup error.
|
||||||
|
type DnsError struct {
|
||||||
|
Error string // description of the error
|
||||||
|
Name string // name looked for
|
||||||
|
Server string // server used
|
||||||
|
IsTimeout bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *DnsError) String() string {
|
||||||
|
s := "lookup " + e.Name
|
||||||
|
if e.Server != "" {
|
||||||
|
s += " on " + e.Server
|
||||||
|
}
|
||||||
|
s += ": " + e.Error
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *DnsError) Timeout() bool { return e.IsTimeout }
|
||||||
|
func (e *DnsError) Temporary() bool { return e.IsTimeout }
|
||||||
|
|
||||||
|
const noSuchHost = "no such host"
|
||||||
|
|
||||||
|
type Resolver struct {
|
||||||
|
Servers []string // servers to use
|
||||||
|
Search []string // suffixes to append to local name
|
||||||
|
Ndots int // number of dots in name to trigger absolute lookup
|
||||||
|
Timeout int // seconds before giving up on packet
|
||||||
|
Attempts int // lost packets before giving up on server
|
||||||
|
Rotate bool // round robin among servers
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send a request on the connection and hope for a reply.
|
||||||
|
// Up to res.Attempts attempts.
|
||||||
|
func Exchange(res *Resolver, c net.Conn, name string, qtype uint16, qclass uint16) (*Msg, os.Error) {
|
||||||
|
if len(name) >= 256 {
|
||||||
|
return nil, &DnsError{Error: "name too long", Name: name}
|
||||||
|
}
|
||||||
|
out := new(Msg)
|
||||||
|
out.id = uint16(rand.Int()) ^ uint16(time.Nanoseconds())
|
||||||
|
out.question = []Question{
|
||||||
|
Question{name, qtype, qclass},
|
||||||
|
}
|
||||||
|
out.recursion_desired = true
|
||||||
|
msg, ok := out.Pack()
|
||||||
|
if !ok {
|
||||||
|
return nil, &DnsError{Error: "internal error - cannot pack message", Name: name}
|
||||||
|
}
|
||||||
|
|
||||||
|
for attempt := 0; attempt < res.Attempts; attempt++ {
|
||||||
|
n, err := c.Write(msg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.SetReadTimeout(int64(res.Timeout) * 1e9) // nanoseconds
|
||||||
|
// EDNS
|
||||||
|
buf := make([]byte, 2000) // More than enough.
|
||||||
|
n, err = c.Read(buf)
|
||||||
|
if err != nil {
|
||||||
|
// if e, ok := err.(Error); ok && e.Timeout() {
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
buf = buf[0:n]
|
||||||
|
in := new(Msg)
|
||||||
|
if !in.Unpack(buf) || in.id != out.id {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return in, nil
|
||||||
|
}
|
||||||
|
var server string
|
||||||
|
if a := c.RemoteAddr(); a != nil {
|
||||||
|
server = a.String()
|
||||||
|
}
|
||||||
|
return nil, &DnsError{Error: "no answer from server", Name: name, Server: server, IsTimeout: true}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find answer for name in dns message.
|
||||||
|
// On return, if err == nil, addrs != nil.
|
||||||
|
func answer(name, server string, dns *Msg, qtype uint16) (addrs []RR, err os.Error) {
|
||||||
|
addrs = make([]RR, 0, len(dns.answer))
|
||||||
|
|
||||||
|
if dns.rcode == RcodeNameError && dns.recursion_available {
|
||||||
|
return nil, &DnsError{Error: noSuchHost, Name: name}
|
||||||
|
}
|
||||||
|
if dns.rcode != RcodeSuccess {
|
||||||
|
// None of the error codes make sense
|
||||||
|
// for the query we sent. If we didn't get
|
||||||
|
// a name error and we didn't get success,
|
||||||
|
// the server is behaving incorrectly.
|
||||||
|
return nil, &DnsError{Error: "server misbehaving", Name: name, Server: server}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look for the name.
|
||||||
|
// Presotto says it's okay to assume that servers listed in
|
||||||
|
// /etc/resolv.conf are recursive resolvers.
|
||||||
|
// We asked for recursion, so it should have included
|
||||||
|
// all the answers we need in this one packet.
|
||||||
|
Cname:
|
||||||
|
for cnameloop := 0; cnameloop < 10; cnameloop++ {
|
||||||
|
addrs = addrs[0:0]
|
||||||
|
for i := 0; i < len(dns.answer); i++ {
|
||||||
|
rr := dns.answer[i]
|
||||||
|
h := rr.Header()
|
||||||
|
if h.Class == ClassINET && h.Name == name {
|
||||||
|
switch h.Rrtype {
|
||||||
|
case qtype:
|
||||||
|
n := len(addrs)
|
||||||
|
addrs = addrs[0 : n+1]
|
||||||
|
addrs[n] = rr
|
||||||
|
case TypeCNAME:
|
||||||
|
// redirect to cname
|
||||||
|
name = rr.(*RR_CNAME).Cname
|
||||||
|
continue Cname
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(addrs) == 0 {
|
||||||
|
return nil, &DnsError{Error: noSuchHost, Name: name, Server: server}
|
||||||
|
}
|
||||||
|
return addrs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, &DnsError{Error: "too many redirects", Name: name, Server: server}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look up a single name
|
||||||
|
|
||||||
|
func (res *Resolver) Query(name string, qtype uint16, qclass uint16) (msg *Msg, err os.Error) {
|
||||||
|
if len(res.Servers) == 0 {
|
||||||
|
return nil, &DnsError{Error: "no DNS servers", Name: name}
|
||||||
|
}
|
||||||
|
for i := 0; i < len(res.Servers); i++ {
|
||||||
|
// Calling Dial here is scary -- we have to be sure
|
||||||
|
// not to dial a name that will require a DNS lookup,
|
||||||
|
// or Dial will call back here to translate it.
|
||||||
|
// The DNS config parser has already checked that
|
||||||
|
// all the res.Servers[i] are IP addresses, which
|
||||||
|
// Dial will use without a DNS lookup.
|
||||||
|
server := res.Servers[i] + ":53"
|
||||||
|
c, cerr := net.Dial("udp", "", server)
|
||||||
|
if cerr != nil {
|
||||||
|
err = cerr
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
msg, err = Exchange(res, c, name, qtype, qclass)
|
||||||
|
c.Close()
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do a lookup for a single name, which must be rooted
|
||||||
|
// (otherwise answer will not find the answers).
|
||||||
|
func (res *Resolver) TryOneName(name string, qtype uint16) (addrs []RR, err os.Error) {
|
||||||
|
if len(res.Servers) == 0 {
|
||||||
|
return nil, &DnsError{Error: "no DNS servers", Name: name}
|
||||||
|
}
|
||||||
|
for i := 0; i < len(res.Servers); i++ {
|
||||||
|
// Calling Dial here is scary -- we have to be sure
|
||||||
|
// not to dial a name that will require a DNS lookup,
|
||||||
|
// or Dial will call back here to translate it.
|
||||||
|
// The DNS config parser has already checked that
|
||||||
|
// all the res.Servers[i] are IP addresses, which
|
||||||
|
// Dial will use without a DNS lookup.
|
||||||
|
server := res.Servers[i] + ":53"
|
||||||
|
c, cerr := net.Dial("udp", "", server)
|
||||||
|
if cerr != nil {
|
||||||
|
err = cerr
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
msg, merr := Exchange(res, c, name, qtype, ClassINET)
|
||||||
|
c.Close()
|
||||||
|
if merr != nil {
|
||||||
|
err = merr
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
addrs, err = answer(name, server, msg, qtype)
|
||||||
|
if err == nil || err.(*DnsError).Error == noSuchHost {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var res *Resolver
|
||||||
|
var dnserr os.Error
|
||||||
|
|
||||||
|
func isDomainName(s string) bool {
|
||||||
|
// Requirements on DNS name:
|
||||||
|
// * must not be empty.
|
||||||
|
// * must be alphanumeric plus - and .
|
||||||
|
// * each of the dot-separated elements must begin
|
||||||
|
// and end with a letter or digit.
|
||||||
|
// RFC 1035 required the element to begin with a letter,
|
||||||
|
// but RFC 3696 says this has been relaxed to allow digits too.
|
||||||
|
// still, there must be a letter somewhere in the entire name.
|
||||||
|
if len(s) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if s[len(s)-1] != '.' { // simplify checking loop: make name end in dot
|
||||||
|
s += "."
|
||||||
|
}
|
||||||
|
|
||||||
|
last := byte('.')
|
||||||
|
ok := false // ok once we've seen a letter
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
c := s[i]
|
||||||
|
switch {
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
case 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z':
|
||||||
|
ok = true
|
||||||
|
case '0' <= c && c <= '9':
|
||||||
|
// fine
|
||||||
|
case c == '-':
|
||||||
|
// byte before dash cannot be dot
|
||||||
|
if last == '.' {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
case c == '.':
|
||||||
|
// byte before dot cannot be dot, dash
|
||||||
|
if last == '.' || last == '-' {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
last = c
|
||||||
|
}
|
||||||
|
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func lookup(name string, qtype uint16) (cname string, addrs []RR, err os.Error) {
|
||||||
|
if !isDomainName(name) {
|
||||||
|
return name, nil, &DnsError{Error: "invalid domain name", Name: name}
|
||||||
|
}
|
||||||
|
|
||||||
|
if dnserr != nil || res == nil {
|
||||||
|
err = dnserr
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// If name is rooted (trailing dot) or has enough dots,
|
||||||
|
// try it by itself first.
|
||||||
|
rooted := len(name) > 0 && name[len(name)-1] == '.'
|
||||||
|
if rooted || count(name, '.') >= res.Ndots {
|
||||||
|
rname := name
|
||||||
|
if !rooted {
|
||||||
|
rname += "."
|
||||||
|
}
|
||||||
|
// Can try as ordinary name.
|
||||||
|
addrs, err = res.TryOneName(rname, qtype)
|
||||||
|
if err == nil {
|
||||||
|
cname = rname
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if rooted {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, try suffixes.
|
||||||
|
for i := 0; i < len(res.Search); i++ {
|
||||||
|
rname := name + "." + res.Search[i]
|
||||||
|
if rname[len(rname)-1] != '.' {
|
||||||
|
rname += "."
|
||||||
|
}
|
||||||
|
addrs, err = res.TryOneName(rname, qtype)
|
||||||
|
if err == nil {
|
||||||
|
cname = rname
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Last ditch effort: try unsuffixed.
|
||||||
|
rname := name
|
||||||
|
if !rooted {
|
||||||
|
rname += "."
|
||||||
|
}
|
||||||
|
addrs, err = res.TryOneName(rname, qtype)
|
||||||
|
if err == nil {
|
||||||
|
cname = rname
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
|
@ -0,0 +1,105 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// Read system DNS config from /etc/resolv.conf
|
||||||
|
// Or any other file with the same format.
|
||||||
|
|
||||||
|
package dns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
// See resolv.conf(5) on a Linux machine.
|
||||||
|
// TODO(rsc): Supposed to call uname() and chop the beginning
|
||||||
|
// of the host name to get the default search domain.
|
||||||
|
// We assume it's in resolv.conf anyway.
|
||||||
|
func ReadConfig(f string) (*Resolver, os.Error) {
|
||||||
|
if f == "" {
|
||||||
|
f = "/etc/resolv.conf"
|
||||||
|
}
|
||||||
|
|
||||||
|
file, err := open(f)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err // DnsError
|
||||||
|
}
|
||||||
|
r := new(Resolver)
|
||||||
|
r.Servers = make([]string, 3)[0:0] // small, but the standard limit
|
||||||
|
r.Search = make([]string, 0)
|
||||||
|
r.Ndots = 1
|
||||||
|
r.Timeout = 5
|
||||||
|
r.Attempts = 2
|
||||||
|
r.Rotate = false
|
||||||
|
for line, ok := file.readLine(); ok; line, ok = file.readLine() {
|
||||||
|
f := getFields(line)
|
||||||
|
if len(f) < 1 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
switch f[0] {
|
||||||
|
case "nameserver": // add one name server
|
||||||
|
a := r.Servers
|
||||||
|
n := len(a)
|
||||||
|
if len(f) > 1 && n < cap(a) {
|
||||||
|
// One more check: make sure server name is
|
||||||
|
// just an IP address. Otherwise we need DNS
|
||||||
|
// to look it up.
|
||||||
|
name := f[1]
|
||||||
|
switch len(net.ParseIP(name)) {
|
||||||
|
case 16:
|
||||||
|
name = "[" + name + "]"
|
||||||
|
fallthrough
|
||||||
|
case 4:
|
||||||
|
a = a[0 : n+1]
|
||||||
|
a[n] = name
|
||||||
|
r.Servers = a
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case "domain": // set search path to just this domain
|
||||||
|
if len(f) > 1 {
|
||||||
|
r.Search = make([]string, 1)
|
||||||
|
r.Search[0] = f[1]
|
||||||
|
} else {
|
||||||
|
r.Search = make([]string, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
case "search": // set search path to given servers
|
||||||
|
r.Search = make([]string, len(f)-1)
|
||||||
|
for i := 0; i < len(r.Search); i++ {
|
||||||
|
r.Search[i] = f[i+1]
|
||||||
|
}
|
||||||
|
|
||||||
|
case "options": // magic options
|
||||||
|
for i := 1; i < len(f); i++ {
|
||||||
|
s := f[i]
|
||||||
|
switch {
|
||||||
|
case len(s) >= 6 && s[0:6] == "ndots:":
|
||||||
|
n, _, _ := dtoi(s, 6)
|
||||||
|
if n < 1 {
|
||||||
|
n = 1
|
||||||
|
}
|
||||||
|
r.Ndots = n
|
||||||
|
case len(s) >= 8 && s[0:8] == "timeout:":
|
||||||
|
n, _, _ := dtoi(s, 8)
|
||||||
|
if n < 1 {
|
||||||
|
n = 1
|
||||||
|
}
|
||||||
|
r.Timeout = n
|
||||||
|
case len(s) >= 8 && s[0:9] == "attempts:":
|
||||||
|
n, _, _ := dtoi(s, 9)
|
||||||
|
if n < 1 {
|
||||||
|
n = 1
|
||||||
|
}
|
||||||
|
r.Attempts = n
|
||||||
|
case s == "rotate":
|
||||||
|
r.Rotate = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file.close()
|
||||||
|
|
||||||
|
return r, nil
|
||||||
|
}
|
|
@ -0,0 +1,631 @@
|
||||||
|
// 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. See RFC 1035.
|
||||||
|
//
|
||||||
|
// 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(miekg):
|
||||||
|
|
||||||
|
package dns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"reflect"
|
||||||
|
"net"
|
||||||
|
"strconv"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/hex"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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 strings for each RR wire type.
|
||||||
|
var class_str = map[uint16]string{
|
||||||
|
ClassINET: "IN",
|
||||||
|
ClassCSNET: "CS",
|
||||||
|
ClassCHAOS: "CH",
|
||||||
|
ClassHESIOD: "HS",
|
||||||
|
ClassANY: "ANY",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map of strings for opcodes.
|
||||||
|
var opcode_str = map[int]string{
|
||||||
|
0: "QUERY",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map of strings for rcode
|
||||||
|
var rcode_str = map[int]string{
|
||||||
|
0: "NOERROR",
|
||||||
|
|
||||||
|
|
||||||
|
3: "NXDOMAIN",
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(rsc): Move into generic library?
|
||||||
|
// Pack a reflect.StructValue into msg. Struct members can only be uint16, uint32, string,
|
||||||
|
// and other (often anonymous) structs.
|
||||||
|
// IPV6 IPV4 still to do
|
||||||
|
func packStructValue(val *reflect.StructValue, msg []byte, off int) (off1 int, ok bool) {
|
||||||
|
for i := 0; i < val.NumField(); i++ {
|
||||||
|
f := val.Type().(*reflect.StructType).Field(i)
|
||||||
|
switch fv := val.Field(i).(type) {
|
||||||
|
default:
|
||||||
|
BadType:
|
||||||
|
fmt.Fprintf(os.Stderr, "net: dns: unknown packing type %v", f.Type)
|
||||||
|
return len(msg), false
|
||||||
|
case *reflect.StructValue:
|
||||||
|
off, ok = packStructValue(fv, msg, off)
|
||||||
|
case *reflect.UintValue:
|
||||||
|
i := fv.Get()
|
||||||
|
switch fv.Type().Kind() {
|
||||||
|
default:
|
||||||
|
goto BadType
|
||||||
|
case reflect.Uint8:
|
||||||
|
if off+1 > len(msg) {
|
||||||
|
return len(msg), false
|
||||||
|
}
|
||||||
|
msg[off] = byte(i)
|
||||||
|
off++
|
||||||
|
case reflect.Uint16:
|
||||||
|
if off+2 > len(msg) {
|
||||||
|
return len(msg), false
|
||||||
|
}
|
||||||
|
msg[off] = byte(i >> 8)
|
||||||
|
msg[off+1] = byte(i)
|
||||||
|
off += 2
|
||||||
|
case reflect.Uint32:
|
||||||
|
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+3] = byte(i)
|
||||||
|
off += 4
|
||||||
|
}
|
||||||
|
case *reflect.StringValue:
|
||||||
|
// There are multiple string encodings.
|
||||||
|
// The tag distinguishes ordinary strings from domain names.
|
||||||
|
s := fv.Get()
|
||||||
|
switch f.Tag {
|
||||||
|
default:
|
||||||
|
return len(msg), false
|
||||||
|
case "base64":
|
||||||
|
//TODO
|
||||||
|
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 structValue(any interface{}) *reflect.StructValue {
|
||||||
|
return reflect.NewValue(any).(*reflect.PtrValue).Elem().(*reflect.StructValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
func packStruct(any interface{}, msg []byte, off int) (off1 int, ok bool) {
|
||||||
|
off, ok = packStructValue(structValue(any), 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.NumField(); i++ {
|
||||||
|
f := val.Type().(*reflect.StructType).Field(i)
|
||||||
|
switch fv := val.Field(i).(type) {
|
||||||
|
default:
|
||||||
|
BadType:
|
||||||
|
fmt.Fprintf(os.Stderr, "net: dns: unknown packing type %v", f.Type)
|
||||||
|
return len(msg), false
|
||||||
|
case *reflect.SliceValue:
|
||||||
|
switch f.Tag {
|
||||||
|
default:
|
||||||
|
fmt.Fprintf(os.Stderr, "net: dns: unknown IP tag %v", f.Tag)
|
||||||
|
return len(msg), false
|
||||||
|
case "ipv4":
|
||||||
|
if off+net.IPv4len > len(msg) {
|
||||||
|
return len(msg), false
|
||||||
|
}
|
||||||
|
b := net.IPv4(msg[off], msg[off+1], msg[off+2], msg[off+3])
|
||||||
|
fv.Set(reflect.NewValue(b).(*reflect.SliceValue))
|
||||||
|
off += net.IPv4len
|
||||||
|
case "ipv6":
|
||||||
|
if off+net.IPv6len > len(msg) {
|
||||||
|
return len(msg), false
|
||||||
|
}
|
||||||
|
p := make(net.IP, net.IPv6len)
|
||||||
|
copy(p, msg[off:off+net.IPv6len])
|
||||||
|
b := net.IP(p)
|
||||||
|
fv.Set(reflect.NewValue(b).(*reflect.SliceValue))
|
||||||
|
off += net.IPv6len
|
||||||
|
}
|
||||||
|
case *reflect.StructValue:
|
||||||
|
off, ok = unpackStructValue(fv, msg, off)
|
||||||
|
case *reflect.UintValue:
|
||||||
|
switch fv.Type().Kind() {
|
||||||
|
default:
|
||||||
|
goto BadType
|
||||||
|
case reflect.Uint8:
|
||||||
|
if off+1 > len(msg) {
|
||||||
|
return len(msg), false
|
||||||
|
}
|
||||||
|
i := uint8(msg[off])
|
||||||
|
fv.Set(uint64(i))
|
||||||
|
off++
|
||||||
|
case reflect.Uint16:
|
||||||
|
if off+2 > len(msg) {
|
||||||
|
return len(msg), false
|
||||||
|
}
|
||||||
|
i := uint16(msg[off])<<8 | uint16(msg[off+1])
|
||||||
|
fv.Set(uint64(i))
|
||||||
|
off += 2
|
||||||
|
case reflect.Uint32:
|
||||||
|
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])
|
||||||
|
fv.Set(uint64(i))
|
||||||
|
off += 4
|
||||||
|
}
|
||||||
|
case *reflect.StringValue:
|
||||||
|
var s string
|
||||||
|
switch f.Tag {
|
||||||
|
default:
|
||||||
|
fmt.Fprintf(os.Stderr, "net: dns: unknown string tag %v", f.Tag)
|
||||||
|
return len(msg), false
|
||||||
|
case "hex":
|
||||||
|
// Rest of the RR is hex encoded
|
||||||
|
rdlength := int(val.FieldByName("Hdr").(*reflect.StructValue).FieldByName("Rdlength").(*reflect.UintValue).Get())
|
||||||
|
var consumed int
|
||||||
|
switch val.Type().Name() {
|
||||||
|
case "RR_DS":
|
||||||
|
consumed = 4 // KeyTag(2) + Algorithm(1) + DigestType(1)
|
||||||
|
default:
|
||||||
|
consumed = 0 // TODO
|
||||||
|
}
|
||||||
|
s = hex.EncodeToString(msg[off:off+rdlength-consumed])
|
||||||
|
off += rdlength-consumed
|
||||||
|
case "base64":
|
||||||
|
// Rest of the RR is base64 encoded value
|
||||||
|
rdlength := int(val.FieldByName("Hdr").(*reflect.StructValue).FieldByName("Rdlength").(*reflect.UintValue).Get())
|
||||||
|
// Need to know how much of rdlength is already consumed
|
||||||
|
var consumed int
|
||||||
|
// Can't I figure out via reflect how many bytes there are already consumed??
|
||||||
|
switch val.Type().Name() {
|
||||||
|
case "RR_DNSKEY":
|
||||||
|
consumed = 4 // Flags(2) + Protocol(1) + Algorithm(1)
|
||||||
|
case "RR_DS":
|
||||||
|
consumed = 4 // KeyTag(2) + Algorithm(1) + DigestType(1)
|
||||||
|
default:
|
||||||
|
consumed = 0 // TODO
|
||||||
|
}
|
||||||
|
b64 := make([]byte, base64.StdEncoding.EncodedLen(len(msg[off:off+rdlength-consumed])))
|
||||||
|
base64.StdEncoding.Encode(b64, msg[off:off+rdlength-consumed])
|
||||||
|
s = string(b64)
|
||||||
|
off += rdlength-consumed
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
fv.Set(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return off, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func unpackStruct(any interface{}, msg []byte, off int) (off1 int, ok bool) {
|
||||||
|
off, ok = unpackStructValue(structValue(any), 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.NumField(); i++ {
|
||||||
|
if i > 0 {
|
||||||
|
s += ", "
|
||||||
|
}
|
||||||
|
f := val.Type().(*reflect.StructType).Field(i)
|
||||||
|
if !f.Anonymous {
|
||||||
|
s += f.Name + "="
|
||||||
|
}
|
||||||
|
fval := val.Field(i)
|
||||||
|
if fv, ok := fval.(*reflect.StructValue); ok {
|
||||||
|
s += printStructValue(fv)
|
||||||
|
} else if fv, ok := fval.(*reflect.UintValue); ok && f.Tag == "ipv4" {
|
||||||
|
i := fv.Get()
|
||||||
|
s += net.IPv4(byte(i>>24), byte(i>>16), byte(i>>8), byte(i)).String()
|
||||||
|
} else {
|
||||||
|
s += fmt.Sprint(fval.Interface())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s += "}"
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func PrintStruct(any interface{}) string { return printStructValue(structValue(any)) }
|
||||||
|
|
||||||
|
// Resource record packer.
|
||||||
|
func packRR(rr 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 RR, off1 int, ok bool) {
|
||||||
|
// unpack just the header, to find the rr type and length
|
||||||
|
var h 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 MsgHdr struct {
|
||||||
|
id uint16
|
||||||
|
response bool
|
||||||
|
opcode int
|
||||||
|
authoritative bool
|
||||||
|
truncated bool
|
||||||
|
recursion_desired bool
|
||||||
|
recursion_available bool
|
||||||
|
rcode int
|
||||||
|
}
|
||||||
|
|
||||||
|
//;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 48404
|
||||||
|
//;; flags: qr aa rd ra;
|
||||||
|
func (h *MsgHdr) String() string {
|
||||||
|
s := ";; ->>HEADER<<- opcode: " + opcode_str[h.opcode]
|
||||||
|
s += ", status: " + rcode_str[h.rcode]
|
||||||
|
s += ", id: " + strconv.Itoa(int(h.id)) + "\n"
|
||||||
|
|
||||||
|
s += ";; flags: "
|
||||||
|
if h.authoritative {
|
||||||
|
s += "aa "
|
||||||
|
}
|
||||||
|
if h.truncated {
|
||||||
|
s += "tc "
|
||||||
|
}
|
||||||
|
if h.recursion_desired {
|
||||||
|
s += "rd "
|
||||||
|
}
|
||||||
|
if h.recursion_available {
|
||||||
|
s += "ra "
|
||||||
|
}
|
||||||
|
s += ";"
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
type Msg struct {
|
||||||
|
MsgHdr
|
||||||
|
question []Question
|
||||||
|
edns []Edns
|
||||||
|
answer []RR
|
||||||
|
ns []RR
|
||||||
|
extra []RR
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func (dns *Msg) Pack() (msg []byte, ok bool) {
|
||||||
|
var dh Header
|
||||||
|
|
||||||
|
// Convert convenient Msg into wire-like 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 = packRR(answer[i], msg, off)
|
||||||
|
}
|
||||||
|
for i := 0; i < len(ns); i++ {
|
||||||
|
off, ok = packRR(ns[i], msg, off)
|
||||||
|
}
|
||||||
|
for i := 0; i < len(extra); i++ {
|
||||||
|
off, ok = packRR(extra[i], msg, off)
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
return msg[0:off], true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dns *Msg) Unpack(msg []byte) bool {
|
||||||
|
// Header.
|
||||||
|
var dh 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([]Question, dh.Qdcount)
|
||||||
|
dns.answer = make([]RR, dh.Ancount)
|
||||||
|
dns.ns = make([]RR, dh.Nscount)
|
||||||
|
dns.extra = make([]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 *Msg) String() string {
|
||||||
|
s := dns.MsgHdr.String() + " "
|
||||||
|
s += "QUERY: " + strconv.Itoa(len(dns.question)) + ", "
|
||||||
|
s += "ANSWER: " + strconv.Itoa(len(dns.answer)) + ", "
|
||||||
|
s += "AUTHORITY: " + strconv.Itoa(len(dns.ns)) + ", "
|
||||||
|
s += "ADDITIONAL: " + strconv.Itoa(len(dns.extra)) + "\n"
|
||||||
|
if len(dns.question) > 0 {
|
||||||
|
s += "\n;; QUESTION SECTION:\n"
|
||||||
|
for i := 0; i < len(dns.question); i++ {
|
||||||
|
s += dns.question[i].String() + "\n"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(dns.answer) > 0 {
|
||||||
|
s += "\n;; ANSWER SECTION:\n"
|
||||||
|
for i := 0; i < len(dns.answer); i++ {
|
||||||
|
s += dns.answer[i].String() + "\n"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(dns.ns) > 0 {
|
||||||
|
s += "\n;; AUTHORITY SECTION:\n"
|
||||||
|
for i := 0; i < len(dns.ns); i++ {
|
||||||
|
s += dns.ns[i].String() + "\n"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(dns.extra) > 0 {
|
||||||
|
s += "\n;; ADDITIONAL SECTION:\n"
|
||||||
|
for i := 0; i < len(dns.extra); i++ {
|
||||||
|
s += dns.extra[i].String() + "\n"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
|
@ -0,0 +1,509 @@
|
||||||
|
// 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 Resource Records Types. See RFC 1035 and ...
|
||||||
|
//
|
||||||
|
|
||||||
|
package dns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Packet formats
|
||||||
|
|
||||||
|
// Wire constants.
|
||||||
|
const (
|
||||||
|
// valid RR_Header.Rrtype and Question.qtype
|
||||||
|
TypeA = 1
|
||||||
|
TypeNS = 2
|
||||||
|
TypeMD = 3
|
||||||
|
TypeMF = 4
|
||||||
|
TypeCNAME = 5
|
||||||
|
TypeSOA = 6
|
||||||
|
TypeMB = 7
|
||||||
|
TypeMG = 8
|
||||||
|
TypeMR = 9
|
||||||
|
TypeNULL = 10
|
||||||
|
TypeWKS = 11
|
||||||
|
TypePTR = 12
|
||||||
|
TypeHINFO = 13
|
||||||
|
TypeMINFO = 14
|
||||||
|
TypeMX = 15
|
||||||
|
TypeTXT = 16
|
||||||
|
TypeAAAA = 28
|
||||||
|
TypeSRV = 33
|
||||||
|
|
||||||
|
// EDNS
|
||||||
|
TypeOPT = 41
|
||||||
|
|
||||||
|
// DNSSEC
|
||||||
|
TypeDS = 43
|
||||||
|
TypeRRSIG = 46
|
||||||
|
TypeNSEC = 47
|
||||||
|
TypeDNSKEY = 48
|
||||||
|
TypeNSEC3 = 50
|
||||||
|
TypeNSEC3PARAM = 51
|
||||||
|
|
||||||
|
// valid Question.qtype only
|
||||||
|
TypeAXFR = 252
|
||||||
|
TypeMAILB = 253
|
||||||
|
TypeMAILA = 254
|
||||||
|
TypeALL = 255
|
||||||
|
|
||||||
|
// valid Question.qclass
|
||||||
|
ClassINET = 1
|
||||||
|
ClassCSNET = 2
|
||||||
|
ClassCHAOS = 3
|
||||||
|
ClassHESIOD = 4
|
||||||
|
ClassANY = 255
|
||||||
|
|
||||||
|
// Msg.rcode
|
||||||
|
RcodeSuccess = 0
|
||||||
|
RcodeFormatError = 1
|
||||||
|
RcodeServerFailure = 2
|
||||||
|
RcodeNameError = 3
|
||||||
|
RcodeNotImplemented = 4
|
||||||
|
RcodeRefused = 5
|
||||||
|
)
|
||||||
|
|
||||||
|
// The wire format for the DNS packet header.
|
||||||
|
type Header struct {
|
||||||
|
Id uint16
|
||||||
|
Bits uint16
|
||||||
|
Qdcount, Ancount, Nscount, Arcount uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
// 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
|
||||||
|
// _AD = 1 << ? // authenticated data
|
||||||
|
// _CD = 1 << ? // checking disabled
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// DNSSEC algorithms
|
||||||
|
AlgRSAMD5 = 1
|
||||||
|
AlgDH = 2
|
||||||
|
AlgDSA = 3
|
||||||
|
AlgECC = 4
|
||||||
|
AlgRSASHA1 = 5
|
||||||
|
AlgRSASHA256 = 8
|
||||||
|
AlgRSASHA512 = 10
|
||||||
|
AlgECCGOST = 12
|
||||||
|
)
|
||||||
|
|
||||||
|
// DNS queries.
|
||||||
|
type Question struct {
|
||||||
|
Name string "domain-name" // "domain-name" specifies encoding; see packers below
|
||||||
|
Qtype uint16
|
||||||
|
Qclass uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rcode needs some setting and getting work for _z and _version
|
||||||
|
type Edns struct {
|
||||||
|
Name string "domain-name"
|
||||||
|
Opt uint16 // was type
|
||||||
|
UDPSize uint16 // was class
|
||||||
|
Rcode uint32 // was TTL
|
||||||
|
Rdlength uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func (q *Question) String() string {
|
||||||
|
// prefix with ; (as in dig)
|
||||||
|
s := ";" + q.Name + "\t"
|
||||||
|
s = s + class_str[q.Qclass] + "\t"
|
||||||
|
s = s + rr_str[q.Qtype]
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// DNS responses (resource records).
|
||||||
|
// There are many types of messages,
|
||||||
|
// but they all share the same header.
|
||||||
|
type RR_Header struct {
|
||||||
|
Name string "domain-name"
|
||||||
|
Rrtype uint16
|
||||||
|
Class uint16
|
||||||
|
Ttl uint32
|
||||||
|
Rdlength uint16 // length of data after header
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *RR_Header) Header() *RR_Header {
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *RR_Header) String() string {
|
||||||
|
var s string
|
||||||
|
if len(h.Name) == 0 {
|
||||||
|
s = ".\t"
|
||||||
|
} else {
|
||||||
|
s = h.Name + "\t"
|
||||||
|
}
|
||||||
|
s = s + strconv.Itoa(int(h.Ttl)) + "\t" // why no strconv.Uint16??
|
||||||
|
s = s + class_str[h.Class] + "\t"
|
||||||
|
s = s + rr_str[h.Rrtype] + "\t"
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
type RR interface {
|
||||||
|
Header() *RR_Header
|
||||||
|
String() string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Specific DNS RR formats for each query type.
|
||||||
|
|
||||||
|
type RR_CNAME struct {
|
||||||
|
Hdr RR_Header
|
||||||
|
Cname string "domain-name"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rr *RR_CNAME) Header() *RR_Header {
|
||||||
|
return &rr.Hdr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rr *RR_CNAME) String() string {
|
||||||
|
return rr.Hdr.String() + rr.Cname
|
||||||
|
}
|
||||||
|
|
||||||
|
type RR_HINFO struct {
|
||||||
|
Hdr RR_Header
|
||||||
|
Cpu string
|
||||||
|
Os string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rr *RR_HINFO) Header() *RR_Header {
|
||||||
|
return &rr.Hdr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rr *RR_HINFO) String() string {
|
||||||
|
return rr.Hdr.String() + rr.Cpu + " " + rr.Os
|
||||||
|
}
|
||||||
|
|
||||||
|
type RR_MB struct {
|
||||||
|
Hdr RR_Header
|
||||||
|
Mb string "domain-name"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rr *RR_MB) Header() *RR_Header {
|
||||||
|
return &rr.Hdr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rr *RR_MB) String() string {
|
||||||
|
return rr.Hdr.String() + rr.Mb
|
||||||
|
}
|
||||||
|
|
||||||
|
type RR_MG struct {
|
||||||
|
Hdr RR_Header
|
||||||
|
Mg string "domain-name"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rr *RR_MG) Header() *RR_Header {
|
||||||
|
return &rr.Hdr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rr *RR_MG) String() string {
|
||||||
|
return rr.Hdr.String() + rr.Mg
|
||||||
|
}
|
||||||
|
|
||||||
|
type RR_MINFO struct {
|
||||||
|
Hdr RR_Header
|
||||||
|
Rmail string "domain-name"
|
||||||
|
Email string "domain-name"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rr *RR_MINFO) Header() *RR_Header {
|
||||||
|
return &rr.Hdr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rr *RR_MINFO) String() string {
|
||||||
|
return rr.Hdr.String() + rr.Rmail + " " + rr.Email
|
||||||
|
}
|
||||||
|
|
||||||
|
type RR_MR struct {
|
||||||
|
Hdr RR_Header
|
||||||
|
Mr string "domain-name"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rr *RR_MR) Header() *RR_Header {
|
||||||
|
return &rr.Hdr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rr *RR_MR) String() string {
|
||||||
|
return rr.Hdr.String() + rr.Mr
|
||||||
|
}
|
||||||
|
|
||||||
|
type RR_MX struct {
|
||||||
|
Hdr RR_Header
|
||||||
|
Pref uint16
|
||||||
|
Mx string "domain-name"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rr *RR_MX) Header() *RR_Header {
|
||||||
|
return &rr.Hdr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rr *RR_MX) String() string {
|
||||||
|
return rr.Hdr.String() + strconv.Itoa(int(rr.Pref)) + " " + rr.Mx
|
||||||
|
}
|
||||||
|
|
||||||
|
type RR_NS struct {
|
||||||
|
Hdr RR_Header
|
||||||
|
Ns string "domain-name"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rr *RR_NS) Header() *RR_Header {
|
||||||
|
return &rr.Hdr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rr *RR_NS) String() string {
|
||||||
|
return rr.Hdr.String() + rr.Ns
|
||||||
|
}
|
||||||
|
|
||||||
|
type RR_PTR struct {
|
||||||
|
Hdr RR_Header
|
||||||
|
Ptr string "domain-name"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rr *RR_PTR) Header() *RR_Header {
|
||||||
|
return &rr.Hdr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rr *RR_PTR) String() string {
|
||||||
|
return rr.Hdr.String() + rr.Ptr
|
||||||
|
}
|
||||||
|
|
||||||
|
type RR_SOA struct {
|
||||||
|
Hdr RR_Header
|
||||||
|
Ns string "domain-name"
|
||||||
|
Mbox string "domain-name"
|
||||||
|
Serial uint32
|
||||||
|
Refresh uint32
|
||||||
|
Retry uint32
|
||||||
|
Expire uint32
|
||||||
|
Minttl uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rr *RR_SOA) Header() *RR_Header {
|
||||||
|
return &rr.Hdr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rr *RR_SOA) String() string {
|
||||||
|
return rr.Hdr.String() + rr.Ns + " " + rr.Mbox +
|
||||||
|
" " + strconv.Itoa(int(rr.Serial)) +
|
||||||
|
" " + strconv.Itoa(int(rr.Refresh)) +
|
||||||
|
" " + strconv.Itoa(int(rr.Retry)) +
|
||||||
|
" " + strconv.Itoa(int(rr.Expire)) +
|
||||||
|
" " + strconv.Itoa(int(rr.Minttl))
|
||||||
|
}
|
||||||
|
|
||||||
|
type RR_TXT struct {
|
||||||
|
Hdr RR_Header
|
||||||
|
Txt string // not domain name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rr *RR_TXT) Header() *RR_Header {
|
||||||
|
return &rr.Hdr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rr *RR_TXT) String() string {
|
||||||
|
return rr.Hdr.String() + "\"" + rr.Txt + "\""
|
||||||
|
}
|
||||||
|
|
||||||
|
type RR_SRV struct {
|
||||||
|
Hdr RR_Header
|
||||||
|
Priority uint16
|
||||||
|
Weight uint16
|
||||||
|
Port uint16
|
||||||
|
Target string "domain-name"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rr *RR_SRV) Header() *RR_Header {
|
||||||
|
return &rr.Hdr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rr *RR_SRV) String() string {
|
||||||
|
return rr.Hdr.String() +
|
||||||
|
strconv.Itoa(int(rr.Priority)) + " " +
|
||||||
|
strconv.Itoa(int(rr.Weight)) + " " +
|
||||||
|
strconv.Itoa(int(rr.Port)) + " " + rr.Target
|
||||||
|
}
|
||||||
|
|
||||||
|
type RR_A struct {
|
||||||
|
Hdr RR_Header
|
||||||
|
A net.IP "ipv4"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rr *RR_A) Header() *RR_Header {
|
||||||
|
return &rr.Hdr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rr *RR_A) String() string {
|
||||||
|
return rr.Hdr.String() + rr.A.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
type RR_AAAA struct {
|
||||||
|
Hdr RR_Header
|
||||||
|
AAAA net.IP "ipv6"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rr *RR_AAAA) Header() *RR_Header {
|
||||||
|
return &rr.Hdr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rr *RR_AAAA) String() string {
|
||||||
|
return rr.Hdr.String() + rr.AAAA.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// DNSSEC types
|
||||||
|
type RR_RRSIG struct {
|
||||||
|
Hdr RR_Header
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rr *RR_RRSIG) Header() *RR_Header {
|
||||||
|
return &rr.Hdr
|
||||||
|
}
|
||||||
|
func (rr *RR_RRSIG) String() string {
|
||||||
|
return "BLAH"
|
||||||
|
}
|
||||||
|
|
||||||
|
type RR_NSEC struct {
|
||||||
|
Hdr RR_Header
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rr *RR_NSEC) Header() *RR_Header {
|
||||||
|
return &rr.Hdr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rr *RR_NSEC) String() string {
|
||||||
|
return "BLAH"
|
||||||
|
}
|
||||||
|
|
||||||
|
type RR_DS struct {
|
||||||
|
Hdr RR_Header
|
||||||
|
KeyTag uint16
|
||||||
|
Algorithm uint8
|
||||||
|
DigestType uint8
|
||||||
|
Digest string "hex"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rr *RR_DS) Header() *RR_Header {
|
||||||
|
return &rr.Hdr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rr *RR_DS) String() string {
|
||||||
|
return rr.Hdr.String() +
|
||||||
|
" " + strconv.Itoa(int(rr.KeyTag)) +
|
||||||
|
" " + alg_str[rr.Algorithm] +
|
||||||
|
" " + strconv.Itoa(int(rr.DigestType)) +
|
||||||
|
" " + rr.Digest
|
||||||
|
}
|
||||||
|
|
||||||
|
type RR_DNSKEY struct {
|
||||||
|
Hdr RR_Header
|
||||||
|
Flags uint16
|
||||||
|
Protocol uint8
|
||||||
|
Algorithm uint8
|
||||||
|
PubKey string "base64"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rr *RR_DNSKEY) Header() *RR_Header {
|
||||||
|
return &rr.Hdr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rr *RR_DNSKEY) String() string {
|
||||||
|
return rr.Hdr.String() +
|
||||||
|
" " + strconv.Itoa(int(rr.Flags)) +
|
||||||
|
" " + strconv.Itoa(int(rr.Protocol)) +
|
||||||
|
" " + alg_str[rr.Algorithm] +
|
||||||
|
" " + rr.PubKey // encoding/base64
|
||||||
|
}
|
||||||
|
|
||||||
|
type RR_NSEC3 struct {
|
||||||
|
Hdr RR_Header
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rr *RR_NSEC3) Header() *RR_Header {
|
||||||
|
return &rr.Hdr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rr *RR_NSEC3) String() string {
|
||||||
|
return "BLAH"
|
||||||
|
}
|
||||||
|
|
||||||
|
type RR_NSEC3PARAM struct {
|
||||||
|
Hdr RR_Header
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rr *RR_NSEC3PARAM) Header() *RR_Header {
|
||||||
|
return &rr.Hdr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rr *RR_NSEC3PARAM) String() string {
|
||||||
|
return "BLAH"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map of constructors for each RR wire type.
|
||||||
|
var rr_mk = map[int]func() RR{
|
||||||
|
TypeCNAME: func() RR { return new(RR_CNAME) },
|
||||||
|
TypeHINFO: func() RR { return new(RR_HINFO) },
|
||||||
|
TypeMB: func() RR { return new(RR_MB) },
|
||||||
|
TypeMG: func() RR { return new(RR_MG) },
|
||||||
|
TypeMINFO: func() RR { return new(RR_MINFO) },
|
||||||
|
TypeMR: func() RR { return new(RR_MR) },
|
||||||
|
TypeMX: func() RR { return new(RR_MX) },
|
||||||
|
TypeNS: func() RR { return new(RR_NS) },
|
||||||
|
TypePTR: func() RR { return new(RR_PTR) },
|
||||||
|
TypeSOA: func() RR { return new(RR_SOA) },
|
||||||
|
TypeTXT: func() RR { return new(RR_TXT) },
|
||||||
|
TypeSRV: func() RR { return new(RR_SRV) },
|
||||||
|
TypeA: func() RR { return new(RR_A) },
|
||||||
|
TypeAAAA: func() RR { return new(RR_AAAA) },
|
||||||
|
TypeDS: func() RR { return new(RR_DS) },
|
||||||
|
TypeRRSIG: func() RR { return new(RR_RRSIG) },
|
||||||
|
TypeNSEC: func() RR { return new(RR_NSEC) },
|
||||||
|
TypeDNSKEY: func() RR { return new(RR_DNSKEY) },
|
||||||
|
TypeNSEC3: func() RR { return new(RR_NSEC3) },
|
||||||
|
TypeNSEC3PARAM: func() RR { return new(RR_NSEC3PARAM) },
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map of strings for each RR wire type.
|
||||||
|
var rr_str = map[uint16]string{
|
||||||
|
TypeCNAME: "CNAME",
|
||||||
|
TypeHINFO: "HINFO",
|
||||||
|
TypeMB: "MB",
|
||||||
|
TypeMG: "MG",
|
||||||
|
TypeMINFO: "MINFO",
|
||||||
|
TypeMR: "MR",
|
||||||
|
TypeMX: "MX",
|
||||||
|
TypeNS: "NS",
|
||||||
|
TypePTR: "PTR",
|
||||||
|
TypeSOA: "SOA",
|
||||||
|
TypeTXT: "TXT",
|
||||||
|
TypeSRV: "SRV",
|
||||||
|
TypeA: "A",
|
||||||
|
TypeAAAA: "AAAA",
|
||||||
|
TypeDS: "DS",
|
||||||
|
TypeRRSIG: "RRSIG",
|
||||||
|
TypeNSEC: "NSEC",
|
||||||
|
TypeDNSKEY: "DNSKEY",
|
||||||
|
TypeNSEC3: "NSEC3",
|
||||||
|
TypeNSEC3PARAM: "NSEC3PARAM",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map for algorithm names.
|
||||||
|
var alg_str = map[uint8]string{
|
||||||
|
AlgRSAMD5: "RSAMD5",
|
||||||
|
AlgDH: "DH",
|
||||||
|
AlgDSA: "DSA",
|
||||||
|
AlgRSASHA1: "RSASHA1",
|
||||||
|
AlgRSASHA256: "RSASHA256",
|
||||||
|
AlgRSASHA512: "RSASHA512",
|
||||||
|
AlgECCGOST: "ECC-GOST",
|
||||||
|
}
|
|
@ -0,0 +1,216 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// Simple file i/o and string manipulation, to avoid
|
||||||
|
// depending on strconv and bufio and strings.
|
||||||
|
|
||||||
|
// Don't want or NEED this. Must GO
|
||||||
|
|
||||||
|
package dns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
type file struct {
|
||||||
|
file *os.File
|
||||||
|
data []byte
|
||||||
|
atEOF bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *file) close() { f.file.Close() }
|
||||||
|
|
||||||
|
func (f *file) getLineFromData() (s string, ok bool) {
|
||||||
|
data := f.data
|
||||||
|
i := 0
|
||||||
|
for i = 0; i < len(data); i++ {
|
||||||
|
if data[i] == '\n' {
|
||||||
|
s = string(data[0:i])
|
||||||
|
ok = true
|
||||||
|
// move data
|
||||||
|
i++
|
||||||
|
n := len(data) - i
|
||||||
|
copy(data[0:], data[i:])
|
||||||
|
f.data = data[0:n]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if f.atEOF && len(f.data) > 0 {
|
||||||
|
// EOF, return all we have
|
||||||
|
s = string(data)
|
||||||
|
f.data = f.data[0:0]
|
||||||
|
ok = true
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *file) readLine() (s string, ok bool) {
|
||||||
|
if s, ok = f.getLineFromData(); ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(f.data) < cap(f.data) {
|
||||||
|
ln := len(f.data)
|
||||||
|
n, err := io.ReadFull(f.file, f.data[ln:cap(f.data)])
|
||||||
|
if n >= 0 {
|
||||||
|
f.data = f.data[0 : ln+n]
|
||||||
|
}
|
||||||
|
if err == os.EOF {
|
||||||
|
f.atEOF = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s, ok = f.getLineFromData()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func open(name string) (*file, os.Error) {
|
||||||
|
fd, err := os.Open(name, os.O_RDONLY, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &file{fd, make([]byte, 1024)[0:0], false}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func byteIndex(s string, c byte) int {
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
if s[i] == c {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count occurrences in s of any bytes in t.
|
||||||
|
func countAnyByte(s string, t string) int {
|
||||||
|
n := 0
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
if byteIndex(t, s[i]) >= 0 {
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// Split s at any bytes in t.
|
||||||
|
func splitAtBytes(s string, t string) []string {
|
||||||
|
a := make([]string, 1+countAnyByte(s, t))
|
||||||
|
n := 0
|
||||||
|
last := 0
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
if byteIndex(t, s[i]) >= 0 {
|
||||||
|
if last < i {
|
||||||
|
a[n] = string(s[last:i])
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
last = i + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if last < len(s) {
|
||||||
|
a[n] = string(s[last:])
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
return a[0:n]
|
||||||
|
}
|
||||||
|
|
||||||
|
func getFields(s string) []string { return splitAtBytes(s, " \r\t\n") }
|
||||||
|
|
||||||
|
// Bigger than we need, not too big to worry about overflow
|
||||||
|
const big = 0xFFFFFF
|
||||||
|
|
||||||
|
// Decimal to integer starting at &s[i0].
|
||||||
|
// Returns number, new offset, success.
|
||||||
|
func dtoi(s string, i0 int) (n int, i int, ok bool) {
|
||||||
|
n = 0
|
||||||
|
for i = i0; i < len(s) && '0' <= s[i] && s[i] <= '9'; i++ {
|
||||||
|
n = n*10 + int(s[i]-'0')
|
||||||
|
if n >= big {
|
||||||
|
return 0, i, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if i == i0 {
|
||||||
|
return 0, i, false
|
||||||
|
}
|
||||||
|
return n, i, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hexadecimal to integer starting at &s[i0].
|
||||||
|
// Returns number, new offset, success.
|
||||||
|
func xtoi(s string, i0 int) (n int, i int, ok bool) {
|
||||||
|
n = 0
|
||||||
|
for i = i0; i < len(s); i++ {
|
||||||
|
if '0' <= s[i] && s[i] <= '9' {
|
||||||
|
n *= 16
|
||||||
|
n += int(s[i] - '0')
|
||||||
|
} else if 'a' <= s[i] && s[i] <= 'f' {
|
||||||
|
n *= 16
|
||||||
|
n += int(s[i]-'a') + 10
|
||||||
|
} else if 'A' <= s[i] && s[i] <= 'F' {
|
||||||
|
n *= 16
|
||||||
|
n += int(s[i]-'A') + 10
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if n >= big {
|
||||||
|
return 0, i, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if i == i0 {
|
||||||
|
return 0, i, false
|
||||||
|
}
|
||||||
|
return n, i, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Integer to decimal.
|
||||||
|
func itoa(i int) string {
|
||||||
|
var buf [30]byte
|
||||||
|
n := len(buf)
|
||||||
|
neg := false
|
||||||
|
if i < 0 {
|
||||||
|
i = -i
|
||||||
|
neg = true
|
||||||
|
}
|
||||||
|
ui := uint(i)
|
||||||
|
for ui > 0 || n == len(buf) {
|
||||||
|
n--
|
||||||
|
buf[n] = byte('0' + ui%10)
|
||||||
|
ui /= 10
|
||||||
|
}
|
||||||
|
if neg {
|
||||||
|
n--
|
||||||
|
buf[n] = '-'
|
||||||
|
}
|
||||||
|
return string(buf[n:])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Number of occurrences of b in s.
|
||||||
|
func count(s string, b byte) int {
|
||||||
|
n := 0
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
if s[i] == b {
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the prefix of s up to but not including the character c
|
||||||
|
func prefixBefore(s string, c byte) string {
|
||||||
|
for i, v := range s {
|
||||||
|
if v == int(c) {
|
||||||
|
return s[0:i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// Index of rightmost occurrence of b in s.
|
||||||
|
func last(s string, b byte) int {
|
||||||
|
i := len(s)
|
||||||
|
for i--; i >= 0; i-- {
|
||||||
|
if s[i] == b {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return i
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"dns"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
res := new(dns.Resolver)
|
||||||
|
res.Servers = []string{"192.168.1.2"}
|
||||||
|
res.Timeout = 2
|
||||||
|
res.Attempts = 1
|
||||||
|
|
||||||
|
a := new(dns.RR_A)
|
||||||
|
a.A = net.ParseIP("192.168.1.2").To4()
|
||||||
|
|
||||||
|
aaaa := new(dns.RR_AAAA)
|
||||||
|
aaaa.AAAA = net.ParseIP("2003::53").To16()
|
||||||
|
|
||||||
|
fmt.Printf("%v\n", a)
|
||||||
|
fmt.Printf("%v\n", aaaa)
|
||||||
|
|
||||||
|
// msg, _ := res.Query("miek.nl.", dns.TypeTXT, dns.ClassINET)
|
||||||
|
// fmt.Printf("%v\n", msg)
|
||||||
|
//
|
||||||
|
// msg, _ = res.Query("www.nlnetlabs.nl", dns.TypeAAAA, dns.ClassINET)
|
||||||
|
// fmt.Printf("%v\n", msg)
|
||||||
|
//
|
||||||
|
msg, _ := res.Query("nlnetlabs.nl", dns.TypeDNSKEY, dns.ClassINET)
|
||||||
|
fmt.Printf("%v\n", msg)
|
||||||
|
|
||||||
|
msg, _ = res.Query("jelte.nlnetlabs.nl", dns.TypeDS, dns.ClassINET)
|
||||||
|
fmt.Printf("%v\n", msg)
|
||||||
|
}
|
Loading…
Reference in New Issue