fix 0x3400 parsing to use offset-based string references
This commit is contained in:
@@ -545,73 +545,34 @@ func (t *Tendrils) queryDanteSubscriptions3400(conn *net.UDPConn, ip net.IP, rxC
|
||||
log.Printf("[dante] %s: 0x3400 page %d: found %d records", ip, pageNum, recordCount)
|
||||
}
|
||||
|
||||
lastDeviceName := ""
|
||||
for i := 0; i < recordCount; i++ {
|
||||
offsetPos := 18 + i*2
|
||||
if offsetPos+2 > len(resp) {
|
||||
break
|
||||
}
|
||||
rawOffset := int(binary.BigEndian.Uint16(resp[offsetPos : offsetPos+2]))
|
||||
recordOffset := rawOffset - 30
|
||||
if recordOffset < 0 || recordOffset+24 >= len(resp) {
|
||||
if rawOffset+48 > len(resp) {
|
||||
continue
|
||||
}
|
||||
|
||||
txChOffset := int(binary.BigEndian.Uint16(resp[rawOffset+44 : rawOffset+46]))
|
||||
txDevOffset := int(binary.BigEndian.Uint16(resp[rawOffset+46 : rawOffset+48]))
|
||||
|
||||
if txChOffset == 0 && txDevOffset == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
var txDeviceName, txChannelName string
|
||||
firstByte := resp[recordOffset]
|
||||
if firstByte >= 0x20 && firstByte < 0x7f {
|
||||
txChannelName = extractNullTerminatedString(resp, recordOffset)
|
||||
txDeviceName = extractNullTerminatedString(resp, recordOffset+len(txChannelName)+1)
|
||||
} else {
|
||||
stringStart := -1
|
||||
for j := recordOffset + 10; j < recordOffset+24 && j+3 < len(resp); j++ {
|
||||
if resp[j] == 0x02 && resp[j+1] == 0x02 && resp[j+2] == 0x00 && resp[j+3] == 0x00 {
|
||||
stringStart = j + 4
|
||||
break
|
||||
}
|
||||
}
|
||||
if stringStart >= 0 && stringStart < len(resp) {
|
||||
str1 := extractNullTerminatedString(resp, stringStart)
|
||||
str2 := extractNullTerminatedString(resp, stringStart+len(str1)+1)
|
||||
hasLetter := func(s string) bool {
|
||||
for _, c := range s {
|
||||
if (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
if !hasLetter(str1) && !hasLetter(str2) {
|
||||
continue
|
||||
}
|
||||
noDeviceIndicator := stringStart >= 6 && resp[stringStart-6] == 0x00 && resp[stringStart-5] == 0x00
|
||||
str2IsNumeric := len(str2) > 0
|
||||
for _, c := range str2 {
|
||||
if c < '0' || c > '9' {
|
||||
str2IsNumeric = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if noDeviceIndicator && str2IsNumeric && lastDeviceName != "" {
|
||||
txChannelName = str1
|
||||
txDeviceName = lastDeviceName
|
||||
} else if str2IsNumeric {
|
||||
txDeviceName = str1
|
||||
txChannelName = str2
|
||||
} else if hasLetter(str2) {
|
||||
txDeviceName = str2
|
||||
txChannelName = str1
|
||||
} else {
|
||||
txChannelName = str1
|
||||
txDeviceName = lastDeviceName
|
||||
}
|
||||
}
|
||||
if txChOffset > 0 && txChOffset < len(resp) {
|
||||
txChannelName = extractNullTerminatedString(resp, txChOffset)
|
||||
}
|
||||
if txDevOffset > 0 && txDevOffset < len(resp) {
|
||||
txDeviceName = extractNullTerminatedString(resp, txDevOffset)
|
||||
}
|
||||
|
||||
if txDeviceName == "" {
|
||||
continue
|
||||
}
|
||||
lastDeviceName = txDeviceName
|
||||
|
||||
rxChannel := startChannel + i
|
||||
if t.DebugDante {
|
||||
|
||||
BIN
notes/dante.pcap
BIN
notes/dante.pcap
Binary file not shown.
134
notes/dantepacket.md
Normal file
134
notes/dantepacket.md
Normal file
@@ -0,0 +1,134 @@
|
||||
# Dante Control Protocol Notes
|
||||
|
||||
All Dante control packets are UDP on port 4440.
|
||||
|
||||
## Packet Header (10 bytes)
|
||||
|
||||
```
|
||||
0x00: u8 magic # 0x27=standard, 0x28=extended
|
||||
0x01: u8 seq_lo
|
||||
0x02: u16 length # total packet length
|
||||
0x04: u16 seq_id
|
||||
0x06: u16 command
|
||||
0x08: u16 status
|
||||
0x0a: ... args/data
|
||||
```
|
||||
|
||||
All multi-byte fields are big-endian.
|
||||
|
||||
## Commands
|
||||
|
||||
### 0x1000 - Channel Count Query
|
||||
|
||||
Response:
|
||||
```
|
||||
0x0c: u16 tx_count
|
||||
0x0e: u16 rx_count
|
||||
```
|
||||
|
||||
### 0x3000 - Subscription Query (Standard)
|
||||
|
||||
Works for multicast; may return empty for unicast.
|
||||
|
||||
Request args (6 bytes):
|
||||
```
|
||||
0x00: u16 0x0001
|
||||
0x02: u16 page_num
|
||||
0x04: u16 0x0000
|
||||
```
|
||||
|
||||
Response record types (at 0x0e):
|
||||
- `0x0006` = unicast
|
||||
- `0x000e` = multicast
|
||||
|
||||
### 0x3400 - Subscription Query (Extended)
|
||||
|
||||
Uses magic=0x28. Works for unicast flows.
|
||||
|
||||
Request args (24 bytes):
|
||||
```
|
||||
0x07: u8 0x01
|
||||
0x08: u16 page_type # 0x0001=first, 0x0003=subsequent
|
||||
0x0a: u16 start_ch # 1, 17, 33, 49, ...
|
||||
```
|
||||
|
||||
## 0x3400 Response
|
||||
|
||||
```
|
||||
0x00: [10] header
|
||||
0x0a: [8] extended_header
|
||||
0x12: [32] offset_table # 16 × u16 record offsets
|
||||
0x32: ... records + strings
|
||||
```
|
||||
|
||||
Status (at 0x08):
|
||||
- `0x8112` = has subscription data
|
||||
- `0x0001` = empty page
|
||||
|
||||
### Offset Table
|
||||
|
||||
16 entries at 0x12-0x31, each a u16 absolute offset to a record.
|
||||
Zero offset = no more records.
|
||||
|
||||
### Subscription Record
|
||||
|
||||
Each record is 56+ bytes. The offset table points to record start.
|
||||
|
||||
```
|
||||
record+0x00: [40] record_header
|
||||
record+0x28: u16 0x0608 # marker
|
||||
record+0x2a: u16 0x0000
|
||||
record+0x2c: u16 tx_ch_offset # absolute offset to TX channel name string
|
||||
record+0x2e: u16 tx_dev_offset # absolute offset to TX device name string
|
||||
record+0x30: [4] flags
|
||||
record+0x34: u32 0x02020000 # string marker
|
||||
record+0x38: ... inline_strings # (unreliable, use offsets above)
|
||||
```
|
||||
|
||||
**Subscription status:**
|
||||
- Both offsets non-zero = subscribed
|
||||
- Both offsets zero = unsubscribed
|
||||
|
||||
### String Table
|
||||
|
||||
Null-terminated strings referenced by absolute offset from packet start.
|
||||
Multiple records share strings (e.g., same device name).
|
||||
|
||||
## Example: Page 2 Response (channels 17-32)
|
||||
|
||||
```
|
||||
Offset table: 0050 0090 00d0 0110 0158 0194 01d8 021c 0258 ...
|
||||
|
||||
Record at 0x0050 (RX ch 17):
|
||||
0x0050+0x2c: 0032 0035
|
||||
String at 0x32: "01" -> TX channel
|
||||
String at 0x35: "MICS-E" -> TX device
|
||||
Result: MICS-E[01] -> ch17
|
||||
|
||||
Record at 0x0158 (RX ch 21):
|
||||
0x0158+0x2c: 0032 0148
|
||||
String at 0x32: "01"
|
||||
String at 0x148: "TX-QLAB-1"
|
||||
Result: TX-QLAB-1[01] -> ch21
|
||||
|
||||
Record at 0x01d8 (RX ch 23):
|
||||
0x01d8+0x2c: 01cc 01d1
|
||||
String at 0x1cc: "Left"
|
||||
String at 0x1d1: "BT"
|
||||
Result: BT[Left] -> ch23
|
||||
|
||||
Record at 0x0258 (RX ch 25):
|
||||
0x0258+0x2c: 0000 0000
|
||||
Result: UNSUBSCRIBED
|
||||
```
|
||||
|
||||
## Parsing Algorithm
|
||||
|
||||
1. Check magic=0x28, command=0x3400, status=0x8112
|
||||
2. Read 16 offsets from 0x12-0x31
|
||||
3. For each non-zero offset:
|
||||
- Read u16 at offset+0x2c (tx_ch_offset)
|
||||
- Read u16 at offset+0x2e (tx_dev_offset)
|
||||
- If both zero: skip (unsubscribed)
|
||||
- Else: read null-terminated strings at those offsets
|
||||
- RX channel = page_start + record_index
|
||||
Reference in New Issue
Block a user