First pass for edns subnet support

This commit is contained in:
Miek Gieben 2012-05-31 21:40:07 +02:00
parent 29365cc9d8
commit 1bf0864632
3 changed files with 98 additions and 15 deletions

78
edns.go
View File

@ -2,18 +2,19 @@ package dns
import ( import (
"encoding/hex" "encoding/hex"
"errors"
"net" "net"
"strconv" "strconv"
) )
// EDNS0 Option codes. // EDNS0 Option codes.
const ( const (
_ = iota _ = iota
EDNS0LLQ // not used EDNS0LLQ // not used
EDNS0UL // not used EDNS0UL // not used
EDNS0NSID // NSID, RFC5001 EDNS0NSID // NSID, RFC5001
EDNS0SUBNET = 0x50fa // client-subnet draft EDNS0SUBNET = 0x50fa // client-subnet draft
_DO = 1 << 7 // dnssec ok _DO = 1 << 7 // dnssec ok
) )
/* /*
@ -58,6 +59,8 @@ func (rr *RR_OPT) String() string {
} }
s += " " + r s += " " + r
} }
case *EDNS0_SUBNET:
s += "\n; SUBNET: " + o.String()
} }
} }
return s return s
@ -130,8 +133,8 @@ type EDNS0 interface {
} }
type EDNS0_NSID struct { type EDNS0_NSID struct {
Code uint16 Code uint16 // Always EDNS0NSID
Nsid string // This string needs to be hex encoded Nsid string // This string needs to be hex encoded
} }
func (e *EDNS0_NSID) Option() uint16 { func (e *EDNS0_NSID) Option() uint16 {
@ -155,9 +158,70 @@ func (e *EDNS0_NSID) String() string {
} }
type EDNS0_SUBNET struct { type EDNS0_SUBNET struct {
Code uint16 Code uint16 // Always EDNS0SUBNET
Family uint16 Family uint16 // 1 for IP, 2 for IP6
SourceNetmask uint8 SourceNetmask uint8
SourceScope uint8 SourceScope uint8
Address []net.IP Address net.IP
}
func (e *EDNS0_SUBNET) Option() uint16 {
return e.Code
}
func (e *EDNS0_SUBNET) Pack() ([]byte, error) {
b := make([]byte, 4)
b[0], b[1] = packUint16(e.Family)
b[2] = e.SourceNetmask
b[3] = e.SourceScope
switch e.Family {
case 1:
// just copy? TODO (also in msg.go...)
ip := make([]byte, net.IPv4len)
for i := 0; i < net.IPv4len; i++ {
if i+1 > len(e.Address) {
break
}
ip[i] = e.Address[i]
}
b = append(b, ip...)
case 2:
ip := make([]byte, net.IPv6len)
for i := 0; i < net.IPv6len; i++ {
if i+1 > len(e.Address) {
break
}
ip[i] = e.Address[i]
}
b = append(b, ip...)
default:
return nil, errors.New("bad address family")
}
return b, nil
}
func (e *EDNS0_SUBNET) Unpack(b []byte) {
// TODO: length of b
e.Family, _ = unpackUint16(b, 0)
e.SourceNetmask = b[2]
e.SourceScope = b[3]
switch e.Family {
case 1:
if len(b) == 8 {
e.Address = net.IPv4(b[4], b[5], b[6], b[7])
}
case 2:
if len(b) == 20 {
e.Address = net.IP{b[4], b[4+1], b[4+2], b[4+3], b[4+4],
b[4+5], b[4+6], b[4+7], b[4+8], b[4+9], b[4+10],
b[4+11], b[4+12], b[4+13], b[4+14], b[4+15]}
}
}
return
}
func (e *EDNS0_SUBNET) String() string {
return e.Address.String() + "/" +
strconv.Itoa(int(e.SourceNetmask)) + "/" +
strconv.Itoa(int(e.SourceScope))
} }

View File

@ -4,6 +4,7 @@ import (
"dns" "dns"
"flag" "flag"
"fmt" "fmt"
"net"
"os" "os"
"strconv" "strconv"
"strings" "strings"
@ -47,7 +48,8 @@ func main() {
rd := flag.Bool("rd", true, "set RD flag in query") rd := flag.Bool("rd", true, "set RD flag in query")
fallback := flag.Bool("fallback", false, "fallback to 4096 bytes bufsize and after that TCP") fallback := flag.Bool("fallback", false, "fallback to 4096 bytes bufsize and after that TCP")
tcp := flag.Bool("tcp", false, "TCP mode") tcp := flag.Bool("tcp", false, "TCP mode")
nsid := flag.Bool("nsid", false, "ask for NSID") nsid := flag.Bool("nsid", false, "set edns nsid option")
client := flag.String("client", "", "set edns client-subnet option")
flag.Usage = func() { flag.Usage = func() {
fmt.Fprintf(os.Stderr, "Usage: %s [@server] [qtype] [qclass] [name ...]\n", os.Args[0]) fmt.Fprintf(os.Stderr, "Usage: %s [@server] [qtype] [qclass] [name ...]\n", os.Args[0])
flag.PrintDefaults() flag.PrintDefaults()
@ -162,7 +164,8 @@ Flags:
m.MsgHdr.CheckingDisabled = *cd m.MsgHdr.CheckingDisabled = *cd
m.MsgHdr.RecursionDesired = *rd m.MsgHdr.RecursionDesired = *rd
m.Question = make([]dns.Question, 1) m.Question = make([]dns.Question, 1)
if *dnssec || *nsid {
if *dnssec || *nsid || *client != "" {
o := new(dns.RR_OPT) o := new(dns.RR_OPT)
o.Hdr.Name = "." o.Hdr.Name = "."
o.Hdr.Rrtype = dns.TypeOPT o.Hdr.Rrtype = dns.TypeOPT
@ -171,12 +174,26 @@ Flags:
o.SetUDPSize(dns.DefaultMsgSize) o.SetUDPSize(dns.DefaultMsgSize)
} }
if *nsid { if *nsid {
// Ask for it
e := new(dns.EDNS0_NSID) e := new(dns.EDNS0_NSID)
e.Nsid = ""
e.Code = dns.EDNS0NSID e.Code = dns.EDNS0NSID
o.Option = append(o.Option, e) o.Option = append(o.Option, e)
} }
if *client != "" {
e := new(dns.EDNS0_SUBNET)
e.Code = dns.EDNS0SUBNET
e.SourceNetmask = 0
e.SourceScope = 0
e.Address = net.ParseIP(*client)
if e.Address == nil {
fmt.Fprintf(os.Stderr, "Failure to parse IP address: %s\n", *client)
return
}
e.Family = 1 // IP4
if len(e.Address) > net.IPv4len {
e.Family = 2 // IP6
}
o.Option = append(o.Option, e)
}
m.Extra = append(m.Extra, o) m.Extra = append(m.Extra, o)
} }
@ -243,8 +260,7 @@ forever:
} }
fmt.Printf("%v", r.Reply) fmt.Printf("%v", r.Reply)
fmt.Printf("\n;; query time: %.3d µs, server: %s(%s), size: %dB\n", r.Rtt/1e3, r.RemoteAddr, r.RemoteAddr.Network(), r.Reply.Len()) fmt.Printf("\n;; query time: %.3d µs, server: %s(%s), size: %d bytes\n", r.Rtt/1e3, r.RemoteAddr, r.RemoteAddr.Network(), r.Reply.Len())
// Server maybe
} }
i++ i++
if i == len(qname) { if i == len(qname) {

7
msg.go
View File

@ -404,7 +404,7 @@ func packStructValue(val reflect.Value, msg []byte, off int, compression map[str
element := val.Field(i).Index(j).Interface() element := val.Field(i).Index(j).Interface()
b, e := element.(EDNS0).Pack() b, e := element.(EDNS0).Pack()
if e != nil { if e != nil {
println("dns: failure packing OTP") println("dns: failure packing OPT")
return lenmsg, false return lenmsg, false
} }
// Option code // Option code
@ -707,7 +707,10 @@ func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, ok boo
edns = append(edns, e) edns = append(edns, e)
off = off1 + int(optlen) off = off1 + int(optlen)
case EDNS0SUBNET: case EDNS0SUBNET:
// .. e := new(EDNS0_SUBNET)
e.Unpack(msg[off1 : off1+int(optlen)])
edns = append(edns, e)
off = off1 + int(optlen)
} }
fv.Set(reflect.ValueOf(edns)) fv.Set(reflect.ValueOf(edns))
// goto ?? // goto ??