/
writer.go
133 lines (123 loc) · 3.04 KB
/
writer.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
package rscp
import (
"bytes"
"crypto/cipher"
"encoding/binary"
"hash/crc32"
"time"
log "github.com/sirupsen/logrus"
)
// Now returns the current local time.
// patchable for tests.
var Now = time.Now
// write wraps the binary.Write for little endian.
// supports some extra type's.
func write(buf *bytes.Buffer, v interface{}) error {
if v == nil {
return nil
}
// dereference in case we got a pointer to a value
switch v := dereferencePtr(v).(type) {
default:
if err := binary.Write(buf, binary.LittleEndian, v); err != nil {
// not testable
return err
}
case string:
if err := write(buf, []byte(v)); err != nil {
// not testable
return err
}
case time.Time:
if err := write(buf, v.Unix()); err != nil {
// not testable
return err
}
//nolint: gomnd
if err := write(buf, int32(v.UnixNano()-(v.Unix()*1e9))); err != nil {
// not testable
return err
}
case []Message:
for i := 0; i < len(v); i++ {
if err := writeMessage(buf, v[i]); err != nil {
// not testable
return err
}
}
}
return nil
}
// writeMessage writes a message recursive.
func writeMessage(buf *bytes.Buffer, m Message) error {
if err := write(buf, m.Tag); err != nil {
return err
}
if err := write(buf, m.DataType); err != nil {
return err
}
l := m.valueSize()
if err := write(buf, &l); err != nil {
return err
}
if err := write(buf, m.Value); err != nil {
return err
}
return nil
}
// writeFrame writes the rscp frame.
func writeFrame(messages []Message, useChecksum bool) ([]byte, error) {
buf := bytes.NewBuffer(make([]byte, 0, RSCP_FRAME_HEADER_SIZE))
// write header
if err := write(buf, RSCP_MAGIC); err != nil {
return nil, err
}
ctrl := (RSCP_CTRL_BIT_MASK_VERSION & (uint16(RSCP_VERSION_1_0) << RSCP_FLAG_BIT_VERSION))
if useChecksum {
ctrl |= (RSCP_CTRL_BIT_MASK_CRC & (uint16(RSCP_CRC_ENABLED) << RSCP_FLAG_BIT_CRC))
} else {
ctrl |= (RSCP_CTRL_BIT_MASK_CRC & (uint16(RSCP_CRC_DISABLED) << RSCP_FLAG_BIT_CRC))
}
if err := write(buf, ctrl); err != nil {
return nil, err
}
t := Now().UTC()
if err := write(buf, t); err != nil {
return nil, err
}
l := messagesSize(messages)
if err := write(buf, l); err != nil {
return nil, err
}
// write length and data
if err := write(buf, messages); err != nil {
return nil, err
}
// write checksum
if useChecksum {
if err := write(buf, crc32.ChecksumIEEE(buf.Bytes())); err != nil {
return nil, err
}
}
return buf.Bytes(), nil
}
// Write writes the messages and returns the encrypted rscp frame.
func Write(mode *cipher.BlockMode, messages []Message, useChecksum bool) ([]byte, error) {
log.Debugf("write %s", messages)
var (
d []byte
err error
)
if d, err = writeFrame(messages, useChecksum); err != nil {
return nil, err
}
log.Tracef("write plain %#v", d)
// padding
if len(d)%int(RSCP_CRYPT_BLOCK_SIZE) != 0 {
d = append(d, bytes.Repeat([]byte{RSCP_CRYPT_BLOCK_PADDING}, int(RSCP_CRYPT_BLOCK_SIZE)-len(d)%int(RSCP_CRYPT_BLOCK_SIZE))...)
}
// encrypt
(*mode).CryptBlocks(d, d)
log.Tracef("write crypt %#v", d)
return d, nil
}