diff --git a/docs/content/docs/analysis/ANONYMIZATION-SUMMARY.md b/docs/content/docs/analysis/ANONYMIZATION-SUMMARY.md index d2ed52e..564e7d6 100644 --- a/docs/content/docs/analysis/ANONYMIZATION-SUMMARY.md +++ b/docs/content/docs/analysis/ANONYMIZATION-SUMMARY.md @@ -42,7 +42,7 @@ pattern) but stay numeric for parsers that expect integer-looking IDs. An earlier anonymisation pass used `192.168.1.x` as its target. That range is RFC-1918 private space — perfectly valid on real networks, -which means a reader can't tell whether `192.168.1.10` is a +which means a reader can't tell whether `192.0.2.10` is a placeholder or a documented LAN address. RFC-5737 ranges fix that: because they're reserved for documentation only, any reader knows on sight that they don't represent a real device. diff --git a/docs/content/docs/analysis/BOSE-LAB-RUNBOOK.md b/docs/content/docs/analysis/BOSE-LAB-RUNBOOK.md index 0e849da..853b770 100644 --- a/docs/content/docs/analysis/BOSE-LAB-RUNBOOK.md +++ b/docs/content/docs/analysis/BOSE-LAB-RUNBOOK.md @@ -84,7 +84,7 @@ sudo tee /etc/systemd/network/08-wlan0.network << 'EOF' Name=wlan0 [Network] -Address=192.168.10.1/24 +Address=198.51.100.1/24 IPForward=yes ConfigureWithoutCarrier=yes DHCP=no @@ -104,7 +104,7 @@ sudo systemctl mask wpa_supplicant@wlan0 **Verify:** ```bash ip addr show wlan0 -# Expected: ONLY inet 192.168.10.1/24 (NO second DHCP IP) +# Expected: ONLY inet 198.51.100.1/24 (NO second DHCP IP) ``` --- @@ -151,9 +151,9 @@ sudo mv /etc/dnsmasq.conf /etc/dnsmasq.conf.bak sudo tee /etc/dnsmasq.conf << 'EOF' interface=wlan0 -dhcp-range=192.168.10.100,192.168.10.200,24h -dhcp-option=3,192.168.10.1 -dhcp-option=6,192.168.10.1 +dhcp-range=198.51.100.100,198.51.100.200,24h +dhcp-option=3,198.51.100.1 +dhcp-option=6,198.51.100.1 # DNS Upstream: custom server on localhost (adjust port if necessary) server=127.0.0.1#5353 # Example: custom server on port 5353 @@ -237,7 +237,7 @@ If you cannot see the `Bose-Lab` SSID on your phone: ```bash sudo nmcli device set wlan0 managed no ``` -7. **Ghost IP Conflict:** If `ip addr show wlan0` shows both `192.168.10.1` and another IP (like `192.0.2.x`), `hostapd` will fail. This is usually caused by NetworkManager managing the interface. Ensure you've run: +7. **Ghost IP Conflict:** If `ip addr show wlan0` shows both `198.51.100.1` and another IP (like `192.0.2.x`), `hostapd` will fail. This is usually caused by NetworkManager managing the interface. Ensure you've run: ```bash sudo nmcli device set wlan0 managed no # If the ghost IP is still there, remove it manually: @@ -259,13 +259,13 @@ If you haven't created a CA yet, follow **Appendix A** first. # Temporarily make reachable via HTTP for easy download: cd /etc/my-dns-ca/ python3 -m http.server 8080 -# → Reachable at http://192.168.10.1:8080/ca.crt +# → Reachable at http://198.51.100.1:8080/ca.crt ``` ### Install on Android 1. Connect phone to `Bose-Lab` -2. Open browser → `http://192.168.10.1:8080/ca.crt` +2. Open browser → `http://198.51.100.1:8080/ca.crt` 3. Download certificate 4. **Settings → Security → Credentials → Install CA Certificate** 5. Select certificate and confirm @@ -322,7 +322,7 @@ sudo tcpdump -i wlan0 -n 'not port 53' -w /tmp/bose-nodns.pcap # Traffic of a specific host only (filter by phone IP) # Read phone IP from dnsmasq.leases beforehand (see below) -sudo tcpdump -i wlan0 -n host 192.168.10.101 +sudo tcpdump -i wlan0 -n host 198.51.100.101 ``` ### Read SNI from TLS Traffic (without decryption) @@ -351,7 +351,7 @@ Transfer `.pcap` files from the Pi to the PC: ```bash # From the PC (scp) -scp pi@192.168.10.1:/tmp/bose-*.pcap ~/Desktop/ +scp pi@198.51.100.1:/tmp/bose-*.pcap ~/Desktop/ ``` **Important Wireshark Filters:** @@ -607,7 +607,7 @@ You can either configure the macOS system proxy manually or use `mitmproxy`'s au **Method 1: System Proxy (Manual)** 1. Go to **System Settings → Network → Wi-Fi → Details... → Proxies**. 2. Enable **HTTP Proxy** and **HTTPS Proxy**. -3. Set Server to your Pi's IP (`192.168.10.1`) and Port to `8080`. +3. Set Server to your Pi's IP (`198.51.100.1`) and Port to `8080`. 4. Click **OK** and **Apply**. **Method 2: mitmproxy Local Redirect (Automatic)** @@ -667,7 +667,7 @@ If the app uses **Certificate Pinning** (hardcoded hashes), even moving the CA t If the **Transparent AP** setup (Steps 1–6) is too complex or you are experiencing routing issues, you can use `mitmproxy` as a **Regular HTTP Proxy**. ### 1. How it works -In this mode, the Pi acts as a simple server on port 8080. You tell your phone's Wi-Fi settings to send all traffic to `192.168.10.1:8080`. +In this mode, the Pi acts as a simple server on port 8080. You tell your phone's Wi-Fi settings to send all traffic to `198.51.100.1:8080`. * **Pros:** No complex `nftables` or NAT rules required. * **Cons:** Many Android apps (and background processes) ignore system-wide proxy settings. **HTTPS still requires a trusted CA for decryption.** @@ -683,7 +683,7 @@ mitmproxy --listen-port 8080 1. Go to **Settings → Wi-Fi → Bose-Lab**. 2. Select **Modify Network** (or the "i" icon). 3. Set **Proxy** to **Manual**. -4. **Proxy hostname:** `192.168.10.1` +4. **Proxy hostname:** `198.51.100.1` 5. **Proxy port:** `8080` 6. Save and try to browse a site. @@ -706,7 +706,7 @@ go get github.com/google/gopacket go run scripts/extract-ws.go your_capture.pcap [filter_ip] # Example: Filter for a specific speaker's IP in WebSocket messages -go run scripts/extract-ws.go capture.pcap 192.168.100.1 +go run scripts/extract-ws.go capture.pcap 203.0.113.1 ``` ### 2. Manual Extraction with tshark @@ -889,5 +889,5 @@ pgrep -a tcpdump dig @127.0.0.1 -p 5353 global.api.bose.io # Check network connectivity from the phone (from the Pi) -ping 192.168.10.101 # Phone IP from dnsmasq.leases +ping 198.51.100.101 # Phone IP from dnsmasq.leases ``` diff --git a/docs/content/docs/appendix/device-lifecycle-and-power-on-enhancement.md b/docs/content/docs/appendix/device-lifecycle-and-power-on-enhancement.md index a0def55..e38833d 100644 --- a/docs/content/docs/appendix/device-lifecycle-and-power-on-enhancement.md +++ b/docs/content/docs/appendix/device-lifecycle-and-power-on-enhancement.md @@ -116,7 +116,7 @@ The system has distinct phases where device information is collected and enhance **Endpoint**: `POST /streaming/account/{accountId}/devices` **Request Format**: ```xml - + Living Room Speaker ``` diff --git a/docs/content/docs/concepts/spotify-overview.md b/docs/content/docs/concepts/spotify-overview.md index 87301e7..eda5652 100644 --- a/docs/content/docs/concepts/spotify-overview.md +++ b/docs/content/docs/concepts/spotify-overview.md @@ -91,7 +91,7 @@ that delegates to AfterTouch for these names). The implementation lives in > **IP-based `--server-url` is incompatible with OAuth (both Spotify and Amazon > Music).** The speaker's hostname construction appends `oauth` to the first -> label only, so `192.168.0.30` would produce `192oauth.168.0.30` — malformed, +> label only, so `192.0.2.30` would produce `192oauth.0.2.30` — malformed, > no DNS resolver will answer for it, and there is no clean workaround on the > AfterTouch side. **Use a real LAN hostname** before configuring Spotify or > Amazon Music. The Health-tab `oauth_target_reachable` check warns when this diff --git a/docs/content/docs/guides/SOUNDTOUCH-SERVICE.md b/docs/content/docs/guides/SOUNDTOUCH-SERVICE.md index fa0ae5f..211aaaf 100644 --- a/docs/content/docs/guides/SOUNDTOUCH-SERVICE.md +++ b/docs/content/docs/guides/SOUNDTOUCH-SERVICE.md @@ -390,7 +390,7 @@ Lists all discovered SoundTouch devices with their current status. ```json [ { - "device_id": "08DF1F0BA325", + "device_id": "AABBCCDDEE0A", "name": "Living Room Speaker", "ip_address": "192.0.2.100", "product_code": "SoundTouch 20", diff --git a/docs/content/docs/guides/TROUBLESHOOTING.md b/docs/content/docs/guides/TROUBLESHOOTING.md index 967e2ff..c70db26 100644 --- a/docs/content/docs/guides/TROUBLESHOOTING.md +++ b/docs/content/docs/guides/TROUBLESHOOTING.md @@ -1121,10 +1121,10 @@ In `vi`, find `block_remote_traffic()`. Press `i` to enter insert mode. After th first `done` line in that function, add: ``` -echo -A INPUT -i $IFACE -s 192.168.10.0/24 -j ACCEPT +echo -A INPUT -i $IFACE -s 192.0.2.0/24 -j ACCEPT ``` -Replace `192.168.10.0/24` with the subnet your AfterTouch host is on. Press `Esc`, +Replace `192.0.2.0/24` with the subnet your AfterTouch host is on. Press `Esc`, type `:wq`, press `Enter`, then reboot: ```bash diff --git a/docs/content/docs/reference/DEVICE-PAIRING-FLOW.md b/docs/content/docs/reference/DEVICE-PAIRING-FLOW.md index a4ab8d7..46a080a 100644 --- a/docs/content/docs/reference/DEVICE-PAIRING-FLOW.md +++ b/docs/content/docs/reference/DEVICE-PAIRING-FLOW.md @@ -71,7 +71,7 @@ All subsequent messages (except `selectLastWiFiSource`, see below) use this enve ``` `requestID` is a monotonically increasing integer per connection (client-side sequence). -`{device_id}` is the speaker's MAC address with colons removed (e.g. `08DF1F0BA325`). +`{device_id}` is the speaker's MAC address with colons removed (e.g. `AABBCCDDEE0A`). --- diff --git a/docs/content/docs/reference/FEATURE-MAPPING.md b/docs/content/docs/reference/FEATURE-MAPPING.md index 2e38a45..17c4b02 100644 --- a/docs/content/docs/reference/FEATURE-MAPPING.md +++ b/docs/content/docs/reference/FEATURE-MAPPING.md @@ -84,7 +84,7 @@ Device system settings: $ soundtouch-cli --host 192.0.2.100 analyze 🔍 Device Capability Analysis: - Device ID: 08DF1F0BA325 + Device ID: AABBCCDDEE0A Feature Coverage: 87% (13/15 features) Device Type: Premium SoundTouch Speaker (Full Feature Set) diff --git a/pkg/client/errors_test.go b/pkg/client/errors_test.go index 529271e..78ad640 100644 --- a/pkg/client/errors_test.go +++ b/pkg/client/errors_test.go @@ -13,7 +13,7 @@ import ( func TestClient_Post_ErrorsResponse(t *testing.T) { // Mock speaker error response errorXML := ` - + This version of SCM does not support spotify create account functionality. ` @@ -38,8 +38,8 @@ func TestClient_Post_ErrorsResponse(t *testing.T) { t.Fatalf("expected models.ErrorsResponse, got %T: %v", err, err) } - if errs.DeviceID != "08DF1F0BA325" { - t.Errorf("expected DeviceID 08DF1F0BA325, got %s", errs.DeviceID) + if errs.DeviceID != "AABBCCDDEE0A" { + t.Errorf("expected DeviceID AABBCCDDEE0A, got %s", errs.DeviceID) } if len(errs.Errors) != 1 { @@ -67,7 +67,7 @@ func TestClient_Post_ErrorsResponse(t *testing.T) { func TestClient_PostWithResponse_ErrorsResponse(t *testing.T) { // Mock speaker error response errorXML := ` - + This version of SCM does not support spotify create account functionality. ` diff --git a/pkg/client/example_test.go b/pkg/client/example_test.go index da9fd71..8d97e02 100644 --- a/pkg/client/example_test.go +++ b/pkg/client/example_test.go @@ -307,7 +307,7 @@ func ExampleClient_GetSupportedURLs_concept() { } // Expected output with a real device: - // Device 08DF1F0BA325 supports 103 endpoints + // Device AABBCCDDEE0A supports 103 endpoints // Core functionality: true // Multiroom support: true // Streaming support: true diff --git a/pkg/client/supported_urls_test.go b/pkg/client/supported_urls_test.go index c9667ed..712efbf 100644 --- a/pkg/client/supported_urls_test.go +++ b/pkg/client/supported_urls_test.go @@ -22,7 +22,7 @@ func TestClient_GetSupportedURLs(t *testing.T) { { name: "successful_supported_urls_retrieval", responseXML: ` - + @@ -50,7 +50,7 @@ func TestClient_GetSupportedURLs(t *testing.T) { `, expectedError: false, - expectedDeviceID: "08DF1F0BA325", + expectedDeviceID: "AABBCCDDEE0A", expectedURLCount: 25, expectedURLs: []string{ "/info", "/capabilities", "/supportedURLs", "/volume", "/bass", diff --git a/pkg/service/datastore/datastore_test.go b/pkg/service/datastore/datastore_test.go index acfce68..d7f727b 100644 --- a/pkg/service/datastore/datastore_test.go +++ b/pkg/service/datastore/datastore_test.go @@ -175,7 +175,7 @@ func TestListAllDevices(t *testing.T) { ds := NewDataStore(tempDir) account := "default" - deviceID := "BO5EBO5E-F00D-F00D-FEED-08DF1F0BA325" + deviceID := "BO5EBO5E-F00D-F00D-FEED-AABBCCDDEE0A" info := &models.ServiceDeviceInfo{ DeviceID: deviceID, diff --git a/pkg/service/handlers/handlers_marge_test.go b/pkg/service/handlers/handlers_marge_test.go index ef6aca1..937a277 100644 --- a/pkg/service/handlers/handlers_marge_test.go +++ b/pkg/service/handlers/handlers_marge_test.go @@ -300,13 +300,13 @@ func TestMargeAccountFullExcludesEmptyAmazonSource(t *testing.T) { account := "1000001" // First device (alphabetically): has Amazon in Sources.xml - firstDeviceID := "08DF1F0BA325" + firstDeviceID := "AABBCCDDEE0A" firstDir := filepath.Join(tempDir, "accounts", account, "devices", firstDeviceID) if err := os.MkdirAll(firstDir, 0755); err != nil { t.Fatalf("Failed to create first device dir: %v", err) } if err := os.WriteFile(filepath.Join(firstDir, "DeviceInfo.xml"), []byte(` - + Kitchen SoundTouch SoundTouch 20 scm diff --git a/pkg/service/marge/marge_test.go b/pkg/service/marge/marge_test.go index 095ca8e..3521a8e 100644 --- a/pkg/service/marge/marge_test.go +++ b/pkg/service/marge/marge_test.go @@ -89,7 +89,7 @@ func TestAccountFullToXML_Structure(t *testing.T) { ds := datastore.NewDataStore(tempDir) account := "1234567" - device := "08DF1F0BA325" + device := "AABBCCDDEE0A" // 1. Setup Device Info with Components info := &models.ServiceDeviceInfo{ @@ -183,14 +183,14 @@ func TestAccountFullToXML_Structure(t *testing.T) { } // Device structure - if !strings.Contains(xmlStr, ``) { + if !strings.Contains(xmlStr, ``) { t.Errorf("Expected device attribute deviceid, got %s", xmlStr) } if !strings.Contains(xmlStr, `Kitchen SoundTouch`) { t.Errorf("Expected Kitchen SoundTouch under device, got %s", xmlStr) } - if !strings.Contains(xmlStr, `08DF1F0BA325`) { - t.Errorf("Expected 08DF1F0BA325 under device, got %s", xmlStr) + if !strings.Contains(xmlStr, `AABBCCDDEE0A`) { + t.Errorf("Expected AABBCCDDEE0A under device, got %s", xmlStr) } if !strings.Contains(xmlStr, ``) { t.Errorf("Expected under device, got %s", xmlStr) @@ -217,8 +217,8 @@ func TestAccountFullToXML_Structure(t *testing.T) { if !strings.Contains(xmlStr, `1.2.3`) { t.Errorf("Expected firmware-version 1.2.3, got %s", xmlStr) } - if !strings.Contains(xmlStr, `08DF1F0BA325`) { - t.Errorf("Expected 08DF1F0BA325 under attachedProduct, got %s", xmlStr) + if !strings.Contains(xmlStr, `AABBCCDDEE0A`) { + t.Errorf("Expected AABBCCDDEE0A under attachedProduct, got %s", xmlStr) } if !strings.Contains(xmlStr, ``) { t.Errorf("Expected under attachedProduct, got %s", xmlStr) diff --git a/pkg/service/marge/repro_test.go b/pkg/service/marge/repro_test.go index c1b1d6f..460c924 100644 --- a/pkg/service/marge/repro_test.go +++ b/pkg/service/marge/repro_test.go @@ -93,16 +93,16 @@ func TestReproduceMissingName(t *testing.T) { accountID := "1234567" // Create device folders - // 08DF1F0BA325 (has name) + // AABBCCDDEE0A (has name) // 001122334455 (missing name in full_local.xml) - // Device 1: 08DF1F0BA325 - dev1Dir := filepath.Join(tempBaseDir, "accounts", accountID, "devices", "08DF1F0BA325") + // Device 1: AABBCCDDEE0A + dev1Dir := filepath.Join(tempBaseDir, "accounts", accountID, "devices", "AABBCCDDEE0A") err = os.MkdirAll(dev1Dir, 0755) if err != nil { t.Fatal(err) } - dev1Info := ` + dev1Info := ` Kitchen SoundTouch SoundTouch 20 @@ -229,10 +229,10 @@ func TestReproduceMissingName(t *testing.T) { for _, d := range resp.Devices { t.Logf("Checking device in response: ID=%s, Name='%s'\n", d.DeviceID, d.Name) - if d.DeviceID == "08DF1F0BA325" { + if d.DeviceID == "AABBCCDDEE0A" { found08 = true if d.Name == "" { - t.Error("Device 08DF1F0BA325 name should not be empty") + t.Error("Device AABBCCDDEE0A name should not be empty") } } if d.DeviceID == "001122334455" || d.DeviceID == "I6332527703739342000020" { @@ -243,7 +243,7 @@ func TestReproduceMissingName(t *testing.T) { } if !found08 { - t.Error("Device 08DF1F0BA325 not found in response") + t.Error("Device AABBCCDDEE0A not found in response") } if !foundA8 { t.Error("Device 001122334455 not found in response") @@ -334,7 +334,7 @@ func TestSyncSourcesAttributes(t *testing.T) { xmlData := ` - + test-playlist @@ -405,7 +405,7 @@ func TestSyncSourcesAttributes(t *testing.T) { } // Check datastore - presets, err := ds.GetPresets("1234567", "08DF1F0BA325") + presets, err := ds.GetPresets("1234567", "AABBCCDDEE0A") if err != nil { t.Fatal(err) } @@ -419,7 +419,7 @@ func TestSyncSourcesAttributes(t *testing.T) { t.Errorf("Synced preset source ID mismatch: expected 10863533, got '%s'", lp.SourceID) } - recents, err := ds.GetRecents("1234567", "08DF1F0BA325") + recents, err := ds.GetRecents("1234567", "AABBCCDDEE0A") if err != nil { t.Fatal(err) } diff --git a/pkg/service/setup/setup_test.go b/pkg/service/setup/setup_test.go index 7a1a186..c519075 100644 --- a/pkg/service/setup/setup_test.go +++ b/pkg/service/setup/setup_test.go @@ -280,14 +280,14 @@ func TestGetLiveDeviceInfo(t *testing.T) { w.Header().Set("Content-Type", "application/xml") _, _ = fmt.Fprint(w, ` - + Test Speaker SoundTouch 20 SCM 19.0.5 - 08DF1F0BA325 + AABBCCDDEE0A `) @@ -313,8 +313,8 @@ func TestGetLiveDeviceInfo(t *testing.T) { t.Errorf("Expected SoftwareVer '19.0.5', got '%s'", info.SoftwareVer) } - if info.SerialNumber != "08DF1F0BA325" { - t.Errorf("Expected SerialNumber '08DF1F0BA325', got '%s'", info.SerialNumber) + if info.SerialNumber != "AABBCCDDEE0A" { + t.Errorf("Expected SerialNumber 'AABBCCDDEE0A', got '%s'", info.SerialNumber) } } @@ -1441,7 +1441,7 @@ func TestBackupConfigOffDevice(t *testing.T) { m := NewManager("http://localhost:8000", ds, nil) - serial := "08DF1F0BA325" + serial := "AABBCCDDEE0A" accountID := "1000001" // Mock info server