Files
openlan/pkg/proxy/name.go
2025-04-21 10:45:08 +08:00

167 lines
3.4 KiB
Go
Executable File

package proxy
import (
"fmt"
"runtime"
"sync"
"time"
"github.com/luscis/openlan/pkg/access"
"github.com/luscis/openlan/pkg/config"
"github.com/luscis/openlan/pkg/libol"
"github.com/luscis/openlan/pkg/network"
"github.com/miekg/dns"
)
type NameProxy struct {
listen string
cfg *config.NameProxy
server *dns.Server
out *libol.SubLogger
lock sync.RWMutex
names map[string]string
addrs map[string]string
access []*access.Access
}
func NewNameProxy(cfg *config.NameProxy) *NameProxy {
n := &NameProxy{
listen: cfg.Listen,
cfg: cfg,
out: libol.NewSubLogger(cfg.Listen),
names: make(map[string]string),
addrs: make(map[string]string),
}
n.Initialize()
return n
}
func (n *NameProxy) Initialize() {
for _, cfg := range n.cfg.Access {
acc := access.NewAccess(cfg)
acc.Initialize()
n.access = append(n.access, acc)
}
}
func (n *NameProxy) Forward(name, addr, nexthop string) {
opts := []string{}
if runtime.GOOS == "linux" {
opts = []string{"metric", fmt.Sprintf("%d", n.cfg.Metric)}
}
if out, err := network.RouteAdd("", addr, nexthop, opts...); err != nil {
n.out.Warn("Access.Forward: %s %s: %s", addr, err, out)
return
}
n.out.Info("NameProxy.Forward: %s <- %s via %s ", nexthop, name, addr)
}
func (n *NameProxy) UpdateDNS(name, addr string) bool {
n.lock.Lock()
defer n.lock.Unlock()
updated := false
if _, ok := n.names[name]; !ok {
n.names[name] = addr
updated = true
}
if _, ok := n.addrs[addr]; !ok {
n.addrs[addr] = name
updated = true
}
return updated
}
func (n *NameProxy) FindBackend(r *dns.Msg) *config.ForwardTo {
if len(r.Question) == 0 {
return nil
}
name := r.Question[0].Name
n.out.Debug("NameProxy.FindBackend %s", name)
n.lock.RLock()
defer n.lock.RUnlock()
via := n.cfg.Backends.FindBackend(name)
if via != nil {
n.out.Debug("NameProxy.FindBackend %s via %s", name, via.Server)
}
return via
}
func (n *NameProxy) handleDNS(conn dns.ResponseWriter, r *dns.Msg) {
client := &dns.Client{
ReadTimeout: 3 * time.Second,
WriteTimeout: 3 * time.Second,
Net: "udp",
}
nameto := n.cfg.Nameto
libol.Go(func() {
via := n.FindBackend(r)
if via != nil {
nameto = via.Nameto
}
config.SetListen(&nameto, 53)
if nameto == "0.0.0.0:53" || nameto == n.listen {
n.out.Error("NameProxy.handleDNS nil(%s)", nameto)
return
}
n.out.Info("NameProxy.handleDNS %s <- %v via %s", nameto, r.Question, conn.RemoteAddr())
resp, _, err := client.Exchange(r, nameto)
if err != nil {
n.out.Error("NameProxy.handleDNS %s: %v", r, err)
return
}
if via != nil && via.Server != "" {
for _, rr := range resp.Answer {
if aa, ok := rr.(*dns.A); ok {
name := aa.Hdr.Name
addr := aa.A.String()
if n.UpdateDNS(name, addr) {
n.Forward(name, addr, via.Server)
}
}
}
}
if err := conn.WriteMsg(resp); err != nil {
n.out.Error("NameProxy.handleDNS %s", err)
}
})
}
func (n *NameProxy) Start() {
dns.HandleFunc(".", n.handleDNS)
n.server = &dns.Server{Addr: n.listen, Net: "udp"}
n.out.Info("NameProxy.StartDNS on %s", n.listen)
for _, acc := range n.access {
libol.Go(acc.Start)
}
if err := n.server.ListenAndServe(); err != nil {
n.out.Error("NameProxy.StartDNS server: %v", err)
}
}
func (n *NameProxy) Stop() {
for _, acc := range n.access {
acc.Stop()
}
n.access = nil
if n.server != nil {
n.server.Shutdown()
n.server = nil
}
n.out.Info("NameProxy.Stop")
}
func (n *NameProxy) Save() {
}