Dual-mode session navigation for TerminalActivity:
- Top bar mode: 36dp tab strip with scrollable session tabs (teal accent),
SFTP tab (amber), + popup menu (New session / Open SFTP / Switch to drawer)
- Drawer mode: 260dp left DrawerLayout with session list (teal dot indicators),
SFTP section, hamburger button on quick bar, footer to switch back
- In-session switching via popup/drawer footer; global default in Settings
- Session switching rebinds TerminalSurfaceView.screenBuffer to target session
New files:
- TerminalSessionNav.kt — tab bar, drawer, session switching, hamburger button
- SftpActivity.kt — stub ("SFTP — coming soon"), registered in manifest
- SessionNavTest.kt — 8 unit tests for NavMode enum and label truncation
Settings:
- Session navigation preference (Top bar / Drawer) in Appearance section
- Show session tab bar toggle (when top bar mode selected)
- sessionNavStyle + showSessionTabBar in TerminalPreferences + SettingsViewModel
Also: rewrote docs/SOCKS5_TEST.md to use adb forward from dev machine
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
4.7 KiB
SOCKS5 Dynamic Port Forwarding — Manual Test Guide
All tests run from your dev machine terminal, not from adb shell. The SOCKS5
proxy listens on the Android device's loopback; adb forward bridges it to your
machine's loopback so standard tools (curl, browsers) work directly.
Prerequisites
- SSH Workbench installed on the device (debug build).
- A saved SSH connection with a DYNAMIC port forward:
- Type: Dynamic (SOCKS5)
- Local port: 1080
- Bind address: 127.0.0.1
- Open the connection in the app — the terminal should be live.
- Bridge the port to your dev machine (once per ADB session):
adb forward tcp:1080 tcp:1080
- Verify the forward is active:
adb forward --list
Expected output includes a line like:
22160523026079 tcp:1080 tcp:1080
If it shows nothing, the forward wasn't set up — re-run step 4.
You can also confirm the proxy is listening on-device:
adb shell netstat -tlnp | grep 1080
Should show LISTEN on 127.0.0.1:1080.
Test 1: Basic HTTP
curl --socks5-hostname 127.0.0.1:1080 http://example.com
Expected: HTML content from example.com.
Failure: curl: (7) Failed to connect to 127.0.0.1 port 1080 means either
the app isn't connected, the DYNAMIC forward isn't configured, or adb forward
wasn't run. Check the terminal for a red "Port forward failed" banner.
Test 2: HTTPS — verify tunnel routing
curl --socks5-hostname 127.0.0.1:1080 https://httpbin.org/ip
Expected: JSON showing the SSH server's public IP, not your dev machine's IP. This confirms traffic is routed through the SSH tunnel, not sent directly.
Test 3: DNS-through-tunnel
The --socks5-hostname flag sends the domain name to the SOCKS5 proxy, which
resolves it on the SSH server side. This avoids DNS leaks.
# DNS resolved on the SSH server (no local DNS leak):
curl --socks5-hostname 127.0.0.1:1080 https://example.com
# For comparison — DNS resolved locally, only TCP tunneled:
curl --socks5 127.0.0.1:1080 https://example.com
Both should succeed. Use --socks5-hostname in production to prevent DNS leaks.
Test 4: Concurrent connections
Browsers open 6+ parallel connections. Verify the proxy handles them:
curl --socks5-hostname 127.0.0.1:1080 http://example.com &
curl --socks5-hostname 127.0.0.1:1080 https://httpbin.org/ip &
wait
Expected: Both complete without either one hanging. If the second request blocks until the first finishes, the proxy has a concurrency bug.
Test 5: RFC 1929 username/password auth
Some clients (Proxifier, strict browsers) require SOCKS5 username/password auth. The proxy accepts any credentials — the SSH session is the real auth boundary.
curl --socks5-hostname --proxy-user anyuser:anypass 127.0.0.1:1080 https://example.com
Expected: Same HTML as Test 1. The credentials are accepted and ignored.
Failure: curl: (97) Can't complete SOCKS5 connection means RFC 1929
negotiation is broken.
Test 6: Connection refused error
Request a port that nothing is listening on (on the SSH server's side):
curl --socks5-hostname 127.0.0.1:1080 http://127.0.0.1:9999
Expected: curl reports an error (SOCKS5 reply code 0x05 — connection refused). The SSH session should remain active and usable for subsequent requests.
Test 7: Unknown host error
Request a domain that doesn't resolve:
curl --socks5-hostname 127.0.0.1:1080 http://thishostdoesnotexist.invalid
Expected: curl reports an error (SOCKS5 reply code 0x04 — host unreachable). The SSH session should remain active.
Cleanup
Remove the ADB port forward when done testing:
adb forward --remove tcp:1080
Verify it's gone:
adb forward --list
Teardown note
Disconnecting the SSH session in the app closes the SOCKS5 proxy's ServerSocket.
After disconnect, adb forward still exists but curl will get "Connection refused"
because there's nothing listening on port 1080 on the device anymore. Run
adb forward --remove tcp:1080 to clean up the stale forward.
Troubleshooting
| Symptom | Likely cause |
|---|---|
Failed to connect to 127.0.0.1 port 1080 |
adb forward not set, or app not connected |
Connection refused after successful setup |
SSH session disconnected — proxy closed |
| Second request hangs until first completes | Sequential accept loop — concurrency bug |
Can't complete SOCKS5 connection with --proxy-user |
RFC 1929 auth negotiation broken |
| Truncated downloads | Half-close not implemented correctly |
| Slow throughput | Relay buffer too small (should be 64 KB) |