Create a package
This commit is contained in:
parent
1950e95d0f
commit
2d9042a983
17
README.md
17
README.md
@ -10,16 +10,15 @@ make
|
|||||||
```
|
```
|
||||||
2) Generate the default config for your host
|
2) Generate the default config for your host
|
||||||
```
|
```
|
||||||
./meshnamed genconf 200:6fc8:9220:f400:5cc2:305a:4ac6:967e | tee /tmp/meshnamed.conf
|
./meshnamed -genconf 200:6fc8:9220:f400:5cc2:305a:4ac6:967e | tee /tmp/meshnamed.conf
|
||||||
```
|
```
|
||||||
3) Optionally, set the configuration with environment variables
|
3) Run the daemon
|
||||||
```
|
```
|
||||||
export LISTEN_ADDR=[::1]:53535
|
./meshnamed -useconffile /tmp/meshnamed.conf
|
||||||
export MESH_SUBNET=200::/7
|
|
||||||
```
|
```
|
||||||
4) Run the daemon
|
4) Optionally, set the configuration flags
|
||||||
```
|
```
|
||||||
./meshnamed daemon /tmp/meshnamed.conf
|
./meshnamed -listenaddr [::1]:53535 -meshsubnet 200::/7 -debug -useconffile /tmp/meshnamed.conf
|
||||||
```
|
```
|
||||||
Add new DNS records to configuration file and restart the daemon to apply settings.
|
Add new DNS records to configuration file and restart the daemon to apply settings.
|
||||||
A record can be of any valid string form parsed by [miekg/dns](https://godoc.org/github.com/miekg/dns#NewRR).
|
A record can be of any valid string form parsed by [miekg/dns](https://godoc.org/github.com/miekg/dns#NewRR).
|
||||||
@ -61,9 +60,9 @@ In this example, meshnamed is configured as authoritative for two domain zones:
|
|||||||
|
|
||||||
## Using meshnamed as a standalone DNS server
|
## Using meshnamed as a standalone DNS server
|
||||||
|
|
||||||
Set environment varialbe to listen on all interfaces and a standard DNS server port
|
Set the flag to listen on all interfaces and a standard DNS server port
|
||||||
|
|
||||||
export LISTEN_ADDR=[::]:53
|
./meshnamed -listenaddr [::]:53 -useconffile /tmp/meshnamed.conf
|
||||||
|
|
||||||
Allow incoming connections to port 53/UDP in firewall settings.
|
Run as root and allow incoming connections to port 53/UDP in firewall settings.
|
||||||
|
|
||||||
|
@ -1,47 +1,17 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/base32"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
"os"
|
"os"
|
||||||
"errors"
|
|
||||||
|
"github.com/zhoreeq/meshname/src/meshname"
|
||||||
)
|
)
|
||||||
|
|
||||||
var domainZone = ".meshname"
|
|
||||||
|
|
||||||
func reverse_lookup(target string) (string, error) {
|
|
||||||
ip := net.ParseIP(target)
|
|
||||||
if ip == nil {
|
|
||||||
return "", errors.New("Invalid IP address")
|
|
||||||
}
|
|
||||||
str := base32.StdEncoding.EncodeToString(ip)[0:26]
|
|
||||||
return strings.ToLower(str) + domainZone, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func lookup(target string) (string, error) {
|
|
||||||
labels := strings.Split(target, ".")
|
|
||||||
if len(labels) < 2 || strings.HasSuffix(domainZone, target) {
|
|
||||||
return "", errors.New("Invalid domain")
|
|
||||||
}
|
|
||||||
subDomain := labels[len(labels) - 2]
|
|
||||||
if len(subDomain) != 26 {
|
|
||||||
return "", errors.New("Invalid subdomain length")
|
|
||||||
}
|
|
||||||
name := strings.ToUpper(subDomain) + "======"
|
|
||||||
data, err := base32.StdEncoding.DecodeString(name)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
s := net.IP(data)
|
|
||||||
if s == nil {
|
|
||||||
return "", errors.New("Invalid IP address")
|
|
||||||
}
|
|
||||||
return s.String(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
domainZone := strings.TrimSuffix(meshname.DomainZone, ".")
|
||||||
|
|
||||||
usage := "Usage:\n\nmeshname lookup DOMAIN\nmeshname reverse_lookup IP"
|
usage := "Usage:\n\nmeshname lookup DOMAIN\nmeshname reverse_lookup IP"
|
||||||
if len(os.Args) != 3 {
|
if len(os.Args) != 3 {
|
||||||
fmt.Println(usage)
|
fmt.Println(usage)
|
||||||
@ -53,20 +23,32 @@ func main() {
|
|||||||
|
|
||||||
switch action {
|
switch action {
|
||||||
case "lookup":
|
case "lookup":
|
||||||
result, err := lookup(target)
|
labels := strings.Split(target, ".")
|
||||||
|
if len(labels) < 2 || !strings.HasSuffix(target, domainZone) {
|
||||||
|
fmt.Println("Invalid domain")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
subDomain := labels[len(labels) - 2]
|
||||||
|
if len(subDomain) != 26 {
|
||||||
|
fmt.Println("Invalid subdomain length")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := meshname.IPFromDomain(subDomain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Error:", err)
|
fmt.Println("Error:", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fmt.Println(result)
|
fmt.Println(result.String())
|
||||||
return
|
return
|
||||||
case "reverse_lookup":
|
case "reverse_lookup":
|
||||||
result, err := reverse_lookup(target)
|
ip := net.ParseIP(target)
|
||||||
if err != nil {
|
if ip == nil {
|
||||||
fmt.Println("Error:", err)
|
fmt.Println("Invalid IP address")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fmt.Println(result)
|
result := meshname.DomainFromIP(ip)
|
||||||
|
fmt.Println(result + "." + domainZone)
|
||||||
return
|
return
|
||||||
default:
|
default:
|
||||||
fmt.Println(usage)
|
fmt.Println(usage)
|
||||||
|
@ -1,175 +1,55 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/base32"
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"fmt"
|
||||||
|
"flag"
|
||||||
|
|
||||||
"github.com/miekg/dns"
|
"github.com/gologme/log"
|
||||||
|
|
||||||
|
"github.com/zhoreeq/meshname/src/meshname"
|
||||||
)
|
)
|
||||||
|
|
||||||
const domainZone = "meshname."
|
|
||||||
|
|
||||||
var _, validSubnet, _ = net.ParseCIDR("::/0")
|
|
||||||
var zoneConfigPath = ""
|
|
||||||
var zoneConfig = map[string][]dns.RR{}
|
|
||||||
var dnsClient = new(dns.Client)
|
|
||||||
|
|
||||||
func loadConfig() {
|
|
||||||
if zoneConfigPath == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
reader, err := os.Open(zoneConfigPath)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("Can't open config:", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
type Zone struct {
|
|
||||||
Domain string
|
|
||||||
Records []string
|
|
||||||
}
|
|
||||||
|
|
||||||
dec := json.NewDecoder(reader)
|
|
||||||
for {
|
|
||||||
var m Zone
|
|
||||||
if err := dec.Decode(&m); err == io.EOF {
|
|
||||||
break
|
|
||||||
} else if err != nil {
|
|
||||||
fmt.Println("Syntax error in config:", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, v := range m.Records {
|
|
||||||
rr, err := dns.NewRR(v)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("Invalid DNS record:", v)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
zoneConfig[m.Domain] = append(zoneConfig[m.Domain], rr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fmt.Println("Config loaded:", zoneConfigPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
func lookup(domain string) (net.IP, error) {
|
|
||||||
name := strings.ToUpper(domain) + "======"
|
|
||||||
data, err := base32.StdEncoding.DecodeString(name)
|
|
||||||
if err != nil {
|
|
||||||
return net.IP{}, err
|
|
||||||
}
|
|
||||||
if len(data) != 16 {
|
|
||||||
return net.IP{}, errors.New("Invalid subdomain")
|
|
||||||
}
|
|
||||||
ipAddr := net.IP(data)
|
|
||||||
if ipAddr == nil {
|
|
||||||
return net.IP{}, errors.New("Invalid IP address")
|
|
||||||
}
|
|
||||||
if !validSubnet.Contains(ipAddr) {
|
|
||||||
return net.IP{}, errors.New("Address from invalid subnet")
|
|
||||||
}
|
|
||||||
return ipAddr, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func genConf(target string) (string, error) {
|
|
||||||
ip := net.ParseIP(target)
|
|
||||||
if ip == nil {
|
|
||||||
return "", errors.New("Invalid IP address")
|
|
||||||
}
|
|
||||||
zone := strings.ToLower(base32.StdEncoding.EncodeToString(ip)[0:26])
|
|
||||||
selfRecord := fmt.Sprintf("\t\t\"%s.%s AAAA %s\"\n", zone, domainZone, target)
|
|
||||||
confString := fmt.Sprintf("{\n\t\"Domain\":\"%s\",\n\t\"Records\":[\n%s\t]\n}", zone, selfRecord)
|
|
||||||
|
|
||||||
return confString, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleRequest(w dns.ResponseWriter, r *dns.Msg) {
|
|
||||||
var remoteLookups = map[string][]dns.Question{}
|
|
||||||
m := new(dns.Msg)
|
|
||||||
m.SetReply(r)
|
|
||||||
|
|
||||||
for _, q := range r.Question {
|
|
||||||
labels := dns.SplitDomainName(q.Name)
|
|
||||||
if len(labels) < 2 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
subDomain := labels[len(labels)-2]
|
|
||||||
|
|
||||||
resolvedAddr, err := lookup(subDomain)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if records, ok := zoneConfig[subDomain]; ok {
|
|
||||||
for _, rec := range records {
|
|
||||||
if h := rec.Header(); h.Name == q.Name && h.Rrtype == q.Qtype && h.Class == q.Qclass {
|
|
||||||
m.Answer = append(m.Answer, rec)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if ra := w.RemoteAddr().String(); strings.HasPrefix(ra, "[::1]:") || strings.HasPrefix(ra, "127.0.0.1:") {
|
|
||||||
// do remote lookups only for local clients
|
|
||||||
remoteLookups[resolvedAddr.String()] = append(remoteLookups[resolvedAddr.String()], q)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for remoteServer, questions := range remoteLookups {
|
|
||||||
rm := new(dns.Msg)
|
|
||||||
rm.Question = questions
|
|
||||||
resp, _, err := dnsClient.Exchange(rm, "["+remoteServer+"]:53") // no retries
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
m.Answer = append(m.Answer, resp.Answer...)
|
|
||||||
}
|
|
||||||
w.WriteMsg(m)
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
helpMessage := "Usage:\nmeshnamed genconf [IP] > /etc/meshnamed.conf\nmeshnamed daemon /etc/meshnamed.conf"
|
genconf := flag.String("genconf", "", "generate a new config for IP address")
|
||||||
if len(os.Args) < 2 {
|
useconffile := flag.String("useconffile", "", "run daemon with a config file")
|
||||||
fmt.Println(helpMessage)
|
listenAddr := flag.String("listenaddr", "[::1]:53535", "address to listen on")
|
||||||
return
|
meshSubnetStr := flag.String("meshsubnet", "::/0", "valid IPv6 address space")
|
||||||
|
debug := flag.Bool("debug", false, "enable debug logging")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
var logger *log.Logger
|
||||||
|
logger = log.New(os.Stdout, "", log.Flags())
|
||||||
|
|
||||||
|
logger.EnableLevel("error")
|
||||||
|
logger.EnableLevel("warn")
|
||||||
|
logger.EnableLevel("info")
|
||||||
|
if *debug {
|
||||||
|
logger.EnableLevel("debug")
|
||||||
}
|
}
|
||||||
|
|
||||||
action := os.Args[1]
|
switch {
|
||||||
if action == "genconf" && len(os.Args) == 3 {
|
case *genconf != "":
|
||||||
confString, err := genConf(os.Args[2])
|
confString, err := meshname.GenConf(*genconf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
logger.Errorln(err)
|
||||||
} else {
|
} else {
|
||||||
fmt.Println(confString)
|
fmt.Println(confString)
|
||||||
}
|
}
|
||||||
} else if action == "daemon" {
|
case *useconffile != "":
|
||||||
if len(os.Args) == 3 {
|
s := new(meshname.MeshnameServer)
|
||||||
zoneConfigPath = os.Args[2]
|
|
||||||
loadConfig()
|
_, validSubnet, err := net.ParseCIDR(*meshSubnetStr)
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorln(err)
|
||||||
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
addr := "[::1]:53535"
|
s.Init(logger, meshname.MeshnameOptions{ListenAddr: *listenAddr, ConfigPath: *useconffile, ValidSubnet: validSubnet})
|
||||||
if os.Getenv("LISTEN_ADDR") != "" {
|
s.Start()
|
||||||
addr = os.Getenv("LISTEN_ADDR")
|
default:
|
||||||
}
|
flag.PrintDefaults()
|
||||||
|
|
||||||
if os.Getenv("MESH_SUBNET") != "" {
|
|
||||||
_, meshSubnet, err := net.ParseCIDR(os.Getenv("MESH_SUBNET"))
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
validSubnet = meshSubnet
|
|
||||||
}
|
|
||||||
|
|
||||||
dnsClient.Timeout = 5000000000 // increased 5 seconds timeout
|
|
||||||
|
|
||||||
dnsServer := &dns.Server{Addr: addr, Net: "udp"}
|
|
||||||
fmt.Println("Started meshnamed on:", addr)
|
|
||||||
dns.HandleFunc(domainZone, handleRequest)
|
|
||||||
dnsServer.ListenAndServe()
|
|
||||||
} else {
|
|
||||||
fmt.Println(helpMessage)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
5
go.mod
5
go.mod
@ -2,4 +2,7 @@ module github.com/zhoreeq/meshname
|
|||||||
|
|
||||||
go 1.13
|
go 1.13
|
||||||
|
|
||||||
require github.com/miekg/dns v1.1.27
|
require (
|
||||||
|
github.com/gologme/log v1.2.0
|
||||||
|
github.com/miekg/dns v1.1.27
|
||||||
|
)
|
||||||
|
2
go.sum
2
go.sum
@ -1,3 +1,5 @@
|
|||||||
|
github.com/gologme/log v1.2.0 h1:Ya5Ip/KD6FX7uH0S31QO87nCCSucKtF44TLbTtO7V4c=
|
||||||
|
github.com/gologme/log v1.2.0/go.mod h1:gq31gQ8wEHkR+WekdWsqDuf8pXTUZA9BnnzTuPz1Y9U=
|
||||||
github.com/miekg/dns v1.1.27 h1:aEH/kqUzUxGJ/UHcEKdJY+ugH6WEzsEBBSPa8zuy1aM=
|
github.com/miekg/dns v1.1.27 h1:aEH/kqUzUxGJ/UHcEKdJY+ugH6WEzsEBBSPa8zuy1aM=
|
||||||
github.com/miekg/dns v1.1.27/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
github.com/miekg/dns v1.1.27/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
@ -9,9 +9,7 @@ Group=nogroup
|
|||||||
ProtectHome=true
|
ProtectHome=true
|
||||||
ProtectSystem=true
|
ProtectSystem=true
|
||||||
SyslogIdentifier=meshnamed
|
SyslogIdentifier=meshnamed
|
||||||
Environment="LISTEN_ADDR=[::1]:53535"
|
ExecStart=/usr/local/bin/meshnamed -listenaddr [::1]:53535 -meshsubnet 200::/7 -useconffile /etc/meshnamed.conf
|
||||||
Environment="MESH_SUBNET=::/0"
|
|
||||||
ExecStart=/usr/local/bin/meshnamed daemon /etc/meshnamed.conf
|
|
||||||
Restart=always
|
Restart=always
|
||||||
TimeoutStopSec=5
|
TimeoutStopSec=5
|
||||||
|
|
||||||
|
172
src/meshname/server.go
Normal file
172
src/meshname/server.go
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
package meshname
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base32"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gologme/log"
|
||||||
|
"github.com/miekg/dns"
|
||||||
|
)
|
||||||
|
|
||||||
|
const DomainZone = "meshname."
|
||||||
|
|
||||||
|
func DomainFromIP(target net.IP) string {
|
||||||
|
return strings.ToLower(base32.StdEncoding.EncodeToString(target)[0:26])
|
||||||
|
}
|
||||||
|
|
||||||
|
func IPFromDomain(domain string) (net.IP, error) {
|
||||||
|
name := strings.ToUpper(domain) + "======"
|
||||||
|
data, err := base32.StdEncoding.DecodeString(name)
|
||||||
|
if err != nil {
|
||||||
|
return net.IP{}, err
|
||||||
|
}
|
||||||
|
if len(data) != 16 {
|
||||||
|
return net.IP{}, errors.New("Invalid subdomain")
|
||||||
|
}
|
||||||
|
ipAddr := net.IP(data)
|
||||||
|
if ipAddr == nil {
|
||||||
|
return net.IP{}, errors.New("Invalid IP address")
|
||||||
|
}
|
||||||
|
return ipAddr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GenConf(target string) (string, error) {
|
||||||
|
ip := net.ParseIP(target)
|
||||||
|
if ip == nil {
|
||||||
|
return "", errors.New("Invalid IP address")
|
||||||
|
}
|
||||||
|
zone := DomainFromIP(ip)
|
||||||
|
selfRecord := fmt.Sprintf("\t\t\"%s.%s AAAA %s\"\n", zone, DomainZone, target)
|
||||||
|
confString := fmt.Sprintf("{\n\t\"Domain\":\"%s\",\n\t\"Records\":[\n%s\t]\n}", zone, selfRecord)
|
||||||
|
|
||||||
|
return confString, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type MeshnameServer struct {
|
||||||
|
validSubnet *net.IPNet
|
||||||
|
log *log.Logger
|
||||||
|
listenAddr, zoneConfigPath string
|
||||||
|
zoneConfig map[string][]dns.RR
|
||||||
|
dnsClient *dns.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
type MeshnameOptions struct {
|
||||||
|
ListenAddr, ConfigPath string
|
||||||
|
ValidSubnet *net.IPNet
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MeshnameServer) Init(log *log.Logger, options interface{}) {
|
||||||
|
mnoptions := options.(MeshnameOptions)
|
||||||
|
s.log = log
|
||||||
|
s.listenAddr = mnoptions.ListenAddr
|
||||||
|
s.validSubnet = mnoptions.ValidSubnet
|
||||||
|
s.zoneConfigPath = mnoptions.ConfigPath
|
||||||
|
s.zoneConfig = make(map[string][]dns.RR)
|
||||||
|
if s.dnsClient == nil {
|
||||||
|
s.dnsClient = new(dns.Client)
|
||||||
|
s.dnsClient.Timeout = 5000000000 // increased 5 seconds timeout
|
||||||
|
}
|
||||||
|
s.LoadConfig()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MeshnameServer) LoadConfig() {
|
||||||
|
if s.zoneConfigPath == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for k := range s.zoneConfig {
|
||||||
|
delete(s.zoneConfig, k)
|
||||||
|
}
|
||||||
|
|
||||||
|
reader, err := os.Open(s.zoneConfigPath)
|
||||||
|
if err != nil {
|
||||||
|
s.log.Errorln("Can't open config:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type Zone struct {
|
||||||
|
Domain string
|
||||||
|
Records []string
|
||||||
|
}
|
||||||
|
|
||||||
|
dec := json.NewDecoder(reader)
|
||||||
|
for {
|
||||||
|
var m Zone
|
||||||
|
if err := dec.Decode(&m); err == io.EOF {
|
||||||
|
break
|
||||||
|
} else if err != nil {
|
||||||
|
s.log.Errorln("Syntax error in config:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, v := range m.Records {
|
||||||
|
rr, err := dns.NewRR(v)
|
||||||
|
if err != nil {
|
||||||
|
s.log.Errorln("Invalid DNS record:", v)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
s.zoneConfig[m.Domain] = append(s.zoneConfig[m.Domain], rr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.log.Infoln("Meshname config loaded:", s.zoneConfigPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MeshnameServer) Start() {
|
||||||
|
dnsServer := &dns.Server{Addr: s.listenAddr, Net: "udp"}
|
||||||
|
s.log.Infoln("Started meshnamed on:", s.listenAddr)
|
||||||
|
dns.HandleFunc(DomainZone, s.handleRequest)
|
||||||
|
dnsServer.ListenAndServe()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MeshnameServer) handleRequest(w dns.ResponseWriter, r *dns.Msg) {
|
||||||
|
var remoteLookups = make(map[string][]dns.Question)
|
||||||
|
m := new(dns.Msg)
|
||||||
|
m.SetReply(r)
|
||||||
|
|
||||||
|
for _, q := range r.Question {
|
||||||
|
labels := dns.SplitDomainName(q.Name)
|
||||||
|
if len(labels) < 2 {
|
||||||
|
s.log.Debugln("Error: invalid domain requested")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
subDomain := labels[len(labels)-2]
|
||||||
|
|
||||||
|
resolvedAddr, err := IPFromDomain(subDomain)
|
||||||
|
if err != nil {
|
||||||
|
s.log.Debugln(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !s.validSubnet.Contains(resolvedAddr) {
|
||||||
|
s.log.Debugln("Error: subnet doesn't match")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if records, ok := s.zoneConfig[subDomain]; ok {
|
||||||
|
for _, rec := range records {
|
||||||
|
if h := rec.Header(); h.Name == q.Name && h.Rrtype == q.Qtype && h.Class == q.Qclass {
|
||||||
|
m.Answer = append(m.Answer, rec)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if ra := w.RemoteAddr().String(); strings.HasPrefix(ra, "[::1]:") || strings.HasPrefix(ra, "127.0.0.1:") {
|
||||||
|
// TODO prefix whitelists ?
|
||||||
|
// do remote lookups only for local clients
|
||||||
|
remoteLookups[resolvedAddr.String()] = append(remoteLookups[resolvedAddr.String()], q)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for remoteServer, questions := range remoteLookups {
|
||||||
|
rm := new(dns.Msg)
|
||||||
|
rm.Question = questions
|
||||||
|
resp, _, err := s.dnsClient.Exchange(rm, "["+remoteServer+"]:53") // no retries
|
||||||
|
if err != nil {
|
||||||
|
s.log.Debugln(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
m.Answer = append(m.Answer, resp.Answer...)
|
||||||
|
}
|
||||||
|
w.WriteMsg(m)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user