kappanhang/portcontrol.go
2020-10-17 23:53:33 +02:00

313 lines
14 KiB
Go

package main
import (
"bytes"
"crypto/rand"
"encoding/binary"
"os"
"time"
"github.com/nonoo/kappanhang/log"
)
type portControl struct {
port portCommon
authSendSeq uint16
authInnerSendSeq uint16
authID [6]byte
randIDByteForPktSeven [1]byte
expectedPkt7ReplySeq uint16
lastReauthAt time.Time
}
func (p *portControl) sendPkt7(replyID []byte, seq uint16) {
// Example request from PC: 0x15, 0x00, 0x00, 0x00, 0x07, 0x00, 0x09, 0x00, 0xbe, 0xd9, 0xf2, 0x63, 0xe4, 0x35, 0xdd, 0x72, 0x00, 0x78, 0x40, 0xf6, 0x02
// Example reply from radio: 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x09, 0x00, 0xe4, 0x35, 0xdd, 0x72, 0xbe, 0xd9, 0xf2, 0x63, 0x01, 0x78, 0x40, 0xf6, 0x02
var replyFlag byte
if replyID == nil {
replyID = make([]byte, 4)
var randID [2]byte
_, err := rand.Read(randID[:])
if err != nil {
log.Fatal(err)
}
replyID[0] = randID[0]
replyID[1] = randID[1]
replyID[2] = p.randIDByteForPktSeven[0]
replyID[3] = 0x03
} else {
replyFlag = 0x01
}
p.expectedPkt7ReplySeq = p.port.sendSeq
p.port.send([]byte{0x15, 0x00, 0x00, 0x00, 0x07, 0x00, byte(seq), byte(seq >> 8),
byte(p.port.localSID >> 24), byte(p.port.localSID >> 16), byte(p.port.localSID >> 8), byte(p.port.localSID),
byte(p.port.remoteSID >> 24), byte(p.port.remoteSID >> 16), byte(p.port.remoteSID >> 8), byte(p.port.remoteSID),
replyFlag, replyID[0], replyID[1], replyID[2], replyID[3]})
}
func (p *portControl) sendPktLogin() {
// The reply to the login packet will contain a 6 bytes long auth ID with the first 2 bytes set to our randID.
var randID [2]byte
_, err := rand.Read(randID[:])
if err != nil {
log.Fatal(err)
}
p.port.send([]byte{0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
byte(p.port.localSID >> 24), byte(p.port.localSID >> 16), byte(p.port.localSID >> 8), byte(p.port.localSID),
byte(p.port.remoteSID >> 24), byte(p.port.remoteSID >> 16), byte(p.port.remoteSID >> 8), byte(p.port.remoteSID),
0x00, 0x00, 0x00, 0x70, 0x01, 0x00, 0x00, byte(p.authInnerSendSeq),
byte(p.authInnerSendSeq >> 8), 0x00, randID[0], randID[1], 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x2b, 0x3f, 0x55, 0x5c, 0x00, 0x00, 0x00, 0x00, // username: beer
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x2b, 0x3f, 0x55, 0x5c, 0x3f, 0x25, 0x77, 0x58, // pass: beerbeer
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x69, 0x63, 0x6f, 0x6d, 0x2d, 0x70, 0x63, 0x00, // icom-pc in plain text
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
p.authSendSeq++
p.authInnerSendSeq++
}
func (p *portControl) sendPktReauth(firstReauthSend bool) {
var magic byte
if firstReauthSend {
magic = 0x02
} else {
magic = 0x05
}
// Example request from PC: 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00,
// 0xbb, 0x41, 0x3f, 0x2b, 0xe6, 0xb2, 0x7b, 0x7b,
// 0x00, 0x00, 0x00, 0x30, 0x01, 0x05, 0x00, 0x02,
// 0x00, 0x00, 0x5d, 0x37, 0x12, 0x82, 0x3b, 0xde,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
// Example reply from radio: 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00,
// 0xe6, 0xb2, 0x7b, 0x7b, 0xbb, 0x41, 0x3f, 0x2b,
// 0x00, 0x00, 0x00, 0x30, 0x02, 0x05, 0x00, 0x02,
// 0x00, 0x00, 0x5d, 0x37, 0x12, 0x82, 0x3b, 0xde,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
p.port.send([]byte{0x40, 0x00, 0x00, 0x00, 0x00, 0x00, byte(p.authSendSeq), byte(p.authSendSeq >> 8),
byte(p.port.localSID >> 24), byte(p.port.localSID >> 16), byte(p.port.localSID >> 8), byte(p.port.localSID),
byte(p.port.remoteSID >> 24), byte(p.port.remoteSID >> 16), byte(p.port.remoteSID >> 8), byte(p.port.remoteSID),
0x00, 0x00, 0x00, 0x30, 0x01, magic, 0x00, byte(p.authInnerSendSeq),
byte(p.authInnerSendSeq >> 8), 0x00, p.authID[0], p.authID[1], p.authID[2], p.authID[3], p.authID[4], p.authID[5],
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
p.authSendSeq++
p.authInnerSendSeq++
p.lastReauthAt = time.Now()
}
func (p *portControl) SendDisconnect() {
p.port.send([]byte{0x40, 0x00, 0x00, 0x00, 0x00, 0x00, byte(p.port.sendSeq), byte(p.port.sendSeq >> 8),
byte(p.port.localSID >> 24), byte(p.port.localSID >> 16), byte(p.port.localSID >> 8), byte(p.port.localSID),
byte(p.port.remoteSID >> 24), byte(p.port.remoteSID >> 16), byte(p.port.remoteSID >> 8), byte(p.port.remoteSID),
0x00, 0x00, 0x00, 0x30, 0x01, 0x01, 0x00, byte(p.authInnerSendSeq),
byte(p.authInnerSendSeq >> 8), 0x00, p.authID[0], p.authID[1], p.authID[2], p.authID[3], p.authID[4], p.authID[5],
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
p.port.send([]byte{0x10, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
byte(p.port.localSID >> 24), byte(p.port.localSID >> 16), byte(p.port.localSID >> 8), byte(p.port.localSID),
byte(p.port.remoteSID >> 24), byte(p.port.remoteSID >> 16), byte(p.port.remoteSID >> 8), byte(p.port.remoteSID)})
}
func (p *portControl) sendRequestSerialAndAudio() {
log.Print("requesting serial and audio stream")
p.port.send([]byte{0x90, 0x00, 0x00, 0x00, 0x00, 0x00, byte(p.authSendSeq), byte(p.authSendSeq >> 8),
byte(p.port.localSID >> 24), byte(p.port.localSID >> 16), byte(p.port.localSID >> 8), byte(p.port.localSID),
byte(p.port.remoteSID >> 24), byte(p.port.remoteSID >> 16), byte(p.port.remoteSID >> 8), byte(p.port.remoteSID),
0x00, 0x00, 0x00, 0x80, 0x01, 0x03, 0x00, byte(p.authInnerSendSeq),
byte(p.authInnerSendSeq >> 8), 0x00, p.authID[0], p.authID[1], p.authID[2], p.authID[3], p.authID[4], p.authID[5],
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
0x80, 0x00, 0x00, 0x90, 0xc7, 0x0e, 0x86, 0x01, // The last 5 bytes from this row can be acquired from a reply starting with 0xa8 or 0x90
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x49, 0x43, 0x2d, 0x37, 0x30, 0x35, 0x00, 0x00, // IC-705 in plain text
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x2b, 0x3f, 0x55, 0x5c, 0x00, 0x00, 0x00, 0x00, // username: beer
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x01, 0x04, 0x04, 0x00, 0x00, 0xbb, 0x80,
0x00, 0x00, 0xbb, 0x80, 0x00, 0x00, 0xc3, 0x52,
0x00, 0x00, 0xc3, 0x53, 0x00, 0x00, 0x00, 0x64,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
p.authSendSeq++
p.authInnerSendSeq++
}
func (p *portControl) StartStream() {
p.port.open(50001)
p.port.sendPkt3()
p.port.sendSeq = 1
p.sendPkt7(nil, p.port.sendSeq)
p.port.sendSeq = 0
p.port.sendPkt3()
// Expecting a Pkt4 answer.
// Example answer from radio: 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x8c, 0x7d, 0x45, 0x7a, 0x1d, 0xf6, 0xe9, 0x0b
r := p.port.expect(16, []byte{0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00})
p.port.remoteSID = binary.BigEndian.Uint32(r[8:12])
log.Debugf("got remote session id %.8x", p.port.remoteSID)
p.authSendSeq = 1
p.authInnerSendSeq = 0x50
p.port.sendPkt6()
// Expecting a Pkt6 answer.
r = p.port.expect(16, []byte{0x10, 0x00, 0x00, 0x00, 0x06, 0x00, 0x01, 0x00})
p.port.remoteSID = binary.BigEndian.Uint32(r[8:12]) // TODO
p.sendPktLogin()
p.port.sendSeq = 5
// Example success auth packet: 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
// 0xe6, 0xb2, 0x7b, 0x7b, 0xbb, 0x41, 0x3f, 0x2b,
// 0x00, 0x00, 0x00, 0x50, 0x02, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x5d, 0x37, 0x12, 0x82, 0x3b, 0xde,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x46, 0x54, 0x54, 0x48, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
r = p.port.expect(96, []byte{0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00})
if bytes.Equal(r[48:52], []byte{0xff, 0xff, 0xff, 0xfe}) {
log.Fatal("invalid user/password")
}
copy(p.authID[:], r[26:32])
log.Print("auth ok")
p.sendPktReauth(true)
time.AfterFunc(time.Second*2, p.sendRequestSerialAndAudio)
var lastPingAt time.Time
var lastStatusLog time.Time
var errCount int
_, err := rand.Read(p.randIDByteForPktSeven[:])
if err != nil {
log.Fatal(err)
}
for {
r, err := p.port.read()
if err != nil {
errCount++
if errCount > 5 {
log.Fatal("timeout")
}
log.Error("stream break detected")
}
errCount = 0
if len(r) == 21 && bytes.Equal(r[1:6], []byte{0x00, 0x00, 0x00, 0x07, 0x00}) {
gotSeq := binary.LittleEndian.Uint16(r[6:8])
if r[16] == 0x00 { // This is a pkt7 request from the radio.
// Replying to the radio.
// Example request from radio: 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x1c, 0x0e, 0xe4, 0x35, 0xdd, 0x72, 0xbe, 0xd9, 0xf2, 0x63, 0x00, 0x57, 0x2b, 0x12, 0x00
// Example answer from PC: 0x15, 0x00, 0x00, 0x00, 0x07, 0x00, 0x1c, 0x0e, 0xbe, 0xd9, 0xf2, 0x63, 0xe4, 0x35, 0xdd, 0x72, 0x01, 0x57, 0x2b, 0x12, 0x00
p.sendPkt7(r[17:21], gotSeq)
} else {
if p.expectedPkt7ReplySeq != gotSeq { // TODO
var missingPkts int
if gotSeq > p.expectedPkt7ReplySeq {
missingPkts = int(gotSeq) - int(p.expectedPkt7ReplySeq)
} else {
missingPkts = int(gotSeq) + 65536 - int(p.expectedPkt7ReplySeq)
}
if missingPkts < 1000 {
log.Error("lost ", missingPkts, " packets ", gotSeq, " ", p.expectedPkt7ReplySeq)
}
}
}
}
if len(r) == 16 && bytes.Equal(r[:6], []byte{0x10, 0x00, 0x00, 0x00, 0x00, 0x00}) {
// Replying to the radio.
// Example request from radio: 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0xe4, 0x35, 0xdd, 0x72, 0xbe, 0xd9, 0xf2, 0x63
// Example answer from PC: 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0xbe, 0xd9, 0xf2, 0x63, 0xe4, 0x35, 0xdd, 0x72
gotSeq := binary.LittleEndian.Uint16(r[6:8])
p.port.send([]byte{0x10, 0x00, 0x00, 0x00, 0x00, 0x00, byte(gotSeq), byte(gotSeq >> 8),
byte(p.port.localSID >> 24), byte(p.port.localSID >> 16), byte(p.port.localSID >> 8), byte(p.port.localSID),
byte(p.port.remoteSID >> 24), byte(p.port.remoteSID >> 16), byte(p.port.remoteSID >> 8), byte(p.port.remoteSID)})
}
if len(r) == 80 && bytes.Equal(r[:6], []byte{0x50, 0x00, 0x00, 0x00, 0x00, 0x00}) && bytes.Equal(r[48:51], []byte{0xff, 0xff, 0xff}) {
// Example answer from radio: 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00,
// 0x86, 0x1f, 0x2f, 0xcc, 0x03, 0x03, 0x89, 0x29,
// 0x00, 0x00, 0x00, 0x40, 0x02, 0x03, 0x00, 0x52,
// 0x00, 0x00, 0xf8, 0xad, 0x06, 0x8d, 0xda, 0x7b,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
// 0x80, 0x00, 0x00, 0x90, 0xc7, 0x0e, 0x86, 0x01,
// 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
log.Error("reauth failed")
p.SendDisconnect()
os.Exit(1)
}
if len(r) == 144 && bytes.Equal(r[:6], []byte{0x90, 0x00, 0x00, 0x00, 0x00, 0x00}) && r[96] == 1 {
// Example answer:
// 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00,
// 0xc6, 0x5f, 0x6f, 0x0c, 0x5f, 0x8b, 0x1e, 0x89,
// 0x00, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x31, 0x30, 0x31, 0x47, 0x39, 0x07,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
// 0x80, 0x00, 0x00, 0x90, 0xc7, 0x0e, 0x86, 0x01,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x49, 0x43, 0x2d, 0x37, 0x30, 0x35, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x01, 0x00, 0x00, 0x00, 0x69, 0x63, 0x6f, 0x6d,
// 0x2d, 0x70, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0xc0, 0xa8, 0x03, 0x03,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
log.Print("serial and audio request success")
go ports.audio.StartStream()
}
if time.Since(lastPingAt) >= 100*time.Millisecond {
p.sendPkt7(nil, p.port.sendSeq)
p.port.sendPkt3()
p.port.sendSeq++
lastPingAt = time.Now()
if time.Since(p.lastReauthAt) >= 60*time.Second {
p.sendPktReauth(false)
}
if time.Since(lastStatusLog) >= 10*time.Second {
log.Print("still connected")
lastStatusLog = time.Now()
}
}
}
}