initial commit

This commit is contained in:
MDK
2024-02-21 15:28:24 +08:00
commit 66580aad74
6 changed files with 231 additions and 0 deletions

12
go.mod Normal file
View File

@@ -0,0 +1,12 @@
module fpdns_client
go 1.20
require github.com/miekg/dns v1.1.58
require (
golang.org/x/mod v0.14.0 // indirect
golang.org/x/net v0.20.0 // indirect
golang.org/x/sys v0.16.0 // indirect
golang.org/x/tools v0.17.0 // indirect
)

11
go.sum Normal file
View File

@@ -0,0 +1,11 @@
github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4=
github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY=
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc=
golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps=

13
main.go Normal file
View File

@@ -0,0 +1,13 @@
package main
import (
"fmt"
"fpdns_client/methods"
)
func main() {
_, err := methods.BehaviorTest("127.0.0.1", "127.0.0.1", "8888", "echodns.xyz")
if err != nil {
fmt.Println(err)
}
}

96
methods/test_methods.go Normal file
View File

@@ -0,0 +1,96 @@
package methods
import (
"errors"
"fmt"
"fpdns_client/utils"
"strconv"
"time"
"github.com/miekg/dns"
)
func ProbeStart(addr string, sld string) (int, error) {
time_now := strconv.FormatInt(time.Now().Unix(), 10)
domain := dns.Fqdn("start-" + time_now + "." + sld)
res, err := utils.DNSQuery(addr, domain, false, 1, 1, false)
if err == nil {
if len(res.Answer) == 1 {
if a, ok := res.Answer[0].(*dns.A); ok {
ip_str := a.A.String()
ip_val, _ := utils.IPv4ToInt(ip_str)
return int(ip_val), nil
}
}
} else {
return 0, err
}
return 0, errors.New("wrong token response")
}
func InitProbe(addr string, token int, sld string) error {
time_now := strconv.FormatInt(time.Now().Unix(), 10)
domain := dns.Fqdn("init-" + time_now + "-" + strconv.Itoa(token) + "." + sld)
res, err := utils.DNSQuery(addr, domain, true, 1, 1, false)
if err != nil {
return err
} else {
if len(res.Answer) == 1 {
if a, ok := res.Answer[0].(*dns.A); ok {
ip_str := a.A.String()
if ip_str == "200.200.200.200" {
return nil
}
} else {
return errors.New("invalid Reply Code")
}
}
}
return errors.New("init query failure")
}
func ProbeTerminate(addr string, token int, sld string) error {
time_now := strconv.FormatInt(time.Now().Unix(), 10)
domain := dns.Fqdn("end-" + time_now + "-" + strconv.Itoa(token) + "." + sld)
res, err := utils.DNSQuery(addr, domain, false, 1, 1, false)
if err != nil {
return err
} else {
if len(res.Answer) == 1 {
if a, ok := res.Answer[0].(*dns.A); ok {
ip_str := a.A.String()
if ip_str == "200.200.200.200" {
return nil
}
} else {
return errors.New("invalid Reply Code")
}
}
}
return errors.New("terminate query failure")
}
func BehaviorTest(target string, server string, api_port string, sld string) (string, error) {
target_addr := target + ":53"
server_addr := server + ":53"
result_addr := server + ":" + api_port
token, err := ProbeStart(server_addr, sld)
if err != nil {
return "", err
}
err = InitProbe(target_addr, token, sld)
if err != nil {
return "", err
}
err = ProbeTerminate(server_addr, token, sld)
if err != nil {
return "", err
}
data, err := utils.GetResult(result_addr, token)
if err != nil {
return "", err
}
json_str, _ := utils.OutputJson(data)
fmt.Println(json_str)
return "", nil
}

31
utils/dns_utils.go Normal file
View File

@@ -0,0 +1,31 @@
// dns utils
package utils
import (
"github.com/miekg/dns"
)
// build the question section of a dns packet
func QuestionMaker(domain string, qclass uint16, qtype uint16) *dns.Question {
return &dns.Question{Name: dns.Fqdn(domain), Qtype: qtype, Qclass: qclass}
}
// build a specific query message
func QueryMaker(domain string, rd bool, qclass uint16, qtype uint16, edns bool) *dns.Msg {
msg := new(dns.Msg)
msg.Id = dns.Id()
msg.RecursionDesired = rd
msg.Question = make([]dns.Question, 1)
msg.Question[0] = *QuestionMaker(domain, qclass, qtype)
if edns {
msg = msg.SetEdns0(4096, false)
}
return msg
}
// query and receive the response
func DNSQuery(addr string, domain string, rd bool, qclass uint16, qtype uint16, edns bool) (*dns.Msg, error) {
msg := QueryMaker(domain, rd, qclass, qtype, edns)
res, err := dns.Exchange(msg, addr)
return res, err
}

68
utils/other_utils.go Normal file
View File

@@ -0,0 +1,68 @@
package utils
import (
"encoding/json"
"fmt"
"net"
"net/http"
"strconv"
"strings"
)
type ResolverBehavior struct {
Dnssec bool `json:"dnssec"`
QnameEncode bool `json:"0x20"`
QueryMerge int `json:"merge_dup"`
MaxNsDepth int `json:"max_ns_depth"`
MaxCnameDepth int `json:"max_cname_depth"`
RetryLimit int `json:"retry_limit"`
FetchLimit int `json:"fetch_limit"`
TimeoutStart int64 `json:"timeout_start"`
TimeoutEnd int64 `json:"timeout_end"`
}
func IsValidIP(ip string) bool {
res := net.ParseIP(ip)
return res != nil
}
func IPv4ToInt(ip string) (uint32, error) {
if !IsValidIP(ip) {
return 0, fmt.Errorf("invalid IP address: %s", ip)
}
labels := strings.Split(ip, ".")
var result uint32
for _, label := range labels {
label_val, _ := strconv.Atoi(label)
result = (result << 8) | uint32(label_val)
}
return result, nil
}
func GetResult(addr string, token int) (*ResolverBehavior, error) {
data := new(ResolverBehavior)
url := "http://" + addr + "/results/" + strconv.Itoa(token)
response, err := http.Get(url)
if err != nil {
return data, err
}
defer response.Body.Close()
if response.StatusCode != http.StatusOK {
return data, fmt.Errorf("wrong HTTP status code : %v", response.StatusCode)
}
err = json.NewDecoder(response.Body).Decode(data)
if err != nil {
return data, err
}
return data, nil
}
func OutputJson(data interface{}) (string, error) {
json_byte, err := json.Marshal(data)
if err != nil {
return "", err
}
return string(json_byte), nil
}