L×Box v2.20.4
The VPN core is updated. Three crashes that killed the app outright are gone,
node checks no longer hang forever on a silent server, and the core is back in
step with upstream sing-box after two months of drift. Nothing in the app
itself changed — the entire release is the core underneath.
Обновлено VPN-ядро. Ушли три краша, при которых приложение падало целиком; All three came from the field with crash dumps attached, and all three are Switching WiFi↔mobile while connecting killed the app. Whenever the default A connection to a dead node could take down the whole tunnel. A TCP The tunnel could crash on start. This one was introduced and fixed between Some servers accept a connection and then go quiet: the handshake is never Three things had to coincide, so all three were fixed: connections of that The fork had drifted two months behind SagerNet's sing-box. That gap is now Everything the fork adds is intact: the detour chain, the DNS query stream, OpenVPN and OpenConnect arrived upstream and are compiled in Two lessons were written into the core's release procedure, both paid for:
Two places in the core kept breaking in the same way on every upstream merge — The Java surface of the core did not move across this bump: 226 classes at every Все три пришли с поля вместе с крашдампами, и все три старше того релиза, Смена WiFi↔мобильной сети в момент подключения роняла приложение. При Соединение с мёртвой нодой могло уронить весь туннель. TCP-соединение, Туннель мог падать при запуске. Этот дефект появился и был исправлен между Некоторые серверы принимают соединение и замолкают: рукопожатие остаётся без Совпасть должны были три вещи, поэтому исправлены все три: соединения этого Форк отстал от sing-box на два месяца. Отставание закрыто полностью: 217 Всё, что добавляет форк, на месте: цепочка detour, поток DNS-запросов, пул OpenVPN и OpenConnect появились в upstream и включены в сборку В процедуру выпуска ядра записаны два урока, оба оплаченных:
Два места в ядре ломались одинаково при каждом слиянии с upstream — дважды Java-поверхность ядра при этом бампе не сдвинулась: 226 классов на каждом шаге,
проверки нод больше не залипают навсегда на молчащем сервере; ядро снова в ногу
с upstream sing-box после двух месяцев отставания. В самом приложении не
изменилось ничего — весь релиз это ядро под ним.
🇬🇧 English
🐛 Fixed — three crashes
older than the release that fixes them.
network changes, the app performs a routine "reset network" action — that is
what keeps connections alive as you walk out of WiFi range. It was allowed to
run as soon as the core object existed, but a tunnel needs a moment longer to
finish assembling, and during that moment the parts it reaches for are not
there yet. A network change landing inside that window — a few hundred
milliseconds around tunnel start, wider when the core was opening its cache
file — killed the process. Tapping connect at the exact moment the network
switched was enough. Such commands now wait for the tunnel to be genuinely up
and are ignored before that; on a running tunnel behaviour is unchanged.
connection that never completes is an everyday event: subscriptions accumulate
dead nodes, latency tests probe them, a reconnect retries them all at once.
Under a narrow race, cleaning up such a connection released part of its state a
fraction of a second before the connection was formally closed — and a packet
arriving in that gap reached code that assumed the state was still there. The
result was not one dropped connection but a dead VPN. It struck rarely and
unpredictably, and grew far more likely exactly when many connections were
failing at once. A late packet is now ignored instead of crashing.
pre-releases, so no shipped version carried it — it is named here because it
explains the release procedure change below.
📡 Fixed — "the ping is lost"
answered, but nothing is refused either. A node check that ran into one simply
stopped, with no way to interrupt it — and stopping the VPN did not clear it.
The check kept running in the background, still holding the whole node list it
had started with; the reported dump had two of them alive, aged 100 and 43
minutes, holding 2806 nodes from a subscription that was no longer even loaded.
Because a group waits for all its checks before publishing results, the ping
column stayed empty, and memory climbed until the system started shooting the
app down. Restarting the VPN did not help — only rebuilding the config did.
transport type now support timeouts at all, cancelling a check now reaches the
handshake it is stuck in, and stopping a group now stops the checks it started.
Nodes that are merely slow are unaffected — the timeout ends only a check that
has genuinely stalled.
🔄 Changed — the core is back in step with upstream
closed completely: 217 commits, base moved to v1.14.0-beta.8, every
dependency on the exact revision upstream expects. Visible in daily use: a
deadlock in the TUN packet dispatcher, more consistent URLTest results, a race
in DNS rule matching where one rule could block another, several WireGuard
corrections, and refreshed gvisor/QUIC. Where upstream had since solved
something this fork had solved on its own, the upstream version was preferred —
but only after checking it covered the whole case.
URLTest pooling, idle-suspend, AmneziaWG obfuscation, and all eight extended
client commands.
(openvpn-client, openconnect). These are new — the fork has no field
experience with them. Treat them as untested until you have run them yourself.
🔧 Process
expects, and its history is taken whole — never cherry-picked by what the
compiler happens to complain about. Taking 3 of 14 commits that way produced
a combination that never existed upstream, and a tunnel that crashed on
start.
twice in a row, invisibly, caught only by the build and by tests. The cause was
not upstream removing anything: that code does not exist on their side. It was
the shape of our own additions, each sitting where both sides append, so the
merge tool combined them into something neither side wrote, without reporting a
conflict. Both were restructured rather than patched again, so a future
upstream change collides loudly instead of being absorbed silently.
🧪 Verification
step, javap diff of every signature — 0 differing lines, both rc.6→rc.7
and rc.7→rc.8. No client-side change was required. flutter analyze is
clean, 2947 tests pass, all four l10n checkers pass. The build was verified on
device on rc.7; rc.8 on top of it is an internal restructuring with no
behaviour change.
🇷🇺 Русский
🐛 Исправлено — три краша
который их чинит.
каждой смене сети по умолчанию приложение выполняет штатное действие «сброс
сети» — именно оно сохраняет соединения живыми, когда вы выходите из зоны WiFi.
Ему разрешалось выполняться, как только появлялся объект ядра, — но туннелю
нужно ещё время, чтобы собраться до конца, и в эти мгновения тех частей, к
которым действие обращается, ещё не существует. Смена сети, попавшая в это окно
(несколько сотен миллисекунд вокруг старта, шире — когда ядро открывает свой
файл кеша), убивала процесс. Достаточно было нажать «подключить» ровно в момент
переключения сети. Теперь такие команды дожидаются, пока туннель действительно
поднимется, а до того игнорируются; на работающем туннеле поведение прежнее.
которое так и не устанавливается, — событие ежедневное: в подписках копятся
мёртвые ноды, их простукивают тесты задержки, переподключение пробует их все
разом. В узкой гонке при уборке такого соединения часть его состояния
освобождалась на доли секунды раньше, чем соединение формально закрывалось, — и
пришедший в этот зазор пакет попадал в код, считавший, что состояние ещё на
месте. Итогом был не разрыв одного соединения, а мёртвый VPN. Срабатывало редко
и непредсказуемо, а вероятность росла ровно тогда, когда неудачных соединений
было много сразу. Опоздавший пакет теперь игнорируется, а не роняет ядро.
пререлизами, так что ни в одну выпущенную версию не попал, — назван здесь
потому, что объясняет изменение процедуры ниже.
📡 Исправлено — «утерялся пинг»
ответа, но и отказа нет. Проверка ноды, наткнувшаяся на такой сервер, просто
останавливалась, и прервать её было нечем — а остановка VPN её не убирала.
Проверка продолжала жить в фоне, держа весь список нод, с которым начинала: в
присланном дампе их было две, возрастом 100 и 43 минуты, на 2806 нод от
подписки, которая уже даже не была загружена. Группа перед публикацией
результатов ждёт завершения всех своих проверок — оттого колонка пинга
оставалась пустой, а память росла до того, что система начинала убивать
приложение. Перезапуск VPN не помогал, лечил только пересбор конфига.
типа транспорта теперь вообще умеют таймауты, отмена проверки достигает
рукопожатия, в котором та застряла, а остановка группы останавливает и
запущенные ею проверки. На просто медленные ноды это не влияет — таймаут
завершает только по-настоящему зависшую проверку.
🔄 Изменено — ядро снова в ногу с upstream
коммитов, база переставлена на v1.14.0-beta.8, все зависимости — ровно на тех
ревизиях, которых ждёт upstream. Из заметного в повседневной работе:
взаимоблокировка в диспетчере пакетов TUN, более стабильные результаты URLTest,
гонка в сопоставлении DNS-правил, где одно правило могло блокировать другое,
несколько исправлений WireGuard, обновлённые gvisor и QUIC. Там, где upstream к
этому времени исправил то же, что форк решал сам, предпочтён вариант upstream —
но только после проверки, что он покрывает случай целиком.
URLTest, засыпание простаивающих подключений, обфускация AmneziaWG и все восемь
расширенных клиентских команд.
(openvpn-client, openconnect). Это новинка, полевого опыта с ней у форка
нет — считайте её непроверенной, пока не прогоните сами.
🔧 Процесс
её история берётся целиком — без выборки по тому, на что ругается
компилятор. Взятые так 3 коммита из 14 дали сочетание, которого у upstream
никогда не было, и туннель, падающий при старте.
подряд, незаметно, ловили только сборка и тесты. Причина не в том, что upstream
что-то удалял: этого кода у него нет. Ломалась форма наших добавок — обе
сидели там, где дописывают обе стороны, и merge склеивал их в то, чего не писал
никто, не сообщая о конфликте. Оба места перестроены, а не залатаны ещё раз:
теперь будущее изменение upstream столкнётся с ними громко, а не поглотит
молча.
🧪 Проверка
javap-diff всех сигнатур — 0 строк расхождений, и на rc.6→rc.7, и на
rc.7→rc.8. Правок на стороне клиента не потребовалось. flutter analyze
чист, 2947 тестов проходят, все четыре l10n-чекера зелёные. Сборка проверена на
устройстве на rc.7; rc.8 поверх него — внутренняя перестройка без изменения
поведения.
Install / Установка
adb install -r LxBox-v2.20.4-arm64-v8a.apkБез uninstall! Поверх существующей установки. Настройки и подписки сохранятся.
No uninstall needed — install over the existing one. Settings and
subscriptions are preserved.
Previous release / Предыдущий релиз: v2.20.3.