kappanhang/main.go

322 lines
12 KiB
Go
Raw Normal View History

2020-10-16 17:13:46 +02:00
package main
import (
"bytes"
2020-10-16 18:22:22 +02:00
"crypto/rand"
2020-10-16 17:13:46 +02:00
"encoding/binary"
"fmt"
"net"
2020-10-16 19:25:02 +02:00
"os"
"os/signal"
"syscall"
2020-10-16 17:13:46 +02:00
"time"
"github.com/nonoo/kappanhang/log"
)
var conn *net.UDPConn
2020-10-16 19:25:02 +02:00
var localSID uint32
var remoteSID uint32
var sendSeq uint16
2020-10-16 23:37:12 +02:00
var authSendSeq uint16
2020-10-16 19:25:02 +02:00
var authID [6]byte
2020-10-16 23:37:12 +02:00
var randIDByteForPktSeven [1]byte
var expectedPkt7ReplySeq uint16
2020-10-16 17:13:46 +02:00
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
}
2020-10-16 19:25:02 +02:00
func setupCloseHandler() {
c := make(chan os.Signal)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
go func() {
<-c
log.Print("disconnecting")
2020-10-16 23:37:12 +02:00
sendDisconnect()
2020-10-16 19:25:02 +02:00
os.Exit(0)
}()
}
2020-10-17 12:15:56 +02:00
func sendPkt7(replyID []byte, seq uint16) {
2020-10-16 23:37:12 +02:00
// 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
2020-10-17 12:15:56 +02:00
send([]byte{0x15, 0x00, 0x00, 0x00, 0x07, 0x00, byte(seq), byte(seq >> 8),
2020-10-16 23:37:12 +02:00
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() {
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(authSendSeq),
byte(authSendSeq >> 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
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
}
func sendPktReauth() {
// 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(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, 0x05, 0x00, byte(authSendSeq),
byte(authSendSeq >> 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})
}
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(authSendSeq),
byte(authSendSeq >> 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() {
send([]byte{0x90, 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, 0x80, 0x01, 0x03, 0x00, byte(authSendSeq),
byte(authSendSeq >> 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 pr 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
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})
}
2020-10-16 17:13:46 +02:00
func main() {
log.Init()
parseArgs()
2020-10-16 19:25:02 +02:00
setupCloseHandler()
2020-10-16 17:13:46 +02:00
hostPort := fmt.Sprint(connectAddress, ":", connectPort)
log.Print("connecting to ", hostPort)
raddr, err := net.ResolveUDPAddr("udp", hostPort)
if err != nil {
log.Fatal(err)
}
conn, err = net.DialUDP("udp", nil, raddr)
if err != nil {
log.Fatal(err)
}
// Constructing the local session ID by combining the local IP address and port.
laddr := conn.LocalAddr().(*net.UDPAddr)
2020-10-16 19:25:02 +02:00
localSID = binary.BigEndian.Uint32(laddr.IP[len(laddr.IP)-4:])<<16 | uint32(laddr.Port&0xffff)
2020-10-16 17:13:46 +02:00
log.Debugf("using session id %.8x", localSID)
2020-10-16 23:37:12 +02:00
sendPkt3()
sendSeq = 1
2020-10-17 12:15:56 +02:00
sendPkt7(nil, sendSeq)
2020-10-16 23:37:12 +02:00
sendSeq = 0
sendPkt3()
2020-10-16 17:13:46 +02:00
for {
2020-10-16 23:37:12 +02:00
// Expecting a Pkt4 answer.
2020-10-17 11:05:07 +02:00
// Example answer from radio: 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x8c, 0x7d, 0x45, 0x7a, 0x1d, 0xf6, 0xe9, 0x0b
2020-10-16 17:13:46 +02:00
r, _ := read()
if 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)
2020-10-16 23:37:12 +02:00
sendSeq = 1
authSendSeq = 1
sendPkt6()
sendPktLogin()
authSendSeq++
2020-10-16 17:13:46 +02:00
var lastPingAt time.Time
2020-10-16 23:37:12 +02:00
var lastReauthAt time.Time
var lastStatusLog time.Time
2020-10-16 17:13:46 +02:00
var errCount int
2020-10-16 23:37:12 +02:00
_, err = rand.Read(randIDByteForPktSeven[:])
if err != nil {
log.Fatal(err)
}
2020-10-16 17:13:46 +02:00
for {
r, err := read()
if err != nil {
errCount++
if errCount > 5 {
log.Fatal("timeout")
}
log.Error("stream break detected")
}
errCount = 0
2020-10-16 18:51:22 +02:00
// 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
2020-10-16 17:43:30 +02:00
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")
} else {
2020-10-16 18:51:22 +02:00
copy(authID[:], r[26:32])
2020-10-16 17:43:30 +02:00
log.Print("auth ok")
}
}
2020-10-16 18:22:22 +02:00
if len(r) == 21 && bytes.Equal(r[1:6], []byte{0x00, 0x00, 0x00, 0x07, 0x00}) {
2020-10-17 12:15:56 +02:00
gotSeq := binary.LittleEndian.Uint16(r[6:8])
2020-10-16 23:37:12 +02:00
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
2020-10-17 12:15:56 +02:00
sendPkt7(r[17:21], gotSeq)
2020-10-16 23:37:12 +02:00
} else {
if expectedPkt7ReplySeq != gotSeq {
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)
}
}
2020-10-16 17:13:46 +02:00
}
2020-10-16 18:22:22 +02:00
}
if len(r) == 16 && bytes.Equal(r[:6], []byte{0x10, 0x00, 0x00, 0x00, 0x00, 0x00}) {
// Replying to the radio.
2020-10-16 18:51:22 +02:00
// 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
2020-10-16 19:25:02 +02:00
gotSeq := binary.LittleEndian.Uint16(r[6:8])
2020-10-16 23:37:12 +02:00
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)})
2020-10-16 18:22:22 +02:00
}
2020-10-16 17:13:46 +02:00
2020-10-16 18:22:22 +02:00
if time.Since(lastPingAt) >= 100*time.Millisecond {
2020-10-17 12:15:56 +02:00
sendPkt7(nil, sendSeq)
2020-10-16 23:37:12 +02:00
sendPkt3()
2020-10-16 17:13:46 +02:00
sendSeq++
lastPingAt = time.Now()
2020-10-16 18:51:22 +02:00
2020-10-16 23:37:12 +02:00
if time.Since(lastReauthAt) >= 60*time.Second {
sendPktReauth()
authSendSeq++
lastReauthAt = time.Now()
}
if time.Since(lastStatusLog) >= 10*time.Second {
log.Print("still connected")
lastStatusLog = time.Now()
2020-10-16 18:51:22 +02:00
}
2020-10-16 17:13:46 +02:00
}
}
}