114 lines
2.8 KiB
Go
114 lines
2.8 KiB
Go
package TPLinkClient
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"fmt"
|
|
)
|
|
|
|
// Most of the protocol implementation is from:
|
|
// https://github.com/philippechataignon/smrt/blob/master/protocol.py
|
|
|
|
func doQuery(header *PacketHeader, payload []byte) []byte {
|
|
header.OpCode = Get
|
|
header.SequenceID = (header.SequenceID + 1) % 1000
|
|
|
|
// Send the query packet
|
|
packet := assmblePacket(*header, payload)
|
|
applyKey(&packet)
|
|
sendPacket(packet)
|
|
|
|
// Listen for a response
|
|
buf := make([]byte, 8000)
|
|
len := recvPacket(&buf)
|
|
if len < 1 {
|
|
return []byte{}
|
|
}
|
|
data := make([]byte, len)
|
|
copy(data, buf)
|
|
applyKey(&data)
|
|
|
|
return data
|
|
}
|
|
|
|
func assmblePacket(header PacketHeader, payload []byte) []byte {
|
|
// Packet size is: 32 bytes header + length of the payload + length of the end bytes
|
|
header.CheckLength = int16(32 + len(payload) + len(PACKET_END))
|
|
|
|
// Encode the header
|
|
var headerBytes bytes.Buffer
|
|
err := binary.Write(&headerBytes, binary.BigEndian, header)
|
|
if err != nil {
|
|
fmt.Println("Failed generating header bytes")
|
|
}
|
|
|
|
// Concatenate header, payload and end bytes
|
|
return append(headerBytes.Bytes(), append(payload, PACKET_END...)...)
|
|
}
|
|
|
|
func buildPayload(requestType uint16, data []byte) []byte {
|
|
var payload = make([]byte, 4)
|
|
binary.BigEndian.PutUint16(payload[:2], requestType)
|
|
copy(payload[2:4], data)
|
|
return payload
|
|
}
|
|
|
|
func decodeHeader(data []byte) PacketHeader {
|
|
var header PacketHeader
|
|
// Decode header bytes to struct
|
|
buf := bytes.NewBuffer(data)
|
|
binary.Read(buf, binary.BigEndian, &header)
|
|
return header
|
|
}
|
|
|
|
func decodePayload(data []byte) []PayloadItem {
|
|
// Payload begins at byte 32 (after the header)
|
|
payload := make([]byte, len(data)-32)
|
|
copy(payload[:], data[32:])
|
|
|
|
results := make([]PayloadItem, 0)
|
|
for len(payload) > len(PACKET_END) {
|
|
// Decode TLV encoded datatype and length
|
|
var dtype uint16
|
|
var dlen uint16
|
|
if err := binary.Read(bytes.NewReader(payload[:2]), binary.BigEndian, &dtype); err != nil {
|
|
fmt.Println("Failed readling payload type")
|
|
continue
|
|
}
|
|
if err := binary.Read(bytes.NewReader(payload[2:4]), binary.BigEndian, &dlen); err != nil {
|
|
fmt.Println("Failed readling payload length")
|
|
continue
|
|
}
|
|
|
|
// Get TLV data
|
|
data := payload[4 : 4+dlen]
|
|
// Try to decode the datatype
|
|
payloadType := PayloadType{Id: dtype, Name: "unknown", DataType: "raw"}
|
|
for _, e := range payloadTypes {
|
|
if e.Id == dtype {
|
|
payloadType = e
|
|
}
|
|
}
|
|
// Append to list
|
|
result := PayloadItem{
|
|
Type: payloadType,
|
|
Value: data,
|
|
}
|
|
results = append(results, result)
|
|
payload = payload[4+dlen:]
|
|
}
|
|
return results
|
|
}
|
|
|
|
func applyKey(data *[]byte) {
|
|
s := make([]byte, len(KEY))
|
|
copy(s, KEY)
|
|
var j byte = 0
|
|
for k := range *data {
|
|
i := (k + 1) & 0xFF
|
|
j = (j + s[i]) & 0xFF
|
|
s[i], s[j] = s[j], s[i] // Swap elements in slice
|
|
(*data)[k] ^= s[(s[i]+s[j])&0xFF] // XOR operation
|
|
}
|
|
}
|