2025-02-13 21:08:11 +03:00
|
|
|
package kvas2
|
2024-08-26 19:10:40 +03:00
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2024-08-27 03:07:58 +03:00
|
|
|
"errors"
|
2024-08-26 19:10:40 +03:00
|
|
|
"fmt"
|
2024-08-30 04:44:08 +03:00
|
|
|
"net"
|
2024-10-21 00:03:51 +03:00
|
|
|
"os"
|
2024-09-06 16:50:56 +03:00
|
|
|
"strings"
|
2024-08-27 03:07:58 +03:00
|
|
|
"time"
|
|
|
|
|
2025-02-13 21:08:11 +03:00
|
|
|
"kvas2/dns-mitm-proxy"
|
|
|
|
"kvas2/group"
|
|
|
|
"kvas2/models"
|
|
|
|
"kvas2/netfilter-helper"
|
|
|
|
"kvas2/records"
|
2024-09-04 09:15:03 +03:00
|
|
|
|
2025-02-08 06:23:36 +03:00
|
|
|
"github.com/miekg/dns"
|
2024-09-04 09:15:03 +03:00
|
|
|
"github.com/rs/zerolog/log"
|
2024-09-06 15:48:15 +03:00
|
|
|
"github.com/vishvananda/netlink"
|
2024-10-22 23:23:07 +03:00
|
|
|
"github.com/vishvananda/netlink/nl"
|
2024-08-27 03:07:58 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
2025-02-14 03:17:43 +03:00
|
|
|
ErrAlreadyRunning = errors.New("already running")
|
|
|
|
ErrGroupIDConflict = errors.New("group id conflict")
|
|
|
|
ErrRuleIDConflict = errors.New("rule id conflict")
|
|
|
|
ErrConfigUnsupportedVersion = errors.New("config unsupported version")
|
2024-08-26 19:10:40 +03:00
|
|
|
)
|
|
|
|
|
2025-02-14 03:17:43 +03:00
|
|
|
var DefaultAppConfig = models.App{
|
|
|
|
DNSProxy: models.DNSProxy{
|
|
|
|
Host: models.DNSProxyServer{Address: "[::]", Port: 3553},
|
|
|
|
Upstream: models.DNSProxyServer{Address: "127.0.0.1", Port: 53},
|
|
|
|
DisableRemap53: false,
|
|
|
|
DisableFakePTR: false,
|
|
|
|
DisableDropAAAA: false,
|
|
|
|
},
|
|
|
|
Netfilter: models.Netfilter{
|
|
|
|
IPTables: models.IPTables{
|
|
|
|
ChainPrefix: "KVAS2_",
|
|
|
|
},
|
|
|
|
IPSet: models.IPSet{
|
|
|
|
TablePrefix: "kvas2_",
|
|
|
|
AdditionalTTL: 3600,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Link: []string{"br0"},
|
|
|
|
LogLevel: "info",
|
2024-08-26 19:10:40 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
type App struct {
|
2025-02-14 03:17:43 +03:00
|
|
|
config models.App
|
|
|
|
unprocessedGroups []models.Group
|
2024-08-30 04:29:05 +03:00
|
|
|
|
2025-02-14 03:17:43 +03:00
|
|
|
dnsMITM *dnsMitmProxy.DNSMITMProxy
|
|
|
|
nfHelper4 *netfilterHelper.NetfilterHelper
|
|
|
|
nfHelper6 *netfilterHelper.NetfilterHelper
|
|
|
|
records *records.Records
|
|
|
|
groups []*group.Group
|
2024-10-22 23:23:07 +03:00
|
|
|
|
2024-09-14 15:16:50 +03:00
|
|
|
isRunning bool
|
|
|
|
dnsOverrider4 *netfilterHelper.PortRemap
|
2024-10-22 01:52:31 +03:00
|
|
|
dnsOverrider6 *netfilterHelper.PortRemap
|
2024-08-26 19:10:40 +03:00
|
|
|
}
|
|
|
|
|
2024-10-21 00:03:51 +03:00
|
|
|
func (a *App) handleLink(event netlink.LinkUpdate) {
|
|
|
|
switch event.Change {
|
2025-02-11 02:50:13 +03:00
|
|
|
case 0x00000001:
|
2025-02-13 21:08:11 +03:00
|
|
|
log.Trace().
|
|
|
|
Str("interface", event.Link.Attrs().Name).
|
|
|
|
Int("change", int(event.Change)).
|
|
|
|
Msg("interface event")
|
2025-02-12 04:11:28 +03:00
|
|
|
ifaceName := event.Link.Attrs().Name
|
2025-02-14 03:17:43 +03:00
|
|
|
for _, group := range a.groups {
|
2025-02-12 04:11:28 +03:00
|
|
|
if group.Interface != ifaceName {
|
|
|
|
continue
|
|
|
|
}
|
2025-02-08 06:23:36 +03:00
|
|
|
|
2025-02-12 04:11:28 +03:00
|
|
|
err := group.LinkUpdateHook(event)
|
|
|
|
if err != nil {
|
|
|
|
log.Error().Str("group", group.ID.String()).Err(err).Msg("error while handling interface up")
|
2024-10-21 00:03:51 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
case 0xFFFFFFFF:
|
|
|
|
switch event.Header.Type {
|
|
|
|
case 16:
|
|
|
|
log.Debug().
|
|
|
|
Str("interface", event.Link.Attrs().Name).
|
|
|
|
Int("type", int(event.Header.Type)).
|
|
|
|
Msg("interface add")
|
|
|
|
case 17:
|
|
|
|
log.Debug().
|
|
|
|
Str("interface", event.Link.Attrs().Name).
|
|
|
|
Int("type", int(event.Header.Type)).
|
|
|
|
Msg("interface del")
|
|
|
|
}
|
2024-08-30 04:29:05 +03:00
|
|
|
}
|
2024-10-21 00:03:51 +03:00
|
|
|
}
|
2024-08-26 19:10:40 +03:00
|
|
|
|
2025-02-08 06:23:36 +03:00
|
|
|
func (a *App) start(ctx context.Context) (err error) {
|
2025-02-14 03:17:43 +03:00
|
|
|
a.dnsMITM = &dnsMitmProxy.DNSMITMProxy{
|
|
|
|
UpstreamDNSAddress: a.config.DNSProxy.Upstream.Address,
|
|
|
|
UpstreamDNSPort: a.config.DNSProxy.Upstream.Port,
|
|
|
|
RequestHook: func(clientAddr net.Addr, reqMsg dns.Msg, network string) (*dns.Msg, *dns.Msg, error) {
|
|
|
|
if a.config.DNSProxy.DisableFakePTR {
|
|
|
|
return nil, nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: Проверить на интерфейс
|
|
|
|
if len(reqMsg.Question) == 1 && reqMsg.Question[0].Qtype == dns.TypePTR {
|
|
|
|
respMsg := &dns.Msg{
|
|
|
|
MsgHdr: dns.MsgHdr{
|
|
|
|
Id: reqMsg.Id,
|
|
|
|
Response: true,
|
|
|
|
RecursionAvailable: true,
|
|
|
|
Rcode: dns.RcodeNameError,
|
|
|
|
},
|
|
|
|
Question: reqMsg.Question,
|
|
|
|
}
|
|
|
|
return nil, respMsg, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, nil, nil
|
|
|
|
},
|
|
|
|
ResponseHook: func(clientAddr net.Addr, reqMsg dns.Msg, respMsg dns.Msg, network string) (*dns.Msg, error) {
|
|
|
|
defer a.handleMessage(respMsg, clientAddr, &network)
|
|
|
|
|
|
|
|
if a.config.DNSProxy.DisableDropAAAA {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
var idx int
|
|
|
|
for _, answer := range respMsg.Answer {
|
|
|
|
if answer.Header().Rrtype == dns.TypeAAAA {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
respMsg.Answer[idx] = answer
|
|
|
|
idx++
|
|
|
|
}
|
|
|
|
respMsg.Answer = respMsg.Answer[:idx]
|
|
|
|
|
|
|
|
return &respMsg, nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
a.records = records.New()
|
|
|
|
|
|
|
|
nh4, err := netfilterHelper.New(false)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("netfilter helper init fail: %w", err)
|
|
|
|
}
|
|
|
|
err = nh4.CleanIPTables(a.config.Netfilter.IPTables.ChainPrefix)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to clear iptables: %w", err)
|
|
|
|
}
|
|
|
|
a.nfHelper4 = nh4
|
|
|
|
|
|
|
|
nh6, err := netfilterHelper.New(true)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("netfilter helper init fail: %w", err)
|
|
|
|
}
|
|
|
|
err = nh6.CleanIPTables(a.config.Netfilter.IPTables.ChainPrefix)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to clear iptables: %w", err)
|
|
|
|
}
|
|
|
|
a.nfHelper6 = nh6
|
|
|
|
|
2024-10-21 00:03:51 +03:00
|
|
|
newCtx, cancel := context.WithCancel(ctx)
|
|
|
|
defer cancel()
|
2024-08-26 19:10:40 +03:00
|
|
|
|
2025-02-08 06:23:36 +03:00
|
|
|
errChan := make(chan error)
|
|
|
|
|
|
|
|
/*
|
|
|
|
DNS Proxy
|
|
|
|
*/
|
|
|
|
|
|
|
|
go func() {
|
2025-02-14 03:17:43 +03:00
|
|
|
addr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", a.config.DNSProxy.Host.Address, a.config.DNSProxy.Host.Port))
|
2025-02-11 02:50:13 +03:00
|
|
|
if err != nil {
|
|
|
|
errChan <- fmt.Errorf("failed to resolve udp address: %v", err)
|
|
|
|
return
|
|
|
|
}
|
2025-02-14 03:17:43 +03:00
|
|
|
err = a.dnsMITM.ListenUDP(newCtx, addr)
|
2025-02-08 06:23:36 +03:00
|
|
|
if err != nil {
|
|
|
|
errChan <- fmt.Errorf("failed to serve DNS UDP proxy: %v", err)
|
2025-02-11 02:50:13 +03:00
|
|
|
return
|
2025-02-08 06:23:36 +03:00
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
2024-10-21 00:03:51 +03:00
|
|
|
go func() {
|
2025-02-14 03:17:43 +03:00
|
|
|
addr, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("%s:%d", a.config.DNSProxy.Host.Address, a.config.DNSProxy.Host.Port))
|
2025-02-11 02:50:13 +03:00
|
|
|
if err != nil {
|
|
|
|
errChan <- fmt.Errorf("failed to resolve tcp address: %v", err)
|
|
|
|
return
|
|
|
|
}
|
2025-02-14 03:17:43 +03:00
|
|
|
err = a.dnsMITM.ListenTCP(newCtx, addr)
|
2024-10-21 00:03:51 +03:00
|
|
|
if err != nil {
|
2025-02-08 06:23:36 +03:00
|
|
|
errChan <- fmt.Errorf("failed to serve DNS TCP proxy: %v", err)
|
2025-02-11 02:50:13 +03:00
|
|
|
return
|
2024-08-26 19:10:40 +03:00
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
2025-02-14 03:17:43 +03:00
|
|
|
var addrList []netlink.Addr
|
|
|
|
for _, linkName := range a.config.Link {
|
|
|
|
link, err := netlink.LinkByName(linkName)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to find link %s: %w", linkName, err)
|
|
|
|
}
|
|
|
|
linkAddrList, err := netlink.AddrList(link, nl.FAMILY_ALL)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to list address of interface: %w", err)
|
|
|
|
}
|
|
|
|
addrList = append(addrList, linkAddrList...)
|
2024-10-22 23:23:07 +03:00
|
|
|
}
|
|
|
|
|
2025-02-14 03:17:43 +03:00
|
|
|
if !a.config.DNSProxy.DisableRemap53 {
|
|
|
|
a.dnsOverrider4 = a.nfHelper4.PortRemap(fmt.Sprintf("%sDNSOR", a.config.Netfilter.IPTables.ChainPrefix), 53, a.config.DNSProxy.Host.Port, addrList)
|
|
|
|
err = a.dnsOverrider4.Enable()
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to override DNS (IPv4): %v", err)
|
|
|
|
}
|
|
|
|
defer func() { _ = a.dnsOverrider4.Disable() }()
|
2024-08-26 19:10:40 +03:00
|
|
|
|
2025-02-14 03:17:43 +03:00
|
|
|
a.dnsOverrider6 = a.nfHelper6.PortRemap(fmt.Sprintf("%sDNSOR", a.config.Netfilter.IPTables.ChainPrefix), 53, a.config.DNSProxy.Host.Port, addrList)
|
|
|
|
err = a.dnsOverrider6.Enable()
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to override DNS (IPv6): %v", err)
|
|
|
|
}
|
|
|
|
defer func() { _ = a.dnsOverrider6.Disable() }()
|
2024-10-22 01:52:31 +03:00
|
|
|
}
|
|
|
|
|
2025-02-08 06:23:36 +03:00
|
|
|
/*
|
|
|
|
Groups
|
|
|
|
*/
|
|
|
|
|
2025-02-14 03:17:43 +03:00
|
|
|
for _, group := range a.unprocessedGroups {
|
|
|
|
err := a.AddGroup(group)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for _, group := range a.groups {
|
2024-09-06 14:52:42 +03:00
|
|
|
err = group.Enable()
|
2024-08-27 03:19:10 +03:00
|
|
|
if err != nil {
|
2024-10-21 00:03:51 +03:00
|
|
|
return fmt.Errorf("failed to enable group: %w", err)
|
2024-08-27 03:19:10 +03:00
|
|
|
}
|
|
|
|
}
|
2024-10-21 00:03:51 +03:00
|
|
|
defer func() {
|
2025-02-14 03:17:43 +03:00
|
|
|
for _, group := range a.groups {
|
2025-02-14 01:24:52 +03:00
|
|
|
_ = group.Destroy()
|
2024-08-26 19:10:40 +03:00
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
2025-02-08 06:23:36 +03:00
|
|
|
/*
|
|
|
|
Socket (for netfilter.d events)
|
|
|
|
*/
|
2025-02-13 21:08:11 +03:00
|
|
|
socketPath := "/opt/var/run/kvas2.sock"
|
2024-10-21 00:03:51 +03:00
|
|
|
err = os.Remove(socketPath)
|
|
|
|
if err != nil && !errors.Is(err, os.ErrNotExist) {
|
|
|
|
return fmt.Errorf("failed to remove existed UNIX socket: %w", err)
|
|
|
|
}
|
|
|
|
socket, err := net.Listen("unix", socketPath)
|
2024-09-06 16:50:56 +03:00
|
|
|
if err != nil {
|
2024-10-21 00:03:51 +03:00
|
|
|
return fmt.Errorf("error while serve UNIX socket: %v", err)
|
2024-09-06 16:50:56 +03:00
|
|
|
}
|
2024-10-21 00:03:51 +03:00
|
|
|
defer func() {
|
|
|
|
_ = socket.Close()
|
|
|
|
_ = os.Remove(socketPath)
|
|
|
|
}()
|
2024-09-06 16:50:56 +03:00
|
|
|
|
|
|
|
go func() {
|
|
|
|
for {
|
2025-02-11 02:50:13 +03:00
|
|
|
if newCtx.Err() != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-10-21 00:03:51 +03:00
|
|
|
conn, err := socket.Accept()
|
2024-09-06 16:50:56 +03:00
|
|
|
if err != nil {
|
2024-10-21 00:03:51 +03:00
|
|
|
if !strings.Contains(err.Error(), "use of closed network connection") {
|
|
|
|
log.Error().Err(err).Msg("error while listening unix socket")
|
|
|
|
}
|
|
|
|
break
|
2024-09-06 16:50:56 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
go func(conn net.Conn) {
|
2025-02-11 02:50:13 +03:00
|
|
|
defer func() { _ = conn.Close() }()
|
2024-09-06 16:50:56 +03:00
|
|
|
|
|
|
|
buf := make([]byte, 1024)
|
|
|
|
n, err := conn.Read(buf)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
args := strings.Split(string(buf[:n]), ":")
|
|
|
|
if len(args) == 3 && args[0] == "netfilter.d" {
|
|
|
|
log.Debug().Str("table", args[2]).Msg("netfilter.d event")
|
2025-02-11 22:42:08 +03:00
|
|
|
err = a.dnsOverrider4.NetfilterDHook(args[2])
|
2025-02-11 15:53:58 +03:00
|
|
|
if err != nil {
|
|
|
|
log.Error().Err(err).Msg("error while fixing iptables after netfilter.d")
|
2024-09-06 16:50:56 +03:00
|
|
|
}
|
2025-02-11 22:42:08 +03:00
|
|
|
err = a.dnsOverrider6.NetfilterDHook(args[2])
|
2025-02-11 15:53:58 +03:00
|
|
|
if err != nil {
|
|
|
|
log.Error().Err(err).Msg("error while fixing iptables after netfilter.d")
|
2025-02-11 02:50:13 +03:00
|
|
|
}
|
2025-02-14 03:17:43 +03:00
|
|
|
for _, group := range a.groups {
|
2025-02-11 22:42:08 +03:00
|
|
|
err := group.NetfilterDHook(args[2])
|
2025-02-11 15:11:58 +03:00
|
|
|
if err != nil {
|
|
|
|
log.Error().Err(err).Msg("error while fixing iptables after netfilter.d")
|
2024-09-06 16:50:56 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}(conn)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
2025-02-08 06:23:36 +03:00
|
|
|
/*
|
|
|
|
Interface updates
|
|
|
|
*/
|
|
|
|
linkUpdateChannel := make(chan netlink.LinkUpdate)
|
|
|
|
linkUpdateDone := make(chan struct{})
|
|
|
|
err = netlink.LinkSubscribe(linkUpdateChannel, linkUpdateDone)
|
2024-10-21 00:03:51 +03:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to subscribe to link updates: %w", err)
|
|
|
|
}
|
2025-02-12 04:11:28 +03:00
|
|
|
defer close(linkUpdateDone)
|
2024-10-21 00:03:51 +03:00
|
|
|
|
2025-02-08 06:23:36 +03:00
|
|
|
/*
|
|
|
|
Global loop
|
|
|
|
*/
|
2024-09-06 15:48:15 +03:00
|
|
|
for {
|
|
|
|
select {
|
2025-02-08 06:23:36 +03:00
|
|
|
case event := <-linkUpdateChannel:
|
2024-10-21 00:03:51 +03:00
|
|
|
a.handleLink(event)
|
|
|
|
case err := <-errChan:
|
|
|
|
return err
|
2024-09-06 15:48:15 +03:00
|
|
|
case <-ctx.Done():
|
2024-10-21 00:03:51 +03:00
|
|
|
return nil
|
2024-09-06 15:48:15 +03:00
|
|
|
}
|
2024-08-26 19:10:40 +03:00
|
|
|
}
|
2024-10-21 00:03:51 +03:00
|
|
|
}
|
2024-08-26 19:10:40 +03:00
|
|
|
|
2025-02-08 06:23:36 +03:00
|
|
|
func (a *App) Start(ctx context.Context) (err error) {
|
2024-10-21 00:03:51 +03:00
|
|
|
if a.isRunning {
|
|
|
|
return ErrAlreadyRunning
|
2024-08-27 03:19:10 +03:00
|
|
|
}
|
2024-10-21 00:03:51 +03:00
|
|
|
a.isRunning = true
|
|
|
|
defer func() {
|
|
|
|
a.isRunning = false
|
|
|
|
}()
|
2024-08-27 03:19:10 +03:00
|
|
|
|
2024-10-21 00:03:51 +03:00
|
|
|
defer func() {
|
|
|
|
if r := recover(); r != nil {
|
|
|
|
var ok bool
|
2025-02-08 06:23:36 +03:00
|
|
|
if err, ok = r.(error); !ok {
|
|
|
|
err = fmt.Errorf("%v", r)
|
2024-10-21 00:03:51 +03:00
|
|
|
}
|
|
|
|
|
2025-02-08 06:23:36 +03:00
|
|
|
err = fmt.Errorf("recovered error: %w", err)
|
2024-08-30 04:29:05 +03:00
|
|
|
}
|
2024-10-21 00:03:51 +03:00
|
|
|
}()
|
|
|
|
|
2025-02-08 06:23:36 +03:00
|
|
|
err = a.start(ctx)
|
2024-08-30 04:29:05 +03:00
|
|
|
|
2024-10-21 00:03:51 +03:00
|
|
|
return err
|
2024-08-27 03:07:58 +03:00
|
|
|
}
|
|
|
|
|
2025-02-14 03:17:43 +03:00
|
|
|
func (a *App) AddGroup(groupModel models.Group) error {
|
|
|
|
for _, group := range a.groups {
|
2025-02-12 00:34:14 +03:00
|
|
|
if groupModel.ID == group.ID {
|
|
|
|
return ErrGroupIDConflict
|
|
|
|
}
|
|
|
|
}
|
|
|
|
dup := make(map[[4]byte]struct{})
|
|
|
|
for _, rule := range groupModel.Rules {
|
|
|
|
if _, exists := dup[rule.ID]; exists {
|
|
|
|
return ErrRuleIDConflict
|
|
|
|
}
|
|
|
|
dup[rule.ID] = struct{}{}
|
2024-09-14 16:13:59 +03:00
|
|
|
}
|
|
|
|
|
2025-02-14 03:17:43 +03:00
|
|
|
grp, err := group.NewGroup(groupModel, a.nfHelper4, a.config.Netfilter.IPTables.ChainPrefix, a.config.Netfilter.IPSet.TablePrefix)
|
2024-09-14 18:20:44 +03:00
|
|
|
if err != nil {
|
2025-02-11 22:42:08 +03:00
|
|
|
return fmt.Errorf("failed to create group: %w", err)
|
2024-09-14 16:13:59 +03:00
|
|
|
}
|
2025-02-14 03:17:43 +03:00
|
|
|
a.groups = append(a.groups, grp)
|
2025-02-12 00:34:14 +03:00
|
|
|
|
2025-02-13 21:08:11 +03:00
|
|
|
log.Debug().Str("id", grp.ID.String()).Str("name", grp.Name).Msg("added group")
|
2025-02-12 00:34:14 +03:00
|
|
|
|
2025-02-14 03:17:43 +03:00
|
|
|
if a.isRunning {
|
|
|
|
return grp.Sync(a.records)
|
|
|
|
}
|
|
|
|
return nil
|
2024-09-14 16:13:59 +03:00
|
|
|
}
|
|
|
|
|
2024-08-30 04:44:08 +03:00
|
|
|
func (a *App) ListInterfaces() ([]net.Interface, error) {
|
|
|
|
interfaceNames := make([]net.Interface, 0)
|
|
|
|
|
|
|
|
interfaces, err := net.Interfaces()
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to get interfaces: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, iface := range interfaces {
|
|
|
|
if iface.Flags&net.FlagPointToPoint == 0 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
interfaceNames = append(interfaceNames, iface)
|
|
|
|
}
|
|
|
|
|
|
|
|
return interfaceNames, nil
|
|
|
|
}
|
|
|
|
|
2025-02-13 21:08:11 +03:00
|
|
|
func (a *App) processARecord(aRecord dns.A, clientAddr net.Addr, network *string) {
|
|
|
|
var clientAddrStr, networkStr string
|
|
|
|
if clientAddr != nil {
|
|
|
|
clientAddrStr = clientAddr.String()
|
|
|
|
}
|
|
|
|
if network != nil {
|
|
|
|
networkStr = *network
|
|
|
|
}
|
2024-09-04 09:15:03 +03:00
|
|
|
log.Trace().
|
2025-02-08 06:23:36 +03:00
|
|
|
Str("name", aRecord.Hdr.Name).
|
|
|
|
Str("address", aRecord.A.String()).
|
|
|
|
Int("ttl", int(aRecord.Hdr.Ttl)).
|
2025-02-13 21:08:11 +03:00
|
|
|
Str("clientAddr", clientAddrStr).
|
|
|
|
Str("network", networkStr).
|
2024-09-04 09:15:03 +03:00
|
|
|
Msg("processing a record")
|
|
|
|
|
2025-02-14 03:17:43 +03:00
|
|
|
ttlDuration := aRecord.Hdr.Ttl + a.config.Netfilter.IPSet.AdditionalTTL
|
2024-08-27 01:44:17 +03:00
|
|
|
|
2025-02-14 03:17:43 +03:00
|
|
|
a.records.AddARecord(aRecord.Hdr.Name[:len(aRecord.Hdr.Name)-1], aRecord.A, ttlDuration)
|
2024-08-27 01:44:17 +03:00
|
|
|
|
2025-02-14 03:17:43 +03:00
|
|
|
names := a.records.GetAliases(aRecord.Hdr.Name[:len(aRecord.Hdr.Name)-1])
|
|
|
|
for _, group := range a.groups {
|
2025-02-08 06:23:36 +03:00
|
|
|
Rule:
|
|
|
|
for _, domain := range group.Rules {
|
2024-09-14 18:36:23 +03:00
|
|
|
if !domain.IsEnabled() {
|
|
|
|
continue
|
|
|
|
}
|
2024-09-14 18:20:44 +03:00
|
|
|
for _, name := range names {
|
|
|
|
if !domain.IsMatch(name) {
|
|
|
|
continue
|
|
|
|
}
|
2025-02-11 02:50:13 +03:00
|
|
|
// TODO: Check already existed
|
2025-02-11 15:29:26 +03:00
|
|
|
err := group.AddIP(aRecord.A, ttlDuration)
|
2024-09-14 18:20:44 +03:00
|
|
|
if err != nil {
|
|
|
|
log.Error().
|
2025-02-08 06:23:36 +03:00
|
|
|
Str("address", aRecord.A.String()).
|
2024-09-14 18:20:44 +03:00
|
|
|
Err(err).
|
|
|
|
Msg("failed to add address")
|
2024-09-14 19:26:23 +03:00
|
|
|
} else {
|
2025-02-13 21:08:11 +03:00
|
|
|
log.Debug().
|
2025-02-08 06:23:36 +03:00
|
|
|
Str("address", aRecord.A.String()).
|
|
|
|
Str("aRecordDomain", aRecord.Hdr.Name).
|
2024-09-14 19:26:23 +03:00
|
|
|
Str("cNameDomain", name).
|
|
|
|
Msg("add address")
|
2024-09-14 18:20:44 +03:00
|
|
|
}
|
2025-02-08 06:23:36 +03:00
|
|
|
break Rule
|
2024-09-14 18:20:44 +03:00
|
|
|
}
|
2024-08-27 01:44:17 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-02-13 21:08:11 +03:00
|
|
|
func (a *App) processCNameRecord(cNameRecord dns.CNAME, clientAddr net.Addr, network *string) {
|
|
|
|
var clientAddrStr, networkStr string
|
|
|
|
if clientAddr != nil {
|
|
|
|
clientAddrStr = clientAddr.String()
|
|
|
|
}
|
|
|
|
if network != nil {
|
|
|
|
networkStr = *network
|
|
|
|
}
|
2024-09-14 18:20:44 +03:00
|
|
|
log.Trace().
|
2025-02-08 06:23:36 +03:00
|
|
|
Str("name", cNameRecord.Hdr.Name).
|
|
|
|
Str("cname", cNameRecord.Target).
|
|
|
|
Int("ttl", int(cNameRecord.Hdr.Ttl)).
|
2025-02-13 21:08:11 +03:00
|
|
|
Str("clientAddr", clientAddrStr).
|
|
|
|
Str("network", networkStr).
|
2024-09-14 18:20:44 +03:00
|
|
|
Msg("processing cname record")
|
|
|
|
|
2025-02-14 03:17:43 +03:00
|
|
|
ttlDuration := cNameRecord.Hdr.Ttl + a.config.Netfilter.IPSet.AdditionalTTL
|
2024-08-27 01:44:17 +03:00
|
|
|
|
2025-02-14 03:17:43 +03:00
|
|
|
a.records.AddCNameRecord(cNameRecord.Hdr.Name[:len(cNameRecord.Hdr.Name)-1], cNameRecord.Target[:len(cNameRecord.Target)-1], ttlDuration)
|
2024-09-14 19:26:23 +03:00
|
|
|
|
|
|
|
// TODO: Optimization
|
|
|
|
now := time.Now()
|
2025-02-14 03:17:43 +03:00
|
|
|
aRecords := a.records.GetARecords(cNameRecord.Hdr.Name[:len(cNameRecord.Hdr.Name)-1])
|
|
|
|
names := a.records.GetAliases(cNameRecord.Hdr.Name[:len(cNameRecord.Hdr.Name)-1])
|
|
|
|
for _, group := range a.groups {
|
2025-02-08 06:23:36 +03:00
|
|
|
Rule:
|
|
|
|
for _, domain := range group.Rules {
|
2024-09-14 19:26:23 +03:00
|
|
|
if !domain.IsEnabled() {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
for _, name := range names {
|
|
|
|
if !domain.IsMatch(name) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
for _, aRecord := range aRecords {
|
2025-02-11 23:05:24 +03:00
|
|
|
err := group.AddIP(aRecord.Address, uint32(now.Sub(aRecord.Deadline).Seconds()))
|
2024-09-14 19:26:23 +03:00
|
|
|
if err != nil {
|
|
|
|
log.Error().
|
|
|
|
Str("address", aRecord.Address.String()).
|
|
|
|
Err(err).
|
|
|
|
Msg("failed to add address")
|
|
|
|
} else {
|
2025-02-13 21:08:11 +03:00
|
|
|
log.Debug().
|
2024-09-14 19:26:23 +03:00
|
|
|
Str("address", aRecord.Address.String()).
|
|
|
|
Str("cNameDomain", name).
|
|
|
|
Msg("add address")
|
|
|
|
}
|
|
|
|
}
|
2025-02-08 06:23:36 +03:00
|
|
|
continue Rule
|
2024-09-14 19:26:23 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-08-27 01:44:17 +03:00
|
|
|
}
|
|
|
|
|
2025-02-13 21:08:11 +03:00
|
|
|
func (a *App) handleRecord(rr dns.RR, clientAddr net.Addr, network *string) {
|
2024-08-30 03:37:00 +03:00
|
|
|
switch v := rr.(type) {
|
2025-02-08 06:23:36 +03:00
|
|
|
case *dns.A:
|
2025-02-13 21:08:11 +03:00
|
|
|
a.processARecord(*v, clientAddr, network)
|
2025-02-08 06:23:36 +03:00
|
|
|
case *dns.CNAME:
|
2025-02-13 21:08:11 +03:00
|
|
|
a.processCNameRecord(*v, clientAddr, network)
|
2024-08-30 03:37:00 +03:00
|
|
|
default:
|
2024-08-26 19:10:40 +03:00
|
|
|
}
|
2024-08-30 03:37:00 +03:00
|
|
|
}
|
2024-08-26 19:10:40 +03:00
|
|
|
|
2025-02-13 21:08:11 +03:00
|
|
|
func (a *App) handleMessage(msg dns.Msg, clientAddr net.Addr, network *string) {
|
2025-02-08 06:23:36 +03:00
|
|
|
for _, rr := range msg.Answer {
|
2025-02-13 21:08:11 +03:00
|
|
|
a.handleRecord(rr, clientAddr, network)
|
2024-08-26 19:10:40 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-02-14 03:17:43 +03:00
|
|
|
func (a *App) ImportConfig(cfg models.Config) error {
|
|
|
|
if !strings.HasPrefix(cfg.ConfigVersion, "0.1.") {
|
|
|
|
return ErrConfigUnsupportedVersion
|
2025-02-12 00:34:14 +03:00
|
|
|
}
|
|
|
|
|
2025-02-14 03:17:43 +03:00
|
|
|
if cfg.App.DNSProxy.Upstream.Address != "" {
|
|
|
|
a.config.DNSProxy.Upstream.Address = cfg.App.DNSProxy.Upstream.Address
|
2025-02-12 00:34:14 +03:00
|
|
|
}
|
2025-02-14 03:17:43 +03:00
|
|
|
if cfg.App.DNSProxy.Upstream.Port != 0 {
|
|
|
|
a.config.DNSProxy.Upstream.Port = cfg.App.DNSProxy.Upstream.Port
|
2025-02-08 06:23:36 +03:00
|
|
|
}
|
2025-02-14 03:17:43 +03:00
|
|
|
if cfg.App.DNSProxy.Host.Address != "" {
|
|
|
|
a.config.DNSProxy.Host.Address = cfg.App.DNSProxy.Host.Address
|
2025-02-08 06:23:36 +03:00
|
|
|
}
|
2025-02-14 03:17:43 +03:00
|
|
|
if cfg.App.DNSProxy.Host.Port != 0 {
|
|
|
|
a.config.DNSProxy.Host.Port = cfg.App.DNSProxy.Host.Port
|
2024-10-22 23:23:07 +03:00
|
|
|
}
|
2025-02-14 03:17:43 +03:00
|
|
|
a.config.DNSProxy.DisableRemap53 = cfg.App.DNSProxy.DisableRemap53
|
|
|
|
a.config.DNSProxy.DisableFakePTR = cfg.App.DNSProxy.DisableFakePTR
|
|
|
|
a.config.DNSProxy.DisableDropAAAA = cfg.App.DNSProxy.DisableDropAAAA
|
|
|
|
if cfg.App.Netfilter.IPTables.ChainPrefix != "" {
|
|
|
|
a.config.Netfilter.IPTables.ChainPrefix = cfg.App.Netfilter.IPTables.ChainPrefix
|
2024-08-26 19:10:40 +03:00
|
|
|
}
|
2025-02-14 03:17:43 +03:00
|
|
|
if cfg.App.Netfilter.IPSet.TablePrefix != "" {
|
|
|
|
a.config.Netfilter.IPSet.TablePrefix = cfg.App.Netfilter.IPSet.TablePrefix
|
2024-10-21 23:11:59 +03:00
|
|
|
}
|
2025-02-14 03:17:43 +03:00
|
|
|
a.config.Netfilter.IPSet.AdditionalTTL = cfg.App.Netfilter.IPSet.AdditionalTTL
|
2024-08-26 19:10:40 +03:00
|
|
|
|
2025-02-14 03:17:43 +03:00
|
|
|
a.unprocessedGroups = cfg.Groups
|
2024-10-22 01:52:31 +03:00
|
|
|
|
2025-02-14 03:17:43 +03:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *App) ExportConfig() models.Config {
|
|
|
|
groups := make([]models.Group, len(a.groups))
|
|
|
|
for idx, group := range a.groups {
|
|
|
|
groups[idx] = group.Group
|
2025-02-12 00:34:14 +03:00
|
|
|
}
|
2025-02-14 03:17:43 +03:00
|
|
|
return models.Config{
|
|
|
|
ConfigVersion: "0.1.0",
|
|
|
|
App: a.config,
|
|
|
|
Groups: groups,
|
|
|
|
}
|
|
|
|
}
|
2024-08-26 19:10:40 +03:00
|
|
|
|
2025-02-14 03:17:43 +03:00
|
|
|
func New() *App {
|
|
|
|
return &App{config: DefaultAppConfig}
|
2024-08-26 19:10:40 +03:00
|
|
|
}
|