forked from pforemski/dingo
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgdns.go
130 lines (113 loc) · 3.31 KB
/
gdns.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
/**
* dingo: a DNS caching proxy written in Go
* This file implements a Google DNS-over-HTTPS client
*
* Copyright (C) 2016 Pawel Foremski <[email protected]>
* Licensed under GNU GPL v3
*/
package main
import (
"encoding/json"
"flag"
"fmt"
"math/rand"
"net/url"
"time"
)
type Gdns struct {
workers *int
server *string
auto *bool
auto6 *bool
sni *string
host *string
edns *string
nopad *bool
}
/* command-line arguments */
func (r *Gdns) Init() {
r.workers = flag.Int("gdns:workers", 0,
"Google DNS: number of independent workers")
r.server = flag.String("gdns:server", "216.58.195.78",
"Google DNS: server address")
r.auto = flag.Bool("gdns:auto", false,
"Google DNS: try to lookup the closest IPv4 server")
r.auto6 = flag.Bool("gdns:auto6", false,
"Google DNS: try to lookup the closest IPv6 server")
r.sni = flag.String("gdns:sni", "www.google.com",
"Google DNS: SNI string to send (should match server certificate)")
r.host = flag.String("gdns:host", "dns.google.com",
"Google DNS: HTTP 'Host' header (real FQDN, encrypted in TLS)")
r.edns = flag.String("gdns:edns", "",
"Google DNS: EDNS client subnet (set 0.0.0.0/0 to disable)")
r.nopad = flag.Bool("gdns:nopad", false,
"Google DNS: disable random padding")
}
/**********************************************************************/
func (R *Gdns) Start() {
if *R.workers <= 0 {
return
}
if *R.auto6 {
dbg(1, "resolving dns.google.com...")
r6 := R.resolve(NewHttps(*R.sni, false), *R.server, "dns.google.com", 28)
if r6.Status == 0 && len(r6.Answer) > 0 {
R.server = &r6.Answer[0].Data
}
} else {
if *R.auto {
dbg(1, "resolving dns.google.com...")
r4 := R.resolve(NewHttps(*R.sni, false), *R.server, "dns.google.com", 1)
if r4.Status == 0 && len(r4.Answer) > 0 {
R.server = &r4.Answer[0].Data
}
}
}
dbg(1, "starting %d Google Public DNS client(s) querying server %s",
*R.workers, *R.server)
for i := 0; i < *R.workers; i++ {
go R.worker(*R.server)
}
}
// To prevent misinterpretation of the URL, restrict the padding characters to the unreserved URL characters:
// upper- and lower-case letters, digits, hyphen, period, underscore and tilde. http://stackoverflow.com/a/695469/18829
const padChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~"
func getPaddedStr(n int) string {
s := make([]byte, n)
for i := range s {
s[i] = padChars[rand.Intn(len(padChars))]
}
return string(s)
}
func (R *Gdns) worker(server string) {
var https = NewHttps(*R.sni, false)
for q := range qchan {
*q.rchan <- *R.resolve(https, server, q.Name, q.Type)
}
}
func (R *Gdns) resolve(https *Https, server string, qname string, qtype int) *Reply {
r := Reply{Status: -1}
v := url.Values{}
/* prepare */
v.Set("name", qname)
v.Set("type", fmt.Sprintf("%d", qtype))
if len(*R.edns) > 0 {
v.Set("edns_client_subnet", *R.edns)
}
if !*R.nopad {
// maximum dnslength+type.length (longest possible Type 5 digits)
// minus current to make always equal query length url
v.Set("random_padding", getPaddedStr(259-len(qname)-len(fmt.Sprintf("%d", qtype))))
}
/* query */
buf, err := https.Get(server, *R.host, "/resolve?"+v.Encode())
if err != nil {
return &r
}
/* parse */
r.Now = time.Now()
json.Unmarshal(buf, &r)
return &r
}
/* register module */
var _ = register("gdns", new(Gdns))