aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShulhan <ms@kilabit.info>2019-05-17 23:29:26 +0700
committerShulhan <ms@kilabit.info>2019-05-17 23:29:26 +0700
commit7a51fa5f8b16423f417f03c449b736c31bad08eb (patch)
treed7f2f54a92a279ee0566bafef67cf2b5c402e90a
parent0f7bc7c001dbd05f50620dfe6ce850fdc6ee46d3 (diff)
downloadpakakeh.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.go124
-rw-r--r--lib/dns/funcs_test.go66
-rw-r--r--lib/dns/idpool_test.go8
-rw-r--r--lib/dns/testdata/kilabit.info6
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.