🦄 Sync 2026-09-17 23:34:47

This commit is contained in:
github-actions[bot] committed 2026-09-17 23:34:47 +08:00
1 parent fdb7b42e2b
commit ff5040d573
29 files changed
+1816 -1317

No files matched your search

+1 -1
View File
@@ -14,7 +14,7 @@ PKG_SOURCE_URL:=https://github.com/Ysurac/dsvpn.git
PKG_SOURCE_VERSION:=aae011339bba733d3fa4ffe8b5dd95dab2efb4ab
PKG_NAME:=dsvpn
PKG_VERSION:=0.1.5
PKG_RELEASE:=2
PKG_RELEASE:=3
PKG_LICENSE:=MIT
PKG_LICENSE_FILES:=LICENSE
+15 -1
View File
@@ -1,6 +1,16 @@
#!/bin/sh /etc/rc.common
# Copyright (C) 2019 Ycarus (Yannick Chabanois) <ycarus@zugaina.org> for OpenMPTCProuter project
# network.globals.multipath='disable' means the kernel is running with
# net.mptcp.enabled=0. Asking for an MPTCP socket then fails outright with
# ENOPROTOOPT ("Protocol not available") -- the kernel has no implicit
# fallback to plain TCP, every caller has to do it itself, and most don't.
# Only an explicit "disable" counts, so this package keeps its stock
# behaviour wherever network.globals.multipath doesn't exist at all.
_omr_mptcp_globally_disabled() {
[ "$(uci -q get network.globals.multipath)" = "disable" ]
}
START=90
STOP=10
@@ -75,7 +85,11 @@ start_instance() {
_log "starting ${PROG_NAME} ${1} instance $*"
if version_over_5_4; then
# mptcpize LD_PRELOADs a socket() that always asks for IPPROTO_MPTCP and
# never falls back, so with MPTCP globally off dsvpn cannot create its
# socket at all ("Couldn't create connection: Protocol not available")
# and procd just respawns it forever. Run it unwrapped instead.
if version_over_5_4 && ! _omr_mptcp_globally_disabled; then
PROG="mptcpize run ${PROG}"
fi
+1 -1
View File
@@ -9,7 +9,7 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=glorytun
PKG_RELEASE:=4
PKG_RELEASE:=5
PKG_SOURCE_PROTO:=git
PKG_SOURCE_VERSION:=95fa5ead2154adb546bcdb454c56c876e9f60bb5
PKG_SOURCE:=glorytun-$(PKG_VERSION).tar.gz
+14
View File
@@ -3,6 +3,16 @@
# Copyright (C) 2015 ovh.com
# Copyright (C) 2017 Ycarus (Yannick Chabanois) <ycarus@zugaina.org>
# network.globals.multipath='disable' means the kernel is running with
# net.mptcp.enabled=0. Asking for an MPTCP socket then fails outright with
# ENOPROTOOPT ("Protocol not available") -- the kernel has no implicit
# fallback to plain TCP, every caller has to do it itself, and most don't.
# Only an explicit "disable" counts, so this package keeps its stock
# behaviour wherever network.globals.multipath doesn't exist at all.
_omr_mptcp_globally_disabled() {
[ "$(uci -q get network.globals.multipath)" = "disable" ]
}
START=90
STOP=10
@@ -81,6 +91,10 @@ start_instance() {
${dev:+dev "$dev"}
[ "${mode}" = "listener" ] && procd_append_param command listener
# Glorytun asks the kernel for an MPTCP socket when told "mptcp"; with
# MPTCP globally off that socket cannot be created, so drop the argument
# and let it run over plain TCP instead of failing to connect at all.
[ "${mptcp}" = "1" ] && _omr_mptcp_globally_disabled && mptcp=0
[ "${mptcp}" = "1" ] && procd_append_param command mptcp
[ "${chacha20}" = "1" ] && procd_append_param command chacha20
[ "${multiqueue}" = "1" ] && procd_append_param command multiqueue
+2 -2
View File
@@ -11,8 +11,8 @@ LUCI_DESCRIPTION:=Live firewall log table (nftables/fw4 and iptables LOG) with f
LUCI_DEPENDS:=+luci-base +logd +jsonfilter
LUCI_PKGARCH:=all
PKG_VERSION:=0.1.42
PKG_RELEASE:=22
PKG_VERSION:=0.1.43
PKG_RELEASE:=23
# Reproducible build: honor SOURCE_DATE_EPOCH from the build environment (set by docker-sdk.sh / CI).
ifdef SOURCE_DATE_EPOCH
+1
View File
@@ -19,6 +19,7 @@ LuCI **Firewall Live View** — client-side JS view polling `ubus fwlive poll` (
| `htdocs/luci-static/resources/fwlive/buffer.js` | Ring-buffer apply/merge helpers (pause ingest + resume merge) |
| `htdocs/luci-static/resources/fwlive/poll-coordinator.js` | Request coalescing, visibility, cadence, and disposal |
| `htdocs/luci-static/resources/fwlive/render-policy.js` | Pure weak-device display-cap and render-cost decisions |
| `htdocs/luci-static/resources/fwlive/render-scheduler.js` | Epoch-safe frame coalescing and render token-bucket state |
| `htdocs/luci-static/resources/fwlive/hostname.js` | Hostname cache LRU + failure TTL helpers |
| `root/usr/share/luci/menu.d/*.json` | Menu entry (`admin/status/fwlive`) |
| `root/usr/share/rpcd/acl.d/*.json` | ubus ACL (read + write for logging enable/disable) |
@@ -9,7 +9,7 @@
*/
return baseclass.extend({
/* Keep in sync with openwrt-feed/luci-app-fwlive/Makefile PKG_VERSION. */
APP_VERSION: '0.1.42',
APP_VERSION: '0.1.43',
ROW_LIMIT_OPTIONS: [25, 50, 100, 250, 500, 1000, 2000],
DEFAULT_ROW_LIMIT: 100,
FETCH_MODE_OPTIONS: ['auto', 'manual'],
@@ -0,0 +1,131 @@
'use strict';
/* SPDX-License-Identifier: Apache-2.0 */
/* Copyright 2025-2026 Lucas Albers <lucas.b.albers@gmail.com> */
'require baseclass';
/* Own queued render intent and the token bucket; the view owns DOM work.
* Adapters supply the epoch, paint callback, cost policy, clock and frame APIs.
* Without a frame API, schedule() paints synchronously. */
function createScheduler(options) {
const { getEpoch, render, renderCost, capacity, requestFrame, cancelFrame } = options;
if (!Number.isFinite(capacity) || capacity <= 0)
throw new TypeError('capacity must be a positive number');
const now = options.now || Date.now;
let pending = null;
let nextForce = null;
let frameId = null;
let bucket = capacity;
let bucketMs = null;
let floodSuppressed = false;
let lastRenderedRowCount = 0;
let lastRenderedHeadId = '';
let disposed = false;
function schedule(force) {
if (disposed) return;
force = !!force || nextForce !== null;
nextForce = null;
const epoch = getEpoch();
if (typeof requestFrame !== 'function') {
render(!!force);
return;
}
if (pending) {
/* Retain display changes (e.g. resolved names) even when a new epoch
* replaces the frame. The callback always reads current view state. */
pending.force = pending.force || !!force;
pending.epoch = epoch;
return;
}
pending = { epoch: epoch, force: !!force };
frameId = requestFrame(function () {
const request = pending;
pending = null;
frameId = null;
if (disposed || request.epoch !== getEpoch()) return;
/* A current request queued behind stale work gets its own frame. */
if (epoch !== request.epoch) schedule(request.force);
else render(request.force);
});
}
/* A successful decision reserves tokens, but only markRendered commits
* row identity after the view has actually painted. Forced paints bypass
* the bucket without clearing an existing high-rate warning. */
function shouldRender(rows, force, lastBatchNewIdCount) {
if (disposed) return false;
if (force) return true;
const cost = renderCost({
visibleRowCount: rows.length,
visibleHeadId: rows.length ? rows[0].id : '',
lastRenderedRowCount: lastRenderedRowCount,
lastRenderedHeadId: lastRenderedHeadId,
lastBatchNewIdCount: lastBatchNewIdCount
});
if (cost <= 0) {
floodSuppressed = false;
return false;
}
const timestamp = now();
const elapsed = bucketMs === null ? 0 : Math.max(0, timestamp - bucketMs);
bucketMs = timestamp;
bucket = Math.min(capacity, bucket + (elapsed * capacity) / 1000);
floodSuppressed = cost > bucket;
if (floodSuppressed) return false;
bucket -= cost;
return true;
}
function markRendered(rows) {
lastRenderedRowCount = rows.length;
lastRenderedHeadId = rows.length ? rows[0].id : '';
}
function resetBudget() {
bucket = capacity;
bucketMs = now();
floodSuppressed = false;
}
/* Reserve force for the next scheduled render. The caller can cancel its
* own unused reservation, never a queued frame or a newer reservation. */
function forceNextRender() {
const reservation = {};
if (!disposed) nextForce = reservation;
return function () {
if (nextForce === reservation) nextForce = null;
};
}
function dispose() {
if (disposed) return;
disposed = true;
pending = null;
nextForce = null;
if (frameId !== null && typeof cancelFrame === 'function') cancelFrame(frameId);
frameId = null;
}
return {
schedule: schedule,
shouldRender: shouldRender,
markRendered: markRendered,
resetBudget: resetBudget,
forceNextRender: forceNextRender,
isFloodSuppressed: function () {
return floodSuppressed;
},
dispose: dispose
};
}
return baseclass.extend({
create: createScheduler
});
@@ -21,6 +21,7 @@
'require fwlive.proto as proto';
'require fwlive.poll-coordinator as pollCoordinator';
'require fwlive.render-policy as renderPolicy';
'require fwlive.render-scheduler as renderScheduler';
const callFwlivePoll = rpc.declare({
object: 'fwlive',
@@ -108,6 +109,7 @@ return view.extend({
/* One-shot: first live poll after unpause merges instead of replacing. */
resumeMerge: false,
pollCoordinator: null,
renderScheduler: null,
pagehideHandler: null,
/* Layer 2 — visibility / RTT cadence / shed surfacing. */
rttStreakKind: null,
@@ -126,14 +128,8 @@ return view.extend({
summaryRowsShown: false,
summaryData: null,
resolveLoadShed: false,
renderRaf: 0,
filterInputTimer: null,
messageLayout: 'wrap',
renderBucket: constants.RENDER_CAP_PER_SEC,
renderBucketMs: 0,
floodSuppressed: false,
pendingForceRender: false,
pendingRenderEpoch: null,
/* Session-new IDs from the last applied batch; this is not buffer growth. */
lastBatchNewIdCount: 0,
showHostnames: false,
@@ -146,8 +142,6 @@ return view.extend({
resolveGeneration: 0,
lastPollError: false,
lastRulesError: null,
lastRenderedRowCount: 0,
lastRenderedHeadId: '',
followLive: true,
rulesMap: {},
firewallBackend: 'nft',
@@ -1002,7 +996,8 @@ return view.extend({
const cap = this.ingestCap();
if (this.entries.length >= cap && cap > 0) bits.push(_('buffer full'));
if (this.floodSuppressed) bits.push(_('render paused (high rate)'));
if (this.ensureRenderScheduler().isFloodSuppressed())
bits.push(_('render paused (high rate)'));
if (this.weakDevice && this.rowLimit > constants.WEAK_DEVICE_DISPLAY_ROW_CAP)
bits.push(
_('Display limited to %d rows on this device').format(
@@ -1072,6 +1067,26 @@ return view.extend({
return this.pollCoordinator;
},
ensureRenderScheduler() {
if (this.renderScheduler) return this.renderScheduler;
this.renderScheduler = renderScheduler.create({
getEpoch: () => this.currentPollEpoch(),
render: (force) => this.renderRows(force),
renderCost: renderPolicy.renderCost,
now: () => this.nowMs(),
capacity: constants.RENDER_CAP_PER_SEC,
requestFrame:
typeof window !== 'undefined' && typeof window.requestAnimationFrame === 'function'
? (cb) => window.requestAnimationFrame(cb)
: null,
cancelFrame:
typeof window !== 'undefined' && typeof window.cancelAnimationFrame === 'function'
? (id) => window.cancelAnimationFrame(id)
: null
});
return this.renderScheduler;
},
currentPollEpoch() {
return this.ensurePollCoordinator().getState().epoch;
},
@@ -1112,6 +1127,7 @@ return view.extend({
disposeView() {
if (this.pollCoordinator) this.pollCoordinator.dispose();
if (this.renderScheduler) this.renderScheduler.dispose();
this.resolveGeneration = (this.resolveGeneration || 0) + 1;
this.resolveInFlight = false;
if (this.pagehideHandler) {
@@ -1283,76 +1299,7 @@ return view.extend({
},
scheduleRenderRows(force) {
const doForce = !!force;
const epoch = this.currentPollEpoch();
if (typeof requestAnimationFrame !== 'function') {
this.renderRows(doForce);
return;
}
if (this.renderRaf) {
this.pendingForceRender = this.pendingForceRender || doForce;
this.pendingRenderEpoch = epoch;
return;
}
this.renderRaf = requestAnimationFrame(
function () {
this.renderRaf = 0;
const f = doForce || !!this.pendingForceRender;
const pendingEpoch = this.pendingRenderEpoch;
this.pendingForceRender = false;
this.pendingRenderEpoch = null;
if (epoch !== this.currentPollEpoch()) {
/* A newer epoch may have requested a render while this frame was
* queued. Drop stale paint and requeue only that current request. */
if (pendingEpoch === this.currentPollEpoch()) this.scheduleRenderRows(f);
return;
}
this.renderRows(f);
}.bind(this)
);
},
refillRenderBucket() {
const now = Date.now();
if (!this.renderBucketMs) this.renderBucketMs = now;
const elapsed = now - this.renderBucketMs;
this.renderBucketMs = now;
this.renderBucket = Math.min(
constants.RENDER_CAP_PER_SEC,
this.renderBucket + (elapsed * constants.RENDER_CAP_PER_SEC) / 1000
);
},
consumeRenderBudget(cost) {
if (cost <= 0) {
this.floodSuppressed = false;
return true;
}
this.refillRenderBucket();
if (cost <= this.renderBucket) {
this.renderBucket -= cost;
this.floodSuppressed = false;
return true;
}
this.floodSuppressed = true;
return false;
},
/** Charge by new log events per poll, not full table size (avoids false throttle at high limits). */
renderBudgetCost(rows) {
const count = rows ? rows.length : 0;
const headId = count ? rows[0].id : '';
return renderPolicy.renderCost({
visibleRowCount: count,
visibleHeadId: headId,
lastRenderedRowCount: this.lastRenderedRowCount,
lastRenderedHeadId: this.lastRenderedHeadId,
lastBatchNewIdCount: this.lastBatchNewIdCount
});
this.ensureRenderScheduler().schedule(!!force);
},
updateFloodBanner() {
@@ -1360,7 +1307,7 @@ return view.extend({
if (!el) return;
if (!el.style) el.style = { display: '' };
if (this.floodSuppressed) {
if (this.ensureRenderScheduler().isFloodSuppressed()) {
el.style.display = 'block';
el.textContent = _(
'High event rate — table refresh is throttled to protect the browser. The buffer still updates; refresh will resume automatically.'
@@ -1547,9 +1494,8 @@ return view.extend({
this.saveRowLimit();
this.updateHash(this.readFilters());
/* Reset flood throttle so Limit changes paint even during ping -A. */
this.renderBucket = constants.RENDER_CAP_PER_SEC;
this.floodSuppressed = false;
this.pendingForceRender = true;
this.ensureRenderScheduler().resetBudget();
const cancelForce = this.ensureRenderScheduler().forceNextRender();
if (!this.tablePaused) this.renderRows(true);
else this.updateStatus();
const epoch = this.currentPollEpoch();
@@ -1560,9 +1506,7 @@ return view.extend({
if (this.tablePaused) this.updateStatus();
else this.renderRows(true);
})
.finally(() => {
this.pendingForceRender = false;
});
.finally(cancelForce);
},
limitSelectOptions() {
@@ -1825,17 +1769,14 @@ return view.extend({
this.updateHash(this.readFilters());
const rows = this.filteredRows();
const cost = force ? Math.max(1, rows.length) : this.renderBudgetCost(rows);
const paint = this.ensureRenderScheduler().shouldRender(
rows,
!!force,
this.lastBatchNewIdCount
);
this.updateLoggingToolbarUi();
if (!force && cost === 0) {
this.floodSuppressed = false;
this.updateFloodBanner();
this.updateStatus(rows);
return;
}
if (!force && !this.consumeRenderBudget(cost)) {
if (!paint) {
this.updateFloodBanner();
this.updateStatus(rows);
return;
@@ -1875,15 +1816,14 @@ return view.extend({
else scroll.scrollTop = prevScroll;
}
this.lastRenderedRowCount = rows.length;
this.lastRenderedHeadId = rows.length ? rows[0].id : '';
this.ensureRenderScheduler().markRendered(rows);
if (rows.length && this.rowTintEnabled() && !this.tintProbeDone) {
const runProbe = () => {
if (!this.tintProbeDone) this.probeRowTintPaint();
};
if (typeof requestAnimationFrame === 'function')
requestAnimationFrame(() => requestAnimationFrame(runProbe));
if (typeof window !== 'undefined' && typeof window.requestAnimationFrame === 'function')
window.requestAnimationFrame(() => window.requestAnimationFrame(runProbe));
else setTimeout(runProbe, 0);
} else if (!this.rowTintEnabled() && this.tintFallbackActive) {
this.clearTintFallback(document.querySelector('.fwlive-map'));
@@ -1997,7 +1937,7 @@ return view.extend({
* and stays behind the explicit Show rows control. */
if (this.tablePaused) this.updateStatus();
else if (this.summaryMode) this.renderSummary();
else this.scheduleRenderRows(!!this.pendingForceRender);
else this.scheduleRenderRows();
try {
await this.resolveHostnamesForEntries(this.filteredRows());
@@ -2,7 +2,7 @@
# SPDX-License-Identifier: Apache-2.0
# Copyright 2025-2026 Lucas Albers <lucas.b.albers@gmail.com>
#
# Layer 1 adaptive poll cap (#306). Sourced by rpcd/fwlive.
# Layer 1 adaptive poll cap. Sourced by rpcd/fwlive.
# Always on unless test/triage override (no UCI / no product config):
# FWLIVE_ADAPTIVE=0|false|off|no
# or sentinel ${FWLIVE_ADAPTIVE_OFF_FILE:-<state-dir>/fwlive-adaptive-off}
@@ -18,7 +18,7 @@
# flock, lock busy, or corrupt state. Lock-busy ⇒ unlocked last-writer-wins is
# acceptable (state stays one valid JSON line; ordering is not guaranteed).
# Failed ubus log.read must NOT call record() — a ~0 ms failure is not "cold"
# health and must not clear an existing hot/shed cap (#329 Hermes Q1).
# health and must not clear an existing hot/shed cap.
# Outside the measured duration interval: plan (pre), record/merge (post).
# messages_received is supplied by the filter-side jsonfilter enumeration;
# failed reads still use 0 without scanning the response in ash.
@@ -4,7 +4,7 @@
# GENERATED FILE — do not edit. Run: ./scripts/gen-all.sh
# source: core/fwlive-log.js CLASSIFY_SPEC
# Shared isFirewallEvent parity logic (shell). Sourced by fwlive-log-filter.sh and tests.
# Sourced library: do not add set -euo here (callers own strict mode, #291 C3).
# Sourced library: do not add set -euo here; callers own strict mode.
# One awk process classifies a batch (MODE=json/json_reply) or one message (default).
# The caller sets FILTER_DIR when this file is sourced. Resolve the asset
@@ -6,12 +6,12 @@
# Log messages are treated as data (jsonfilter + awk stdin); never interpolated
# into shell command strings. Usage: ubus call log read '...' | fwlive-log-filter.sh
#
# Perf (#219): one jsonfilter for @.log[*] plus one awk classify. The same
# Performance: one jsonfilter for @.log[*] plus one awk classify. The same
# awk pass counts enumerated entries for messages_received; process count is
# constant per poll, not O(entries).
#
# Entry point (pipeline). The classifier sibling is sourced and must not set
# strict mode itself (#291 C3).
# strict mode itself.
set -eu
if ! command -v jsonfilter >/dev/null 2>&1; then
@@ -25,7 +25,7 @@ FILTER_DIR="$(cd "$(dirname "$0")" && pwd)"
. "$FILTER_DIR/fwlive-is-firewall-event.sh"
# The classifier is a package asset, not generated at request time. Fail
# closed if an incomplete install or a damaged package removed it (#321).
# closed if an incomplete install or a damaged package removed it.
if [ ! -r "$CLASSIFY_AWK" ]; then
printf '%s' '{"log":[],"error":"classifier_missing"}'
exit 1
@@ -33,9 +33,9 @@ fi
# Prefer stdin over -s: Linux MAX_ARG_STRLEN is 128KiB; a raised logd ring
# (or paused FETCH_LINES_MAX poll) can exceed that and make jsonfilter fail
# while this script still returns a valid empty result (#234).
# while this script still returns a valid empty result.
# A valid input with no log entries is not fatal; the classifier closes JSON
# and reports messages_received:0 (#220). A jsonfilter failure is different:
# and reports messages_received:0. A jsonfilter failure is different:
# it must remain an error so rpcd does not record a fast healthy sample. Keep
# jsonfilter output in a secure temporary file before classification so a
# partial pipeline cannot produce malformed JSON on failure.
+26 -26
View File
@@ -42,7 +42,7 @@ NFT_TIMEOUT=5
# CLI-only iptables fixture path for __rulesmap_iptables (no arbitrary paths).
RULESMAP_IPTABLES_FILE='/tmp/rulesmap'
# json_escape lives in fwlive-logging.sh so prerm can source that file alone (#222).
# json_escape lives in fwlive-logging.sh so prerm can source that file alone.
# shellcheck disable=SC1090,SC1091 # LOGGING_SH resolves to sibling ../fwlive-logging.sh
. "$LOGGING_SH"
# shellcheck disable=SC1090,SC1091
@@ -52,10 +52,10 @@ slug_key() {
echo "$1" | tr '[:upper:]' '[:lower:]' | tr ' _' '--'
}
# Cap rules JSON size so a huge ruleset cannot unbound an rpcd reply (#229).
# Cap rules JSON size so a huge ruleset cannot unbound an rpcd reply.
RULES_MAP_MAX_KEYS=512
RULES_MAP_MAX_BYTES=65536
# Same wall-clock bound as nft for iptables-save / ip6tables-save (#229).
# Same wall-clock bound as nft for iptables-save / ip6tables-save.
# Overridable for host tests (e.g. FWLIVE_IPTABLES_TIMEOUT=1).
IPTABLES_TIMEOUT="${FWLIVE_IPTABLES_TIMEOUT:-5}"
@@ -70,7 +70,7 @@ map_add() {
# ("foo" vs "foobar") and value false positives ("see foo").
# Quoting "$esc_key" makes the match literal (no glob escaping needed).
# Callers apply UCI, then labeled (!fw4:) prefixes, then unlabeled
# cosmetics so first-wins prefers authoritative names (#230).
# cosmetics so first-wins prefers authoritative names.
case "$OUT" in
*"\"$esc_key\":"*) return 0 ;;
esac
@@ -92,7 +92,7 @@ map_add() {
uci_rule_names() {
# uci missing / empty firewall config is "no names", not fatal.
# pipefail + set -e cannot apply to this pipeline (#291 C3).
# pipefail + set -e cannot apply to this pipeline.
uci -q show firewall 2>/dev/null | sed -n "s/^firewall\.@rule\[[0-9]*\]\.name='\(.*\)'$/\1/p" || true
}
@@ -142,7 +142,7 @@ run_with_timeout() {
if command -v timeout >/dev/null 2>&1; then
timeout "$secs" "$@" 2>/dev/null
else
# Fail closed (#229): never run unbounded when timeout is absent.
# Fail closed: never run unbounded when timeout is absent.
return 127
fi
}
@@ -170,7 +170,7 @@ map_uci_rule_names() {
# Line-wise (here-doc, not an unquoted for-loop) so a name with
# spaces stays one token. Names that fail is_uci_style_name are
# skipped: parseRuleHint() cannot match them, and fragments of
# "My Rule" must not shadow a real rule named Rule (#226).
# "My Rule" must not shadow a real rule named Rule.
# The while-read stays in the current shell (here-doc, not a
# pipeline) so map_add mutations reach OUT.
while IFS= read -r name || [ -n "$name" ]; do
@@ -187,7 +187,7 @@ EOF
}
# pass=labeled|unlabeled|all — labeled before unlabeled so !fw4: beats
# an earlier cosmetic for the same prefix under first-wins (#230).
# an earlier cosmetic for the same prefix under first-wins.
map_from_nft_stream() {
pass="${1:-all}"
while IFS= read -r line; do
@@ -288,7 +288,7 @@ build_rules_map() {
backend=$(detect_firewall_backend)
# UCI first, then labeled stream prefixes, then unlabeled cosmetics
# (#230). First-wins keeps UCI over nft and !fw4: over cosmetic.
# First-wins keeps UCI over nft and !fw4: over cosmetic.
map_uci_rule_names
case "$backend" in
@@ -435,10 +435,10 @@ fetch_firewall_logs() {
shed_flag=${2:-0}
# Adaptive plan prints: lines shed bucket
start_cs=$(fwlive_adaptive_clock_cs)
# Drop dead oneshot flag — only consumed on logd's stream branch (#306).
# Drop dead oneshot flag — only consumed on logd's stream branch.
if ! raw=$(ubus call log read "{\"lines\":$lines,\"stream\":false}" 2>/dev/null); then
# Do not record ~0 ms as cold: failure is not healthy fast processing
# and must not clear an existing hot/shed cap (#329 Hermes Q1).
# and must not clear an existing hot/shed cap.
out='{"log":[],"error":"log_read_failed"}'
fwlive_adaptive_merge_reply "$out" "$shed_flag" "$lines" 0 0 0
return 0
@@ -446,7 +446,7 @@ fetch_firewall_logs() {
# Summary aggregation is part of the adaptive contract. Keep it out of
# adaptive=0 replies, while the classifier still remains the single pass
# over decoded log entries (#306).
# over decoded log entries.
if fwlive_adaptive_enabled; then
FWLIVE_SUMMARY=1
else
@@ -454,7 +454,7 @@ fetch_firewall_logs() {
fi
export FWLIVE_SUMMARY
# Do not swallow a filter failure into a silent empty table (#220).
# Do not swallow a filter failure into a silent empty table.
# If the filter exits non-zero but still printed JSON, keep it.
# Empty success is not valid poll JSON — the shipped filter always
# prints {"log":[...]}, but a stub/regression must not emit a blank body.
@@ -476,7 +476,7 @@ fetch_firewall_logs() {
*'"error":'*) _filter_ok=0 ;;
esac
end_cs=$(fwlive_adaptive_clock_cs)
# Uptime wrap / skew: end < start — do not record as cold (Grok #329 P2).
# Uptime wrap / skew: end < start — do not record as cold.
if [ "$_filter_ok" = 1 ] && [ "$end_cs" -ge "$start_cs" ]; then
_dur=$(((end_cs - start_cs) * 10))
fwlive_adaptive_record "$_dur" "$lines"
@@ -524,7 +524,7 @@ resolve_hostname() {
[ -n "$ip" ] || return 1
is_resolvable_address "$ip" || return 1
command -v nslookup >/dev/null 2>&1 || return 1
# timeout/nslookup miss is "no PTR", not a plugin abort (#291 C3).
# timeout/nslookup miss is "no PTR", not a plugin abort.
out=$(run_with_timeout "$RESOLVE_TIMEOUT" nslookup "$ip") || return 1
[ -n "$out" ] || return 1
name=$(parse_nslookup_name "$out") || return 1
@@ -532,8 +532,8 @@ resolve_hostname() {
printf '%s' "$name"
}
# Strict IPv4/IPv6 validation before nslookup (issue #190): the old char-class
# check admitted hostname-shaped tokens of hex chars + dots/colons
# Strict IPv4/IPv6 validation before nslookup: the old char-class check
# admitted hostname-shaped tokens of hex chars + dots/colons
# (e.g. "dead.beef.cafe.baad"), letting the resolver fall back to upstream DNS
# queries chosen by an authenticated session. Validate the real address
# families here so nslookup only ever sees genuine numeric addresses.
@@ -545,8 +545,8 @@ is_resolvable_address() {
# Reject anything outside the address alphabet BEFORE awk, so embedded
# newlines/whitespace (which awk would treat as record separators) can
# never let a valid first record mask trailing garbage that still reaches
# nslookup (#190, CodeRabbit/luna fold): nslookup only ever sees a single
# token. Note the check intentionally allows only hex/colon/dot.
# nslookup. It must see a single token. Note the check intentionally allows
# only hex/colon/dot.
case "$addr" in
*[!0-9a-fA-F:.]*) return 1 ;;
esac
@@ -618,7 +618,7 @@ is_resolvable_address() {
}
resolve_addresses() {
# Missing resolver must not look like "no PTR" (#218). Check before
# Missing resolver must not look like "no PTR". Check before
# starting the budget clock or reading stdin.
if ! command -v nslookup >/dev/null 2>&1; then
command -v logger >/dev/null 2>&1 && logger -t fwlive "nslookup not found; resolve disabled"
@@ -626,7 +626,7 @@ resolve_addresses() {
return 0
fi
# Load shed: previous poll was hot (#306 Layer 1).
# Load shed: previous poll was hot.
if fwlive_adaptive_is_hot; then
printf '{"names":{},"disabled":"load"}'
return 0
@@ -679,12 +679,12 @@ resolve_addresses() {
# never START a lookup past the budget.
now=$(date +%s)
[ $((now - start)) -ge "$RESOLVE_BUDGET" ] && break
# Missing/empty element: set -u cannot read bare $ip (#291 C3).
# Missing/empty element: set -u cannot read bare $ip.
if ! is_resolvable_address "${ip:-}"; then
continue
fi
# NXDOMAIN / timeout: resolve_hostname returns 1. set -e
# cannot apply to this assignment (#291 C3).
# cannot apply to this assignment.
name=$(resolve_hostname "$ip") || name=
if [ -n "$name" ]; then
map_add "$ip" "$name"
@@ -751,7 +751,7 @@ run_selftest() {
return 1
fi
# Issue #190: hostname-shaped tokens of hex chars + dots/colons passed the
# Hostname-shaped tokens of hex chars + dots/colons passed the
# old char-class check and made the resolver fall back to upstream DNS. Each case
# below must REJECT (and would be ACCEPTED by the reverted char-class code).
if is_resolvable_address 'ab.cd.ef.01'; then
@@ -871,7 +871,7 @@ Address: 127.0.0.1:53")
run_logging_selftest || return 1
# Strict-mode smoke (#291 C3): status JSON uses soft-fail helpers
# Strict-mode smoke: status JSON uses soft-fail helpers
# (uci miss, empty WAN zone). Must not abort under dash/ash set -e.
_status=$(build_logging_status_json) || {
echo "build_logging_status_json: aborted under set -e" >&2
@@ -972,7 +972,7 @@ case "${1:-}" in
build_rules_map
;;
poll)
# rpcd may omit argv $3 and pass JSON on stdin (#291 C3).
# rpcd may omit argv $3 and pass JSON on stdin.
poll_logs "${3:-}"
;;
resolve)
+5 -3
View File
@@ -1,9 +1,9 @@
#
msgid ""
msgstr ""
"PO-Revision-Date: 2020-08-14 08:04+0000\n"
"Last-Translator: Andreas Dorfer <adorferen@gmail.com>\n"
"Language-Team: German <http://weblate.openmptcprouter.com/projects/omr/"
"luciapplicationsiperf/de/>\n"
"Language-Team: German <http://weblate.openmptcprouter.com/projects/omr/luciapplicationsiperf/de/>\n"
"Language: de\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
@@ -30,7 +30,7 @@ msgstr "Downstream"
#: luci-app-iperf/root/usr/share/rpcd/acl.d/luci-app-iperf.json:3
msgid "Grant UCI access for luci-app-iperf"
msgstr ""
msgstr "UCI-Zugriff für luci-app-iperf gewähren"
#: luci-app-iperf/luasrc/view/iperf/test.htm:114
msgid "Internet protocol"
@@ -60,6 +60,8 @@ msgstr "Server"
#: luci-app-iperf/luasrc/view/iperf/test.htm:165
msgid "Server VPS IP is bypassed, so this will test only default route speed."
msgstr ""
"Die IP des VPS-Servers wird umgangen, daher wird nur die Geschwindigkeit der"
" Standardroute getestet."
#: luci-app-iperf/luasrc/view/iperf/test.htm:102
msgid "Settings"
+5 -3
View File
@@ -1,9 +1,9 @@
#
msgid ""
msgstr ""
"PO-Revision-Date: 2020-09-21 12:51+0000\n"
"Last-Translator: Weblate Admin <contact@openmptcprouter.com>\n"
"Language-Team: Italian <http://weblate.openmptcprouter.com/projects/omr/"
"luciapplicationsiperf/it/>\n"
"Language-Team: Italian <http://weblate.openmptcprouter.com/projects/omr/luciapplicationsiperf/it/>\n"
"Language: it\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
@@ -28,7 +28,7 @@ msgstr "Scarica"
#: luci-app-iperf/root/usr/share/rpcd/acl.d/luci-app-iperf.json:3
msgid "Grant UCI access for luci-app-iperf"
msgstr ""
msgstr "Concedi l'accesso UCI a luci-app-iperf"
#: luci-app-iperf/luasrc/view/iperf/test.htm:114
msgid "Internet protocol"
@@ -58,6 +58,8 @@ msgstr "Server"
#: luci-app-iperf/luasrc/view/iperf/test.htm:165
msgid "Server VPS IP is bypassed, so this will test only default route speed."
msgstr ""
"L'IP del server VPS è escluso, quindi verrà testata solo la velocità della "
"rotta predefinita."
#: luci-app-iperf/luasrc/view/iperf/test.htm:102
msgid "Settings"
+5 -3
View File
@@ -1,9 +1,9 @@
#
msgid ""
msgstr ""
"PO-Revision-Date: 2020-08-21 20:21+0000\n"
"Last-Translator: Quentin PAGÈS <githubou@quentino.fr>\n"
"Language-Team: Occitan <http://weblate.openmptcprouter.com/projects/omr/"
"luciapplicationsiperf/oc/>\n"
"Language-Team: Occitan <http://weblate.openmptcprouter.com/projects/omr/luciapplicationsiperf/oc/>\n"
"Language: oc\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
@@ -28,7 +28,7 @@ msgstr "Telecargament"
#: luci-app-iperf/root/usr/share/rpcd/acl.d/luci-app-iperf.json:3
msgid "Grant UCI access for luci-app-iperf"
msgstr ""
msgstr "Acordar l'accès UCI a luci-app-iperf"
#: luci-app-iperf/luasrc/view/iperf/test.htm:114
msgid "Internet protocol"
@@ -58,6 +58,8 @@ msgstr "Servidor"
#: luci-app-iperf/luasrc/view/iperf/test.htm:165
msgid "Server VPS IP is bypassed, so this will test only default route speed."
msgstr ""
"L'IP del servidor VPS es contornejada, doncas aquò testarà solament la "
"velocitat de la rota per defaut."
#: luci-app-iperf/luasrc/view/iperf/test.htm:102
msgid "Settings"
+4 -4
View File
@@ -1,9 +1,9 @@
#
msgid ""
msgstr ""
"PO-Revision-Date: 2020-08-14 08:04+0000\n"
"Last-Translator: Andreas Dorfer <adorferen@gmail.com>\n"
"Language-Team: German <http://weblate.openmptcprouter.com/projects/omr/"
"luciapplicationsmail/de/>\n"
"Language-Team: German <http://weblate.openmptcprouter.com/projects/omr/luciapplicationsmail/de/>\n"
"Language: de\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
@@ -12,7 +12,7 @@ msgstr ""
#: luci-app-mail/root/usr/share/luci/menu.d/luci-app-mail.json:3
msgid "E-Mail"
msgstr ""
msgstr "E-Mail"
#: luci-app-mail/luasrc/model/cbi/mail.lua:34
msgid "From"
@@ -20,7 +20,7 @@ msgstr "Von"
#: luci-app-mail/root/usr/share/rpcd/acl.d/luci-app-mail.json:3
msgid "Grant UCI access for luci-app-mail"
msgstr ""
msgstr "UCI-Zugriff für luci-app-mail gewähren"
#: luci-app-mail/luasrc/controller/mail.lua:4
#: luci-app-mail/luasrc/model/cbi/mail.lua:4
+4 -4
View File
@@ -1,9 +1,9 @@
#
msgid ""
msgstr ""
"PO-Revision-Date: 2020-12-01 10:49+0000\n"
"Last-Translator: Giuseppe Dipierro <giuseppe.dipierro@uania.com>\n"
"Language-Team: Italian <http://weblate.openmptcprouter.com/projects/omr/"
"luciapplicationsmail/it/>\n"
"Language-Team: Italian <http://weblate.openmptcprouter.com/projects/omr/luciapplicationsmail/it/>\n"
"Language: it\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
@@ -12,7 +12,7 @@ msgstr ""
#: luci-app-mail/root/usr/share/luci/menu.d/luci-app-mail.json:3
msgid "E-Mail"
msgstr ""
msgstr "E-mail"
#: luci-app-mail/luasrc/model/cbi/mail.lua:34
msgid "From"
@@ -20,7 +20,7 @@ msgstr "Da"
#: luci-app-mail/root/usr/share/rpcd/acl.d/luci-app-mail.json:3
msgid "Grant UCI access for luci-app-mail"
msgstr ""
msgstr "Concedi l'accesso UCI a luci-app-mail"
#: luci-app-mail/luasrc/controller/mail.lua:4
#: luci-app-mail/luasrc/model/cbi/mail.lua:4
+6 -6
View File
@@ -1,9 +1,9 @@
#
msgid ""
msgstr ""
"PO-Revision-Date: 2020-08-21 20:21+0000\n"
"Last-Translator: Quentin PAGÈS <githubou@quentino.fr>\n"
"Language-Team: Occitan <http://weblate.openmptcprouter.com/projects/omr/"
"luciapplicationsmail/oc/>\n"
"Language-Team: Occitan <http://weblate.openmptcprouter.com/projects/omr/luciapplicationsmail/oc/>\n"
"Language: oc\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
@@ -12,7 +12,7 @@ msgstr ""
#: luci-app-mail/root/usr/share/luci/menu.d/luci-app-mail.json:3
msgid "E-Mail"
msgstr ""
msgstr "Corrièr electronic"
#: luci-app-mail/luasrc/model/cbi/mail.lua:34
msgid "From"
@@ -20,7 +20,7 @@ msgstr "De"
#: luci-app-mail/root/usr/share/rpcd/acl.d/luci-app-mail.json:3
msgid "Grant UCI access for luci-app-mail"
msgstr ""
msgstr "Acordar l'accès UCI a luci-app-mail"
#: luci-app-mail/luasrc/controller/mail.lua:4
#: luci-app-mail/luasrc/model/cbi/mail.lua:4
@@ -50,8 +50,8 @@ msgstr "Servidor"
#: luci-app-mail/luasrc/model/cbi/mail.lua:4
msgid "Set mail settings for services that need to send mails."
msgstr ""
"Definissètz los paramètres de messatjariá pels servicis que devon enviar d’e-"
"mails."
"Definissètz los paramètres de messatjariá pels servicis que devon enviar "
"d’e-mails."
#: luci-app-mail/luasrc/model/cbi/mail.lua:21
msgid "TLS"
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+1 -1
View File
@@ -8,7 +8,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-passwall
PKG_VERSION:=26.9.16
PKG_RELEASE:=277
PKG_RELEASE:=278
PKG_PO_VERSION:=$(PKG_VERSION)
PKG_CONFIG_DEPENDS:= \
@@ -1486,6 +1486,7 @@ function gen_config(var)
w = api.trim(w)
if w == "" or w:find("#") == 1 then return end
if w:find("rule-set:", 1, true) == 1 or w:find("rs:") == 1 then return end
if w:find("ext:", 1, true) == 1 then return end -- Rule currently not support ext:
table.insert(domains, w)
table.insert(domain_table.domain, w)
end)
@@ -1504,6 +1505,7 @@ function gen_config(var)
w = api.trim(w)
if w == "" or w:find("#") == 1 then return end
if w:find("rule-set:", 1, true) == 1 or w:find("rs:") == 1 then return end
if w:find("ext:", 1, true) == 1 then return end -- Rule currently not support ext:
table.insert(ip, w)
end)
if #ip == 0 then ip = nil end
+1 -1
View File
@@ -7,7 +7,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-passwall2
PKG_VERSION:=26.9.16
PKG_RELEASE:=126
PKG_RELEASE:=127
PKG_PO_VERSION:=$(PKG_VERSION)
PKG_CONFIG_DEPENDS:= \
@@ -1461,6 +1461,7 @@ function gen_config(var)
string.gsub(e.domain_list, '[^' .. "\r\n" .. ']+', function(w)
if w:find("#") == 1 then return end
if w:find("rule-set:", 1, true) == 1 or w:find("rs:") == 1 then return end
if w:find("ext:", 1, true) == 1 then return end -- Rule currently not support ext:
table.insert(domains, w)
table.insert(domain_table.domain, w)
end)
@@ -1483,6 +1484,7 @@ function gen_config(var)
string.gsub(e.ip_list, '[^' .. "\r\n" .. ']+', function(w)
if w:find("#") == 1 then return end
if w:find("rule-set:", 1, true) == 1 or w:find("rs:") == 1 then return end
if w:find("ext:", 1, true) == 1 then return end -- Rule currently not support ext:
table.insert(ip, w)
end)
if #ip == 0 then ip = nil end