Files
Tobias GesellchenandClaude Opus 5 b8427b0bbe feat(on-device): reach AfterTouch from the LAN without an SSH tunnel
On-device installs were only reachable through an SSH tunnel, and the
docs blamed it on the service binding loopback-only. That was wrong.

Some SoundTouch chassis carry a BCO ("SMSC") Wi-Fi/Bluetooth
co-processor, and inbound LAN traffic reaches the main Linux SoC only
for a fixed set of Bose's own service ports, a list that appears to be
compiled into the co-processor firmware. AfterTouch's :8000 was never
part of that design, so connections never arrive at the SoC at all.
Confirmed on an ST20: a port sweep from a LAN client showed Bose's
:82/:8080/:8090/:8091/:8200/:17000 all answering while :8000 failed,
and tcpdump on the speaker's own eth0 recorded zero packets for it.
Ruled out along the way: iptables (empty), nft/ebtables (absent), the
router, Wi-Fi isolation, and the binding itself (0.0.0.0 is correct).

The init script now redirects one of the relayed ports to AfterTouch,
so http://<speaker-ip>:17008 works with no tunnel. 17008 is Bose's
software-update listener, whose cloud no longer exists. Only external
traffic is matched, so anything on the speaker still reaches :8000 as
before. Auto-enabled only where has-bco reports the co-processor, and
configurable via AFTERTOUCH_LAN_PORT (auto/none/port) in
aftertouch.conf. The rule is re-applied on every start and removed on
stop and uninstall, so it needs no watchdog; unlike prior art it is not
pinned to the LAN IP, so it also survives DHCP changes.

Credit for the REDIRECT technique goes to the STR / SoundTouch Reborn
project, which documented and shipped it first.

Also de-hardcodes the service port, which was baked independently into
the daemon args, the readiness poll and status, and makes install.sh
print the speaker's real address instead of a <your-device-ip>
placeholder it never filled in.

Adds a model support matrix, since the repo had no per-model
compatibility record and this behaviour is entirely chassis-dependent.
Only the verified ST20 row is filled in; everything else is marked
unknown rather than inferred.

Verified on hardware: auto-detection, idempotency across restarts,
teardown and restore, persistence across a full reboot, and LAN access
returning the service's health JSON.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 15:54:00 +02:00

35 lines
1.2 KiB
Bash

#!/bin/sh
# Uninstall AfterTouch on-device. Handles both the historical
# layout (/opt/aftertouch as a directory) and the post-#268 layout
# (/opt/aftertouch as a symlink into /mnt/nv/aftertouch).
set -eu
/etc/init.d/aftertouch stop || true
# `stop` normally removes the LAN entry-port redirect. Repeat it directly in
# case the init script was already gone or failed, so no rule is left behind
# pointing at a service that no longer exists.
iptables -t nat -S PREROUTING 2>/dev/null \
| grep -- '--to-ports 8000' \
| sed 's/^-A /-D /' \
| while read -r rule; do
# shellcheck disable=SC2086
iptables -t nat $rule 2>/dev/null || true
done
rm -f /etc/init.d/aftertouch
update-rc.d -f aftertouch remove
# If /opt/aftertouch is a symlink, resolve it and remove the target
# before unlinking, so we don't leave ~12 MB of orphan binary on
# /mnt/nv. Tolerate either layout — readlink -f returns the same
# path for a real directory, and rm -rf on a missing path with
# set -eu would abort.
target="$(readlink -f /opt/aftertouch 2>/dev/null || echo /opt/aftertouch)"
if [ -e "$target" ]; then
rm -rf "$target"
fi
if [ -L /opt/aftertouch ] || [ -e /opt/aftertouch ]; then
rm -rf /opt/aftertouch
fi