diff options
| author | Shulhan <ms@kilabit.info> | 2019-05-17 23:29:26 +0700 |
|---|---|---|
| committer | Shulhan <ms@kilabit.info> | 2019-05-17 23:29:26 +0700 |
| commit | 7a51fa5f8b16423f417f03c449b736c31bad08eb (patch) | |
| tree | d7f2f54a92a279ee0566bafef67cf2b5c402e90a | |
| parent | 0f7bc7c001dbd05f50620dfe6ce850fdc6ee46d3 (diff) | |
| download | pakakeh.go-7a51fa5f8b16423f417f03c449b736c31bad08eb.tar.xz | |
dns: add function to lookup PTR record by IP address
The function LookupPTR accept any IP address (either IPv4 or IPv6) and
return a single domain name on success or an error on failed. If IP
address does not have PTR record it will return empty string without
error.
| -rw-r--r-- | lib/dns/funcs.go | 124 | ||||
| -rw-r--r-- | lib/dns/funcs_test.go | 66 | ||||
| -rw-r--r-- | lib/dns/idpool_test.go | 8 | ||||
| -rw-r--r-- | lib/dns/testdata/kilabit.info | 6 |
4 files changed, 200 insertions, 4 deletions
diff --git a/lib/dns/funcs.go b/lib/dns/funcs.go index 756c3472..8687c4b7 100644 --- a/lib/dns/funcs.go +++ b/lib/dns/funcs.go @@ -5,7 +5,9 @@ package dns import ( + "fmt" "net" + "strings" libnet "github.com/shuLhan/share/lib/net" ) @@ -45,3 +47,125 @@ func ParseNameServers(nameservers []string) ([]*net.UDPAddr, error) { return udpAddrs, nil } + +// +// LookupPTR accept an IP address (either IPv4 or IPv6) and return a single +// answer as domain name on sucess or an error on failed. +// If IP address does not contains PTR record it will return an empty string +// without error. +// +func LookupPTR(client Client, ip net.IP) (answer string, err error) { + if ip == nil { + return "", fmt.Errorf("empty IP address") + } + + revIP, isIPv4 := reverseIP(ip) + if len(revIP) == 0 { + return "", fmt.Errorf("invalid IP address %q", ip) + } + + if isIPv4 { + revIP = append(revIP, []byte(".in-addr.arpa")...) + } else { + revIP = append(revIP, []byte(".ip6.arpa")...) + } + + msg, err := client.Lookup(true, QueryTypePTR, QueryClassIN, revIP) + if err != nil { + return "", err + } + + rranswers := msg.FilterAnswers(QueryTypePTR) + if len(rranswers) == 0 { + return "", nil + } + + banswer, ok := rranswers[0].RData().([]byte) + if !ok { + return "", fmt.Errorf("invalid PTR record data") + } + + answer = string(banswer) + + return +} + +// +// reverseIP reverse the IP address by dot. +// +func reverseIP(ip net.IP) (revIP []byte, isIPv4 bool) { + strIP := ip.String() + + if strings.Count(strIP, ".") == 3 { + isIPv4 = true + revIP = reverseIPv4(strIP) + return + } + if strings.Count(strIP, ":") >= 2 { + revIP = reverseIPv6(strIP) + return + } + + return nil, false +} + +// +// reverseIPv4 reverse the IPv4 address. For example, given "127.0.0.1" it +// will return "1.0.0.127". +// +func reverseIPv4(ip string) (rev []byte) { + addrs := strings.Split(ip, ".") + for x := len(addrs) - 1; x >= 0; x-- { + if len(rev) > 0 { + rev = append(rev, '.') + } + rev = append(rev, addrs[x]...) + } + return +} + +// +// reverseIPv6 reverse the IPv6 address. For example, given "2001:db8::cb01" +// it will return "1.0.b.c.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.1.0.0.2". +// +func reverseIPv6(ip string) (rev []byte) { + addrs := strings.Split(ip, ":") + + var notempty int + for x := 0; x < len(addrs); x++ { + if len(addrs[x]) != 0 { + notempty++ + } + } + gap := 8 - notempty + + for x := len(addrs) - 1; x >= 0; x-- { + addr := addrs[x] + + // Fill the gap with "0.0.0.0". + if len(addr) == 0 { + for ; gap > 0; gap-- { + if len(rev) > 0 { + rev = append(rev, '.') + } + rev = append(rev, []byte("0.0.0.0")...) + } + continue + } + + // Reverse the sub address "2001" into "1.0.0.2". + for y := len(addr) - 1; y >= 0; y-- { + if len(rev) > 0 { + rev = append(rev, '.') + } + rev = append(rev, addr[y]) + } + + // Fill the sub address with zero. + for y := len(addr); y < 4; y++ { + rev = append(rev, []byte(".0")...) + } + } + + return +} diff --git a/lib/dns/funcs_test.go b/lib/dns/funcs_test.go index fb9e2946..56699c9f 100644 --- a/lib/dns/funcs_test.go +++ b/lib/dns/funcs_test.go @@ -5,6 +5,7 @@ package dns import ( + "net" "testing" "github.com/shuLhan/share/lib/test" @@ -29,3 +30,68 @@ func TestGetSystemNameServers(t *testing.T) { test.Assert(t, "NameServers", c.exp, got, true) } } + +func TestReverseIP(t *testing.T) { + cases := []struct { + ip string + exp []byte + expIsIPv4 bool + }{{ + ip: "", + }, { + ip: "192.0.2.1", + exp: []byte("1.2.0.192"), + expIsIPv4: true, + }, { + ip: "2001:db8::68", + exp: []byte("8.6.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2"), + }} + + for _, c := range cases { + ip := net.ParseIP(c.ip) + + gotIP, gotIsIPv4 := reverseIP(ip) + + test.Assert(t, "reverseIP", c.exp, gotIP, true) + test.Assert(t, "isIPv4", c.expIsIPv4, gotIsIPv4, true) + } +} + +func TestLookupPTR(t *testing.T) { + cl, err := NewUDPClient(testServerAddress) + if err != nil { + t.Fatal(err) + } + + cases := []struct { + ip net.IP + exp string + expErr string + }{{ + ip: nil, + expErr: "empty IP address", + }, { + ip: net.ParseIP("127.0.0.2"), + }, { + ip: net.ParseIP("127.0.0.10"), + exp: "kilabit.info", + }, { + ip: net.ParseIP("::1"), + exp: "kilabit.info", + }, { + ip: net.ParseIP("2001:db8::cb01"), + exp: "kilabit.info", + }} + + for _, c := range cases { + t.Logf("ip: %s", c.ip) + + got, err := LookupPTR(cl, c.ip) + if err != nil { + test.Assert(t, "error", c.expErr, err.Error(), true) + continue + } + + test.Assert(t, "LookupPTR", c.exp, got, true) + } +} diff --git a/lib/dns/idpool_test.go b/lib/dns/idpool_test.go index c5ce44fd..641f524b 100644 --- a/lib/dns/idpool_test.go +++ b/lib/dns/idpool_test.go @@ -11,8 +11,8 @@ import ( ) func TestIDPool(t *testing.T) { - test.Assert(t, "getNextID()=1", getNextID(), uint16(1), true) - test.Assert(t, "getNextID()=2", getNextID(), uint16(2), true) - test.Assert(t, "getNextID()=3", getNextID(), uint16(3), true) - test.Assert(t, "getNextID()=4", getNextID(), uint16(4), true) + test.Assert(t, "getNextID()=5", getNextID(), uint16(5), true) + test.Assert(t, "getNextID()=6", getNextID(), uint16(6), true) + test.Assert(t, "getNextID()=7", getNextID(), uint16(7), true) + test.Assert(t, "getNextID()=8", getNextID(), uint16(8), true) } diff --git a/lib/dns/testdata/kilabit.info b/lib/dns/testdata/kilabit.info index f6dbde12..d0165bb6 100644 --- a/lib/dns/testdata/kilabit.info +++ b/lib/dns/testdata/kilabit.info @@ -7,3 +7,9 @@ @ IN A 127.0.0.1 TXT "This is a test server" + +10.0.0.127.in-addr.arpa. PTR kilabit.info. + +1.0.0.0.0.0.0.0.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. PTR kilabit.info. + +1.0.b.c.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa. PTR kilabit.info. |
