Commit cb5394ee authored by Vitaly Lipatov's avatar Vitaly Lipatov

router: cache per-file resolve results, report changed files

Only re-resolve list files that actually changed (detected by mtime vs saved hash file). Unchanged files reuse cached resolved IPs from resolved_parts/ directory. Log message now shows which files triggered the re-resolve. Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
parent 2c830dc4
...@@ -15,6 +15,7 @@ cd "$(dirname "$(realpath "$0")")" || exit ...@@ -15,6 +15,7 @@ cd "$(dirname "$(realpath "$0")")" || exit
ROUTES_DIR=routes.d ROUTES_DIR=routes.d
ROUTES6_DIR=routes6.d ROUTES6_DIR=routes6.d
STATE_DIR=.state STATE_DIR=.state
HISTORY_SIZE=10
FORCE= FORCE=
RESOLVE= RESOLVE=
...@@ -109,6 +110,41 @@ vlog() { [ -n "$VERBOSE" ] && log " $*" || true ; } ...@@ -109,6 +110,41 @@ vlog() { [ -n "$VERBOSE" ] && log " $*" || true ; }
md5_lists() { cat "$@" 2>/dev/null | md5sum | awk '{print $1}' ; } md5_lists() { cat "$@" 2>/dev/null | md5sum | awk '{print $1}' ; }
ensure_state_dir() { mkdir -p "$STATE_DIR/$1" ; } ensure_state_dir() { mkdir -p "$STATE_DIR/$1" ; }
# Detect domains with DNS balancing (single A record + short TTL)
# Writes volatile domain list to STATE_DIR/$state/volatile_domains
# Args: $1=state_dir $2=name $3=label $4...=domain list files
detect_volatile_domains()
{
local state="$1" name="$2" label="$3"
shift 3
local volatile_file="$STATE_DIR/$state/volatile_domains"
local ttl_threshold=120
local domains=$(mktemp)
trap "rm -f $domains" RETURN
# Extract unique domain names from list files
for f in "$@" ; do
cat_expanded "$f"
done | grep -v '^#' | grep -v '^$' | grep '[a-zA-Z]' | sort -u > "$domains"
[ -s "$domains" ] || { rm -f "$volatile_file" ; return 0 ; }
> "$volatile_file"
while read -r domain ; do
local output=$(dig +noall +answer "$domain" A 2>/dev/null | grep "IN[[:space:]]*A[[:space:]]")
local count=$(echo "$output" | grep -c .)
local ttl=$(echo "$output" | head -1 | awk '{print $2}')
if [ "$count" -le 1 ] && [ "${ttl:-9999}" -le "$ttl_threshold" ] ; then
echo "$domain ttl=$ttl records=$count" >> "$volatile_file"
fi
done < "$domains"
if [ -s "$volatile_file" ] ; then
vlog "[$name]$label volatile domains (TTL≤${ttl_threshold}s, single A): $(wc -l < "$volatile_file")"
[ -n "$VERBOSE" ] && sed "s/^/ [$name]$label /" "$volatile_file" >&2 || true
fi
}
# Look up table number: first from rt_tables by name, then from last IP octet # Look up table number: first from rt_tables by name, then from last IP octet
# If derived from IP, auto-creates entry in rt_tables # If derived from IP, auto-creates entry in rt_tables
lookup_table() lookup_table()
...@@ -348,36 +384,66 @@ process_routes() ...@@ -348,36 +384,66 @@ process_routes()
if [ -n "$RESOLVE" ] ; then if [ -n "$RESOLVE" ] ; then
log "[$name]$label Re-resolving..." log "[$name]$label Re-resolving..."
else else
log "[$name]$label Changes detected, resolving..." # Report which files changed (newer than saved hash)
local changed_files=""
local hash_file="$STATE_DIR/$state/hash"
if [ -f "$hash_file" ] ; then
for f in "$gwdir/gateway" "$gwdir/options" $lists ; do
if [ "$f" -nt "$hash_file" ] ; then
changed_files="$changed_files $(basename "$f")"
fi
done
fi
log "[$name]$label Changes detected${changed_files:+ in:$changed_files}, resolving..."
fi fi
# Split: pure IP files go through cat, domain files through resolve # Resolve each list file individually, cache results per file
local ip_lists="" domain_lists="" local resolved_parts_dir="$STATE_DIR/$state/resolved_parts"
mkdir -p "$resolved_parts_dir"
for f in $lists ; do for f in $lists ; do
local bname=$(basename "$f")
local cached="$resolved_parts_dir/$bname"
# Re-resolve only changed files (or all if no cache)
if [ -f "$cached" ] && [ -f "$hash_file" ] && ! [ "$f" -nt "$hash_file" ] ; then
vlog "[$name]$label $bname: unchanged, using cache"
continue
fi
vlog "[$name]$label $bname: resolving..."
if grep -v '^#' "$f" | grep -q '[a-zA-Z]' ; then if grep -v '^#' "$f" | grep -q '[a-zA-Z]' ; then
domain_lists="$domain_lists $f" # Domain file: resolve
cat_expanded "$f" | grep -v '^#' | grep -v '^$' | $resolve_func | \
grep -v '^#' | grep -v '^$' > "$cached.tmp"
else else
ip_lists="$ip_lists $f" # IP-only file: just filter
fi
done
vlog "[$name]$label ip_lists:$(echo $ip_lists | wc -w) domain_lists:$(echo $domain_lists | wc -w)"
{
for f in $ip_lists ; do
if [ "$ipcmd" = "ip -6" ] ; then if [ "$ipcmd" = "ip -6" ] ; then
# IPv6 mode: only pass lines containing ":" grep -v '^#' "$f" | grep -v '^$' | grep ':' > "$cached.tmp"
grep -v '^#' "$f" | grep -v '^$' | grep ':'
else else
grep -v '^#' "$f" | grep -v '^$' grep -v '^#' "$f" | grep -v '^$' > "$cached.tmp"
fi
fi fi
mv "$cached.tmp" "$cached"
done done
if [ -n "$domain_lists" ] ; then cat "$resolved_parts_dir"/* 2>/dev/null | sed 's|/32$||' | sort -u > "$resolved_new"
for f in $domain_lists ; do
cat_expanded "$f" # Accumulate resolve history to handle DNS balancing (round-robin)
done | grep -v '^#' | grep -v '^$' | $resolve_func | \ # Keeps last HISTORY_SIZE snapshots; IPs not seen for HISTORY_SIZE runs expire
grep -v '^#' | grep -v '^$' local history_dir="$STATE_DIR/$state/resolve_history"
mkdir -p "$history_dir"
rm -f "$history_dir/$HISTORY_SIZE"
local i=$((HISTORY_SIZE - 1))
while [ $i -ge 1 ] ; do
[ -f "$history_dir/$i" ] && mv "$history_dir/$i" "$history_dir/$((i + 1))"
i=$((i - 1))
done
cp "$resolved_new" "$history_dir/1"
sort -u "$history_dir"/* > "${resolved_new}.merged"
mv "${resolved_new}.merged" "$resolved_new"
vlog "[$name]$label resolve history: $(ls "$history_dir" | wc -l) snapshots, $(wc -l < "$resolved_new") unique IPs"
# Detect domains using DNS balancing (only for IPv4, where dig A applies)
if [ -n "$domain_lists" ] && [ "$ipcmd" = "ip" ] ; then
detect_volatile_domains "$state" "$name" "$label" $domain_lists
fi fi
} | sed 's|/32$||' | sort -u > "$resolved_new"
fi fi
# Check if resolved IPs actually changed # Check if resolved IPs actually changed
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment