🌴 Sync 2026-09-22 14:48:47

This commit is contained in:
github-actions[bot] committed 2026-09-22 14:48:47 +08:00
1 parent bf4289d9a4
commit fdc72bdc39
17 files changed
+260 -91

No files matched your search

@@ -454,7 +454,8 @@ fwlive_adaptive_merge_reply() {
_adapt=1
fwlive_adaptive_enabled || _adapt=0
case "$_body" in
*\}) ;;
'{}') _sep= ;;
*\}) _sep=, ;;
*) printf '%s' "$_body"; return 0 ;;
esac
_base=${_body%\}}
@@ -466,26 +467,26 @@ fwlive_adaptive_merge_reply() {
esac
if [ "$_adapt" = 0 ]; then
if [ "$_has_msgs" = 1 ]; then
printf '%s,"adaptive":0}' "$_base"
printf '%s%s"adaptive":0}' "$_base" "$_sep"
else
printf '%s,"adaptive":0,"messages_received":%s}' "$_base" "$_msgs"
printf '%s%s"adaptive":0,"messages_received":%s}' "$_base" "$_sep" "$_msgs"
fi
return 0
fi
if [ "$_shed" = 1 ]; then
if [ "$_has_msgs" = 1 ]; then
printf '%s,"adaptive":1%s,"truncated":%s,"shed":{"level":"hot","limit":%s}}' \
"$_base" "$_effective" "$_trunc" "$_limit"
printf '%s%s"adaptive":1%s,"truncated":%s,"shed":{"level":"hot","limit":%s}}' \
"$_base" "$_sep" "$_effective" "$_trunc" "$_limit"
else
printf '%s,"adaptive":1,"messages_received":%s%s,"truncated":%s,"shed":{"level":"hot","limit":%s}}' \
"$_base" "$_msgs" "$_effective" "$_trunc" "$_limit"
printf '%s%s"adaptive":1,"messages_received":%s%s,"truncated":%s,"shed":{"level":"hot","limit":%s}}' \
"$_base" "$_sep" "$_msgs" "$_effective" "$_trunc" "$_limit"
fi
else
if [ "$_has_msgs" = 1 ]; then
printf '%s,"adaptive":1%s,"truncated":%s}' "$_base" "$_effective" "$_trunc"
printf '%s%s"adaptive":1%s,"truncated":%s}' "$_base" "$_sep" "$_effective" "$_trunc"
else
printf '%s,"adaptive":1,"messages_received":%s%s,"truncated":%s}' \
"$_base" "$_msgs" "$_effective" "$_trunc"
printf '%s%s"adaptive":1,"messages_received":%s%s,"truncated":%s}' \
"$_base" "$_sep" "$_msgs" "$_effective" "$_trunc"
fi
fi
}
@@ -28,7 +28,7 @@ function has_hint(s, lc) {
}
function non_fw_prefix(s, lc) {
lc = tolower(s)
return lc ~ "^(dnsmasq|procd|ubusd|netifd|odhcpd|logd|dropbear|uhttpd|hostapd|wpad)([^a-z0-9_]|$)"
return lc ~ "^(dnsmasq|procd|ubusd|netifd|odhcpd|logd|dropbear|uhttpd|hostapd|wpad)([^a-z0-9_-]|$)"
}
function detect_action(s, words, n, i, w, wl, lc, start, pos, before, afterc, best, bestpos) {
n = split("ACCEPT ALLOW PASS DROP REJECT DENY BLOCK", words, " ")
@@ -445,40 +445,41 @@ restore_wan_log_baseline() {
}
wan_filter_log_enabled() {
log_val="$1"
[ -n "$log_val" ] || return 1
case "$log_val" in
*[!0-9]*) return 1 ;;
esac
log_val=$(wan_filter_log_decimal "$1") || return 1
[ $((log_val & 1)) -ne 0 ]
}
wan_filter_log_decimal() {
_log_val="$1"
case "$_log_val" in
''|*[!0-9]*) return 1 ;;
esac
while [ "${_log_val#0}" != "$_log_val" ]; do
_log_val=${_log_val#0}
done
[ -n "$_log_val" ] || _log_val=0
printf '%s' "$_log_val"
}
wan_filter_log_target_value() {
current="$1"
current=$(wan_filter_log_decimal "$1") || {
printf '1'
return 0
}
if wan_filter_log_enabled "$current"; then
printf '%s' "$current"
return 0
fi
case "$current" in
''|*[!0-9]*)
printf '1'
;;
*)
printf '%d' $((current | 1))
;;
esac
printf '%d' $((current | 1))
}
# Clear filter-log bit 0 only. Prints remaining value, or empty when the option
# should be deleted (no bits left / non-numeric / already empty).
wan_filter_log_clear_value() {
current="$1"
case "$current" in
''|*[!0-9]*)
printf ''
return 0
;;
esac
current=$(wan_filter_log_decimal "$1") || {
printf ''
return 0
}
cleared=$((current & ~1))
if [ "$cleared" -eq 0 ]; then
printf ''
@@ -677,11 +678,27 @@ restore_wan_zone_log() {
logger -t fwlive "WAN log rollback skipped: firewall changes pending" 2>/dev/null || true
return 1
fi
# Capture the committed value before staging our rollback. If another
# writer stages a firewall delta after this point, the post-stage guard
# below must be able to undo only our rollback staging without reverting
# the other writer's change.
committed=$(wan_zone_log_value "$zone")
if [ -z "$previous" ]; then
uci -q delete "firewall.${zone}.log" 2>/dev/null || true
else
uci -q set "firewall.${zone}.log=${previous}" 2>/dev/null || true
fi
_staged=$(uci -q changes firewall 2>/dev/null || true)
_foreign=$(wan_log_foreign_staged_lines "$zone" "$_staged")
if [ -n "$_foreign" ]; then
if [ -z "$committed" ]; then
uci delete "firewall.${zone}.log" 2>/dev/null || true
else
uci set "firewall.${zone}.log=${committed}" 2>/dev/null || true
fi
logger -t fwlive "WAN log rollback skipped after stage: firewall changes staged by another writer" 2>/dev/null || true
return 1
fi
uci commit firewall 2>/dev/null || true
}
@@ -692,13 +709,14 @@ restore_wan_zone_log() {
# TOCTOU hardening: UCI staging is global per config file, so a
# non-cooperating writer (another admin's `uci set`, the LuCI firewall page)
# can stage a delta AFTER the toggle's early firewall_changes_pending check.
# Staging and committing therefore live INSIDE this function — the only path
# to `uci commit firewall` in the toggle flow, so callers cannot bypass it —
# gated by:
# Staging and committing therefore live INSIDE this function, gated by:
# 1. a pending re-check BEFORE our own delta exists in staging (foreign-only);
# 2. a post-stage re-check AFTER our set/delete: if anything besides our
# log option is staged, undo our staging (restore the pre-stage committed
# value) and abort without commit — foreign staging stays intact.
# The reload-failure rollback has its own pre/post-stage guard for the same
# reason; it must not bypass this invariant merely because it is compensating
# for a failed reload.
# Residual window: a writer that stages between the post-stage check and
# `uci commit` can still ride along; post-commit verification detects a
# mismatched log bit. Same-option races (another writer also staging
@@ -977,6 +995,20 @@ run_logging_selftest() {
echo 'wan_filter_log_enabled 2: expected false' >&2
return 1
fi
for leading_value in 011 010 08 0; do
if ! wan_filter_log_decimal "$leading_value" >/dev/null; then
echo "wan_filter_log_decimal $leading_value: expected decimal digits" >&2
return 1
fi
done
if wan_filter_log_enabled '08' || wan_filter_log_enabled '010'; then
echo 'leading-zero even values: expected false' >&2
return 1
fi
if ! wan_filter_log_enabled '011'; then
echo 'leading-zero odd value: expected true' >&2
return 1
fi
got=$(wan_filter_log_target_value '')
if [ "$got" != '1' ]; then
@@ -995,6 +1027,16 @@ run_logging_selftest() {
echo "wan_filter_log_target_value 1: expected 1 got $got" >&2
return 1
fi
got=$(wan_filter_log_target_value '08')
if [ "$got" != '9' ]; then
echo "wan_filter_log_target_value 08: expected 9 got $got" >&2
return 1
fi
got=$(wan_filter_log_target_value '011')
if [ "$got" != '11' ]; then
echo "wan_filter_log_target_value 011: expected 11 got $got" >&2
return 1
fi
# Disable clears bit 0 only: log=3 -> 2; log=1 -> delete (empty).
got=$(wan_filter_log_clear_value '3')
@@ -1015,6 +1057,17 @@ run_logging_selftest() {
return 1
fi
got=$(wan_filter_log_clear_value '010')
if [ "$got" != '10' ]; then
echo "wan_filter_log_clear_value 010: expected 10 got $got" >&2
return 1
fi
got=$(wan_filter_log_clear_value '0')
if [ -n "$got" ]; then
echo "wan_filter_log_clear_value 0: expected empty got $got" >&2
return 1
fi
# Enable/disable parity around multi-bit values.
got=$(wan_filter_log_target_value '2')
if [ "$got" != '3' ]; then
+30 -5
View File
@@ -564,7 +564,8 @@ resolve_addresses() {
start=$(date +%s)
# Optional JSON on $1 (rpcd may also pass argv $3 at the call site); else stdin.
input=$(read_rpc_input "$1")
count=0
lookups=0
truncated=0
OUT=''
if ! command -v jshn >/dev/null 2>&1; then
@@ -597,27 +598,51 @@ resolve_addresses() {
return 0
}
while IFS= read -r ip; do
[ "$count" -ge "$RESOLVE_MAX" ] && break
if [ "$lookups" -ge "$RESOLVE_MAX" ]; then
truncated=1
break
fi
# Abort cleanly once the whole-call wall-clock budget is spent;
# never START a lookup past the budget.
now=$(date +%s)
[ $((now - start)) -ge "$RESOLVE_BUDGET" ] && break
if [ $((now - start)) -ge "$RESOLVE_BUDGET" ]; then
truncated=1
break
fi
# Missing/empty element: set -u cannot read bare $ip.
if ! is_resolvable_address "${ip:-}"; then
continue
fi
lookups=$((lookups + 1))
# NXDOMAIN / timeout: resolve_hostname returns 1. set -e
# cannot apply to this assignment.
name=$(resolve_hostname "$ip") || name=
if [ -n "$name" ]; then
map_add "$ip" "$name"
count=$((count + 1))
else
# Record a completed miss so the view can fail-mark NXDOMAIN
# without treating the unprocessed truncated tail as tried.
# map_add skips empty values (rules-map labels); resolve needs them.
esc_key=$(printf '%s' "$ip" | json_escape)
case "$OUT" in
*"\"$esc_key\":"*) ;;
*)
if [ -n "$OUT" ]; then
OUT="${OUT},"
fi
OUT="${OUT}\"${esc_key}\":\"\""
;;
esac
fi
done <<EOF
$_addresses
EOF
printf '{"names":{%s}}' "$OUT"
if [ "$truncated" -eq 1 ]; then
printf '{"names":{%s},"truncated":true}' "$OUT"
else
printf '{"names":{%s}}' "$OUT"
fi
}
check_eq() {