kappanhang/main.go
2020-10-17 19:09:53 +02:00

395 lines
15 KiB
Go

package main
import (
"bytes"
"crypto/rand"
"encoding/binary"
"fmt"
"net"
"os"
"os/signal"
"syscall"
"time"
"github.com/nonoo/kappanhang/log"
)
var conn *net.UDPConn
var localSID uint32
var remoteSID uint32
var sendSeq uint16
var authSendSeq uint16
var authInnerSendSeq uint16
var authID [6]byte
var randIDByteForPktSeven [1]byte
var expectedPkt7ReplySeq uint16
var lastReauthAt time.Time
func send(d []byte) {
_, err := conn.Write(d)
if err != nil {
log.Fatal(err)
}
}
func read() ([]byte, error) {
err := conn.SetReadDeadline(time.Now().Add(time.Second))
if err != nil {
log.Fatal(err)
}
b := make([]byte, 1500)
n, _, err := conn.ReadFromUDP(b)
if err != nil {
if err, ok := err.(net.Error); ok && !err.Timeout() {
log.Fatal(err)
}
}
return b[:n], err
}
func setupCloseHandler() {
c := make(chan os.Signal)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
go func() {
<-c
log.Print("disconnecting")
sendDisconnect()
os.Exit(0)
}()
}
func 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] = randIDByteForPktSeven[0]
replyID[3] = 0x03
} else {
replyFlag = 0x01
}
expectedPkt7ReplySeq = sendSeq
send([]byte{0x15, 0x00, 0x00, 0x00, 0x07, 0x00, byte(seq), byte(seq >> 8),
byte(localSID >> 24), byte(localSID >> 16), byte(localSID >> 8), byte(localSID),
byte(remoteSID >> 24), byte(remoteSID >> 16), byte(remoteSID >> 8), byte(remoteSID),
replyFlag, replyID[0], replyID[1], replyID[2], replyID[3]})
}
func sendPkt3() {
send([]byte{0x10, 0x00, 0x00, 0x00, 0x03, 0x00, byte(sendSeq), byte(sendSeq >> 8),
byte(localSID >> 24), byte(localSID >> 16), byte(localSID >> 8), byte(localSID),
byte(remoteSID >> 24), byte(remoteSID >> 16), byte(remoteSID >> 8), byte(remoteSID)})
}
func sendPkt6() {
send([]byte{0x10, 0x00, 0x00, 0x00, 0x06, 0x00, 0x01, 0x00,
byte(localSID >> 24), byte(localSID >> 16), byte(localSID >> 8), byte(localSID),
byte(remoteSID >> 24), byte(remoteSID >> 16), byte(remoteSID >> 8), byte(remoteSID)})
}
func 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)
}
send([]byte{0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
byte(localSID >> 24), byte(localSID >> 16), byte(localSID >> 8), byte(localSID),
byte(remoteSID >> 24), byte(remoteSID >> 16), byte(remoteSID >> 8), byte(remoteSID),
0x00, 0x00, 0x00, 0x70, 0x01, 0x00, 0x00, byte(authInnerSendSeq),
byte(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})
authSendSeq++
authInnerSendSeq++
}
func 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
send([]byte{0x40, 0x00, 0x00, 0x00, 0x00, 0x00, byte(authSendSeq), byte(authSendSeq >> 8),
byte(localSID >> 24), byte(localSID >> 16), byte(localSID >> 8), byte(localSID),
byte(remoteSID >> 24), byte(remoteSID >> 16), byte(remoteSID >> 8), byte(remoteSID),
0x00, 0x00, 0x00, 0x30, 0x01, magic, 0x00, byte(authInnerSendSeq),
byte(authInnerSendSeq >> 8), 0x00, authID[0], authID[1], authID[2], authID[3], authID[4], 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})
authSendSeq++
authInnerSendSeq++
lastReauthAt = time.Now()
}
func sendDisconnect() {
send([]byte{0x40, 0x00, 0x00, 0x00, 0x00, 0x00, byte(sendSeq), byte(sendSeq >> 8),
byte(localSID >> 24), byte(localSID >> 16), byte(localSID >> 8), byte(localSID),
byte(remoteSID >> 24), byte(remoteSID >> 16), byte(remoteSID >> 8), byte(remoteSID),
0x00, 0x00, 0x00, 0x30, 0x01, 0x01, 0x00, byte(authInnerSendSeq),
byte(authInnerSendSeq >> 8), 0x00, authID[0], authID[1], authID[2], authID[3], authID[4], 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})
send([]byte{0x10, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
byte(localSID >> 24), byte(localSID >> 16), byte(localSID >> 8), byte(localSID),
byte(remoteSID >> 24), byte(remoteSID >> 16), byte(remoteSID >> 8), byte(remoteSID)})
}
func sendRequestSerialAndAudio() {
log.Print("requesting serial and audio stream")
send([]byte{0x90, 0x00, 0x00, 0x00, 0x00, 0x00, byte(authSendSeq), byte(authSendSeq >> 8),
byte(localSID >> 24), byte(localSID >> 16), byte(localSID >> 8), byte(localSID), byte(remoteSID >> 24), byte(remoteSID >> 16), byte(remoteSID >> 8), byte(remoteSID),
0x00, 0x00, 0x00, 0x80, 0x01, 0x03, 0x00, byte(authInnerSendSeq),
byte(authInnerSendSeq >> 8), 0x00, authID[0], authID[1], authID[2], authID[3], authID[4], 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})
authSendSeq++
authInnerSendSeq++
}
func main() {
log.Init()
parseArgs()
setupCloseHandler()
hostPort := fmt.Sprint(connectAddress, ":50001")
log.Print("connecting to ", hostPort)
raddr, err := net.ResolveUDPAddr("udp", hostPort)
if err != nil {
log.Fatal(err)
}
laddr := net.UDPAddr{
Port: 50001,
}
conn, err = net.DialUDP("udp", &laddr, raddr)
if err != nil {
log.Fatal(err)
}
localSID = uint32(time.Now().Unix())
log.Debugf("using session id %.8x", localSID)
sendPkt3()
sendSeq = 1
sendPkt7(nil, sendSeq)
sendSeq = 0
sendPkt3()
for {
// 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, _ := read()
if len(r) == 16 && bytes.Equal(r[:8], []byte{0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00}) {
remoteSID = binary.BigEndian.Uint32(r[8:12])
break
}
}
log.Debugf("got remote session id %.8x", remoteSID)
authSendSeq = 1
authInnerSendSeq = 0x50
sendPkt6()
for {
// Expecting a Pkt6 answer.
r, _ := read()
if len(r) == 16 && bytes.Equal(r[:8], []byte{0x10, 0x00, 0x00, 0x00, 0x06, 0x00, 0x01, 0x00}) {
remoteSID = binary.BigEndian.Uint32(r[8:12])
break
}
}
sendPktLogin()
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
for {
r, _ := read()
if len(r) == 96 && bytes.Equal(r[:8], []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(authID[:], r[26:32])
log.Print("auth ok")
sendPktReauth(true)
time.AfterFunc(time.Second*2, sendRequestSerialAndAudio)
break
}
}
var lastPingAt time.Time
var lastStatusLog time.Time
var errCount int
_, err = rand.Read(randIDByteForPktSeven[:])
if err != nil {
log.Fatal(err)
}
for {
r, err := 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
sendPkt7(r[17:21], gotSeq)
} else {
if expectedPkt7ReplySeq != gotSeq { // TODO
var missingPkts int
if gotSeq > expectedPkt7ReplySeq {
missingPkts = int(gotSeq) - int(expectedPkt7ReplySeq)
} else {
missingPkts = int(gotSeq) + 65536 - int(expectedPkt7ReplySeq)
}
if missingPkts < 1000 {
log.Error("lost ", missingPkts, " packets ", gotSeq, " ", 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])
send([]byte{0x10, 0x00, 0x00, 0x00, 0x00, 0x00, byte(gotSeq), byte(gotSeq >> 8), byte(localSID >> 24), byte(localSID >> 16), byte(localSID >> 8), byte(localSID), byte(remoteSID >> 24), byte(remoteSID >> 16), byte(remoteSID >> 8), byte(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")
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 openAudioStream()
}
if time.Since(lastPingAt) >= 100*time.Millisecond {
sendPkt7(nil, sendSeq)
sendPkt3()
sendSeq++
lastPingAt = time.Now()
if time.Since(lastReauthAt) >= 60*time.Second {
sendPktReauth(false)
}
if time.Since(lastStatusLog) >= 10*time.Second {
log.Print("still connected")
lastStatusLog = time.Now()
}
}
}
}