MagiTrickle/kvas2.go

250 lines
5.6 KiB
Go
Raw Normal View History

2024-08-26 19:10:40 +03:00
package main
import (
"context"
2024-08-27 03:07:58 +03:00
"errors"
2024-08-26 19:10:40 +03:00
"fmt"
2024-08-27 03:07:58 +03:00
"strconv"
"sync"
"time"
2024-08-26 19:10:40 +03:00
"kvas2-go/models"
"kvas2-go/pkg/dns-proxy"
2024-08-27 03:07:58 +03:00
"kvas2-go/pkg/ip-helper"
2024-08-26 19:10:40 +03:00
"kvas2-go/pkg/iptables-helper"
2024-08-27 03:07:58 +03:00
)
var (
ErrGroupIDConflict = errors.New("group id conflict")
2024-08-26 19:10:40 +03:00
)
type Config struct {
MinimalTTL time.Duration
ChainPostfix string
TargetDNSServerAddress string
ListenPort uint16
}
type App struct {
Config Config
DNSProxy *dnsProxy.DNSProxy
DNSOverrider *iptablesHelper.DNSOverrider
Records *Records
2024-08-27 03:19:10 +03:00
Groups map[int]*Group
2024-08-26 19:10:40 +03:00
}
func (a *App) Listen(ctx context.Context) []error {
errs := make([]error, 0)
isError := make(chan struct{})
var once sync.Once
var errsMu sync.Mutex
handleError := func(err error) {
errsMu.Lock()
defer errsMu.Unlock()
errs = append(errs, err)
once.Do(func() { close(isError) })
}
defer func() {
if r := recover(); r != nil {
if err, ok := r.(error); ok {
handleError(err)
} else {
handleError(fmt.Errorf("%v", r))
}
}
}()
newCtx, cancel := context.WithCancel(ctx)
defer cancel()
if err := a.DNSOverrider.Enable(); err != nil {
handleError(fmt.Errorf("failed to override DNS: %w", err))
return errs
}
2024-08-27 03:19:10 +03:00
for idx, _ := range a.Groups {
err := a.usingGroup(idx)
if err != nil {
handleError(fmt.Errorf("failed to using group: %w", err))
return errs
}
}
2024-08-26 19:10:40 +03:00
go func() {
if err := a.DNSProxy.Listen(newCtx); err != nil {
handleError(fmt.Errorf("failed to initialize DNS proxy: %v", err))
}
}()
select {
case <-ctx.Done():
case <-isError:
}
2024-08-27 03:19:10 +03:00
for idx, _ := range a.Groups {
err := a.releaseGroup(idx)
if err != nil {
handleError(fmt.Errorf("failed to release group: %w", err))
return errs
}
}
2024-08-26 19:10:40 +03:00
if err := a.DNSOverrider.Disable(); err != nil {
handleError(fmt.Errorf("failed to rollback override DNS changes: %w", err))
}
return errs
}
2024-08-27 03:19:10 +03:00
func (a *App) usingGroup(idx int) error {
2024-08-27 03:07:58 +03:00
fwmark, err := ipHelper.GetUnusedFwMark()
if err != nil {
return fmt.Errorf("error while getting fwmark: %w", err)
}
table, err := ipHelper.GetUnusedTable()
if err != nil {
return fmt.Errorf("error while getting table: %w", err)
}
out, err := ipHelper.ExecIp("rule", "add", "fwmark", strconv.Itoa(int(fwmark)), "table", strconv.Itoa(int(table)))
if err != nil {
return err
}
if len(out) != 0 {
return errors.New(string(out))
}
2024-08-27 03:19:10 +03:00
a.Groups[idx].FWMark = fwmark
a.Groups[idx].Table = table
return nil
}
func (a *App) releaseGroup(idx int) error {
out, err := ipHelper.ExecIp("rule", "del", "fwmark", strconv.Itoa(int(a.Groups[idx].FWMark)), "table", strconv.Itoa(int(a.Groups[idx].Table)))
if err != nil {
return err
}
if len(out) != 0 {
return errors.New(string(out))
}
return nil
}
func (a *App) AppendGroup(group *models.Group) error {
if _, exists := a.Groups[group.ID]; exists {
return ErrGroupIDConflict
}
a.Groups[group.ID] = &Group{
Group: group,
2024-08-27 03:07:58 +03:00
}
return nil
}
2024-08-27 01:44:17 +03:00
func (a *App) processARecord(aRecord dnsProxy.Address) {
ttlDuration := time.Duration(aRecord.TTL) * time.Second
if ttlDuration < a.Config.MinimalTTL {
ttlDuration = a.Config.MinimalTTL
}
a.Records.PutARecord(aRecord.Name.String(), aRecord.Address, ttlDuration)
cNames := append([]string{aRecord.Name.String()}, a.Records.GetCNameRecords(aRecord.Name.String(), true, true)...)
fmt.Printf("Relates CNames:\n")
for idx, cName := range cNames {
fmt.Printf("|- #%d: %s\n", idx, cName)
}
for _, group := range a.Groups {
for _, domain := range group.Domains {
if !domain.IsEnabled() {
continue
}
for _, cName := range cNames {
if domain.IsMatch(cName) {
fmt.Printf("|- Matched %s (%s) for %s in %s group!\n", cName, aRecord.Name, domain.Domain, group.Name)
}
}
}
}
}
func (a *App) processCNameRecord(cNameRecord dnsProxy.CName) {
ttlDuration := time.Duration(cNameRecord.TTL) * time.Second
if ttlDuration < a.Config.MinimalTTL {
ttlDuration = a.Config.MinimalTTL
}
a.Records.PutCNameRecord(cNameRecord.Name.String(), cNameRecord.CName.String(), ttlDuration)
}
2024-08-26 19:10:40 +03:00
func (a *App) handleRecord(msg *dnsProxy.Message) {
printKnownRecords := func() {
for _, q := range msg.QD {
fmt.Printf("%04x: DBG Known addresses for: %s\n", msg.ID, q.QName.String())
2024-08-27 01:44:17 +03:00
for idx, addr := range a.Records.GetARecords(q.QName.String(), true, false) {
2024-08-26 19:10:40 +03:00
fmt.Printf("%04x: #%d: %s\n", msg.ID, idx, addr.String())
}
}
}
parseResponseRecord := func(rr dnsProxy.ResourceRecord) {
switch v := rr.(type) {
case dnsProxy.Address:
fmt.Printf("%04x: -> A: Name: %s; Address: %s; TTL: %d\n", msg.ID, v.Name, v.Address.String(), v.TTL)
2024-08-27 01:44:17 +03:00
a.processARecord(v)
2024-08-26 19:10:40 +03:00
case dnsProxy.CName:
fmt.Printf("%04x: -> CNAME: Name: %s; CName: %s\n", msg.ID, v.Name, v.CName)
2024-08-27 01:44:17 +03:00
a.processCNameRecord(v)
2024-08-26 19:10:40 +03:00
default:
fmt.Printf("%04x: -> Unknown: %x\n", msg.ID, v.EncodeResource())
}
}
printKnownRecords()
for _, q := range msg.QD {
fmt.Printf("%04x: <- Request name: %s\n", msg.ID, q.QName.String())
}
for _, a := range msg.AN {
parseResponseRecord(a)
}
for _, a := range msg.NS {
parseResponseRecord(a)
}
for _, a := range msg.AR {
parseResponseRecord(a)
}
printKnownRecords()
2024-08-27 01:44:17 +03:00
fmt.Println()
2024-08-26 19:10:40 +03:00
}
func New(config Config) (*App, error) {
var err error
app := &App{}
app.Config = config
app.DNSProxy = dnsProxy.New(app.Config.ListenPort, app.Config.TargetDNSServerAddress)
app.DNSProxy.MsgHandler = app.handleRecord
app.Records = NewRecords()
app.DNSOverrider, err = iptablesHelper.NewDNSOverrider(fmt.Sprintf("%s_DNSOVERRIDER", app.Config.ChainPostfix), app.Config.ListenPort)
if err != nil {
return nil, fmt.Errorf("failed to initialize DNS overrider: %w", err)
}
2024-08-27 03:19:10 +03:00
app.Groups = make(map[int]*Group)
2024-08-26 19:10:40 +03:00
return app, nil
}