summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--LICENSE0
-rw-r--r--cmd/root.go46
-rw-r--r--cmd/upstream.go32
-rw-r--r--go.mod15
-rw-r--r--go.sum20
-rw-r--r--main.go11
-rw-r--r--prober/rdns_prober.go133
-rw-r--r--utils/dns_utils.go55
-rw-r--r--utils/httpdns_utils.go112
-rw-r--r--utils/output_utils.go15
10 files changed, 439 insertions, 0 deletions
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/LICENSE
diff --git a/cmd/root.go b/cmd/root.go
new file mode 100644
index 0000000..6597d83
--- /dev/null
+++ b/cmd/root.go
@@ -0,0 +1,46 @@
+/*
+Copyright © 2023 NAME HERE <EMAIL ADDRESS>
+*/
+package cmd
+
+import (
+ "os"
+
+ "github.com/spf13/cobra"
+)
+
+// rootCmd represents the base command when called without any subcommands
+var rootCmd = &cobra.Command{
+ Use: "dtool",
+ Short: "dns resolver probing tool",
+ Long: `A tool for probing open resolvers for various purpose. The following modules are implemented.
+
+Modules:
+ upstream probe to get the upstream recursive resolvers of the given resolver as many as possible
+ ttl probe to test if resolvers on the resolution path change the authoritative TTL value
+ cache probe to enumerate the cache`,
+ // Uncomment the following line if your bare application
+ // has an action associated with it:
+ // Run: func(cmd *cobra.Command, args []string) { },
+}
+
+// Execute adds all child commands to the root command and sets flags appropriately.
+// This is called by main.main(). It only needs to happen once to the rootCmd.
+func Execute() {
+ err := rootCmd.Execute()
+ if err != nil {
+ os.Exit(1)
+ }
+}
+
+func init() {
+ // Here you will define your flags and configuration settings.
+ // Cobra supports persistent flags, which, if defined here,
+ // will be global for your application.
+
+ // rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.dtool.yaml)")
+
+ // Cobra also supports local flags, which will only run
+ // when this action is called directly.
+ rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
+}
diff --git a/cmd/upstream.go b/cmd/upstream.go
new file mode 100644
index 0000000..e7d4071
--- /dev/null
+++ b/cmd/upstream.go
@@ -0,0 +1,32 @@
+package cmd
+
+import (
+ _ "dtool/prober"
+ "dtool/utils"
+
+ "github.com/spf13/cobra"
+)
+
+var filename string
+var thread_num int
+var upstreamCmd = &cobra.Command{
+ Use: "upstream",
+ //Aliases: []string{"up", "stream"},
+ Short: "probe upstream recursive resolvers",
+ Long: `get the upstream recursive resolvers of the input resolvers
+input target can be added as an argument or as a file
+
+ -f input file with limited ip addresses (limit=50)
+ -t number of goroutine`,
+ Args: cobra.ExactArgs(1),
+ Run: func(cmd *cobra.Command, args []string) {
+ //prober.Get_upstream_ip(args[0])
+ utils.SendTencentHttpdnsQuery()
+ },
+}
+
+func init() {
+ upstreamCmd.Flags().StringVarP(&filename, "file", "f", "", "input filename")
+ upstreamCmd.Flags().IntVarP(&thread_num, "threads", "t", 10, "number of concurrent threads")
+ rootCmd.AddCommand(upstreamCmd)
+}
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..c08d21e
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,15 @@
+module dtool
+
+go 1.20
+
+require github.com/spf13/cobra v1.7.0
+
+require (
+ github.com/inconshreveable/mousetrap v1.1.0 // indirect
+ github.com/miekg/dns v1.1.54 // indirect
+ github.com/spf13/pflag v1.0.5 // indirect
+ golang.org/x/mod v0.7.0 // indirect
+ golang.org/x/net v0.2.0 // indirect
+ golang.org/x/sys v0.2.0 // indirect
+ golang.org/x/tools v0.3.0 // indirect
+)
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..7fa2eff
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,20 @@
+github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
+github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
+github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
+github.com/miekg/dns v1.1.54 h1:5jon9mWcb0sFJGpnI99tOMhCPyJ+RPVz5b63MQG0VWI=
+github.com/miekg/dns v1.1.54/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
+github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
+github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
+github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
+github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA=
+golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
+golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU=
+golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
+golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
+golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/tools v0.3.0 h1:SrNbZl6ECOS1qFzgTdQfWXZM9XBkiA6tkFrH9YSTPHM=
+golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/main.go b/main.go
new file mode 100644
index 0000000..e422189
--- /dev/null
+++ b/main.go
@@ -0,0 +1,11 @@
+/*
+Copyright © 2023 NAME HERE <EMAIL ADDRESS>
+
+*/
+package main
+
+import "dtool/cmd"
+
+func main() {
+ cmd.Execute()
+}
diff --git a/prober/rdns_prober.go b/prober/rdns_prober.go
new file mode 100644
index 0000000..08d500c
--- /dev/null
+++ b/prober/rdns_prober.go
@@ -0,0 +1,133 @@
+package prober
+
+import (
+ "bufio"
+ "fmt"
+ "io"
+ "os"
+ "strconv"
+ "strings"
+ "sync"
+ "time"
+
+ "dtool/utils"
+)
+
+type Data struct {
+ target string
+ dict map[string]bool
+}
+
+var dataset map[string][]string
+
+func retrieve_ip(pool chan string, filename string) {
+ cnt := 0
+ f, err := os.Open(filename)
+ if err != nil {
+ fmt.Printf("cannot open file %s\n", filename)
+ return
+ }
+ fmt.Println("sending msg ...")
+ reader := bufio.NewReader(f)
+ for {
+ s, err := reader.ReadString('\n')
+ if err == io.EOF {
+ break
+ }
+ s = strings.Trim(s, "\n")
+ pool <- s
+ cnt++
+ if cnt%10 == 0 {
+ fmt.Println(cnt)
+ }
+ }
+ close(pool)
+}
+
+func active_probe(n int, addr string) Data {
+ target_ip := addr[:len(addr)-3]
+ data := Data{target_ip, make(map[string]bool)}
+ stop := 0
+ timestamp := strconv.FormatInt(time.Now().Unix(), 10)
+ for i := 0; i < n; i++ {
+ subdomain := strings.Join([]string{strings.Replace(target_ip, ".", "-", -1), "echo", strconv.Itoa(i), timestamp}, "-")
+ rdns_ip, err := utils.SendQuery(addr, subdomain)
+ if err == nil {
+ data.dict[rdns_ip] = true
+ } else {
+ stop += 1
+ }
+ if stop == 3 {
+ break
+ }
+ }
+ return data
+}
+
+func upstream_prober(ip_pool chan string, data_pool chan Data, wg *sync.WaitGroup) {
+ for {
+ if s, ok := <-ip_pool; ok {
+ addr := s + ":53"
+ data := active_probe(20, addr)
+ if data.dict != nil {
+ data_pool <- data
+ }
+ } else {
+ break
+ }
+ }
+ wg.Done()
+}
+
+func create_probers(num int, ip_pool chan string, data_pool chan Data, wg *sync.WaitGroup) {
+ for i := 0; i < num; i++ {
+ wg.Add(1)
+ go upstream_prober(ip_pool, data_pool, wg)
+ }
+}
+
+func store_data(pool chan Data, wg *sync.WaitGroup) {
+ wg.Add(1)
+ for {
+ var temp []string
+ if data, ok := <-pool; ok {
+ if len(data.dict) > 0 {
+ for rdns := range data.dict {
+ temp = append(temp, rdns)
+ }
+ dataset[data.target] = temp
+ }
+ } else {
+ break
+ }
+ }
+ wg.Done()
+}
+
+func Get_upstream_file(filename string, prober_num int) {
+ dataset = map[string][]string{}
+ ip_pool := make(chan string, 500)
+ data_pool := make(chan Data, 200)
+ var probe_tasks sync.WaitGroup
+ var store_task sync.WaitGroup
+
+ go retrieve_ip(ip_pool, filename)
+ create_probers(prober_num, ip_pool, data_pool, &probe_tasks)
+ go store_data(data_pool, &store_task)
+ probe_tasks.Wait()
+ close(data_pool)
+ store_task.Wait()
+ utils.OutputJSON(dataset)
+}
+
+func Get_upstream_ip(ip string) {
+ dataset = make(map[string][]string)
+ var temp []string
+ addr := ip + ":53"
+ data := active_probe(10, addr)
+ for rdns := range data.dict {
+ temp = append(temp, rdns)
+ }
+ dataset[data.target] = temp
+ utils.OutputJSON(dataset)
+}
diff --git a/utils/dns_utils.go b/utils/dns_utils.go
new file mode 100644
index 0000000..f6d4578
--- /dev/null
+++ b/utils/dns_utils.go
@@ -0,0 +1,55 @@
+package utils
+
+import (
+ "fmt"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/miekg/dns"
+)
+
+type WrongAnswerError struct {
+ Message string
+}
+
+func (e *WrongAnswerError) Error() string {
+ return fmt.Sprintf("Wrong Answer: %s", e.Message)
+}
+
+func SendQuery(addr string, dn string) (string, error) {
+ var (
+ domain string
+ rdns_ip string
+ )
+ if dn == "timestamp" {
+ timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
+ domain = strings.Join([]string{timestamp, "-scan.echodns.xyz."}, "")
+ } else {
+ domain = strings.Join([]string{dn, ".echodns.xyz."}, "")
+ }
+ //fmt.Println(domain)
+ m := new(dns.Msg)
+ m.SetQuestion(domain, dns.TypeA)
+ m.RecursionDesired = true
+
+ res, err := dns.Exchange(m, addr)
+ if err == nil {
+ if len(res.Answer) == 1 {
+ if a, ok := res.Answer[0].(*dns.A); ok {
+ rdns_ip = a.A.String()
+ } else {
+ rdns_ip = ""
+ err = &WrongAnswerError{
+ Message: "Wrong Record Type",
+ }
+ }
+ } else {
+ rdns_ip = ""
+ err = &WrongAnswerError{
+ Message: "Wrong Answer Section",
+ }
+ }
+ }
+ return rdns_ip, err
+}
diff --git a/utils/httpdns_utils.go b/utils/httpdns_utils.go
new file mode 100644
index 0000000..11c891d
--- /dev/null
+++ b/utils/httpdns_utils.go
@@ -0,0 +1,112 @@
+package utils
+
+import (
+ "bytes"
+ "crypto/des"
+ "encoding/hex"
+ "fmt"
+ "io/ioutil"
+ "net/http"
+)
+
+func pkcs5Padding(plaintext []byte, blocksize int) []byte {
+ padding := blocksize - len(plaintext)%blocksize
+ padtext := bytes.Repeat([]byte{byte(padding)}, padding)
+ return append(plaintext, padtext...)
+}
+
+func pkcs5Unpadding(plaintext []byte) []byte {
+ padding := plaintext[len(plaintext)-1]
+ return plaintext[:len(plaintext)-int(padding)]
+}
+
+func encryptDES(plaintext, key []byte) ([]byte, error) {
+ block, err := des.NewCipher(key)
+ if err != nil {
+ return nil, err
+ }
+
+ blocksize := block.BlockSize()
+ plaintext = pkcs5Padding(plaintext, blocksize)
+
+ ciphertext := make([]byte, len(plaintext))
+ for i := 0; i < len(plaintext); i += blocksize {
+ block.Encrypt(ciphertext[i:], plaintext[i:i+blocksize])
+ }
+
+ return ciphertext, nil
+}
+
+func decryptDES(ciphertext, key []byte) ([]byte, error) {
+ block, err := des.NewCipher(key)
+ if err != nil {
+ return nil, err
+ }
+ blocksize := block.BlockSize()
+ decryptedtext := make([]byte, len(ciphertext))
+ for i := 0; i < len(ciphertext); i += blocksize {
+ block.Decrypt(decryptedtext[i:], ciphertext[i:i+blocksize])
+ }
+ decryptedtext = pkcs5Unpadding(decryptedtext)
+ return decryptedtext, nil
+}
+
+func SendTencentHttpdnsQuery() {
+ client := &http.Client{}
+ domain := []byte("echo.echodns.xyz")
+ key := []byte("046Ju3Cw")
+ encrypted_bytes, err := encryptDES(domain, key)
+ if err != nil {
+ return
+ }
+ encrypted_domain := hex.EncodeToString(encrypted_bytes)
+ url := fmt.Sprintf("http://119.29.29.98/d?dn=%s&id=61188", encrypted_domain)
+ fmt.Println(url)
+
+ req, err := http.NewRequest("GET", url, nil)
+ if err != nil {
+ fmt.Printf("create new request failed. Error: %s\n", err)
+ }
+
+ resp, err := client.Do(req)
+ if err != nil {
+ fmt.Printf("request went wrong. Error: %s\n", err)
+ return
+ }
+ defer resp.Body.Close()
+
+ body, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ fmt.Printf("read content failed. Error: %s\n", err)
+ return
+ }
+ encrypted_response, _ := hex.DecodeString(string(body))
+ decrypted_response, err := decryptDES(encrypted_response, key)
+ if err != nil {
+ return
+ }
+ fmt.Println(string(decrypted_response))
+}
+
+func SendAlicloudHttpdnsQurey() {
+ client := &http.Client{}
+ req, err := http.NewRequest("GET", "http://203.107.1.33/149702/d?host=echo.echodns.xyz", nil)
+ if err != nil {
+ fmt.Printf("create new request failed. Error: %s\n", err)
+ }
+
+ resp, err := client.Do(req)
+ if err != nil {
+ fmt.Printf("request went wrong. Error: %s\n", err)
+ return
+ }
+ defer resp.Body.Close()
+
+ body, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ fmt.Printf("read content failed. Error: %s\n", err)
+ return
+ }
+
+ fmt.Println(string(body))
+}
diff --git a/utils/output_utils.go b/utils/output_utils.go
new file mode 100644
index 0000000..88a649c
--- /dev/null
+++ b/utils/output_utils.go
@@ -0,0 +1,15 @@
+package utils
+
+import (
+ "encoding/json"
+ "fmt"
+)
+
+func OutputJSON(data map[string][]string) {
+ jsonstr, err := json.MarshalIndent(data, "", " ")
+ if err != nil {
+ fmt.Println("JSON encoding error:", err)
+ return
+ }
+ fmt.Println(string(jsonstr))
+}