Go-based FM stereo transmitter with RDS, Windows-first and cross-platform
Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

116 řádky
2.8KB

  1. package aoiprxkit
  2. import (
  3. "encoding/binary"
  4. "fmt"
  5. "net"
  6. )
  7. const (
  8. DefaultSAPGroup = "224.2.127.254"
  9. DefaultSAPPort = 9875
  10. )
  11. type SAPPacket struct {
  12. Version uint8
  13. AddressTypeIPv6 bool
  14. IsDelete bool
  15. Encrypted bool
  16. Compressed bool
  17. AuthLenWords uint8
  18. MessageIDHash uint16
  19. OriginSource net.IP
  20. PayloadType string
  21. Payload []byte
  22. }
  23. type SAPAnnouncement struct {
  24. ReceivedAt string `json:"receivedAt"`
  25. SourceAddr string `json:"sourceAddr"`
  26. MessageID uint16 `json:"messageIdHash"`
  27. Delete bool `json:"delete"`
  28. PayloadType string `json:"payloadType"`
  29. SDP string `json:"sdp"`
  30. ParsedSDP SDPInfo `json:"parsedSdp"`
  31. }
  32. func ParseSAPPacket(buf []byte) (SAPPacket, error) {
  33. if len(buf) < 8 {
  34. return SAPPacket{}, fmt.Errorf("SAP packet too short")
  35. }
  36. b0 := buf[0]
  37. version := b0 >> 5
  38. if version != 1 {
  39. return SAPPacket{}, fmt.Errorf("unsupported SAP version %d", version)
  40. }
  41. addrTypeIPv6 := (b0 & 0x10) != 0
  42. isDelete := (b0 & 0x04) != 0
  43. encrypted := (b0 & 0x02) != 0
  44. compressed := (b0 & 0x01) != 0
  45. authLenWords := buf[1]
  46. msgID := binary.BigEndian.Uint16(buf[2:4])
  47. hdrLen := 4
  48. var origin net.IP
  49. if addrTypeIPv6 {
  50. if len(buf) < hdrLen+16 {
  51. return SAPPacket{}, fmt.Errorf("SAP packet too short for IPv6 source")
  52. }
  53. origin = net.IP(buf[hdrLen : hdrLen+16])
  54. hdrLen += 16
  55. } else {
  56. if len(buf) < hdrLen+4 {
  57. return SAPPacket{}, fmt.Errorf("SAP packet too short for IPv4 source")
  58. }
  59. origin = net.IP(buf[hdrLen : hdrLen+4])
  60. hdrLen += 4
  61. }
  62. authBytes := int(authLenWords) * 4
  63. if len(buf) < hdrLen+authBytes {
  64. return SAPPacket{}, fmt.Errorf("SAP packet too short for auth section")
  65. }
  66. hdrLen += authBytes
  67. if encrypted || compressed {
  68. return SAPPacket{}, fmt.Errorf("encrypted/compressed SAP payloads are not supported")
  69. }
  70. payloadType := "application/sdp"
  71. payloadStart := hdrLen
  72. if len(buf) > payloadStart && !(len(buf)-payloadStart >= 4 && string(buf[payloadStart:payloadStart+4]) == "v=0\n" || len(buf)-payloadStart >= 5 && string(buf[payloadStart:payloadStart+5]) == "v=0\r\n") {
  73. nul := -1
  74. for i := payloadStart; i < len(buf); i++ {
  75. if buf[i] == 0 {
  76. nul = i
  77. break
  78. }
  79. }
  80. if nul == -1 {
  81. return SAPPacket{}, fmt.Errorf("SAP payload type missing NUL terminator")
  82. }
  83. payloadType = string(buf[payloadStart:nul])
  84. payloadStart = nul + 1
  85. }
  86. if payloadStart > len(buf) {
  87. return SAPPacket{}, fmt.Errorf("invalid SAP payload start")
  88. }
  89. return SAPPacket{
  90. Version: version,
  91. AddressTypeIPv6: addrTypeIPv6,
  92. IsDelete: isDelete,
  93. Encrypted: encrypted,
  94. Compressed: compressed,
  95. AuthLenWords: authLenWords,
  96. MessageIDHash: msgID,
  97. OriginSource: origin,
  98. PayloadType: payloadType,
  99. Payload: append([]byte(nil), buf[payloadStart:]...),
  100. }, nil
  101. }