Ctrl K
ComunidadGuías…

adguardhome/rules.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package adguardhome

import (
	"encoding/json"
	"errors"
	"fmt"
	"strings"

	log "github.com/sirupsen/logrus"
	"sigs.k8s.io/external-dns/endpoint"
)

type ruleFormat struct {
	formatRules    func([]*endpoint.Endpoint, string) ([]string, error)
	needsMigration func(string) bool
}

func parseRules(rules []string, suffix string) (endpoints []*endpoint.Endpoint, unmanaged []string, err error) {
	unmanaged = make([]string, 0)
	for i := 0; i < len(rules); i++ {
		rule := strings.TrimSpace(rules[i])
		e, parseErr := parseRule(rule, suffix)
		if errors.Is(parseErr, errNotManaged) {
			unmanaged = append(unmanaged, rules[i])
			continue
		}
		if errors.Is(parseErr, errArtificialRecord) {
			continue
		}
		if parseErr != nil {
			return nil, nil, fmt.Errorf("failed to parse rule %s: %w", rule, parseErr)
		}
		if strings.HasPrefix(rule, "! ") {
			rewrite, err := dnsRewriteRule(e)
			if err != nil {
				return nil, nil, fmt.Errorf("invalid rewrite metadata: %w", err)
			}
			if i+1 >= len(rules) {
				return nil, nil, fmt.Errorf("missing DNS rewrite after metadata: %s", rule)
			}
			active := strings.TrimSpace(rules[i+1])
			if active != rewrite {
				return nil, nil, fmt.Errorf("mismatched DNS rewrite after metadata: %s", rule)
			}
			i++
		}
		endpoints = append(endpoints, e)
	}
	return endpoints, unmanaged, nil
}

func parseRule(rule, suffix string) (*endpoint.Endpoint, error) {
	rule = strings.TrimSpace(rule)
	if data, ok := strings.CutPrefix(rule, "! "+suffix+" "); ok {
		var e endpoint.Endpoint
		if err := json.Unmarshal([]byte(data), &e); err != nil {
			return nil, fmt.Errorf("invalid TXT metadata: %w", err)
		}
		if e.RecordType != endpoint.RecordTypeTXT || e.DNSName == "" || len(e.Targets) != 1 {
			return nil, fmt.Errorf("TXT metadata must contain a hostname and one TXT target")
		}
		return &e, nil
	}
	if strings.HasPrefix(rule, "! "+managedBy) {
		return nil, errNotManaged
	}

	_, tail, managed := strings.Cut(rule, suffix)
	for managed && tail != "" {
		if strings.HasPrefix(tail, ";labels=") && json.Valid([]byte(strings.TrimPrefix(tail, ";labels="))) {
			break
		}
		_, next, found := strings.Cut(tail, suffix)
		if !found {
			break
		}
		tail = next
	}
	if !managed || (tail != "" && !strings.HasPrefix(tail, ";labels=")) {
		return nil, errNotManaged
	}
	rule = strings.TrimPrefix(rule, "! ")

	// Ignore artificial rules that we manage and will reconstruct
	if strings.HasPrefix(rule, "@@||") {
		return nil, errArtificialRecord
	}

	// Extract labels from the rule if present
	var labels endpoint.Labels
	ruleWithoutLabels := rule
	if tail != "" {
		labelsJSON := strings.TrimPrefix(tail, ";labels=")
		labels = make(endpoint.Labels)
		if err := json.Unmarshal([]byte(labelsJSON), &labels); err != nil {
			log.Warnf("failed to parse labels from rule %s: %v", rule, err)
			labels = nil
		}
		ruleWithoutLabels = strings.TrimSuffix(rule, tail)
	}

	if strings.HasPrefix(rule, "#") {
		r := &endpoint.Endpoint{
			RecordType: endpoint.RecordTypeTXT,
			Labels:     labels,
		}
		content := strings.TrimSuffix(strings.TrimPrefix(ruleWithoutLabels, "# "), " "+suffix)
		separator := strings.LastIndexByte(content, ' ')
		if separator < 0 || separator == len(content)-1 {
			return nil, fmt.Errorf("invalid rule: %s", rule)
		}
		r.DNSName = content[separator+1:]
		r.Targets = endpoint.Targets{content[:separator]}

		return r, nil
	}

	parts := strings.SplitN(ruleWithoutLabels, " ", 3)
	if len(parts) != 3 {
		return nil, fmt.Errorf("invalid rule: %s", rule)
	}

	r := &endpoint.Endpoint{
		RecordType: endpoint.RecordTypeA,
		DNSName:    parts[1],
		Targets:    endpoint.Targets{parts[0]},
		Labels:     labels,
	}

	return r, nil
}

func endpointToString(e *endpoint.Endpoint, suffix string) string {
	// Serialize labels if present
	labelsSuffix := ""
	if len(e.Labels) > 0 {
		labelsJSON, err := json.Marshal(e.Labels)
		if err == nil {
			labelsSuffix = fmt.Sprintf(";labels=%s", string(labelsJSON))
		}
	}

	if e.RecordType == endpoint.RecordTypeTXT {
		return fmt.Sprintf("# %s %s %s%s", e.Targets[0], e.DNSName, suffix, labelsSuffix)
	}

	return fmt.Sprintf("%s %s #%s%s", e.Targets[0], e.DNSName, suffix, labelsSuffix)
}