mirror of
https://github.com/Control-D-Inc/ctrld.git
synced 2026-02-03 22:18:39 +00:00
As part of v1.4.0 release, reading DNS from /etc/resolv.conf file is only available for Macos. However, there's no reason to prevent this function from working on other *nix systems. This commit unify the function to *nix, so it could be added as DNS source for Linux and Freebsd.
131 lines
2.4 KiB
Go
131 lines
2.4 KiB
Go
package ctrld
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"encoding/hex"
|
|
"net"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/Control-D-Inc/ctrld/internal/dns/resolvconffile"
|
|
)
|
|
|
|
const (
|
|
v4RouteFile = "/proc/net/route"
|
|
v6RouteFile = "/proc/net/ipv6_route"
|
|
)
|
|
|
|
func dnsFns() []dnsFn {
|
|
return []dnsFn{dnsFromResolvConf, dns4, dns6, dnsFromSystemdResolver}
|
|
}
|
|
|
|
func dns4() []string {
|
|
f, err := os.Open(v4RouteFile)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
defer f.Close()
|
|
|
|
var dns []string
|
|
seen := make(map[string]bool)
|
|
vis := virtualInterfaces()
|
|
s := bufio.NewScanner(f)
|
|
first := true
|
|
for s.Scan() {
|
|
if first {
|
|
first = false
|
|
continue
|
|
}
|
|
fields := bytes.Fields(s.Bytes())
|
|
if len(fields) < 2 {
|
|
continue
|
|
}
|
|
// Skip virtual interfaces.
|
|
if vis.contains(string(bytes.TrimSpace(fields[0]))) {
|
|
continue
|
|
}
|
|
gw := make([]byte, net.IPv4len)
|
|
// Third fields is gateway.
|
|
if _, err := hex.Decode(gw, fields[2]); err != nil {
|
|
continue
|
|
}
|
|
ip := net.IPv4(gw[3], gw[2], gw[1], gw[0])
|
|
if ip.Equal(net.IPv4zero) || seen[ip.String()] {
|
|
continue
|
|
}
|
|
seen[ip.String()] = true
|
|
dns = append(dns, ip.String())
|
|
}
|
|
return dns
|
|
}
|
|
|
|
func dns6() []string {
|
|
f, err := os.Open(v6RouteFile)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
defer f.Close()
|
|
|
|
var dns []string
|
|
vis := virtualInterfaces()
|
|
s := bufio.NewScanner(f)
|
|
for s.Scan() {
|
|
fields := bytes.Fields(s.Bytes())
|
|
if len(fields) < 4 {
|
|
continue
|
|
}
|
|
// Skip virtual interfaces.
|
|
if vis.contains(string(bytes.TrimSpace(fields[len(fields)-1]))) {
|
|
continue
|
|
}
|
|
|
|
gw := make([]byte, net.IPv6len)
|
|
// Fifth fields is gateway.
|
|
if _, err := hex.Decode(gw, fields[4]); err != nil {
|
|
continue
|
|
}
|
|
ip := net.IP(gw)
|
|
if ip.Equal(net.IPv6zero) {
|
|
continue
|
|
}
|
|
dns = append(dns, ip.String())
|
|
}
|
|
return dns
|
|
}
|
|
|
|
func dnsFromSystemdResolver() []string {
|
|
c, err := resolvconffile.ParseFile("/run/systemd/resolve/resolv.conf")
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
ns := make([]string, 0, len(c.Nameservers))
|
|
for _, nameserver := range c.Nameservers {
|
|
ns = append(ns, nameserver.String())
|
|
}
|
|
return ns
|
|
}
|
|
|
|
type set map[string]struct{}
|
|
|
|
func (s *set) add(e string) {
|
|
(*s)[e] = struct{}{}
|
|
}
|
|
|
|
func (s *set) contains(e string) bool {
|
|
_, ok := (*s)[e]
|
|
return ok
|
|
}
|
|
|
|
// virtualInterfaces returns a set of virtual interfaces on current machine.
|
|
func virtualInterfaces() set {
|
|
s := make(set)
|
|
entries, _ := os.ReadDir("/sys/devices/virtual/net")
|
|
for _, entry := range entries {
|
|
if entry.IsDir() {
|
|
s.add(strings.TrimSpace(entry.Name()))
|
|
}
|
|
}
|
|
return s
|
|
}
|