🎉 Initial version
This commit is contained in:
113
TPLinkClient/Protocol.go
Normal file
113
TPLinkClient/Protocol.go
Normal file
@ -0,0 +1,113 @@
|
||||
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
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user