github Leadaxe/LxBox v2.18.2
LxBox v2.18.2

latest releases: v2.25.20, v2.25.19, v2.25.19-rc.3...
3 months ago

L×Box v2.18.2

A DNS group set to any channel other than "direct" would not let the tunnel
start at all. Plus import fixes for WebSocket subscriptions, and the story of
one fix that turned out to be wrong and was reverted after measuring it on a
device.

DNS-группа с любым каналом, кроме «direct», не давала туннелю стартовать
вовсе. Плюс исправления импорта WebSocket-подписок и история одного фикса,
который оказался неверным и был откачен после замера на устройстве.

Core / Ядро: sing-box-lx v1.14.0-lx.17-rc.3 (was / было lx.17-rc.1).


🇬🇧 English

🐛 Fixed

🛡️ A DNS group on any channel but "direct" broke the start

Switching a DNS group's outbound from "direct" to a proxy channel made
"start" fail with an error on the home screen.

A DNS group has no transport of its own — it does not reach the network, its
members do, each with its own detour. The core encodes exactly that: the
GroupDNSServerOptions struct has no detour field, and sing-box rejects a
config with an unknown field, so the tunnel would not come up at all.

The editor, however, drew the "Outbound (detour)" picker for every inline
server without telling a group apart. Picking a channel wrote the key into the
config, and the core refused it on start. "direct" appeared to work only
because that key is stripped as a default anyway.

The picker is now hidden for groups, and the key is stripped on build for any
group — including configs already saved with it.

🔗 WebSocket early data in the ?ed=&eh= form was lost on import

Some subscription generators put WebSocket early data in flat query
parameters — ?ed=2560&eh=Sec-WebSocket-Protocol — instead of the path tail
(path=/x?ed=2560) that the parser already knew how to read. Both were
dropped.

Losing eh is what broke the connection: with an empty
early_data_header_name the core appends the payload to the path, while a
server that asked for eh=Sec-WebSocket-Protocol expects it in the header —
so the client sent a garbage path and got a 404. The path tail still wins when
both forms are present; eh without ed is ignored, since the core enables
early data by size, not by header name. Xray JSON (wsSettings.ed / .eh) is
read too.

🔤 Double percent-encoded paths gave a 404

Aggregators hand out path=%2F%252Fassignment. Query parsing decodes exactly
once, so /%2Fassignment went into the config instead of //assignment, and
the server answered 404. The residue is now stripped — before the ?ed= tail
is cut, so a doubly-encoded tail is still recognised. Paths are not otherwise
touched: emoji, // and @ are all legal.

🔄 Reverted

🎭 ECH from subscriptions is no longer applied

An earlier build of this release started honouring the ech= parameter, on
the assumption that dropping it silently was a loss. Measuring on a device
showed the opposite — it killed nodes that had been working.

The Xray form ech=<name>+<resolver> carries no ECH key. It says "fetch the
config list from the DNS HTTPS record of <name>", and the key so obtained
belongs to that name. Subscriptions point it at public ECH probes: DNS
returns the very same config list for ip.gs and encryptedsni.com, decoding
to public_name = cloudflare-ech.com, while the node's SNI is something else
entirely. Encrypting the ClientHello with a stranger's key cannot work.

Suitability cannot be checked before connecting, and sing-box has no fallback
to plain TLS. So the parameter is ignored — and, unlike before, the node now
carries a note saying so. echfq is not read at all: its counterpart in the
core is marked legacy and rejects the whole config.

An ALPN filter from the same build was reverted too. It stripped h2/h3 for
WebSocket transports on the theory that HTTP/2 negotiation breaks the upgrade —
a theory that was never measured, and such links usually list http/1.1
alongside anyway. The config now follows the link.

🧠 Core

Updated to v1.14.0-lx.17-rc.3 — report archives no longer grow without bound
(one device had accumulated 427 MB of OOM and crash snapshots over 19 days),
Endpoint.Close() reports tun-device close failures again instead of
swallowing them, and 240 upstream commits are merged. The Java API is
unchanged — verified by diffing the AAR before the build.

🇷🇺 Русский

🐛 Исправлено

🛡️ DNS-группа на любом канале, кроме «direct», ломала старт

Стоило переключить outbound DNS-группы с «direct» на прокси-канал, как
«старт» падал с ошибкой на главном экране.

У DNS-группы нет своего транспорта — в сеть ходит не она, а её участники,
каждый со своим detour. Ядро это и кодирует: в структуре
GroupDNSServerOptions поля detour нет, а sing-box падает на неизвестном
поле, так что туннель не поднимался вовсе.

Форма редактора при этом рисовала пикер «Outbound (detour)» для любого
inline-сервера, не отличая группу. Выбранный канал уходил в конфиг, и ядро
отвергало его на старте. «direct» работал только потому, что этот ключ и так
стирается как значение по умолчанию.

Теперь пикер для групп скрыт, а ключ вычищается на сборке конфига для любой
группы — включая уже сохранённые с ним.

🔗 Early data в форме ?ed=&eh= терялась при импорте

Часть генераторов подписок кладёт WebSocket early data в плоские параметры —
?ed=2560&eh=Sec-WebSocket-Protocol — вместо хвоста пути
(path=/x?ed=2560), который парсер уже умел читать. Терялись оба.

Ломала подключение именно потеря eh: при пустом early_data_header_name
ядро дописывает данные в путь, тогда как сервер с
eh=Sec-WebSocket-Protocol ждёт их в заголовке — клиент слал мусорный путь и
получал 404. Хвост пути по-прежнему в приоритете, если заданы обе формы; eh
без ed игнорируется, поскольку ядро включает early data по размеру, а не по
имени заголовка. Xray JSON (wsSettings.ed / .eh) тоже читается.

🔤 Путь с двойным percent-кодированием давал 404

Агрегаторы отдают path=%2F%252Fassignment. Разбор query декодирует ровно
один раз, поэтому в конфиг уходило /%2Fassignment вместо //assignment, и
сервер отвечал 404. Остаток кодирования теперь снимается — до срезки хвоста
?ed=, так что дважды закодированный хвост по-прежнему распознаётся. В
остальном путь не трогаем: эмодзи, // и @ легальны.

🔄 Откачено

🎭 ECH из подписок больше не применяется

Более ранняя сборка этого релиза начала учитывать параметр ech=, исходя из
того, что молчаливое отбрасывание — это потеря. Замер на устройстве показал
обратное: параметр убивал работавшие узлы.

Xray-форма ech=<name>+<resolver> не несёт ECH-ключа. Она означает «возьми
список конфигураций из DNS HTTPS-записи имени <name>», и полученный ключ
принадлежит этому имени. Подписки указывают там публичные ECH-пробники: DNS
отдаёт один и тот же список для ip.gs и encryptedsni.com, а декодируется
он в public_name = cloudflare-ech.com — тогда как SNI узла совсем другой.
Шифровать ClientHello чужим ключом бессмысленно.

Проверить пригодность до подключения нельзя, а отката на обычный TLS в
sing-box нет. Поэтому параметр игнорируется — но, в отличие от прежнего
поведения, у узла теперь появляется соответствующая пометка. echfq не
читается вовсе: парная опция ядра помечена как legacy и роняет весь конфиг.

Фильтр ALPN из той же сборки тоже откачен. Он срезал h2/h3 для
WebSocket-транспортов, исходя из того, что согласование HTTP/2 ломает
апгрейд, — эта версия не проверялась замером, а в таких ссылках рядом обычно
указан и http/1.1. Теперь конфиг следует ссылке.

🧠 Ядро

Обновлено до v1.14.0-lx.17-rc.3 — архив отчётов больше не растёт без границ
(на одном устройстве за 19 дней накопилось 427 МБ OOM- и crash-снимков),
Endpoint.Close() снова сообщает об ошибке закрытия tun-устройства вместо
того, чтобы её проглотить, и влиты 240 upstream-коммитов. Java-API не
изменился — сверено диффом AAR перед сборкой.


📲 Install

adb install -r LxBox-v2.18.2-arm64-v8a.apk

No uninstall needed — installs over the existing app, settings and
subscriptions are preserved.

Без uninstall! Поверх существующей установки. Настройки и подписки сохранятся.


Previous release / Предыдущий релиз: v2.18.1.

Don't miss a new LxBox release

NewReleases is sending notifications on new releases.