first commit

master
cheetah 8 months ago
commit 7ad747c43a

@ -0,0 +1,112 @@
{
"packets": [
{
"payload": [
1,0,0,0,235,3,0,0,232,3,238,0,4,0,0,0,49,48,48,51,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,40,0,1,77,101,111,119,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
"handler": 40,
"payloadSize": 448,
"kind": 9233
},{
"payload": [
0,0,0,0,0,0,0,0,0,0,0,0],
"handler": 40,
"payloadSize": 20,
"kind": 1
},{
"payload": [
48,0,120,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
"handler": 15,
"payloadSize": 64,
"kind": 1553
},{
"payload": [
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
"handler": 15,
"payloadSize": 64,
"kind": 1554
},{
"payload": [
1,0,0,0,235,3,0,0,232,3,238,0,4,0,0,0,49,48,48,51,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,176,0,1,66,114,104,101,104,101,104,101,104,101,104,117,101,117,101,117,101,117,101,117,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
"handler": 41,
"payloadSize": 448,
"kind": 9233
},{
"payload": [
0,0,0,0,0,0,0,0,0,0,0,0],
"handler": 41,
"payloadSize": 20,
"kind": 1
},{
"payload": [
48,0,120,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
"handler": 16,
"payloadSize": 64,
"kind": 1553
},{
"payload": [
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
"handler": 16,
"payloadSize": 64,
"kind": 1554
},{
"payload": [
48,0,120,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
"handler": 17,
"payloadSize": 64,
"kind": 1553
},{
"payload": [
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
"handler": 17,
"payloadSize": 64,
"kind": 1554
},{
"payload": [
48,0,120,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
"handler": 18,
"payloadSize": 64,
"kind": 1553
},{
"payload": [
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
"handler": 18,
"payloadSize": 64,
"kind": 1554
},{
"payload": [
48,0,120,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
"handler": 19,
"payloadSize": 64,
"kind": 1553
},{
"payload": [
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
"handler": 19,
"payloadSize": 64,
"kind": 1554
},{
"payload": [
1,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,49,48,48,51,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,224,3,1,2,1,2,1,65,98,99,100,101,102,103,104,106,105,106,108,109,110,105,112,113,114,97,116,117,118,119,120,121,122,49,50,51,51,52,52,56,50,56,51,56,51,56,51,55,51,55,51,55,55,51,55,55,51,51,55,51,51,55,51,55,51,55,51,65,98,99,100,101,102,103,104,106,105,106,108,109,110,105,112,113,114,97,116,117,118,119,120,121,122,49,50,51,51,52,52,56,50,56,51,56,51,56,51,55,51,55,51,55,55,51,55,55,51,51,55,51,51,55,51,55,51,55,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
"handler": 42,
"payloadSize": 448,
"kind": 9233
},{
"payload": [
0,0,0,0,0,0,0,0,0,0,0,0],
"handler": 42,
"payloadSize": 20,
"kind": 1
},{
"payload": [
1,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,49,48,48,51,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,224,3,1,2,2,51,65,98,99,100,101,102,103,104,106,105,106,108,109,110,105,112,113,114,97,116,117,118,119,120,121,122,49,50,51,51,52,52,56,50,56,51,56,51,56,51,55,51,55,51,55,55,51,55,55,51,51,55,51,51,55,51,55,51,55,51,65,98,99,100,101,102,103,104,106,105,106,108,109,110,105,112,113,114,97,116,117,118,119,120,121,122,49,50,51,51,52,52,56,50,56,51,56,51,56,51,55,51,55,51,55,55,51,55,55,51,51,55,51,51,55,51,55,51,55,51,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
"handler": 43,
"payloadSize": 448,
"kind": 9233
},{
"payload": [
0,0,0,0,0,0,0,0,0,0,0,0],
"handler": 43,
"payloadSize": 20,
"kind": 1
}]
}

@ -0,0 +1,57 @@
package common
type NetIdentity struct {
SSI uint32
MNC uint16
MCC uint16
}
func (identity *NetIdentity) IsValid() bool {
return identity.SSI > 0
}
type TetraFlexAddressKind byte
const (
SSI TetraFlexAddressKind = 0
UserNo TetraFlexAddressKind = 1
)
type TetraFlexAddress struct {
Kind TetraFlexAddressKind
UserNo string
TSI NetIdentity
}
func NewAddress() (addr *TetraFlexAddress) {
return &TetraFlexAddress{
Kind: SSI,
UserNo: "",
TSI: NetIdentity{
SSI: 0,
MNC: 0,
MCC: 0,
},
}
}
func NewAddressFromSSINumber(ssi uint16) (addr *TetraFlexAddress) {
return &TetraFlexAddress{
Kind: SSI,
UserNo: "",
TSI: NetIdentity{
SSI: uint32(ssi),
MNC: 0,
MCC: 0,
},
}
}
var TetraFlexAddressEmpty = TetraFlexAddress{
Kind: SSI,
UserNo: "",
TSI: NetIdentity{
SSI: 0,
MNC: 0,
MCC: 0,
},
}

@ -0,0 +1,11 @@
package common
type TetraFlexAudioChannelTypes byte
const (
AudioChannel_RTP_uLaw TetraFlexAudioChannelTypes = 1
AudioChannel_RTP_aLaw TetraFlexAudioChannelTypes = 2
AudioChannel_CMoIP_uLaw TetraFlexAudioChannelTypes = 16
AudioChannel_CMoIP_aLaw TetraFlexAudioChannelTypes = 32
AudioChannel_CMoIP_Tetra TetraFlexAudioChannelTypes = 64
)

@ -0,0 +1,9 @@
package common
type AuthenticationChallenge struct {
RandomSeed []byte
RandomChallenge []byte
Response []byte
PinRequested bool
SecurityKeyRequested bool
}

@ -0,0 +1,37 @@
package common
type TetraFlexCallTypes byte
const (
CallTypeIndividualDuplex TetraFlexCallTypes = 0
CallTypeIndividualSimplex TetraFlexCallTypes = 1
CallTypeGroup TetraFlexCallTypes = 2
CallTypeBroadcast TetraFlexCallTypes = 3
CallTypeAmbience TetraFlexCallTypes = 4
)
type TetraFlexCallPriorities byte
const (
CallPriorityNotDefined TetraFlexCallPriorities = 0
CallPriority1 TetraFlexCallPriorities = 1
CallPriority2 TetraFlexCallPriorities = 2
CallPriority3 TetraFlexCallPriorities = 3
CallPriority4 TetraFlexCallPriorities = 4
CallPriority5 TetraFlexCallPriorities = 5
CallPriority6 TetraFlexCallPriorities = 6
CallPriority7 TetraFlexCallPriorities = 7
CallPriority8 TetraFlexCallPriorities = 8
CallPriority9 TetraFlexCallPriorities = 9
CallPriority10 TetraFlexCallPriorities = 10
CallPriority11 TetraFlexCallPriorities = 11
CallPriorityPreEmptive1 TetraFlexCallPriorities = 12
CallPriorityPreEmptive2 TetraFlexCallPriorities = 13
CallPriorityPreEmptive3 TetraFlexCallPriorities = 14
CallPriorityPreEmptive4 TetraFlexCallPriorities = 15
CallPriorityPreEmptive5 TetraFlexCallPriorities = 16
CallPriorityDefault TetraFlexCallPriorities = 0
CallPriorityEmergency TetraFlexCallPriorities = 15
)

@ -0,0 +1,35 @@
package common
type AttachReasons uint16
const (
AttachReasonPositions AttachReasons = 0
AttachReasonPTT AttachReasons = 1
AttachReasonScan AttachReasons = 2
)
type AttachedScanKind uint16
const (
NotScanned AttachedScanKind = 0
Selected AttachedScanKind = 1
Scanned AttachedScanKind = 2
)
/*
// Token: 0x02000157 RID: 343
public enum AttachedScanKind
{
// Token: 0x040004E3 RID: 1251
NotScanned,
// Token: 0x040004E4 RID: 1252
Selected,
// Token: 0x040004E5 RID: 1253
Scanned
}
*/
type GroupConfig struct {
Group *TetraFlexAddress
ScanMode AttachedScanKind
}

@ -0,0 +1,9 @@
package common
func uint16ToBytes(n uint16) []byte {
return []byte{byte(n), byte(n >> 8)}
}
func uint32ToBytes(n uint32) []byte {
return []byte{byte(n), byte(n >> 8), byte(n >> 16), byte(n >> 24)}
}

@ -0,0 +1,42 @@
package common
type TetraFlexIdentityKind byte
const (
IdentityKindUnknown TetraFlexIdentityKind = 0x00
IdentityKindMobile TetraFlexIdentityKind = 0x01
IdentityKindGroup TetraFlexIdentityKind = 0x02
IdentityKindDMRRadio TetraFlexIdentityKind = 0x03
IdentityKindNumber TetraFlexIdentityKind = 0x04
IdentityKindApplication TetraFlexIdentityKind = 0x05
IdentityKindDialInUser TetraFlexIdentityKind = 0x06
IdentityKindEmergencyNumber TetraFlexIdentityKind = 0x07
IdentityKindTerminal TetraFlexIdentityKind = 0x08
IdentityKindPersonal TetraFlexIdentityKind = 0x09
IdentityKindSWMI TetraFlexIdentityKind = 0x3C
IdentityKindISDN TetraFlexIdentityKind = 0x3D
IdentityKindPABX TetraFlexIdentityKind = 0x3E
IdentityKindPSTN TetraFlexIdentityKind = 0x3F
IdentityKindAnonymous TetraFlexIdentityKind = 0x50
IdentityKindExternal TetraFlexIdentityKind = 0x64
IdentityKindExternalUnknown TetraFlexIdentityKind = 0x65
)
type TetraFlexUnifiedSSIKinds byte
const (
UnifiedKindUnifiedNumber TetraFlexUnifiedSSIKinds = 0x00
UnifiedKindEmergencyNumber TetraFlexUnifiedSSIKinds = 0x01
UnifiedKindPersonalNumber TetraFlexUnifiedSSIKinds = 0x02
UnifiedKindMultiClient TetraFlexUnifiedSSIKinds = 0x03
UnifiedKindExternalNumber TetraFlexUnifiedSSIKinds = 0x04
UnifiedKindGSMPhone TetraFlexUnifiedSSIKinds = 0x05
UnifiedKindPABXNumber TetraFlexUnifiedSSIKinds = 0x06
)
type TetraFlexIdentityInfo struct {
Description string
Kind TetraFlexIdentityKind
Address TetraFlexAddress
UnifiedSSIKinds TetraFlexUnifiedSSIKinds
}

@ -0,0 +1,15 @@
package common
type KeepAliveChallenge struct {
Value1 uint16
Value2 uint16
Value3 uint32
}
type KeepAliveResponse struct {
GPSLatitude int32
GPSLongitude int32
Value3 uint16
Value4 uint16
Accuracy uint32
GPSTime uint32
}

@ -0,0 +1,14 @@
package common
type RegistrationInfo struct {
Address TetraFlexAddress
ConnectionState uint32
RemoteVersion uint32
MaxVersion uint32
MyNumber uint32
MyShortNumber uint32
CallCount byte
StreamCount byte
RoamingRef uint32
RTPPorts []uint16
}

@ -0,0 +1,91 @@
package common
import (
"bufio"
"encoding/binary"
"fmt"
"io"
"net"
"test/common/tmkind"
)
type TerminalClient struct {
address string
conn net.Conn
netWriter *bufio.Writer
netReader *bufio.Reader
GroupConfig []GroupConfig
HandlerMap map[uint32]chan any
RX chan *TerminalMessageResponse
}
func NewTerminalClient(address string) (tc *TerminalClient) {
return &TerminalClient{
address: address,
RX: make(chan *TerminalMessageResponse),
}
}
func (tc *TerminalClient) Connect() (err error) {
tc.conn, err = net.Dial("tcp", tc.address)
if err != nil {
return err
}
tc.netWriter = bufio.NewWriter(tc.conn)
tc.netReader = bufio.NewReader(tc.conn)
go tc.rxLoop()
return nil
}
func (tc *TerminalClient) Send(message *TerminalMessage) (err error) {
_, err = tc.netWriter.Write(message.Encode())
if err != nil {
return err
}
err = tc.netWriter.Flush()
if err != nil {
return err
}
return nil
}
func (tc *TerminalClient) rxLoop() {
var err error
for {
dataHeader := make([]byte, 2) //2 bytes for uint16
_, err = io.ReadFull(tc.netReader, dataHeader)
if err != nil {
panic(err)
}
payloadLength := binary.LittleEndian.Uint16(dataHeader)
fmt.Printf("length: %d bytes\n", payloadLength)
kindData := make([]byte, 2) //2 bytes for uint16
_, err = io.ReadFull(tc.netReader, kindData)
if err != nil {
panic(err)
}
messageKind := binary.LittleEndian.Uint16(kindData)
fmt.Printf("kind: %d\n", messageKind)
handlerData := make([]byte, 4) //4 bytes for uint32
_, err = io.ReadFull(tc.netReader, handlerData)
if err != nil {
panic(err)
}
messageHandler := binary.LittleEndian.Uint32(handlerData)
fmt.Printf("handler: %d\n", messageHandler)
dataPayload := make([]byte, payloadLength-(2+2+4)) //8 bytes for header
_, err = io.ReadFull(tc.netReader, dataPayload)
if err != nil {
panic(err)
}
parsedResp, err := NewTerminalMessageResponse(payloadLength, tmkind.TetraFlexTerminalMessageKinds(messageKind), messageHandler, dataPayload)
if err != nil {
panic(err)
}
tc.RX <- parsedResp
}
}

@ -0,0 +1,431 @@
package common
import (
"bytes"
"test/common/tmkind"
)
type TerminalMessage struct {
length uint16
kind tmkind.TetraFlexTerminalMessageKinds
handler uint32
data bytes.Buffer
}
func NewTerminalMessage(length uint16, kind tmkind.TetraFlexTerminalMessageKinds, handler uint32) (tm *TerminalMessage, err error) {
tm = &TerminalMessage{
length: length,
kind: kind,
handler: handler,
}
if err = tm.WriteShort(length); err != nil {
return nil, err
}
if err = tm.WriteShort(uint16(kind)); err != nil {
return nil, err
}
if err = tm.WriteUInt(handler); err != nil {
return nil, err
}
return tm, nil
}
func (tm *TerminalMessage) WriteShort(data uint16) (err error) {
_, err = tm.data.Write(uint16ToBytes(data))
return err
}
func (tm *TerminalMessage) WriteUInt(data uint32) (err error) {
_, err = tm.data.Write(uint32ToBytes(data))
return err
}
func (tm *TerminalMessage) WriteByte(data byte) (err error) {
return tm.data.WriteByte(data)
}
func (tm *TerminalMessage) WriteBytes(data []byte, count int) (err error) {
bytes := make([]byte, count)
copy(bytes, data)
_, err = tm.data.Write(bytes)
return err
}
func (tm *TerminalMessage) WriteString(text string, len int) (err error) {
bytes := make([]byte, len)
copy(bytes, text)
_, err = tm.data.Write(bytes)
return err
}
func (tm *TerminalMessage) WriteTFAddress(addr TetraFlexAddress) (err error) {
if err = tm.data.WriteByte(byte(addr.Kind)); err != nil {
return err
}
var (
text string
num1 uint32
num2, num3 uint16
num4 byte
)
if len(addr.UserNo) > 0 {
text = addr.UserNo
}
if addr.TSI.IsValid() {
num1 = addr.TSI.SSI
num2 = addr.TSI.MNC
num3 = addr.TSI.MCC
}
if err = tm.FillZero(3); err != nil {
return err
}
if err = tm.WriteUInt(num1); err != nil {
return err
}
if err = tm.WriteShort(num2); err != nil {
return err
}
if err = tm.WriteShort(num3); err != nil {
return err
}
num4 = byte(len(text))
if num4 > 24 {
num4 = 24
}
// this.#c.Write((byte)num4);
if err = tm.data.WriteByte(num4); err != nil {
return err
}
// this.#c.Write(0); 3x
if err = tm.FillZero(3); err != nil {
return err
}
if err = tm.WriteString(text, 24); err != nil {
return err
}
/*
AddressType kind = #LBc.Kind;
string text = #i.#f.#8q(473);
uint num = 0U;
ushort num2 = 0;
ushort num3 = 0;
if (#LBc.#v6c())
{
text = #LBc.UserNumber.Number;
}
if (#LBc.#x6c())
{
num = #LBc.TSI.SSI;
num2 = #LBc.TSI.MNC;
num3 = #LBc.TSI.MCC;
}
this.#c.Write((byte)kind);
this.#c.Write(num);
this.#c.Write(num2);
this.#c.Write(num3);
uint num4 = (uint)text.Length;
if (num4 > 24U)
{
num4 = 24U;
}
this.#c.Write((byte)num4);
this.#c.Write(0);
this.#c.Write(0);
this.#c.Write(0);
for (uint num5 = 0U; num5 < num4; num5 += 1U)
{
this.#c.Write((byte)text[(int)num5]);
}
for (uint num6 = num4; num6 < 24U; num6 += 1U)
{
this.#c.Write(0);
}
*/
//return tm.data.WriteByte(data)
return nil
}
func (tm *TerminalMessage) WriteIdentityInfoAddress(identityInfo TetraFlexIdentityInfo) (err error) {
return tm.WriteTFAddress(identityInfo.Address)
}
func (tm *TerminalMessage) WriteIdentityInfoKind(identityInfo TetraFlexIdentityInfo) (err error) {
/*
case TetraFlexIdentityKind.Mobile:
case TetraFlexIdentityKind.Group:
case TetraFlexIdentityKind.DMRRADIO:
case TetraFlexIdentityKind.Application:
case TetraFlexIdentityKind.DialInUser:
case TetraFlexIdentityKind.EmergencyNumber:
case TetraFlexIdentityKind.Terminal:
case TetraFlexIdentityKind.Personal:
*/
return tm.data.WriteByte(byte(identityInfo.Kind))
}
func (tm *TerminalMessage) WriteIdentityInfoUnfied(identityInfo TetraFlexIdentityInfo) (err error) {
byteVal := byte(0)
if identityInfo.Kind == IdentityKindPersonal {
byteVal = byte(UnifiedKindPersonalNumber)
}
return tm.data.WriteByte(byteVal)
}
func (tm *TerminalMessage) FillZero(zeroCount uint32) (err error) {
_, err = tm.data.Write(make([]byte, zeroCount))
return err
}
func (tm *TerminalMessage) Encode() []byte {
return tm.data.Bytes()
}
func NewTMAuthReq(addr TetraFlexAddress, num uint32, randomBytes []byte, terminalTypeID byte, terminalTypeStr string, terminalVersionDate uint32, terminalVersionStr string) (tm *TerminalMessage, err error) {
tfAuthReq, err := NewTerminalMessage(256, tmkind.IpApiRegistrationRequest, 0)
if err != nil {
return nil, err
}
if err = tfAuthReq.WriteUInt(20140604); err != nil {
return nil, err
}
if err = tfAuthReq.WriteTFAddress(addr); err != nil {
return nil, err
}
if err = tfAuthReq.WriteByte(10); err != nil {
return nil, err
}
if err = tfAuthReq.WriteByte(10); err != nil {
return nil, err
}
if err = tfAuthReq.WriteByte(0); err != nil {
return nil, err
}
if err = tfAuthReq.WriteByte(terminalTypeID); err != nil {
return nil, err
}
if err = tfAuthReq.WriteUInt(num); err != nil {
return nil, err
}
if err = tfAuthReq.WriteString(terminalTypeStr, 16); err != nil {
return nil, err
}
if err = tfAuthReq.WriteString(terminalVersionStr, 16); err != nil {
return nil, err
}
if err = tfAuthReq.WriteBytes(randomBytes, 10); err != nil {
return nil, err
}
if err = tfAuthReq.FillZero(2); err != nil {
return nil, err
}
if err = tfAuthReq.WriteUInt(terminalVersionDate); err != nil {
return nil, err
}
if err = tfAuthReq.FillZero(148); err != nil {
return nil, err
}
return tfAuthReq, nil
}
func NewTMKeepAliveResponse(challenge KeepAliveChallenge) (tm *TerminalMessage, err error) {
response := KeepAliveResponse{}
terminalMessage, err := NewTerminalMessage(64, tmkind.IpApiKeepAliveResponse, 0)
if err != nil {
return nil, err
}
if err = terminalMessage.FillZero(8); err != nil {
return nil, err
}
if err = terminalMessage.WriteUInt(uint32(response.GPSLatitude)); err != nil {
return nil, err
}
if err = terminalMessage.WriteUInt(uint32(response.GPSLongitude)); err != nil {
return nil, err
}
if err = terminalMessage.WriteShort(response.Value3); err != nil {
return nil, err
}
if err = terminalMessage.WriteShort(response.Value4); err != nil {
return nil, err
}
if err = terminalMessage.WriteUInt(response.GPSTime); err != nil {
return nil, err
}
if err = terminalMessage.WriteUInt(response.Accuracy); err != nil {
return nil, err
}
if err = terminalMessage.FillZero(28); err != nil {
return nil, err
}
return terminalMessage, nil
}
func NewTMNotificationRequest(messageTypes []tmkind.TetraFlexTerminalMessageKinds) (tm *TerminalMessage, err error) {
terminalMessage, err := NewTerminalMessage(128, tmkind.IpApiNotificationRequest, 0)
if err != nil {
return nil, err
}
// IpApiNotificationRequest
if err = terminalMessage.WriteShort(uint16(len(messageTypes))); err != nil {
return nil, err
}
if err = terminalMessage.FillZero(2); err != nil {
return nil, err
}
for i := range messageTypes {
if err = terminalMessage.WriteShort(uint16(messageTypes[i])); err != nil {
return nil, err
}
}
for i := len(messageTypes); i < 32; i++ {
if err = terminalMessage.WriteShort(0); err != nil {
return nil, err
}
}
/*
if err = terminalMessage.FillZero(uint32(2 * (32 - len(messageTypes)))); err != nil {
return nil, err
}*/
if err = terminalMessage.FillZero(52); err != nil {
return nil, err
}
return terminalMessage, nil
}
func NewTMIpApiGroupAttachRequest(groupConfig []GroupConfig) (tm *TerminalMessage, err error) {
terminalMessage, err := NewTerminalMessage(384, tmkind.IpApiGroupAttachRequest, 0)
if err != nil {
return nil, err
}
if err = terminalMessage.FillZero(4); err != nil {
return nil, err
}
for _, group := range groupConfig {
if err = terminalMessage.WriteTFAddress(*group.Group); err != nil {
return nil, err
}
}
for i := 0; i < 8-len(groupConfig); i++ {
if err = terminalMessage.WriteTFAddress(TetraFlexAddressEmpty); err != nil {
return nil, err
}
}
if err = terminalMessage.FillZero(52); err != nil {
return nil, err
}
return terminalMessage, nil
}
func NewTMIpApiCallSetupRequest(address TetraFlexAddress, callType TetraFlexCallTypes, callPriority TetraFlexCallPriorities, idkBool bool, identityInfo TetraFlexIdentityInfo) (tm *TerminalMessage, err error) {
terminalMessage, err := NewTerminalMessage(192, tmkind.IpApiCallSetupRequest, 0)
if err != nil {
return nil, err
}
if err = terminalMessage.WriteByte(byte(callType)); err != nil {
return nil, err
}
if err = terminalMessage.FillZero(1); err != nil {
return nil, err
}
if err = terminalMessage.WriteByte(byte(callPriority)); err != nil {
return nil, err
}
if idkBool {
if err = terminalMessage.WriteByte(4); err != nil {
return nil, err
}
} else {
if err = terminalMessage.WriteByte(0); err != nil {
return nil, err
}
}
if err = terminalMessage.WriteTFAddress(address); err != nil {
return nil, err
}
//WriteIdentityInfoAddress
if err = terminalMessage.WriteIdentityInfoAddress(identityInfo); err != nil {
return nil, err
}
//WriteIdentityInfoDescr (protoHelper1:334)
if err = terminalMessage.WriteString(identityInfo.Description, 64); err != nil {
return nil, err
}
//WriteIdentityInfoKind
if err = terminalMessage.WriteIdentityInfoKind(identityInfo); err != nil {
return nil, err
}
//WriteByte 0
if err = terminalMessage.WriteByte(0); err != nil {
return nil, err
}
//WriteIdentityInfoUnfied ()
if err = terminalMessage.WriteIdentityInfoUnfied(identityInfo); err != nil {
return nil, err
}
//Fill 33
if err = terminalMessage.FillZero(33); err != nil {
return nil, err
}
return terminalMessage, nil
}
func NewTMSendIpApiCallStreamRequest(uint1 uint, callRef byte, streamRef byte, audioChannelType TetraFlexAudioChannelTypes) (tm *TerminalMessage, err error) {
terminalMessage, err := NewTerminalMessage(64, tmkind.IpApiCallStreamRequest, 0)
if err != nil {
return nil, err
}
if err = terminalMessage.WriteByte(callRef); err != nil {
return nil, err
}
if audioChannelType < AudioChannel_CMoIP_uLaw {
switch audioChannelType {
case AudioChannel_RTP_uLaw:
if err = terminalMessage.WriteByte(1); err != nil {
return nil, err
}
if err = terminalMessage.WriteByte(0); err != nil {
return nil, err
}
case AudioChannel_RTP_aLaw:
if err = terminalMessage.WriteByte(1); err != nil {
return nil, err
}
if err = terminalMessage.WriteByte(8); err != nil {
return nil, err
}
default:
if audioChannelType == AudioChannel_CMoIP_uLaw {
if err = terminalMessage.WriteByte(2); err != nil {
return nil, err
}
if err = terminalMessage.WriteByte(16); err != nil {
return nil, err
}
}
}
} else {
if audioChannelType == AudioChannel_CMoIP_aLaw {
if err = terminalMessage.WriteByte(2); err != nil {
return nil, err
}
if err = terminalMessage.WriteByte(1); err != nil {
return nil, err
}
}
if audioChannelType == AudioChannel_CMoIP_Tetra {
if err = terminalMessage.WriteByte(2); err != nil {
return nil, err
}
if err = terminalMessage.WriteByte(2); err != nil {
return nil, err
}
}
}
if err = terminalMessage.WriteByte(streamRef); err != nil {
return nil, err
}
//Fill 52
if err = terminalMessage.FillZero(52); err != nil {
return nil, err
}
return terminalMessage, nil
}

@ -0,0 +1,205 @@
package common
import (
"bytes"
"encoding/binary"
"test/common/tmkind"
)
type TerminalMessageResponse struct {
Length uint16
Kind tmkind.TetraFlexTerminalMessageKinds
Handler uint32
data bytes.Buffer
payload []byte
}
func NewTerminalMessageResponse(length uint16, kind tmkind.TetraFlexTerminalMessageKinds, handler uint32, payload []byte) (tm *TerminalMessageResponse, err error) {
tm = &TerminalMessageResponse{
Length: length,
Kind: kind,
Handler: handler,
data: *bytes.NewBuffer(payload),
payload: payload,
}
return tm, nil
}
func (tmr *TerminalMessageResponse) Bytes() []byte {
return tmr.payload
}
func (tmr *TerminalMessageResponse) readUInt32() (result uint32, err error) {
temp := make([]byte, 4)
if _, err = tmr.data.Read(temp); err != nil {
return result, err
}
result = binary.LittleEndian.Uint32(temp)
return result, nil
}
func (tmr *TerminalMessageResponse) readUInt16() (result uint16, err error) {
temp := make([]byte, 2)
if _, err = tmr.data.Read(temp); err != nil {
return result, err
}
result = binary.LittleEndian.Uint16(temp)
return result, nil
}
func (tmr *TerminalMessageResponse) readByte() (result byte, err error) {
result, err = tmr.data.ReadByte()
return result, err
}
func (tmr *TerminalMessageResponse) skipByte() (err error) {
_, err = tmr.data.ReadByte()
return err
}
func (tmr *TerminalMessageResponse) skipBytes(count int) (err error) {
for i := 0; i < count; i++ {
_, err = tmr.data.ReadByte()
if err != nil {
return err
}
}
return nil
}
func (tmr *TerminalMessageResponse) readAddress() (result TetraFlexAddress, err error) {
kindByte, err := tmr.readByte()
if err != nil {
return result, err
}
result.Kind = TetraFlexAddressKind(kindByte)
if err = tmr.skipByte(); err != nil {
return result, err
}
if err = tmr.skipByte(); err != nil {
return result, err
}
if err = tmr.skipByte(); err != nil {
return result, err
}
result.TSI.SSI, err = tmr.readUInt32()
if err != nil {
return result, err
}
result.TSI.MCC, err = tmr.readUInt16()
if err != nil {
return result, err
}
result.TSI.MCC, err = tmr.readUInt16()
if err != nil {
return result, err
}
textLength, err := tmr.readByte()
if err != nil {
return result, err
}
if err = tmr.skipByte(); err != nil {
return result, err
}
if err = tmr.skipByte(); err != nil {
return result, err
}
if err = tmr.skipByte(); err != nil {
return result, err
}
textBytes := make([]byte, textLength)
if _, err = tmr.data.Read(textBytes); err != nil {
return result, err
}
for i := textLength; i < 24; i++ {
if err = tmr.skipByte(); err != nil {
return result, err
}
}
return result, nil
}
func (tmr *TerminalMessageResponse) AsAuthenticationChallenge() (authChallenge AuthenticationChallenge, err error) {
authChallenge.RandomSeed = make([]byte, 10)
if _, err = tmr.data.Read(authChallenge.RandomSeed); err != nil {
return authChallenge, err
}
tmr.data.ReadByte()
tmr.data.ReadByte()
authChallenge.RandomChallenge = make([]byte, 10)
if _, err = tmr.data.Read(authChallenge.RandomChallenge); err != nil {
return authChallenge, err
}
flagByte, err := tmr.data.ReadByte()
if err != nil {
return authChallenge, err
}
authChallenge.PinRequested = flagByte&1 != 0
authChallenge.SecurityKeyRequested = flagByte&2 != 0
authChallenge.Response = make([]byte, 4)
if _, err = tmr.data.Read(authChallenge.Response); err != nil {
return authChallenge, err
}
return authChallenge, nil
}
func (tmr *TerminalMessageResponse) AsRegistrationInfo() (regInfo RegistrationInfo, err error) {
if regInfo.ConnectionState, err = tmr.readUInt32(); err != nil {
return regInfo, err
}
if regInfo.RemoteVersion, err = tmr.readUInt32(); err != nil {
return regInfo, err
}
if regInfo.Address, err = tmr.readAddress(); err != nil {
return regInfo, err
}
if regInfo.MyNumber, err = tmr.readUInt32(); err != nil {
return regInfo, err
}
if regInfo.MyShortNumber, err = tmr.readUInt32(); err != nil {
return regInfo, err
}
if err = tmr.skipBytes(4); err != nil {
return regInfo, err
}
if regInfo.CallCount, err = tmr.readByte(); err != nil {
return regInfo, err
}
if regInfo.StreamCount, err = tmr.readByte(); err != nil {
return regInfo, err
}
if err = tmr.skipBytes(2); err != nil {
return regInfo, err
}
if regInfo.MaxVersion, err = tmr.readUInt32(); err != nil {
return regInfo, err
}
if regInfo.RoamingRef, err = tmr.readUInt32(); err != nil {
return regInfo, err
}
if regInfo.RemoteVersion > 20120702 {
port1, err := tmr.readUInt16()
if err != nil {
return regInfo, err
}
port2, err := tmr.readUInt16()
if err != nil {
return regInfo, err
}
regInfo.RTPPorts = []uint16{port1, port2}
}
return regInfo, nil
}
func (tmr *TerminalMessageResponse) AsKeepAliveChallenge() (keepAliveChallenge KeepAliveChallenge, err error) {
if keepAliveChallenge.Value1, err = tmr.readUInt16(); err != nil {
return keepAliveChallenge, err
}
if keepAliveChallenge.Value2, err = tmr.readUInt16(); err != nil {
return keepAliveChallenge, err
}
if keepAliveChallenge.Value3, err = tmr.readUInt32(); err != nil {
return keepAliveChallenge, err
}
return keepAliveChallenge, nil
}

@ -0,0 +1,41 @@
package tmkind
type TetraFlexTerminalMessageKinds uint16
const (
NONE TetraFlexTerminalMessageKinds = 0
IpApiGeneralConfirm TetraFlexTerminalMessageKinds = 1
IpApiRegistrationRequest TetraFlexTerminalMessageKinds = 273
IpApiRegistrationConfirm TetraFlexTerminalMessageKinds = 274
IpApiAuthenticationChallenge TetraFlexTerminalMessageKinds = 529
IpApiAuthenticationResponse TetraFlexTerminalMessageKinds = 530
IpApiNotificationRequest TetraFlexTerminalMessageKinds = 785
IpApiNotificationConfirm TetraFlexTerminalMessageKinds = 786
IpApiGroupAttachRequest TetraFlexTerminalMessageKinds = 1041
IpApiGroupAttachConfirm TetraFlexTerminalMessageKinds = 1042
IpApiDeregistrationRequest TetraFlexTerminalMessageKinds = 1297
IpApiKeepAliveChallenge TetraFlexTerminalMessageKinds = 1553
IpApiKeepAliveResponse TetraFlexTerminalMessageKinds = 1554
IpApiSubscriberInfoRequest TetraFlexTerminalMessageKinds = 1809
IpApiSubscriberInfoConfirm TetraFlexTerminalMessageKinds = 1810
IpApiSoftwareInfoRequest TetraFlexTerminalMessageKinds = 2065
IpApiSoftwareInfoConfirm TetraFlexTerminalMessageKinds = 2066
IpApiCallSetupRequest TetraFlexTerminalMessageKinds = 4369
IpApiCallSetupConfirm TetraFlexTerminalMessageKinds = 4370
IpApiCallConnectRequest TetraFlexTerminalMessageKinds = 4625
IpApiCallDisconnectRequest TetraFlexTerminalMessageKinds = 4881
IpApiCallPttRequest TetraFlexTerminalMessageKinds = 5137
IpApiCallStreamRequest TetraFlexTerminalMessageKinds = 5393
IpApiCallUpdateNotification TetraFlexTerminalMessageKinds = 5649
IpApiCallRoamingFinishedNotification TetraFlexTerminalMessageKinds = 5905
IpApiSdsDataType4Request TetraFlexTerminalMessageKinds = 9233
IpApiSdsStatusRequest TetraFlexTerminalMessageKinds = 9489
IpApiSdsTlTransferRequest TetraFlexTerminalMessageKinds = 9745
IpApiSdsTlReportRequest TetraFlexTerminalMessageKinds = 10001
IpApiSdsTlShortReportRequest TetraFlexTerminalMessageKinds = 10257
IpApiSdsDataType4Notification TetraFlexTerminalMessageKinds = 13329
IpApiSdsStatusNotification TetraFlexTerminalMessageKinds = 13585
IpApiSdsTlTransferNotification TetraFlexTerminalMessageKinds = 13841
IpApiSdsTlReportNotification TetraFlexTerminalMessageKinds = 14097
IpApiSdsTlShortReportNotification TetraFlexTerminalMessageKinds = 14353
)

@ -0,0 +1,3 @@
module git.cheetah.cat/cheetah/opentetraflex-go
go 1.21.0

@ -0,0 +1,132 @@
package main
import (
"crypto/rand"
"encoding/hex"
"encoding/json"
"fmt"
"test/common"
"test/common/tmkind"
// "strings"
)
func main() {
var err error
terminalClient := common.NewTerminalClient("192.168.133.106:42381")
//data := make([]byte, 256)
err = terminalClient.Connect()
if err != nil {
panic(err)
}
randomBytes := make([]byte, 10)
_, err = rand.Read(randomBytes)
if err != nil {
panic(err)
}
myAddr := common.TetraFlexAddress{
Kind: common.UserNo,
UserNo: "1005",
}
// 20160111
tfAuthReq, err := common.NewTMAuthReq(myAddr, 0, randomBytes, 1, "OpenTetraFlex", 20230831, "1.0.0")
if err != nil {
panic(err)
}
fmt.Println(hex.EncodeToString(tfAuthReq.Encode()))
terminalClient.Send(tfAuthReq)
for {
select {
case parsedMsg := <-terminalClient.RX:
fmt.Println(hex.EncodeToString(parsedMsg.Bytes()))
if parsedMsg.Kind == tmkind.IpApiAuthenticationChallenge {
authChall, err := parsedMsg.AsAuthenticationChallenge()
if err != nil {
panic(err)
}
if authChall.PinRequested {
fmt.Println("Sending Auth Pin")
//terminalClient.SendTM()
}
}
if parsedMsg.Kind == tmkind.IpApiKeepAliveChallenge {
challenge, err := parsedMsg.AsKeepAliveChallenge()
if err != nil {
panic(err)
}
keepAliveBytes, _ := json.Marshal(challenge)
fmt.Println(string(keepAliveBytes))
response, err := common.NewTMKeepAliveResponse(challenge)
if err != nil {
panic(err)
}
fmt.Println(hex.EncodeToString(response.Encode()))
terminalClient.Send(response)
// Send Subscribe
response, err = common.NewTMNotificationRequest([]tmkind.TetraFlexTerminalMessageKinds{
tmkind.IpApiCallUpdateNotification,
tmkind.IpApiSdsTlReportNotification,
tmkind.IpApiSdsTlShortReportNotification,
tmkind.IpApiSdsDataType4Notification,
tmkind.IpApiSdsStatusNotification,
tmkind.IpApiSdsTlTransferNotification,
})
if err != nil {
panic(err)
}
fmt.Println(hex.EncodeToString(response.Encode()))
terminalClient.Send(response)
response, err = common.NewTMIpApiGroupAttachRequest([]common.GroupConfig{
{
Group: common.NewAddressFromSSINumber(101),
ScanMode: common.Scanned,
},
{
Group: common.NewAddressFromSSINumber(102),
ScanMode: common.Scanned,
},
{
Group: common.NewAddressFromSSINumber(103),
ScanMode: common.Scanned,
},
})
if err != nil {
panic(err)
}
fmt.Println(hex.EncodeToString(response.Encode()))
terminalClient.Send(response)
groupAddr := common.NewAddressFromSSINumber(103)
identityNum := common.NewAddressFromSSINumber(1007)
response, err = common.NewTMIpApiCallSetupRequest(*groupAddr, common.CallTypeGroup, common.CallPriorityDefault, false, common.TetraFlexIdentityInfo{
Description: "blurb",
Kind: common.IdentityKindTerminal,
Address: *identityNum,
UnifiedSSIKinds: common.UnifiedKindPersonalNumber,
})
if err != nil {
panic(err)
}
fmt.Println(hex.EncodeToString(response.Encode()))
terminalClient.Send(response)
}
if parsedMsg.Kind == tmkind.IpApiRegistrationConfirm {
regInfo, err := parsedMsg.AsRegistrationInfo()
if err != nil {
panic(err)
}
fmt.Println(hex.EncodeToString(parsedMsg.Bytes()))
regInfoBytes, _ := json.Marshal(regInfo)
fmt.Println(string(regInfoBytes))
}
//if parsedMsg.Kind == tmkind.IpApiNo
}
}
}
Loading…
Cancel
Save