forked from aerostitch/nscatools
-
Notifications
You must be signed in to change notification settings - Fork 2
/
datapacket.go
318 lines (288 loc) · 8.91 KB
/
datapacket.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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
package nscatools
import (
"bytes"
"encoding/binary"
"fmt"
"hash/crc32"
"io"
"io/ioutil"
"time"
)
// DataPacket stores the data received for the client-server communication
type DataPacket struct {
Version int16
Crc uint32
Timestamp uint32
State int16
HostName string
Service string
PluginOutput string
Ipkt *InitPacket
Password []byte
Encryption int
}
// NewDataPacket initializes a new blank data packet
func NewDataPacket(encryption int, password []byte, ipkt *InitPacket) *DataPacket {
packet := DataPacket{
Version: 3,
Crc: 0,
Timestamp: uint32(time.Now().Unix()),
State: StateUnknown,
HostName: "",
Service: "",
PluginOutput: "",
Ipkt: ipkt,
Password: password,
Encryption: encryption,
}
return &packet
}
// CalculateCrc returns the Crc of a packet ready to be sent over the network,
// ignoring the Crc data part of it as it's done in the original nsca code
func (p *DataPacket) CalculateCrc(buffer []byte) uint32 {
crcdPacket := make([]byte, len(buffer))
copy(crcdPacket, buffer[0:4])
copy(crcdPacket[8:], buffer[8:])
return crc32.ChecksumIEEE(crcdPacket)
}
// Read gets the data packet and populates the attributes of the DataPacket
// according. When encountering an error, it returns the error and don't process
// further.
func (p *DataPacket) Read(conn io.Reader) error {
// We need to read the full packet 1st to check the crc and decrypt it too
fullPacket, err := ioutil.ReadAll(conn)
if err != nil {
return err
}
if len(fullPacket) != ShortPacketLength && len(fullPacket) != LongPacketLength {
return fmt.Errorf("Dropping packet with invalid size: %d", len(fullPacket))
}
if err := p.Decrypt(fullPacket); err != nil {
return err
}
p.Crc = binary.BigEndian.Uint32(fullPacket[4:8])
if crc32 := p.CalculateCrc(fullPacket); p.Crc != crc32 {
return fmt.Errorf("Dropping packet with invalid CRC32 - possibly due to client using wrong password or crypto algorithm?")
}
p.Timestamp = binary.BigEndian.Uint32(fullPacket[8:12])
// MaxPacketAge <= 0 means that we don't check it
if MaxPacketAge > 0 {
if p.Timestamp > (p.Ipkt.Timestamp+MaxPacketAge) || p.Timestamp < (p.Ipkt.Timestamp-MaxPacketAge) {
return fmt.Errorf("Dropping packet with stale timestamp - Max age difference is %d seconds", MaxPacketAge)
}
}
sep := []byte("\x00") // sep is used to extract only the useful string
p.Version = int16(binary.BigEndian.Uint16(fullPacket[0:2]))
p.State = int16(binary.BigEndian.Uint16(fullPacket[12:14]))
p.HostName = string(bytes.Split(fullPacket[14:78], sep)[0])
p.Service = string(bytes.Split(fullPacket[78:206], sep)[0])
p.PluginOutput = string(bytes.Split(fullPacket[206:], sep)[0])
return nil
}
// Write generates the buffer to write to the writer based on the populated
// fields of the DataPacket instance encrypts it if needed and send it to the
// writer.
// When encountering an error, it returns the error and don't process
// further.
func (p *DataPacket) Write(w io.Writer) error {
// Build network packet
packet := new(bytes.Buffer)
binary.Write(packet, binary.BigEndian, p.Version)
binary.Write(packet, binary.BigEndian, make([]byte, 6))
binary.Write(packet, binary.BigEndian, p.Timestamp)
binary.Write(packet, binary.BigEndian, p.State)
h := make([]byte, 64)
copy(h, p.HostName)
binary.Write(packet, binary.BigEndian, h)
s := make([]byte, 128)
copy(s, p.Service)
binary.Write(packet, binary.BigEndian, s)
o := make([]byte, 64)
copy(o, p.PluginOutput)
binary.Write(packet, binary.BigEndian, o)
binary.Write(packet, binary.BigEndian, make([]byte, 2))
buf := make([]byte, LongPacketLength)
copy(buf, packet.Bytes())
// Calculate the Crc
binary.BigEndian.PutUint32(buf[4:8], p.CalculateCrc(buf))
// Encrypt
if err := p.Encrypt(buf); err != nil {
return err
}
// Write + consistency check
if n, err := w.Write(buf); err != nil || n != len(buf) {
return fmt.Errorf("%d bytes written, expecting %d. Error: %s", n, len(buf), err)
}
return nil
}
// Performs a XOR operation on a buffer using the initialization vector and the
// password.
func (p *DataPacket) xor(buffer []byte) {
bufferSize := len(buffer)
ivSize := len(p.Ipkt.Iv)
pwdSize := len(p.Password)
// Rotating over the initialization vector of the connection and the password
for y := 0; y < bufferSize; y++ {
buffer[y] ^= p.Ipkt.Iv[y%ivSize] ^ p.Password[y%pwdSize]
}
}
// setAlgo translates the encryption to an mcrypt-understandable from algorithm name
func (p *DataPacket) setAlgo() string {
var algo string
switch p.Encryption {
case EncryptDES:
algo = "des"
case Encrypt3DES:
algo = "tripledes"
case EncryptCAST128:
algo = "cast-128"
case EncryptCAST256:
algo = "cast-256"
case EncryptXTEA:
algo = "xtea"
case Encrypt3WAY:
algo = "threeway"
case EncryptBLOWFISH:
algo = "blowfish"
case EncryptTWOFISH:
algo = "twofish"
case EncryptLOKI97:
algo = "loki97"
case EncryptRC2:
algo = "rc2"
case EncryptARCFOUR:
algo = "arcfour"
case EncryptRIJNDAEL128:
algo = "rijndael-128"
case EncryptRIJNDAEL192:
algo = "rijndael-192"
case EncryptRIJNDAEL256:
algo = "rijndael-256"
case EncryptWAKE:
algo = "wake"
case EncryptSERPENT:
algo = "serpent"
case EncryptENIGMA:
algo = "enigma"
case EncryptGOST:
algo = "gost"
case EncryptSAFER64:
algo = "safer-sk64"
case EncryptSAFER128:
algo = "safer-sk128"
case EncryptSAFERPLUS:
algo = "saferplus"
default:
algo = "Unknown"
}
return algo
}
// Decrypt decrypts a buffer
func (p *DataPacket) Decrypt(buffer []byte) error {
var (
algo string
err error
)
switch p.Encryption {
case EncryptNone: // Just don't do anything
case EncryptXOR:
p.xor(buffer)
case EncryptIDEA: // Unsupported in standard NSCA
err = fmt.Errorf("Unimplemented encryption algorithm")
case EncryptRC6: // Unsupported in standard NSCA
err = fmt.Errorf("Unimplemented encryption algorithm")
case EncryptMARS: // Unsupported in standard NSCA
err = fmt.Errorf("Unimplemented encryption algorithm")
case EncryptPANAMA: // Unsupported in standard NSCA
err = fmt.Errorf("Unimplemented encryption algorithm")
default:
algo = p.setAlgo()
if algo == "Unknown" {
err = fmt.Errorf("%d is an unrecognized encryption integer", p.Encryption)
}
}
if err != nil {
return err
}
if algo != "" {
err = MCrypt(algo, buffer, p.Password, p.Ipkt.Iv, true)
}
return err
}
// Encrypt encrypts a buffer
func (p *DataPacket) Encrypt(buffer []byte) error {
var (
algo string
err error
)
switch p.Encryption {
case EncryptNone:
case EncryptXOR:
p.xor(buffer)
case EncryptIDEA: // Unsupported in standard NSCA
err = fmt.Errorf("Unimplemented encryption algorithm")
case EncryptRC6: // Unsupported in standard NSCA
err = fmt.Errorf("Unimplemented encryption algorithm")
case EncryptMARS: // Unsupported in standard NSCA
err = fmt.Errorf("Unimplemented encryption algorithm")
case EncryptPANAMA: // Unsupported in standard NSCA
err = fmt.Errorf("Unimplemented encryption algorithm")
default:
algo = p.setAlgo()
if algo == "Unknown" {
err = fmt.Errorf("%d is an unrecognized encryption integer", p.Encryption)
}
}
if err != nil {
return err
}
if algo != "" {
err = MCrypt(algo, buffer, p.Password, p.Ipkt.Iv, false)
}
return err
}
// Encrypt* are the encryptions supported by the standard NSCA configuration
const (
EncryptNone = iota // no encryption
EncryptXOR // Simple XOR (No security, just obfuscation, but very fast)
EncryptDES // DES
Encrypt3DES // 3DES or Triple DES
EncryptCAST128 // CAST-128
EncryptCAST256 // CAST-256
EncryptXTEA // xTEA
Encrypt3WAY // 3-WAY
EncryptBLOWFISH // SKIPJACK
EncryptTWOFISH // TWOFISH
EncryptLOKI97 // LOKI97
EncryptRC2 // RC2
EncryptARCFOUR // RC4
EncryptRC6 // RC6 - Unsupported in standard NSCA
EncryptRIJNDAEL128 // AES-128
EncryptRIJNDAEL192 // AES-192
EncryptRIJNDAEL256 // AES-256
EncryptMARS // MARS - Unsupported in standard NSCA
EncryptPANAMA // PANAMA - Unsupported in standard NSCA
EncryptWAKE // WAKE
EncryptSERPENT // SERPENT
EncryptIDEA // IDEA - Unsupported in standard NSCA
EncryptENIGMA // ENIGMA (Unix crypt)
EncryptGOST // GOST
EncryptSAFER64 // SAFER-sk64
EncryptSAFER128 // SAFER-sk128
EncryptSAFERPLUS // SAFER+
)
// State* are the states understood by NSCA
const (
StateOK = iota
StateWarning
StateCritical
StateUnknown
)
// MaxPacketAge is the number of seconds difference allowed between the
// initialization packet epoch and the epoch of the data packet received
const MaxPacketAge = 30
// NSCA supports two lengths of the plugin output:
// ShortPacketLength - short one:
const ShortPacketLength = 720
// LongPacketLength - long one:
const LongPacketLength = 4304