Skip to content

Commit 62609ec

Browse files
committed
simulator/encrypted-dns: new encrypted DNS module
DoH and DoT. TODO: DNSCrypt
1 parent 487ab0f commit 62609ec

File tree

12 files changed

+629
-0
lines changed

12 files changed

+629
-0
lines changed

cmd/run/run.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,16 @@ var allModules = []Module{
241241
HostMsg: "Simulating DNS tunneling via *.%s",
242242
Timeout: 10 * time.Second,
243243
},
244+
Module{
245+
Module: simulator.NewEncryptedDNS(),
246+
Name: "encrypted-dns",
247+
Pipeline: PipelineDNS,
248+
NumOfHosts: 1,
249+
// HeaderMsg: "Preparing DNS tunnel hostnames",
250+
HostMsg: "Simulating Encrypted DNS via *.%s",
251+
Timeout: 10 * time.Second,
252+
},
253+
244254
Module{
245255
Module: simulator.CreateModule(wisdom.NewWisdomHosts("cryptomining", wisdom.HostTypeIP), simulator.NewStratumMiner()),
246256
Name: "miner",

simulator/encdns/doh/doh.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Package doh provides general DNS-over-HTTPS functionality.
2+
package doh
3+
4+
import (
5+
"encoding/json"
6+
"net/http"
7+
)
8+
9+
// Question section of DoH query.
10+
type Question struct {
11+
Name string `json:"name"`
12+
Type int `json:"type"`
13+
}
14+
15+
// Answer section of DoH query.
16+
type Answer struct {
17+
Name string `json:"name"`
18+
Type int `json:"type"`
19+
TTL int `json:"TTL"`
20+
Data string `json:"data"`
21+
}
22+
23+
// Response to a DoH query.
24+
type Response struct {
25+
Status int `json:"Status"`
26+
TC bool `json:"TC"`
27+
RD bool `json:"RD"`
28+
RA bool `json:"RA"`
29+
AD bool `json:"AD"`
30+
CD bool `json:"CD"`
31+
Question []Question `json:"Question"`
32+
Answer []Answer `json:"Answer"`
33+
Comment string `json:"Comment"`
34+
}
35+
36+
// Decode decodes a DoH response, returning a pointer to a Response and an error.
37+
// NOTE: Currently we don't care about parsing responses, but perhaps in the future.
38+
func Decode(r *http.Response) (*Response, error) {
39+
var resp Response
40+
err := json.NewDecoder(r.Body).Decode(&resp)
41+
if err != nil {
42+
return nil, err
43+
}
44+
return &resp, nil
45+
46+
}
47+
48+
// IsValidResponse returns a boolean indicating if the the *http.Response r was a 200OK.
49+
func IsValidResponse(r *http.Response) bool {
50+
return r != nil && r.StatusCode == 200
51+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package providers
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"net"
7+
"net/http"
8+
9+
"github.com/alphasoc/flightsim/simulator/encdns"
10+
)
11+
12+
type CloudFlare struct {
13+
Provider
14+
}
15+
16+
// NewCloudFlare returns a *CloudFlare wrapping a Provider ready to use for DoH queries.
17+
func NewCloudFlare(ctx context.Context) *CloudFlare {
18+
p := CloudFlare{
19+
Provider{
20+
addr: "cloudflare-dns.com:443",
21+
queryURL: "https://cloudflare-dns.com/dns-query",
22+
},
23+
}
24+
d := net.Dialer{}
25+
tr := &http.Transport{
26+
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
27+
return d.DialContext(ctx, "tcp", p.addr)
28+
},
29+
}
30+
p.client = &http.Client{Transport: tr}
31+
return &p
32+
}
33+
34+
// QueryTXT performs a DoH TXT lookup on CloudFlare and returns an *encdns.Response and an
35+
// error.
36+
func (p *CloudFlare) QueryTXT(ctx context.Context, domain string) (*encdns.Response, error) {
37+
reqStr := fmt.Sprintf("%v?name=%v&type=TXT", p.queryURL, domain)
38+
req, err := http.NewRequestWithContext(ctx, "GET", reqStr, nil)
39+
if err != nil {
40+
return nil, err
41+
}
42+
req.Header.Add("accept", "application/dns-json")
43+
clientResp, err := p.client.Do(req)
44+
return &encdns.Response{U: clientResp}, err
45+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package providers
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"net"
7+
"net/http"
8+
9+
"github.com/alphasoc/flightsim/simulator/encdns"
10+
)
11+
12+
type Google struct {
13+
Provider
14+
}
15+
16+
// NewGoogle returns a *Google wrapping a Provider ready to use for DoH queries.
17+
func NewGoogle(ctx context.Context) *Google {
18+
p := Google{
19+
Provider{
20+
addr: "dns.google:443",
21+
queryURL: "https://dns.google/resolve",
22+
},
23+
}
24+
d := net.Dialer{}
25+
tr := &http.Transport{
26+
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
27+
return d.DialContext(ctx, "tcp", p.addr)
28+
},
29+
}
30+
p.client = &http.Client{Transport: tr}
31+
return &p
32+
}
33+
34+
// QueryTXT performs a DoH TXT lookup on Google and returns an *encdns.Response and an
35+
// error.
36+
func (p *Google) QueryTXT(ctx context.Context, domain string) (*encdns.Response, error) {
37+
reqStr := fmt.Sprintf("%v?name=%v&type=TXT", p.queryURL, domain)
38+
req, err := http.NewRequestWithContext(ctx, "GET", reqStr, nil)
39+
if err != nil {
40+
return nil, err
41+
}
42+
clientResp, err := p.client.Do(req)
43+
return &encdns.Response{U: clientResp}, err
44+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package providers
2+
3+
import (
4+
"context"
5+
"encoding/base64"
6+
"fmt"
7+
"net"
8+
"net/http"
9+
10+
"github.com/alphasoc/flightsim/simulator/encdns"
11+
"golang.org/x/net/dns/dnsmessage"
12+
)
13+
14+
type OpenDNS struct {
15+
Provider
16+
}
17+
18+
// NewOpenDNS returns an *OpenDNS wrapping a Provider ready to use for DoH queries.
19+
func NewOpenDNS(ctx context.Context) *OpenDNS {
20+
p := OpenDNS{
21+
Provider{
22+
addr: "doh.opendns.com:443",
23+
queryURL: "https://doh.opendns.com/dns-query",
24+
},
25+
}
26+
d := net.Dialer{}
27+
tr := &http.Transport{
28+
DialContext: func(ctxt context.Context, network, addr string) (net.Conn, error) {
29+
return d.DialContext(ctx, "tcp", p.addr)
30+
},
31+
}
32+
p.client = &http.Client{Transport: tr}
33+
return &p
34+
}
35+
36+
// QueryTXT performs a DoH TXT lookup on OpenDNS and returns an *encdns.Response and an
37+
// error.
38+
func (p *OpenDNS) QueryTXT(ctx context.Context, domain string) (*encdns.Response, error) {
39+
// OpenDNS requires requests to be in DNS wire format.
40+
dnsReq, err := encdns.NewUDPRequest(domain, dnsmessage.TypeTXT)
41+
if err != nil {
42+
return nil, err
43+
}
44+
reqStr := fmt.Sprintf("%v?dns=%v", p.queryURL, base64.StdEncoding.EncodeToString(dnsReq))
45+
req, err := http.NewRequestWithContext(ctx, "GET", reqStr, nil)
46+
if err != nil {
47+
return nil, err
48+
}
49+
req.Header.Add("accept", "application/dns-message")
50+
clientResp, err := p.client.Do(req)
51+
return &encdns.Response{U: clientResp}, err
52+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Package providers ...
2+
package providers
3+
4+
import (
5+
"context"
6+
"math/rand"
7+
"net/http"
8+
9+
"github.com/alphasoc/flightsim/simulator/encdns"
10+
)
11+
12+
// Provider represents a DoH provider. addr is used to dial, queryURL is the base for
13+
// DoH queries, and client is the HTTP client used in the actual queries.
14+
type Provider struct {
15+
addr string
16+
queryURL string
17+
client *http.Client
18+
}
19+
20+
// Providers supporting DoH.
21+
var providers = []encdns.ProviderType{
22+
encdns.GoogleProvider,
23+
encdns.CloudFlareProvider,
24+
encdns.Quad9Provider,
25+
encdns.OpenDNSProvider,
26+
}
27+
28+
// NewRandom returns a 'random' Queryable provider.
29+
func NewRandom(ctx context.Context) encdns.Queryable {
30+
pIdx := encdns.ProviderType(rand.Intn(len(providers)))
31+
var p encdns.Queryable
32+
switch providers[pIdx] {
33+
case encdns.GoogleProvider:
34+
p = NewGoogle(ctx)
35+
case encdns.CloudFlareProvider:
36+
p = NewCloudFlare(ctx)
37+
case encdns.Quad9Provider:
38+
p = NewQuad9(ctx)
39+
case encdns.OpenDNSProvider:
40+
p = NewOpenDNS(ctx)
41+
}
42+
return p
43+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package providers
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"net"
7+
"net/http"
8+
9+
"github.com/alphasoc/flightsim/simulator/encdns"
10+
)
11+
12+
type Quad9 struct {
13+
Provider
14+
}
15+
16+
// NewQuad9 returns a *Quad9 wrapping a Provider ready to use for DoH queries.
17+
func NewQuad9(ctx context.Context) *Quad9 {
18+
p := Quad9{
19+
Provider{
20+
addr: "dns.quad9.net:5053",
21+
queryURL: "https://dns.quad9.net:5053/dns-query",
22+
},
23+
}
24+
d := net.Dialer{}
25+
tr := &http.Transport{
26+
DialContext: func(ctxt context.Context, network, addr string) (net.Conn, error) {
27+
return d.DialContext(ctx, "tcp", p.addr)
28+
},
29+
}
30+
p.client = &http.Client{Transport: tr}
31+
return &p
32+
}
33+
34+
// QueryTXT performs a DoH TXT lookup on Quad9 and returns an *encdns.Response and
35+
// an error.
36+
func (p *Quad9) QueryTXT(ctx context.Context, domain string) (*encdns.Response, error) {
37+
reqStr := fmt.Sprintf("%v?name=%v&type=TXT", p.queryURL, domain)
38+
req, err := http.NewRequestWithContext(ctx, "GET", reqStr, nil)
39+
if err != nil {
40+
return nil, err
41+
}
42+
clientResp, err := p.client.Do(req)
43+
return &encdns.Response{U: clientResp}, err
44+
}

simulator/encdns/dot/dot.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// Package dot provides general DNS-over-TLS functionality.
2+
package dot
3+
4+
// IsValidResponse returns a boolean indicating if the []string carries a response.
5+
func IsValidResponse(r []string) bool {
6+
return len(r) > 0
7+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// Package providers ...
2+
package providers
3+
4+
import (
5+
"context"
6+
"crypto/tls"
7+
"math/rand"
8+
"net"
9+
10+
"github.com/alphasoc/flightsim/simulator/encdns"
11+
)
12+
13+
// Provider represents a DoT provider. addr and ctx are used to dial.
14+
type Provider struct {
15+
ctx context.Context
16+
addr string
17+
}
18+
19+
// Providers supporting DoT.
20+
var providers = []encdns.ProviderType{
21+
encdns.GoogleProvider,
22+
encdns.CloudFlareProvider,
23+
encdns.Quad9Provider,
24+
// OpenDNS does not, and does not plan to support DoT.
25+
}
26+
27+
// NewRandom returns a 'random' Queryable provider.
28+
func NewRandom(ctx context.Context) encdns.Queryable {
29+
pIdx := encdns.ProviderType(rand.Intn(len(providers)))
30+
var p encdns.Queryable
31+
switch providers[pIdx] {
32+
case encdns.GoogleProvider:
33+
p = NewGoogle(ctx)
34+
case encdns.CloudFlareProvider:
35+
p = NewCloudFlare(ctx)
36+
case encdns.Quad9Provider:
37+
p = NewQuad9(ctx)
38+
}
39+
return p
40+
}
41+
42+
// NewGoogle returns a *Provider for Google's DoT service.
43+
func NewGoogle(ctx context.Context) *Provider {
44+
return &Provider{ctx: ctx, addr: "dns.google:853"}
45+
}
46+
47+
// NewGoogle returns a *Provider tied for CloudFlare's DoT service.
48+
func NewCloudFlare(ctx context.Context) *Provider {
49+
return &Provider{ctx: ctx, addr: "1dot1dot1dot1.cloudflare-dns.com:853"}
50+
}
51+
52+
// NewGoogle returns a *Provider for Quad9's DoT service.
53+
func NewQuad9(ctx context.Context) *Provider {
54+
return &Provider{ctx: ctx, addr: "dns.quad9.net:853"}
55+
}
56+
57+
// QueryTXT performs a DoT TXT lookup using Provider p, and returns a *encdns.Response and
58+
// an error.
59+
func (p *Provider) QueryTXT(ctx context.Context, domain string) (*encdns.Response, error) {
60+
d := tls.Dialer{}
61+
r := &net.Resolver{
62+
PreferGo: true,
63+
Dial: func(ctx context.Context, network, addr string) (net.Conn, error) {
64+
return d.DialContext(p.ctx, "tcp", p.addr)
65+
},
66+
}
67+
resp, err := r.LookupTXT(ctx, domain)
68+
return &encdns.Response{U: resp}, err
69+
}

0 commit comments

Comments
 (0)