standard commit

This commit is contained in:
MDK
2023-08-03 15:25:17 +08:00
parent 38be845348
commit 4fcf28804c
12 changed files with 466 additions and 87 deletions

37
cmd/cache.go Normal file
View File

@@ -0,0 +1,37 @@
package cmd
import (
"dtool/prober"
"dtool/scheduler"
"dtool/utils"
"github.com/spf13/cobra"
)
var query_cnt int
var inputfile string
var outputfile string
var cacheCmd = &cobra.Command{
Use: "cache",
Short: "cache related test",
Long: "cache related test",
Run: cache_test,
}
func cache_test(cmd *cobra.Command, args []string) {
if len(args) == 1 {
if utils.IsValidIP(args[0]) {
prober.RecursiveCacheTest(args[0], query_cnt)
}
} else {
scheduler.CreateTask(prober.RecursiveCacheProbe, inputfile, outputfile, 10)
}
}
func init() {
cacheCmd.Flags().StringVarP(&inputfile, "input", "i", "", "input file(optional)")
cacheCmd.Flags().StringVarP(&outputfile, "output", "o", "", "output file(optional)")
cacheCmd.MarkFlagsRequiredTogether("input", "output")
cacheCmd.Flags().IntVarP(&query_cnt, "num", "n", 20, "number of queries in one test")
rootCmd.AddCommand(cacheCmd)
}

25
cmd/domain.go Normal file
View File

@@ -0,0 +1,25 @@
package cmd
import (
"github.com/spf13/cobra"
)
var input string
var output string
var domainCmd = &cobra.Command{
Use: "domain",
Short: "query the ip and nameserver information",
Long: "query the ip and nameserver information",
Run: getDomainInfo,
}
func getDomainInfo(cmd *cobra.Command, args []string) {
}
func init() {
domainCmd.Flags().StringVarP(&input, "input", "i", "", "")
domainCmd.Flags().StringVarP(&output, "output", "o", "", "")
domainCmd.MarkFlagsRequiredTogether("input", "output")
rootCmd.AddCommand(domainCmd)
}

View File

@@ -2,9 +2,7 @@ package cmd
import ( import (
"dtool/prober" "dtool/prober"
_ "dtool/prober"
"dtool/utils" "dtool/utils"
_ "dtool/utils"
"errors" "errors"
"github.com/spf13/cobra" "github.com/spf13/cobra"

20
cmd/version.go Normal file
View File

@@ -0,0 +1,20 @@
package cmd
import (
"github.com/spf13/cobra"
)
var versionCmd = &cobra.Command{
Use: "version",
Short: "get server version with version.bind",
Long: "get server version with version.bind chaos txt request",
Run: version,
}
func version(cmd *cobra.Command, args []string) {
}
func init() {
rootCmd.AddCommand(versionCmd)
}

66
prober/cache_prober.go Normal file
View File

@@ -0,0 +1,66 @@
package prober
import (
"dtool/utils"
_ "fmt"
"strconv"
"strings"
"time"
)
const query_num = 20
type CacheStruct struct {
target string
dict map[int]map[string]bool
}
func RecursiveCacheProbe(ip string) CacheStruct {
data := CacheStruct{ip, make(map[int]map[string]bool)}
stop := 0
time_now := strconv.FormatInt(time.Now().Unix(), 10)
for i := 0; i < query_num; i++ {
if stop >= 3 {
break
}
subdomain := strings.Join([]string{strings.Replace(ip, ".", "-", -1), "fwd", strconv.Itoa(i), time_now}, "-")
domain := subdomain + ".echodns.xyz."
res, err := utils.SendQuery(ip, domain)
if err != nil {
//fmt.Printf("Error sending query: %s\n", err)
stop += 1
continue
}
cache_id, rdns, err := utils.ParseCNAMEChain(res)
if err != nil {
//fmt.Printf("Error parsing response: %s\n", err)
stop += 1
continue
}
if data.dict[cache_id] == nil {
data.dict[cache_id] = make(map[string]bool)
}
data.dict[cache_id][rdns] = true
stop = 0
}
return data
}
func RecursiveCacheTest(ip string, num int) {
res := make(map[string]map[int][]string)
temp := make(map[int][]string)
data := RecursiveCacheProbe(ip)
if len(data.dict) > 0 {
for cache_id := range data.dict {
for rdns := range data.dict[cache_id] {
temp[cache_id] = append(temp[cache_id], rdns)
}
}
}
res[ip] = temp
utils.OutputJSON(res, "-", " ")
}
func ClientCacheProbe(ip string) {
}

View File

@@ -1,56 +1,37 @@
package prober package prober
import ( import (
"bufio"
"fmt"
"io"
"os"
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
"time" "time"
"dtool/utils" "dtool/utils"
"github.com/miekg/dns"
) )
type Data struct { type RecursiveStruct struct {
target string target string
dict map[string]bool dict map[string]bool
} }
var dataset map[string][]string var dataset map[string][]string
func retrieve_ip(pool chan string, filename string) { func active_probe(n int, addr string) RecursiveStruct {
cnt := 0 target_ip := addr
f, err := os.Open(filename) data := RecursiveStruct{target_ip, make(map[string]bool)}
if err != nil {
panic(err)
}
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 stop := 0
timestamp := strconv.FormatInt(time.Now().Unix(), 10) timestamp := strconv.FormatInt(time.Now().Unix(), 10)
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
subdomain := strings.Join([]string{strings.Replace(target_ip, ".", "-", -1), "echo", strconv.Itoa(i), timestamp}, "-") subdomain := strings.Join([]string{strings.Replace(target_ip, ".", "-", -1), "echo", strconv.Itoa(i), timestamp}, "-")
rdns_ip, err := utils.SendQuery(addr, subdomain) domain := dns.Fqdn(subdomain + ".echodns.xyz")
res, err := utils.SendQuery(addr, domain)
if err != nil {
stop += 1
continue
}
rdns_ip, err := utils.ParseAResponse(res)
if err == nil { if err == nil {
data.dict[rdns_ip] = true data.dict[rdns_ip] = true
} else { } else {
@@ -63,11 +44,10 @@ func active_probe(n int, addr string) Data {
return data return data
} }
func upstream_prober(ip_pool chan string, data_pool chan Data, wg *sync.WaitGroup) { func upstream_prober(ip_pool chan string, data_pool chan RecursiveStruct, wg *sync.WaitGroup) {
for { for {
if s, ok := <-ip_pool; ok { if s, ok := <-ip_pool; ok {
addr := s + ":53" data := active_probe(20, s)
data := active_probe(20, addr)
if data.dict != nil { if data.dict != nil {
data_pool <- data data_pool <- data
} }
@@ -78,14 +58,14 @@ func upstream_prober(ip_pool chan string, data_pool chan Data, wg *sync.WaitGrou
wg.Done() wg.Done()
} }
func create_probers(num int, ip_pool chan string, data_pool chan Data, wg *sync.WaitGroup) { func create_probers(num int, ip_pool chan string, data_pool chan RecursiveStruct, wg *sync.WaitGroup) {
for i := 0; i < num; i++ { for i := 0; i < num; i++ {
wg.Add(1) wg.Add(1)
go upstream_prober(ip_pool, data_pool, wg) go upstream_prober(ip_pool, data_pool, wg)
} }
} }
func store_data(pool chan Data, wg *sync.WaitGroup) { func store_data(pool chan RecursiveStruct, wg *sync.WaitGroup) {
wg.Add(1) wg.Add(1)
for { for {
var temp []string var temp []string
@@ -106,27 +86,26 @@ func store_data(pool chan Data, wg *sync.WaitGroup) {
func Get_upstream_file(filename string, output string, prober_num int) { func Get_upstream_file(filename string, output string, prober_num int) {
dataset = map[string][]string{} dataset = map[string][]string{}
ip_pool := make(chan string, 500) ip_pool := make(chan string, 500)
data_pool := make(chan Data, 200) data_pool := make(chan RecursiveStruct, 200)
var probe_tasks sync.WaitGroup var probe_tasks sync.WaitGroup
var store_task sync.WaitGroup var store_task sync.WaitGroup
go retrieve_ip(ip_pool, filename) go utils.RetrieveLines(ip_pool, filename)
create_probers(prober_num, ip_pool, data_pool, &probe_tasks) create_probers(prober_num, ip_pool, data_pool, &probe_tasks)
go store_data(data_pool, &store_task) go store_data(data_pool, &store_task)
probe_tasks.Wait() probe_tasks.Wait()
close(data_pool) close(data_pool)
store_task.Wait() store_task.Wait()
utils.OutputJSON(dataset, output) utils.OutputJSON(dataset, output, "")
} }
func Get_upstream_ip(ip string) { func Get_upstream_ip(ip string) {
dataset = make(map[string][]string) dataset = make(map[string][]string)
var temp []string var temp []string
addr := ip + ":53" data := active_probe(10, ip)
data := active_probe(10, addr)
for rdns := range data.dict { for rdns := range data.dict {
temp = append(temp, rdns) temp = append(temp, rdns)
} }
dataset[data.target] = temp dataset[data.target] = temp
utils.OutputJSON(dataset, "-") utils.OutputJSON(dataset, "-", " ")
} }

31
prober/result_handler.go Normal file
View File

@@ -0,0 +1,31 @@
package prober
import "dtool/utils"
func OutputHandler(data interface{}) (string, error) {
var output_str string
var err error
switch value := data.(type) {
case CacheStruct:
result := make(map[string]map[int][]string)
temp := make(map[int][]string)
if len(value.dict) > 0 {
for cache_id := range value.dict {
for rdns := range value.dict[cache_id] {
temp[cache_id] = append(temp[cache_id], rdns)
}
}
}
result[value.target] = temp
output_str, err = utils.ToJSON(result, "")
case RecursiveStruct:
result := make(map[string][]string)
temp := []string{}
for rdns := range value.dict {
temp = append(temp, rdns)
}
result[value.target] = temp
output_str, err = utils.ToJSON(result, "")
}
return output_str, err
}

1
prober/version_prober.go Normal file
View File

@@ -0,0 +1 @@
package prober

68
scheduler/scheduler.go Normal file
View File

@@ -0,0 +1,68 @@
package scheduler
import (
"bufio"
"dtool/prober"
"dtool/utils"
"fmt"
"os"
"sync"
)
//type ProbeTask func(string) interface{}
func output_process(output chan interface{}, file string, wg *sync.WaitGroup) {
f, err := os.Create(file)
if err != nil {
panic(err)
}
defer f.Close()
writer := bufio.NewWriter(f)
for {
if data, ok := <-output; ok {
str, err := prober.OutputHandler(data)
if err != nil {
fmt.Printf("Error generating output: %s\n", err)
continue
}
_, err = writer.WriteString(str + "\n")
if err != nil {
fmt.Printf("Error writing file: %s\n", err)
}
} else {
break
}
}
writer.Flush()
wg.Done()
}
func concurrent_execution[T any](fn func(string) T, input chan string, output chan interface{}, wg *sync.WaitGroup) {
for {
if ip, ok := <-input; ok {
data := fn(ip)
output <- data
} else {
break
}
}
wg.Done()
}
func CreateTask[T any](fn func(string) T, input_file string, output_file string, concurrent_num int) {
input_pool := make(chan string, 500)
output_pool := make(chan interface{}, 500)
var probe_tasks sync.WaitGroup
var store_tasks sync.WaitGroup
go utils.RetrieveLines(input_pool, input_file)
probe_tasks.Add(concurrent_num)
for i := 0; i < concurrent_num; i++ {
go concurrent_execution(fn, input_pool, output_pool, &probe_tasks)
}
store_tasks.Add(1)
go output_process(output_pool, output_file, &store_tasks)
probe_tasks.Wait()
close(output_pool)
store_tasks.Wait()
}

View File

@@ -1,10 +1,11 @@
// dns_utils.go contains the necessary functions for building
// and parsing a DNS packet
package utils package utils
import ( import (
"encoding/binary"
"fmt" "fmt"
"strconv"
"strings" "strings"
"time"
"github.com/miekg/dns" "github.com/miekg/dns"
) )
@@ -13,43 +14,144 @@ type WrongAnswerError struct {
Message string Message string
} }
type QueryStruct struct {
Id uint16
RD bool
Qname string
Qclass uint16
Qtype uint16
}
type DomainInfo struct {
IPList []string
NSList map[string][]string
}
func (e *WrongAnswerError) Error() string { func (e *WrongAnswerError) Error() string {
return fmt.Sprintf("Wrong Answer: %s", e.Message) return fmt.Sprintf("Wrong Answer: %s", e.Message)
} }
func SendQuery(addr string, dn string) (string, error) { // build the question section of a dns packet
var ( func QuestionMaker(domain string, qclass uint16, qtype uint16) *dns.Question {
domain string return &dns.Question{Name: domain, Qtype: qtype, Qclass: qclass}
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) // build a dns query message
if err == nil { func QueryMaker(query QueryStruct) *dns.Msg {
if len(res.Answer) == 1 { msg := new(dns.Msg)
if a, ok := res.Answer[0].(*dns.A); ok { if query.Id < 0 {
rdns_ip = a.A.String() msg.Id = dns.Id()
} else { } else {
rdns_ip = "" msg.Id = query.Id
err = &WrongAnswerError{ }
Message: "Wrong Record Type", msg.RecursionDesired = query.RD
}
} var query_name string
var query_class, query_type uint16
msg.Question = make([]dns.Question, 1)
query_name = dns.Fqdn(query.Qname)
// default class INET
if query.Qclass == 0 {
query_class = 1
} else {
query_class = query.Qclass
}
// default type A
if query.Qtype == 0 {
query_type = 1
} else {
query_type = query.Qtype
}
question := QuestionMaker(query_name, query_class, query_type)
msg.Question[0] = *question
return msg
}
func ParseAResponse(msg *dns.Msg) (string, error) {
var ip_str string
if len(msg.Answer) == 1 {
if a, ok := msg.Answer[0].(*dns.A); ok {
ip_str = a.A.String()
} else { } else {
rdns_ip = "" err := &WrongAnswerError{
err = &WrongAnswerError{ Message: "Wrong record type",
Message: "Wrong Answer Section", }
return ip_str, err
}
} else {
err := &WrongAnswerError{
Message: "Wrong answer section",
}
return ip_str, err
}
return ip_str, nil
}
func ParseCNAMEChain(msg *dns.Msg) (int, string, error) {
var cache_id int
var rdns_ip string
if len(msg.Answer) == 3 {
if cname, ok := msg.Answer[0].(*dns.CNAME); ok {
rdns_ip = strings.Join(strings.Split(strings.Split(cname.Target, ".")[0], "-")[1:], ".")
if a, ok := msg.Answer[2].(*dns.A); ok {
cache_id = int(binary.BigEndian.Uint32(a.A))
} }
} }
} else {
err := &WrongAnswerError{
Message: "Wrong record number",
}
return cache_id, rdns_ip, err
} }
return rdns_ip, err return cache_id, rdns_ip, nil
}
func ParseTXTResponse(msg *dns.Msg) (string, error) {
var txt_string string
if len(msg.Answer) == 1 {
if txt, ok := msg.Answer[0].(*dns.TXT); ok {
txt_string = txt.String()
} else {
err := &WrongAnswerError{
Message: "Wrong record type",
}
return txt_string, err
}
} else {
err := &WrongAnswerError{
Message: "wrong record number",
}
return txt_string, err
}
return txt_string, nil
}
func SendQuery(ip string, domain string) (*dns.Msg, error) {
addr := ip + ":53"
query := new(QueryStruct)
query.Qname = domain
query.RD = true
query.Id = dns.Id()
m := QueryMaker(*query)
res, err := dns.Exchange(m, addr)
return res, err
}
func SendVersionQuery(ip string) (*dns.Msg, error) {
addr := ip + "53"
query := new(QueryStruct)
query.Id = dns.Id()
query.Qname = "version.bind"
query.Qclass = dns.ClassCHAOS
query.Qtype = dns.TypeTXT
m := QueryMaker(*query)
res, err := dns.Exchange(m, addr)
return res, err
} }

32
utils/input_utils.go Normal file
View File

@@ -0,0 +1,32 @@
package utils
import (
"bufio"
"fmt"
"io"
"os"
"strings"
)
func RetrieveLines(pool chan string, filename string) {
cnt := 0
f, err := os.Open(filename)
if err != nil {
panic(err)
}
fmt.Println("reading file ...")
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)
}

View File

@@ -3,22 +3,42 @@ package utils
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil" "os"
) )
func OutputJSON(data interface{}, filename string) error { func ToJSON(data interface{}, indent string) (string, error) {
jsonstr, err := json.MarshalIndent(data, "", " ") var jsonbyte []byte
if err != nil { var err error
fmt.Println("JSON encoding error:", err) if indent == "" {
return err jsonbyte, err = json.Marshal(data)
}
if filename == "-" {
fmt.Println(string(jsonstr))
} else { } else {
err := ioutil.WriteFile(filename, jsonstr, 0666) jsonbyte, err = json.MarshalIndent(data, "", indent)
}
return string(jsonbyte), err
}
func WriteFileLine(filename string, data string) error {
if filename == "-" {
fmt.Println(data)
} else {
err := os.WriteFile(filename, []byte(data), 0666)
if err != nil { if err != nil {
return err return err
} }
} }
return nil return nil
} }
func OutputJSON(data interface{}, filename string, indent string) error {
str, err := ToJSON(data, indent)
if err != nil {
fmt.Printf("Error encoding JSON: %s", err)
return err
}
err = WriteFileLine(filename, str)
if err != nil {
fmt.Printf("Error writing to file: %s", err)
return err
}
return nil
}