diff --git a/audio.go b/audio.go deleted file mode 100644 index 7b41b6c..0000000 --- a/audio.go +++ /dev/null @@ -1,82 +0,0 @@ -package main - -import ( - "bytes" - "encoding/binary" - "fmt" - "net" - "time" - - "github.com/nonoo/kappanhang/log" -) - -var audioConn *net.UDPConn -var audioSendSeq uint16 -var localAudioSID uint32 -var remoteAudioSID uint32 - -func audioStreamSend(d []byte) { - _, err := audioConn.Write(d) - if err != nil { - log.Fatal(err) - } -} - -func readAudioStream() ([]byte, error) { - err := audioConn.SetReadDeadline(time.Now().Add(time.Second)) - if err != nil { - log.Fatal(err) - } - - b := make([]byte, 1500) - n, _, err := audioConn.ReadFromUDP(b) - if err != nil { - if err, ok := err.(net.Error); ok && !err.Timeout() { - log.Fatal(err) - } - } - return b[:n], err -} - -func sendAudioPkt3() { - audioStreamSend([]byte{0x10, 0x00, 0x00, 0x00, 0x03, 0x00, byte(audioSendSeq), byte(audioSendSeq >> 8), - byte(localAudioSID >> 24), byte(localAudioSID >> 16), byte(localAudioSID >> 8), byte(localAudioSID), - byte(remoteAudioSID >> 24), byte(remoteAudioSID >> 16), byte(remoteAudioSID >> 8), byte(remoteAudioSID)}) -} - -func sendAudioPkt6() { - audioStreamSend([]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 openAudioStream() { - hostPort := fmt.Sprint(connectAddress, ":50003") - log.Print("connecting to ", hostPort) - raddr, err := net.ResolveUDPAddr("udp", hostPort) - if err != nil { - log.Fatal(err) - } - laddr := net.UDPAddr{ - Port: 50003, - } - audioConn, err = net.DialUDP("udp", &laddr, raddr) - if err != nil { - log.Fatal(err) - } - - localAudioSID = uint32(time.Now().Unix()) - log.Debugf("using audio session id %.8x", localAudioSID) - - sendAudioPkt3() - 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, _ := readAudioStream() - 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 - } - } - sendAudioPkt6() -} diff --git a/main.go b/main.go index 3f1ff90..50ca79f 100644 --- a/main.go +++ b/main.go @@ -1,52 +1,15 @@ 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 -} +var portControl PortControl +var portAudio PortAudio func setupCloseHandler() { c := make(chan os.Signal) @@ -54,341 +17,15 @@ func setupCloseHandler() { go func() { <-c log.Print("disconnecting") - sendDisconnect() + portControl.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() - } - } - } + portControl.StartStream() } diff --git a/portaudio.go b/portaudio.go new file mode 100644 index 0000000..6be9ade --- /dev/null +++ b/portaudio.go @@ -0,0 +1,97 @@ +package main + +import ( + "bytes" + "encoding/binary" + "fmt" + "net" + "time" + + "github.com/nonoo/kappanhang/log" +) + +type PortAudio struct { + conn *net.UDPConn + sendSeq uint16 + localSID uint32 + remoteSID uint32 +} + +func (p *PortAudio) send(d []byte) { + _, err := p.conn.Write(d) + if err != nil { + log.Fatal(err) + } +} + +func (p *PortAudio) read() ([]byte, error) { + err := p.conn.SetReadDeadline(time.Now().Add(time.Second)) + if err != nil { + log.Fatal(err) + } + + b := make([]byte, 1500) + n, _, err := p.conn.ReadFromUDP(b) + if err != nil { + if err, ok := err.(net.Error); ok && !err.Timeout() { + log.Fatal(err) + } + } + return b[:n], err +} + +func (p *PortAudio) expect(packetLength int, b []byte) []byte { + var r []byte + expectStart := time.Now() + for { + r, _ = p.read() + if len(r) == packetLength && bytes.Equal(r[:len(b)], b) { + break + } + if time.Since(expectStart) > time.Second { + log.Fatal("expect timeout") + } + } + return r +} + +func (p *PortAudio) sendAudioPkt3() { + p.send([]byte{0x10, 0x00, 0x00, 0x00, 0x03, 0x00, byte(p.sendSeq), byte(p.sendSeq >> 8), + byte(p.localSID >> 24), byte(p.localSID >> 16), byte(p.localSID >> 8), byte(p.localSID), + byte(p.remoteSID >> 24), byte(p.remoteSID >> 16), byte(p.remoteSID >> 8), byte(p.remoteSID)}) +} + +func (p *PortAudio) sendAudioPkt6() { + p.send([]byte{0x10, 0x00, 0x00, 0x00, 0x06, 0x00, 0x01, 0x00, + byte(p.localSID >> 24), byte(p.localSID >> 16), byte(p.localSID >> 8), byte(p.localSID), + byte(p.remoteSID >> 24), byte(p.remoteSID >> 16), byte(p.remoteSID >> 8), byte(p.remoteSID)}) +} + +func (p *PortAudio) StartStream() { + hostPort := fmt.Sprint(connectAddress, ":50003") + log.Print("connecting to ", hostPort) + raddr, err := net.ResolveUDPAddr("udp", hostPort) + if err != nil { + log.Fatal(err) + } + laddr := net.UDPAddr{ + Port: 50003, + } + p.conn, err = net.DialUDP("udp", &laddr, raddr) + if err != nil { + log.Fatal(err) + } + + p.localSID = uint32(time.Now().Unix()) + log.Debugf("using local session id %.8x", p.localSID) + + p.sendAudioPkt3() + + // 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.expect(16, []byte{0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00}) + p.remoteSID = binary.BigEndian.Uint32(r[8:12]) + p.sendAudioPkt6() + + log.Debugf("got remote session id %.8x", p.remoteSID) +} diff --git a/portcontrol.go b/portcontrol.go new file mode 100644 index 0000000..1757ee1 --- /dev/null +++ b/portcontrol.go @@ -0,0 +1,379 @@ +package main + +import ( + "bytes" + "crypto/rand" + "encoding/binary" + "fmt" + "net" + "os" + "time" + + "github.com/nonoo/kappanhang/log" +) + +type PortControl struct { + conn *net.UDPConn + localSID uint32 + remoteSID uint32 + sendSeq uint16 + authSendSeq uint16 + authInnerSendSeq uint16 + authID [6]byte + randIDByteForPktSeven [1]byte + expectedPkt7ReplySeq uint16 + lastReauthAt time.Time +} + +func (p *PortControl) send(d []byte) { + _, err := p.conn.Write(d) + if err != nil { + log.Fatal(err) + } +} + +func (p *PortControl) read() ([]byte, error) { + err := p.conn.SetReadDeadline(time.Now().Add(time.Second)) + if err != nil { + log.Fatal(err) + } + + b := make([]byte, 1500) + n, _, err := p.conn.ReadFromUDP(b) + if err != nil { + log.Fatal(err) + } + return b[:n], err +} + +func (p *PortControl) expect(packetLength int, b []byte) []byte { + var r []byte + expectStart := time.Now() + for { + r, _ = p.read() + if len(r) == packetLength && bytes.Equal(r[:len(b)], b) { + break + } + if time.Since(expectStart) > time.Second { + log.Fatal("expect timeout") + } + } + return r +} + +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.sendSeq + + p.send([]byte{0x15, 0x00, 0x00, 0x00, 0x07, 0x00, byte(seq), byte(seq >> 8), + byte(p.localSID >> 24), byte(p.localSID >> 16), byte(p.localSID >> 8), byte(p.localSID), + byte(p.remoteSID >> 24), byte(p.remoteSID >> 16), byte(p.remoteSID >> 8), byte(p.remoteSID), + replyFlag, replyID[0], replyID[1], replyID[2], replyID[3]}) +} + +func (p *PortControl) sendPkt3() { + p.send([]byte{0x10, 0x00, 0x00, 0x00, 0x03, 0x00, byte(p.sendSeq), byte(p.sendSeq >> 8), + byte(p.localSID >> 24), byte(p.localSID >> 16), byte(p.localSID >> 8), byte(p.localSID), + byte(p.remoteSID >> 24), byte(p.remoteSID >> 16), byte(p.remoteSID >> 8), byte(p.remoteSID)}) +} + +func (p *PortControl) sendPkt6() { + p.send([]byte{0x10, 0x00, 0x00, 0x00, 0x06, 0x00, 0x01, 0x00, + byte(p.localSID >> 24), byte(p.localSID >> 16), byte(p.localSID >> 8), byte(p.localSID), + byte(p.remoteSID >> 24), byte(p.remoteSID >> 16), byte(p.remoteSID >> 8), byte(p.remoteSID)}) +} + +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.send([]byte{0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + byte(p.localSID >> 24), byte(p.localSID >> 16), byte(p.localSID >> 8), byte(p.localSID), + byte(p.remoteSID >> 24), byte(p.remoteSID >> 16), byte(p.remoteSID >> 8), byte(p.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.send([]byte{0x40, 0x00, 0x00, 0x00, 0x00, 0x00, byte(p.authSendSeq), byte(p.authSendSeq >> 8), + byte(p.localSID >> 24), byte(p.localSID >> 16), byte(p.localSID >> 8), byte(p.localSID), + byte(p.remoteSID >> 24), byte(p.remoteSID >> 16), byte(p.remoteSID >> 8), byte(p.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.send([]byte{0x40, 0x00, 0x00, 0x00, 0x00, 0x00, byte(p.sendSeq), byte(p.sendSeq >> 8), + byte(p.localSID >> 24), byte(p.localSID >> 16), byte(p.localSID >> 8), byte(p.localSID), + byte(p.remoteSID >> 24), byte(p.remoteSID >> 16), byte(p.remoteSID >> 8), byte(p.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.send([]byte{0x10, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + byte(p.localSID >> 24), byte(p.localSID >> 16), byte(p.localSID >> 8), byte(p.localSID), + byte(p.remoteSID >> 24), byte(p.remoteSID >> 16), byte(p.remoteSID >> 8), byte(p.remoteSID)}) +} + +func (p *PortControl) sendRequestSerialAndAudio() { + log.Print("requesting serial and audio stream") + p.send([]byte{0x90, 0x00, 0x00, 0x00, 0x00, 0x00, byte(p.authSendSeq), byte(p.authSendSeq >> 8), + byte(p.localSID >> 24), byte(p.localSID >> 16), byte(p.localSID >> 8), byte(p.localSID), byte(p.remoteSID >> 24), byte(p.remoteSID >> 16), byte(p.remoteSID >> 8), byte(p.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() { + 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, + } + p.conn, err = net.DialUDP("udp", &laddr, raddr) + if err != nil { + log.Fatal(err) + } + + p.localSID = uint32(time.Now().Unix()) + log.Debugf("using session id %.8x", p.localSID) + + p.sendPkt3() + p.sendSeq = 1 + p.sendPkt7(nil, p.sendSeq) + p.sendSeq = 0 + p.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.expect(16, []byte{0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00}) + p.remoteSID = binary.BigEndian.Uint32(r[8:12]) + + log.Debugf("got remote session id %.8x", p.remoteSID) + + p.authSendSeq = 1 + p.authInnerSendSeq = 0x50 + p.sendPkt6() + + // Expecting a Pkt6 answer. + r = p.expect(16, []byte{0x10, 0x00, 0x00, 0x00, 0x06, 0x00, 0x01, 0x00}) + p.remoteSID = binary.BigEndian.Uint32(r[8:12]) // TODO + + p.sendPktLogin() + p.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.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.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.send([]byte{0x10, 0x00, 0x00, 0x00, 0x00, 0x00, byte(gotSeq), byte(gotSeq >> 8), + byte(p.localSID >> 24), byte(p.localSID >> 16), byte(p.localSID >> 8), byte(p.localSID), + byte(p.remoteSID >> 24), byte(p.remoteSID >> 16), byte(p.remoteSID >> 8), byte(p.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 portAudio.StartStream() + } + + if time.Since(lastPingAt) >= 100*time.Millisecond { + p.sendPkt7(nil, p.sendSeq) + p.sendPkt3() + p.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() + } + } + } +}