Ctrl K
ComunidadGuías…

adguardhome/rewrite_rules.go

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

Archivo original ↗Preguntar a KiBot
package adguardhome

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

	"sigs.k8s.io/external-dns/endpoint"
)

func rewriteRuleFormat() ruleFormat {
	return ruleFormat{
		formatRules: formatRewriteRules,
		needsMigration: func(rule string) bool {
			return !strings.HasPrefix(rule, "! ")
		},
	}
}

func formatRewriteRules(endpoints []*endpoint.Endpoint, suffix string) ([]string, error) {
	rules := make([]string, 0, 2*len(endpoints))
	for _, e := range endpoints {
		if strings.Contains(e.DNSName, "*") {
			return nil, fmt.Errorf("wildcard hostname %q is not supported in rewrite mode", e.DNSName)
		}
		rewrite, err := dnsRewriteRule(e)
		if err != nil {
			return nil, fmt.Errorf("invalid %s record for %s: %w", e.RecordType, e.DNSName, err)
		}
		// Adblock-style rules cannot carry inline ownership comments.
		metadata := "! " + endpointToString(e, suffix)
		if e.RecordType == endpoint.RecordTypeTXT {
			data, err := json.Marshal(e)
			if err != nil {
				return nil, fmt.Errorf("encode TXT metadata: %w", err)
			}
			metadata = fmt.Sprintf("! %s %s", suffix, data)
		}
		rules = append(rules, metadata, rewrite)
	}
	return rules, nil
}

func dnsRewriteRule(e *endpoint.Endpoint) (string, error) {
	value := e.Targets[0]
	if e.RecordType == endpoint.RecordTypeTXT {
		var err error
		value, err = txtRewriteValue(value)
		if err != nil {
			return "", err
		}
		return fmt.Sprintf("|%s|$dnstype=TXT,dnsrewrite=NOERROR;TXT;%s", e.DNSName, value), nil
	}
	return fmt.Sprintf("|%s|$dnsrewrite=NOERROR;%s;%s", e.DNSName, e.RecordType, value), nil
}

func txtRewriteValue(target string) (string, error) {
	if strings.ContainsAny(target, "\r\n") {
		return "", fmt.Errorf("TXT target must not contain literal line breaks")
	}

	value := target
	if strings.HasPrefix(target, `"`) {
		if len(target) < 2 || !strings.HasSuffix(target, `"`) {
			return "", fmt.Errorf("unterminated quoted TXT target")
		}
		var decoded strings.Builder
		quoted := target[1 : len(target)-1]
		for i := 0; i < len(quoted); i++ {
			b := quoted[i]
			switch b {
			case '"':
				return "", fmt.Errorf("TXT target must contain a single quoted string")
			case '\\':
				i++
				if i == len(quoted) {
					return "", fmt.Errorf("incomplete TXT escape")
				}
				b = quoted[i]
				if i+2 < len(quoted) && b >= '0' && b <= '9' && quoted[i+1] >= '0' && quoted[i+1] <= '9' && quoted[i+2] >= '0' && quoted[i+2] <= '9' {
					n, err := strconv.ParseUint(quoted[i:i+3], 10, 8)
					if err != nil {
						return "", fmt.Errorf("invalid DNS TXT escape: %w", err)
					}
					b = byte(n)
					i += 2
				}
			}
			decoded.WriteByte(b)
		}
		value = decoded.String()
	}

	// AdGuard's rewrite path emits one DNS character-string per TXT value.
	if len(value) > 255 {
		return "", fmt.Errorf("TXT value exceeds 255 bytes")
	}

	var escaped strings.Builder
	for i := 0; i < len(value); i++ {
		b := value[i]
		if b <= ' ' || b >= 127 || strings.ContainsRune(",$#\"\\", rune(b)) {
			// Decimal DNS escapes survive rule parsing and avoid modifier injection.
			fmt.Fprintf(&escaped, "\\%03d", b)
		} else {
			escaped.WriteByte(b)
		}
	}
	return escaped.String(), nil
}