+ dns: respond to PTR requests for internal IP addresses from DHCP
Close #1682
Squashed commit of the following:
commit 2fad3544bf8853b1f8f19ad8b7bc8a490c96e533
Author: Simon Zolin <s.zolin@adguard.com>
Date: Mon Jun 22 17:32:45 2020 +0300
minor
commit 7c17992424702d95e6de91f30e8ae2dfcd8de257
Author: Simon Zolin <s.zolin@adguard.com>
Date: Mon Jun 22 16:09:34 2020 +0300
build
commit 16a52e11a015a97d3cbf30362482a4abd052192b
Merge: 7b6a73c8 2c47053c
Author: Simon Zolin <s.zolin@adguard.com>
Date: Mon Jun 22 16:08:32 2020 +0300
Merge remote-tracking branch 'origin/master' into 1682-dhcp-resolve
commit 7b6a73c84b5cb9a073a9dfb7d7bdecd22e1e1318
Author: Simon Zolin <s.zolin@adguard.com>
Date: Mon Jun 22 16:01:34 2020 +0300
tests
commit c2654abb2e5e7b7e3a04e4ddb8e1064b37613929
Author: Simon Zolin <s.zolin@adguard.com>
Date: Mon Jun 1 15:15:13 2020 +0300
+ dnsforward: respond to PTR requests for internal IP addresses
{[IP] => "host"} <- DNSforward <-(leases)-- DHCP
This commit is contained in:
parent
2c47053cfe
commit
49a92605b8
|
@ -92,7 +92,7 @@ type Server struct {
|
||||||
conf ServerConfig
|
conf ServerConfig
|
||||||
|
|
||||||
// Called when the leases DB is modified
|
// Called when the leases DB is modified
|
||||||
onLeaseChanged onLeaseChangedT
|
onLeaseChanged []onLeaseChangedT
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print information about the available network interfaces
|
// Print information about the available network interfaces
|
||||||
|
@ -146,14 +146,16 @@ func (s *Server) Init(config ServerConfig) error {
|
||||||
|
|
||||||
// SetOnLeaseChanged - set callback
|
// SetOnLeaseChanged - set callback
|
||||||
func (s *Server) SetOnLeaseChanged(onLeaseChanged onLeaseChangedT) {
|
func (s *Server) SetOnLeaseChanged(onLeaseChanged onLeaseChangedT) {
|
||||||
s.onLeaseChanged = onLeaseChanged
|
s.onLeaseChanged = append(s.onLeaseChanged, onLeaseChanged)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) notify(flags int) {
|
func (s *Server) notify(flags int) {
|
||||||
if s.onLeaseChanged == nil {
|
if len(s.onLeaseChanged) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
s.onLeaseChanged(flags)
|
for _, f := range s.onLeaseChanged {
|
||||||
|
f(flags)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteDiskConfig - write configuration
|
// WriteDiskConfig - write configuration
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/AdguardTeam/AdGuardHome/dhcpd"
|
||||||
"github.com/AdguardTeam/AdGuardHome/dnsfilter"
|
"github.com/AdguardTeam/AdGuardHome/dnsfilter"
|
||||||
"github.com/AdguardTeam/AdGuardHome/querylog"
|
"github.com/AdguardTeam/AdGuardHome/querylog"
|
||||||
"github.com/AdguardTeam/AdGuardHome/stats"
|
"github.com/AdguardTeam/AdGuardHome/stats"
|
||||||
|
@ -45,10 +46,14 @@ var webRegistered bool
|
||||||
type Server struct {
|
type Server struct {
|
||||||
dnsProxy *proxy.Proxy // DNS proxy instance
|
dnsProxy *proxy.Proxy // DNS proxy instance
|
||||||
dnsFilter *dnsfilter.Dnsfilter // DNS filter instance
|
dnsFilter *dnsfilter.Dnsfilter // DNS filter instance
|
||||||
|
dhcpServer *dhcpd.Server // DHCP server instance (optional)
|
||||||
queryLog querylog.QueryLog // Query log instance
|
queryLog querylog.QueryLog // Query log instance
|
||||||
stats stats.Stats
|
stats stats.Stats
|
||||||
access *accessCtx
|
access *accessCtx
|
||||||
|
|
||||||
|
tablePTR map[string]string // "IP -> hostname" table for reverse lookup
|
||||||
|
tablePTRLock sync.Mutex
|
||||||
|
|
||||||
// DNS proxy instance for internal usage
|
// DNS proxy instance for internal usage
|
||||||
// We don't Start() it and so no listen port is required.
|
// We don't Start() it and so no listen port is required.
|
||||||
internalProxy *proxy.Proxy
|
internalProxy *proxy.Proxy
|
||||||
|
@ -59,13 +64,27 @@ type Server struct {
|
||||||
conf ServerConfig
|
conf ServerConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DNSCreateParams - parameters for NewServer()
|
||||||
|
type DNSCreateParams struct {
|
||||||
|
DNSFilter *dnsfilter.Dnsfilter
|
||||||
|
Stats stats.Stats
|
||||||
|
QueryLog querylog.QueryLog
|
||||||
|
DHCPServer *dhcpd.Server
|
||||||
|
}
|
||||||
|
|
||||||
// NewServer creates a new instance of the dnsforward.Server
|
// NewServer creates a new instance of the dnsforward.Server
|
||||||
// Note: this function must be called only once
|
// Note: this function must be called only once
|
||||||
func NewServer(dnsFilter *dnsfilter.Dnsfilter, stats stats.Stats, queryLog querylog.QueryLog) *Server {
|
func NewServer(p DNSCreateParams) *Server {
|
||||||
s := &Server{}
|
s := &Server{}
|
||||||
s.dnsFilter = dnsFilter
|
s.dnsFilter = p.DNSFilter
|
||||||
s.stats = stats
|
s.stats = p.Stats
|
||||||
s.queryLog = queryLog
|
s.queryLog = p.QueryLog
|
||||||
|
s.dhcpServer = p.DHCPServer
|
||||||
|
|
||||||
|
if s.dhcpServer != nil {
|
||||||
|
s.dhcpServer.SetOnLeaseChanged(s.onDHCPLeaseChanged)
|
||||||
|
s.onDHCPLeaseChanged(dhcpd.LeaseChangedAdded)
|
||||||
|
}
|
||||||
|
|
||||||
if runtime.GOARCH == "mips" || runtime.GOARCH == "mipsle" {
|
if runtime.GOARCH == "mips" || runtime.GOARCH == "mipsle" {
|
||||||
// Use plain DNS on MIPS, encryption is too slow
|
// Use plain DNS on MIPS, encryption is too slow
|
||||||
|
|
|
@ -15,6 +15,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/AdguardTeam/AdGuardHome/dhcpd"
|
||||||
"github.com/AdguardTeam/AdGuardHome/dnsfilter"
|
"github.com/AdguardTeam/AdGuardHome/dnsfilter"
|
||||||
"github.com/AdguardTeam/dnsproxy/proxy"
|
"github.com/AdguardTeam/dnsproxy/proxy"
|
||||||
"github.com/AdguardTeam/dnsproxy/upstream"
|
"github.com/AdguardTeam/dnsproxy/upstream"
|
||||||
|
@ -496,7 +497,7 @@ func TestBlockedCustomIP(t *testing.T) {
|
||||||
c := dnsfilter.Config{}
|
c := dnsfilter.Config{}
|
||||||
|
|
||||||
f := dnsfilter.New(&c, filters)
|
f := dnsfilter.New(&c, filters)
|
||||||
s := NewServer(f, nil, nil)
|
s := NewServer(DNSCreateParams{DNSFilter: f})
|
||||||
conf := ServerConfig{}
|
conf := ServerConfig{}
|
||||||
conf.UDPListenAddr = &net.UDPAddr{Port: 0}
|
conf.UDPListenAddr = &net.UDPAddr{Port: 0}
|
||||||
conf.TCPListenAddr = &net.TCPAddr{Port: 0}
|
conf.TCPListenAddr = &net.TCPAddr{Port: 0}
|
||||||
|
@ -648,7 +649,7 @@ func TestRewrite(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
f := dnsfilter.New(&c, nil)
|
f := dnsfilter.New(&c, nil)
|
||||||
s := NewServer(f, nil, nil)
|
s := NewServer(DNSCreateParams{DNSFilter: f})
|
||||||
conf := ServerConfig{}
|
conf := ServerConfig{}
|
||||||
conf.UDPListenAddr = &net.UDPAddr{Port: 0}
|
conf.UDPListenAddr = &net.UDPAddr{Port: 0}
|
||||||
conf.TCPListenAddr = &net.TCPAddr{Port: 0}
|
conf.TCPListenAddr = &net.TCPAddr{Port: 0}
|
||||||
|
@ -705,7 +706,7 @@ func createTestServer(t *testing.T) *Server {
|
||||||
c.CacheTime = 30
|
c.CacheTime = 30
|
||||||
|
|
||||||
f := dnsfilter.New(&c, filters)
|
f := dnsfilter.New(&c, filters)
|
||||||
s := NewServer(f, nil, nil)
|
s := NewServer(DNSCreateParams{DNSFilter: f})
|
||||||
s.conf.UDPListenAddr = &net.UDPAddr{Port: 0}
|
s.conf.UDPListenAddr = &net.UDPAddr{Port: 0}
|
||||||
s.conf.TCPListenAddr = &net.TCPAddr{Port: 0}
|
s.conf.TCPListenAddr = &net.TCPAddr{Port: 0}
|
||||||
s.conf.UpstreamDNS = []string{"8.8.8.8:53", "8.8.4.4:53"}
|
s.conf.UpstreamDNS = []string{"8.8.8.8:53", "8.8.4.4:53"}
|
||||||
|
@ -1012,3 +1013,39 @@ func TestMatchDNSName(t *testing.T) {
|
||||||
assert.True(t, !matchDNSName(dnsNames, ""))
|
assert.True(t, !matchDNSName(dnsNames, ""))
|
||||||
assert.True(t, !matchDNSName(dnsNames, "*.host2"))
|
assert.True(t, !matchDNSName(dnsNames, "*.host2"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPTRResponse(t *testing.T) {
|
||||||
|
dhcp := &dhcpd.Server{}
|
||||||
|
dhcp.IPpool = make(map[[4]byte]net.HardwareAddr)
|
||||||
|
|
||||||
|
c := dnsfilter.Config{}
|
||||||
|
f := dnsfilter.New(&c, nil)
|
||||||
|
s := NewServer(DNSCreateParams{DNSFilter: f, DHCPServer: dhcp})
|
||||||
|
s.conf.UDPListenAddr = &net.UDPAddr{Port: 0}
|
||||||
|
s.conf.TCPListenAddr = &net.TCPAddr{Port: 0}
|
||||||
|
s.conf.UpstreamDNS = []string{"127.0.0.1:53"}
|
||||||
|
s.conf.FilteringConfig.ProtectionEnabled = true
|
||||||
|
err := s.Prepare(nil)
|
||||||
|
assert.True(t, err == nil)
|
||||||
|
assert.Nil(t, s.Start())
|
||||||
|
|
||||||
|
l := dhcpd.Lease{}
|
||||||
|
l.IP = net.ParseIP("127.0.0.1").To4()
|
||||||
|
l.HWAddr, _ = net.ParseMAC("aa:aa:aa:aa:aa:aa")
|
||||||
|
l.Hostname = "localhost"
|
||||||
|
dhcp.AddStaticLease(l)
|
||||||
|
|
||||||
|
addr := s.dnsProxy.Addr(proxy.ProtoUDP)
|
||||||
|
req := createTestMessage("1.0.0.127.in-addr.arpa.")
|
||||||
|
req.Question[0].Qtype = dns.TypePTR
|
||||||
|
|
||||||
|
resp, err := dns.Exchange(req, addr.String())
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, 1, len(resp.Answer))
|
||||||
|
assert.Equal(t, dns.TypePTR, resp.Answer[0].Header().Rrtype)
|
||||||
|
assert.Equal(t, "1.0.0.127.in-addr.arpa.", resp.Answer[0].Header().Name)
|
||||||
|
ptr := resp.Answer[0].(*dns.PTR)
|
||||||
|
assert.Equal(t, "localhost.", ptr.Ptr)
|
||||||
|
|
||||||
|
s.Close()
|
||||||
|
}
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
package dnsforward
|
package dnsforward
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/AdguardTeam/AdGuardHome/dhcpd"
|
||||||
"github.com/AdguardTeam/AdGuardHome/dnsfilter"
|
"github.com/AdguardTeam/AdGuardHome/dnsfilter"
|
||||||
|
"github.com/AdguardTeam/AdGuardHome/util"
|
||||||
"github.com/AdguardTeam/dnsproxy/proxy"
|
"github.com/AdguardTeam/dnsproxy/proxy"
|
||||||
"github.com/AdguardTeam/golibs/log"
|
"github.com/AdguardTeam/golibs/log"
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
|
@ -39,6 +42,7 @@ func (s *Server) handleDNSRequest(_ *proxy.Proxy, d *proxy.DNSContext) error {
|
||||||
type modProcessFunc func(ctx *dnsContext) int
|
type modProcessFunc func(ctx *dnsContext) int
|
||||||
mods := []modProcessFunc{
|
mods := []modProcessFunc{
|
||||||
processInitial,
|
processInitial,
|
||||||
|
processInternalIPAddrs,
|
||||||
processFilteringBeforeRequest,
|
processFilteringBeforeRequest,
|
||||||
processUpstream,
|
processUpstream,
|
||||||
processDNSSECAfterResponse,
|
processDNSSECAfterResponse,
|
||||||
|
@ -88,11 +92,82 @@ func processInitial(ctx *dnsContext) int {
|
||||||
return resultDone
|
return resultDone
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Server) onDHCPLeaseChanged(flags int) {
|
||||||
|
switch flags {
|
||||||
|
case dhcpd.LeaseChangedAdded,
|
||||||
|
dhcpd.LeaseChangedAddedStatic,
|
||||||
|
dhcpd.LeaseChangedRemovedStatic:
|
||||||
|
//
|
||||||
|
default:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
m := make(map[string]string)
|
||||||
|
ll := s.dhcpServer.Leases(dhcpd.LeasesAll)
|
||||||
|
for _, l := range ll {
|
||||||
|
if len(l.Hostname) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
m[l.IP.String()] = l.Hostname
|
||||||
|
}
|
||||||
|
log.Debug("DNS: added %d PTR entries from DHCP", len(m))
|
||||||
|
s.tablePTRLock.Lock()
|
||||||
|
s.tablePTR = m
|
||||||
|
s.tablePTRLock.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Respond to PTR requests if the target IP address is leased by our DHCP server
|
||||||
|
func processInternalIPAddrs(ctx *dnsContext) int {
|
||||||
|
s := ctx.srv
|
||||||
|
req := ctx.proxyCtx.Req
|
||||||
|
if req.Question[0].Qtype != dns.TypePTR {
|
||||||
|
return resultDone
|
||||||
|
}
|
||||||
|
|
||||||
|
arpa := req.Question[0].Name
|
||||||
|
arpa = strings.TrimSuffix(arpa, ".")
|
||||||
|
arpa = strings.ToLower(arpa)
|
||||||
|
ip := util.DNSUnreverseAddr(arpa)
|
||||||
|
if ip == nil {
|
||||||
|
return resultDone
|
||||||
|
}
|
||||||
|
|
||||||
|
s.tablePTRLock.Lock()
|
||||||
|
if s.tablePTR == nil {
|
||||||
|
s.tablePTRLock.Unlock()
|
||||||
|
return resultDone
|
||||||
|
}
|
||||||
|
host, ok := s.tablePTR[ip.String()]
|
||||||
|
s.tablePTRLock.Unlock()
|
||||||
|
if !ok {
|
||||||
|
return resultDone
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debug("DNS: reverse-lookup: %s -> %s", arpa, host)
|
||||||
|
|
||||||
|
resp := s.makeResponse(req)
|
||||||
|
ptr := &dns.PTR{}
|
||||||
|
ptr.Hdr = dns.RR_Header{
|
||||||
|
Name: req.Question[0].Name,
|
||||||
|
Rrtype: dns.TypePTR,
|
||||||
|
Ttl: s.conf.BlockedResponseTTL,
|
||||||
|
Class: dns.ClassINET,
|
||||||
|
}
|
||||||
|
ptr.Ptr = host + "."
|
||||||
|
resp.Answer = append(resp.Answer, ptr)
|
||||||
|
ctx.proxyCtx.Res = resp
|
||||||
|
return resultDone
|
||||||
|
}
|
||||||
|
|
||||||
// Apply filtering logic
|
// Apply filtering logic
|
||||||
func processFilteringBeforeRequest(ctx *dnsContext) int {
|
func processFilteringBeforeRequest(ctx *dnsContext) int {
|
||||||
s := ctx.srv
|
s := ctx.srv
|
||||||
d := ctx.proxyCtx
|
d := ctx.proxyCtx
|
||||||
|
|
||||||
|
if d.Res != nil {
|
||||||
|
return resultDone // response is already set - nothing to do
|
||||||
|
}
|
||||||
|
|
||||||
s.RLock()
|
s.RLock()
|
||||||
// Synchronize access to s.dnsFilter so it won't be suddenly uninitialized while in use.
|
// Synchronize access to s.dnsFilter so it won't be suddenly uninitialized while in use.
|
||||||
// This could happen after proxy server has been stopped, but its workers are not yet exited.
|
// This could happen after proxy server has been stopped, but its workers are not yet exited.
|
||||||
|
|
|
@ -61,7 +61,13 @@ func initDNSServer() error {
|
||||||
filterConf.HTTPRegister = httpRegister
|
filterConf.HTTPRegister = httpRegister
|
||||||
Context.dnsFilter = dnsfilter.New(&filterConf, nil)
|
Context.dnsFilter = dnsfilter.New(&filterConf, nil)
|
||||||
|
|
||||||
Context.dnsServer = dnsforward.NewServer(Context.dnsFilter, Context.stats, Context.queryLog)
|
p := dnsforward.DNSCreateParams{
|
||||||
|
DNSFilter: Context.dnsFilter,
|
||||||
|
Stats: Context.stats,
|
||||||
|
QueryLog: Context.queryLog,
|
||||||
|
DHCPServer: Context.dhcpServer,
|
||||||
|
}
|
||||||
|
Context.dnsServer = dnsforward.NewServer(p)
|
||||||
dnsConfig := generateServerConfig()
|
dnsConfig := generateServerConfig()
|
||||||
err = Context.dnsServer.Prepare(&dnsConfig)
|
err = Context.dnsServer.Prepare(&dnsConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
|
|
||||||
func prepareTestDNSServer() error {
|
func prepareTestDNSServer() error {
|
||||||
config.DNS.Port = 1234
|
config.DNS.Port = 1234
|
||||||
Context.dnsServer = dnsforward.NewServer(nil, nil, nil)
|
Context.dnsServer = dnsforward.NewServer(dnsforward.DNSCreateParams{})
|
||||||
conf := &dnsforward.ServerConfig{}
|
conf := &dnsforward.ServerConfig{}
|
||||||
conf.UpstreamDNS = []string{"8.8.8.8"}
|
conf.UpstreamDNS = []string{"8.8.8.8"}
|
||||||
return Context.dnsServer.Prepare(conf)
|
return Context.dnsServer.Prepare(conf)
|
||||||
|
|
|
@ -296,70 +296,6 @@ func (a *AutoHosts) Process(host string, qtype uint16) []net.IP {
|
||||||
return ipsCopy
|
return ipsCopy
|
||||||
}
|
}
|
||||||
|
|
||||||
// convert character to hex number
|
|
||||||
func charToHex(n byte) int8 {
|
|
||||||
if n >= '0' && n <= '9' {
|
|
||||||
return int8(n) - '0'
|
|
||||||
} else if (n|0x20) >= 'a' && (n|0x20) <= 'f' {
|
|
||||||
return (int8(n) | 0x20) - 'a' + 10
|
|
||||||
}
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
// parse IPv6 reverse address
|
|
||||||
func ipParseArpa6(s string) net.IP {
|
|
||||||
if len(s) != 63 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
ip6 := make(net.IP, 16)
|
|
||||||
|
|
||||||
for i := 0; i != 64; i += 4 {
|
|
||||||
|
|
||||||
// parse "0.1."
|
|
||||||
n := charToHex(s[i])
|
|
||||||
n2 := charToHex(s[i+2])
|
|
||||||
if s[i+1] != '.' || (i != 60 && s[i+3] != '.') ||
|
|
||||||
n < 0 || n2 < 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
ip6[16-i/4-1] = byte(n2<<4) | byte(n&0x0f)
|
|
||||||
}
|
|
||||||
return ip6
|
|
||||||
}
|
|
||||||
|
|
||||||
// ipReverse - reverse IP address: 1.0.0.127 -> 127.0.0.1
|
|
||||||
func ipReverse(ip net.IP) net.IP {
|
|
||||||
n := len(ip)
|
|
||||||
r := make(net.IP, n)
|
|
||||||
for i := 0; i != n; i++ {
|
|
||||||
r[i] = ip[n-i-1]
|
|
||||||
}
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert reversed ARPA address to a normal IP address
|
|
||||||
func dnsUnreverseAddr(s string) net.IP {
|
|
||||||
const arpaV4 = ".in-addr.arpa"
|
|
||||||
const arpaV6 = ".ip6.arpa"
|
|
||||||
|
|
||||||
if strings.HasSuffix(s, arpaV4) {
|
|
||||||
ip := strings.TrimSuffix(s, arpaV4)
|
|
||||||
ip4 := net.ParseIP(ip).To4()
|
|
||||||
if ip4 == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return ipReverse(ip4)
|
|
||||||
|
|
||||||
} else if strings.HasSuffix(s, arpaV6) {
|
|
||||||
ip := strings.TrimSuffix(s, arpaV6)
|
|
||||||
return ipParseArpa6(ip)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil // unknown suffix
|
|
||||||
}
|
|
||||||
|
|
||||||
// ProcessReverse - process PTR request
|
// ProcessReverse - process PTR request
|
||||||
// Return "" if not found or an error occurred
|
// Return "" if not found or an error occurred
|
||||||
func (a *AutoHosts) ProcessReverse(addr string, qtype uint16) string {
|
func (a *AutoHosts) ProcessReverse(addr string, qtype uint16) string {
|
||||||
|
@ -367,7 +303,7 @@ func (a *AutoHosts) ProcessReverse(addr string, qtype uint16) string {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
ipReal := dnsUnreverseAddr(addr)
|
ipReal := DNSUnreverseAddr(addr)
|
||||||
if ipReal == nil {
|
if ipReal == nil {
|
||||||
return "" // invalid IP in question
|
return "" // invalid IP in question
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,11 +104,13 @@ func TestAutoHostsFSNotify(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIP(t *testing.T) {
|
func TestIP(t *testing.T) {
|
||||||
assert.True(t, dnsUnreverseAddr("1.0.0.127.in-addr.arpa").Equal(net.ParseIP("127.0.0.1").To4()))
|
assert.Equal(t, "127.0.0.1", DNSUnreverseAddr("1.0.0.127.in-addr.arpa").String())
|
||||||
assert.True(t, dnsUnreverseAddr("4.3.2.1.d.c.b.a.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa").Equal(net.ParseIP("::abcd:1234")))
|
assert.Equal(t, "::abcd:1234", DNSUnreverseAddr("4.3.2.1.d.c.b.a.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa").String())
|
||||||
|
assert.Equal(t, "::abcd:1234", DNSUnreverseAddr("4.3.2.1.d.c.B.A.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa").String())
|
||||||
|
|
||||||
assert.True(t, dnsUnreverseAddr("1.0.0.127.in-addr.arpa.") == nil)
|
assert.Nil(t, DNSUnreverseAddr("1.0.0.127.in-addr.arpa."))
|
||||||
assert.True(t, dnsUnreverseAddr(".0.0.127.in-addr.arpa") == nil)
|
assert.Nil(t, DNSUnreverseAddr(".0.0.127.in-addr.arpa"))
|
||||||
assert.True(t, dnsUnreverseAddr(".3.2.1.d.c.b.a.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa") == nil)
|
assert.Nil(t, DNSUnreverseAddr(".3.2.1.d.c.b.a.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa"))
|
||||||
assert.True(t, dnsUnreverseAddr("4.3.2.1.d.c.b.a.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0..ip6.arpa") == nil)
|
assert.Nil(t, DNSUnreverseAddr("4.3.2.1.d.c.b.a.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0..ip6.arpa"))
|
||||||
|
assert.Nil(t, DNSUnreverseAddr("4.3.2.1.d.c.b. .0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa"))
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// convert character to hex number
|
||||||
|
func charToHex(n byte) int8 {
|
||||||
|
if n >= '0' && n <= '9' {
|
||||||
|
return int8(n) - '0'
|
||||||
|
} else if (n|0x20) >= 'a' && (n|0x20) <= 'f' {
|
||||||
|
return (int8(n) | 0x20) - 'a' + 10
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse IPv6 reverse address
|
||||||
|
func ipParseArpa6(s string) net.IP {
|
||||||
|
if len(s) != 63 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
ip6 := make(net.IP, 16)
|
||||||
|
|
||||||
|
for i := 0; i != 64; i += 4 {
|
||||||
|
|
||||||
|
// parse "0.1."
|
||||||
|
n := charToHex(s[i])
|
||||||
|
n2 := charToHex(s[i+2])
|
||||||
|
if s[i+1] != '.' || (i != 60 && s[i+3] != '.') ||
|
||||||
|
n < 0 || n2 < 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ip6[16-i/4-1] = byte(n2<<4) | byte(n&0x0f)
|
||||||
|
}
|
||||||
|
return ip6
|
||||||
|
}
|
||||||
|
|
||||||
|
// ipReverse - reverse IP address: 1.0.0.127 -> 127.0.0.1
|
||||||
|
func ipReverse(ip net.IP) net.IP {
|
||||||
|
n := len(ip)
|
||||||
|
r := make(net.IP, n)
|
||||||
|
for i := 0; i != n; i++ {
|
||||||
|
r[i] = ip[n-i-1]
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// DNSUnreverseAddr - convert reversed ARPA address to a normal IP address
|
||||||
|
func DNSUnreverseAddr(s string) net.IP {
|
||||||
|
const arpaV4 = ".in-addr.arpa"
|
||||||
|
const arpaV6 = ".ip6.arpa"
|
||||||
|
|
||||||
|
if strings.HasSuffix(s, arpaV4) {
|
||||||
|
ip := strings.TrimSuffix(s, arpaV4)
|
||||||
|
ip4 := net.ParseIP(ip).To4()
|
||||||
|
if ip4 == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return ipReverse(ip4)
|
||||||
|
|
||||||
|
} else if strings.HasSuffix(s, arpaV6) {
|
||||||
|
ip := strings.TrimSuffix(s, arpaV6)
|
||||||
|
return ipParseArpa6(ip)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil // unknown suffix
|
||||||
|
}
|
Loading…
Reference in New Issue