mirror of
https://github.com/luscis/openlan.git
synced 2025-10-04 16:22:51 +08:00
167 lines
3.4 KiB
Go
Executable File
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() {
|
|
}
|