109 lines
1.9 KiB
Go
109 lines
1.9 KiB
Go
package exchanger
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"net"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/miekg/dns"
|
|
)
|
|
|
|
type systemExchanger struct {
|
|
rawAddr string
|
|
timeout time.Duration
|
|
lookupIP func(ctx context.Context, network, host string) ([]net.IP, error)
|
|
}
|
|
|
|
func newSystemExchanger(rawAddr string, opts Options) Exchanger {
|
|
if opts.timeout <= 0 {
|
|
opts.timeout = 5 * time.Second
|
|
}
|
|
ex := &systemExchanger{
|
|
rawAddr: rawAddr,
|
|
timeout: opts.timeout,
|
|
}
|
|
ex.lookupIP = net.DefaultResolver.LookupIP
|
|
return ex
|
|
}
|
|
|
|
func (ex *systemExchanger) Exchange(ctx context.Context, msg []byte) ([]byte, error) {
|
|
mq := new(dns.Msg)
|
|
if err := mq.Unpack(msg); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
mr := new(dns.Msg)
|
|
mr.SetReply(mq)
|
|
mr.RecursionAvailable = true
|
|
|
|
if len(mq.Question) == 0 {
|
|
return mr.Pack()
|
|
}
|
|
|
|
q := mq.Question[0]
|
|
network := "ip"
|
|
switch q.Qtype {
|
|
case dns.TypeA:
|
|
network = "ip4"
|
|
case dns.TypeAAAA:
|
|
network = "ip6"
|
|
default:
|
|
return mr.Pack()
|
|
}
|
|
|
|
if ex.timeout > 0 {
|
|
var cancel context.CancelFunc
|
|
ctx, cancel = context.WithTimeout(ctx, ex.timeout)
|
|
defer cancel()
|
|
}
|
|
|
|
host := strings.TrimSuffix(q.Name, ".")
|
|
ips, err := ex.lookupIP(ctx, network, host)
|
|
if err != nil {
|
|
var dnsErr *net.DNSError
|
|
if !errors.As(err, &dnsErr) || !dnsErr.IsNotFound {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
for _, ip := range ips {
|
|
switch q.Qtype {
|
|
case dns.TypeA:
|
|
if ip4 := ip.To4(); ip4 != nil {
|
|
mr.Answer = append(mr.Answer, &dns.A{
|
|
Hdr: dns.RR_Header{
|
|
Name: q.Name,
|
|
Rrtype: dns.TypeA,
|
|
Class: q.Qclass,
|
|
},
|
|
A: ip4,
|
|
})
|
|
}
|
|
case dns.TypeAAAA:
|
|
if ip.To4() == nil {
|
|
if ip16 := ip.To16(); ip16 != nil {
|
|
mr.Answer = append(mr.Answer, &dns.AAAA{
|
|
Hdr: dns.RR_Header{
|
|
Name: q.Name,
|
|
Rrtype: dns.TypeAAAA,
|
|
Class: q.Qclass,
|
|
},
|
|
AAAA: ip16,
|
|
})
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return mr.Pack()
|
|
}
|
|
|
|
func (ex *systemExchanger) String() string {
|
|
if ex.rawAddr == "" {
|
|
return "system"
|
|
}
|
|
return ex.rawAddr
|
|
}
|