2021-02-27 06:26:33 +01:00
|
|
|
package app
|
2020-12-17 12:27:44 +01:00
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2022-05-17 06:51:14 +02:00
|
|
|
"math/rand"
|
2020-12-17 12:27:44 +01:00
|
|
|
"net"
|
|
|
|
"net/url"
|
2022-05-17 06:51:14 +02:00
|
|
|
"time"
|
2020-12-17 12:27:44 +01:00
|
|
|
|
2021-04-24 17:27:43 +02:00
|
|
|
"github.com/ameshkov/dnsstamps"
|
2021-01-20 11:02:15 +01:00
|
|
|
"github.com/mr-karan/doggo/pkg/config"
|
2021-02-26 15:36:46 +01:00
|
|
|
"github.com/mr-karan/doggo/pkg/models"
|
2020-12-17 17:38:23 +01:00
|
|
|
)
|
|
|
|
|
2021-02-27 06:26:33 +01:00
|
|
|
// LoadNameservers reads all the user given
|
|
|
|
// nameservers and loads to App.
|
|
|
|
func (app *App) LoadNameservers() error {
|
|
|
|
for _, srv := range app.QueryFlags.Nameservers {
|
2020-12-17 12:27:44 +01:00
|
|
|
ns, err := initNameserver(srv)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error parsing nameserver: %s", srv)
|
|
|
|
}
|
|
|
|
// check if properly initialised.
|
|
|
|
if ns.Address != "" && ns.Type != "" {
|
2021-02-27 06:26:33 +01:00
|
|
|
app.Nameservers = append(app.Nameservers, ns)
|
2020-12-17 12:27:44 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-15 05:41:08 +01:00
|
|
|
// Set `ndots` to the user specified value.
|
2021-02-27 06:26:33 +01:00
|
|
|
app.ResolverOpts.Ndots = app.QueryFlags.Ndots
|
2020-12-17 12:27:44 +01:00
|
|
|
// fallback to system nameserver
|
|
|
|
// in case no nameserver is specified by user.
|
2021-02-27 06:26:33 +01:00
|
|
|
if len(app.Nameservers) == 0 {
|
2022-05-17 06:51:14 +02:00
|
|
|
ns, ndots, search, err := getDefaultServers(app.QueryFlags.Strategy)
|
2020-12-17 12:27:44 +01:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error fetching system default nameserver")
|
|
|
|
}
|
2021-02-15 05:41:08 +01:00
|
|
|
// `-1` indicates the flag is not set.
|
|
|
|
// use from config if user hasn't specified any value.
|
2021-02-27 06:26:33 +01:00
|
|
|
if app.ResolverOpts.Ndots == -1 {
|
|
|
|
app.ResolverOpts.Ndots = ndots
|
2020-12-24 17:25:20 +01:00
|
|
|
}
|
2021-02-27 06:26:33 +01:00
|
|
|
if len(search) > 0 && app.QueryFlags.UseSearchList {
|
|
|
|
app.ResolverOpts.SearchList = search
|
2020-12-17 12:27:44 +01:00
|
|
|
}
|
2021-02-27 06:26:33 +01:00
|
|
|
app.Nameservers = append(app.Nameservers, ns...)
|
2020-12-17 12:27:44 +01:00
|
|
|
}
|
2021-02-15 05:41:08 +01:00
|
|
|
// if the user hasn't given any override of `ndots` AND has
|
|
|
|
// given a custom nameserver. Set `ndots` to 1 as the fallback value
|
2021-02-27 06:26:33 +01:00
|
|
|
if app.ResolverOpts.Ndots == -1 {
|
|
|
|
app.ResolverOpts.Ndots = 0
|
2021-02-15 05:41:08 +01:00
|
|
|
}
|
2020-12-17 12:27:44 +01:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-02-26 15:36:46 +01:00
|
|
|
func initNameserver(n string) (models.Nameserver, error) {
|
2020-12-17 17:38:23 +01:00
|
|
|
// Instantiate a UDP resolver with default port as a fallback.
|
2021-02-26 15:36:46 +01:00
|
|
|
ns := models.Nameserver{
|
|
|
|
Type: models.UDPResolver,
|
|
|
|
Address: net.JoinHostPort(n, models.DefaultUDPPort),
|
2020-12-17 12:27:44 +01:00
|
|
|
}
|
|
|
|
u, err := url.Parse(n)
|
|
|
|
if err != nil {
|
|
|
|
return ns, err
|
|
|
|
}
|
2021-04-25 12:43:03 +02:00
|
|
|
switch u.Scheme {
|
|
|
|
case "sdns":
|
2021-04-24 17:27:43 +02:00
|
|
|
stamp, err := dnsstamps.NewServerStampFromString(n)
|
|
|
|
if err != nil {
|
|
|
|
return ns, err
|
|
|
|
}
|
|
|
|
switch stamp.Proto {
|
|
|
|
case dnsstamps.StampProtoTypeDoH:
|
|
|
|
ns.Type = models.DOHResolver
|
|
|
|
address := url.URL{Scheme: "https", Host: stamp.ProviderName, Path: stamp.Path}
|
|
|
|
ns.Address = address.String()
|
|
|
|
case dnsstamps.StampProtoTypeDNSCrypt:
|
|
|
|
ns.Type = models.DNSCryptResolver
|
|
|
|
ns.Address = n
|
|
|
|
default:
|
|
|
|
return ns, fmt.Errorf("unsupported protocol: %v", stamp.Proto.String())
|
|
|
|
}
|
2021-04-25 12:43:03 +02:00
|
|
|
|
|
|
|
case "https":
|
2021-02-26 15:36:46 +01:00
|
|
|
ns.Type = models.DOHResolver
|
2020-12-17 12:27:44 +01:00
|
|
|
ns.Address = u.String()
|
2021-04-25 12:43:03 +02:00
|
|
|
|
|
|
|
case "tls":
|
2021-02-26 15:36:46 +01:00
|
|
|
ns.Type = models.DOTResolver
|
2020-12-17 12:27:44 +01:00
|
|
|
if u.Port() == "" {
|
2021-02-26 15:36:46 +01:00
|
|
|
ns.Address = net.JoinHostPort(u.Hostname(), models.DefaultTLSPort)
|
2020-12-17 12:27:44 +01:00
|
|
|
} else {
|
|
|
|
ns.Address = net.JoinHostPort(u.Hostname(), u.Port())
|
|
|
|
}
|
2021-04-25 12:43:03 +02:00
|
|
|
|
|
|
|
case "tcp":
|
2021-02-26 15:36:46 +01:00
|
|
|
ns.Type = models.TCPResolver
|
2020-12-17 12:27:44 +01:00
|
|
|
if u.Port() == "" {
|
2021-02-26 15:36:46 +01:00
|
|
|
ns.Address = net.JoinHostPort(u.Hostname(), models.DefaultTCPPort)
|
2020-12-17 12:27:44 +01:00
|
|
|
} else {
|
|
|
|
ns.Address = net.JoinHostPort(u.Hostname(), u.Port())
|
|
|
|
}
|
2021-04-25 12:43:03 +02:00
|
|
|
|
|
|
|
case "udp":
|
2021-02-26 15:36:46 +01:00
|
|
|
ns.Type = models.UDPResolver
|
2020-12-17 12:27:44 +01:00
|
|
|
if u.Port() == "" {
|
2021-02-26 15:36:46 +01:00
|
|
|
ns.Address = net.JoinHostPort(u.Hostname(), models.DefaultUDPPort)
|
2020-12-17 12:27:44 +01:00
|
|
|
} else {
|
|
|
|
ns.Address = net.JoinHostPort(u.Hostname(), u.Port())
|
|
|
|
}
|
2021-12-11 16:45:24 +01:00
|
|
|
case "quic":
|
|
|
|
ns.Type = models.DOQResolver
|
|
|
|
if u.Port() == "" {
|
|
|
|
ns.Address = net.JoinHostPort(u.Hostname(), models.DefaultDOQPort)
|
|
|
|
} else {
|
|
|
|
ns.Address = net.JoinHostPort(u.Hostname(), u.Port())
|
|
|
|
}
|
2020-12-17 12:27:44 +01:00
|
|
|
}
|
|
|
|
return ns, nil
|
|
|
|
}
|
2021-02-27 06:26:33 +01:00
|
|
|
|
2022-05-17 06:51:14 +02:00
|
|
|
func getDefaultServers(strategy string) ([]models.Nameserver, int, []string, error) {
|
|
|
|
// Load nameservers from `/etc/resolv.conf`.
|
2021-02-27 06:26:33 +01:00
|
|
|
dnsServers, ndots, search, err := config.GetDefaultServers()
|
|
|
|
if err != nil {
|
|
|
|
return nil, 0, nil, err
|
|
|
|
}
|
|
|
|
servers := make([]models.Nameserver, 0, len(dnsServers))
|
2022-05-17 06:51:14 +02:00
|
|
|
|
|
|
|
switch strategy {
|
|
|
|
case "random":
|
|
|
|
// Choose a random server from the list.
|
|
|
|
rand.Seed(time.Now().Unix())
|
|
|
|
srv := dnsServers[rand.Intn(len(dnsServers))]
|
2021-02-27 06:26:33 +01:00
|
|
|
ns := models.Nameserver{
|
|
|
|
Type: models.UDPResolver,
|
2022-05-17 06:51:14 +02:00
|
|
|
Address: net.JoinHostPort(srv, models.DefaultUDPPort),
|
2021-02-27 06:26:33 +01:00
|
|
|
}
|
|
|
|
servers = append(servers, ns)
|
2022-05-17 06:51:14 +02:00
|
|
|
|
|
|
|
case "first":
|
|
|
|
// Choose the first from the list, always.
|
|
|
|
srv := dnsServers[0]
|
|
|
|
ns := models.Nameserver{
|
|
|
|
Type: models.UDPResolver,
|
|
|
|
Address: net.JoinHostPort(srv, models.DefaultUDPPort),
|
|
|
|
}
|
|
|
|
servers = append(servers, ns)
|
|
|
|
|
|
|
|
default:
|
|
|
|
// Default behaviour is to load all nameservers.
|
|
|
|
for _, s := range dnsServers {
|
|
|
|
ns := models.Nameserver{
|
|
|
|
Type: models.UDPResolver,
|
|
|
|
Address: net.JoinHostPort(s, models.DefaultUDPPort),
|
|
|
|
}
|
|
|
|
servers = append(servers, ns)
|
|
|
|
}
|
2021-02-27 06:26:33 +01:00
|
|
|
}
|
2022-05-17 06:51:14 +02:00
|
|
|
|
2021-02-27 06:26:33 +01:00
|
|
|
return servers, ndots, search, nil
|
|
|
|
}
|