forked from mosajjal/sniproxy
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdns.go
136 lines (118 loc) · 3.14 KB
/
dns.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
package main
import (
"bufio"
"crypto/tls"
"fmt"
"net/http"
"net/url"
"os"
"strings"
"time"
"github.com/mosajjal/sniproxy/doh"
doqclient "github.com/natesales/doqd/pkg/client"
log "github.com/sirupsen/logrus"
"github.com/miekg/dns"
)
// inDomainList returns true if the domain exists in the routeDomainList
// todo: this needs to be replaced by a few tst
func inDomainList(name string) bool {
for _, item := range c.routeDomainList {
if len(item) == 2 {
if item[1] == "suffix" {
if strings.HasSuffix(name, item[0]) {
return true
}
} else if item[1] == "fqdn" {
if name == item[0] {
return true
}
} else if item[1] == "prefix" {
if strings.HasPrefix(name, item[0]) {
return true
}
}
}
}
return false
}
var dnsClient struct {
Doq doqclient.Client
Doh doh.Client
classicDNS dns.Client
}
func loadDomainsToList(Filename string) [][]string {
log.Info("Loading the domain from file/url to a list")
var lines [][]string
var scanner *bufio.Scanner
if strings.HasPrefix(Filename, "http://") || strings.HasPrefix(Filename, "https://") {
log.Info("domain list is a URL, trying to fetch")
http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
client := http.Client{
CheckRedirect: func(r *http.Request, via []*http.Request) error {
r.URL.Opaque = r.URL.Path
return nil
},
}
resp, err := client.Get(Filename)
if err != nil {
log.Fatal(err)
}
log.Info("(re)fetching URL: ", Filename)
defer resp.Body.Close()
scanner = bufio.NewScanner(resp.Body)
} else {
file, err := os.Open(Filename)
if err != nil {
log.Fatal(err)
}
log.Info("(re)loading File: ", Filename)
defer file.Close()
scanner = bufio.NewScanner(file)
}
for scanner.Scan() {
lowerCaseLine := strings.ToLower(scanner.Text())
lines = append(lines, strings.Split(lowerCaseLine, ","))
}
log.Infof("%s loaded with %d lines", Filename, len(lines))
return lines
}
func performExternalQuery(question dns.Question, server string) (*dns.Msg, time.Duration, error) {
dnsURL, err := url.Parse(server)
if err != nil {
log.Fatalf("Invalid upstream DNS URL: %s", server)
}
msg := dns.Msg{
MsgHdr: dns.MsgHdr{
Id: dns.Id(),
RecursionDesired: true,
},
Question: []dns.Question{question},
}
if dnsURL.Scheme == "quic" {
rmsg, err := dnsClient.Doq.SendQuery(msg)
return &rmsg, 0, err
}
if dnsURL.Scheme == "https" {
rmsg, t, err := dnsClient.Doh.SendQuery(msg)
return &rmsg, t, err
}
return dnsClient.classicDNS.Exchange(&msg, dnsURL.Host)
}
func processQuestion(q dns.Question) ([]dns.RR, error) {
if c.AllDomains || inDomainList(q.Name) {
// Return the public IP.
rr, err := dns.NewRR(fmt.Sprintf("%s A %s", q.Name, c.PublicIP))
if err != nil {
return nil, err
}
log.Printf("returned sniproxy address for domain: %s", q.Name)
return []dns.RR{rr}, nil
}
// Otherwise do an upstream query and use that answer.
resp, rtt, err := performExternalQuery(q, c.UpstreamDNS)
if err != nil {
return nil, err
}
log.Printf("returned origin address for domain: %s, rtt: %s", q.Name, rtt)
return resp.Answer, nil
}